Repository navigation
Conversation
added 4 commits
November 2, 2026 11:29
…m LoopKit (LoopKit #674, Loop #2635)
Watch plugins that publish a Bluetooth service need the bluetooth-peripheral background mode. It is on by default; uncommenting INFOPLIST_PREPROCESSOR_DEFINITIONS in LoopConfigOverride.xcconfig leaves it out. Loop points at the fork until its PR is merged.
LoopKit's SmartwatchManager plugin type and Loop's host, with the LupedSmartwatchKit submodule, its projects in the workspace and its plugin bundle in the LoopWorkspace scheme. elbbepTransport (Swift package, tagged releases) is the Bluetooth link to the Pebble. LoopKit and Loop point at the forks until their PRs are merged.
Bolus, carbs, alert mute and presets requested from the watch, each refused unless the plugin declares it and the user allows it in Watch Permissions. The Pebble plugin declares none yet. Revert this commit (and the matching commit in LoopKit, Loop and LupedSmartwatchKit) to leave remote actions out.
This was referenced Nov 2, 2026
The base strings (47e7c4ab) and the remote action strings (647c0dd4), so they reach translation. To leave remote actions out, revert 647c0dd4 together with e3425e1d in Loop.
…LupedSmartwatchKit to their merges with next-dev LoopKit#568 is merged, so its commits leave this PR. LoopKit's SmartwatchManager.delete() is async now, like CGMManager and PumpManager after #685, and the plugin follows.
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.
LoopWorkspace: watch plugins and the Pebble plugin
Adds the LupedSmartwatchKit Pebble plugin and points LoopKit and Loop at the forks until their PRs (LoopKit, Loop) are merged.
Commits
061cd2abluetooth-peripheralbackground mode, with a commented-out switch inLoopConfigOverride.xcconfig326a3aaLupedSmartwatchKitsubmodule, its projects in the workspace and its plugin bundle in theLoopWorkspacescheme; elbbepTransport 0.3.59 (Swift package, tagged releases), the Bluetooth link to the Pebblef21b56682fe7c14), Loop (e3425e1dwith its strings,647c0dd4) and LupedSmartwatchKit (d66e597), to leave remote actions out. Since thenext-devmerges, LoopKit's and Loop's reverts each have one small conflict; their PRs say how to resolve it.9f53a6d47e7c4ab, and647c0dd4for remote actions), so they reach translationbcb0504c50e77f,4243102next-dev: #568 (kits on LoopKit's own value types, now merged) and #570 (async device deletion).4243102also moves LoopKit, Loop and LupedSmartwatchKit to their merges withnext-dev, whereSmartwatchManager.delete()is async likeCGMManagerandPumpManager.Both versions,
326a3aaandf21b566, build and pass their tests (checked again onnext-devwith #568 and #570);9f53a6dadds only strings, and the catalog compiles.Repositories: happy to move LupedSmartwatchKit, elbbepTransport and the watchface to the LoopKit organization, or another org you prefer.
Add a SmartwatchManager plugin type, with a Pebble plugin
Watches can now ship as plugins, like CGMs and pumps, instead of as code inside Loop. They appear under Settings → Devices → Add Watch and write to the device log. The first plugin, LupedSmartwatchKit, shows Loop's glucose, loop status and glucose alerts on a Pebble running the Luped CGM watchface.
Supported watches: Core Devices' Pebble Time 2 and Pebble Round 2. The watchface is built for those two platforms only (
emery,gabbro). Older Pebbles, Apple Watch and Garmin aren't part of this PR; Apple Watch keeps Loop's existing app. Daily use is on a Time 2.Using it: for people with diabetes and caregivers
This section is for the person wearing the watch, or a family member. It explains what the watch does; it isn't medical advice. Talk to your care team about your alert settings.
What it does: your Pebble shows the glucose Loop sees, and buzzes when Loop raises a glucose alert. It's an extra screen on your wrist: your iPhone still alarms as before, and you can't give insulin or enter carbs from the watch.
What you need:
Setting it up: in Loop, go to Settings → Devices → Add Watch → Pebble and follow the steps. Add Watch is for watches other than Apple Watch: Loop's Apple Watch app needs no setup there, and is installed from the Watch app on your iPhone. Loop shows when it is waiting for the watch, when the watch is connected, and when the watch has received your first reading. If the watch is added in another diabetes app, remove it there first: the watch follows one app at a time.
On the watch:
When something is wrong:
Always check Loop on your iPhone, or your CGM, before acting on what the watch shows, especially when it shows ERR or the Bluetooth symbol.
Settings (Settings → Devices → Pebble): Layout, Status Line and Last Loop; Watch Alarms, which turns the buzz on or off (the ring still changes colour); Send Now; Diagnostics, with the watch's battery and a message log for troubleshooting; and Delete Pebble.
Screenshots
These are from an iPhone, with a Pebble Time 2 running Luped CGM, on these branches. The phone is set to Norwegian, so Loop's existing labels are Norwegian; the new texts have no translations yet.
The watch
Adding a watch
Next, not in this PR: a second plugin, GarminSmartwatchKit, built on the same plugin type from Trio's GarminManager (display only). My personal build lists it as coming soon and doesn't load it; the Add Watch screenshot above is from that build.
Pebble settings
Diagnostics
Branches
All are named
smartwatch-manager-reviewand sit on currentnext-dev. In each repo the feature comes first, then one commit with everything for remote actions (in Loop, plus one for their strings in the string catalog) (requests from the watch, off by default; the Pebble plugin doesn't use them). Reverting that commit in each repo leaves a working plugin type without remote actions. Both versions build and pass their tests.27b16fd2; after mergingnext-dev,0c5f5c38(asyncdelete())82fe7c1436bc837d(background mode),19e32506,47e7c4ab(strings),097ada11;6c4ee8ffmergesnext-deve3425e1d,647c0dd4(strings)60176ac;f84aa68(asyncdelete())d66e597061cd2a,326a3aa;4243102mergesnext-devf21b566;9f53a6dpins Loop's strings commitsRelated branches (no PR needed):
main22a71808, tag0.3.59mainff0b1d15, Luped 0.1.22Not in this PR, for reference:
main51e69cffsmartwatch-manager6693c43bdevhosting the same pluginsexperimental/garmin-plugin-from-deve3fc7a4etrio-next-dev-smartwatch-manager4a8bf252devpinsDependencies
LupedSmartwatchKitsubmodule (iOS 17.6, like Loop), and through it elbbepTransport 0.3.59, a Swift package with tagged releases. It uses only CoreBluetooth and CryptoKit, has no third-party dependencies, and is MIT licensed, like the plugin.Why a new plugin type
Servicehas no device log, has no channel for the display unit or alerts, uploads incrementally from an anchor, and lives under Services.StatefulPluggablehas no UI, settings row or data delegate.rawState, logs to the device log and belongs under Devices.SmartwatchManagerfollows that model, shaped like Loop's Apple Watch contract (glucose, unit, prediction, loop state) plus host-issued alerts, because watches without iOS notification mirroring can't otherwise know when Loop alerts.LoopKit: the protocols
SmartwatchManager.swiftimports only Foundation and uses no other LoopKit type, so the same plugin builds against other apps' LoopKit forks (Trio's, for example). Every method is documented: when it is called, on which thread, and what the other side does.glucose,history,prediction,loopStatus(IOB, COB, last loop, closed loop),therapy(basal, ISF, eventual glucose, correction range, active preset) andalerts. The remote actions commit addsremoteBolus,remoteCarbs,remoteMute,remoteOverrideandremoteTempTarget. The host acts only oneffectiveCapabilities(declared ∩ requested), so a plugin can't get what it didn't declare.SmartwatchStatusSnapshotholds plain mg/dL values. The host fills only what the plugin asks for.SmartwatchAlertcarriesglucoseAlertType(urgentLow,low,high,predictedLow, ornil). Each app names its alerts differently, so the host maps its own, and a watch can colour a low and a high differently without thresholds of its own.SmartwatchManagerUI,SmartwatchManagerUIPluginand the bundle keys, as for CGM and pump plugins.Loop: the host
PluginManagerfinds watch plugins.DeviceDataManagerkeeps one watch manager, saved asSmartwatchManagerStateand kept if a build lacks the plugin. Settings → Devices gets a Watch row: Add Watch, a picker, the plugin's setup and settings.SmartwatchManagerHostsends a snapshot as soon as a reading is stored and again when the loop's output changes, without resending unchanged ones, in HealthKit's stored glucose unit. The Apple Watch path is untouched.AlertForwarderhook onAlertManagerpasses Loop's immediate, unmuted glucose alerts and their retractions to the plugin.Loop/Info.plistlistsbluetooth-peripheralunder#if LOOP_BLUETOOTH_PERIPHERAL, through Info.plist preprocessing set inLoop.xcconfig. ClearingINFOPLIST_PREPROCESSOR_DEFINITIONSleaves it out. The built Info.plist is otherwise unchanged, and the mode does nothing unless a plugin publishes a Bluetooth service.The Pebble plugin
LupedSmartwatchKit: the manager;LupedSmartwatchKitUI: setup and settings;LupedSmartwatchPlugin: the.loopplugin.glucose,history,alerts,loopStatusandtherapy, and requests only what the chosen display needs. It declares no remote actions.How the link works alongside the Pebble app
Loop doesn't replace the Pebble app; it adds its own channel to the watch, used only by the Luped watchface (elbbepTransport).
DATAfrom Loop, acknowledged by the watch (rejected while Luped isn't the running watchface), and refresh requests and hourly diagnostics from the watch. elbbepTransport handles sequencing, retransmission and resets.What follows from this:
bluetooth-peripheralbackground mode to keep its service up in the background.Remote actions: the gates
A request from the watch reaches Loop's dosing only if it passes every gate:
remoteBolus, …), and the user hasn't turned it off in the plugin. Otherwise the host never receives the request. The Pebble plugin declares none.SmartwatchRemoteActionPolicy.swift):inProgress, not a second bolus;remoteActionLimits), for example a longer window for a watch app that can't send a token back. Loop's own checks below still apply.Please note that I explicitly deny any remote actions for now in multiple gates:
Questions for reviewers
Background mode on by default. It's harmless without a watch plugin, and DIY builders get watch support without editing anything. Would you rather have it off in Loop and turned on in LoopWorkspace's
LoopConfigOverride.xcconfig? This is to be compatible with (or not get in the way of) Tidepool Loop which may or may not want to have this background mode enabled.Remote actions: keep them, or should I revert the remote actions commits so this PR is display and alerts only?
Repositories: I'm happy to move LupedSmartwatchKit, elbbepTransport and the Luped watchface to the LoopKit organization, or another org you prefer.