fitbit_air 0.1.4
fitbit_air: ^0.1.4 copied to clipboard
Experimental Android Bluetooth access to Fitbit Air, including haptics, live heart rate, device information, and diagnostics.
0.1.4 #
Package #
- Add
FitbitAir.readBattery(): the battery percentage Google Health shows (verified equal), voltage in millivolts, and whether the Air is on its charger (verified on and off the charger). - Add
FitbitAir.isAirConnected()andwatchConnection()from Android's system connection and Bluetooth on/off events (no connection of their own), andreadSignalStrength()for a rough proximity hint in dBm. - Add
FitbitAirDiagnostics.watchChannelActivity(): passively reports whether Google Health is idle, polling or syncing on the Air's shared channel. - Sync-aware hold: before each secure command, passively observe the shared
tracker channel and wait (up to 10 s) while Google Health is transferring
data. Polling never blocks a command. Reported as
channelHold. In 8 vibrations sent during Google Health syncs, all 8 syncs completed. - Request the connection-priority boost only after the session exists; a fast link during the handshake let Google Health win the Air's handshake race.
- Retry a lost handshake race (
DTLSerror-1) after 1.5 s instead of waiting for the 6 s stall deadline. - Add optional explicit tap intensity to
AirHapticCommand; other command kinds reject it. Existing commands and defaults remain compatible. - Research: taps, including a recognized alarm dismissal, send nothing to the phone; real-time tap detection is not possible with the tested firmware.
Example app (Air Relay) #
- Home/Rules/Device navigation with accessible Lab tools; per-app and WhatsApp-chat vibration presets, quiet hours and a timed pause.
- A four-step guided setup that opens full screen on first launch: permission explanations, app selection and optional vibration confirmation, with saved progress. Reading notifications and showing relay controls are separate, explicit grants; starting the relay no longer prompts unexpectedly.
- Home shows the Air's battery (timestamped, refreshed hourly while visible and on resume, never by stopping the relay) and its passive connection state. A Device overview adds signal strength and model/firmware.
- Battery reads and notification commands share the relay's worker, so they never overlap; Start queues behind a finishing test or battery check.
- Actionable relay readiness that separates past delivery failures from current permission, Bluetooth, pause and startup states.
- WhatsApp people get a dedicated screen with chat search and vibration editing.
- Merged alerts use a deterministic preset while keeping the transport cooldown; alerts during quiet hours are suppressed and stale ones expire.
- Calmer UI: stable Start/Stop headings and button geometry during transitions, stable app rows and icons while toggling rules, subtle haptic feedback on Start/Stop, flat settings rows, reduced-motion support.
- Lab shows timestamped wrist observations from manual trials only.
0.1.3 #
-
Add
FitbitAir.tap(): exactly one pulse at the strength set in Google Health, which is kept. It sends the other strength and the original in one settings message; the Air supersedes the first pulse.tap(intensity: …)pulses at an explicit strength (a different strength is kept and reported aschanged).AirHapticKind.tapdispatches it. Verified in 14 felt trials on one Air. -
Relay demo: One pulse (
tap(), now the default) or Two pulses (vibrate()). Test vibration follows the selected style whether or not the relay is running. The relay screen is simplified: Start is the single primary action; a "Vibration" section holds the style selector and a compact Try it button that shows progress and "Sent" in place, without the layout-shifting "In progress" banner. Failures appear in a floating bar with Retry. The "I felt vibration" marker moved to the Lab only. Device tab adds Single tap (keeps strength). -
Research: the Air pulses only when its strength becomes different from the last non-off strength; native notification and heart-rate sharing resources return
4.01on the accessible session. -
Speed up commands by about 40%: request high connection priority for the command's session only and return the link to low power afterwards. The idle link (~150 ms interval with peripheral latency) made every round trip wait several radio events. Measured end to end: 8.7 s before, 4.0–4.8 s after.
-
Retry a stalled handshake once. After the Air resets its link, Google Health and the SDK can both answer its handshake and neither wins; previously the command then hung until the 45 s timeout while Google Health was also stuck. A handshake missing after 6 s now gets one fresh attempt (no device command has been sent at that point), then fails with
HANDSHAKE_TIMEOUT. -
Relay demo: cooldown reduced from 20 s to 15 s, based on measured Google Health sync times after a handoff.
-
Relay demo: merge notification bursts. The first alert still vibrates immediately; alerts during a command or its 20 s cooldown join one follow-up vibration sent when the cooldown ends. A burst costs at most two tracker sessions instead of one per message, leaving Google Health quiet windows. History shows merged alerts as
merged. -
Fix Google Health sync stalling for about a minute after every DTLS command. The Air has one Gattlink/DTLS channel shared by all clients on the link; our session left Google Health's channel out of sync until its ~60 s watchdog reconnected. Commands now end with a handoff: a short session that refuses the Air's key, which makes the Air reset the link for every client, after which the SDK goes silent so Google Health re-handshakes alone. Results are returned after the handoff (typically 1–3 s). Verified in five vibrations on a Pixel 7 Pro, including the relay worker and Google Health in foreground.
-
Serialize command Bluetooth callbacks with teardown, discard stale native callbacks/completions, and track partially initialized resources for cleanup. These fix lifecycle races; Health sync coexistence still needs phone validation.
-
Expose bounded, process-wide command cleanup diagnostics in demo reports, including teardown failures and app-local transport ownership. This aids Health sync investigation; it does not establish or fix the underlying cause.
-
Fix empty WhatsApp capture by inspecting unread notifications immediately, retaining results after listening stops, and exposing safe diagnostic counts.
-
Add explicitly confirmed exact-chat-name rules when message notifications lack a conversation ID; show duplicate-name/rename limitations before saving.
-
Simplify relay setup with a visible Choose people card, collapsed completed device setup, clearer capture states and a nearby sanitized debug report.
-
Add example-only WhatsApp/WhatsApp Business conversation allowlists with explicit 60-second local capture, app-scoped conversation matching, fail-closed handling of missing IDs and controls locked while relaying.
-
Keep conversation labels/IDs out of diagnostic reports and Android backups; direct-chat selection supports people, while selected groups allow all senders.
0.1.2 #
-
Organize the example into Relay, Device and Lab screens with a shared named action controller, explicit transport ownership, and safe relay-stop handoff.
-
Add app search/icons, setup checks, a relay-aware vibration test, and bounded persistent metadata history with terminal outcomes, latency and recovery hints.
-
Add saved research durations, structured bounded trial results, sanitized JSON export, manual physical-observation markers, stable control keys and opt-in physical-device integration scenarios. Existing research tools remain available.
-
Generalize the example relay to a saved app allowlist with an installed-app picker, including WhatsApp. Accept newer MessagingStyle timestamps on reused chat notification keys while suppressing duplicate updates and old history.
-
Add an example-only Google Calendar notification relay with notification-access setup, foreground Start/Stop controls, duplicate filtering and bounded haptic dispatch. WhatsApp relay smoke success was user-reported on 2026-10-16; long-term reliability and the revised UI still need physical validation.
-
Allow bounded 30–90 second secure-event research trials; use 90 seconds for alarm dismissal so delayed events can be observed after the gesture.
-
Acknowledge valid POSTs on both event routes with CoAP
2.04, matching Google Health's event handler; reject malformed event payloads with4.00. -
Add a manual tap-attempt marker independent of LED confirmation in the demo.
-
Add an alarm-dismissal observation trial with separate manual onset, attempt, and stopped-vibration markers; the demo does not create alarms.
-
Observe additional incoming routes, decode bounded
eventsSecureevent IDs, and report partial observation duration on interruption.WRS_ONreceipt verified in a bootstrap session, including both presence states during a user-confirmed removal/replacement trial. -
Add a dedicated wrist-presence trial with manual action markers in the demo.
-
Add experimental, bounded
watchWristPresence()with typed device-reported states and phone receipt times. -
Guide physical tap research with timed quiet/tap intervals and explicitly manual LED-observation markers in the demo; no tap-detection API is claimed.
-
Fix the incoming CoAP request-filter JNI signatures to match the installed transport; add ABI regression tests for active native constructors.
-
Add bounded, opt-in secure protocol-event observation. Startup/sync and wrist-presence events verified on hardware; wrist-tap detection remains open.
-
Add a fixed read-only resource-access probe without exposing response bodies. Device-info access succeeds; alarms, live activity and sync status are denied in the tested bootstrap session.
0.1.1 #
- Add bounded haptic command dispatch with expiry, duplicate suppression, rate limiting, and no automatic retry of ambiguous device failures.
- Guard transport ownership across Flutter engines in the same process.
- Add bounded CPU wake-lock handling to the example headless service.
- Verify one controlled screen-off haptic with physical user confirmation and simulated duplicate suppression; deep-idle/restart reliability remains open.
- Add research-only battery-resource access and selected raw-field decoding; bootstrap GET access verified, units/percentage scale not yet claimed.
- Update installation instructions for the published 0.1.0 package and describe the broader Bluetooth API beyond haptics.
- Add user-started, headless read/haptic tests to the Android example and document background integration limitations. Headless reads and release-mode haptics verified on Pixel 7 Pro; user physically confirmed the pulses.
- Include the full Apache-2.0 license for the adapted JNI bindings.
- Add Dart dispatcher regression tests and native lease/protobuf tests to CI.
0.1.0 #
- Add opt-in, local-only diagnostic BLE event monitoring (not tap detection).
- Preserve JNI binding names in minified Android builds and allow bounded haptic recovery requests to finish before reporting an operation failure.
- Add read-only device information, BLE service inventory, and link diagnostics.
- Add standard heart-rate discovery, verified readings, continuous streaming, and read cancellation. Google Health heart-rate sharing must be active.
- Add direct Android BLE discovery for an already-paired Fitbit Air.
- Add bounded haptic command with best-effort restoration and verification.
- Add typed read/set intensity controls and a verified single-pulse toggle.
- Load the compatible native transport from a certificate-pinned, installed Google Health APK without redistributing its binary or reading private data.
- Include adapted Apache-2.0 Golden Gate JNI-facing Kotlin bindings.
- Add a demo app covering every supported operation and a typed Dart API.