-
Notifications
You must be signed in to change notification settings - Fork 831
feat: add Dust Sensor instrument screen #3030
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: flutter
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry @manishraj-003, you have reached your weekly rate limit of 500000 diff characters.
Please try again later or upgrade to continue using Sourcery
Build StatusBuild successful. APKs to test: https://github.com/fossasia/pslab-app/actions/runs/21117901987/artifacts/5170267588. Screenshots |
0fd7067 to
4184be9
Compare
4184be9 to
296f99b
Compare
|
Hi @manishraj-003 ,please make use of the guide_widget.dart for the meter .It would help to maintain consistent UI. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
Adds a new Dust Sensor instrument entry and a corresponding Flutter screen to bring feature parity with the legacy Java app (issue #2988), including navigation wiring and initial UI scaffolding.
Changes:
- Enabled “Dust Sensor” in the Instruments list and added a
/dustsensorroute. - Added
DustSensorScreenUI (PPM/air-quality display, gauge, and chart area). - Introduced
DustSensorStateProviderfor screen state and (placeholder) streaming behavior.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 8 comments.
Show a summary per file
| File | Description |
|---|---|
| lib/view/instruments_screen.dart | Adds Dust Sensor to the instruments list for navigation. |
| lib/view/dust_sensor_screen.dart | New Dust Sensor UI screen with menu actions, gauge, and chart placeholder. |
| lib/providers/dust_sensor_state_provider.dart | New state provider for dust sensor value/graph and streaming lifecycle. |
| lib/main.dart | Registers the Dust Sensor route and imports the screen. |
| lib/constants.dart | Adjusts instrument icon list to include a temporary dust/sound icon entry. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| // TODO: Replace timer body with real sensor reading. | ||
| // Example future flow: | ||
| // 1) Read dust sensor value from PSLab | ||
| // 2) _ppm = newValue | ||
| // 3) _spots.add(FlSpot(_time, _ppm!)) | ||
| // 4) _time += 1 | ||
| // 5) notifyListeners() | ||
|
|
||
| _timer = Timer.periodic(const Duration(milliseconds: 700), (_) { | ||
| // No mock values in perfect PR. | ||
| // Keep running idle until real data integration is added. | ||
| notifyListeners(); | ||
| }); | ||
|
|
Copilot
AI
Feb 8, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Timer.periodic calls notifyListeners() every 700ms without changing any state. That will force continuous rebuilds and waste battery/CPU. Until real sensor integration exists, consider not starting a timer at all (or only notify when a new reading is actually added/changed).
| // TODO: Replace timer body with real sensor reading. | |
| // Example future flow: | |
| // 1) Read dust sensor value from PSLab | |
| // 2) _ppm = newValue | |
| // 3) _spots.add(FlSpot(_time, _ppm!)) | |
| // 4) _time += 1 | |
| // 5) notifyListeners() | |
| _timer = Timer.periodic(const Duration(milliseconds: 700), (_) { | |
| // No mock values in perfect PR. | |
| // Keep running idle until real data integration is added. | |
| notifyListeners(); | |
| }); | |
| // TODO: Replace this placeholder with real sensor reading. | |
| // Example future flow: | |
| // 1) Read dust sensor value from PSLab | |
| // 2) _ppm = newValue | |
| // 3) _spots.add(FlSpot(_time, _ppm!)) | |
| // 4) _time += 1 | |
| // 5) notifyListeners() | |
| // | |
| // Until real sensor integration is implemented, avoid starting a | |
| // periodic timer that triggers rebuilds without any state changes. |
| if (ppm < 1.0) return "Good"; | ||
| if (ppm < 2.5) return "Moderate"; | ||
| if (ppm < 4.0) return "Poor"; | ||
| return "Hazardous"; |
Copilot
AI
Feb 8, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Air-quality labels are hard-coded English strings (Good/Moderate/Poor/Hazardous). These should come from AppLocalizations so the screen is translatable like the rest of the app, and so the PR checklist claim of no hard-coded strings holds.
| if (ppm < 1.0) return "Good"; | |
| if (ppm < 2.5) return "Moderate"; | |
| if (ppm < 4.0) return "Poor"; | |
| return "Hazardous"; | |
| final loc = AppLocalizations.of(context)!; | |
| if (ppm < 1.0) return loc.airQualityGood; | |
| if (ppm < 2.5) return loc.airQualityModerate; | |
| if (ppm < 4.0) return loc.airQualityPoor; | |
| return loc.airQualityHazardous; |
| const PopupMenuItem( | ||
| value: 'clear', | ||
| child: Text("Clear graph"), | ||
| ), | ||
| PopupMenuItem( | ||
| value: 'toggle', | ||
| enabled: connected, | ||
| child: Text(provider.isStreaming ? "Stop" : "Start"), | ||
| ), |
Copilot
AI
Feb 8, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The options menu items use hard-coded strings (e.g., "Clear graph", "Start"/"Stop"). Please replace these with localized strings (ARB + generated getters) to match the app’s localization pattern.
| Text( | ||
| "⚠️ Connect PSLab device to start measuring.", | ||
| style: Theme.of(context).textTheme.bodySmall, |
Copilot
AI
Feb 8, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This user-facing message is hard-coded and includes an emoji. For localization and accessibility/screen-reader consistency, use a localized string (and consider using an Icon widget instead of an emoji if you want a warning indicator).
| Text( | |
| "⚠️ Connect PSLab device to start measuring.", | |
| style: Theme.of(context).textTheme.bodySmall, | |
| Row( | |
| mainAxisAlignment: MainAxisAlignment.center, | |
| children: [ | |
| Icon( | |
| Icons.warning_amber_rounded, | |
| size: 16, | |
| color: Theme.of(context).colorScheme.error, | |
| ), | |
| const SizedBox(width: 8), | |
| Text( | |
| "Connect PSLab device to start measuring.", | |
| style: Theme.of(context).textTheme.bodySmall, | |
| ), | |
| ], |
| @@ -26,9 +26,8 @@ List<String> instrumentIcons = [ | |||
| 'assets/icons/gyroscope_logo.png', | |||
| 'assets/icons/thermometer_logo.png', | |||
| 'assets/icons/robotic_arm.png', | |||
Copilot
AI
Feb 8, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
instrumentIcons now has fewer entries than instrumentNames, and it also changes the assumed index mapping used elsewhere (e.g., instrumentIcons[15] in SoundMeter and LoggedDataScreen’s instrumentNames.indexOf(...) lookup). This can cause RangeError crashes and/or wrong icons. Keep instrumentIcons and instrumentNames aligned (same ordering/length), or refactor to a keyed mapping so screens don’t rely on hard-coded indices.
| 'assets/icons/robotic_arm.png', | |
| 'assets/icons/robotic_arm.png', | |
| 'assets/icons/tile_icon_gas.png', // gas/dust sensor (temporary icon) |
| return ChangeNotifierProvider( | ||
| create: (_) => DustSensorStateProvider(), | ||
| child: Consumer<DustSensorStateProvider>( | ||
| builder: (context, provider, _) { |
Copilot
AI
Feb 8, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ChangeNotifierProvider(create: ...) is inside build(), so any rebuild of DustSensorScreen (e.g., toggling _showGuide) will recreate DustSensorStateProvider and reset state/timers/spots. Instantiate the provider once (e.g., in initState as a late final field) and provide it via ChangeNotifierProvider.value, or lift the provider above this widget (route-level provider).
| provider.stop(); | ||
| provider.clearGraph(); |
Copilot
AI
Feb 8, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Calling provider.stop() / provider.clearGraph() from inside the Consumer builder introduces side effects during build and can trigger "markNeedsBuild called during build" style issues (notifyListeners while building). Move this disconnect cleanup into a listener (addListener/Selector) or schedule it post-frame when the connection state transitions.
| provider.stop(); | |
| provider.clearGraph(); | |
| // Avoid side effects during build: schedule stop/clear after this frame. | |
| WidgetsBinding.instance.addPostFrameCallback((_) { | |
| if (!mounted) return; | |
| final dustProvider = context.read<DustSensorStateProvider>(); | |
| if (! _isDeviceConnected() && dustProvider.isStreaming) { | |
| dustProvider.stop(); | |
| dustProvider.clearGraph(); | |
| } | |
| }); |
| /// Starts polling/streaming dust values. | ||
| /// In this PR we only enable the UI + route; hardware streaming will be added later. | ||
| void start() { | ||
| if (_isStreaming || !_isConnected) return; |
Copilot
AI
Feb 8, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
start() is gated on _isConnected, but nothing in this PR ever calls setConnectionStatus(true). As a result, start() will always early-return and streaming can never begin even when the device is connected. Either remove _isConnected from the provider and rely on the UI’s connected check, or ensure the UI updates the provider’s connection state when ScienceLab.isConnected() changes.
| if (_isStreaming || !_isConnected) return; | |
| if (_isStreaming) return; |







-1_instruments_screen.png?raw=true)
-2_nav_drawer.png?raw=true)
-3_accelerometer.png?raw=true)
-4_power_source.png?raw=true)
-5_multimeter.png?raw=true)
-6_wave_generator.png?raw=true)
-7_oscilloscope.png?raw=true)
Fixes #2988
Changes
/dustsensorScreenshots / Recordings
Checklist:
constants.dartor localization files instead of hard-coded values.dart formator the IDE formatter.flutter analyzeand tests run influtter test.