Skip to content

Decode calibration replies, expire stale calibrations, keep the display type, recover unbonded reconnects - #98

Merged
ps2 merged 1 commit into
next-devfrom
fix/g7-calibration-reply-and-reconnect
Oct 22, 2026
Merged

ps2 merged 1 commit into
next-devfrom
fix/g7-calibration-reply-and-reconnect

Conversation

@ps2

@ps2 ps2 commented Oct 22, 2026

Copy link
Copy Markdown
Contributor

Four fixes from comparing the direct-mode code with xDrip4iOS 7.1.1.

Calibration reply decoding. The 0x34 reply is 34 xx result detail. Result 0 is accepted (high), 1 accepted (low), 2 rejected with a reason in the detail byte, and 3 factory calibrated. An acceptance only counts when the detail byte is 0. We read the two bytes as one number and treated only 1 as accepted, so an "accepted high" reply (34 xx 00 00) showed as refused. The byte meanings are from xDrip4iOS (DexcomG7CalibrationMessages.swift).

  • The one reply we've seen from a sensor, 34 00 01 00 (2026-09-15), still decodes as accepted.
  • A refusal now shows the sensor's reason in settings, e.g. "refused by the sensor: the value is too far from its own reading". Reasons we don't have text for still show the status code.
  • The calibration state (0x32) is now requested after every reply, not just after an acceptance, so the settings screen follows what the sensor actually did.

Queued calibrations expire. A calibration still waiting when the sensor connects more than 5 minutes after it was entered is dropped and shown as "not sent", instead of going out with a stale meter value. This covers a calibration carried across an app relaunch. xDrip uses the same 5-minute limit.

Display type is saved. It was not part of the manager's saved state, so every relaunch went back to the phone slot. Older saved state still defaults to phone.

Stored-key reconnect that isn't bonded. When a reconnect with a stored key gets back anything other than authenticated and bonded (e.g. 05 01 02 after "Forget This Device" in iOS Bluetooth settings), the stored key is now dropped and the next connection pairs again with the saved code, the same as for a challenge mismatch or noAppKey. Before, it threw unexpectedResponse("reconnect") and stayed on "Connection Refused" until the user re-paired by hand. Without a saved code the key is kept, as before.

Testing: the G7SensorKit unit tests pass (176), with new tests for each change, and the LoopWorkspace scheme builds. Not tried on a sensor.

…ay type, recover unbonded reconnects

- The 0x34 reply is result + detail bytes: 0/1 accepted (detail 0),
  2 rejected with a reason, 3 factory calibrated (meanings from
  xDrip4iOS). "Accepted high" used to show as refused. Settings now
  shows the refusal reason, and the calibration state is requested
  after every reply.
- A queued calibration more than 5 minutes old when the sensor connects
  is dropped and shown as not sent.
- The display type is saved in the manager state, so a relaunch keeps
  the slot it paired with.
- A stored-key reconnect that isn't answered as authenticated and
  bonded (e.g. 05 01 02 after "Forget This Device") drops the key so
  the saved code pairs again, like a challenge mismatch or noAppKey.
@ps2
ps2 merged commit 45a1a96 into next-dev Oct 22, 2026
ps2 added a commit to LoopKit/LoopWorkspace that referenced this pull request Oct 22, 2026
- G7SensorKit: calibration reply decoding and stale calibration expiry (LoopKit/G7SensorKit#98), reconnect after a SystemMemoryReset (LoopKit/G7SensorKit#100), temporary sensor issue in settings (LoopKit/G7SensorKit#99)
- LoopSupport: view the diagnostic report before sharing or sending it (LoopKit/LoopSupport#42)
- OmnipodKit: pump view signal loss uses the last response time (loopandlearn/OmnipodKit#196)
- EversenseKit: retry GetTxCertificate with tx_flags inverted on the 365 (LoopKit/EversenseKit#8)
ps2 added a commit to jeremybarnum/G7SensorKit that referenced this pull request Oct 23, 2026
…tch setting

- A reconnect the sensor answers authenticated but not bonded (05 01 02
  after the system bond was lost) runs the certificate phase with the
  saved key and bonds again, without the code (EJM0's fix). If that
  fails, the key is dropped and the next connection pairs with the
  code, as LoopKit#98 does.
- A pairing whose bond the sensor never confirmed, because the system
  pairing request couldn't show with the app in the background, used to
  surface later as a wrong pairing code. When that key is refused, the
  new Sensor Pairing Not Finished alert says to keep the app open and
  accept the request; it is retracted at the next reading (Kristin
  Kandschur's fix, worded for any app).
- A Read on Apple Watch setting in the G7 settings, offered when a
  paired watch has the app installed or while it is on. The manager
  reports it as isConfigurationSharingEnabled, so the host passes the
  sensor to its watch only while it is on; the footer says the watch
  takes the sensor's watch slot. Eavesdropping, the code entry the
  watch needs sits under the setting.
- The settings card counts down to the next reading in minutes and
  seconds, instead of counting up the minutes since the last; once a
  reading is a minute late it shows the minutes since, as before.
- The setting, the new-sensor alert and the status text are
  localizable.

Co-Authored-By: elias maurer <eliasjosefmaurer@gmail.com>
Co-Authored-By: Kristin Kandschur <kristin.kandschur@proton.me>
kristinkand added a commit to kristinkand/G7SensorKit that referenced this pull request Oct 24, 2026
…with the code

From Pete Schwamb's LoopKit#80 (b97ad44, after LoopKit#98): a stored-key reconnect the sensor
doesn't answer as authenticated, or whose certificate phase fails while bonding again (05 01 02 after the
system bond was lost), throws storedKeyNotBonded. That counts as a stale key: it's dropped while a code is
at hand, and the next connection pairs with it. Before, the first case was an unexpectedResponse that kept
the key, and the second failed on every connection.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LgnnHLJbhxmQaEjQNYR5VZ
kristinkand pushed a commit to kristinkand/G7SensorKit that referenced this pull request Oct 24, 2026
…ay type, recover unbonded reconnects (LoopKit#98)

- The 0x34 reply is result + detail bytes: 0/1 accepted (detail 0),
  2 rejected with a reason, 3 factory calibrated (meanings from
  xDrip4iOS). "Accepted high" used to show as refused. Settings now
  shows the refusal reason, and the calibration state is requested
  after every reply.
- A queued calibration more than 5 minutes old when the sensor connects
  is dropped and shown as not sent.
- The display type is saved in the manager state, so a relaunch keeps
  the slot it paired with.
- A stored-key reconnect that isn't answered as authenticated and
  bonded (e.g. 05 01 02 after "Forget This Device") drops the key so
  the saved code pairs again, like a challenge mismatch or noAppKey.

(cherry picked from commit 45a1a96, LoopKit/G7SensorKit next-dev. This fork already decides the display slot
from configuredByAnotherController, so the stored displayType is left out; the reconnect part was already
here as storedKeyNotBonded.)
EJM0 pushed a commit to EJM0/G7SensorKit that referenced this pull request Oct 24, 2026
…with the code

From Pete Schwamb's LoopKit#80 (b97ad44, after LoopKit#98): a stored-key reconnect the sensor
doesn't answer as authenticated, or whose certificate phase fails while bonding again (05 01 02 after the
system bond was lost), throws storedKeyNotBonded. That counts as a stale key: it's dropped while a code is
at hand, and the next connection pairs with it. Before, the first case was an unexpectedResponse that kept
the key, and the second failed on every connection.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LgnnHLJbhxmQaEjQNYR5VZ
(cherry picked from commit 30ec7a1)
ps2 added a commit to jeremybarnum/G7SensorKit that referenced this pull request Oct 24, 2026
…tch setting

- A reconnect the sensor answers authenticated but not bonded (05 01 02
  after the system bond was lost) runs the certificate phase with the
  saved key and bonds again, without the code (EJM0's fix). If that
  fails, the key is dropped and the next connection pairs with the
  code, as LoopKit#98 does.
- A pairing whose bond the sensor never confirmed, because the system
  pairing request couldn't show with the app in the background, used to
  surface later as a wrong pairing code. When that key is refused, the
  new Sensor Pairing Not Finished alert says to keep the app open and
  accept the request; it is retracted at the next reading (kristinkand's
  fix, worded for any app).
- A Read on Apple Watch setting in the G7 settings, offered when a
  paired watch has the app installed or while it is on. The manager
  reports it as isConfigurationSharingEnabled, so the host passes the
  sensor to its watch only while it is on; the footer says the watch
  takes the sensor's watch slot. Eavesdropping, the code entry the
  watch needs sits under the setting.
- The settings card counts down to the next reading in minutes and
  seconds, instead of counting up the minutes since the last; once a
  reading is a minute late it shows the minutes since, as before.
- The setting, the new-sensor alert and the status text are
  localizable.

Co-Authored-By: elias maurer <eliasjosefmaurer@gmail.com>
Co-Authored-By: kristinkand <163904051+kristinkand@users.noreply.github.com>
kristinkand added a commit to kristinkand/G7SensorKit that referenced this pull request Oct 24, 2026
…with the code

From Pete Schwamb's LoopKit#80 (b97ad44, after LoopKit#98): a stored-key reconnect the sensor
doesn't answer as authenticated, or whose certificate phase fails while bonding again (05 01 02 after the
system bond was lost), throws storedKeyNotBonded. That counts as a stale key: it's dropped while a code is
at hand, and the next connection pairs with it. Before, the first case was an unexpectedResponse that kept
the key, and the second failed on every connection.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LgnnHLJbhxmQaEjQNYR5VZ
kristinkand added a commit to kristinkand/G7SensorKit that referenced this pull request Oct 24, 2026
…with the code

From Pete Schwamb's LoopKit#80 (b97ad44, after LoopKit#98): a stored-key reconnect the sensor
doesn't answer as authenticated, or whose certificate phase fails while bonding again (05 01 02 after the
system bond was lost), throws storedKeyNotBonded. That counts as a stale key: it's dropped while a code is
at hand, and the next connection pairs with it. Before, the first case was an unexpectedResponse that kept
the key, and the second failed on every connection.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LgnnHLJbhxmQaEjQNYR5VZ
kristinkand pushed a commit to kristinkand/G7SensorKit that referenced this pull request Oct 24, 2026
…ay type, recover unbonded reconnects (LoopKit#98)

- The 0x34 reply is result + detail bytes: 0/1 accepted (detail 0),
  2 rejected with a reason, 3 factory calibrated (meanings from
  xDrip4iOS). "Accepted high" used to show as refused. Settings now
  shows the refusal reason, and the calibration state is requested
  after every reply.
- A queued calibration more than 5 minutes old when the sensor connects
  is dropped and shown as not sent.
- The display type is saved in the manager state, so a relaunch keeps
  the slot it paired with.
- A stored-key reconnect that isn't answered as authenticated and
  bonded (e.g. 05 01 02 after "Forget This Device") drops the key so
  the saved code pairs again, like a challenge mismatch or noAppKey.

(cherry picked from commit 45a1a96, LoopKit/G7SensorKit next-dev. This fork already decides the display slot
from configuredByAnotherController, so the stored displayType is left out; the reconnect part was already
here as storedKeyNotBonded.)
kristinkand added a commit to kristinkand/G7SensorKit that referenced this pull request Oct 24, 2026
…with the code

From Pete Schwamb's LoopKit#80 (b97ad44, after LoopKit#98): a stored-key reconnect the sensor
doesn't answer as authenticated, or whose certificate phase fails while bonding again (05 01 02 after the
system bond was lost), throws storedKeyNotBonded. That counts as a stale key: it's dropped while a code is
at hand, and the next connection pairs with it. Before, the first case was an unexpectedResponse that kept
the key, and the second failed on every connection.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LgnnHLJbhxmQaEjQNYR5VZ
EJM0 pushed a commit to EJM0/G7SensorKit that referenced this pull request Oct 24, 2026
…with the code

From Pete Schwamb's LoopKit#80 (b97ad44, after LoopKit#98): a stored-key reconnect the sensor
doesn't answer as authenticated, or whose certificate phase fails while bonding again (05 01 02 after the
system bond was lost), throws storedKeyNotBonded. That counts as a stale key: it's dropped while a code is
at hand, and the next connection pairs with it. Before, the first case was an unexpectedResponse that kept
the key, and the second failed on every connection.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LgnnHLJbhxmQaEjQNYR5VZ
(cherry picked from commit 30ec7a1)
kristinkand pushed a commit to kristinkand/G7SensorKit that referenced this pull request Oct 25, 2026
…ay type, recover unbonded reconnects (LoopKit#98)

- The 0x34 reply is result + detail bytes: 0/1 accepted (detail 0),
  2 rejected with a reason, 3 factory calibrated (meanings from
  xDrip4iOS). "Accepted high" used to show as refused. Settings now
  shows the refusal reason, and the calibration state is requested
  after every reply.
- A queued calibration more than 5 minutes old when the sensor connects
  is dropped and shown as not sent.
- The display type is saved in the manager state, so a relaunch keeps
  the slot it paired with.
- A stored-key reconnect that isn't answered as authenticated and
  bonded (e.g. 05 01 02 after "Forget This Device") drops the key so
  the saved code pairs again, like a challenge mismatch or noAppKey.

(cherry picked from commit 45a1a96, LoopKit/G7SensorKit next-dev. This fork already decides the display slot
from configuredByAnotherController, so the stored displayType is left out; the reconnect part was already
here as storedKeyNotBonded.)
(cherry picked from commit 8d07c48)
EJM0 pushed a commit to EJM0/G7SensorKit that referenced this pull request Oct 25, 2026
…connect into the tail, Pete Schwamb's LoopKit#83, LoopKit#93, LoopKit#98

ba78db3: as the app leaves the screen the hold keeps running, no start delay
(CBConnectPeripheralOptionStartDelayKey doesn't hold on the watch) and no connect
into the sensor's advertising tail, which watchOS counts against the watch.
5390728 is the refused-rebond fix this branch has as 4a2f884.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0135v4JApHewuwGjWKiSPDNY
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant