@mmerterden/multi-agent-pipeline 20.7.0 → 20.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +9 -0
- package/LICENSE +0 -10
- package/docs/facts.json +1 -1
- package/manifest.json +266 -267
- package/package.json +2 -2
- package/pipeline/scripts/_notices.mjs +1 -1
- package/pipeline/skills/.skill-manifest.json +68 -68
- package/pipeline/skills/shared/README.md +70 -70
- package/pipeline/skills/shared/external/alarmkit/SKILL.md +373 -381
- package/pipeline/skills/shared/external/alarmkit/evals/evals.json +23 -18
- package/pipeline/skills/shared/external/alarmkit/references/alarmkit-patterns.md +328 -378
- package/pipeline/skills/shared/external/app-clips/SKILL.md +260 -160
- package/pipeline/skills/shared/external/app-clips/evals/evals.json +27 -27
- package/pipeline/skills/shared/external/app-clips/references/data-handoff-notifications-location.md +150 -83
- package/pipeline/skills/shared/external/app-clips/references/routing-and-experiences.md +135 -83
- package/pipeline/skills/shared/external/app-clips/references/size-capabilities-and-promotion.md +143 -85
- package/pipeline/skills/shared/external/app-intents/SKILL.md +302 -304
- package/pipeline/skills/shared/external/app-intents/evals/evals.json +21 -21
- package/pipeline/skills/shared/external/app-intents/references/appintents-advanced.md +594 -894
- package/pipeline/skills/shared/external/app-store-optimization/SKILL.md +339 -277
- package/pipeline/skills/shared/external/app-store-optimization/evals/evals.json +27 -23
- package/pipeline/skills/shared/external/app-store-optimization/references/keyword-research-methodology.md +105 -122
- package/pipeline/skills/shared/external/app-store-optimization/references/product-page-variants.md +143 -166
- package/pipeline/skills/shared/external/app-store-review/SKILL.md +307 -326
- package/pipeline/skills/shared/external/app-store-review/evals/evals.json +21 -21
- package/pipeline/skills/shared/external/app-store-review/references/privacy-manifest.md +105 -67
- package/pipeline/skills/shared/external/app-store-review/references/review-checklists.md +114 -101
- package/pipeline/skills/shared/external/apple-on-device-ai/SKILL.md +333 -360
- package/pipeline/skills/shared/external/apple-on-device-ai/evals/evals.json +24 -27
- package/pipeline/skills/shared/external/apple-on-device-ai/references/coreml-conversion.md +215 -322
- package/pipeline/skills/shared/external/apple-on-device-ai/references/coreml-optimization.md +161 -256
- package/pipeline/skills/shared/external/apple-on-device-ai/references/foundation-models.md +277 -387
- package/pipeline/skills/shared/external/apple-on-device-ai/references/mlx-swift.md +196 -210
- package/pipeline/skills/shared/external/authentication/SKILL.md +265 -381
- package/pipeline/skills/shared/external/authentication/evals/evals.json +25 -25
- package/pipeline/skills/shared/external/authentication/references/keychain-biometric.md +133 -178
- package/pipeline/skills/shared/external/authentication/references/passkeys.md +111 -147
- package/pipeline/skills/shared/external/avkit/SKILL.md +267 -364
- package/pipeline/skills/shared/external/avkit/evals/evals.json +26 -26
- package/pipeline/skills/shared/external/avkit/references/avkit-patterns.md +375 -493
- package/pipeline/skills/shared/external/background-processing/SKILL.md +270 -382
- package/pipeline/skills/shared/external/background-processing/evals/evals.json +22 -22
- package/pipeline/skills/shared/external/background-processing/references/background-task-patterns.md +169 -317
- package/pipeline/skills/shared/external/callkit-voip/SKILL.md +290 -371
- package/pipeline/skills/shared/external/callkit-voip/evals/evals.json +24 -24
- package/pipeline/skills/shared/external/callkit-voip/references/callkit-patterns.md +175 -343
- package/pipeline/skills/shared/external/cloudkit-sync/SKILL.md +292 -381
- package/pipeline/skills/shared/external/cloudkit-sync/evals/evals.json +33 -30
- package/pipeline/skills/shared/external/cloudkit-sync/references/cloudkit-patterns.md +227 -355
- package/pipeline/skills/shared/external/contacts-framework/SKILL.md +197 -346
- package/pipeline/skills/shared/external/contacts-framework/evals/evals.json +19 -21
- package/pipeline/skills/shared/external/contacts-framework/references/contacts-patterns.md +169 -308
- package/pipeline/skills/shared/external/core-bluetooth/SKILL.md +226 -376
- package/pipeline/skills/shared/external/core-bluetooth/evals/evals.json +25 -22
- package/pipeline/skills/shared/external/core-bluetooth/references/ble-patterns.md +257 -337
- package/pipeline/skills/shared/external/core-data/SKILL.md +292 -368
- package/pipeline/skills/shared/external/core-data/evals/evals.json +30 -27
- package/pipeline/skills/shared/external/core-motion/SKILL.md +235 -324
- package/pipeline/skills/shared/external/core-motion/evals/evals.json +31 -27
- package/pipeline/skills/shared/external/core-motion/references/motion-patterns.md +210 -310
- package/pipeline/skills/shared/external/core-nfc/SKILL.md +292 -366
- package/pipeline/skills/shared/external/core-nfc/evals/evals.json +22 -24
- package/pipeline/skills/shared/external/core-nfc/references/nfc-patterns.md +233 -329
- package/pipeline/skills/shared/external/coreml/SKILL.md +323 -367
- package/pipeline/skills/shared/external/coreml/evals/evals.json +24 -21
- package/pipeline/skills/shared/external/coreml/references/coreml-swift-integration.md +562 -565
- package/pipeline/skills/shared/external/cryptokit/SKILL.md +253 -394
- package/pipeline/skills/shared/external/cryptokit/evals/evals.json +20 -18
- package/pipeline/skills/shared/external/cryptokit/references/cryptokit-patterns.md +299 -488
- package/pipeline/skills/shared/external/debugging-instruments/SKILL.md +270 -323
- package/pipeline/skills/shared/external/debugging-instruments/evals/evals.json +27 -30
- package/pipeline/skills/shared/external/debugging-instruments/references/instruments-guide.md +167 -315
- package/pipeline/skills/shared/external/debugging-instruments/references/lldb-patterns.md +140 -193
- package/pipeline/skills/shared/external/device-integrity/SKILL.md +230 -353
- package/pipeline/skills/shared/external/device-integrity/evals/evals.json +25 -21
- package/pipeline/skills/shared/external/device-integrity/references/device-integrity-patterns.md +159 -197
- package/pipeline/skills/shared/external/energykit/SKILL.md +225 -392
- package/pipeline/skills/shared/external/energykit/evals/evals.json +29 -28
- package/pipeline/skills/shared/external/energykit/references/energykit-patterns.md +174 -470
- package/pipeline/skills/shared/external/eventkit-calendar/SKILL.md +261 -383
- package/pipeline/skills/shared/external/eventkit-calendar/evals/evals.json +25 -22
- package/pipeline/skills/shared/external/eventkit-calendar/references/eventkit-patterns.md +165 -268
- package/pipeline/skills/shared/external/healthkit/SKILL.md +252 -303
- package/pipeline/skills/shared/external/healthkit/evals/evals.json +24 -23
- package/pipeline/skills/shared/external/healthkit/references/healthkit-patterns.md +369 -523
- package/pipeline/skills/shared/external/homekit-matter/SKILL.md +233 -348
- package/pipeline/skills/shared/external/homekit-matter/evals/evals.json +27 -22
- package/pipeline/skills/shared/external/homekit-matter/references/matter-commissioning.md +199 -305
- package/pipeline/skills/shared/external/ios-accessibility/SKILL.md +368 -340
- package/pipeline/skills/shared/external/ios-accessibility/evals/evals.json +28 -27
- package/pipeline/skills/shared/external/ios-accessibility/references/a11y-patterns.md +314 -260
- package/pipeline/skills/shared/external/ios-accessibility/references/media-accessibility.md +97 -67
- package/pipeline/skills/shared/external/ios-accessibility/references/nutrition-labels.md +165 -101
- package/pipeline/skills/shared/external/ios-localization/SKILL.md +258 -371
- package/pipeline/skills/shared/external/ios-localization/evals/evals.json +23 -23
- package/pipeline/skills/shared/external/ios-localization/references/formatstyle-locale.md +283 -491
- package/pipeline/skills/shared/external/ios-localization/references/string-catalogs.md +313 -440
- package/pipeline/skills/shared/external/ios-networking/SKILL.md +265 -341
- package/pipeline/skills/shared/external/ios-networking/evals/evals.json +24 -24
- package/pipeline/skills/shared/external/ios-networking/references/background-websocket.md +425 -652
- package/pipeline/skills/shared/external/ios-networking/references/file-storage-patterns.md +143 -285
- package/pipeline/skills/shared/external/ios-networking/references/lightweight-clients.md +93 -53
- package/pipeline/skills/shared/external/ios-networking/references/network-framework.md +231 -456
- package/pipeline/skills/shared/external/ios-networking/references/urlsession-patterns.md +517 -784
- package/pipeline/skills/shared/external/ios-simulator/SKILL.md +265 -393
- package/pipeline/skills/shared/external/ios-simulator/evals/evals.json +21 -21
- package/pipeline/skills/shared/external/ios-simulator/references/simctl-commands.md +177 -270
- package/pipeline/skills/shared/external/live-activities/SKILL.md +318 -360
- package/pipeline/skills/shared/external/live-activities/evals/evals.json +21 -21
- package/pipeline/skills/shared/external/live-activities/references/activitykit-patterns.md +478 -710
- package/pipeline/skills/shared/external/mapkit-location/SKILL.md +295 -267
- package/pipeline/skills/shared/external/mapkit-location/evals/evals.json +28 -24
- package/pipeline/skills/shared/external/mapkit-location/references/mapkit-corelocation-patterns.md +378 -532
- package/pipeline/skills/shared/external/mapkit-location/references/mapkit-patterns.md +397 -499
- package/pipeline/skills/shared/external/metrickit-diagnostics/SKILL.md +165 -348
- package/pipeline/skills/shared/external/metrickit-diagnostics/evals/evals.json +26 -23
- package/pipeline/skills/shared/external/metrickit-diagnostics/references/metrickit-patterns.md +123 -130
- package/pipeline/skills/shared/external/musickit-audio/SKILL.md +189 -315
- package/pipeline/skills/shared/external/musickit-audio/evals/evals.json +22 -21
- package/pipeline/skills/shared/external/musickit-audio/references/musickit-patterns.md +181 -270
- package/pipeline/skills/shared/external/natural-language/SKILL.md +188 -340
- package/pipeline/skills/shared/external/natural-language/evals/evals.json +21 -21
- package/pipeline/skills/shared/external/natural-language/references/translation-patterns.md +171 -225
- package/pipeline/skills/shared/external/passkit-wallet/SKILL.md +258 -392
- package/pipeline/skills/shared/external/passkit-wallet/evals/evals.json +30 -29
- package/pipeline/skills/shared/external/passkit-wallet/references/wallet-passes.md +164 -231
- package/pipeline/skills/shared/external/pdfkit/SKILL.md +312 -344
- package/pipeline/skills/shared/external/pdfkit/evals/evals.json +19 -19
- package/pipeline/skills/shared/external/pdfkit/references/pdfkit-patterns.md +413 -624
- package/pipeline/skills/shared/external/pencilkit-drawing/SKILL.md +242 -358
- package/pipeline/skills/shared/external/pencilkit-drawing/evals/evals.json +25 -21
- package/pipeline/skills/shared/external/pencilkit-drawing/references/pencilkit-patterns.md +161 -226
- package/pipeline/skills/shared/external/permissionkit/SKILL.md +282 -400
- package/pipeline/skills/shared/external/permissionkit/evals/evals.json +27 -30
- package/pipeline/skills/shared/external/permissionkit/references/permissionkit-patterns.md +237 -350
- package/pipeline/skills/shared/external/photos-camera-media/SKILL.md +276 -325
- package/pipeline/skills/shared/external/photos-camera-media/references/av-playback.md +299 -545
- package/pipeline/skills/shared/external/photos-camera-media/references/camera-capture.md +344 -588
- package/pipeline/skills/shared/external/photos-camera-media/references/image-loading-caching.md +316 -660
- package/pipeline/skills/shared/external/photos-camera-media/references/photokit-patterns.md +270 -416
- package/pipeline/skills/shared/external/push-notifications/SKILL.md +312 -340
- package/pipeline/skills/shared/external/push-notifications/evals/evals.json +27 -26
- package/pipeline/skills/shared/external/push-notifications/references/notification-patterns.md +328 -485
- package/pipeline/skills/shared/external/push-notifications/references/rich-notifications.md +327 -560
- package/pipeline/skills/shared/external/realitykit-ar/SKILL.md +218 -410
- package/pipeline/skills/shared/external/realitykit-ar/evals/evals.json +24 -27
- package/pipeline/skills/shared/external/realitykit-ar/references/realitykit-patterns.md +221 -348
- package/pipeline/skills/shared/external/shareplay-activities/SKILL.md +222 -393
- package/pipeline/skills/shared/external/shareplay-activities/evals/evals.json +23 -24
- package/pipeline/skills/shared/external/shareplay-activities/references/shareplay-patterns.md +280 -420
- package/pipeline/skills/shared/external/speech-recognition/SKILL.md +217 -421
- package/pipeline/skills/shared/external/speech-recognition/evals/evals.json +23 -26
- package/pipeline/skills/shared/external/speech-recognition/references/speechanalyzer-patterns.md +133 -125
- package/pipeline/skills/shared/external/storekit/SKILL.md +228 -204
- package/pipeline/skills/shared/external/storekit/evals/evals.json +27 -24
- package/pipeline/skills/shared/external/storekit/references/app-review-guidelines.md +98 -109
- package/pipeline/skills/shared/external/storekit/references/core-patterns.md +298 -242
- package/pipeline/skills/shared/external/storekit/references/storekit-advanced.md +356 -649
- package/pipeline/skills/shared/external/swift-api-design-guidelines/SKILL.md +274 -399
- package/pipeline/skills/shared/external/swift-api-design-guidelines/evals/evals.json +22 -24
- package/pipeline/skills/shared/external/swift-api-design-guidelines/references/argument-labels-and-parameters.md +107 -108
- package/pipeline/skills/shared/external/swift-api-design-guidelines/references/conventions-and-special-rules.md +93 -165
- package/pipeline/skills/shared/external/swift-api-design-guidelines/references/naming-and-clarity.md +99 -137
- package/pipeline/skills/shared/external/swift-api-design-guidelines/references/side-effects-and-mutating-pairs.md +77 -120
- package/pipeline/skills/shared/external/swift-architecture/SKILL.md +334 -350
- package/pipeline/skills/shared/external/swift-architecture/evals/evals.json +22 -22
- package/pipeline/skills/shared/external/swift-charts/SKILL.md +208 -394
- package/pipeline/skills/shared/external/swift-charts/evals/evals.json +27 -30
- package/pipeline/skills/shared/external/swift-charts/references/charts-patterns.md +351 -762
- package/pipeline/skills/shared/external/swift-codable/SKILL.md +339 -343
- package/pipeline/skills/shared/external/swift-codable/evals/evals.json +20 -20
- package/pipeline/skills/shared/external/swift-concurrency/SKILL.md +303 -351
- package/pipeline/skills/shared/external/swift-concurrency/evals/evals.json +27 -24
- package/pipeline/skills/shared/external/swift-concurrency/references/approachable-concurrency.md +65 -80
- package/pipeline/skills/shared/external/swift-concurrency/references/async-algorithms.md +48 -84
- package/pipeline/skills/shared/external/swift-concurrency/references/bridging-interop.md +134 -79
- package/pipeline/skills/shared/external/swift-concurrency/references/concurrency-patterns.md +145 -167
- package/pipeline/skills/shared/external/swift-concurrency/references/diagnostics.md +62 -50
- package/pipeline/skills/shared/external/swift-concurrency/references/swiftui-concurrency.md +92 -121
- package/pipeline/skills/shared/external/swift-concurrency/references/synchronization-primitives.md +177 -241
- package/pipeline/skills/shared/external/swift-formatstyle/SKILL.md +258 -234
- package/pipeline/skills/shared/external/swift-language/SKILL.md +342 -382
- package/pipeline/skills/shared/external/swift-language/evals/evals.json +24 -27
- package/pipeline/skills/shared/external/swift-language/references/swift-attributes-interop.md +79 -56
- package/pipeline/skills/shared/external/swift-language/references/swift-patterns-extended.md +297 -340
- package/pipeline/skills/shared/external/swift-security/SKILL.md +180 -161
- package/pipeline/skills/shared/external/swift-security/evals/evals.json +25 -25
- package/pipeline/skills/shared/external/swift-security/references/biometric-authentication.md +314 -469
- package/pipeline/skills/shared/external/swift-security/references/certificate-trust.md +408 -476
- package/pipeline/skills/shared/external/swift-security/references/common-anti-patterns.md +260 -530
- package/pipeline/skills/shared/external/swift-security/references/compliance-owasp-mapping.md +270 -477
- package/pipeline/skills/shared/external/swift-security/references/credential-storage-patterns.md +573 -571
- package/pipeline/skills/shared/external/swift-security/references/cryptokit-public-key.md +370 -441
- package/pipeline/skills/shared/external/swift-security/references/cryptokit-symmetric.md +332 -433
- package/pipeline/skills/shared/external/swift-security/references/keychain-access-control.md +346 -468
- package/pipeline/skills/shared/external/swift-security/references/keychain-fundamentals.md +352 -472
- package/pipeline/skills/shared/external/swift-security/references/keychain-item-classes.md +431 -432
- package/pipeline/skills/shared/external/swift-security/references/keychain-sharing.md +328 -425
- package/pipeline/skills/shared/external/swift-security/references/migration-legacy-stores.md +341 -579
- package/pipeline/skills/shared/external/swift-security/references/secure-enclave.md +396 -457
- package/pipeline/skills/shared/external/swift-security/references/testing-security-code.md +354 -614
- package/pipeline/skills/shared/external/swift-testing/SKILL.md +188 -175
- package/pipeline/skills/shared/external/swift-testing/evals/evals.json +26 -24
- package/pipeline/skills/shared/external/swift-testing/references/testing-advanced.md +80 -84
- package/pipeline/skills/shared/external/swift-testing/references/testing-patterns.md +317 -433
- package/pipeline/skills/shared/external/swiftdata/SKILL.md +392 -256
- package/pipeline/skills/shared/external/swiftdata/evals/evals.json +24 -24
- package/pipeline/skills/shared/external/swiftdata/references/core-data-coexistence.md +206 -402
- package/pipeline/skills/shared/external/swiftdata/references/indexing.md +59 -52
- package/pipeline/skills/shared/external/swiftdata/references/predicate-pitfalls.md +57 -33
- package/pipeline/skills/shared/external/swiftdata/references/swiftdata-advanced.md +354 -747
- package/pipeline/skills/shared/external/swiftdata/references/swiftdata-queries.md +300 -508
- package/pipeline/skills/shared/external/swiftlint/SKILL.md +175 -226
- package/pipeline/skills/shared/external/swiftlint/references/adoption-and-configuration.md +141 -208
- package/pipeline/skills/shared/external/swiftlint/references/custom-rules-and-analyze.md +100 -109
- package/pipeline/skills/shared/external/swiftlint/references/plugins-run-scripts-and-integrations.md +159 -179
- package/pipeline/skills/shared/external/swiftlint/references/rule-reference.md +383 -18
- package/pipeline/skills/shared/external/swiftlint/references/rules-suppressions-and-baselines.md +143 -229
- package/pipeline/skills/shared/external/swiftui-animation/SKILL.md +283 -366
- package/pipeline/skills/shared/external/swiftui-animation/references/animation-advanced.md +396 -608
- package/pipeline/skills/shared/external/swiftui-animation/references/core-animation-bridge.md +336 -385
- package/pipeline/skills/shared/external/swiftui-gestures/SKILL.md +239 -349
- package/pipeline/skills/shared/external/swiftui-gestures/references/gesture-patterns.md +228 -310
- package/pipeline/skills/shared/external/swiftui-layout-components/SKILL.md +260 -249
- package/pipeline/skills/shared/external/swiftui-layout-components/references/form.md +92 -74
- package/pipeline/skills/shared/external/swiftui-layout-components/references/grids.md +112 -177
- package/pipeline/skills/shared/external/swiftui-layout-components/references/list.md +61 -64
- package/pipeline/skills/shared/external/swiftui-layout-components/references/scrollview.md +94 -134
- package/pipeline/skills/shared/external/swiftui-liquid-glass/SKILL.md +193 -225
- package/pipeline/skills/shared/external/swiftui-liquid-glass/references/liquid-glass.md +173 -327
- package/pipeline/skills/shared/external/swiftui-navigation/SKILL.md +193 -168
- package/pipeline/skills/shared/external/swiftui-navigation/references/deeplinks.md +127 -150
- package/pipeline/skills/shared/external/swiftui-navigation/references/navigationstack.md +132 -133
- package/pipeline/skills/shared/external/swiftui-navigation/references/sheets.md +152 -117
- package/pipeline/skills/shared/external/swiftui-navigation/references/tabview.md +106 -140
- package/pipeline/skills/shared/external/swiftui-patterns/SKILL.md +316 -252
- package/pipeline/skills/shared/external/swiftui-patterns/references/architecture-patterns.md +341 -332
- package/pipeline/skills/shared/external/swiftui-patterns/references/deprecated-migration.md +547 -854
- package/pipeline/skills/shared/external/swiftui-patterns/references/design-polish.md +485 -537
- package/pipeline/skills/shared/external/swiftui-patterns/references/platform-and-sharing.md +417 -499
- package/pipeline/skills/shared/external/swiftui-performance/SKILL.md +213 -376
- package/pipeline/skills/shared/external/swiftui-performance/references/demystify-swiftui-performance-wwdc23.md +86 -175
- package/pipeline/skills/shared/external/swiftui-performance/references/optimizing-swiftui-performance-instruments.md +89 -195
- package/pipeline/skills/shared/external/swiftui-performance/references/understanding-hangs-in-your-app.md +95 -182
- package/pipeline/skills/shared/external/swiftui-performance/references/understanding-improving-swiftui-performance.md +71 -149
- package/pipeline/skills/shared/external/swiftui-performance/references/wwdc-session-sources.md +21 -27
- package/pipeline/skills/shared/external/swiftui-uikit-interop/SKILL.md +303 -295
- package/pipeline/skills/shared/external/swiftui-uikit-interop/references/hosting-migration.md +204 -387
- package/pipeline/skills/shared/external/swiftui-uikit-interop/references/representable-recipes.md +469 -683
- package/pipeline/skills/shared/external/swiftui-webkit/SKILL.md +140 -186
- package/pipeline/skills/shared/external/swiftui-webkit/references/loading-and-observation.md +75 -86
- package/pipeline/skills/shared/external/swiftui-webkit/references/local-content-and-custom-schemes.md +63 -60
- package/pipeline/skills/shared/external/swiftui-webkit/references/migration-and-fallbacks.md +69 -137
- package/pipeline/skills/shared/external/swiftui-webkit/references/navigation-and-javascript.md +95 -67
- package/pipeline/skills/shared/external/tipkit/SKILL.md +220 -335
- package/pipeline/skills/shared/external/tipkit/references/tipkit-patterns.md +356 -494
- package/pipeline/skills/shared/external/vision-framework/SKILL.md +260 -375
- package/pipeline/skills/shared/external/vision-framework/references/vision-requests.md +393 -515
- package/pipeline/skills/shared/external/vision-framework/references/visionkit-scanner.md +363 -539
- package/pipeline/skills/shared/external/weatherkit/SKILL.md +152 -310
- package/pipeline/skills/shared/external/weatherkit/references/weatherkit-patterns.md +288 -407
- package/pipeline/skills/shared/external/widgetkit/SKILL.md +216 -288
- package/pipeline/skills/shared/external/widgetkit/references/widgetkit-advanced.md +414 -719
- package/pipeline/skills/shared/external/NOTICE-swift-ios-skills.md +0 -39
|
@@ -1,500 +1,333 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: energykit
|
|
3
|
-
description: "
|
|
3
|
+
description: "EnergyKit on iOS 26: grid electricity forecasts via ElectricityGuidance, EnergyVenue, EV and HVAC load events, ElectricityInsightService history. Use when building smart home, EV charging, HVAC scheduling or energy dashboard features that shift home power use to cleaner or cheaper grid periods."
|
|
4
4
|
metadata:
|
|
5
|
-
source:
|
|
5
|
+
source: multi-agent-pipeline
|
|
6
6
|
---
|
|
7
|
-
# EnergyKit
|
|
8
7
|
|
|
9
|
-
|
|
10
|
-
EnergyKit identifies times when grid electricity is relatively cleaner and,
|
|
11
|
-
when cost information is available, less expensive. Apps use that guidance to
|
|
12
|
-
shift or reduce managed device load. Targets Swift 6.3 / iOS 26+.
|
|
8
|
+
# EnergyKit
|
|
13
9
|
|
|
14
|
-
|
|
15
|
-
|
|
10
|
+
EnergyKit tells an app when grid electricity is expected to be relatively
|
|
11
|
+
cleaner, and cheaper too when the person's rate plan is known, so the app can
|
|
12
|
+
move or trim the load of devices it controls. Examples use Swift 6.3 and
|
|
13
|
+
assume a deployment target of iOS 26.1 unless a snippet shows its own
|
|
14
|
+
availability check.
|
|
16
15
|
|
|
17
|
-
|
|
16
|
+
**Beta-sensitive.** EnergyKit is new in iOS 26 and its API may still shift.
|
|
17
|
+
Check Apple's current documentation before depending on any detail below.
|
|
18
18
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
-
|
|
24
|
-
- [Submitting Load Events](#submitting-load-events)
|
|
25
|
-
- [Electricity Insights](#electricity-insights)
|
|
26
|
-
- [Common Mistakes](#common-mistakes)
|
|
27
|
-
- [Review Checklist](#review-checklist)
|
|
28
|
-
- [References](#references)
|
|
19
|
+
A full observable manager, EV and HVAC session managers, a dashboard, chart
|
|
20
|
+
preparation, retries and venue discovery are in the
|
|
21
|
+
[EnergyKit patterns reference](references/energykit-patterns.md).
|
|
22
|
+
Out of scope: HomeKit automation (`homekit-matter`), scheduling background work
|
|
23
|
+
(`background-processing`) and app power diagnostics (`metrickit-diagnostics`).
|
|
29
24
|
|
|
30
25
|
## Setup
|
|
31
26
|
|
|
32
|
-
### Entitlement
|
|
27
|
+
### Entitlement first
|
|
33
28
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
surface as `EnergyKitError.permissionDenied`.
|
|
29
|
+
Add the **EnergyKit** capability, which writes the
|
|
30
|
+
`com.apple.developer.energykit` entitlement. Do this before writing any
|
|
31
|
+
guidance, venue, load-event or insight code; without it calls fail with errors
|
|
32
|
+
such as `EnergyKitError.permissionDenied`.
|
|
39
33
|
|
|
40
|
-
### Import
|
|
34
|
+
### Import and availability
|
|
41
35
|
|
|
42
36
|
```swift
|
|
43
37
|
import EnergyKit
|
|
44
38
|
```
|
|
45
39
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
40
|
+
The core API needs iOS 26.0 or iPadOS 26.0. Some pieces arrived in 26.1 and
|
|
41
|
+
need `if #available(iOS 26.1, *)` when the app still deploys to 26.0 (the `iOS`
|
|
42
|
+
platform name covers iPadOS; there is no separate `iPadOS` availability name):
|
|
43
|
+
|
|
44
|
+
- the grid-cleanliness insight breakdown (`dataByGridCleanliness`)
|
|
45
|
+
- venue lookup: `EnergyVenue.venues()`, `venue(for:)` and `venue(matchingHomeUniqueIdentifier:)`
|
|
46
|
+
- the error cases `.unsupportedRegion`, `.rateLimitExceeded` and `.locationServicesDenied`
|
|
49
47
|
|
|
50
|
-
|
|
48
|
+
Guidance queries, load-event submission and the insight service itself are 26.0.
|
|
51
49
|
|
|
52
|
-
|
|
50
|
+
## Concepts
|
|
53
51
|
|
|
54
|
-
|
|
55
|
-
electricity is cleaner and, when rate data is available, less expensive
|
|
56
|
-
2. **Load Events** -- telemetry from managed devices (EV chargers, HVAC)
|
|
57
|
-
submitted by the same device/app that requested guidance so EnergyKit can
|
|
58
|
-
generate insights
|
|
52
|
+
EnergyKit does two jobs:
|
|
59
53
|
|
|
60
|
-
|
|
54
|
+
1. **Electricity guidance** - a forecast of weighted time intervals showing
|
|
55
|
+
when power is cleaner and, with rate data, cheaper.
|
|
56
|
+
2. **Load events** - telemetry from managed devices such as EV chargers and
|
|
57
|
+
heat pumps. The same device and app that asked for guidance submits them,
|
|
58
|
+
and EnergyKit turns them into insights.
|
|
61
59
|
|
|
62
60
|
| Type | Role |
|
|
63
61
|
|---|---|
|
|
64
|
-
| `ElectricityGuidance` | Forecast
|
|
65
|
-
| `ElectricityGuidance.Service` |
|
|
66
|
-
| `ElectricityGuidance.Query` |
|
|
67
|
-
| `ElectricityGuidance.Value` |
|
|
68
|
-
| `EnergyVenue` | A
|
|
69
|
-
| `ElectricVehicleLoadEvent` |
|
|
70
|
-
| `ElectricHVACLoadEvent` |
|
|
71
|
-
| `ElectricityInsightService` |
|
|
72
|
-
| `ElectricityInsightRecord` | Historical energy or runtime
|
|
73
|
-
| `ElectricityInsightQuery` |
|
|
74
|
-
|
|
75
|
-
### Suggested Actions
|
|
76
|
-
|
|
77
|
-
| Action | Use Case |
|
|
78
|
-
|---|---|
|
|
79
|
-
| `.shift` | Devices that can move consumption to a different time (EV charging) |
|
|
80
|
-
| `.reduce` | Devices that can lower consumption without stopping (HVAC setback) |
|
|
62
|
+
| `ElectricityGuidance` | Forecast made of weighted intervals |
|
|
63
|
+
| `ElectricityGuidance.Service` | Delivers guidance |
|
|
64
|
+
| `ElectricityGuidance.Query` | Declares whether you will shift or reduce load |
|
|
65
|
+
| `ElectricityGuidance.Value` | One interval with a 0.0 to 1.0 rating |
|
|
66
|
+
| `EnergyVenue` | A home registered for energy management |
|
|
67
|
+
| `ElectricVehicleLoadEvent` | EV charger telemetry |
|
|
68
|
+
| `ElectricHVACLoadEvent` | Heating and cooling telemetry |
|
|
69
|
+
| `ElectricityInsightService` | Answers energy and runtime history queries |
|
|
70
|
+
| `ElectricityInsightRecord` | Historical energy or runtime, optionally split by tariff or (26.1) cleanliness |
|
|
71
|
+
| `ElectricityInsightQuery` | Describes the history you want |
|
|
81
72
|
|
|
82
|
-
|
|
73
|
+
Suggested actions:
|
|
83
74
|
|
|
84
|
-
|
|
75
|
+
- `.shift` - the device can move its consumption to another time (EV charging).
|
|
76
|
+
- `.reduce` - the device can use less without stopping (a thermostat setback).
|
|
85
77
|
|
|
86
|
-
|
|
87
|
-
import EnergyKit
|
|
78
|
+
## Getting guidance
|
|
88
79
|
|
|
89
|
-
|
|
80
|
+
```swift
|
|
81
|
+
@MainActor
|
|
82
|
+
func watchChargingWindows(for venueID: UUID) async throws {
|
|
90
83
|
let query = ElectricityGuidance.Query(suggestedAction: .shift)
|
|
91
84
|
let service = ElectricityGuidance.sharedService
|
|
92
85
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
// Check if rate plan information is available
|
|
101
|
-
if guidance.options.contains(.guidanceIncorporatesRatePlan) {
|
|
102
|
-
print("Rate plan data incorporated")
|
|
103
|
-
}
|
|
104
|
-
if guidance.options.contains(.locationHasRatePlan) {
|
|
105
|
-
print("Location has a rate plan")
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
processGuidanceValues(guidance.values)
|
|
86
|
+
for try await forecast in service.guidance(using: query, at: venueID) {
|
|
87
|
+
ChargePlanner.shared.token = forecast.guidanceToken // keep the real token
|
|
88
|
+
ChargePlanner.shared.update(with: forecast.values)
|
|
89
|
+
let ratePlanUsed = forecast.options.contains(.guidanceIncorporatesRatePlan)
|
|
90
|
+
let hasRatePlan = forecast.options.contains(.locationHasRatePlan)
|
|
91
|
+
ChargePlanner.shared.showsCostHint = ratePlanUsed || hasRatePlan
|
|
109
92
|
}
|
|
110
93
|
}
|
|
111
94
|
```
|
|
112
95
|
|
|
113
|
-
|
|
96
|
+
An `ElectricityGuidance` exposes `guidanceToken`, `interval`, `energyVenueID`,
|
|
97
|
+
`options` and `values`.
|
|
114
98
|
|
|
115
|
-
|
|
116
|
-
from 0.0 to 1.0. Lower ratings indicate better times to use electricity.
|
|
99
|
+
### Reading the values
|
|
117
100
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
for value in values {
|
|
121
|
-
let interval = value.interval
|
|
122
|
-
let rating = value.rating // 0.0 (best) to 1.0 (worst)
|
|
101
|
+
Each value has an `interval` and a `rating` from 0.0 (best) to 1.0 (worst).
|
|
102
|
+
Lower is better.
|
|
123
103
|
|
|
124
|
-
|
|
104
|
+
```swift
|
|
105
|
+
extension Array where Element == ElectricityGuidance.Value {
|
|
106
|
+
var bestSlot: ElectricityGuidance.Value? { self.min { $0.rating < $1.rating } }
|
|
107
|
+
func slots(atOrBelow limit: Double = 0.3) -> [ElectricityGuidance.Value] {
|
|
108
|
+
filter { $0.rating <= limit }
|
|
125
109
|
}
|
|
126
110
|
}
|
|
127
|
-
|
|
128
|
-
// Find the best time to charge
|
|
129
|
-
func bestChargingWindow(
|
|
130
|
-
in values: [ElectricityGuidance.Value]
|
|
131
|
-
) -> ElectricityGuidance.Value? {
|
|
132
|
-
values.min(by: { $0.rating < $1.rating })
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
// Find all "good" windows below a threshold
|
|
136
|
-
func goodWindows(
|
|
137
|
-
in values: [ElectricityGuidance.Value],
|
|
138
|
-
threshold: Double = 0.3
|
|
139
|
-
) -> [ElectricityGuidance.Value] {
|
|
140
|
-
values.filter { $0.rating <= threshold }
|
|
141
|
-
}
|
|
142
111
|
```
|
|
143
112
|
|
|
144
|
-
###
|
|
113
|
+
### Showing it in SwiftUI
|
|
145
114
|
|
|
146
115
|
```swift
|
|
147
|
-
|
|
148
|
-
import EnergyKit
|
|
149
|
-
|
|
150
|
-
struct GuidanceTimelineView: View {
|
|
116
|
+
struct ForecastList: View {
|
|
151
117
|
let values: [ElectricityGuidance.Value]
|
|
152
118
|
|
|
153
119
|
var body: some View {
|
|
154
|
-
List(values, id: \.interval.start) {
|
|
120
|
+
List(values, id: \.interval.start) { slot in
|
|
155
121
|
HStack {
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
.foregroundStyle(.secondary)
|
|
160
|
-
}
|
|
122
|
+
Text(slot.interval.start, style: .time)
|
|
123
|
+
Text("to")
|
|
124
|
+
Text(slot.interval.end, style: .time)
|
|
161
125
|
Spacer()
|
|
162
|
-
|
|
126
|
+
RatingBadge(rating: slot.rating)
|
|
163
127
|
}
|
|
164
128
|
}
|
|
165
129
|
}
|
|
166
130
|
}
|
|
167
131
|
|
|
168
|
-
struct
|
|
132
|
+
struct RatingBadge: View {
|
|
169
133
|
let rating: Double
|
|
170
134
|
|
|
171
|
-
var
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
var label: String {
|
|
178
|
-
if rating <= 0.3 { return "Good" }
|
|
179
|
-
if rating <= 0.6 { return "Fair" }
|
|
180
|
-
return "Avoid"
|
|
135
|
+
private var style: (label: String, tint: Color) {
|
|
136
|
+
switch rating {
|
|
137
|
+
case ...0.3: ("Good", .green)
|
|
138
|
+
case ...0.6: ("Fair", .yellow)
|
|
139
|
+
default: ("Avoid", .red)
|
|
140
|
+
}
|
|
181
141
|
}
|
|
182
142
|
|
|
183
143
|
var body: some View {
|
|
184
|
-
Text(label)
|
|
185
|
-
.
|
|
186
|
-
.padding(.
|
|
187
|
-
.background(
|
|
188
|
-
.foregroundStyle(
|
|
189
|
-
.clipShape(Capsule())
|
|
144
|
+
Text(style.label)
|
|
145
|
+
.font(.caption.bold())
|
|
146
|
+
.padding(.horizontal, 8)
|
|
147
|
+
.background(style.tint.opacity(0.2), in: .capsule)
|
|
148
|
+
.foregroundStyle(style.tint)
|
|
190
149
|
}
|
|
191
150
|
}
|
|
192
151
|
```
|
|
193
152
|
|
|
194
|
-
##
|
|
195
|
-
|
|
196
|
-
An `EnergyVenue` represents a physical location registered for energy management.
|
|
153
|
+
## Venues
|
|
197
154
|
|
|
198
155
|
```swift
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
// Get a specific venue by ID
|
|
205
|
-
func getVenue(id: UUID) async throws -> EnergyVenue {
|
|
206
|
-
try await EnergyVenue.venue(for: id)
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
// Get a venue matching a HomeKit home
|
|
210
|
-
func getVenueForHome(homeID: UUID) async throws -> EnergyVenue {
|
|
211
|
-
try await EnergyVenue.venue(matchingHomeUniqueIdentifier: homeID)
|
|
212
|
-
}
|
|
156
|
+
let homes = try await EnergyVenue.venues() // every venue
|
|
157
|
+
let remembered = try await EnergyVenue.venue(for: savedVenueID) // by UUID
|
|
158
|
+
let fromHomeKit = try await EnergyVenue.venue(matchingHomeUniqueIdentifier: home.uniqueIdentifier)
|
|
159
|
+
VenueMenu.shared.show(homes.map(\.name) + [remembered.name, fromHomeKit.name])
|
|
213
160
|
```
|
|
214
161
|
|
|
215
|
-
|
|
162
|
+
Always discover venues first. A hardcoded or invented venue UUID fails, and the
|
|
163
|
+
person may have no venue at all.
|
|
216
164
|
|
|
217
|
-
|
|
218
|
-
let venue = try await EnergyVenue.venue(for: venueID)
|
|
219
|
-
print("Venue ID: \(venue.id)")
|
|
220
|
-
print("Venue name: \(venue.name)")
|
|
221
|
-
```
|
|
222
|
-
|
|
223
|
-
## Submitting Load Events
|
|
165
|
+
## Load events
|
|
224
166
|
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
the guidance token returned by EnergyKit. Do not invent a token.
|
|
167
|
+
Load events report actual consumption so the system can build insights. Only
|
|
168
|
+
the device and app that requested guidance submit them, and they carry the
|
|
169
|
+
`guidanceToken` EnergyKit returned. Never make one up.
|
|
229
170
|
|
|
230
|
-
### EV
|
|
171
|
+
### EV charging
|
|
231
172
|
|
|
232
173
|
```swift
|
|
233
|
-
func
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
) async throws {
|
|
174
|
+
func reportCharging(state: ElectricVehicleLoadEvent.Session.State,
|
|
175
|
+
sessionID: UUID, kilowatts: Double, kilowattHours: Double,
|
|
176
|
+
percent: Int, followingGuidance: Bool,
|
|
177
|
+
token: UUID, charger: String, venue: EnergyVenue) async throws {
|
|
238
178
|
let session = ElectricVehicleLoadEvent.Session(
|
|
239
|
-
id:
|
|
240
|
-
state:
|
|
241
|
-
guidanceState:
|
|
242
|
-
wasFollowingGuidance: true,
|
|
243
|
-
guidanceToken: guidanceToken
|
|
244
|
-
)
|
|
179
|
+
id: sessionID,
|
|
180
|
+
state: state,
|
|
181
|
+
guidanceState: .init(wasFollowingGuidance: followingGuidance, guidanceToken: token)
|
|
245
182
|
)
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
stateOfCharge: 45,
|
|
183
|
+
let reading = ElectricVehicleLoadEvent.ElectricalMeasurement(
|
|
184
|
+
stateOfCharge: percent,
|
|
249
185
|
direction: .imported,
|
|
250
|
-
power: Measurement(value:
|
|
251
|
-
energy: Measurement(value:
|
|
186
|
+
power: Measurement(value: kilowatts, unit: .kilowatts),
|
|
187
|
+
energy: Measurement(value: kilowattHours, unit: .kilowattHours)
|
|
252
188
|
)
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
timestamp: Date(),
|
|
256
|
-
measurement: measurement,
|
|
257
|
-
session: session,
|
|
258
|
-
deviceID: deviceID
|
|
259
|
-
)
|
|
260
|
-
|
|
189
|
+
let event = ElectricVehicleLoadEvent(timestamp: .now, measurement: reading,
|
|
190
|
+
session: session, deviceID: charger)
|
|
261
191
|
try await venue.submitEvents([event])
|
|
262
192
|
}
|
|
263
193
|
```
|
|
264
194
|
|
|
265
|
-
###
|
|
195
|
+
### Heating and cooling
|
|
266
196
|
|
|
267
197
|
```swift
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
guidanceToken:
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
id: UUID(),
|
|
276
|
-
state: .active,
|
|
277
|
-
guidanceState: ElectricHVACLoadEvent.Session.GuidanceState(
|
|
278
|
-
wasFollowingGuidance: true,
|
|
279
|
-
guidanceToken: guidanceToken
|
|
280
|
-
)
|
|
281
|
-
)
|
|
282
|
-
|
|
283
|
-
let measurement = ElectricHVACLoadEvent.ElectricalMeasurement(stage: stage)
|
|
284
|
-
|
|
285
|
-
let event = ElectricHVACLoadEvent(
|
|
286
|
-
timestamp: Date(),
|
|
287
|
-
measurement: measurement,
|
|
288
|
-
session: session,
|
|
289
|
-
deviceID: deviceID
|
|
290
|
-
)
|
|
291
|
-
|
|
292
|
-
try await venue.submitEvents([event])
|
|
293
|
-
}
|
|
198
|
+
let hvacSession = ElectricHVACLoadEvent.Session(
|
|
199
|
+
id: runID, state: .active,
|
|
200
|
+
guidanceState: .init(wasFollowingGuidance: true, guidanceToken: token))
|
|
201
|
+
let stageReading = ElectricHVACLoadEvent.ElectricalMeasurement(stage: 2)
|
|
202
|
+
let hvacEvent = ElectricHVACLoadEvent(timestamp: .now, measurement: stageReading,
|
|
203
|
+
session: hvacSession, deviceID: thermostatID)
|
|
204
|
+
try await venue.submitEvents([hvacEvent])
|
|
294
205
|
```
|
|
295
206
|
|
|
296
|
-
### Session
|
|
207
|
+
### Session lifecycle and cadence
|
|
297
208
|
|
|
298
|
-
| State | When
|
|
209
|
+
| State | When |
|
|
299
210
|
|---|---|
|
|
300
|
-
| `.begin` |
|
|
301
|
-
| `.active` |
|
|
302
|
-
| `.end` |
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
and extra samples
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
month.
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
)!
|
|
332
|
-
|
|
333
|
-
let query = ElectricityInsightQuery(
|
|
334
|
-
options: [.cleanliness, .tariff],
|
|
335
|
-
range: DateInterval(
|
|
336
|
-
start: sevenDaysAgo,
|
|
337
|
-
end: Date()
|
|
338
|
-
),
|
|
339
|
-
granularity: .hourly,
|
|
340
|
-
flowDirection: .imported
|
|
341
|
-
)
|
|
211
|
+
| `.begin` | The device starts drawing power |
|
|
212
|
+
| `.active` | Periodic updates while it runs |
|
|
213
|
+
| `.end` | The device stops |
|
|
214
|
+
|
|
215
|
+
- **EV**: a begin and an end event, a steady sample roughly every 15 minutes,
|
|
216
|
+
and extra samples when the user acts, charging pauses, new guidance arrives,
|
|
217
|
+
or power changes quickly.
|
|
218
|
+
- **HVAC**: separate events when equipment starts, at every heating or cooling
|
|
219
|
+
stage change (heat stage 1 to 2, heat to cool, cool to idle), and when it stops.
|
|
220
|
+
- Batch events when you can for performance.
|
|
221
|
+
- Insights exist only for events you submitted.
|
|
222
|
+
- Everyone who shares the associated home in the Home app can see that venue's
|
|
223
|
+
load events. Tell users.
|
|
224
|
+
|
|
225
|
+
## Insights
|
|
226
|
+
|
|
227
|
+
`ElectricityInsightService` returns energy or runtime history. An empty
|
|
228
|
+
`ElectricityInsightQuery.Options` means totals only; add `.cleanliness`,
|
|
229
|
+
`.tariff` or both only when the interface shows those splits. MetricKit power
|
|
230
|
+
metrics are not a replacement: insights come from the EnergyKit load events
|
|
231
|
+
of the managed device.
|
|
232
|
+
|
|
233
|
+
Match granularity to the range. A seven-day chart uses `.hourly`; `.daily`
|
|
234
|
+
needs at least a calendar month.
|
|
235
|
+
|
|
236
|
+
| Granularity | Shortest allowed range |
|
|
237
|
+
|---|---|
|
|
238
|
+
| `.hourly` | One calendar week |
|
|
239
|
+
| `.daily` | One calendar month |
|
|
240
|
+
| `.weekly` | Six months |
|
|
241
|
+
| `.monthly`, `.yearly` | One calendar year |
|
|
342
242
|
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
243
|
+
`ElectricityInsightService` is an actor and `ElectricityInsightRecord` is not
|
|
244
|
+
`Sendable`, so in Swift 6 the returned stream cannot leave the actor: a call
|
|
245
|
+
from a nonisolated or main-actor function fails to compile. Run the loop on the
|
|
246
|
+
service's own isolation with an `isolated` parameter and hand back only
|
|
247
|
+
`Sendable` values.
|
|
347
248
|
|
|
249
|
+
```swift
|
|
250
|
+
func lastWeekUsage(charger: String, venueID: UUID,
|
|
251
|
+
service: isolated ElectricityInsightService = .shared) async throws -> [Double] {
|
|
252
|
+
let week = DateInterval(start: .now.addingTimeInterval(-7 * 86_400), end: .now)
|
|
253
|
+
let query = ElectricityInsightQuery(options: [], range: week,
|
|
254
|
+
granularity: .hourly, flowDirection: .imported)
|
|
255
|
+
var totals: [Double] = []
|
|
256
|
+
let stream = try await service.energyInsights(forDeviceID: charger, using: query,
|
|
257
|
+
atVenue: venueID)
|
|
348
258
|
for await record in stream {
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
let cleaner = record.dataByGridCleanliness?.cleaner {
|
|
353
|
-
print("Cleaner: \(cleaner)")
|
|
259
|
+
totals.append(record.totalEnergy?.converted(to: .kilowattHours).value ?? 0)
|
|
260
|
+
if #available(iOS 26.1, *) { // only needed when deploying to 26.0
|
|
261
|
+
_ = record.dataByGridCleanliness?.cleaner // needs .cleanliness in options
|
|
354
262
|
}
|
|
355
263
|
}
|
|
264
|
+
return totals
|
|
356
265
|
}
|
|
357
266
|
```
|
|
358
267
|
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
`.yearly`. Choose a range that matches Apple's minimum aggregation windows:
|
|
362
|
-
hourly for at least a calendar week, daily for at least a calendar month,
|
|
363
|
-
weekly for at least six months, and monthly or yearly for at least a calendar
|
|
364
|
-
year. See [references/energykit-patterns.md](references/energykit-patterns.md) for full insight examples.
|
|
365
|
-
|
|
366
|
-
## Common Mistakes
|
|
367
|
-
|
|
368
|
-
### DON'T: Forget the EnergyKit entitlement
|
|
369
|
-
|
|
370
|
-
Without the entitlement, EnergyKit APIs can fail with permission errors such as
|
|
371
|
-
`EnergyKitError.permissionDenied`. Treat the EnergyKit capability as setup, not
|
|
372
|
-
as an implementation detail to discover after writing queries.
|
|
268
|
+
For time running rather than energy, call
|
|
269
|
+
`runtimeInsights(forDeviceID:using:atVenue:)` in the same isolated way.
|
|
373
270
|
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
EnergyKit is not available in all regions. Handle the `.unsupportedRegion`
|
|
377
|
-
and `.guidanceUnavailable` errors.
|
|
271
|
+
## Errors
|
|
378
272
|
|
|
379
273
|
```swift
|
|
380
|
-
// WRONG: Assume guidance is always available
|
|
381
|
-
for try await guidance in service.guidance(using: query, at: venueID) {
|
|
382
|
-
updateUI(guidance)
|
|
383
|
-
}
|
|
384
|
-
|
|
385
|
-
// CORRECT: Handle region-specific errors
|
|
386
274
|
do {
|
|
387
|
-
|
|
388
|
-
updateUI(guidance)
|
|
389
|
-
}
|
|
275
|
+
try await watchChargingWindows(for: venueID)
|
|
390
276
|
} catch let error as EnergyKitError {
|
|
391
277
|
switch error {
|
|
392
|
-
case .unsupportedRegion:
|
|
393
|
-
|
|
394
|
-
case .
|
|
395
|
-
|
|
396
|
-
case .
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
showPermissionDeniedMessage()
|
|
400
|
-
case .serviceUnavailable:
|
|
401
|
-
retryLater()
|
|
402
|
-
case .rateLimitExceeded:
|
|
403
|
-
backOff()
|
|
404
|
-
default:
|
|
405
|
-
break
|
|
278
|
+
case .unsupportedRegion: showMessage("Grid forecasts aren't offered where you live yet.")
|
|
279
|
+
case .guidanceUnavailable: showMessage("No forecast right now.")
|
|
280
|
+
case .venueUnavailable: showMessage("Set up your home in the Home app first.")
|
|
281
|
+
case .permissionDenied: showMessage("EnergyKit access is off.")
|
|
282
|
+
case .serviceUnavailable: scheduleRetry()
|
|
283
|
+
case .rateLimitExceeded: backOff()
|
|
284
|
+
default: showMessage("Something went wrong.")
|
|
406
285
|
}
|
|
407
286
|
}
|
|
408
287
|
```
|
|
409
288
|
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
// CORRECT: Full session lifecycle
|
|
439
|
-
try await venue.submitEvents([beginEvent])
|
|
440
|
-
// ... periodic active events ...
|
|
441
|
-
try await venue.submitEvents([activeEvent])
|
|
442
|
-
// ... when done ...
|
|
443
|
-
try await venue.submitEvents([endEvent])
|
|
444
|
-
```
|
|
445
|
-
|
|
446
|
-
### DON'T: Query guidance without a venue
|
|
447
|
-
|
|
448
|
-
EnergyKit requires a venue ID. List venues first and select the appropriate one.
|
|
449
|
-
|
|
450
|
-
```swift
|
|
451
|
-
// WRONG: Use a hardcoded UUID
|
|
452
|
-
let fakeID = UUID()
|
|
453
|
-
service.guidance(using: query, at: fakeID) // Will fail
|
|
454
|
-
|
|
455
|
-
// CORRECT: Discover venues first
|
|
456
|
-
let venues = try await EnergyVenue.venues()
|
|
457
|
-
guard let venue = venues.first else {
|
|
458
|
-
showNoVenueSetup()
|
|
459
|
-
return
|
|
460
|
-
}
|
|
461
|
-
let guidanceStream = service.guidance(using: query, at: venue.id)
|
|
462
|
-
```
|
|
463
|
-
|
|
464
|
-
## Review Checklist
|
|
465
|
-
|
|
466
|
-
- [ ] `com.apple.developer.energykit` entitlement added to the project
|
|
467
|
-
- [ ] `EnergyKitError.unsupportedRegion` handled with user-facing message
|
|
468
|
-
- [ ] `EnergyKitError.permissionDenied` handled gracefully
|
|
469
|
-
- [ ] Guidance token stored and passed to load event submissions
|
|
470
|
-
- [ ] No placeholder or fabricated guidance tokens are used in load events
|
|
471
|
-
- [ ] The same EnergyKit-capable device/app that requested guidance submits the corresponding load events
|
|
472
|
-
- [ ] Venues discovered via `EnergyVenue.venues()` before querying guidance
|
|
473
|
-
- [ ] Load event sessions follow `.begin` -> `.active` -> `.end` lifecycle
|
|
474
|
-
- [ ] EV/HVAC event cadence follows Apple guidance and events are batched when practical
|
|
475
|
-
- [ ] `ElectricityGuidance.Value.rating` interpreted correctly (lower is better)
|
|
476
|
-
- [ ] `SuggestedAction` matches the device type (`.shift` for EV, `.reduce` for HVAC)
|
|
477
|
-
- [ ] Insight queries use appropriate minimum ranges for their granularity
|
|
478
|
-
- [ ] Empty insight query options are treated as totals-only, not as cleanliness or tariff requests
|
|
479
|
-
- [ ] MetricKit power telemetry is not used as a substitute for EnergyKit load events or insights
|
|
480
|
-
- [ ] Grid cleanliness insight fields are guarded for iOS/iPadOS 26.1+
|
|
481
|
-
- [ ] Users understand load events are shared with people who share the Home
|
|
482
|
-
- [ ] Rate limiting handled via `EnergyKitError.rateLimitExceeded`
|
|
483
|
-
- [ ] Service unavailability handled with retry logic
|
|
289
|
+
## Common mistakes
|
|
290
|
+
|
|
291
|
+
- Missing entitlement, which surfaces as `permissionDenied`. Treat the capability as setup.
|
|
292
|
+
- Assuming every region is supported. Handle `.unsupportedRegion` and `.guidanceUnavailable`.
|
|
293
|
+
- Dropping the guidance token, or passing `UUID()` instead of the stored `guidanceToken` from the device that fetched guidance.
|
|
294
|
+
- Sending one event with no lifecycle. Send `.begin`, then `.active` updates, then `.end`.
|
|
295
|
+
- Querying with a fixed or fake venue UUID instead of calling `EnergyVenue.venues()` and handling an empty result.
|
|
296
|
+
|
|
297
|
+
## Review checklist
|
|
298
|
+
|
|
299
|
+
- [ ] EnergyKit entitlement present
|
|
300
|
+
- [ ] `.unsupportedRegion` shown to the user clearly
|
|
301
|
+
- [ ] `.permissionDenied` handled
|
|
302
|
+
- [ ] Guidance token kept and attached to load events
|
|
303
|
+
- [ ] No placeholder or invented tokens
|
|
304
|
+
- [ ] Load events come from the device and app that requested guidance
|
|
305
|
+
- [ ] Venues discovered before guidance is requested
|
|
306
|
+
- [ ] Sessions go `.begin`, `.active`, `.end`
|
|
307
|
+
- [ ] EV and HVAC cadence follow Apple's guidance; events batched where practical
|
|
308
|
+
- [ ] Ratings treated as lower-is-better
|
|
309
|
+
- [ ] `.shift` for EV, `.reduce` for HVAC
|
|
310
|
+
- [ ] Insight ranges meet the granularity minimums
|
|
311
|
+
- [ ] Empty options understood as totals only
|
|
312
|
+
- [ ] No MetricKit stand-in for insights
|
|
313
|
+
- [ ] Grid-cleanliness fields, venue lookup and the newer error cases behind a 26.1 check when the target is 26.0
|
|
314
|
+
- [ ] Users told that Home members can see load events
|
|
315
|
+
- [ ] `.rateLimitExceeded` backs off
|
|
316
|
+
- [ ] `.serviceUnavailable` retried later
|
|
484
317
|
|
|
485
318
|
## References
|
|
486
319
|
|
|
487
|
-
-
|
|
488
|
-
- [EnergyKit
|
|
489
|
-
- [ElectricityGuidance](https://
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
- [EnergyVenue](https://
|
|
494
|
-
- [ElectricVehicleLoadEvent](https://
|
|
495
|
-
- [ElectricHVACLoadEvent](https://
|
|
496
|
-
- [ElectricityInsightService](https://
|
|
497
|
-
- [ElectricityInsightRecord](https://
|
|
498
|
-
- [ElectricityInsightQuery](https://
|
|
499
|
-
- [EnergyKitError](https://
|
|
500
|
-
-
|
|
320
|
+
- [EnergyKit patterns](references/energykit-patterns.md) - managers, dashboard, charts, retries, venue discovery
|
|
321
|
+
- [EnergyKit](https://developer.apple.com/documentation/energykit)
|
|
322
|
+
- [ElectricityGuidance](https://developer.apple.com/documentation/energykit/electricityguidance),
|
|
323
|
+
[Service](https://developer.apple.com/documentation/energykit/electricityguidance/service),
|
|
324
|
+
[Query](https://developer.apple.com/documentation/energykit/electricityguidance/query),
|
|
325
|
+
[Value](https://developer.apple.com/documentation/energykit/electricityguidance/value)
|
|
326
|
+
- [EnergyVenue](https://developer.apple.com/documentation/energykit/energyvenue)
|
|
327
|
+
- [ElectricVehicleLoadEvent](https://developer.apple.com/documentation/energykit/electricvehicleloadevent)
|
|
328
|
+
- [ElectricHVACLoadEvent](https://developer.apple.com/documentation/energykit/electrichvacloadevent)
|
|
329
|
+
- [ElectricityInsightService](https://developer.apple.com/documentation/energykit/electricityinsightservice)
|
|
330
|
+
- [ElectricityInsightRecord](https://developer.apple.com/documentation/energykit/electricityinsightrecord)
|
|
331
|
+
- [ElectricityInsightQuery](https://developer.apple.com/documentation/energykit/electricityinsightquery)
|
|
332
|
+
- [EnergyKitError](https://developer.apple.com/documentation/energykit/energykiterror)
|
|
333
|
+
- Apple's article on optimizing home electricity usage
|