[Part 3 of 3] refactor: rework CGM settings screen and persist sensor state across launches - #44
[Part 3 of 3] refactor: rework CGM settings screen and persist sensor state across launches#44ETolboom wants to merge 15 commits into
Conversation
There was a problem hiding this comment.
Pull request overview
This PR refactors the CGM settings experience by introducing a richer, lifecycle-driven sensor status display, persisting key sensor state across launches via CGMManager.rawState, and moving lifecycle user-notifications onto LoopKit’s native Alert framework (while adding a separate bridge-transmitter low-battery warning row).
Changes:
- Persist last-known sensor/session state across launches using
LibreCGMManagerStateandrawState/init(rawState:). - Add unified lifecycle + alerting surfaces (
LibreSensorLifecycle,LibreAlertCondition,evaluateAlerts()), and wire them into LoopKit UI hooks (badge/highlight/progress). - Rework Settings UI sensor status row (
LibreSensorStatusDisplay) and remove legacy notification/alarm/snooze/glucose-settings UI +NotificationHelper-based alerting.
Reviewed changes
Copilot reviewed 33 out of 34 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| readme.md | Removes mention of now-deleted glucose smoothing behavior. |
| LibreTransmitterUI/Views/Setup/Libre2DirectSetup.swift | Removes legacy NotificationHelper calibration/setup notifications. |
| LibreTransmitterUI/Views/Setup/BluetoothSelection.swift | Removes notification permission request hook tied to removed notification system. |
| LibreTransmitterUI/Views/Settings/SnoozeView.swift | Deletes legacy snooze UI. |
| LibreTransmitterUI/Views/Settings/SettingsView.swift | Reworks settings layout and adds sensor status + bridge battery warning rows. |
| LibreTransmitterUI/Views/Settings/NotificationSettingsView.swift | Deletes legacy notification settings UI. |
| LibreTransmitterUI/Views/Settings/LibreSensorLifecycle+Display.swift | Adds new settings-row-friendly sensor status display model. |
| LibreTransmitterUI/Views/Settings/GlucoseSettingsView.swift | Deletes legacy glucose settings UI. |
| LibreTransmitterUI/Views/Settings/AlarmSettings/CustomDataPickerView.swift | Deletes legacy alarm schedule UI component. |
| LibreTransmitterUI/Views/Settings/AlarmSettings/CriticalAlarmsVolumeView.swift | Deletes legacy critical-alarm volume UI. |
| LibreTransmitterUI/Views/Settings/AlarmSettings/AlarmSettingsView.swift | Deletes legacy alarm settings UI. |
| LibreTransmitterUI/LibreTransmitterManager+UI.swift | Implements LoopKit UI surfaces (badge/highlight/progress) based on lifecycle. |
| LibreTransmitter/Observables/TransmitterInfo.swift | Adds batteryPercent observable for bridge battery warning and equality updates. |
| LibreTransmitter/Observables/SensorInfo.swift | Adds published lifecycle + pairing state for UI consumption. |
| LibreTransmitter/Observables/AlarmStatus.swift | Deletes legacy alarm-status observable used by removed UI. |
| LibreTransmitter/NotificationHelperOverride.swift | Deletes legacy notification override toggle. |
| LibreTransmitter/NotificationHelper.swift | Deletes legacy local notification + schedule-based alerting implementation. |
| LibreTransmitter/LibreTransmitterManagerV3.swift | Persists/restores state, adds lifecycle computation, and integrates alert evaluation. |
| LibreTransmitter/LibreTransmitterManager+Transmitters.swift | Removes notification side-effects; records fault/lifecycle info and triggers alert evaluation. |
| LibreTransmitter/LibreTransmitterManager+Libre2EU.swift | Removes legacy expiry notifications; triggers alert evaluation post-observable updates. |
| LibreTransmitter/LibreGlucose.swift | Removes redundant smoothing and uses calibrated glucose directly. |
| LibreTransmitter/LibreCGMManagerState.swift | New persisted state container for rawState round-tripping. |
| LibreTransmitter/Alerting/LibreTransmitterManagerV3+Alerts.swift | Adds lifecycle alert diffing and persistence update logic. |
| LibreTransmitter/Alerting/LibreSensorLifecycle.swift | New unified lifecycle model across data paths. |
| LibreTransmitter/Alerting/LibreAlertCondition.swift | Maps lifecycle states to LoopKit Alerts and identifiers. |
| LibreTransmitter/.gitignore | Removes ignore rule for deleted notification override file. |
| LibreTransmitter.xcodeproj/project.pbxproj | Updates project file references to add new files/remove deleted legacy UI/notification files. |
| LibreSensor/GlucoseAlgorithm/GlucoseSmoothing.swift | Deletes the old smoothing implementation. |
| Features.swift | Removes legacy flags tied to deleted alarm/vibration behavior. |
| Common/Settings/UserDefaults+Bluetooth.swift | Adds dangerModeActivated key and shared optionalBool(forKey:). |
| Common/Settings/UserDefaults+Alarmsettings.swift | Deletes legacy alarm/notification-related UserDefaults helpers. |
| Common/Settings/GlucoseSchedules.swift | Deletes legacy glucose schedule model used by removed alarm notifications. |
| build.md | Removes build instructions for removed critical-alerts/vibration features; simplifies build steps. |
| .gitignore | Removes ignore rule for deleted notification override file. |
Suppressed comments (1)
LibreTransmitter/Observables/TransmitterInfo.swift:25
TransmitterInfoconforms toHashableand implements value-based==, but (via theHashable where Self: AnyObjectextension) its hash is based on object identity. This violates Hashable’s requirement that equal values must hash the same, and can break Sets/Dictionaries ifTransmitterInfois ever used as a key.
public static func == (lhs: TransmitterInfo, rhs: TransmitterInfo) -> Bool {
lhs.battery == rhs.battery && lhs.batteryPercent == rhs.batteryPercent && lhs.hardware == rhs.hardware &&
lhs.firmware == rhs.firmware && lhs.connectionState == rhs.connectionState &&
lhs.transmitterType == rhs.transmitterType && lhs.transmitterMacAddress == rhs.transmitterMacAddress &&
lhs.sensorType == rhs.sensorType
}
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
d23c91b to
bc04bf6
Compare
Trio already applies its own smoothing to incoming glucose values, so the 5-point boxcar moving-average filter (CalculateSmothedData5Points) previously run on every trend reading here only added extra lag on top of Trio's own smoothing, with no benefit. Remove the filter and the now-dead GlucoseSmoothing.swift entirely.
…launches - Reorganize the settings screen: add a sensor status row (icon + text) driven by the sensor lifecycle, separate sensor identity fields from device/transmitter fields into their own section, group maintenance actions under a "Manage" section, give the pushed "Device details" screen a title, and remove the now-redundant "Glucose Settings" screen. - Fix the sensor status resolving to "No Sensor" instead of "Connecting" on a fresh launch: gate the status display on persisted pairing state first (independent of the lifecycle enum, which only resolves after a live read), and correct an ordering bug where the lifecycle was evaluated before setObservables() had updated the underlying sensor info it depends on. - Implement CGMManager.rawState/init(rawState:) persistence (previously a no-op stub), matching what other LoopKit CGM drivers already do, so sensor identity/timing/status can be restored immediately at launch instead of waiting for the first live BLE read of each session.
…trings Backfills catalog entries for strings introduced in earlier commits (LibreAlertCondition, the bridge battery row, sensor lifecycle status rows) that were never synced into the String Catalogs.
…itle LibreGlucose's initializer accepted a MeasurementError array but never assigned it to self.error, so every reading silently reported .OK regardless of what the sensor's raw error bitfield actually said. Also show the live sensor type (e.g. "Libre 2") as the settings screen title instead of the generic "FreeStyle Libre".
Decode the raw per-reading firmware error/quality bits (temperature out of range, rate-of-change too fast, signal saturated, etc.) that were already being parsed but silently dropped, and surface them in the sensor status row instead of showing "Sensor OK" when the sensor is actively reporting a problem. Wording for TEMP_HIGH, TEMP_LOW, THERMISTOR_OUT_OF_RANGE, PEAK_DELTA_EXCEEDED, AVG_DELTA_EXCEEDED, SIGNAL_SATURATED, SENSOR_SIGNAL_LOW, and INVALID_DATA is grounded in Abbott's own Android app data-processing semantics; the remaining cases are best-effort placeholders pending official terminology.
The 10-minute-ahead linear regression extrapolation shown as "Last Blood Sugar prediction" was display-only, but a speculative future value like this is actively dangerous to keep around in a codebase that feeds an automated insulin dosing system - it invites exactly the kind of misuse (treating a guess as data) an OS-AID pipeline can't afford. Removed the section and its entire pipeline: the per-sensor prediction call sites, the linear regression helper, and the now-dead GlucoseArrayWithPrediction/prediction observable fields.
SettingsView has no NavigationStack, so .navigationTitle never reached UIKit. Titles for the root screen, device details, and calibrations are now set directly on navigationItem, matching SyaiKit's pattern. Also fixes the calibration warning banner sitting outside the List and breaking title layout.
Left behind by the stack rebase: the sync commit on this branch re-added the key independently of the fix (now living on feat/cgm-lifecycle+alerts) that removed the code referencing it.
4821e84 to
245619c
Compare
|
rebase on next-dev and submit please. Preferably Add "persist sensor state" to its own PR if possible, to keep the amount of changes low enough to test individually in-vivo |




Reworks the CGM settings screen's sensor status row into a richer status display (
LibreSensorStatusDisplay, mirroring SyaiKit/LibreLoop's status display pattern) and persists sensor lifecycle state across app launches so status is meaningful before the first BLE read completes this session. Also adds an independent low-battery warning row for bridge transmitters (MiaoMiao/Bubble/Blucon), separate from the sensor lifecycle status since bridge battery is unrelated hardware state.Since this was first opened, 5 more commits landed on top:
LibreGlucose's initializer accepted a parsedMeasurementErrorarray but never assigned it toself.error— every reading silently reported.OKregardless of what the sensor's raw error bitfield said. Also shows the live sensor type (e.g. "Libre 2") as the settings screen title instead of a generic "FreeStyle Libre".Localizable.xcstringsentries for strings from earlier commits in this stack that were never synced into the catalog. Mechanical, no new copy.NavigationStack, so.navigationTitlenever actually reached UIKit — the root screen, "Device details", and the calibrations screen all showed a blank nav bar title. Titles are now set directly onnavigationItem, matching SyaiKit's pattern. Also fixes the calibration screen's warning banner, which was rendering as a sibling of itsListinstead of a row inside it and breaking the title layout.Part 3 of 3 related PRs. Built on top of #43 in my fork, so this diff will include #42 and #43's changes until those merge and this gets rebased.