Enhance sensor telemetry cards

This commit is contained in:
Janez T
2026-03-15 10:10:22 +01:00
parent feb3145854
commit df993cd176
10 changed files with 1664 additions and 87 deletions

View File

@@ -10,6 +10,7 @@ import 'package:meshcore_sar_app/providers/map_provider.dart';
import 'package:meshcore_sar_app/providers/messages_provider.dart';
import 'package:meshcore_sar_app/providers/sensors_provider.dart';
import 'package:meshcore_sar_app/widgets/contacts/contact_tile.dart';
import 'package:meshcore_sar_app/widgets/sensors/sensor_telemetry_card.dart';
import 'package:provider/provider.dart';
import 'package:shared_preferences/shared_preferences.dart';
@@ -40,14 +41,21 @@ void main() {
);
}
Future<void> pumpTile(WidgetTester tester, Contact contact) async {
Future<void> pumpTile(
WidgetTester tester,
Contact contact, {
SensorsProvider? sensorsProvider,
}) async {
final resolvedSensorsProvider = sensorsProvider ?? SensorsProvider();
await tester.pumpWidget(
MultiProvider(
providers: [
ChangeNotifierProvider(create: (_) => ConnectionProvider()),
ChangeNotifierProvider(create: (_) => ContactsProvider()),
ChangeNotifierProvider(create: (_) => MessagesProvider()),
ChangeNotifierProvider(create: (_) => SensorsProvider()),
ChangeNotifierProvider<SensorsProvider>.value(
value: resolvedSensorsProvider,
),
ChangeNotifierProvider(create: (_) => MapProvider()),
],
child: MaterialApp(
@@ -125,6 +133,9 @@ void main() {
temperature: 21.5,
humidity: 58.0,
extraSensorData: const {
'__source_channel:battery': 1,
'__source_channel:temperature': 1,
'__source_channel:humidity': 1,
'co2': 415.0,
'illuminance_2': 500.0,
'current_2': 0.015,
@@ -151,11 +162,29 @@ void main() {
expect(find.text('21.5°C'), findsOneWidget);
expect(find.text('CO2'), findsOneWidget);
expect(find.text('415 ppm'), findsOneWidget);
expect(find.text('Illuminance (ch 2)'), findsOneWidget);
expect(find.text('Illuminance'), findsOneWidget);
expect(find.text('~4.2 W/m2 daylight'), findsOneWidget);
expect(find.text('Current (ch 2)'), findsOneWidget);
expect(find.text('Current'), findsOneWidget);
expect(find.text('15 mA'), findsOneWidget);
expect(find.text('Power (ch 2)'), findsOneWidget);
expect(find.text('Distance (ch 2)'), findsOneWidget);
expect(find.text('Power'), findsOneWidget);
expect(find.text('Distance'), findsOneWidget);
expect(
find.byKey(const ValueKey('sensor_metric_channel_battery')),
findsOneWidget,
);
expect(
find.text('ch1'),
findsWidgets,
);
expect(
find.byKey(const ValueKey('sensor_metric_channel_extra:illuminance_2')),
findsOneWidget,
);
final sensorCardSize = tester.getSize(find.byType(SensorTelemetryCard));
final batteryTileSize = tester.getSize(
find.byKey(const ValueKey('sensor_metric_battery')),
);
expect(batteryTileSize.width, greaterThan(sensorCardSize.width * 0.8));
});
}

View File

@@ -0,0 +1,144 @@
import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:meshcore_sar_app/l10n/app_localizations.dart';
import 'package:meshcore_sar_app/models/contact.dart';
import 'package:meshcore_sar_app/providers/sensors_provider.dart';
import 'package:meshcore_sar_app/widgets/sensors/sensor_telemetry_card.dart';
void main() {
Contact buildContact() {
final publicKey = Uint8List(32);
publicKey[0] = 0x44;
return Contact(
publicKey: publicKey,
type: ContactType.sensor,
flags: 0,
outPathLen: 0,
outPath: Uint8List(64),
advName: 'WX Station',
lastAdvert: DateTime.now().millisecondsSinceEpoch ~/ 1000,
advLat: 0,
advLon: 0,
lastMod: DateTime.now().millisecondsSinceEpoch ~/ 1000,
telemetry: ContactTelemetry(
temperature: 21.5,
extraSensorData: const {
'__source_channel:temperature': 1,
'illuminance_2': 500.0,
},
timestamp: DateTime.now().subtract(const Duration(minutes: 1)),
),
);
}
testWidgets('renders custom labels and channel badges', (tester) async {
final contact = buildContact();
await tester.pumpWidget(
MaterialApp(
localizationsDelegates: AppLocalizations.localizationsDelegates,
supportedLocales: AppLocalizations.supportedLocales,
home: Scaffold(
body: SensorTelemetryCard(
contact: contact,
state: SensorRefreshState.idle,
visibleFields: const {'temperature', 'extra:illuminance_2'},
labelOverrides: const {
'temperature': 'Ambient',
'extra:illuminance_2': 'Light',
},
fieldSpans: sensorFullWidthFieldSpans(
const {'temperature', 'extra:illuminance_2'},
),
),
),
),
);
expect(find.text('Ambient'), findsOneWidget);
expect(find.text('Light'), findsOneWidget);
expect(find.text('Temperature'), findsNothing);
expect(find.text('Illuminance'), findsNothing);
expect(
find.byKey(const ValueKey('sensor_metric_channel_temperature')),
findsOneWidget,
);
expect(
find.byKey(const ValueKey('sensor_metric_channel_extra:illuminance_2')),
findsOneWidget,
);
});
testWidgets('renders metrics in the provided order', (tester) async {
final publicKey = Uint8List(32);
publicKey[0] = 0x45;
final contact = Contact(
publicKey: publicKey,
type: ContactType.sensor,
flags: 0,
outPathLen: 0,
outPath: Uint8List(64),
advName: 'WX Station',
lastAdvert: DateTime.now().millisecondsSinceEpoch ~/ 1000,
advLat: 0,
advLon: 0,
lastMod: DateTime.now().millisecondsSinceEpoch ~/ 1000,
telemetry: ContactTelemetry(
batteryPercentage: 84,
temperature: 21.5,
extraSensorData: const {
'__source_channel:battery': 1,
'__source_channel:temperature': 1,
'illuminance_2': 500.0,
},
timestamp: DateTime.now().subtract(const Duration(minutes: 1)),
),
);
await tester.pumpWidget(
MaterialApp(
localizationsDelegates: AppLocalizations.localizationsDelegates,
supportedLocales: AppLocalizations.supportedLocales,
home: Scaffold(
body: SensorTelemetryCard(
contact: contact,
state: SensorRefreshState.idle,
visibleFields: const {
'battery',
'temperature',
'extra:illuminance_2',
},
fieldOrder: const [
'extra:illuminance_2',
'temperature',
'battery',
],
fieldSpans: sensorFullWidthFieldSpans(
const {
'battery',
'temperature',
'extra:illuminance_2',
},
),
),
),
),
);
final illuminanceTop = tester.getTopLeft(
find.byKey(const ValueKey('sensor_metric_extra:illuminance_2')),
);
final temperatureTop = tester.getTopLeft(
find.byKey(const ValueKey('sensor_metric_temperature')),
);
final batteryTop = tester.getTopLeft(
find.byKey(const ValueKey('sensor_metric_battery')),
);
expect(illuminanceTop.dy, lessThan(temperatureTop.dy));
expect(temperatureTop.dy, lessThan(batteryTop.dy));
});
}