Analog to IntentApiReceiver enhancement #6250
Adds an optional `device` extra containing the MAC address of the target gadget.
Missing `device` extra or a `null` value forward the intent payload to all initialised BangleJS.
The existing device preference filter (device_intents == true) remains unchanged.
PR #6195 widened the syncer guards to match Health Connect's enforced
bounds, but widened too much: it overlooked that some of Gadgetbridge's
in-band sentinel values fall inside HC's accepted range, so placeholder
readings leaked into Health Connect and showed up as spurious data points.
- HeartRate / RestingHeartRate: 255 is GB's "illegal value" marker for a
bad measurement (ActivitySample.getHeartRate), and it sits inside HC's
1..300 / 0..300 range. Exclude it explicitly while keeping HC's bounds.
- SpO2: 0 means "not measured" (sample providers return null on 0 and all
SpO2 charts filter getSpo2() > 0). Raise the lower bound to 1.
Add HeartRateSyncerTest covering the HeartRateSyncer.sync() path (sentinel
255 and 0 are not inserted; valid range passes) and extend
SyncerRangeValidationTest with the new boundaries.
The Bip 6 widget shows "open Zepp to get weather" when our v5 response
omits or empty-stubs any dataset the firmware asked for. Fixes three gaps:
- Implement createDailyAirQuality with a 7-day skeleton (placeholder
AQI 50, level "good"; real ingestion gated on WeatherSpec extension).
- Replace EmptyResponse "{}" for hourlyAirQuality and dailyTide with
populated shape stubs (metadata + empty items list).
- Add a defensive else branch for unknown dataset names that returns a
metadata+empty-items skeleton and logs the key at WARN, so future
firmware additions surface in logs without silently dropping keys.
See docs/bip6-weather-widget-research.md for the diagnosis.
Source: https://github.com/PeterXMR/Gadgetbridge
(cherry picked from commit 8b9d4f319ecc69dcb3c9ebbe77c35a06df7ebbbc)
The v5 place dataset hard-coded latitude/longitude to "0.0" and used a
constant locationKey "accu:123456", which prevented watches that were
never provisioned via the official Zepp app (Bip 6, Active 2, ...) from
accepting the response as authoritative — they treated the zero coords
as a missing-location signal and silently refused to display weather
(issue #5653).
- ZeppOsWeatherHandlerV5.createPlace: pull lat/lon from WeatherSpec
when set, else fall back to the user-configured location pref. Format
with Locale.ROOT and derive a stable per-location locationKey from a
hash of the coords so the watch can dedupe responses.
- ZeppOsWeatherService.onSendWeather: same fallback chain for the
CMD_SET_DEFAULT_LOCATION packet so the watch's default-location state
is bootstrapped with real coordinates instead of "1.234,-5.678".
Refs #5653.
source: https://github.com/PeterXMR/Gadgetbridge
(cherry picked from commit be87ac3364fbf5725f43e3dbac87ef2e103fdd89)
Three lifecycle bugs in GarminSupport's transferNotification:
* addFileToDownloadList incremented totalSize unconditionally, so a directory listing arriving on connect posted a notification with the unlocalised "Unknown transfer" fallback title before any transfer started.
* The start/finish pair gated on gbDevice.isBusy(), which other paths set and clear — start could be skipped while the matching finish ran anyway, or vice versa.
* dispose() didn't finish the notification, leaving it pinned after a mid-sync disconnect.
Gate the increments and the start/finish pair on the private isBusyFetching flag, and finish + clear the flag in dispose().
Long-running per-line fetches (e.g. ExploreSync's catalog walk) hold the device's busy task for the entire session, so the busy→idle edge WorkoutListActivity refreshes on only fires at session end. Listen for ACTION_NEW_DATA too so each newly imported activity appears as it lands.
Add a silent loadSummaries path that skips the isLoading flips, and disable the RecyclerView change animator so the dashboard row's notifyItemChanged(0) doesn't cross-fade — both would otherwise read as a distracting blink during an active sync.
Wires the new ExploreSync sub-service into ProtocolBufferHandler and GarminSupport so a paired device's historical-line catalog can be fetched independently of FIT — which Garmin Connect Mobile irreversibly archives files out of after the first sync (see issues like #5694 and #5994). Activities the FIT path can no longer see become visible again here.
Each historical line is reconstructed into an ActivityTrack, written as GPX, and persisted as a BaseActivitySummary keyed on the first-point timestamp — the same surface FIT-imported workouts use, so UI and exporters need no ExploreSync awareness. GarminCoordinator now picks the activity track provider from the summary contents (gpxTrack vs rawDetailsPath) instead of hard-coding the FIT one.
Three call sites (FileTransferHandler, FitImporter, GarminSupport) each open-coded the same [TYPE]/[YEAR]/[TYPE]_[timestamp]_[suffix] filename shape and re-created their own SimpleDateFormat. Hoist the formatters (now thread-safe DateTimeFormatter) and the path builder into GarminUtils as buildExportPath(type, instant, suffix, ext).
FitImporter's null-type fallback now uses "NULL" consistently for both directory and filename, fixing a latent NPE.
Every watch-bound Smart RPC site followed the same shape: sendOutgoingMessage(taskName, protocolBufferHandler.prepareProtobufRequest(payload)). Fold it into a single sendProtobufRequest(taskName, payload) on GarminSupport. No behaviour change.
The watch encodes lat/lon as signed 32-bit semicircles (1<<31 == 180°). Move the bidirectional conversion behind named helpers backed by a single constant, and migrate toLocationData and FieldDefinitionCoordinate onto them.
- harmonise extra `address` to `device` - the old extra `address` logs a warning but is still supported
- support optional `device` extra for action nodomain.freeyourgadget.gadgetbridge.command.ACTIVITY_SYNC
- support optional `device` extra for action nodomain.freeyourgadget.gadgetbridge.command.DEBUG_SEND_NOTIFICATION
- support optional `device` extra for action nodomain.freeyourgadget.gadgetbridge.command.DEBUG_INCOMING_CALL
- support optional `device` extra for action nodomain.freeyourgadget.gadgetbridge.command.DEBUG_END_CALL
- support ComponentName extras for action nodomain.freeyourgadget.gadgetbridge.command.DEBUG_TEST_NEW_FUNCTION
- code review
See Freeyourgadget/website#247 for the documentation update.