Skip to content

feat(location): live GPS location tracking - #246

Open
deaflynx wants to merge 43 commits into
thingsboard:develop/1.9.0from
deaflynx:feat/gps-tracker
Open

feat(location): live GPS location tracking#246
deaflynx wants to merge 43 commits into
thingsboard:develop/1.9.0from
deaflynx:feat/gps-tracker

Conversation

@deaflynx

@deaflynx deaflynx commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds live GPS location tracking to the mobile app. A dashboard widget can start and stop a tracking session through two new mobile actions (startLiveLocation / stopLiveLocation); while a session is active the app streams GPS fixes — including in the background — and saves them to the target entity as time series using the key mapping from the widget's action configuration. A new Live tracking page and an app-wide tracking bar show the session state.

Changes

Location service (lib/utils/services/location/)

  • New reusable LocationService (ILocationService) wrapping geolocator: one-shot getCurrentPosition() and a background-capable position stream with configurable LocationStreamSettings.
  • GeoPosition now exposes accuracy, timestamp, altitude, speed and heading; the getLocation action result includes accuracy and timestamp.
  • GetLocationAction refactored to delegate to LocationService, with shared result mapping via LocationActionResultMapper.

Live tracking session (lib/utils/services/live_location_tracking/)

  • LiveLocationTrackingService manages the session lifecycle: parses LiveTrackingConfig from the widget action, subscribes to the position stream, and saves telemetry through ILiveTrackingRemote as background requests using the configured key mapping.
  • Localized session errors, a visible "paused" state while location is unavailable, and start failures surfaced as a toast.
  • Target entity display name resolved off the start critical path (EntityNameResolver), with a re-check of session identity after the async store read.
  • Last session persisted through ILiveTrackingStore (LastTrackingRecord with end reason and resolved name); tracking is stopped and the record cleared on logout.

UI (lib/modules/location_tracking/)

  • Live tracking page (route /liveTracking), shown in navigation/More when the mobile bundle layout includes the live_location_tracking page: the idle state shows the last session summary with a Start button; the active state shows live session details with a linkable target entity and source dashboard.
  • Full-width pulsing collapsed tracking bar visible across the app while a session is running.

Mobile actions

  • New WidgetMobileActionType values: startLiveLocation, stopLiveLocation.

Platform configuration

  • Android: ACCESS_FINE_LOCATION is no longer capped by maxSdkVersion (live tracking needs fine location on all API levels, including 36+); added FOREGROUND_SERVICE and FOREGROUND_SERVICE_LOCATION for background tracking.
  • iOS: added location to UIBackgroundModes.

🤖 Generated with Claude Code

@deaflynx deaflynx added the enhancement New feature or request label Jun 10, 2026
@deaflynx deaflynx added this to the 1.9.0 milestone Jun 10, 2026
deaflynx added 25 commits July 3, 2026 13:14
…pike page

- positionStream now accepts LocationStreamSettings (accuracy tier,
  distance filter, interval, optional background mode)
- background mode uses geolocator foreground service notification on
  Android and background location updates on iOS
- add FOREGROUND_SERVICE / FOREGROUND_SERVICE_LOCATION permissions and
  the iOS 'location' background mode
- add debug-only live tracking spike page (More menu) that streams
  fixes and saves latitude/longitude/gpsAccuracy telemetry to the
  current user entity
…cking

The ESP32 provisioning branch capped ACCESS_FINE_LOCATION at
maxSdkVersion=35, which strips the permission on API 36+ (Android 16).
Live GPS tracking then fell back to approximate location and delivered
only a single fix. Use tools:remove to drop any dependency-imposed
maxSdkVersion so fine location is granted on all API levels.
start() was awaiting the network entity-name lookup before persisting
the recoverable "interrupted" record and subscribing to GPS. A force
kill (or slow/offline network) in that window left nothing persisted
to recover from, and a concurrent stop()/logout could resume start()
afterwards and resurrect a subscription and record for a session that
had already ended.

Reorder start() so the record write, status attributes, GPS
subscription and max-duration timer all happen synchronously before
any network call, then resolve the name in the background and patch
it into the persisted record. The patch is guarded by the session's
startedAt so a stop, logout, or newer start() supersedes it and the
late name resolution becomes a no-op.

Also:
- remove the dead liveTrackingNoSession l10n key (its only consumer,
  the old session page, is gone) and regenerate l10n
- tighten two loose test matchers in the location tracking widget
  tests (findsOneWidget instead of findsWidgets; assert the exact
  primaryContainer color instead of just isNotNull)
deaflynx added 14 commits July 22, 2026 21:34
…ping

The dashboard now sends the keys to save rather than a fixed pair plus
flags: each entry names a location value, the entity key it is written
under and whether it lands in server attributes or time series. Replaces
latitudeKey, longitudeKey, includeMetadata, mirrorToAttributes and
writeStatusAttributes, drops the redundant gpsLastUpdateTime attribute,
and switches the maximum duration to seconds to match the wire config.
…oard

- carry the web-resolved target name and source dashboard in the tracking
  wire config and persist them with the last-session record
- return trackingInfo (target name, saved keys) from the stop action ack
  so the dashboard dialog can show what was tracked
- tracking page: target entity links to its details (device profile
  dashboard, asset or customer details), source dashboard opens on tap
- tracking page: "Save location to entity" label, play/pause status icon
  with green/amber color, start/end times without milliseconds
The teardown added in d80b3ba lived only in the legacy TbContext.logout()
which no user-facing path calls, so logging out left GPS streaming, kept
posting telemetry with a dead token, and resumed the session on the next
login. LoginProvider.logout() now stops tracking and clears the record
before tbClient.logout(), while the token needed for the gpsActive=false
write is still valid. Covers every logout entry point.
Periodic save failures raised the global modal "Connection error" dialog
over whatever the user was doing (once per failed save) and toggled the
global loading state. Telemetry/attribute saves and the target-name query
now carry ignoreErrors+ignoreLoading, so the session screen's error
counter and last-error line remain the only surface.
…is lost

- Store lastError as a LiveTrackingError cause instead of a raw exception
  string; the session screen mapped a Dio/ThingsboardError toString() with
  ten stack frames onto the UI. Causes are classified by status/error code
  (the 404 arrives with an empty message) and localized in the UI layer;
  full exception + trace keep going to the logger only.
- Clear lastError on a successful fix so a resolved error does not stay on
  screen forever.
- Map geolocator stream errors to the sealed LocationFix cases: turning
  device location off mid-session previously landed as a generic fix error
  and the session kept looking healthy. It now pauses visibly and writes
  gpsActive=false.
- Show a system notification while a session is paused, cleared on
  resume/stop and at app start (a leftover notification must not outlive
  the process the session died with).
@deaflynx deaflynx changed the title Feat/gps tracker feat(location): live GPS location tracking Aug 5, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant