feat: offer to calibrate sensor after provisioning

This commit is contained in:
2026-05-22 14:43:19 +02:00
parent 5c90c7f514
commit f6c6315596
@@ -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<BleProvisionSheet> {
_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<BleProvisionSheet> {
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<BleProvisionSheet> {
),
_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<BleProvisionSheet> {
),
);
}
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),