feat: grab sensor firmware version for detail sheet

This commit is contained in:
2026-05-20 19:43:15 +02:00
parent e0a3d5e481
commit acbba735a0
6 changed files with 49 additions and 12 deletions
@@ -52,6 +52,9 @@ class PhoenixSensorRepository implements SensorRepository {
Future<Sensor> unplaceSensor(int id) async =>
Sensor.fromJson(await client.unplaceSensor(id));
@override
Future<String?> getVersion(int id) => client.getVersion(id);
@override
Stream<Map<String, dynamic>> sensorEvents() => realtime
.channelMessages('sensors')
@@ -11,6 +11,8 @@ abstract class SensorRepository {
{required int roomId, required double x, required double y});
Future<Sensor> unplaceSensor(int id);
Future<String?> getVersion(int id);
/// Stream of raw SensorsChannel messages. Each map contains an `event` key
/// (`sensor_announced` or `sensor_enrollment_timeout`) plus the payload.
Stream<Map<String, dynamic>> sensorEvents();
+27 -11
View File
@@ -13,30 +13,46 @@ class SensorClient extends LocaliserdClient {
await get('/api/sensors/$id') as Map<String, dynamic>;
Future<Map<String, dynamic>> updateSensor(
int id, Map<String, dynamic> params) async =>
await put('/api/sensors/$id', params) as Map<String, dynamic>;
int id,
Map<String, dynamic> params,
) async => await put('/api/sensors/$id', params) as Map<String, dynamic>;
Future<void> deleteSensor(int id) => delete('/api/sensors/$id');
Future<Map<String, dynamic>> placeSensor(
int id, Map<String, dynamic> params) async =>
int id,
Map<String, dynamic> params,
) async =>
await put('/api/sensors/$id/place', params) as Map<String, dynamic>;
Future<Map<String, dynamic>> unplaceSensor(int id) async =>
await deleteBody('/api/sensors/$id/place') as Map<String, dynamic>;
Future<Map<String, dynamic>> createSensor(String sensorId,
{String? name}) async =>
Future<Map<String, dynamic>> createSensor(
String sensorId, {
String? name,
}) async =>
await post('/api/sensors', {
'sensor_id': sensorId,
if (name != null) 'name': name,
}) as Map<String, dynamic>;
'sensor_id': sensorId,
if (name != null) 'name': name,
})
as Map<String, dynamic>;
Future<Map<String, dynamic>> startCalibration(
int id, double referenceDistance) async =>
await post('/api/sensors/$id/calibration/start',
{'reference_distance': referenceDistance}) as Map<String, dynamic>;
int id,
double referenceDistance,
) async =>
await post('/api/sensors/$id/calibration/start', {
'reference_distance': referenceDistance,
})
as Map<String, dynamic>;
Future<Map<String, dynamic>> stopCalibration(int id) async =>
await post('/api/sensors/$id/calibration/stop') as Map<String, dynamic>;
Future<String> getVersion(int id) async {
final response =
(await get('/api/sensors/$id/version') as Map<String, dynamic>);
return response['version']!;
}
}
+5 -1
View File
@@ -7,6 +7,7 @@ class Sensor {
this.roomId,
this.x,
this.y,
this.version,
});
final int id;
@@ -16,6 +17,7 @@ class Sensor {
final int? roomId;
final double? x; // room relative
final double? y; // room relative
final String? version;
bool get isPlaced => roomId != null && x != null && y != null;
String get displayName => name ?? sensorId;
@@ -28,6 +30,7 @@ class Sensor {
roomId: json['room_id'] as int?,
x: (json['x'] as num?)?.toDouble(),
y: (json['y'] as num?)?.toDouble(),
version: json['version'] as String?,
);
Sensor copyWith({
@@ -35,7 +38,7 @@ class Sensor {
int? roomId,
double? x,
double? y,
double? rssiRef,
String? version,
}) =>
Sensor(
id: id,
@@ -45,5 +48,6 @@ class Sensor {
roomId: roomId ?? this.roomId,
x: x ?? this.x,
y: y ?? this.y,
version: version ?? this.version,
);
}
@@ -89,6 +89,7 @@ class _SensorDetailSheetState extends ConsumerState<SensorDetailSheet> {
Widget build(BuildContext context) {
final sensorAsync = ref.watch(sensorProvider(widget.sensorId));
final roomsAsync = ref.watch(roomsProvider);
final versionAsync = ref.watch(sensorVersionProvider(widget.sensorId));
return sensorAsync.when(
loading: () => const SizedBox(
@@ -110,9 +111,12 @@ class _SensorDetailSheetState extends ConsumerState<SensorDetailSheet> {
?.name,
);
final version = versionAsync.whenOrNull(data: (v) => v);
return _SheetBody(
sensor: sensor,
roomName: roomName,
version: version,
editing: _editing,
nameCtrl: _nameCtrl,
onEditToggle: () => setState(() {
@@ -132,6 +136,7 @@ class _SheetBody extends StatelessWidget {
const _SheetBody({
required this.sensor,
required this.roomName,
required this.version,
required this.editing,
required this.nameCtrl,
required this.onEditToggle,
@@ -142,6 +147,7 @@ class _SheetBody extends StatelessWidget {
final Sensor sensor;
final String? roomName;
final String? version;
final bool editing;
final TextEditingController nameCtrl;
final VoidCallback onEditToggle;
@@ -210,6 +216,7 @@ class _SheetBody extends StatelessWidget {
? '(${sensor.x!.toStringAsFixed(2)}, ${sensor.y!.toStringAsFixed(2)})'
: 'Not placed',
),
_InfoRow(label: 'Firmware', value: version ?? ''),
const SizedBox(height: 24),
FilledButton.icon(
icon: Icon(Icons.my_location),
+5
View File
@@ -145,6 +145,11 @@ final sensorProvider =
return ref.watch(sensorRepositoryProvider).getSensor(id);
});
final sensorVersionProvider =
FutureProvider.autoDispose.family<String?, int>((ref, id) {
return ref.watch(sensorRepositoryProvider).getVersion(id);
});
// ---------------------------------------------------------------------------
// Tag data
// ---------------------------------------------------------------------------