Add FlockSquawk scanner integration (USB + BLE)#37
Add FlockSquawk scanner integration (USB + BLE)#37dougborg wants to merge 11 commits intoFoggedLens:mainfrom
Conversation
There was a problem hiding this comment.
Pull request overview
Adds end-to-end FlockSquawk RF scanner support to the app, including USB serial + BLE transports, persistence of detections/sightings, and UI surfaces for status, browsing, and map markers.
Changes:
- Introduces scanner transport abstraction (
ScannerService) with BLE + USB implementations and shared JSON line parsing. - Adds RF detection persistence (SQLite) plus new UI (scanner screen, status indicator, detection sheets/markers).
- Expands test coverage for scanner state, parsers, and services; updates platform permissions/config for BLE/USB.
Reviewed changes
Copilot reviewed 103 out of 104 changed files in this pull request and generated 9 comments.
Show a summary per file
| File | Description |
|---|---|
| test/widget_test.dart | Removes default Flutter counter smoke test. |
| test/state/scanner_state_test.dart | Adds unit tests for ScannerState detection handling, list management, status, and DB passthrough. |
| test/services/usb_scanner_service_test.dart | Adds tests for USB serial buffering/JSON parsing/heartbeat lifecycle. |
| test/services/json_line_parser_test.dart | Adds tests for shared newline-delimited JSON parsing mixin. |
| test/services/deflock_tile_provider_test.dart | Updates imports and AppState mocking to align with new structure. |
| test/services/ble_scanner_service_test.dart | Adds tests for BLE UUIDs and parser behavior under notification fragmentation. |
| test/models/tile_provider_test.dart | Fixes package import and adjusts usability expectations for API-keyed providers. |
| test/models/rf_detection_test.dart | Adds extensive tests for RF detection/sighting parsing and DB row round-trips. |
| test/fixtures/serial_json_fixtures.dart | Adds canonical JSON fixture builder for scanner telemetry tests. |
| scripts/validate_localizations.dart | Switches output to debugPrint and adds Flutter import. |
| pubspec.yaml | Adds scanner dependencies (USB serial + BLE), DB/path helpers, and testing/lint tooling. |
| pubspec.lock | Locks newly added dependencies and updates SDK constraints. |
| lib/widgets/welcome_dialog.dart | Replaces withOpacity with withValues(alpha: ...). |
| lib/widgets/suspected_location_sheet.dart | Removes unused provider/AppState dependency; withValues updates. |
| lib/widgets/submission_guide_dialog.dart | Replaces withOpacity with withValues(alpha: ...). |
| lib/widgets/search_bar.dart | Replaces withOpacity with withValues(alpha: ...) and minor list building change. |
| lib/widgets/scanner_status_indicator.dart | Adds AppBar status icon for scanner connection/transport. |
| lib/widgets/rf_detection_sheet.dart | Adds bottom sheet UI for RF detection details + submission shortcut. |
| lib/widgets/rf_detection_markers.dart | Adds map marker widgets/builders for RF detections. |
| lib/widgets/refine_tags_sheet.dart | Refactors operator profile selection to use RadioGroup. |
| lib/widgets/reauth_messages_dialog.dart | Replaces withOpacity with withValues(alpha: ...). |
| lib/widgets/proximity_warning_dialog.dart | Removes unused import. |
| lib/widgets/proximity_alert_banner.dart | Replaces withOpacity with withValues(alpha: ...). |
| lib/widgets/provisional_pin.dart | Replaces withOpacity with withValues(alpha: ...). |
| lib/widgets/positioning_tutorial_overlay.dart | Replaces withOpacity with withValues(alpha: ...). |
| lib/widgets/nuclear_reset_dialog.dart | Updates to PopScope + context.mounted safety. |
| lib/widgets/node_tag_sheet.dart | Renames local helpers and updates text color opacity usage. |
| lib/widgets/node_provider_with_cache.dart | Removes unused imports. |
| lib/widgets/navigation_sheet.dart | Removes unused import; replaces withOpacity with withValues(alpha: ...). |
| lib/widgets/map_view.dart | Adds RF detection overlays wiring into marker layer builder. |
| lib/widgets/map/tile_layer_manager.dart | Minor maxZoom expression tweak and import cleanup. |
| lib/widgets/map/suspected_location_markers.dart | Constructor cleanup (super.key). |
| lib/widgets/map/overlay_layer_builder.dart | Import cleanup and withValues update. |
| lib/widgets/map/node_refresh_controller.dart | Import cleanup. |
| lib/widgets/map/node_markers.dart | Constructor cleanup (super.key). |
| lib/widgets/map/marker_layer_builder.dart | Adds RF detection markers into map marker composition. |
| lib/widgets/map/map_overlays.dart | Import cleanup and withValues updates. |
| lib/widgets/map/map_data_manager.dart | Removes unused import. |
| lib/widgets/map/layer_selector_button.dart | Updates to newer surface color scheme token. |
| lib/widgets/map/direction_cones.dart | Removes unused helpers/imports; withValues updates. |
| lib/widgets/edit_node_sheet.dart | Refactors commit/proximity flow and adds mounted checks. |
| lib/widgets/download_area_dialog.dart | Extracts download start flow into _startDownload and updates styling strings. |
| lib/widgets/compass_indicator.dart | Import cleanup and withValues updates. |
| lib/widgets/changelog_dialog.dart | Removes unused import. |
| lib/widgets/camera_icon.dart | Replaces withOpacity with withValues(alpha: ...). |
| lib/widgets/advanced_edit_options_sheet.dart | Adds context.mounted guard before redirect. |
| lib/widgets/add_node_sheet.dart | Refactors commit/proximity flow; removes unused tutorial helpers; adds mounted checks. |
| lib/state/upload_queue_state.dart | Switches logging to debugPrint and minor refactors. |
| lib/state/search_state.dart | Removes unused import. |
| lib/state/scanner_state.dart | Adds new ScannerState (transport selection, event processing, DB persistence hooks). |
| lib/state/auth_state.dart | Switches prints to debugPrint. |
| lib/services/usb_scanner_service.dart | Implements USB serial scanner transport with heartbeat + JSON parsing. |
| lib/services/uploader.dart | Refactors error message string interpolation and removes unused _post. |
| lib/services/tile_preview_service.dart | Removes unused import. |
| lib/services/suspected_location_service.dart | Simplifies exception handling. |
| lib/services/suspected_location_database.dart | Removes unused counters. |
| lib/services/suspected_location_cache.dart | Removes unused import. |
| lib/services/scanner_service.dart | Adds scanner transport interface and enums. |
| lib/services/routing_service.dart | Renames variables for style consistency and minor cleanup. |
| lib/services/rf_detection_database.dart | Adds SQLite persistence for RF devices + sightings and query helpers. |
| lib/services/offline_area_service.dart | Import cleanup. |
| lib/services/node_data_manager.dart | Import cleanup. |
| lib/services/node_cache.dart | Adds debugPrint and import needed for it. |
| lib/services/network_status.dart | Removes unused import. |
| lib/services/map_data_submodules/tiles_from_local.dart | Import cleanup. |
| lib/services/map_data_submodules/nodes_from_osm_api.dart | Uses rethrow instead of throw e and removes unused import. |
| lib/services/map_data_provider.dart | Removes unused import. |
| lib/services/json_line_parser.dart | Adds shared newline-delimited JSON parser mixin. |
| lib/services/deflock_tile_provider.dart | Import cleanup and uses rethrow. |
| lib/services/deep_link_service.dart | Import cleanup and docstring tweak. |
| lib/services/changelog_service.dart | Import cleanup and formatting changes. |
| lib/services/ble_scanner_service.dart | Implements BLE scanner transport with reconnection/backoff and JSON parsing. |
| lib/services/auth_service.dart | Logging tweaks, removes unused import, and null-safety cleanup. |
| lib/screens/upload_queue_screen.dart | Import cleanup and withValues updates. |
| lib/screens/tile_provider_editor_screen.dart | Removes unused import. |
| lib/screens/settings_screen.dart | withValues update. |
| lib/screens/settings/sections/upload_mode_section.dart | withValues update and removes unused default case. |
| lib/screens/settings/sections/tile_provider_section.dart | Minor list building change and surface color token update. |
| lib/screens/settings/sections/queue_section.dart | Import cleanup and string interpolation refactor. |
| lib/screens/settings/sections/proximity_alerts_section.dart | withValues updates. |
| lib/screens/settings/sections/offline_areas_section.dart | String interpolation refactor and minor list building change. |
| lib/screens/settings/sections/node_profiles_section.dart | Adds context.mounted guard before navigation. |
| lib/screens/settings/sections/language_section.dart | Refactors to RadioGroup and adds explicit Future<void> return types. |
| lib/screens/scanner_screen.dart | Adds new Scanner management screen (connection/status/stats/list). |
| lib/screens/release_notes_screen.dart | withValues updates. |
| lib/screens/profile_editor.dart | Removes unused controller. |
| lib/screens/osm_account_screen.dart | Updates surface token + withValues for alpha usage. |
| lib/screens/operator_profile_editor.dart | Removes unused controller. |
| lib/screens/navigation_settings_screen.dart | withValues update. |
| lib/screens/home_screen.dart | Wires scanner status indicator + RF detection map overlay + detection sheet; adds scanner listener. |
| lib/screens/coordinators/sheet_coordinator.dart | Import cleanup and minor debug string interpolation fix. |
| lib/screens/advanced_settings_screen.dart | Import cleanup. |
| lib/models/suspected_location.dart | Switches prints to debugPrint and adds import. |
| lib/models/rf_detection.dart | Adds RF detection and sighting domain models with (de)serialization helpers. |
| lib/models/operator_profile.dart | Removes unused import. |
| lib/models/node_profile.dart | Removes unused import. |
| lib/models/direction_fov.dart | Minor string interpolation cleanup. |
| lib/migrations.dart | Adds context.mounted guard before showing nuclear reset dialog. |
| lib/main.dart | Adds /scanner route. |
| lib/app_state.dart | Adds scanner module wiring + public scanner APIs/exports. |
| ios/Runner/Info.plist | Adds Bluetooth usage description and background mode. |
| android/app/src/main/AndroidManifest.xml | Adds BLE permissions for scanning/connecting. |
| android/app/build.gradle.kts | Changes minSdk assignment to flutter.minSdkVersion. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Added: flock-you JSON format supportThis branch now supports parsing detections from flock-you devices in addition to FlockSquawk. Changes in this update (commit e862218):
Firmware side: colonelpanichacks/flock-you#30 |
f16b3a9 to
0d208ba
Compare
|
Can this be bidirectional? So if an unreported flock is detected, we automatically pop up the add node sheet, but also the app can tell the connected device when a reported flock is nearby so it can trigger a similar audio/visual alert |
29adc7b to
83a65b3
Compare
|
Great question! The design intent is for scanner dongles to operate as simple event emitters — they send raw detection events to Deflock, and all the intelligence lives in the app. Specifically:
This keeps the dongle firmware simple and puts all the smarts in one place. The USB transport does have write capability (heartbeat keep-alive), but a full bidirectional command protocol is out of scope for this PR. The "pop up add node sheet for unreported flock" behavior would be a great follow-up once the detection → node matching logic is in place. At that point we could also evaluate whether sending alerts back to the dongle (audible/visual) adds value vs. just using the phone for all alerting. |
|
But we are sending commands back, so alerting back through the dongle is definitely possible, and the more I think about it the more I like it. |
Private State methods were accepting BuildContext as a parameter, shadowing this.context and forcing the less-idiomatic context.mounted guard. Per dart.dev linter guidance, State.mounted is the correct guard when using the State's own context property. - add_node_sheet/edit_node_sheet: Drop context/appState/locService params from _checkProximityAndCommit, _checkSubmissionGuideAndProceed, _checkProximityOnly, _commitWithoutCheck - download_area_dialog: Extract inline async lambda to _startDownload() State method with mounted guards Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Move context.read<AppState>() before the await on getOfflineAreaDir() to avoid reading stale state if the dialog is disposed during the I/O operation. The mounted checks were already added in an earlier commit; this fixes the remaining state-capture ordering issue. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
57 tests covering the ChangeNotifier state layer that widgets depend on: - Session lifecycle: start/clear add vs edit, operator profile detection, direction initialization from nodes with and without directions - Dirty checking: updateSession only notifies on actual changes, profile change regenerates changeset comment, defensive copy of refinedTags - Edit session recalculation: profile change recalculates additionalExistingTags/refinedTags/changesetComment, extractFromWay snap-back, explicit tags override auto-calculation - Direction management: add/remove/cycle with correct min enforcement (min=1 for add, min=0 for edit when original had no directions) - Commit guards: returns null unless target+profile set (add) or profile set (edit), double commit returns null safely - Cancel: clears session and detected operator profile - Changeset comment generation for all operation types Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Small constructor change: MapDataProvider and NodeProviderWithCache are now injectable via optional constructor parameters with defaults to the existing singletons. Production code unchanged. 27 tests covering: - addFromSession: creates PendingUpload with correct operation, adds temp node with negative ID and _pending_upload tag to cache - addFromEditSession: modify marks original with _pending_edit + creates temp node; extract creates only temp node; constrained modify uses original coordinates - addFromNodeDeletion: marks node with _pending_deletion - clearQueue/removeFromQueue: correct cache cleanup dispatch (create removes temp, edit removes temp + pending_edit marker, delete removes pending_deletion marker, extract removes temp only) - Direction formatting: single as double, multiple as semicolon-separated, FOV range notation, 360 FOV, wrapping ranges - Queue persistence: save/load round-trip via SharedPreferences Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
13 tests verifying the coordination between SessionState and UploadQueueState that AppState.commitSession() performs: - Full add flow: startAddSession -> set target + profile -> commitSession -> addFromSession -> queue has 1 item, session null - Full edit flow: both modify and extract paths - Commit guards: incomplete session doesn't add to queue, double commit is safe (second returns null) - Profile deletion callback: deleting profile used in active add/edit session cancels that session; unrelated profile deletion doesn't affect session - Notification propagation: sub-module notifyListeners fires on all state-changing operations Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Enable DeFlock to receive detections from flock-you devices in addition to FlockSquawk. Format is auto-detected by checking for the "mac_address" key (flock-you flat format) vs "target" key (FlockSquawk nested format). - JsonLineParser: accept event == "detection" alongside "target_detected" - RfDetection.fromSerialJson: dispatch to format-specific parser based on top-level key presence; map flock-you detection_method to matchFlags, certainty, and alertLevel - RfSighting.fromSerialJson: handle flock-you flat format (no channel) - Add makeFlockyouDetectionJson() test fixture builder - Add 11 test cases covering all flock-you detection methods, MAC normalization, protocol mapping, and edge cases Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Verify that flock-you format events with event:"detection" pass through the parser alongside the existing target_detected coverage. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
83a65b3 to
976aa65
Compare
Summary
event == "detection"(flock-you) alongside"target_detected"(FlockSquawk)"mac_address"key (flock-you flat) vs"target"key (FlockSquawk nested)detection_method→matchFlags,certainty, andalertLevelTest plan
flutter testand verify all 56 scanner tests pass (45 existing + 11 new flock-you)🤖 Generated with Claude Code