Skip to content

Add FlockSquawk scanner integration (USB + BLE)#37

Draft
dougborg wants to merge 11 commits intoFoggedLens:mainfrom
dougborg:pr/05-scanner
Draft

Add FlockSquawk scanner integration (USB + BLE)#37
dougborg wants to merge 11 commits intoFoggedLens:mainfrom
dougborg:pr/05-scanner

Conversation

@dougborg
Copy link
Collaborator

@dougborg dougborg commented Feb 2, 2026

Stack order: 5/5 — merge after PR4 (state context)

Companion PRs:

Summary

  • Add USB serial scanner integration for connecting to FlockSquawk hardware
    • JSON line parser for serial telemetry protocol
    • RF detection model and SQLite persistence layer
    • Scanner screen with real-time detection display
    • Map overlay markers for RF detections
    • USB serial heartbeat for active connection detection
  • Add BLE transport for wireless FlockSquawk communication
    • BleScannerService via flutter_blue_plus
    • Abstract ScannerService interface with JsonLineParser mixin
    • BLE-primary with USB auto-upgrade on Android
    • Fix BLE connection race condition
    • Transport-aware connection icons (BLE vs USB)
  • Add flock-you device support
    • JsonLineParser accepts event == "detection" (flock-you) alongside "target_detected" (FlockSquawk)
    • RfDetection/RfSighting auto-detect format via "mac_address" key (flock-you flat) vs "target" key (FlockSquawk nested)
    • Map flock-you detection_methodmatchFlags, certainty, and alertLevel
    • 11 new test cases for flock-you format parsing

Test plan

  • Run flutter test and verify all 56 scanner tests pass (45 existing + 11 new flock-you)
  • Connect FlockSquawk via USB and verify detection events stream to app
  • Connect FlockSquawk via BLE and verify detection events stream to app
  • Connect flock-you via BLE and verify detection events stream to app
  • Connect flock-you via USB serial and verify detection events stream to app
  • Verify USB auto-upgrade triggers when cable connected on Android
  • Verify connection icons reflect active transport type
  • Verify RF detection markers appear on map

🤖 Generated with Claude Code

Copy link
Contributor

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

@dougborg
Copy link
Collaborator Author

dougborg commented Feb 8, 2026

Added: flock-you JSON format support

This branch now supports parsing detections from flock-you devices in addition to FlockSquawk.

Changes in this update (commit e862218):

  • JsonLineParser: accept event == "detection" (flock-you) alongside "target_detected" (FlockSquawk)
  • RfDetection.fromSerialJson: auto-detect format by checking for "mac_address" key (flock-you flat) vs "target" key (FlockSquawk nested). Maps detection_methodmatchFlags, certainty, and alertLevel
  • RfSighting.fromSerialJson: handle flock-you flat format (RSSI at top level, no channel)
  • Test fixtures: makeFlockyouDetectionJson() builder
  • 11 new test cases — all 56 tests pass

Firmware side: colonelpanichacks/flock-you#30

@dougborg dougborg force-pushed the pr/05-scanner branch 2 times, most recently from f16b3a9 to 0d208ba Compare February 9, 2026 01:39
@stopflock
Copy link
Collaborator

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

@dougborg dougborg force-pushed the pr/05-scanner branch 4 times, most recently from 29adc7b to 83a65b3 Compare February 9, 2026 23:13
@dougborg
Copy link
Collaborator Author

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:

  • Geolocation / triangulation of signals → handled by Deflock
  • Associating detections with existing nodes → handled by Deflock
  • Proposing new nodes for unreported devices → handled by Deflock
  • Alerts and notifications → centralized in Deflock (rather than per-device alert strategies)

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.

@dougborg
Copy link
Collaborator Author

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.

dougborg and others added 7 commits February 25, 2026 09:45
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>
dougborg and others added 4 commits February 25, 2026 09:45
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>
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.

3 participants