Skip to content

garmin sports watch pairing - #361

Merged
abdulsaheel merged 3 commits into
mainfrom
feat/garmin-gfdi-adapter
Sep 5, 2026
Merged

garmin sports watch pairing#361
abdulsaheel merged 3 commits into
mainfrom
feat/garmin-gfdi-adapter

Conversation

@abdulsaheel

@abdulsaheel abdulsaheel commented Sep 5, 2026

Copy link
Copy Markdown
Collaborator

pairs a garmin watch the same way oura/dafit are wired: opens the multi-link
gfdi channel, acks what needs acking, answers the current-time request, reads
the unprompted device-info push and asks for battery once. bounded session,
no history drain, no realtime streams.

signals is empty — this doesn't decode a physiological signal, just device
identity and battery. depends on protocol#52 (pinned by commit until merged).

Summary by Sourcery

Add experimental Garmin watch pairing that performs a bounded GFDI session for device identity and battery data without importing health signals or draining history.

New Features:

  • Add experimental Garmin sports watch pairing with model, firmware, and battery retrieval through the watch’s Multi-Link/GFDI connection.

Enhancements:

  • Integrate Garmin synchronization into device management, manual sync, background sync, and unpairing flows.
  • Keep Garmin sessions bounded and archive received frames without deriving physiological signals.

Build:

  • Update the protocol dependency pin to include Garmin GFDI support.

Documentation:

  • Add user-facing Garmin pairing guidance requiring the watch’s Phone pairing screen.

Tests:

  • Add registry and adapter coverage for Garmin handshake, refusal, acknowledgements, device information, battery responses, bounded behavior, and signal absence.

opens the multi-link gfdi channel, answers the plumbing it needs answered
(acks, current-time), reads the unprompted device-info push, asks for
battery once. no history drain, no realtime streams — device sits behind
its own picker row, syncs on demand and on the background piggyback, same
as the ring's.
@sourcery-ai

sourcery-ai Bot commented Sep 5, 2026

Copy link
Copy Markdown

Reviewer's Guide

Introduces an explicitly experimental Garmin sports-watch integration backed by the pinned GFDI protocol implementation, with a bounded Multi-Link session that reports device identity and battery, archives raw traffic, and is wired through pairing, manual/background sync, teardown, and tests without exposing physiological signals.

Sequence diagram for the bounded Garmin GFDI sync session

sequenceDiagram
    participant GarminWatch
    participant GarminLink
    participant GarminAdapter
    participant GFDI

    GarminLink->>GarminWatch: connect()
    GarminLink->>GarminAdapter: run(link)
    GarminAdapter->>GFDI: garminCloseAllRequest()
    GFDI-->>GarminAdapter: CLOSE_ALL_RESP
    GarminAdapter->>GFDI: garminRegisterMlRequest(kGarminServiceGfdi)
    GFDI-->>GarminAdapter: REGISTER_ML_RESP
    GFDI-->>GarminAdapter: DEVICE_INFORMATION
    GarminAdapter->>GFDI: garminBuildStatusAck()
    GarminAdapter->>GFDI: garminBuildProtobufRequest()
    GFDI-->>GarminAdapter: PROTOBUF_RESPONSE
    GarminAdapter->>GFDI: garminBuildStatusAck()
    GarminAdapter-->>GarminLink: BandNote(model, firmware, battery)
    GarminLink->>GarminWatch: disconnect()
Loading

File-Level Changes

Change Details Files
Adds an experimental Garmin watch adapter using the Multi-Link/GFDI protocol for bounded identity and battery sessions.
  • Registers Garmin’s service and shared write/notify characteristics.
  • Performs CLOSE_ALL and GFDI registration with handshake timeouts and clean refusal handling.
  • Reassembles COBS payloads, acknowledges inbound messages, answers current-time requests, parses device information and one battery response, and archives raw frames.
  • Declares no physiological signals and avoids history draining or realtime streaming.
lib/ble/adapters/_registry.dart
lib/ble/adapters/garmin.dart
pubspec.yaml
pubspec.lock
Integrates Garmin pairing and synchronization into secondary-device lifecycle management.
  • Adds a serialized connect/discover/run/disconnect flow with bounded session teardown and secondary-link slot usage.
  • Stores model, firmware, and battery notes in memory while preserving raw archive records without battery-table pollution.
  • Supports Garmin-specific forget and manual sync actions, including picker guidance for the watch’s native Pair Phone flow.
  • Piggybacks Garmin synchronization into headless background sync without allowing failures to escape.
lib/ble/garmin_link.dart
lib/ble/hrs_link.dart
lib/sync/background_sync.dart
lib/ui2/profile/devices.dart
Adds registry and protocol-behavior coverage for the Garmin integration.
  • Verifies registry identity, characteristic configuration, and empty signal declaration.
  • Tests handshake ordering, timeout/refusal paths, registration decline behavior, message acknowledgements, device metadata, battery parsing, raw archiving, and absence of samples or offload checkpoints.
test/adapters/garmin_adapter_test.dart
test/adapter_signals_registry_test.dart
test/band_registry_test.dart

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@coderabbitai

coderabbitai Bot commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

Next included review available in 3 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used all 4 included reviews currently available.

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Learn how review limits work.

Review configuration:

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Team

Run ID: 407c262e-8886-4b61-bcf6-66cd0a828bc8

📥 Commits

Reviewing files that changed from the base of the PR and between b98cae6 and faa43df.

⛔ Files ignored due to path filters (4)
  • pubspec.lock is excluded by !**/*.lock
  • test/adapter_signals_registry_test.dart is excluded by !test/**
  • test/adapters/garmin_adapter_test.dart is excluded by !test/**
  • test/band_registry_test.dart is excluded by !test/**
📒 Files selected for processing (8)
  • lib/ble/adapters/_registry.dart
  • lib/ble/adapters/garmin.dart
  • lib/ble/garmin_link.dart
  • lib/ble/hrs_link.dart
  • lib/compute/derivation_engine.dart
  • lib/sync/background_sync.dart
  • lib/ui2/profile/devices.dart
  • pubspec.yaml

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've reviewed your changes and they look great!

Sourcery assessment

Needs a human reviewer. If the Garmin protocol or session behavior is wrong, the watch could have its existing link closed and the session could archive incorrect raw frames or metadata. Reverting stops future sessions, but it does not undo watch-side connection effects or remove records already written; those are bounded and repairable.


Sourcery is free for open source - if you like our reviews please consider sharing them ✨

@github-actions

github-actions Bot commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

Failed to generate code suggestions for PR

pubspec.yaml's protocol ref moved for #361 but this constant didn't follow
it, failing the pin-consistency check. no kAlgoVersion move — the repin
only adds garmin.dart, nothing on the derivation path changed.
@github-actions

github-actions Bot commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

Failed to generate code suggestions for PR

@abdulsaheel

Copy link
Copy Markdown
Collaborator Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

Rate Limit Exceeded

@abdulsaheel have exceeded the limit for the number of chat messages per hour. Please wait 2 minutes and 24 seconds before sending another message.

@abdulsaheel

Copy link
Copy Markdown
Collaborator Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Sep 5, 2026

Copy link
Copy Markdown
Contributor
⚠️ Action not completed

Review rate limited.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@abdulsaheel
abdulsaheel merged commit 189b552 into main Sep 5, 2026
3 checks passed
@github-actions

github-actions Bot commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

PR Reviewer Guide 🔍

Here are some key observations to aid the review process:

⏱️ Estimated effort to review: 3 🔵🔵🔵⚪⚪
🧪 PR contains tests
🔒 No security concerns identified
⚡ No major issues detected

@github-actions

github-actions Bot commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

PR Code Suggestions ✨

Explore these optional code suggestions:

CategorySuggestion                                                                                                                                    Impact
General
Use watch-specific localization key for Garmin sync failure

Use devicesCouldNotReachWatch instead of devicesCouldNotReachRing for the Garmin
watch sync failure message. This ensures the terminology matches the device type and
aligns with other watch sync functions.

lib/ui2/profile/devices.dart [1850-1863]

 Future<void> _syncGarminWatch(BuildContext c) async {
   final l = AppLocalizations.of(c);
   final messenger = ScaffoldMessenger.maybeOf(c);
   messenger?.showSnackBar(const SnackBar(content: Text('Syncing…')));
   final ok = await GarminLink.instance.sync();
   if (!c.mounted) return;
   messenger?.showSnackBar(SnackBar(
     content: Text(ok
         ? (l?.devicesSynced ?? 'Synced.')
-        : (l?.devicesCouldNotReachRing ??
-            'Could not reach it. It has to be nearby, and not connected to '
+        : (l?.devicesCouldNotReachWatch ??
+            'Could not reach the watch. It has to be nearby, and not connected to '
                 'another app.')),
   ));
 }
Suggestion importance[1-10]: 6

__

Why: The suggestion correctly identifies that a Garmin device is a watch, not a ring, and updates the localization key and fallback text accordingly. This improves the accuracy of the UI messages.

Low

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant