From f6c6315596602e7bedb5f8ca3c923a5cc8f6efe2 Mon Sep 17 00:00:00 2001 From: dvdrw Date: Fri, 22 May 2026 14:43:19 +0200 Subject: [PATCH] feat: offer to calibrate sensor after provisioning --- .../ble_provision/ble_provision_sheet.dart | 62 ++++++++++++++++--- 1 file changed, 52 insertions(+), 10 deletions(-) diff --git a/lib/features/ble_provision/ble_provision_sheet.dart b/lib/features/ble_provision/ble_provision_sheet.dart index 76c775a..240332f 100644 --- a/lib/features/ble_provision/ble_provision_sheet.dart +++ b/lib/features/ble_provision/ble_provision_sheet.dart @@ -4,7 +4,9 @@ import 'package:go_router/go_router.dart'; import '../../data/sources/ble/ble_provisioner.dart'; import '../../data/sources/local/credential_store.dart'; +import '../../domain/models/sensor.dart'; import '../../providers.dart'; +import '../sensors/calibration_sheet.dart'; enum _Step { scan, configure, done } @@ -30,6 +32,7 @@ class _BleProvisionSheetState extends ConsumerState { _Step _step = _Step.scan; BleScanResult? _selected; + Sensor? _provisionedSensor; bool _provisioning = false; bool _mqttOverrideEnabled = false; bool _usingNewWifiConnection = true; @@ -102,7 +105,7 @@ class _BleProvisionSheetState extends ConsumerState { mqttHost: mqttHost, mqttPort: mqttPort, ); - await ref + _provisionedSensor = await ref .read(sensorRepositoryProvider) .createSensor(_selected!.name, name: _selected!.name); @@ -254,12 +257,26 @@ class _BleProvisionSheetState extends ConsumerState { ), _DoneStep( onGoToFloorPlan: () { - final router = GoRouter.of(context); - Navigator.of(context).pop(); - router.go('/floorplan'); + if (_provisionedSensor != null) { + _placeOnFloorPlan(context, _provisionedSensor!); + } }, onAddAnother: _reset, onDone: () => Navigator.of(context).pop(), + onCalibrate: _provisionedSensor == null + ? null + : () { + final sensor = _provisionedSensor!; + Navigator.of( + context, + rootNavigator: true, + ).pop(); + showCalibrationSheet( + context, + sensor.id, + sensor.sensorId, + ); + }, ), ] .map( @@ -276,6 +293,16 @@ class _BleProvisionSheetState extends ConsumerState { ), ); } + + void _placeOnFloorPlan(BuildContext context, Sensor sensor) { + final router = GoRouter.of(context); + final fromSensors = + router.routeInformationProvider.value.uri.path == '/sensors'; + ref.read(sensorPlacementProvider.notifier).state = sensor; + ref.read(sensorPlacementOriginSensorsProvider.notifier).state = fromSensors; + Navigator.of(context, rootNavigator: true).pop(); + router.go('/floorplan'); + } } class _ScanPage extends StatefulWidget { @@ -491,9 +518,7 @@ class _ConfigurePageState extends State<_ConfigurePage> { border: const OutlineInputBorder(), suffixIcon: IconButton( icon: Icon( - _obscurePassword - ? Icons.visibility_off - : Icons.visibility, + _obscurePassword ? Icons.visibility_off : Icons.visibility, ), onPressed: () => setState(() => _obscurePassword = !_obscurePassword), @@ -595,14 +620,17 @@ class _DoneStep extends StatelessWidget { required this.onGoToFloorPlan, required this.onAddAnother, required this.onDone, + this.onCalibrate, }); final VoidCallback onGoToFloorPlan; final VoidCallback onAddAnother; final VoidCallback onDone; + final VoidCallback? onCalibrate; @override Widget build(BuildContext context) { + final theme = Theme.of(context); return SingleChildScrollView( padding: const EdgeInsets.symmetric(vertical: 16), child: Column( @@ -613,22 +641,36 @@ class _DoneStep extends StatelessWidget { const SizedBox(height: 16), Text( 'Sensor provisioned!', - style: Theme.of(context).textTheme.titleLarge, + style: theme.textTheme.titleLarge, textAlign: TextAlign.center, ), const SizedBox(height: 8), Text( 'The sensor will appear in the list once it connects to the network. ' 'Open the floor plan to place it.', - style: Theme.of(context).textTheme.bodyMedium, + style: theme.textTheme.bodyMedium, textAlign: TextAlign.center, ), const SizedBox(height: 32), FilledButton.icon( - icon: const Icon(Icons.map_outlined), + icon: const Icon(Icons.gps_fixed), label: const Text('Place on floor plan'), onPressed: onGoToFloorPlan, ), + const SizedBox(height: 16), + OutlinedButton.icon( + icon: const Icon(Icons.tune), + label: const Text('Calibrate sensor'), + onPressed: onCalibrate, + ), + const SizedBox(height: 8), + Text( + 'For best accuracy, calibrate after mounting the sensor in its intended position.', + style: theme.textTheme.bodySmall?.copyWith( + color: theme.colorScheme.onSurfaceVariant, + ), + textAlign: TextAlign.center, + ), const SizedBox(height: 12), OutlinedButton.icon( icon: const Icon(Icons.add),