Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

tracefile + DeviceStatus processing improvement #605

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 27 additions & 5 deletions xDrip/Managers/Nightscout/NightscoutDeviceStatusModels.swift
Original file line number Diff line number Diff line change
Expand Up @@ -141,19 +141,41 @@ struct NightscoutDeviceStatus: Codable {
}
}

// as the minimum deployment target is iOS16.2, we need to provide a nice option for people running it
// even if nearly all users will be running iOS18
func batteryImage(percent: Int?) -> (image: Image, color: Color)? {
if let percent {
switch percent {
case 0...10:
return (Image(systemName: "battery.0percent"), Color(.systemRed))
if #available(iOS 17.0, *) {
return (Image(systemName: "battery.0percent"), Color(.systemRed))
} else {
return (Image(systemName: "minus.plus.batteryblock.slash"), Color(.systemRed))
}
case 11...25:
return (Image(systemName: "battery.25percent"), Color(.systemYellow))
if #available(iOS 17.0, *) {
return (Image(systemName: "battery.25percent"), Color(.systemYellow))
} else {
return (Image(systemName: "minus.plus.batteryblock"), Color(.systemYellow))
}
case 26...65:
return (Image(systemName: "battery.50percent"), Color(.colorSecondary))
if #available(iOS 17.0, *) {
return (Image(systemName: "battery.50percent"), Color(.colorSecondary))
} else {
return (Image(systemName: "minus.plus.batteryblock"), Color(.colorSecondary))
}
case 66...90:
return (Image(systemName: "battery.75percent"), Color(.colorSecondary))
if #available(iOS 17.0, *) {
return (Image(systemName: "battery.75percent"), Color(.colorSecondary))
} else {
return (Image(systemName: "minus.plus.batteryblock.fill"), Color(.colorSecondary))
}
default:
return (Image(systemName: "battery.100percent"), Color(.colorSecondary))
if #available(iOS 17.0, *) {
return (Image(systemName: "battery.100percent"), Color(.colorSecondary))
} else {
return (Image(systemName: "minus.plus.batteryblock.fill"), Color(.colorSecondary))
}
}
}

Expand Down
10 changes: 5 additions & 5 deletions xdrip.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@
4796C6072B9516FD00DE2210 /* Bundle.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8E51D602448E695001C9E5A /* Bundle.swift */; };
47976E362BF536BA005E86EC /* AlertSnoozeStatus.swift in Sources */ = {isa = PBXBuildFile; fileRef = 47976E352BF536BA005E86EC /* AlertSnoozeStatus.swift */; };
47976E372BFBB870005E86EC /* TextsAlerts.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8B3A7B1226A0878004BA588 /* TextsAlerts.swift */; };
47A0E6442D4E263800B31C21 /* LiveActivityIntent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 47A0E6432D4E262A00B31C21 /* LiveActivityIntent.swift */; };
47A6ABE22B790CC60047A4BA /* xDripWatchApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 47A6ABE12B790CC60047A4BA /* xDripWatchApp.swift */; };
47A6ABE42B790CC60047A4BA /* MainView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 47A6ABE32B790CC60047A4BA /* MainView.swift */; };
47A6ABE62B790CC70047A4BA /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 47A6ABE52B790CC70047A4BA /* Assets.xcassets */; };
Expand Down Expand Up @@ -988,6 +989,7 @@
4798BADC27BA7965002583BC /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = tr.lproj/LibreNFC.strings; sourceTree = "<group>"; };
4798BADD27BA7996002583BC /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = tr.lproj/M5StackView.strings; sourceTree = "<group>"; };
4798BADE27BA79B8002583BC /* sv */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sv; path = sv.lproj/LaunchScreen.strings; sourceTree = "<group>"; };
47A0E6432D4E262A00B31C21 /* LiveActivityIntent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LiveActivityIntent.swift; sourceTree = "<group>"; };
47A6ABDF2B790CC60047A4BA /* xDrip Watch App.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "xDrip Watch App.app"; sourceTree = BUILT_PRODUCTS_DIR; };
47A6ABE12B790CC60047A4BA /* xDripWatchApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = xDripWatchApp.swift; sourceTree = "<group>"; };
47A6ABE32B790CC60047A4BA /* MainView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainView.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -2240,6 +2242,7 @@
E4C0061F2B3DE8C100D59303 /* AppIntents */ = {
isa = PBXGroup;
children = (
47A0E6432D4E262A00B31C21 /* LiveActivityIntent.swift */,
E4D530622B418FF80018C6A4 /* AppShortcuts.swift */,
E4C006202B3DE8EC00D59303 /* GlucoseIntent.swift */,
);
Expand Down Expand Up @@ -3938,7 +3941,7 @@
BuildIndependentTargetsInParallel = YES;
DefaultBuildSystemTypeForWorkspace = Latest;
LastSwiftUpdateCheck = 1530;
LastUpgradeCheck = 1500;
LastUpgradeCheck = 1620;
ORGANIZATIONNAME = "Johan Degraeve";
TargetAttributes = {
4716A4EC2B406C3D00419052 = {
Expand Down Expand Up @@ -4509,6 +4512,7 @@
4752B4062635878E0081D551 /* SettingsViewStatisticsSettingsViewModel.swift in Sources */,
F897AAF92200F2D200CDDD10 /* CBPeripheralState.swift in Sources */,
F858CCED25AE4CD100786B91 /* LibreOOPWebError.swift in Sources */,
47A0E6442D4E263800B31C21 /* LiveActivityIntent.swift in Sources */,
F85542362B7575B40058CE09 /* OmniPodHeartBeatBluetoothPeripheralViewModel.swift in Sources */,
F8EE3EAC2B6834FD00B27B96 /* Libre2HeartBeat+CoreDataClass.swift in Sources */,
D4FD899727772F9100689788 /* TreatmentEntryAccessor.swift in Sources */,
Expand Down Expand Up @@ -5595,7 +5599,6 @@
47A6ABEA2B790CC70047A4BA /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
APP_GROUP_IDENTIFIER = "group.com.${DEVELOPMENT_TEAM}.loopkit.LoopGroup";
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
Expand Down Expand Up @@ -5636,7 +5639,6 @@
47A6ABEB2B790CC70047A4BA /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
APP_GROUP_IDENTIFIER = "group.com.${DEVELOPMENT_TEAM}.loopkit.LoopGroup";
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
Expand Down Expand Up @@ -5874,7 +5876,6 @@
F8AC426F21ADEBD70078C348 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
APP_GROUP_IDENTIFIER = "group.com.${DEVELOPMENT_TEAM}.loopkit.LoopGroup";
APP_GROUP_IDENTIFIER_TRIO = "group.org.nightscout.${DEVELOPMENT_TEAM}.trio.trio-app-group";
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
Expand Down Expand Up @@ -5904,7 +5905,6 @@
F8AC427021ADEBD70078C348 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
APP_GROUP_IDENTIFIER = "group.com.${DEVELOPMENT_TEAM}.loopkit.LoopGroup";
APP_GROUP_IDENTIFIER_TRIO = "group.org.nightscout.${DEVELOPMENT_TEAM}.trio.trio-app-group";
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1520"
LastUpgradeVersion = "1620"
version = "1.7">
<BuildAction
parallelizeBuildables = "YES"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1520"
LastUpgradeVersion = "1620"
wasCreatedForAppExtension = "YES"
version = "2.0">
<BuildAction
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1520"
LastUpgradeVersion = "1620"
wasCreatedForAppExtension = "YES"
version = "2.0">
<BuildAction
Expand Down Expand Up @@ -56,16 +56,26 @@
debugServiceExtension = "internal"
allowLocationSimulation = "YES"
launchAutomaticallySubstyle = "2">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<RemoteRunnable
runnableDebuggingMode = "2"
BundleIdentifier = "com.apple.springboard">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "4716A4EC2B406C3D00419052"
BuildableName = "xDrip Widget Extension.appex"
BlueprintName = "xDrip Widget Extension"
ReferencedContainer = "container:xdrip.xcodeproj">
</BuildableReference>
</RemoteRunnable>
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "F8AC425921ADEBD60078C348"
BuildableName = "xdrip.app"
BlueprintName = "xdrip"
ReferencedContainer = "container:xdrip.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</MacroExpansion>
<EnvironmentVariables>
<EnvironmentVariable
key = "_XCWidgetKind"
Expand Down
2 changes: 1 addition & 1 deletion xdrip.xcodeproj/xcshareddata/xcschemes/xdrip.xcscheme
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1500"
LastUpgradeVersion = "1620"
version = "1.7">
<BuildAction
parallelizeBuildables = "YES"
Expand Down
20 changes: 12 additions & 8 deletions xdrip/BluetoothTransmitter/CGM/Dexcom/G7/CGMG7Transmitter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -179,10 +179,6 @@ class CGMG7Transmitter: BluetoothTransmitter, CGMTransmitter {
return
}

trace("in peripheralDidUpdateValueFor, characteristic uuid = %{public}@", log: log, category: ConstantsLog.categoryCGMG7, type: .info, characteristic_UUID.description)

trace("in peripheralDidUpdateValueFor, data = %{public}@", log: log, category: ConstantsLog.categoryCGMG7, type: .info, value.hexEncodedString())

if let error = error {
trace("error: %{public}@", log: log, category: ConstantsLog.categoryCGMG7, type: .error , error.localizedDescription)
}
Expand Down Expand Up @@ -210,6 +206,10 @@ class CGMG7Transmitter: BluetoothTransmitter, CGMTransmitter {
return
}

trace("in peripheralDidUpdateValueFor, characteristic uuid = %{public}@", log: log, category: ConstantsLog.categoryCGMG7, type: .info, characteristic_UUID.description)

trace("in peripheralDidUpdateValueFor, data = %{public}@", log: log, category: ConstantsLog.categoryCGMG7, type: .info, value.hexEncodedString())

let sensorAgeInDays = Double(round((g7GlucoseMessage.sensorAge / 3600 / 24) * 10) / 10)

var maxSensorAgeInDays: Double = 0.0
Expand Down Expand Up @@ -269,11 +269,11 @@ class CGMG7Transmitter: BluetoothTransmitter, CGMTransmitter {
break

case .CBUUID_Backfill:
guard value.count == 9 else { return }

guard value.count == 9 else {
trace(" value.count != 9, no procesing", log: log, category: ConstantsLog.categoryCGMG7, type: .info )
return
}
trace("in peripheralDidUpdateValueFor, characteristic uuid = %{public}@", log: log, category: ConstantsLog.categoryCGMG7, type: .info, characteristic_UUID.description)

trace("in peripheralDidUpdateValueFor, data = %{public}@", log: log, category: ConstantsLog.categoryCGMG7, type: .info, value.hexEncodedString())

if let sensorAge = sensorAge, sensorAge < (ConstantsDexcomG7.maxSensorAgeInDays * 24 * 3600), let dexcomG7BackfillMessage = DexcomG7BackfillMessage(data: value, sensorAge: sensorAge) {
trace(" received backfill mesage, calculatedValue = %{public}@, timeStamp = %{public}@", log: log, category: ConstantsLog.categoryCGMG7, type: .info, dexcomG7BackfillMessage.calculatedValue.description, dexcomG7BackfillMessage.timeStamp.description(with: .current))
Expand All @@ -283,6 +283,10 @@ class CGMG7Transmitter: BluetoothTransmitter, CGMTransmitter {

case .CBUUID_Receive_Authentication:

trace("in peripheralDidUpdateValueFor, characteristic uuid = %{public}@", log: log, category: ConstantsLog.categoryCGMG7, type: .info, characteristic_UUID.description)

trace("in peripheralDidUpdateValueFor, data = %{public}@", log: log, category: ConstantsLog.categoryCGMG7, type: .info, value.hexEncodedString())

if let authChallengeRxMessage = AuthChallengeRxMessage(data: value) {

if authChallengeRxMessage.paired, authChallengeRxMessage.authenticated {
Expand Down
66 changes: 36 additions & 30 deletions xdrip/GlucoseIntent.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,40 +6,33 @@
// Copyright © 2023 Johan Degraeve. All rights reserved.
//

#if canImport(AppIntents)
import AppIntents
#endif
import Foundation
import SwiftUI

struct GlucoseIntent: AppIntent {
static var title: LocalizedStringResource = "What's my glucose level"
static var description = IntentDescription("Ask to read out your blood glucose level.", categoryName: "Information")

static var authenticationPolicy = IntentAuthenticationPolicy.alwaysAllowed

static var title: LocalizedStringResource {
"What's my glucose level"
}

static var description: IntentDescription? {
IntentDescription("Ask to read out your blood glucose level.", categoryName: "Information")
}

@MainActor
func perform() async throws -> some ReturnsValue<Double> & ProvidesDialog & ShowsSnippetView {
let coreDataManager = await CoreDataManager.create(for: ConstantsCoreData.modelName)

let bgReadingsAccessor = BgReadingsAccessor(coreDataManager: coreDataManager)

let bgReadings = bgReadingsAccessor.getLatestBgReadings(limit: nil, fromDate: Date(timeIntervalSinceNow: -14400), forSensor: nil, ignoreRawData: true, ignoreCalculatedValue: false).sorted { $0.timeStamp < $1.timeStamp }

guard let mostRecent = bgReadings.last else {
guard let latestBgReading = bgReadings.last else {
throw IntentError.message("No glucose data")
}

let value = mostRecent.calculatedValue.mgDlToMmol(mgDl: UserDefaults.standard.bloodGlucoseUnitIsMgDl) // UserDefaults.standard.bloodGlucoseUnitIsMgDl ? mostRecent.calculatedValue : (mostRecent.calculatedValue * ConstantsBloodGlucose.mgDlToMmoll)
let isMgDl = UserDefaults.standard.bloodGlucoseUnitIsMgDl

let valueString = value.bgValueToString(mgDl: UserDefaults.standard.bloodGlucoseUnitIsMgDl) // UserDefaults.standard.bloodGlucoseUnitIsMgDl ? value.formatted(.number.precision(.fractionLength(0))) : value.formatted(.number.precision(.fractionLength(1)))
let value = latestBgReading.calculatedValue.mgDlToMmol(mgDl: isMgDl)
let valueString = value.bgValueToString(mgDl: isMgDl)

let trendDescription: LocalizedStringResource = switch mostRecent.slopeTrend() {
let trendDescription: LocalizedStringResource = switch latestBgReading.slopeTrend() {
case .droppingFast: "dropping fast"
case .dropping: "dropping"
case .slowlyDropping: "slowly dropping"
Expand All @@ -58,9 +51,7 @@ struct GlucoseIntent: AppIntent {
}

var dialogString: IntentDialog = "Your blood glucose is currently \(valueString) and \(trendDescription)"

let minutesAgo = (bgReadingDates.last?.timeIntervalSinceNow ?? 999 ) / 60

let minutesAgo = (bgReadingDates.last?.timeIntervalSinceNow ?? .greatestFiniteMagnitude) / 60
let minutesAgoString = abs(Int(minutesAgo))

if minutesAgo < -30 {
Expand All @@ -72,7 +63,22 @@ struct GlucoseIntent: AppIntent {
return .result(
value: value,
dialog: dialogString,
view: GlucoseChartView(glucoseChartType: .siriGlucoseIntent, bgReadingValues: bgReadingValues, bgReadingDates: bgReadingDates, isMgDl: UserDefaults.standard.bloodGlucoseUnitIsMgDl, urgentLowLimitInMgDl: UserDefaults.standard.urgentLowMarkValue, lowLimitInMgDl: UserDefaults.standard.lowMarkValue, highLimitInMgDl: UserDefaults.standard.highMarkValue, urgentHighLimitInMgDl: UserDefaults.standard.urgentHighMarkValue, liveActivityType: nil, hoursToShowScalingHours: nil, glucoseCircleDiameterScalingHours: nil, overrideChartHeight: nil, overrideChartWidth: nil, highContrast: nil)
view: GlucoseChartView(
glucoseChartType: .siriGlucoseIntent,
bgReadingValues: bgReadingValues,
bgReadingDates: bgReadingDates,
isMgDl: isMgDl,
urgentLowLimitInMgDl: UserDefaults.standard.urgentLowMarkValue,
lowLimitInMgDl: UserDefaults.standard.lowMarkValue,
highLimitInMgDl: UserDefaults.standard.highMarkValue,
urgentHighLimitInMgDl: UserDefaults.standard.urgentHighMarkValue,
liveActivityType: nil,
hoursToShowScalingHours: nil,
glucoseCircleDiameterScalingHours: nil,
overrideChartHeight: nil,
overrideChartWidth: nil,
highContrast: nil
)
)
}
}
Expand Down Expand Up @@ -115,19 +121,19 @@ private extension BgReading {
func slopeTrend() -> Trend {
switch calculatedValueSlope * 60000 {
case ..<(-2):
.droppingFast
case -2 ..< -1:
.dropping
case -1 ..< -0.5:
.slowlyDropping
.droppingFast
case -2 ..< -1:
.dropping
case -1 ..< -0.5:
.slowlyDropping
case -0.5 ..< 0.5:
.stable
case 0.5 ..< 1:
.slowlyRising
.stable
case 0.5 ..< 1:
.slowlyRising
case 1 ..< 2:
.rising
default:
.risingFast
.rising
default:
.risingFast
}
}
}
28 changes: 28 additions & 0 deletions xdrip/LiveActivityIntent.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
//
// LiveActivityIntent.swift
// xdrip
//
// Created by Marian Dugaesescu on 13/10/2024 / Edited by Paul Plant on 06/02/2025.
// Copyright © 2024 Johan Degraeve. All rights reserved.
//

import AppIntents
import Foundation

// https://developer.apple.com/documentation/activitykit/displaying-live-data-with-live-activities#Start-and-stop-Live-Activities-from-App-Intents
// https://developer.apple.com/documentation/appintents/liveactivityintent

/// App Intent used to restart the live activities via Apple Shortcuts automation
/// The user needs to add this as an Automation (for somebody who never opens the app, one automation set every 6 hours is needed,
/// although just one set at 03hrs could be enough for most people to ensure the live activity isn't cancelled during the night)
struct RestartLiveActivityIntent: LiveActivityIntent {
static var title: LocalizedStringResource = "Restart Live Activity"
static var description = IntentDescription("Restarts the glucose monitoring live activity.", categoryName: "Live Activity")

@MainActor
func perform() async throws -> some IntentResult {
// restart the live activity via the LiveActivityManager singleton
LiveActivityManager.shared.restartActivityFromLiveActivityIntent()
return .result()
}
}
Loading