energykit
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseEnergyKit
EnergyKit
Provide grid electricity forecasts to help users choose when to use electricity.
EnergyKit identifies times when there is relatively cleaner or less expensive
electricity on the grid, enabling apps to shift or reduce load accordingly.
Targets Swift 6.2 / iOS 26+.
提供电网电力预测,帮助用户选择用电时间。
EnergyKit可识别电网供电相对更清洁或更便宜的时段,让应用能够相应地转移或减少负载。适配Swift 6.2 / iOS 26+。
Contents
目录
Setup
安装配置
Entitlement
权限配置
EnergyKit requires the entitlement. Add it
to your app's entitlements file.
com.apple.developer.energykitEnergyKit需要权限,请将其添加到应用的权限配置文件中。
com.apple.developer.energykitImport
导入
swift
import EnergyKitPlatform availability: iOS 26+, iPadOS 26+.
swift
import EnergyKit支持平台: iOS 26+, iPadOS 26+.
Core Concepts
核心概念
EnergyKit provides two main capabilities:
- Electricity Guidance -- time-weighted forecasts telling apps when electricity is cleaner or cheaper, so devices can shift or reduce consumption
- Load Events -- telemetry from devices (EV chargers, HVAC) submitted back to the system to track how well the app follows guidance
EnergyKit提供两大核心能力:
- 用电指引——带时间权重的预测数据,告知应用何时电力更清洁或更便宜,以便设备转移或降低功耗
- 负载事件——来自设备(EV充电桩、HVAC)的遥测数据回传给系统,用于追踪应用遵循指引的效果
Key Types
核心类型
| Type | Role |
|---|---|
| Forecast data with weighted time intervals |
| Interface for obtaining guidance data |
| Query specifying shift or reduce action |
| A time interval with a rating (0.0-1.0) |
| A physical location (home) registered for energy management |
| Load event for EV charger telemetry |
| Load event for HVAC system telemetry |
| Service for querying energy/runtime insights |
| Historical energy data broken down by cleanliness/tariff |
| Query for historical insight data |
| 类型 | 作用 |
|---|---|
| 带加权时间间隔的预测数据 |
| 获取指引数据的接口 |
| 指定转移或降低负载操作的查询对象 |
| 带评分(0.0-1.0)的时间间隔对象 |
| 注册用于能源管理的物理地点(家庭) |
| 对应EV充电桩遥测的负载事件 |
| 对应HVAC系统遥测的负载事件 |
| 查询能源/运行时长洞察的服务 |
| 按清洁度/电价拆分的历史能源数据 |
| 查询历史洞察数据的查询对象 |
Suggested Actions
建议操作
| Action | Use Case |
|---|---|
| Devices that can move consumption to a different time (EV charging) |
| Devices that can lower consumption without stopping (HVAC setback) |
| 操作 | 使用场景 |
|---|---|
| 可将用电转移到其他时段的设备(如EV充电) |
| 可在不停止运行的前提下降低功耗的设备(如HVAC调温) |
Querying Electricity Guidance
查询用电指引
Use to get a forecast stream for a venue.
ElectricityGuidance.Serviceswift
import EnergyKit
func observeGuidance(venueID: UUID) async throws {
let query = ElectricityGuidance.Query(suggestedAction: .shift)
let service = ElectricityGuidance.sharedService // Verify access pattern against Xcode 26 SDK
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)")
// Check if rate plan information is available
if guidance.options.contains(.guidanceIncorporatesRatePlan) {
print("Rate plan data incorporated")
}
if guidance.options.contains(.locationHasRatePlan) {
print("Location has a rate plan")
}
processGuidanceValues(guidance.values)
}
}使用获取某个场所的预测流。
ElectricityGuidance.Serviceswift
import EnergyKit
func observeGuidance(venueID: UUID) async throws {
let query = ElectricityGuidance.Query(suggestedAction: .shift)
let service = ElectricityGuidance.sharedService // 对照Xcode 26 SDK验证访问模式
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)
}
}Working with Guidance Values
处理指引数值
Each contains a time interval and a rating
from 0.0 to 1.0. Lower ratings indicate better times to use electricity.
ElectricityGuidance.Valueswift
func processGuidanceValues(_ values: [ElectricityGuidance.Value]) {
for value in values {
let interval = value.interval
let rating = value.rating // 0.0 (best) to 1.0 (worst)
print("From \(interval.start) to \(interval.end): rating \(rating)")
}
}
// Find the best time to charge
func bestChargingWindow(
in values: [ElectricityGuidance.Value]
) -> ElectricityGuidance.Value? {
values.min(by: { $0.rating < $1.rating })
}
// Find all "good" windows below a threshold
func goodWindows(
in values: [ElectricityGuidance.Value],
threshold: Double = 0.3
) -> [ElectricityGuidance.Value] {
values.filter { $0.rating <= threshold }
}每个包含一个时间间隔和0.0到1.0的评分,评分越低代表用电时机越好。
ElectricityGuidance.Valueswift
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 }
}Displaying Guidance in SwiftUI
在SwiftUI中展示指引
swift
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 "Good" }
if rating <= 0.6 { return "Fair" }
return "Avoid"
}
var body: some View {
Text(label)
.padding(.horizontal, 8)
.padding(.vertical, 4)
.background(color.opacity(0.2))
.foregroundStyle(color)
.clipShape(Capsule())
}
}swift
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, 8)
.padding(.vertical, 4)
.background(color.opacity(0.2))
.foregroundStyle(color)
.clipShape(Capsule())
}
}Energy Venues
能源场所
An represents a physical location registered for energy management.
EnergyVenueswift
// List all venues
func listVenues() async throws -> [EnergyVenue] {
try await EnergyVenue.venues()
}
// Get a specific venue by ID
func getVenue(id: UUID) async throws -> EnergyVenue {
try await EnergyVenue.venue(for: id)
}
// Get a venue matching a HomeKit home
func getVenueForHome(homeID: UUID) async throws -> EnergyVenue {
try await EnergyVenue.venue(matchingHomeUniqueIdentifier: homeID)
}EnergyVenueswift
// 列出所有场所
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)
}Venue Properties
场所属性
swift
let venue = try await EnergyVenue.venue(for: venueID)
print("Venue ID: \(venue.id)")
print("Venue name: \(venue.name)")swift
let venue = try await EnergyVenue.venue(for: venueID)
print("场所ID: \(venue.id)")
print("场所名称: \(venue.name)")Submitting Load Events
提交负载事件
Report device consumption data back to the system. This helps the system
improve future guidance accuracy.
向系统上报设备用电数据,可帮助系统提升未来指引的准确性。
EV Charger Load Events
EV充电桩负载事件
swift
func submitEVChargingEvent(
at venue: EnergyVenue,
guidanceToken: UUID,
deviceID: 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: 7.2, unit: .kilowatts),
energy: Measurement(value: 0, unit: .kilowattHours)
)
let event = ElectricVehicleLoadEvent(
timestamp: Date(),
measurement: measurement,
session: session,
deviceID: deviceID
)
try await venue.submitEvents([event])
}swift
func submitEVChargingEvent(
at venue: EnergyVenue,
guidanceToken: UUID,
deviceID: 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: 7.2, unit: .kilowatts),
energy: Measurement(value: 0, unit: .kilowattHours)
)
let event = ElectricVehicleLoadEvent(
timestamp: Date(),
measurement: measurement,
session: session,
deviceID: deviceID
)
try await venue.submitEvents([event])
}HVAC Load Events
HVAC负载事件
swift
func submitHVACEvent(
at venue: EnergyVenue,
guidanceToken: UUID,
stage: Int,
deviceID: 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(
timestamp: Date(),
measurement: measurement,
session: session,
deviceID: deviceID
)
try await venue.submitEvents([event])
}swift
func submitHVACEvent(
at venue: EnergyVenue,
guidanceToken: UUID,
stage: Int,
deviceID: 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(
timestamp: Date(),
measurement: measurement,
session: session,
deviceID: deviceID
)
try await venue.submitEvents([event])
}Session States
会话状态
| State | When to Use |
|---|---|
| Device starts consuming electricity |
| Device is actively consuming (periodic updates) |
| Device stops consuming electricity |
| 状态 | 使用时机 |
|---|---|
| 设备开始用电时 |
| 设备正在用电时(周期性上报) |
| 设备停止用电时 |
Electricity Insights
用电洞察
Query historical energy and runtime data for devices using
.
ElectricityInsightServiceswift
func queryEnergyInsights(deviceID: String, venueID: UUID) async throws {
let query = ElectricityInsightQuery(
options: [.cleanliness, .tariff],
range: DateInterval(
start: Calendar.current.date(byAdding: .day, value: -7, to: Date())!,
end: Date()
),
granularity: .daily,
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: \(total)") }
if let cleaner = record.dataByGridCleanliness?.cleaner {
print("Cleaner: \(cleaner)")
}
}
}Use for runtime data instead
of energy. Granularity options: , , , ,
. See for full insight examples.
runtimeInsights(forDeviceID:using:atVenue:).hourly.daily.weekly.monthly.yearlyreferences/energykit-patterns.md使用查询设备的历史能源和运行时长数据。
ElectricityInsightServiceswift
func queryEnergyInsights(deviceID: String, venueID: UUID) async throws {
let query = ElectricityInsightQuery(
options: [.cleanliness, .tariff],
range: DateInterval(
start: Calendar.current.date(byAdding: .day, value: -7, to: Date())!,
end: Date()
),
granularity: .daily,
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 let cleaner = record.dataByGridCleanliness?.cleaner {
print("更清洁时段: \(cleaner)")
}
}
}如需查询运行时长数据而非能源数据,请使用。时间粒度选项:(每小时)、(每日)、(每周)、(每月)、(每年)。完整的洞察示例请查看。
runtimeInsights(forDeviceID:using:atVenue:).hourly.daily.weekly.monthly.yearlyreferences/energykit-patterns.mdCommon Mistakes
常见错误
DON'T: Forget the EnergyKit entitlement
禁止:忘记配置EnergyKit权限
Without the entitlement, all EnergyKit calls fail silently or throw errors.
swift
// WRONG: No entitlement configured
let service = ElectricityGuidance.sharedService // Will fail
// CORRECT: Add com.apple.developer.energykit to entitlements
// Then use the service
let service = ElectricityGuidance.sharedService没有配置权限的话,所有EnergyKit调用会静默失败或抛出错误。
swift
// 错误:未配置权限
let service = ElectricityGuidance.sharedService // 会失败
// 正确:将com.apple.developer.energykit添加到权限配置
// 再使用服务
let service = ElectricityGuidance.sharedServiceDON'T: Ignore unsupported regions
禁止:忽略不支持的地区
EnergyKit is not available in all regions. Handle the
and errors.
.unsupportedRegion.guidanceUnavailableswift
// WRONG: Assume guidance is always available
for try await guidance in service.guidance(using: query, at: venueID) {
updateUI(guidance)
}
// CORRECT: Handle region-specific errors
do {
for try await guidance in service.guidance(using: query, at: venueID) {
updateUI(guidance)
}
} catch let error as EnergyKitError {
switch error {
case .unsupportedRegion:
showUnsupportedRegionMessage()
case .guidanceUnavailable:
showGuidanceUnavailableMessage()
case .venueUnavailable:
showNoVenueMessage()
case .permissionDenied:
showPermissionDeniedMessage()
case .serviceUnavailable:
retryLater()
case .rateLimitExceeded:
backOff()
default:
break
}
}EnergyKit并非在所有地区都可用,请处理和错误。
.unsupportedRegion.guidanceUnavailableswift
// 错误:默认指引始终可用
for try await guidance in service.guidance(using: query, at: venueID) {
updateUI(guidance)
}
// 正确:处理地区相关错误
do {
for try await guidance in service.guidance(using: query, at: venueID) {
updateUI(guidance)
}
} catch let error as EnergyKitError {
switch error {
case .unsupportedRegion:
showUnsupportedRegionMessage()
case .guidanceUnavailable:
showGuidanceUnavailableMessage()
case .venueUnavailable:
showNoVenueMessage()
case .permissionDenied:
showPermissionDeniedMessage()
case .serviceUnavailable:
retryLater()
case .rateLimitExceeded:
backOff()
default:
break
}
}DON'T: Discard the guidance token
禁止:丢弃guidance token
The links load events to the guidance that influenced them.
Always store and pass it through to load event submissions.
guidanceTokenswift
// WRONG: Ignore the guidance token
for try await guidance in guidanceStream {
startCharging()
}
// CORRECT: Store the token for load events
for try await guidance in guidanceStream {
let token = guidance.guidanceToken
startCharging(followingGuidanceToken: token)
}guidanceTokenswift
// 错误:忽略guidance token
for try await guidance in guidanceStream {
startCharging()
}
// 正确:存储token用于后续负载事件提交
for try await guidance in guidanceStream {
let token = guidance.guidanceToken
startCharging(followingGuidanceToken: token)
}DON'T: Submit load events without a session lifecycle
禁止:提交负载事件时不遵循会话生命周期
Always submit , then updates, then events.
.begin.active.endswift
// WRONG: Only submit one event
let event = ElectricVehicleLoadEvent(/* state: .active */)
try await venue.submitEvents([event])
// CORRECT: Full session lifecycle
try await venue.submitEvents([beginEvent])
// ... periodic active events ...
try await venue.submitEvents([activeEvent])
// ... when done ...
try await venue.submitEvents([endEvent])请始终按顺序提交、更新、事件。
.begin.active.endswift
// 错误:仅提交一个事件
let event = ElectricVehicleLoadEvent(/* state: .active */)
try await venue.submitEvents([event])
// 正确:完整的会话生命周期
try await venue.submitEvents([beginEvent])
// ... 周期性提交active事件 ...
try await venue.submitEvents([activeEvent])
// ... 用电结束时 ...
try await venue.submitEvents([endEvent])DON'T: Query guidance without a venue
禁止:没有场所ID就查询指引
EnergyKit requires a venue ID. List venues first and select the appropriate one.
swift
// WRONG: Use a hardcoded UUID
let fakeID = UUID()
service.guidance(using: query, at: fakeID) // Will fail
// CORRECT: Discover venues first
let venues = try await EnergyVenue.venues()
guard let venue = venues.first else {
showNoVenueSetup()
return
}
let guidanceStream = service.guidance(using: query, at: venue.id)EnergyKit需要场所ID,请先列出所有场所并选择合适的ID。
swift
// 错误:使用硬编码的UUID
let fakeID = UUID()
service.guidance(using: query, at: fakeID) // 会失败
// 正确:先发现场所
let venues = try await EnergyVenue.venues()
guard let venue = venues.first else {
showNoVenueSetup()
return
}
let guidanceStream = service.guidance(using: query, at: venue.id)Review Checklist
审核检查清单
- entitlement added to the project
com.apple.developer.energykit - handled with user-facing message
EnergyKitError.unsupportedRegion - handled gracefully
EnergyKitError.permissionDenied - Guidance token stored and passed to load event submissions
- Venues discovered via before querying guidance
EnergyVenue.venues() - Load event sessions follow ->
.begin->.activelifecycle.end - interpreted correctly (lower is better)
ElectricityGuidance.Value.rating - matches the device type (
SuggestedActionfor EV,.shiftfor HVAC).reduce - Insight queries use appropriate granularity for the time range
- Rate limiting handled via
EnergyKitError.rateLimitExceeded - Service unavailability handled with retry logic
- 已将权限添加到项目
com.apple.developer.energykit - 已为配置用户提示
EnergyKitError.unsupportedRegion - 已优雅处理错误
EnergyKitError.permissionDenied - 已存储guidance token并在提交负载事件时传入
- 查询指引前已通过发现可用场所
EnergyVenue.venues() - 负载事件会话遵循->
.begin->.active生命周期.end - 正确解读(数值越低越好)
ElectricityGuidance.Value.rating - 与设备类型匹配(EV用
SuggestedAction,HVAC用.shift).reduce - 洞察查询使用了与时间范围匹配的粒度
- 已通过处理限流逻辑
EnergyKitError.rateLimitExceeded - 已通过重试逻辑处理服务不可用的情况
References
参考文档
- Extended patterns (full app architecture, SwiftUI dashboard):
references/energykit-patterns.md - EnergyKit framework
- ElectricityGuidance
- ElectricityGuidance.Service
- ElectricityGuidance.Query
- ElectricityGuidance.Value
- EnergyVenue
- ElectricVehicleLoadEvent
- ElectricHVACLoadEvent
- ElectricityInsightService
- ElectricityInsightRecord
- ElectricityInsightQuery
- EnergyKitError
- Optimizing home electricity usage
- 扩展模式(完整应用架构、SwiftUI仪表盘):
references/energykit-patterns.md - EnergyKit框架
- ElectricityGuidance
- ElectricityGuidance.Service
- ElectricityGuidance.Query
- ElectricityGuidance.Value
- EnergyVenue
- ElectricVehicleLoadEvent
- ElectricHVACLoadEvent
- ElectricityInsightService
- ElectricityInsightRecord
- ElectricityInsightQuery
- EnergyKitError
- 优化家庭用电