From da8cffafed6ec3c1a3799cbcb2f9c91df67365fa Mon Sep 17 00:00:00 2001 From: Pete Schwamb Date: Wed, 14 Oct 2026 14:14:04 -0500 Subject: [PATCH] Report pump and CGM models to analytics Adds Pump Model and CGM Model user properties from the managers' HKDevice.model, so analytics can tell which hardware is paired, not just which plugin. They are sent at setup and launch, and again when a status update reports a different model. --- Loop/Managers/AnalyticsServicesManager.swift | 10 ++++++++++ Loop/Managers/DeviceDataManager.swift | 13 +++++++++++++ Loop/Managers/LoopAppManager.swift | 2 ++ 3 files changed, 25 insertions(+) diff --git a/Loop/Managers/AnalyticsServicesManager.swift b/Loop/Managers/AnalyticsServicesManager.swift index fdbef3dc77..6ca7e0144f 100644 --- a/Loop/Managers/AnalyticsServicesManager.swift +++ b/Loop/Managers/AnalyticsServicesManager.swift @@ -74,6 +74,14 @@ final class AnalyticsServicesManager { identify("CGM Type", value: cgmType) } + func identifyPumpModel(_ model: String?) { + identify("Pump Model", value: model ?? "Unknown") + } + + func identifyCGMModel(_ model: String?) { + identify("CGM Model", value: model ?? "Unknown") + } + // MARK: - Screens func didDisplayBolusScreen() { @@ -157,6 +165,7 @@ final class AnalyticsServicesManager { func pumpWasRemoved() { logEvent("Pump Removed") identifyPumpType("None") + identifyPumpModel("None") } func pumpWasAdded(identifier: String) { @@ -166,6 +175,7 @@ final class AnalyticsServicesManager { func cgmWasRemoved() { logEvent("CGM Removed") identifyCGMType("None") + identifyCGMModel("None") } func cgmWasAdded(identifier: String) { diff --git a/Loop/Managers/DeviceDataManager.swift b/Loop/Managers/DeviceDataManager.swift index 49625bd26b..4f1397a9d9 100644 --- a/Loop/Managers/DeviceDataManager.swift +++ b/Loop/Managers/DeviceDataManager.swift @@ -68,6 +68,8 @@ final class DeviceDataManager { @Published var cgmHasValidSensorSession: Bool + private var cgmDeviceModel: String? + @Published var pumpIsAllowingAutomation: Bool private var lastCGMLoopTrigger: Date = .distantPast @@ -684,6 +686,8 @@ private extension DeviceDataManager { cgmHasValidSensorSession = cgmManager.cgmManagerStatus.hasValidSensorSession analyticsServicesManager.identifyCGMType(cgmManager.pluginIdentifier) + cgmDeviceModel = cgmManager.cgmManagerStatus.device?.model + analyticsServicesManager.identifyCGMModel(cgmDeviceModel) } // Refresh the GlucoseAlertManager's view of whether the active // CGM does its own alerting. No active CGM ⇒ treat as "no @@ -717,6 +721,7 @@ private extension DeviceDataManager { deliveryUncertaintyAlertManager = DeliveryUncertaintyAlertManager(pumpManager: pumpManager, alertPresenter: alertPresenter) analyticsServicesManager.identifyPumpType(pumpManager.pluginIdentifier) + analyticsServicesManager.identifyPumpModel(pumpManager.status.device.model) updatePumpManagerBLEHeartbeatPreference() } @@ -999,6 +1004,10 @@ extension DeviceDataManager: CGMManagerDelegate { if self.cgmHasValidSensorSession != status.hasValidSensorSession { self.cgmHasValidSensorSession = status.hasValidSensorSession } + if self.cgmDeviceModel != status.device?.model { + self.cgmDeviceModel = status.device?.model + self.analyticsServicesManager.identifyCGMModel(self.cgmDeviceModel) + } } } } @@ -1075,6 +1084,10 @@ extension DeviceDataManager: PumpManagerDelegate { doseStore.device = status.device + if status.device.model != oldStatus.device.model { + analyticsServicesManager.identifyPumpModel(status.device.model) + } + if let newBatteryValue = status.pumpBatteryChargeRemaining, let oldBatteryValue = oldStatus.pumpBatteryChargeRemaining, newBatteryValue - oldBatteryValue >= LoopConstants.batteryReplacementDetectionThreshold { diff --git a/Loop/Managers/LoopAppManager.swift b/Loop/Managers/LoopAppManager.swift index 614976e939..e1de76dd77 100644 --- a/Loop/Managers/LoopAppManager.swift +++ b/Loop/Managers/LoopAppManager.swift @@ -494,9 +494,11 @@ class LoopAppManager: NSObject { analyticsServicesManager.identifyState() if let pumpManager = deviceDataManager.pumpManager { analyticsServicesManager.identifyPumpType(pumpManager.pluginIdentifier) + analyticsServicesManager.identifyPumpModel(pumpManager.status.device.model) } if let cgmManager = deviceDataManager.cgmManager { analyticsServicesManager.identifyCGMType(cgmManager.pluginIdentifier) + analyticsServicesManager.identifyCGMModel(cgmManager.cgmManagerStatus.device?.model) } let serviceNames = servicesManager.activeServices.map { $0.pluginIdentifier } analyticsServicesManager.identify("Services", array: serviceNames)