Skip to content

Conversation

@manishraj-003
Copy link
Contributor

@manishraj-003 manishraj-003 commented Jan 18, 2026

Fixes #2988

Changes

  • Added Dust Sensor instrument to Instruments list
  • Implemented Dust Sensor screen UI (Particles PPM, Air Quality, gauge and chart placeholder)
  • Added route mapping for /dustsensor

Screenshots / Recordings

Screenshot 2026-01-19 005243 Screenshot 2026-01-19 005222

Checklist:

  • No hard coding: I have used values from constants.dart or localization files instead of hard-coded values.
  • No end of file edits: No modifications done at end of resource files.
  • Code reformatting: I have formatted the code using dart format or the IDE formatter.
  • Code analysis: My code passes checks run in flutter analyze and tests run in flutter test.

Copy link
Contributor

@sourcery-ai sourcery-ai bot left a 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

@github-actions
Copy link
Contributor

github-actions bot commented Jan 18, 2026

Build Status

Build successful. APKs to test: https://github.com/fossasia/pslab-app/actions/runs/21117901987/artifacts/5170267588.

Screenshots

Android Screenshots
iPhone Screenshots
iPad Screenshots

@Yugesh-Kumar-S
Copy link
Contributor

Hi @manishraj-003 ,please make use of the guide_widget.dart for the meter .It would help to maintain consistent UI.

Copy link

Copilot AI left a 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 /dustsensor route.
  • Added DustSensorScreen UI (PPM/air-quality display, gauge, and chart area).
  • Introduced DustSensorStateProvider for 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.

Comment on lines +42 to +55
// 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();
});

Copy link

Copilot AI Feb 8, 2026

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).

Suggested change
// 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.

Copilot uses AI. Check for mistakes.
Comment on lines +40 to +43
if (ppm < 1.0) return "Good";
if (ppm < 2.5) return "Moderate";
if (ppm < 4.0) return "Poor";
return "Hazardous";
Copy link

Copilot AI Feb 8, 2026

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.

Suggested change
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;

Copilot uses AI. Check for mistakes.
Comment on lines +85 to +93
const PopupMenuItem(
value: 'clear',
child: Text("Clear graph"),
),
PopupMenuItem(
value: 'toggle',
enabled: connected,
child: Text(provider.isStreaming ? "Stop" : "Start"),
),
Copy link

Copilot AI Feb 8, 2026

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.

Copilot uses AI. Check for mistakes.
Comment on lines +266 to +268
Text(
"⚠️ Connect PSLab device to start measuring.",
style: Theme.of(context).textTheme.bodySmall,
Copy link

Copilot AI Feb 8, 2026

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).

Suggested change
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,
),
],

Copilot uses AI. Check for mistakes.
@@ -26,9 +26,8 @@ List<String> instrumentIcons = [
'assets/icons/gyroscope_logo.png',
'assets/icons/thermometer_logo.png',
'assets/icons/robotic_arm.png',
Copy link

Copilot AI Feb 8, 2026

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.

Suggested change
'assets/icons/robotic_arm.png',
'assets/icons/robotic_arm.png',
'assets/icons/tile_icon_gas.png', // gas/dust sensor (temporary icon)

Copilot uses AI. Check for mistakes.
Comment on lines +50 to +53
return ChangeNotifierProvider(
create: (_) => DustSensorStateProvider(),
child: Consumer<DustSensorStateProvider>(
builder: (context, provider, _) {
Copy link

Copilot AI Feb 8, 2026

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).

Copilot uses AI. Check for mistakes.
Comment on lines +60 to +61
provider.stop();
provider.clearGraph();
Copy link

Copilot AI Feb 8, 2026

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.

Suggested change
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();
}
});

Copilot uses AI. Check for mistakes.
/// 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;
Copy link

Copilot AI Feb 8, 2026

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.

Suggested change
if (_isStreaming || !_isConnected) return;
if (_isStreaming) return;

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add DUST SENSOR instrument

2 participants