@@ -10,17 +10,6 @@ |
||
| 10 | 10 |
3407A133FADB8858DC2A1FED /* MeterNameStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7396C8BB36F4E7F8E0CD8FF8 /* MeterNameStore.swift */; };
|
| 11 | 11 |
4308CF8624176CAB0002E80B /* DataGroupRowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4308CF8524176CAB0002E80B /* DataGroupRowView.swift */; };
|
| 12 | 12 |
4308CF882417770D0002E80B /* DataGroupsSheetView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4308CF872417770D0002E80B /* DataGroupsSheetView.swift */; };
|
| 13 |
- C10000013C8E4A7A00A10001 /* ChargeInsightsModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = C10000113C8E4A7A00A10011 /* ChargeInsightsModel.swift */; };
|
|
| 14 |
- C10000023C8E4A7A00A10002 /* ChargeInsightsStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = C10000123C8E4A7A00A10012 /* ChargeInsightsStore.swift */; };
|
|
| 15 |
- C10000033C8E4A7A00A10003 /* ChargedDeviceQRCodeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C10000133C8E4A7A00A10013 /* ChargedDeviceQRCodeView.swift */; };
|
|
| 16 |
- C10000043C8E4A7A00A10004 /* ChargedDeviceEditorSheetView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C10000143C8E4A7A00A10014 /* ChargedDeviceEditorSheetView.swift */; };
|
|
| 17 |
- C10000053C8E4A7A00A10005 /* ChargedDeviceLibrarySheetView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C10000153C8E4A7A00A10015 /* ChargedDeviceLibrarySheetView.swift */; };
|
|
| 18 |
- C10000063C8E4A7A00A10006 /* ChargedDeviceDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C10000163C8E4A7A00A10016 /* ChargedDeviceDetailView.swift */; };
|
|
| 19 |
- C10000073C8E4A7A00A10007 /* SidebarChargedDevicesSectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C10000173C8E4A7A00A10017 /* SidebarChargedDevicesSectionView.swift */; };
|
|
| 20 |
- C10000083C8E4A7A00A10008 /* BatteryCheckpointEditorSheetView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C10000183C8E4A7A00A10018 /* BatteryCheckpointEditorSheetView.swift */; };
|
|
| 21 |
- C10000093C8E4A7A00A10009 /* CKModel.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = C10000213C8E4A7A00A10021 /* CKModel.xcdatamodeld */; };
|
|
| 22 |
- B0A000113C8F000100A10011 /* ChargerStandbyPowerStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0A000013C8F000100A10001 /* ChargerStandbyPowerStore.swift */; };
|
|
| 23 |
- B0A000123C8F000100A10012 /* ChargerStandbyPowerWizardView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0A000023C8F000100A10002 /* ChargerStandbyPowerWizardView.swift */; };
|
|
| 24 | 13 |
430CB4FC245E07EB006525C2 /* ChevronView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 430CB4FB245E07EB006525C2 /* ChevronView.swift */; };
|
| 25 | 14 |
4311E63A241384960080EA59 /* DeviceHelpView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4311E639241384960080EA59 /* DeviceHelpView.swift */; };
|
| 26 | 15 |
4327461B24619CED0009BE4B /* MeterRowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4327461A24619CED0009BE4B /* MeterRowView.swift */; };
|
@@ -62,12 +51,21 @@ |
||
| 62 | 51 |
43F7792B2465AE1600745DF4 /* UIView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43F7792A2465AE1600745DF4 /* UIView.swift */; };
|
| 63 | 52 |
AAD5F9A72B1CAC0700F8E4F9 /* MeterDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAD5F9A32B1CAC0700F8E4F9 /* MeterDetailView.swift */; };
|
| 64 | 53 |
AAD5F9B12B1CAC7A00F8E4F9 /* SidebarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAD5F9B22B1CAC7A00F8E4F9 /* SidebarView.swift */; };
|
| 54 |
+ B0A000113C8F000100A10011 /* ChargerStandbyPowerStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0A000013C8F000100A10001 /* ChargerStandbyPowerStore.swift */; };
|
|
| 55 |
+ B0A000123C8F000100A10012 /* ChargerStandbyPowerWizardView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0A000023C8F000100A10002 /* ChargerStandbyPowerWizardView.swift */; };
|
|
| 56 |
+ C10000013C8E4A7A00A10001 /* ChargeInsightsModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = C10000113C8E4A7A00A10011 /* ChargeInsightsModel.swift */; };
|
|
| 57 |
+ C10000023C8E4A7A00A10002 /* ChargeInsightsStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = C10000123C8E4A7A00A10012 /* ChargeInsightsStore.swift */; };
|
|
| 58 |
+ C10000033C8E4A7A00A10003 /* ChargedDeviceQRCodeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C10000133C8E4A7A00A10013 /* ChargedDeviceQRCodeView.swift */; };
|
|
| 59 |
+ C10000043C8E4A7A00A10004 /* ChargedDeviceEditorSheetView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C10000143C8E4A7A00A10014 /* ChargedDeviceEditorSheetView.swift */; };
|
|
| 60 |
+ C10000053C8E4A7A00A10005 /* ChargedDeviceLibrarySheetView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C10000153C8E4A7A00A10015 /* ChargedDeviceLibrarySheetView.swift */; };
|
|
| 61 |
+ C10000063C8E4A7A00A10006 /* ChargedDeviceDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C10000163C8E4A7A00A10016 /* ChargedDeviceDetailView.swift */; };
|
|
| 62 |
+ C10000073C8E4A7A00A10007 /* SidebarChargedDevicesSectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C10000173C8E4A7A00A10017 /* SidebarChargedDevicesSectionView.swift */; };
|
|
| 63 |
+ C10000083C8E4A7A00A10008 /* BatteryCheckpointEditorSheetView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C10000183C8E4A7A00A10018 /* BatteryCheckpointEditorSheetView.swift */; };
|
|
| 64 |
+ C10000093C8E4A7A00A10009 /* CKModel.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = C10000213C8E4A7A00A10021 /* CKModel.xcdatamodeld */; };
|
|
| 65 | 65 |
D28F11013C8E4A7A00A10011 /* MeterHomeTabView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D28F11023C8E4A7A00A10012 /* MeterHomeTabView.swift */; };
|
| 66 | 66 |
D28F11033C8E4A7A00A10013 /* MeterLiveTabView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D28F11043C8E4A7A00A10014 /* MeterLiveTabView.swift */; };
|
| 67 | 67 |
D28F11053C8E4A7A00A10015 /* MeterChartTabView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D28F11063C8E4A7A00A10016 /* MeterChartTabView.swift */; };
|
| 68 | 68 |
D28F11073C8E4A7A00A10017 /* MeterSettingsTabView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D28F11083C8E4A7A00A10018 /* MeterSettingsTabView.swift */; };
|
| 69 |
- D28F11413C8E4A7A00A10051 /* MeterChargeRecordTabView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D28F11423C8E4A7A00A10052 /* MeterChargeRecordTabView.swift */; };
|
|
| 70 |
- D28F11433C8E4A7A00A10053 /* MeterDataGroupsTabView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D28F11443C8E4A7A00A10054 /* MeterDataGroupsTabView.swift */; };
|
|
| 71 | 69 |
D28F11113C8E4A7A00A10021 /* MeterInfoCardView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D28F11123C8E4A7A00A10022 /* MeterInfoCardView.swift */; };
|
| 72 | 70 |
D28F11133C8E4A7A00A10023 /* MeterInfoRowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D28F11143C8E4A7A00A10024 /* MeterInfoRowView.swift */; };
|
| 73 | 71 |
D28F11153C8E4A7A00A10025 /* MeterNameEditorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D28F11163C8E4A7A00A10026 /* MeterNameEditorView.swift */; };
|
@@ -81,6 +79,8 @@ |
||
| 81 | 79 |
D28F11393C8E4A7A00A10049 /* MeterConnectionStatusBadgeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D28F113A3C8E4A7A00A1004A /* MeterConnectionStatusBadgeView.swift */; };
|
| 82 | 80 |
D28F113B3C8E4A7A00A1004B /* MeterConnectionActionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D28F113C3C8E4A7A00A1004C /* MeterConnectionActionView.swift */; };
|
| 83 | 81 |
D28F113D3C8E4A7A00A1004D /* MeterOverviewSectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D28F113E3C8E4A7A00A1004E /* MeterOverviewSectionView.swift */; };
|
| 82 |
+ D28F11413C8E4A7A00A10051 /* MeterChargeRecordTabView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D28F11423C8E4A7A00A10052 /* MeterChargeRecordTabView.swift */; };
|
|
| 83 |
+ D28F11433C8E4A7A00A10053 /* MeterDataGroupsTabView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D28F11443C8E4A7A00A10054 /* MeterDataGroupsTabView.swift */; };
|
|
| 84 | 84 |
E430FB6B7CB3E0D4189F6D7D /* MeterMappingDebugView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56BA4CE53B6B2C4EBA42C81A /* MeterMappingDebugView.swift */; };
|
| 85 | 85 |
/* End PBXBuildFile section */ |
| 86 | 86 |
|
@@ -118,23 +118,6 @@ |
||
| 118 | 118 |
1C6B6BB42A2D4F5100A0B001 /* HM-10 and DX-BT18 Module Working Summary.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = "HM-10 and DX-BT18 Module Working Summary.md"; sourceTree = "<group>"; };
|
| 119 | 119 |
4308CF8524176CAB0002E80B /* DataGroupRowView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataGroupRowView.swift; sourceTree = "<group>"; };
|
| 120 | 120 |
4308CF872417770D0002E80B /* DataGroupsSheetView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataGroupsSheetView.swift; sourceTree = "<group>"; };
|
| 121 |
- C10000113C8E4A7A00A10011 /* ChargeInsightsModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChargeInsightsModel.swift; sourceTree = "<group>"; };
|
|
| 122 |
- C10000123C8E4A7A00A10012 /* ChargeInsightsStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChargeInsightsStore.swift; sourceTree = "<group>"; };
|
|
| 123 |
- C10000133C8E4A7A00A10013 /* ChargedDeviceQRCodeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChargedDeviceQRCodeView.swift; sourceTree = "<group>"; };
|
|
| 124 |
- C10000143C8E4A7A00A10014 /* ChargedDeviceEditorSheetView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChargedDeviceEditorSheetView.swift; sourceTree = "<group>"; };
|
|
| 125 |
- C10000153C8E4A7A00A10015 /* ChargedDeviceLibrarySheetView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChargedDeviceLibrarySheetView.swift; sourceTree = "<group>"; };
|
|
| 126 |
- C10000163C8E4A7A00A10016 /* ChargedDeviceDetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChargedDeviceDetailView.swift; sourceTree = "<group>"; };
|
|
| 127 |
- C10000173C8E4A7A00A10017 /* SidebarChargedDevicesSectionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SidebarChargedDevicesSectionView.swift; sourceTree = "<group>"; };
|
|
| 128 |
- C10000183C8E4A7A00A10018 /* BatteryCheckpointEditorSheetView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BatteryCheckpointEditorSheetView.swift; sourceTree = "<group>"; };
|
|
| 129 |
- C10000193C8E4A7A00A10019 /* USB_Meter 4.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "USB_Meter 4.xcdatamodel"; sourceTree = "<group>"; };
|
|
| 130 |
- C100001A3C8E4A7A00A1001A /* USB_Meter 5.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "USB_Meter 5.xcdatamodel"; sourceTree = "<group>"; };
|
|
| 131 |
- C100001B3C8E4A7A00A1001B /* USB_Meter 6.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "USB_Meter 6.xcdatamodel"; sourceTree = "<group>"; };
|
|
| 132 |
- C100001C3C8E4A7A00A1001C /* USB_Meter 7.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "USB_Meter 7.xcdatamodel"; sourceTree = "<group>"; };
|
|
| 133 |
- C100001D3C8E4A7A00A1001D /* USB_Meter 8.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "USB_Meter 8.xcdatamodel"; sourceTree = "<group>"; };
|
|
| 134 |
- C100001E3C8E4A7A00A1001E /* USB_Meter 9.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "USB_Meter 9.xcdatamodel"; sourceTree = "<group>"; };
|
|
| 135 |
- C10000223C8E4A7A00A10022 /* USB_Meter 10.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "USB_Meter 10.xcdatamodel"; sourceTree = "<group>"; };
|
|
| 136 |
- B0A000013C8F000100A10001 /* ChargerStandbyPowerStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChargerStandbyPowerStore.swift; sourceTree = "<group>"; };
|
|
| 137 |
- B0A000023C8F000100A10002 /* ChargerStandbyPowerWizardView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChargerStandbyPowerWizardView.swift; sourceTree = "<group>"; };
|
|
| 138 | 121 |
430CB4FB245E07EB006525C2 /* ChevronView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChevronView.swift; sourceTree = "<group>"; };
|
| 139 | 122 |
4311E639241384960080EA59 /* DeviceHelpView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeviceHelpView.swift; sourceTree = "<group>"; };
|
| 140 | 123 |
4327461A24619CED0009BE4B /* MeterRowView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MeterRowView.swift; sourceTree = "<group>"; };
|
@@ -180,12 +163,27 @@ |
||
| 180 | 163 |
7396C8BB36F4E7F8E0CD8FF8 /* MeterNameStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MeterNameStore.swift; sourceTree = "<group>"; };
|
| 181 | 164 |
AAD5F9A32B1CAC0700F8E4F9 /* MeterDetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MeterDetailView.swift; sourceTree = "<group>"; };
|
| 182 | 165 |
AAD5F9B22B1CAC7A00F8E4F9 /* SidebarView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SidebarView.swift; sourceTree = "<group>"; };
|
| 166 |
+ B0A000013C8F000100A10001 /* ChargerStandbyPowerStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChargerStandbyPowerStore.swift; sourceTree = "<group>"; };
|
|
| 167 |
+ B0A000023C8F000100A10002 /* ChargerStandbyPowerWizardView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChargerStandbyPowerWizardView.swift; sourceTree = "<group>"; };
|
|
| 168 |
+ C10000113C8E4A7A00A10011 /* ChargeInsightsModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChargeInsightsModel.swift; sourceTree = "<group>"; };
|
|
| 169 |
+ C10000123C8E4A7A00A10012 /* ChargeInsightsStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChargeInsightsStore.swift; sourceTree = "<group>"; };
|
|
| 170 |
+ C10000133C8E4A7A00A10013 /* ChargedDeviceQRCodeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChargedDeviceQRCodeView.swift; sourceTree = "<group>"; };
|
|
| 171 |
+ C10000143C8E4A7A00A10014 /* ChargedDeviceEditorSheetView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChargedDeviceEditorSheetView.swift; sourceTree = "<group>"; };
|
|
| 172 |
+ C10000153C8E4A7A00A10015 /* ChargedDeviceLibrarySheetView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChargedDeviceLibrarySheetView.swift; sourceTree = "<group>"; };
|
|
| 173 |
+ C10000163C8E4A7A00A10016 /* ChargedDeviceDetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChargedDeviceDetailView.swift; sourceTree = "<group>"; };
|
|
| 174 |
+ C10000173C8E4A7A00A10017 /* SidebarChargedDevicesSectionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SidebarChargedDevicesSectionView.swift; sourceTree = "<group>"; };
|
|
| 175 |
+ C10000183C8E4A7A00A10018 /* BatteryCheckpointEditorSheetView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BatteryCheckpointEditorSheetView.swift; sourceTree = "<group>"; };
|
|
| 176 |
+ C10000193C8E4A7A00A10019 /* USB_Meter 4.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "USB_Meter 4.xcdatamodel"; sourceTree = "<group>"; };
|
|
| 177 |
+ C100001A3C8E4A7A00A1001A /* USB_Meter 5.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "USB_Meter 5.xcdatamodel"; sourceTree = "<group>"; };
|
|
| 178 |
+ C100001B3C8E4A7A00A1001B /* USB_Meter 6.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "USB_Meter 6.xcdatamodel"; sourceTree = "<group>"; };
|
|
| 179 |
+ C100001C3C8E4A7A00A1001C /* USB_Meter 7.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "USB_Meter 7.xcdatamodel"; sourceTree = "<group>"; };
|
|
| 180 |
+ C100001D3C8E4A7A00A1001D /* USB_Meter 8.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "USB_Meter 8.xcdatamodel"; sourceTree = "<group>"; };
|
|
| 181 |
+ C100001E3C8E4A7A00A1001E /* USB_Meter 9.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "USB_Meter 9.xcdatamodel"; sourceTree = "<group>"; };
|
|
| 182 |
+ C10000223C8E4A7A00A10022 /* USB_Meter 10.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "USB_Meter 10.xcdatamodel"; sourceTree = "<group>"; };
|
|
| 183 | 183 |
D28F11023C8E4A7A00A10012 /* MeterHomeTabView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MeterHomeTabView.swift; sourceTree = "<group>"; };
|
| 184 | 184 |
D28F11043C8E4A7A00A10014 /* MeterLiveTabView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MeterLiveTabView.swift; sourceTree = "<group>"; };
|
| 185 | 185 |
D28F11063C8E4A7A00A10016 /* MeterChartTabView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MeterChartTabView.swift; sourceTree = "<group>"; };
|
| 186 | 186 |
D28F11083C8E4A7A00A10018 /* MeterSettingsTabView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MeterSettingsTabView.swift; sourceTree = "<group>"; };
|
| 187 |
- D28F11423C8E4A7A00A10052 /* MeterChargeRecordTabView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MeterChargeRecordTabView.swift; sourceTree = "<group>"; };
|
|
| 188 |
- D28F11443C8E4A7A00A10054 /* MeterDataGroupsTabView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MeterDataGroupsTabView.swift; sourceTree = "<group>"; };
|
|
| 189 | 187 |
D28F11123C8E4A7A00A10022 /* MeterInfoCardView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MeterInfoCardView.swift; sourceTree = "<group>"; };
|
| 190 | 188 |
D28F11143C8E4A7A00A10024 /* MeterInfoRowView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MeterInfoRowView.swift; sourceTree = "<group>"; };
|
| 191 | 189 |
D28F11163C8E4A7A00A10026 /* MeterNameEditorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MeterNameEditorView.swift; sourceTree = "<group>"; };
|
@@ -199,6 +197,8 @@ |
||
| 199 | 197 |
D28F113A3C8E4A7A00A1004A /* MeterConnectionStatusBadgeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MeterConnectionStatusBadgeView.swift; sourceTree = "<group>"; };
|
| 200 | 198 |
D28F113C3C8E4A7A00A1004C /* MeterConnectionActionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MeterConnectionActionView.swift; sourceTree = "<group>"; };
|
| 201 | 199 |
D28F113E3C8E4A7A00A1004E /* MeterOverviewSectionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MeterOverviewSectionView.swift; sourceTree = "<group>"; };
|
| 200 |
+ D28F11423C8E4A7A00A10052 /* MeterChargeRecordTabView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MeterChargeRecordTabView.swift; sourceTree = "<group>"; };
|
|
| 201 |
+ D28F11443C8E4A7A00A10054 /* MeterDataGroupsTabView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MeterDataGroupsTabView.swift; sourceTree = "<group>"; };
|
|
| 202 | 202 |
/* End PBXFileReference section */ |
| 203 | 203 |
|
| 204 | 204 |
/* Begin PBXFileSystemSynchronizedRootGroup section */ |
@@ -454,19 +454,6 @@ |
||
| 454 | 454 |
path = Views; |
| 455 | 455 |
sourceTree = "<group>"; |
| 456 | 456 |
}; |
| 457 |
- C10000203C8E4A7A00A10020 /* ChargedDevices */ = {
|
|
| 458 |
- isa = PBXGroup; |
|
| 459 |
- children = ( |
|
| 460 |
- C10000163C8E4A7A00A10016 /* ChargedDeviceDetailView.swift */, |
|
| 461 |
- C10000143C8E4A7A00A10014 /* ChargedDeviceEditorSheetView.swift */, |
|
| 462 |
- C10000153C8E4A7A00A10015 /* ChargedDeviceLibrarySheetView.swift */, |
|
| 463 |
- C10000133C8E4A7A00A10013 /* ChargedDeviceQRCodeView.swift */, |
|
| 464 |
- C10000183C8E4A7A00A10018 /* BatteryCheckpointEditorSheetView.swift */, |
|
| 465 |
- C10000173C8E4A7A00A10017 /* SidebarChargedDevicesSectionView.swift */, |
|
| 466 |
- ); |
|
| 467 |
- path = ChargedDevices; |
|
| 468 |
- sourceTree = "<group>"; |
|
| 469 |
- }; |
|
| 470 | 457 |
43CBF67F240D14AC00255B8B /* Extensions */ = {
|
| 471 | 458 |
isa = PBXGroup; |
| 472 | 459 |
children = ( |
@@ -492,6 +479,19 @@ |
||
| 492 | 479 |
path = Sidebar; |
| 493 | 480 |
sourceTree = "<group>"; |
| 494 | 481 |
}; |
| 482 |
+ C10000203C8E4A7A00A10020 /* ChargedDevices */ = {
|
|
| 483 |
+ isa = PBXGroup; |
|
| 484 |
+ children = ( |
|
| 485 |
+ C10000163C8E4A7A00A10016 /* ChargedDeviceDetailView.swift */, |
|
| 486 |
+ C10000143C8E4A7A00A10014 /* ChargedDeviceEditorSheetView.swift */, |
|
| 487 |
+ C10000153C8E4A7A00A10015 /* ChargedDeviceLibrarySheetView.swift */, |
|
| 488 |
+ C10000133C8E4A7A00A10013 /* ChargedDeviceQRCodeView.swift */, |
|
| 489 |
+ C10000183C8E4A7A00A10018 /* BatteryCheckpointEditorSheetView.swift */, |
|
| 490 |
+ C10000173C8E4A7A00A10017 /* SidebarChargedDevicesSectionView.swift */, |
|
| 491 |
+ ); |
|
| 492 |
+ path = ChargedDevices; |
|
| 493 |
+ sourceTree = "<group>"; |
|
| 494 |
+ }; |
|
| 495 | 495 |
D28F10013C8E4A7A00A10001 /* Sheets */ = {
|
| 496 | 496 |
isa = PBXGroup; |
| 497 | 497 |
children = ( |
@@ -568,22 +568,6 @@ |
||
| 568 | 568 |
path = Settings; |
| 569 | 569 |
sourceTree = "<group>"; |
| 570 | 570 |
}; |
| 571 |
- D28F11453C8E4A7A00A10055 /* ChargeRecord */ = {
|
|
| 572 |
- isa = PBXGroup; |
|
| 573 |
- children = ( |
|
| 574 |
- D28F11423C8E4A7A00A10052 /* MeterChargeRecordTabView.swift */, |
|
| 575 |
- ); |
|
| 576 |
- path = ChargeRecord; |
|
| 577 |
- sourceTree = "<group>"; |
|
| 578 |
- }; |
|
| 579 |
- D28F11463C8E4A7A00A10056 /* DataGroups */ = {
|
|
| 580 |
- isa = PBXGroup; |
|
| 581 |
- children = ( |
|
| 582 |
- D28F11443C8E4A7A00A10054 /* MeterDataGroupsTabView.swift */, |
|
| 583 |
- ); |
|
| 584 |
- path = DataGroups; |
|
| 585 |
- sourceTree = "<group>"; |
|
| 586 |
- }; |
|
| 587 | 571 |
D28F111B3C8E4A7A00A1002B /* Subviews */ = {
|
| 588 | 572 |
isa = PBXGroup; |
| 589 | 573 |
children = ( |
@@ -652,6 +636,22 @@ |
||
| 652 | 636 |
path = Components; |
| 653 | 637 |
sourceTree = "<group>"; |
| 654 | 638 |
}; |
| 639 |
+ D28F11453C8E4A7A00A10055 /* ChargeRecord */ = {
|
|
| 640 |
+ isa = PBXGroup; |
|
| 641 |
+ children = ( |
|
| 642 |
+ D28F11423C8E4A7A00A10052 /* MeterChargeRecordTabView.swift */, |
|
| 643 |
+ ); |
|
| 644 |
+ path = ChargeRecord; |
|
| 645 |
+ sourceTree = "<group>"; |
|
| 646 |
+ }; |
|
| 647 |
+ D28F11463C8E4A7A00A10056 /* DataGroups */ = {
|
|
| 648 |
+ isa = PBXGroup; |
|
| 649 |
+ children = ( |
|
| 650 |
+ D28F11443C8E4A7A00A10054 /* MeterDataGroupsTabView.swift */, |
|
| 651 |
+ ); |
|
| 652 |
+ path = DataGroups; |
|
| 653 |
+ sourceTree = "<group>"; |
|
| 654 |
+ }; |
|
| 655 | 655 |
/* End PBXGroup section */ |
| 656 | 656 |
|
| 657 | 657 |
/* Begin PBXNativeTarget section */ |
@@ -828,25 +828,6 @@ |
||
| 828 | 828 |
}; |
| 829 | 829 |
/* End PBXVariantGroup section */ |
| 830 | 830 |
|
| 831 |
-/* Begin XCVersionGroup section */ |
|
| 832 |
- C10000213C8E4A7A00A10021 /* CKModel.xcdatamodeld */ = {
|
|
| 833 |
- isa = XCVersionGroup; |
|
| 834 |
- children = ( |
|
| 835 |
- C10000193C8E4A7A00A10019 /* USB_Meter 4.xcdatamodel */, |
|
| 836 |
- C100001A3C8E4A7A00A1001A /* USB_Meter 5.xcdatamodel */, |
|
| 837 |
- C100001B3C8E4A7A00A1001B /* USB_Meter 6.xcdatamodel */, |
|
| 838 |
- C100001C3C8E4A7A00A1001C /* USB_Meter 7.xcdatamodel */, |
|
| 839 |
- C100001D3C8E4A7A00A1001D /* USB_Meter 8.xcdatamodel */, |
|
| 840 |
- C100001E3C8E4A7A00A1001E /* USB_Meter 9.xcdatamodel */, |
|
| 841 |
- C10000223C8E4A7A00A10022 /* USB_Meter 10.xcdatamodel */, |
|
| 842 |
- ); |
|
| 843 |
- currentVersion = C10000223C8E4A7A00A10022 /* USB_Meter 10.xcdatamodel */; |
|
| 844 |
- path = CKModel.xcdatamodeld; |
|
| 845 |
- sourceTree = "<group>"; |
|
| 846 |
- versionGroupType = wrapper.xcdatamodel; |
|
| 847 |
- }; |
|
| 848 |
-/* End XCVersionGroup section */ |
|
| 849 |
- |
|
| 850 | 831 |
/* Begin XCBuildConfiguration section */ |
| 851 | 832 |
43CBF671240BF3ED00255B8B /* Debug */ = {
|
| 852 | 833 |
isa = XCBuildConfiguration; |
@@ -981,6 +962,7 @@ |
||
| 981 | 962 |
ENABLE_PREVIEWS = YES; |
| 982 | 963 |
ENABLE_RESOURCE_ACCESS_BLUETOOTH = YES; |
| 983 | 964 |
INFOPLIST_FILE = "USB Meter/Info.plist"; |
| 965 |
+ INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.utilities"; |
|
| 984 | 966 |
LD_RUNPATH_SEARCH_PATHS = ( |
| 985 | 967 |
"$(inherited)", |
| 986 | 968 |
"@executable_path/Frameworks", |
@@ -1008,6 +990,7 @@ |
||
| 1008 | 990 |
ENABLE_PREVIEWS = YES; |
| 1009 | 991 |
ENABLE_RESOURCE_ACCESS_BLUETOOTH = YES; |
| 1010 | 992 |
INFOPLIST_FILE = "USB Meter/Info.plist"; |
| 993 |
+ INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.utilities"; |
|
| 1011 | 994 |
LD_RUNPATH_SEARCH_PATHS = ( |
| 1012 | 995 |
"$(inherited)", |
| 1013 | 996 |
"@executable_path/Frameworks", |
@@ -1065,6 +1048,25 @@ |
||
| 1065 | 1048 |
productName = CryptoSwift; |
| 1066 | 1049 |
}; |
| 1067 | 1050 |
/* End XCSwiftPackageProductDependency section */ |
| 1051 |
+ |
|
| 1052 |
+/* Begin XCVersionGroup section */ |
|
| 1053 |
+ C10000213C8E4A7A00A10021 /* CKModel.xcdatamodeld */ = {
|
|
| 1054 |
+ isa = XCVersionGroup; |
|
| 1055 |
+ children = ( |
|
| 1056 |
+ C10000193C8E4A7A00A10019 /* USB_Meter 4.xcdatamodel */, |
|
| 1057 |
+ C100001A3C8E4A7A00A1001A /* USB_Meter 5.xcdatamodel */, |
|
| 1058 |
+ C100001B3C8E4A7A00A1001B /* USB_Meter 6.xcdatamodel */, |
|
| 1059 |
+ C100001C3C8E4A7A00A1001C /* USB_Meter 7.xcdatamodel */, |
|
| 1060 |
+ C100001D3C8E4A7A00A1001D /* USB_Meter 8.xcdatamodel */, |
|
| 1061 |
+ C100001E3C8E4A7A00A1001E /* USB_Meter 9.xcdatamodel */, |
|
| 1062 |
+ C10000223C8E4A7A00A10022 /* USB_Meter 10.xcdatamodel */, |
|
| 1063 |
+ ); |
|
| 1064 |
+ currentVersion = C10000223C8E4A7A00A10022 /* USB_Meter 10.xcdatamodel */; |
|
| 1065 |
+ path = CKModel.xcdatamodeld; |
|
| 1066 |
+ sourceTree = "<group>"; |
|
| 1067 |
+ versionGroupType = wrapper.xcdatamodel; |
|
| 1068 |
+ }; |
|
| 1069 |
+/* End XCVersionGroup section */ |
|
| 1068 | 1070 |
}; |
| 1069 | 1071 |
rootObject = 43CBF654240BF3EB00255B8B /* Project object */; |
| 1070 | 1072 |
} |
@@ -497,15 +497,10 @@ final class AppData : ObservableObject {
|
||
| 497 | 497 |
} |
| 498 | 498 |
|
| 499 | 499 |
@discardableResult |
| 500 |
- func stopChargeSession( |
|
| 501 |
- sessionID: UUID, |
|
| 502 |
- finalBatteryPercent: Double, |
|
| 503 |
- label: String? = "Final" |
|
| 504 |
- ) -> Bool {
|
|
| 500 |
+ func stopChargeSession(sessionID: UUID, finalBatteryPercent: Double) -> Bool {
|
|
| 505 | 501 |
let didSave = chargeInsightsStore?.stopSession( |
| 506 | 502 |
id: sessionID, |
| 507 |
- finalBatteryPercent: finalBatteryPercent, |
|
| 508 |
- label: label |
|
| 503 |
+ finalBatteryPercent: finalBatteryPercent |
|
| 509 | 504 |
) ?? false |
| 510 | 505 |
if didSave {
|
| 511 | 506 |
reloadChargedDevices() |
@@ -524,7 +519,7 @@ final class AppData : ObservableObject {
|
||
| 524 | 519 |
} |
| 525 | 520 |
|
| 526 | 521 |
@discardableResult |
| 527 |
- func addBatteryCheckpoint(percent: Double, label: String?, for meter: Meter) -> Bool {
|
|
| 522 |
+ func addBatteryCheckpoint(percent: Double, for meter: Meter) -> Bool {
|
|
| 528 | 523 |
observeChargeSnapshot(from: meter) |
| 529 | 524 |
|
| 530 | 525 |
let activeSession = activeChargeSessionSummary(for: meter.btSerial.macAddress.description) |
@@ -533,7 +528,6 @@ final class AppData : ObservableObject {
|
||
| 533 | 528 |
|
| 534 | 529 |
let didSave = chargeInsightsStore?.addBatteryCheckpoint( |
| 535 | 530 |
percent: percent, |
| 536 |
- label: label, |
|
| 537 | 531 |
for: meter.btSerial.macAddress.description, |
| 538 | 532 |
measuredEnergyWh: checkpointEnergyWh, |
| 539 | 533 |
measuredChargeAh: checkpointChargeAh |
@@ -547,10 +541,13 @@ final class AppData : ObservableObject {
|
||
| 547 | 541 |
} |
| 548 | 542 |
|
| 549 | 543 |
@discardableResult |
| 550 |
- func addBatteryCheckpoint(percent: Double, label: String?, for sessionID: UUID) -> Bool {
|
|
| 544 |
+ func addBatteryCheckpoint(percent: Double, for sessionID: UUID) -> Bool {
|
|
| 545 |
+ guard canAddBatteryCheckpoint(to: sessionID) else {
|
|
| 546 |
+ return false |
|
| 547 |
+ } |
|
| 548 |
+ |
|
| 551 | 549 |
let didSave = chargeInsightsStore?.addBatteryCheckpoint( |
| 552 | 550 |
percent: percent, |
| 553 |
- label: label, |
|
| 554 | 551 |
for: sessionID |
| 555 | 552 |
) ?? false |
| 556 | 553 |
|
@@ -564,14 +561,16 @@ final class AppData : ObservableObject {
|
||
| 564 | 561 |
@discardableResult |
| 565 | 562 |
func addBatteryCheckpoint( |
| 566 | 563 |
percent: Double, |
| 567 |
- label: String?, |
|
| 568 | 564 |
for sessionID: UUID, |
| 569 | 565 |
measuredEnergyWh: Double?, |
| 570 | 566 |
measuredChargeAh: Double? |
| 571 | 567 |
) -> Bool {
|
| 568 |
+ guard canAddBatteryCheckpoint(to: sessionID) else {
|
|
| 569 |
+ return false |
|
| 570 |
+ } |
|
| 571 |
+ |
|
| 572 | 572 |
let didSave = chargeInsightsStore?.addBatteryCheckpoint( |
| 573 | 573 |
percent: percent, |
| 574 |
- label: label, |
|
| 575 | 574 |
for: sessionID, |
| 576 | 575 |
measuredEnergyWh: measuredEnergyWh, |
| 577 | 576 |
measuredChargeAh: measuredChargeAh |
@@ -584,6 +583,33 @@ final class AppData : ObservableObject {
|
||
| 584 | 583 |
return didSave |
| 585 | 584 |
} |
| 586 | 585 |
|
| 586 |
+ func canAddBatteryCheckpoint(to sessionID: UUID) -> Bool {
|
|
| 587 |
+ guard let session = chargeSessionSummary(id: sessionID), |
|
| 588 |
+ session.status.isOpen, |
|
| 589 |
+ let meterMACAddress = session.meterMACAddress else {
|
|
| 590 |
+ return false |
|
| 591 |
+ } |
|
| 592 |
+ |
|
| 593 |
+ return meter(for: meterMACAddress) != nil |
|
| 594 |
+ } |
|
| 595 |
+ |
|
| 596 |
+ func batteryCheckpointCaptureRequirementMessage(for sessionID: UUID) -> String? {
|
|
| 597 |
+ guard let session = chargeSessionSummary(id: sessionID) else {
|
|
| 598 |
+ return "Battery checkpoints are available only while the charge session is still active." |
|
| 599 |
+ } |
|
| 600 |
+ |
|
| 601 |
+ guard session.status.isOpen else {
|
|
| 602 |
+ return "Battery checkpoints are available only while the charge session is still active." |
|
| 603 |
+ } |
|
| 604 |
+ |
|
| 605 |
+ guard let meterMACAddress = session.meterMACAddress, |
|
| 606 |
+ meter(for: meterMACAddress) != nil else {
|
|
| 607 |
+ return "Add battery checkpoints only on the device that is actively monitoring this charging session. Devices following the session through iCloud may not have data that is fresh or precise enough." |
|
| 608 |
+ } |
|
| 609 |
+ |
|
| 610 |
+ return nil |
|
| 611 |
+ } |
|
| 612 |
+ |
|
| 587 | 613 |
func batteryCheckpointPlausibilityWarning( |
| 588 | 614 |
percent: Double, |
| 589 | 615 |
for sessionID: UUID, |
@@ -322,6 +322,55 @@ struct ChargeCheckpointSummary: Identifiable, Hashable {
|
||
| 322 | 322 |
let currentAmps: Double |
| 323 | 323 |
let voltageVolts: Double? |
| 324 | 324 |
let label: String? |
| 325 |
+ |
|
| 326 |
+ var flag: ChargeCheckpointFlag {
|
|
| 327 |
+ ChargeCheckpointFlag.fromStoredLabel(label) |
|
| 328 |
+ } |
|
| 329 |
+} |
|
| 330 |
+ |
|
| 331 |
+enum ChargeCheckpointFlag: String, CaseIterable {
|
|
| 332 |
+ case initial |
|
| 333 |
+ case intermediate |
|
| 334 |
+ case final |
|
| 335 |
+ |
|
| 336 |
+ var title: String {
|
|
| 337 |
+ switch self {
|
|
| 338 |
+ case .initial: |
|
| 339 |
+ return "Initial" |
|
| 340 |
+ case .intermediate: |
|
| 341 |
+ return "Intermediate" |
|
| 342 |
+ case .final: |
|
| 343 |
+ return "Final" |
|
| 344 |
+ } |
|
| 345 |
+ } |
|
| 346 |
+ |
|
| 347 |
+ var anchorDescription: String {
|
|
| 348 |
+ switch self {
|
|
| 349 |
+ case .initial: |
|
| 350 |
+ return "initial checkpoint" |
|
| 351 |
+ case .intermediate: |
|
| 352 |
+ return "intermediate checkpoint" |
|
| 353 |
+ case .final: |
|
| 354 |
+ return "final checkpoint" |
|
| 355 |
+ } |
|
| 356 |
+ } |
|
| 357 |
+ |
|
| 358 |
+ static func fromStoredLabel(_ label: String?) -> ChargeCheckpointFlag {
|
|
| 359 |
+ let normalized = label? |
|
| 360 |
+ .trimmingCharacters(in: .whitespacesAndNewlines) |
|
| 361 |
+ .lowercased() |
|
| 362 |
+ |
|
| 363 |
+ switch normalized {
|
|
| 364 |
+ case "initial", "start": |
|
| 365 |
+ return .initial |
|
| 366 |
+ case "final", "end": |
|
| 367 |
+ return .final |
|
| 368 |
+ case "intermediate", nil, "": |
|
| 369 |
+ return .intermediate |
|
| 370 |
+ default: |
|
| 371 |
+ return .intermediate |
|
| 372 |
+ } |
|
| 373 |
+ } |
|
| 325 | 374 |
} |
| 326 | 375 |
|
| 327 | 376 |
struct ChargeSessionSampleSummary: Identifiable, Hashable {
|
@@ -1000,12 +1049,11 @@ struct ChargedDeviceSummary: Identifiable, Hashable {
|
||
| 1000 | 1049 |
checkpoint.batteryPercent >= 0 |
| 1001 | 1050 |
} |
| 1002 | 1051 |
.map { checkpoint in
|
| 1003 |
- let trimmedLabel = checkpoint.label?.trimmingCharacters(in: .whitespacesAndNewlines) |
|
| 1004 | 1052 |
return Anchor( |
| 1005 | 1053 |
percent: checkpoint.batteryPercent, |
| 1006 | 1054 |
energyWh: checkpoint.measuredEnergyWh, |
| 1007 | 1055 |
timestamp: checkpoint.timestamp, |
| 1008 |
- description: trimmedLabel.map { "checkpoint \($0)" } ?? "last checkpoint",
|
|
| 1056 |
+ description: checkpoint.flag.anchorDescription, |
|
| 1009 | 1057 |
isCheckpoint: true |
| 1010 | 1058 |
) |
| 1011 | 1059 |
} |
@@ -414,7 +414,7 @@ final class ChargeInsightsStore {
|
||
| 414 | 414 |
} else if let initialBatteryPercent {
|
| 415 | 415 |
guard insertBatteryCheckpoint( |
| 416 | 416 |
percent: initialBatteryPercent, |
| 417 |
- label: "Start", |
|
| 417 |
+ flag: .initial, |
|
| 418 | 418 |
timestamp: snapshot.observedAt, |
| 419 | 419 |
to: session |
| 420 | 420 |
) != nil else {
|
@@ -467,7 +467,6 @@ final class ChargeInsightsStore {
|
||
| 467 | 467 |
session, |
| 468 | 468 |
observedAt: pausedAt.addingTimeInterval(pausedSessionTimeout), |
| 469 | 469 |
finalBatteryPercent: nil, |
| 470 |
- label: nil, |
|
| 471 | 470 |
status: .completed |
| 472 | 471 |
) |
| 473 | 472 |
guard saveContext() else {
|
@@ -508,8 +507,7 @@ final class ChargeInsightsStore {
|
||
| 508 | 507 |
@discardableResult |
| 509 | 508 |
func stopSession( |
| 510 | 509 |
id sessionID: UUID, |
| 511 |
- finalBatteryPercent: Double, |
|
| 512 |
- label: String? |
|
| 510 |
+ finalBatteryPercent: Double |
|
| 513 | 511 |
) -> Bool {
|
| 514 | 512 |
guard finalBatteryPercent.isFinite, finalBatteryPercent >= 0, finalBatteryPercent <= 100 else {
|
| 515 | 513 |
return false |
@@ -530,7 +528,6 @@ final class ChargeInsightsStore {
|
||
| 530 | 528 |
session, |
| 531 | 529 |
observedAt: observedAt, |
| 532 | 530 |
finalBatteryPercent: finalBatteryPercent, |
| 533 |
- label: label, |
|
| 534 | 531 |
status: .completed |
| 535 | 532 |
) |
| 536 | 533 |
|
@@ -551,7 +548,6 @@ final class ChargeInsightsStore {
|
||
| 551 | 548 |
@discardableResult |
| 552 | 549 |
func addBatteryCheckpoint( |
| 553 | 550 |
percent: Double, |
| 554 |
- label: String?, |
|
| 555 | 551 |
for meterMACAddress: String, |
| 556 | 552 |
measuredEnergyWh: Double? = nil, |
| 557 | 553 |
measuredChargeAh: Double? = nil |
@@ -568,9 +564,9 @@ final class ChargeInsightsStore {
|
||
| 568 | 564 |
|
| 569 | 565 |
didSave = addBatteryCheckpoint( |
| 570 | 566 |
percent: percent, |
| 571 |
- label: label, |
|
| 572 | 567 |
measuredEnergyWh: measuredEnergyWh, |
| 573 | 568 |
measuredChargeAh: measuredChargeAh, |
| 569 |
+ flag: .intermediate, |
|
| 574 | 570 |
to: session |
| 575 | 571 |
) |
| 576 | 572 |
} |
@@ -580,7 +576,6 @@ final class ChargeInsightsStore {
|
||
| 580 | 576 |
@discardableResult |
| 581 | 577 |
func addBatteryCheckpoint( |
| 582 | 578 |
percent: Double, |
| 583 |
- label: String?, |
|
| 584 | 579 |
for sessionID: UUID, |
| 585 | 580 |
measuredEnergyWh: Double? = nil, |
| 586 | 581 |
measuredChargeAh: Double? = nil |
@@ -597,9 +592,9 @@ final class ChargeInsightsStore {
|
||
| 597 | 592 |
|
| 598 | 593 |
didSave = addBatteryCheckpoint( |
| 599 | 594 |
percent: percent, |
| 600 |
- label: label, |
|
| 601 | 595 |
measuredEnergyWh: measuredEnergyWh, |
| 602 | 596 |
measuredChargeAh: measuredChargeAh, |
| 597 |
+ flag: .intermediate, |
|
| 603 | 598 |
to: session |
| 604 | 599 |
) |
| 605 | 600 |
} |
@@ -670,7 +665,6 @@ final class ChargeInsightsStore {
|
||
| 670 | 665 |
session, |
| 671 | 666 |
observedAt: dateValue(session, key: "lastObservedAt") ?? Date(), |
| 672 | 667 |
finalBatteryPercent: nil, |
| 673 |
- label: nil, |
|
| 674 | 668 |
status: .completed |
| 675 | 669 |
) |
| 676 | 670 |
|
@@ -1245,7 +1239,6 @@ final class ChargeInsightsStore {
|
||
| 1245 | 1239 |
session, |
| 1246 | 1240 |
observedAt: snapshot.observedAt, |
| 1247 | 1241 |
finalBatteryPercent: nil, |
| 1248 |
- label: nil, |
|
| 1249 | 1242 |
status: .completed |
| 1250 | 1243 |
) |
| 1251 | 1244 |
} |
@@ -1399,7 +1392,6 @@ final class ChargeInsightsStore {
|
||
| 1399 | 1392 |
session, |
| 1400 | 1393 |
observedAt: pausedAt.addingTimeInterval(pausedSessionTimeout), |
| 1401 | 1394 |
finalBatteryPercent: nil, |
| 1402 |
- label: nil, |
|
| 1403 | 1395 |
status: .completed |
| 1404 | 1396 |
) |
| 1405 | 1397 |
|
@@ -1447,13 +1439,12 @@ final class ChargeInsightsStore {
|
||
| 1447 | 1439 |
_ session: NSManagedObject, |
| 1448 | 1440 |
observedAt: Date, |
| 1449 | 1441 |
finalBatteryPercent: Double?, |
| 1450 |
- label: String?, |
|
| 1451 | 1442 |
status: ChargeSessionStatus |
| 1452 | 1443 |
) {
|
| 1453 | 1444 |
if let finalBatteryPercent {
|
| 1454 | 1445 |
_ = insertBatteryCheckpoint( |
| 1455 | 1446 |
percent: finalBatteryPercent, |
| 1456 |
- label: label, |
|
| 1447 |
+ flag: .final, |
|
| 1457 | 1448 |
timestamp: observedAt, |
| 1458 | 1449 |
to: session |
| 1459 | 1450 |
) |
@@ -1616,7 +1607,7 @@ final class ChargeInsightsStore {
|
||
| 1616 | 1607 |
@discardableResult |
| 1617 | 1608 |
private func insertBatteryCheckpoint( |
| 1618 | 1609 |
percent: Double, |
| 1619 |
- label: String?, |
|
| 1610 |
+ flag: ChargeCheckpointFlag, |
|
| 1620 | 1611 |
timestamp: Date = Date(), |
| 1621 | 1612 |
measuredEnergyWhOverride: Double? = nil, |
| 1622 | 1613 |
measuredChargeAhOverride: Double? = nil, |
@@ -1648,7 +1639,7 @@ final class ChargeInsightsStore {
|
||
| 1648 | 1639 |
chargingTransportMode(for: session) == .wired ? optionalDoubleValue(session, key: "lastObservedVoltageVolts") : nil, |
| 1649 | 1640 |
forKey: "voltageVolts" |
| 1650 | 1641 |
) |
| 1651 |
- checkpoint.setValue(normalizedOptionalText(label), forKey: "label") |
|
| 1642 |
+ checkpoint.setValue(flag.rawValue, forKey: "label") |
|
| 1652 | 1643 |
checkpoint.setValue(timestamp, forKey: "createdAt") |
| 1653 | 1644 |
|
| 1654 | 1645 |
let existingStartBatteryPercent = optionalDoubleValue(session, key: "startBatteryPercent") |
@@ -1686,9 +1677,9 @@ final class ChargeInsightsStore {
|
||
| 1686 | 1677 |
@discardableResult |
| 1687 | 1678 |
private func addBatteryCheckpoint( |
| 1688 | 1679 |
percent: Double, |
| 1689 |
- label: String?, |
|
| 1690 | 1680 |
measuredEnergyWh: Double? = nil, |
| 1691 | 1681 |
measuredChargeAh: Double? = nil, |
| 1682 |
+ flag: ChargeCheckpointFlag, |
|
| 1692 | 1683 |
to session: NSManagedObject, |
| 1693 | 1684 |
timestamp: Date = Date() |
| 1694 | 1685 |
) -> Bool {
|
@@ -1701,7 +1692,7 @@ final class ChargeInsightsStore {
|
||
| 1701 | 1692 |
|
| 1702 | 1693 |
guard let chargedDeviceID = insertBatteryCheckpoint( |
| 1703 | 1694 |
percent: percent, |
| 1704 |
- label: label, |
|
| 1695 |
+ flag: flag, |
|
| 1705 | 1696 |
timestamp: timestamp, |
| 1706 | 1697 |
measuredEnergyWhOverride: measuredEnergyWh, |
| 1707 | 1698 |
measuredChargeAhOverride: measuredChargeAh, |
@@ -1894,7 +1885,7 @@ final class ChargeInsightsStore {
|
||
| 1894 | 1885 |
measuredChargeAh: 0, |
| 1895 | 1886 |
currentAmps: 0, |
| 1896 | 1887 |
voltageVolts: nil, |
| 1897 |
- label: "Start" |
|
| 1888 |
+ label: ChargeCheckpointFlag.initial.rawValue |
|
| 1898 | 1889 |
) |
| 1899 | 1890 |
) |
| 1900 | 1891 |
} |
@@ -1911,7 +1902,7 @@ final class ChargeInsightsStore {
|
||
| 1911 | 1902 |
measuredChargeAh: session.measuredChargeAh, |
| 1912 | 1903 |
currentAmps: 0, |
| 1913 | 1904 |
voltageVolts: nil, |
| 1914 |
- label: "End" |
|
| 1905 |
+ label: ChargeCheckpointFlag.final.rawValue |
|
| 1915 | 1906 |
) |
| 1916 | 1907 |
) |
| 1917 | 1908 |
} |
@@ -18,7 +18,6 @@ struct BatteryCheckpointEditorContentView: View {
|
||
| 18 | 18 |
let onSaved: (() -> Void)? |
| 19 | 19 |
|
| 20 | 20 |
@State private var batteryPercent = "" |
| 21 |
- @State private var label = "" |
|
| 22 | 21 |
@State private var showsWarningPopover = false |
| 23 | 22 |
|
| 24 | 23 |
private var plausibilityWarning: BatteryCheckpointPlausibilityWarning? {
|
@@ -83,9 +82,6 @@ struct BatteryCheckpointEditorContentView: View {
|
||
| 83 | 82 |
TextField("Battery %", text: $batteryPercent)
|
| 84 | 83 |
.keyboardType(.decimalPad) |
| 85 | 84 |
.textFieldStyle(.roundedBorder) |
| 86 |
- |
|
| 87 |
- TextField("Label (optional)", text: $label)
|
|
| 88 |
- .textFieldStyle(.roundedBorder) |
|
| 89 | 85 |
} |
| 90 | 86 |
|
| 91 | 87 |
HStack(spacing: 10) {
|
@@ -119,7 +115,6 @@ struct BatteryCheckpointEditorContentView: View {
|
||
| 119 | 115 |
|
| 120 | 116 |
if appData.addBatteryCheckpoint( |
| 121 | 117 |
percent: percent, |
| 122 |
- label: label, |
|
| 123 | 118 |
for: sessionID, |
| 124 | 119 |
measuredEnergyWh: effectiveEnergyWhOverride, |
| 125 | 120 |
measuredChargeAh: measuredChargeAhOverride |
@@ -442,23 +442,29 @@ struct ChargedDeviceDetailView: View {
|
||
| 442 | 442 |
) |
| 443 | 443 |
} |
| 444 | 444 |
|
| 445 |
- Button(showsInlineCheckpointEditor ? "Hide Checkpoint Editor" : "Add Battery Checkpoint") {
|
|
| 446 |
- showsInlineCheckpointEditor.toggle() |
|
| 447 |
- } |
|
| 448 |
- .frame(maxWidth: .infinity) |
|
| 449 |
- .padding(.vertical, 10) |
|
| 450 |
- .meterCard(tint: .green, fillOpacity: 0.16, strokeOpacity: 0.22, cornerRadius: 14) |
|
| 451 |
- .buttonStyle(.plain) |
|
| 445 |
+ if appData.canAddBatteryCheckpoint(to: activeSession.id) {
|
|
| 446 |
+ Button(showsInlineCheckpointEditor ? "Hide Checkpoint Editor" : "Add Battery Checkpoint") {
|
|
| 447 |
+ showsInlineCheckpointEditor.toggle() |
|
| 448 |
+ } |
|
| 449 |
+ .frame(maxWidth: .infinity) |
|
| 450 |
+ .padding(.vertical, 10) |
|
| 451 |
+ .meterCard(tint: .green, fillOpacity: 0.16, strokeOpacity: 0.22, cornerRadius: 14) |
|
| 452 |
+ .buttonStyle(.plain) |
|
| 452 | 453 |
|
| 453 |
- if showsInlineCheckpointEditor {
|
|
| 454 |
- BatteryCheckpointEditorContentView( |
|
| 455 |
- sessionID: activeSession.id, |
|
| 456 |
- message: "The checkpoint is stored on the active charge session and is used for capacity estimation and charge-level prediction.", |
|
| 457 |
- effectiveEnergyWhOverride: nil, |
|
| 458 |
- measuredChargeAhOverride: nil, |
|
| 459 |
- onCancel: { showsInlineCheckpointEditor = false },
|
|
| 460 |
- onSaved: { showsInlineCheckpointEditor = false }
|
|
| 461 |
- ) |
|
| 454 |
+ if showsInlineCheckpointEditor {
|
|
| 455 |
+ BatteryCheckpointEditorContentView( |
|
| 456 |
+ sessionID: activeSession.id, |
|
| 457 |
+ message: "The checkpoint is stored on the active charge session and is used for capacity estimation and charge-level prediction.", |
|
| 458 |
+ effectiveEnergyWhOverride: nil, |
|
| 459 |
+ measuredChargeAhOverride: nil, |
|
| 460 |
+ onCancel: { showsInlineCheckpointEditor = false },
|
|
| 461 |
+ onSaved: { showsInlineCheckpointEditor = false }
|
|
| 462 |
+ ) |
|
| 463 |
+ } |
|
| 464 |
+ } else if let checkpointEditingMessage = appData.batteryCheckpointCaptureRequirementMessage(for: activeSession.id) {
|
|
| 465 |
+ Text(checkpointEditingMessage) |
|
| 466 |
+ .font(.caption2) |
|
| 467 |
+ .foregroundColor(.secondary) |
|
| 462 | 468 |
} |
| 463 | 469 |
|
| 464 | 470 |
Button(activeSession.targetBatteryPercent == nil ? "Set Target Notification" : "Change Target Notification") {
|
@@ -569,6 +575,9 @@ struct ChargedDeviceDetailView: View {
|
||
| 569 | 575 |
Text(checkpoint.timestamp.format()) |
| 570 | 576 |
.font(.caption2) |
| 571 | 577 |
.foregroundColor(.secondary) |
| 578 |
+ Text(checkpoint.flag.title) |
|
| 579 |
+ .font(.caption2.weight(.semibold)) |
|
| 580 |
+ .foregroundColor(.secondary) |
|
| 572 | 581 |
Spacer() |
| 573 | 582 |
Text("\(checkpoint.batteryPercent.format(decimalDigits: 0))%")
|
| 574 | 583 |
.font(.caption.weight(.semibold)) |
@@ -727,6 +727,7 @@ struct MeterChargeRecordContentView: View {
|
||
| 727 | 727 |
private func chargingMonitorCard(_ openChargeSession: ChargeSessionSummary) -> some View {
|
| 728 | 728 |
let displayedEnergyWh = displayedSessionEnergyWh(for: openChargeSession) |
| 729 | 729 |
let displayedChargeAh = displayedSessionChargeAh(for: openChargeSession) |
| 730 |
+ let canAddCheckpoint = appData.canAddBatteryCheckpoint(to: openChargeSession.id) |
|
| 730 | 731 |
return VStack(alignment: .leading, spacing: 12) {
|
| 731 | 732 |
HStack(spacing: 8) {
|
| 732 | 733 |
Text("Charging Monitor")
|
@@ -800,23 +801,29 @@ struct MeterChargeRecordContentView: View {
|
||
| 800 | 801 |
) |
| 801 | 802 |
} |
| 802 | 803 |
|
| 803 |
- Button(showsInlineCheckpointEditor ? "Hide Checkpoint Editor" : "Add Battery Checkpoint") {
|
|
| 804 |
- showsInlineCheckpointEditor.toggle() |
|
| 805 |
- } |
|
| 806 |
- .frame(maxWidth: .infinity) |
|
| 807 |
- .padding(.vertical, 10) |
|
| 808 |
- .meterCard(tint: .green, fillOpacity: 0.16, strokeOpacity: 0.22, cornerRadius: 14) |
|
| 809 |
- .buttonStyle(.plain) |
|
| 804 |
+ if canAddCheckpoint {
|
|
| 805 |
+ Button(showsInlineCheckpointEditor ? "Hide Checkpoint Editor" : "Add Battery Checkpoint") {
|
|
| 806 |
+ showsInlineCheckpointEditor.toggle() |
|
| 807 |
+ } |
|
| 808 |
+ .frame(maxWidth: .infinity) |
|
| 809 |
+ .padding(.vertical, 10) |
|
| 810 |
+ .meterCard(tint: .green, fillOpacity: 0.16, strokeOpacity: 0.22, cornerRadius: 14) |
|
| 811 |
+ .buttonStyle(.plain) |
|
| 810 | 812 |
|
| 811 |
- if showsInlineCheckpointEditor {
|
|
| 812 |
- BatteryCheckpointEditorContentView( |
|
| 813 |
- sessionID: openChargeSession.id, |
|
| 814 |
- message: "The checkpoint is stored on the active charge session and later used for capacity estimation and the typical charge curve.", |
|
| 815 |
- effectiveEnergyWhOverride: displayedEnergyWh, |
|
| 816 |
- measuredChargeAhOverride: displayedChargeAh, |
|
| 817 |
- onCancel: { showsInlineCheckpointEditor = false },
|
|
| 818 |
- onSaved: { showsInlineCheckpointEditor = false }
|
|
| 819 |
- ) |
|
| 813 |
+ if showsInlineCheckpointEditor {
|
|
| 814 |
+ BatteryCheckpointEditorContentView( |
|
| 815 |
+ sessionID: openChargeSession.id, |
|
| 816 |
+ message: "The checkpoint is stored on the active charge session and later used for capacity estimation and the typical charge curve.", |
|
| 817 |
+ effectiveEnergyWhOverride: displayedEnergyWh, |
|
| 818 |
+ measuredChargeAhOverride: displayedChargeAh, |
|
| 819 |
+ onCancel: { showsInlineCheckpointEditor = false },
|
|
| 820 |
+ onSaved: { showsInlineCheckpointEditor = false }
|
|
| 821 |
+ ) |
|
| 822 |
+ } |
|
| 823 |
+ } else if let checkpointEditingMessage = appData.batteryCheckpointCaptureRequirementMessage(for: openChargeSession.id) {
|
|
| 824 |
+ Text(checkpointEditingMessage) |
|
| 825 |
+ .font(.caption) |
|
| 826 |
+ .foregroundColor(.secondary) |
|
| 820 | 827 |
} |
| 821 | 828 |
|
| 822 | 829 |
Button(openChargeSession.targetBatteryPercent == nil ? "Set Target Notification" : "Change Target Notification") {
|
@@ -1059,6 +1066,9 @@ struct MeterChargeRecordContentView: View {
|
||
| 1059 | 1066 |
Text(checkpoint.timestamp.format()) |
| 1060 | 1067 |
.font(.caption2) |
| 1061 | 1068 |
.foregroundColor(.secondary) |
| 1069 |
+ Text(checkpoint.flag.title) |
|
| 1070 |
+ .font(.caption2.weight(.semibold)) |
|
| 1071 |
+ .foregroundColor(.secondary) |
|
| 1062 | 1072 |
Spacer() |
| 1063 | 1073 |
Text("\(checkpoint.batteryPercent.format(decimalDigits: 0))%")
|
| 1064 | 1074 |
.font(.caption.weight(.semibold)) |
@@ -1228,7 +1238,6 @@ struct ChargeSessionCompletionSheetView: View {
|
||
| 1228 | 1238 |
let explanation: String |
| 1229 | 1239 |
|
| 1230 | 1240 |
@State private var batteryPercent = "" |
| 1231 |
- @State private var label = "Final" |
|
| 1232 | 1241 |
|
| 1233 | 1242 |
var body: some View {
|
| 1234 | 1243 |
NavigationView {
|
@@ -1241,7 +1250,6 @@ struct ChargeSessionCompletionSheetView: View {
|
||
| 1241 | 1250 |
) {
|
| 1242 | 1251 |
TextField("Battery %", text: $batteryPercent)
|
| 1243 | 1252 |
.keyboardType(.decimalPad) |
| 1244 |
- TextField("Label", text: $label)
|
|
| 1245 | 1253 |
} |
| 1246 | 1254 |
|
| 1247 | 1255 |
Section {
|
@@ -1272,8 +1280,7 @@ struct ChargeSessionCompletionSheetView: View {
|
||
| 1272 | 1280 |
|
| 1273 | 1281 |
if appData.stopChargeSession( |
| 1274 | 1282 |
sessionID: sessionID, |
| 1275 |
- finalBatteryPercent: batteryPercent, |
|
| 1276 |
- label: label.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty ? "Final" : label |
|
| 1283 |
+ finalBatteryPercent: batteryPercent |
|
| 1277 | 1284 |
) {
|
| 1278 | 1285 |
dismiss() |
| 1279 | 1286 |
} |