Repository navigation
Conversation
Loop publishes its own PebbleOS PPoGATT service with CoreBluetooth and pushes CGM readings to the Luped watchface, side by side with the Pebble app. The legacy PebbleKit iOS SDK is dropped: its discovery never recognizes Core Devices watches. - Answers the phone-version request with 8 KiB AppMessage support and Alloy's startup ready handshake; retries rejected pushes with backoff. - Sends values in Loop's display unit at display precision, and mirrors the active glucose alert profile (thresholds, per-zone switches, repeat intervals). - Settings → Devices → Pebble: Send to Pebble, Watch Alarms, watchface UUID, and a communication log with decoded payloads and watch replies. Compiled only with LUPED_PEBBLE; builds on next-dev's GlucoseAlertManager. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Settings → Devices → Pebble → Full-Screen Ring sends DATA index 13. - Rename the "Luped PebbleKit" build phase and script to "Luped Background Mode" (luped-background-mode.sh); it only adds the bluetooth-peripheral background mode. - Add the Pebble settings strings to Localizable.xcstrings (manual extraction, since they compile only with LUPED_PEBBLE). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Settings → Devices → Pebble shows live link status, the last delivered reading with its round trip, and the next retry; Send Now pushes the current reading. - Watch section shows battery and counters from Luped's diagnostics snapshot. AppMessage tuples are parsed by key so diagnostics replies, Alloy's ready handshake, and refresh commands are told apart. - Explain missing diagnostics replies: Alloy disables a watchface's outbox whenever Loop's session reconnects, until the watchface restarts. - After the full retry backoff (about 12 minutes) without an ACK, pause sending, including new readings, until Luped starts, a new session opens, or Send Now. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Pebble screen requested diagnostics on every onAppear, including each return from the communication log. Limit automatic requests to one a minute, skip them while the watch is known to be unable to reply, and log what triggered each request. Refresh Watch Info always sends. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Drop the automatic diagnostics request on screen appearance. When Luped has not messaged Loop since the session opened, its Alloy outbox is disabled, so Refresh Watch Info first restarts the watchface over the app run-state endpoint (STOP, then RUN) and sends the request after Luped's ready handshake. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Pebble is no longer set up by default: Devices shows Add Smartwatch, and the Pebble screen ends with Delete Pebble (with confirmation), which stops the transport and resets its settings. The Send to Pebble switch is gone. The Pebble screen groups Status (with Send Now), Display, and Alarms (with a link to Glucose Alert Settings); watch info and the communication log move to Diagnostics, and the watchface UUID to Advanced. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Follow Loop Alert Mute (on by default) sends Luped's alarm mask as 0 while Loop's alerts are muted and restores it when the mute ends or is cancelled. Loop owns the mute state; Luped 0.1.1 keeps its no-recent-data buzz regardless. The communication log records each change in why watch alarms are on, off or muted. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
PebbleTransport is a generic link for any JS-free watch app: the PPoGATT service and session, Pebble Protocol framing, the phone-version reply, AppMessage encoding, decoding and ACKs, and app run-state commands. It reports link state, session events, messages, replies and log lines to a delegate. LupedPebbleTransport builds on it with Luped's keys and DATA payload, refresh and diagnostics, delivery with retry and pausing, and alarm reasons. It mirrors the link state into the status row and writes the generic log lines to the communication log unchanged. No behavior change. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The generic Pebble transport and the Luped layer (payload, alarm and mute handling, delivery, commands, status, diagnostics, communication log and settings) now live in https://github.com/dabear/elbbepTransport so Trio can share them. Loop links the LupedKit product and keeps a thin adapter that supplies stored CGM readings in the display unit, the active glucose alert profile and the alert mute. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Its content moves to the pull request description; the transport and Luped protocol are documented in the elbbepTransport package. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
dabear
marked this pull request as draft
October 13, 2026 06:54
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Pebble Watch support
Summary
Adds an opt-in transport that sends Loop's CGM readings and glucose alert settings to the Luped watchface on Core Devices Pebble watches (Pebble Time 2 and Pebble Round 2 since it requires "Alloy" only present on these watches), and a Settings → Devices → Pebble device to manage it. It is compiled only when
LUPED_PEBBLEis set.The transport and Luped logic live in the elbbepTransport Swift package, so Trio and other apps can share them later. Loop keeps a thin adapter and the settings screen. This is direct push over bluetooth.
Since communication to Pebble watches is watchface dependant, Loop will in this version only talk to my Luped watchface. The source for this Pebble watchface is open source and available at (https://github.com/dabear/CrimsonBearPebbleWatchFace/tree/main/variants/luped). If a developer or experienced user wants to do any changes to the luped watchface, you can either build your own based on available sources, or change uuid under Loop->Devices->Pebble->Advanced to talk to entirely different watchface, as long as the luped protocol is used.
These watches typically have more than 10 days wear time per charge. This could also be improved in the future, but requires some modifications to the now-opensource PebbleOS. Pending..
How it reaches the watch
Loop publishes its own PebbleOS PPoGATT ("Pebble Protocol over GATT") service with CoreBluetooth. The watch stays paired with and connected to the Pebble app, which keeps its own session for notifications, PebbleKit JS and other watchfaces. PebbleOS discovers Loop's service on the same Bluetooth connection, reads the app UUID from its meta characteristic, and opens a second session that carries only Luped AppMessages.
The legacy PebbleKit iOS SDK is not used: its watch discovery does not recognize Core Devices watches, so it never reports a connected watch.
Code layout
ElbbepTransport(package): a generic link for any JS-free watch app. It runs the PPoGATT service and session, Pebble Protocol framing, the phone-version reply, AppMessage encoding/decoding and ACKs, and app run-state commands.LupedKit(package): Luped's message keys andDATApayload, the alarm mask and alert-mute handling, the refresh and diagnostics commands, delivery with retry and pausing, link status, watch diagnostics, the communication log, and thelupedPebble…settings.LupedPebbleTransport.swift(Loop): an adapter that supplies stored CGM readings in Loop's display unit, the active glucose alert profile and the alert mute, and asks LupedKit to re-send when any of them change.LupedWatchSettingsView.swift(Loop): the settings screen.Scripts/luped-background-mode.sh(Loop): a build phase that adds thebluetooth-peripheralbackground mode, only whenLUPED_PEBBLEis set.Enable
Add
LUPED_PEBBLEto the Features line inLoopConfigOverride.xcconfig:Pair the watch with the Pebble app and select the Luped watchface.
Without the flag, the Luped sources compile to nothing, the Pebble device does not appear, and no background mode is added. The
elbbepTransportpackage is still resolved and linked statically, but no code uses it.Settings → Devices → Pebble
Pebble appears as a device next to the pump and CGM. It is off until Add Smartwatch is tapped. Delete Pebble (with confirmation) ends the watch session, releases the Bluetooth peripheral manager, stops building payloads, resets the Pebble settings and clears the log.
bf32924c-4839-449e-9e56-ee190249acb2): the app UUID that Loop's service and AppMessages use.Settings are stored in standard user defaults (
lupedPebbleEnabled,lupedPebbleAlarmsEnabled,lupedPebbleFollowAlertMute,lupedPebbleFullScreen,lupedPebbleWatchfaceUUID) and apply within half a second.Delivery
refreshrequest re-sends it.Safety notes
Testing
swift testin elbbepTransport passes 16 tests. They cover Pebble Protocol framing, AppMessage encoding and decoding, the Luped payload format, mmol/L rounding, the trend fallback, the alarm mask, mute handling and the alarm-off reasons.0, then the profile mask againBefore merging
next-dev; the branch is 4 commits behind.elbbepTransport: the branch tracksmain, and a tag or revision pin would make builds reproducible.