使用 EnergyKit 查詢電網電力預測並提交負載事件,幫助使用者最佳化家庭用電。適用於建置智慧家庭 App、電動車充電控制、空調排程或能源管理儀表板,引導使用者在電網更乾淨或電價更低的時段用電。
EnergyKit
利用電網清潔度與成本指引,來轉移或減少受管理裝置的負載。
若要取得受管理裝置的洞察,請即時提交該裝置的真實負載事件。
Beta 敏感注意。 核心 EnergyKit 於 iOS/iPadOS 26 推出。iOS/iPadOS 27 的
ElectricalLoadDevice與 Home 端 LoadEvents 體驗仍為 Beta;在依賴這些 API 前,
請重新確認最新的 Apple 文件。
目錄
設定
授權與版本差異
| 執行環境 | 負載事件裝置 API | 功能權限 |
|---|---|---|
| iOS/iPadOS 26.x | deviceID: 相容性初始化器 |
EnergyKit |
| iOS/iPadOS 27+ Beta | ElectricalLoadDevice 搭配 device: 初始化器 |
EnergyKit;若要整合 Home App 需額外加入 EnergyKit LoadEvents |
所有 EnergyKit 使用都需要 com.apple.developer.energykit;請在 App Target 中啟用 EnergyKit 功能。
在 iOS/iPadOS 27+ 上,僅當 App 需要在 Home App 中顯示裝置名稱、能源情境、活動記錄、歷史圖表或趨勢通知時,
才需加入 EnergyKit LoadEvents 功能(com.apple.developer.energykit.loadevents-experience)。
該 Home 體驗需要同時具備兩項授權。缺少權限可能導致 EnergyKitError.permissionDenied。
匯入
import EnergyKit
平台可用性: 核心 EnergyKit API 適用於 iOS/iPadOS 26.0 以上。部分洞察細分 API(包括電網清潔度分類)
為 26.1 以上,需要加上可用性檢查。Apple 目前僅針對美國本土提供電力指引文件;請處理 EnergyKitError.unsupportedRegion。
核心概念
EnergyKit 提供兩項主要功能:
- 電力指引 — 時間加權的預測,告訴 App 何時用電較乾淨,以及(當費率資料可用時)何時較便宜
- 負載事件 — 來自受管理裝置(電動車充電器、空調)的遙測資料,由同一個請求指引的裝置/App 提交,以便 EnergyKit 產生洞察
主要型別
| 型別 | 角色 |
|---|---|
ElectricityGuidance |
包含加權時間區間的預測資料 |
ElectricityGuidance.Service |
取得指引資料的介面 |
ElectricityGuidance.Query |
指定轉移或減少動作的查詢 |
ElectricityGuidance.Value |
包含評分(0.0-1.0)的時間區間 |
EnergyVenue |
已註冊能源管理的實際位置(家庭) |
ElectricVehicleLoadEvent |
電動車充電器遙測的負載事件 |
ElectricHVACLoadEvent |
空調系統遙測的負載事件 |
ElectricalLoadDevice |
iOS/iPadOS 27+ Beta 中負載事件的裝置識別 |
ElectricityInsightService |
查詢能源/執行時間洞察的服務 |
ElectricityInsightRecord |
歷史能源或執行時間資料,可依費率或 26.1+ 電網清潔度細分 |
ElectricityInsightQuery |
查詢歷史洞察資料的查詢 |
建議動作
| 動作 | 使用情境 |
|---|---|
.shift |
可將用電移至不同時間的裝置(電動車充電) |
.reduce |
可降低用電量而不停止的裝置(空調調節) |
查詢電力指引
使用 ElectricityGuidance.Service 取得某個場域的預測串流。
import EnergyKit
func observeGuidance(venueID: UUID) async throws {
let query = ElectricityGuidance.Query(suggestedAction: .shift)
let service = ElectricityGuidance.sharedService
let guidanceStream = service.guidance(using: query, at: venueID)
for try await guidance in guidanceStream {
print("Guidance token: \(guidance.guidanceToken)")
print("Interval: \(guidance.interval)")
print("Venue: \(guidance.energyVenueID)")
// 檢查是否有費率方案資訊
if guidance.options.contains(.guidanceIncorporatesRatePlan) {
print("已納入費率方案資料")
}
if guidance.options.contains(.locationHasRatePlan) {
print("該地點有費率方案")
}
processGuidanceValues(guidance.values)
}
}
處理指引數值
每個 ElectricityGuidance.Value 包含一個時間區間與一個 0.0 到 1.0 的評分。
評分越低表示用電時機越好。
func processGuidanceValues(_ values: [ElectricityGuidance.Value]) {
for value in values {
let interval = value.interval
let rating = value.rating // 0.0(最佳)到 1.0(最差)
print("從 \(interval.start) 到 \(interval.end):評分 \(rating)")
}
}
// 找出最佳充電時段
func bestChargingWindow(
in values: [ElectricityGuidance.Value]
) -> ElectricityGuidance.Value? {
values.min(by: { $0.rating < $1.rating })
}
// 找出低於門檻的所有「良好」時段
func goodWindows(
in values: [ElectricityGuidance.Value],
threshold: Double = 0.3
) -> [ElectricityGuidance.Value] {
values.filter { $0.rating <= threshold }
}
在 SwiftUI 中顯示指引
import SwiftUI
import EnergyKit
struct GuidanceTimelineView: View {
let values: [ElectricityGuidance.Value]
var body: some View {
List(values, id: \.interval.start) { value in
HStack {
VStack(alignment: .leading) {
Text(value.interval.start, style: .time)
Text(value.interval.end, style: .time)
.foregroundStyle(.secondary)
}
Spacer()
RatingIndicator(rating: value.rating)
}
}
}
}
struct RatingIndicator: View {
let rating: Double
var color: Color {
if rating <= 0.3 { return .green }
if rating <= 0.6 { return .yellow }
return .red
}
var label: String {
if rating <= 0.3 { return "良好" }
if rating <= 0.6 { return "普通" }
return "避免"
}
var body: some View {
Text(label)
.padding(.horizontal)
.padding(.vertical)
.background(color.opacity(0.2))
.foregroundStyle(color)
.clipShape(Capsule())
}
}
能源場域
EnergyVenue 代表已註冊能源管理的實際位置。
// 列出所有場域
func listVenues() async throws -> [EnergyVenue] {
try await EnergyVenue.venues()
}
// 根據 ID 取得特定場域
func getVenue(id: UUID) async throws -> EnergyVenue {
try await EnergyVenue.venue(for: id)
}
// 取得與 HomeKit 家庭相符的場域
func getVenueForHome(homeID: UUID) async throws -> EnergyVenue {
try await EnergyVenue.venue(matchingHomeUniqueIdentifier: homeID)
}
場域屬性
let venue = try await EnergyVenue.venue(for: venueID)
print("場域 ID:\(venue.id)")
print("場域名稱:\(venue.name)")
提交負載事件
將裝置的用電資料回報給系統。這有助於系統產生電力洞察。
必須由同一個具備 EnergyKit 能力的裝置/App(即請求電力指引的那個)提交對應的負載事件,
並使用 EnergyKit 回傳的指引 token。請勿自行偽造 token。
電動車充電器負載事件
func submitEVBeginEvent(
at venue: EnergyVenue,
guidanceToken: UUID,
deviceID: String,
deviceName: String
) async throws {
let session = ElectricVehicleLoadEvent.Session(
id: UUID(),
state: .begin,
guidanceState: ElectricVehicleLoadEvent.Session.GuidanceState(
wasFollowingGuidance: true,
guidanceToken: guidanceToken
)
)
let measurement = ElectricVehicleLoadEvent.ElectricalMeasurement(
stateOfCharge: 45,
direction: .imported,
power: Measurement(value: 0, unit: .kilowatts),
energy: Measurement(value: 0, unit: .kilowattHours)
)
let event: ElectricVehicleLoadEvent
if #available(iOS 27.0, iPadOS 27.0, *) {
let device = ElectricalLoadDevice(
id: deviceID,
name: deviceName,
type: .electricVehicle
)
event = ElectricVehicleLoadEvent(
timestamp: Date(), measurement: measurement,
session: session, device: device
)
} else {
// iOS/iPadOS 26 相容性;在 iOS 27 SDK 中已棄用。
event = ElectricVehicleLoadEvent(
timestamp: Date(), measurement: measurement,
session: session, deviceID: deviceID
)
}
try await venue.submitEvents([event])
}
空調負載事件
func submitHVACEvent(
at venue: EnergyVenue,
guidanceToken: UUID,
stage: Int,
deviceID: String,
deviceName: String
) async throws {
let session = ElectricHVACLoadEvent.Session(
id: UUID(),
state: .active,
guidanceState: ElectricHVACLoadEvent.Session.GuidanceState(
wasFollowingGuidance: true,
guidanceToken: guidanceToken
)
)
let measurement = ElectricHVACLoadEvent.ElectricalMeasurement(stage: stage)
let event: ElectricHVACLoadEvent
if #available(iOS 27.0, iPadOS 27.0, *) {
let device = ElectricalLoadDevice(
id: deviceID,
name: deviceName,
type: .hvac
)
event = ElectricHVACLoadEvent(
timestamp: Date(), measurement: measurement,
session: session, device: device
)
} else {
// iOS/iPadOS 26 相容性;在 iOS 27 SDK 中已棄用。
event = ElectricHVACLoadEvent(
timestamp: Date(), measurement: measurement,
session: session, deviceID: deviceID
)
}
try await venue.submitEvents([event])
}
工作階段狀態
| 狀態 | 使用時機 |
|---|---|
.begin |
裝置開始用電 |
.active |
裝置正在用電(定期更新) |
.end |
裝置停止用電 |
請保持 .begin → .active → .end 的順序,並即時提交事件,避免長時間累積批次。
對於電動車充電,請在開始時提交 .begin(功率與能量為零),大約每 15 分鐘或發生顯著變化時提交 .active,
結束時提交 .end(功率為零,能量為累計值)。保留未確認的事件,並在遇到 EnergyKitError.rateLimitExceeded 時
以有限退避方式重試。請參考
EV 工作階段管理員
或 HVAC 控制管理員 以取得裝置特定的生命週期處理。
僅在 iOS/iPadOS 27+ 且同時具備基礎 EnergyKit 與 EnergyKit LoadEvents 功能時,
才承諾 Home App 中的裝置名稱、能源情境、活動記錄、圖表與趨勢通知。
電力洞察
使用 ElectricityInsightService 查詢裝置的歷史能源與執行時間資料。
空的 ElectricityInsightQuery.Options 選項集只會回傳總計;不會填入清潔度或費率細分。
僅在 UI 需要這些細分時才請求 .cleanliness 及/或 .tariff。
請勿以 MetricKit 的 App 電源指標取代 EnergyKit 洞察;EnergyKit 洞察依賴於為受管理裝置提交的 EnergyKit 負載事件。
根據請求的範圍選擇洞察粒度。對於七天的檢視,請查詢 .hourly;僅當查詢涵蓋至少一個日曆月時才使用 .daily。
func queryEnergyInsights(deviceID: String, venueID: UUID) async throws {
let sevenDaysAgo = Calendar.current.date(
byAdding: .day,
value: -7,
to: Date()
)!
let query = ElectricityInsightQuery(
options: [.cleanliness, .tariff],
range: DateInterval(
start: sevenDaysAgo,
end: Date()
),
granularity: .hourly,
flowDirection: .imported
)
let service = ElectricityInsightService.shared
let stream = try await service.energyInsights(
forDeviceID: deviceID, using: query, atVenue: venueID
)
for await record in stream {
if let total = record.totalEnergy { print("總計:\(total)") }
if #available(iOS 26.1, iPadOS 26.1, *),
let cleaner = record.dataByGridCleanliness?.cleaner {
print("較乾淨:\(cleaner)")
}
}
}
對於執行時間資料,請使用 runtimeInsights(forDeviceID:using:atVenue:) 而非能源資料。
粒度選項:.hourly、.daily、.weekly、.monthly、.yearly。
選擇符合 Apple 最小彙總視窗的範圍:至少一個日曆週使用每小時、至少一個日曆月使用每日、
至少六個月使用每週、至少一個日曆年使用每月或每年。
完整的洞察範例請參閱 references/energykit-patterns.md。
常見錯誤
| 錯誤 | 修正 |
|---|---|
| 在設定功能權限前進行查詢 | 確認 EnergyKit 授權,並處理 .permissionDenied。 |
| 假設每個區域都有指引 | Apple 目前僅在美國本土提供指引文件;請處理不支援區域及場域/指引不可用的狀態。 |
| 偽造或丟棄指引 token | 在請求裝置上保留真實 token,並在提交負載事件時一併提交。 |
| 發送孤立或延遲的負載樣本 | 保持 .begin → .active → .end 順序,即時提交,並在提交成功前保留事件。 |
使用 deviceID: 作為目前預設 |
使用 iOS/iPadOS 27+ 的 ElectricalLoadDevice 與 device:;僅在 26.x 執行環境分支保留 deviceID:。 |
| 使用寫死的場域 ID | 使用 EnergyVenue.venues() 探索場域,並選擇目標場域。 |
審查清單
- [ ] 已加入基礎 EnergyKit 功能;iOS/iPadOS 27+ 的 Home 整合也加入了 EnergyKit LoadEvents
- [ ] 已處理區域、權限、場域探索、指引不可用及服務錯誤
- [ ] 真實的指引 token 保留在請求裝置/App 中,並與其提交的負載事件一同送出
- [ ] iOS/iPadOS 27+ 使用
ElectricalLoadDevice/device:;deviceID:僅限於 26.x 相容性 - [ ]
.begin → .active → .end事件遵循裝置節奏,即時提交,在失敗時保留,並以有限退避重試速率限制 - [ ] 評分/動作的解釋正確;洞察選項、可用性、粒度與最小範圍符合 UI 需求
- [ ] 未以 MetricKit 遙測取代 EnergyKit 負載事件或洞察
參考資料
- 應用程式架構、EV/HVAC 工作階段節奏、儀表板呈現、洞察、錯誤與場域探索的延伸工作流程:
references/energykit-patterns.md - EnergyKit 框架
- ElectricityGuidance
- ElectricityGuidance.Service
- ElectricityGuidance.Query
- ElectricityGuidance.Value
- EnergyVenue
- ElectricalLoadDevice
- ElectricVehicleLoadEvent
- ElectricHVACLoadEvent
- ElectricityInsightService
- ElectricityInsightRecord
- ElectricityInsightQuery
- EnergyKitError
- EnergyKit 授權
- EnergyKit LoadEvents 授權
- 提供電動車充電歷史
- 最佳化家庭用電




