fix: Show irradiance values #123

This commit is contained in:
Janez T
2026-03-19 09:24:53 +01:00
parent e683ab933f
commit a32d5d2c85
8 changed files with 47 additions and 33 deletions

View File

@@ -489,7 +489,7 @@
buildSettings = { buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = 119; CURRENT_PROJECT_VERSION = 121;
DEVELOPMENT_TEAM = JND55328G8; DEVELOPMENT_TEAM = JND55328G8;
ENABLE_BITCODE = NO; ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist; INFOPLIST_FILE = Runner/Info.plist;
@@ -511,7 +511,7 @@
buildSettings = { buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)"; BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 119; CURRENT_PROJECT_VERSION = 121;
DEVELOPMENT_TEAM = JND55328G8; DEVELOPMENT_TEAM = JND55328G8;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0; MARKETING_VERSION = 1.0;
@@ -530,7 +530,7 @@
buildSettings = { buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)"; BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 119; CURRENT_PROJECT_VERSION = 121;
DEVELOPMENT_TEAM = JND55328G8; DEVELOPMENT_TEAM = JND55328G8;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0; MARKETING_VERSION = 1.0;
@@ -547,7 +547,7 @@
buildSettings = { buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)"; BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 119; CURRENT_PROJECT_VERSION = 121;
DEVELOPMENT_TEAM = JND55328G8; DEVELOPMENT_TEAM = JND55328G8;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0; MARKETING_VERSION = 1.0;
@@ -679,7 +679,7 @@
buildSettings = { buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = 119; CURRENT_PROJECT_VERSION = 121;
DEVELOPMENT_TEAM = JND55328G8; DEVELOPMENT_TEAM = JND55328G8;
ENABLE_BITCODE = NO; ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist; INFOPLIST_FILE = Runner/Info.plist;
@@ -702,7 +702,7 @@
buildSettings = { buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = 119; CURRENT_PROJECT_VERSION = 121;
DEVELOPMENT_TEAM = JND55328G8; DEVELOPMENT_TEAM = JND55328G8;
ENABLE_BITCODE = NO; ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist; INFOPLIST_FILE = Runner/Info.plist;

View File

@@ -43,7 +43,7 @@
<key>CFBundleSignature</key> <key>CFBundleSignature</key>
<string>????</string> <string>????</string>
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
<string>119</string> <string>121</string>
<key>LSRequiresIPhoneOS</key> <key>LSRequiresIPhoneOS</key>
<true/> <true/>
<key>ITSAppUsesNonExemptEncryption</key> <key>ITSAppUsesNonExemptEncryption</key>

View File

@@ -5,22 +5,22 @@
<testcase classname="fastlane.lanes" name="0: default_platform" time="0.00103"> <testcase classname="fastlane.lanes" name="0: default_platform" time="0.000814">
</testcase> </testcase>
<testcase classname="fastlane.lanes" name="1: increment_build_number" time="1.372199"> <testcase classname="fastlane.lanes" name="1: increment_build_number" time="0.353323">
</testcase> </testcase>
<testcase classname="fastlane.lanes" name="2: build_app" time="121.394124"> <testcase classname="fastlane.lanes" name="2: build_app" time="115.148883">
</testcase> </testcase>
<testcase classname="fastlane.lanes" name="3: upload_to_app_store" time="365.414242"> <testcase classname="fastlane.lanes" name="3: upload_to_app_store" time="204.902574">
</testcase> </testcase>

View File

@@ -211,8 +211,10 @@ List<SensorMetricOption> sensorMetricOptionsFor(
// Skip them from extraSensorData to avoid duplicates. // Skip them from extraSensorData to avoid duplicates.
final coreFieldKeys = <String>{ final coreFieldKeys = <String>{
if (batteryMilliVolts != null || batteryPercentage != null) if (batteryMilliVolts != null || batteryPercentage != null)
...extraSensorData?.keys.where((k) => ...extraSensorData?.keys.where(
k.startsWith('voltage_') || k.startsWith('analog_input_')) ?? [], (k) => k.startsWith('voltage_') || k.startsWith('analog_input_'),
) ??
[],
if (temperature != null) if (temperature != null)
...extraSensorData?.keys.where((k) => k.startsWith('temperature_')) ?? [], ...extraSensorData?.keys.where((k) => k.startsWith('temperature_')) ?? [],
if (humidity != null) if (humidity != null)
@@ -290,14 +292,14 @@ SensorMetricCardData? _buildOptionPreviewCardData(
); );
case 'illuminance': case 'illuminance':
final lux = _previewAsDouble(value); final lux = _previewAsDouble(value);
final irradiance = lux == null
? null
: _previewFormatDaylightIrradiance(lux);
return SensorMetricCardData( return SensorMetricCardData(
fieldKey: fieldKey, fieldKey: fieldKey,
icon: Icons.light_mode_outlined, icon: Icons.light_mode_outlined,
label: label, label: label,
value: previewValue, value: irradiance ?? previewValue,
secondaryValue: lux == null
? null
: '~${_formatPreviewNumber(_previewApproxDaylightIrradiance(lux), maxFractionDigits: 1)} W/m2 daylight',
accent: const Color(0xFFC17B1D), accent: const Color(0xFFC17B1D),
channel: metricKey.channel, channel: metricKey.channel,
); );
@@ -674,6 +676,10 @@ double _previewApproxDaylightIrradiance(double lux) {
return lux / 120.0; return lux / 120.0;
} }
String _previewFormatDaylightIrradiance(double lux) {
return '~${_formatPreviewNumber(_previewApproxDaylightIrradiance(lux), maxFractionDigits: 1)} W/m2';
}
String _previewFormatCardinalDirection(double degrees) { String _previewFormatCardinalDirection(double degrees) {
const points = <String>['N', 'NE', 'E', 'SE', 'S', 'SW', 'W', 'NW']; const points = <String>['N', 'NE', 'E', 'SE', 'S', 'SW', 'W', 'NW'];
final normalized = ((degrees % 360) + 360) % 360; final normalized = ((degrees % 360) + 360) % 360;
@@ -699,7 +705,7 @@ String? _sensorMetricPreviewValue(String rawKey, dynamic value) {
case 'illuminance': case 'illuminance':
final lux = _previewAsDouble(value); final lux = _previewAsDouble(value);
if (lux == null) return null; if (lux == null) return null;
return '${_formatPreviewNumber(lux, maxFractionDigits: 0)} lx'; return _previewFormatDaylightIrradiance(lux);
case 'presence': case 'presence':
final isPresent = _previewAsBool(value); final isPresent = _previewAsBool(value);
@@ -1439,9 +1445,7 @@ class SensorTelemetryCard extends StatelessWidget {
fieldKey: _extraFieldKey(rawKey), fieldKey: _extraFieldKey(rawKey),
icon: Icons.light_mode_outlined, icon: Icons.light_mode_outlined,
label: label, label: label,
value: '${_formatNumber(lux, maxFractionDigits: 0)} lx', value: _formatDaylightIrradiance(lux),
secondaryValue:
'~${_formatNumber(_approxDaylightIrradiance(lux), maxFractionDigits: 1)} W/m2 daylight',
accent: const Color(0xFFC17B1D), accent: const Color(0xFFC17B1D),
channel: metricKey.channel, channel: metricKey.channel,
); );
@@ -1958,6 +1962,10 @@ class SensorTelemetryCard extends StatelessWidget {
return lux / 120.0; return lux / 120.0;
} }
String _formatDaylightIrradiance(double lux) {
return '~${_formatNumber(_approxDaylightIrradiance(lux), maxFractionDigits: 1)} W/m2';
}
String _formatCurrent(double amps) { String _formatCurrent(double amps) {
final absolute = amps.abs(); final absolute = amps.abs();
if (absolute < 1.0) { if (absolute < 1.0) {

View File

@@ -16,7 +16,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
# In Windows, build-name is used as the major, minor, and patch parts # In Windows, build-name is used as the major, minor, and patch parts
# of the product and file versions while build-number is used as the build suffix. # of the product and file versions while build-number is used as the build suffix.
version: 2026.0317.1+33 version: 2026.0318.1+34
environment: environment:
sdk: ^3.9.2 sdk: ^3.9.2

View File

@@ -164,18 +164,16 @@ void main() {
expect(find.text('CO2'), findsOneWidget); expect(find.text('CO2'), findsOneWidget);
expect(find.text('415 ppm'), findsOneWidget); expect(find.text('415 ppm'), findsOneWidget);
expect(find.text('Illuminance'), findsOneWidget); expect(find.text('Illuminance'), findsOneWidget);
expect(find.text('~4.2 W/m2 daylight'), findsOneWidget); expect(find.text('~4.2 W/m2'), findsOneWidget);
expect(find.textContaining('lx'), findsNothing);
expect(find.text('Current'), findsOneWidget); expect(find.text('Current'), findsOneWidget);
expect(find.text('15 mA'), findsOneWidget); expect(find.text('15 mA'), findsOneWidget);
expect(find.text('Power'), findsOneWidget); expect(find.text('Power'), findsOneWidget);
expect(find.text('Distance'), findsOneWidget); expect(find.text('Distance'), findsOneWidget);
expect( expect(find.byKey(const ValueKey('sensor_metric_battery')), findsOneWidget);
find.byKey(const ValueKey('sensor_metric_channel_battery')),
findsOneWidget,
);
expect(find.text('ch1'), findsWidgets); expect(find.text('ch1'), findsWidgets);
expect( expect(
find.byKey(const ValueKey('sensor_metric_channel_extra:illuminance_2')), find.byKey(const ValueKey('sensor_metric_extra:illuminance_2')),
findsOneWidget, findsOneWidget,
); );

View File

@@ -1,5 +1,6 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_test/flutter_test.dart';
import 'package:meshcore_sar_app/l10n/app_localizations.dart';
import 'package:meshcore_sar_app/screens/sensors_tab.dart'; import 'package:meshcore_sar_app/screens/sensors_tab.dart';
import 'package:meshcore_sar_app/widgets/sensors/sensor_telemetry_card.dart'; import 'package:meshcore_sar_app/widgets/sensors/sensor_telemetry_card.dart';
@@ -7,6 +8,8 @@ void main() {
testWidgets('renders selector previews and channel badges', (tester) async { testWidgets('renders selector previews and channel badges', (tester) async {
await tester.pumpWidget( await tester.pumpWidget(
MaterialApp( MaterialApp(
localizationsDelegates: AppLocalizations.localizationsDelegates,
supportedLocales: AppLocalizations.supportedLocales,
home: Scaffold( home: Scaffold(
body: SensorMetricSelectorItem( body: SensorMetricSelectorItem(
option: const SensorMetricOption( option: const SensorMetricOption(
@@ -14,12 +17,12 @@ void main() {
label: 'Illuminance (ch 2)', label: 'Illuminance (ch 2)',
defaultLabel: 'Illuminance', defaultLabel: 'Illuminance',
channel: 2, channel: 2,
valuePreview: '500 lx', valuePreview: '~4.2 W/m2',
previewCardData: SensorMetricCardData( previewCardData: SensorMetricCardData(
fieldKey: 'extra:illuminance_2', fieldKey: 'extra:illuminance_2',
icon: Icons.light_mode_outlined, icon: Icons.light_mode_outlined,
label: 'Illuminance', label: 'Illuminance',
value: '500 lx', value: '~4.2 W/m2',
accent: Color(0xFFC17B1D), accent: Color(0xFFC17B1D),
channel: 2, channel: 2,
), ),
@@ -39,7 +42,8 @@ void main() {
); );
expect(find.text('Show on sensor card'), findsOneWidget); expect(find.text('Show on sensor card'), findsOneWidget);
expect(find.text('500 lx'), findsOneWidget); expect(find.text('~4.2 W/m2'), findsOneWidget);
expect(find.textContaining('lx'), findsNothing);
expect( expect(
find.byKey(const ValueKey('sensor_selector_channel_extra:illuminance_2')), find.byKey(const ValueKey('sensor_selector_channel_extra:illuminance_2')),
findsOneWidget, findsOneWidget,
@@ -50,6 +54,8 @@ void main() {
testWidgets('omits duplicate channel chip for channel 1', (tester) async { testWidgets('omits duplicate channel chip for channel 1', (tester) async {
await tester.pumpWidget( await tester.pumpWidget(
MaterialApp( MaterialApp(
localizationsDelegates: AppLocalizations.localizationsDelegates,
supportedLocales: AppLocalizations.supportedLocales,
home: Scaffold( home: Scaffold(
body: SensorMetricSelectorItem( body: SensorMetricSelectorItem(
option: const SensorMetricOption( option: const SensorMetricOption(

View File

@@ -63,12 +63,14 @@ void main() {
expect(find.text('Light'), findsOneWidget); expect(find.text('Light'), findsOneWidget);
expect(find.text('Temperature'), findsNothing); expect(find.text('Temperature'), findsNothing);
expect(find.text('Illuminance'), findsNothing); expect(find.text('Illuminance'), findsNothing);
expect(find.text('~4.2 W/m2'), findsOneWidget);
expect(find.textContaining('lx'), findsNothing);
expect( expect(
find.byKey(const ValueKey('sensor_metric_channel_temperature')), find.byKey(const ValueKey('sensor_metric_temperature')),
findsOneWidget, findsOneWidget,
); );
expect( expect(
find.byKey(const ValueKey('sensor_metric_channel_extra:illuminance_2')), find.byKey(const ValueKey('sensor_metric_extra:illuminance_2')),
findsOneWidget, findsOneWidget,
); );
}); });