关于 energykit
energykit 是面向 Apple 平台应用开发者的 EnergyKit 技能,介绍电力指导、设备负载事件和历史洞察的使用方式。内容涵盖 EV 充电与 HVAC 场景,并强调权限、区域和系统版本差异。
主要功能
- 01查询分时电力指导
- 02发现并选择能源场所
- 03提交 EV 与 HVAC 负载事件
- 04查询历史能耗与运行时洞察
- 05处理权限、区域及版本要求
适用场景
- 01安排 EV 充电时段
- 02根据电力指导调整 HVAC 负载
- 03为托管设备生成历史能耗洞察
- 04实现 Home 应用负载事件体验
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ dpearson2699/ swift-ios-skills --skill energykit安装后在 Claude Code 中输入/energykit确认已加载。
附带文件evals/evals.jsonreferences/energykit-patterns.md
详细介绍
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 | 使用 device: 初始化器的 ElectricalLoadDevice | EnergyKit;与 Home 应用集成时添加 EnergyKit LoadEvents |
使用 EnergyKit 均需要 com.apple.developer.energykit;请在应用目标中启用 EnergyKit
功能。在 iOS/iPadOS 27+ 中,只有当应用需要在 Home 应用中显示设备名称、能源背景信息、
活动日志、历史图表或趋势通知时,才添加 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 提供两项主要功能:
- 用电指导 -- 基于时间加权的预测,告知应用何时用电更清洁,以及在费率数据可用时何时 成本更低
- 负载事件 -- 由受管理设备(电动汽车充电器、HVAC)生成的遥测数据;需由请求用电指导的同一设备/应用提交,以便 EnergyKit 生成洞察
关键类型
| 类型 | 作用 |
|---|---|
ElectricityGuidance | 包含加权时间间隔的预测数据 |
ElectricityGuidance.Service | 获取指导数据的接口 |
ElectricityGuidance.Query | 指定调整或减少操作的查询 |
ElectricityGuidance.Value | 包含评级(0.0-1.0)的时间间隔 |
EnergyVenue | 注册用于能源管理的物理地点(住宅) |
ElectricVehicleLoadEvent | 用于电动汽车充电器遥测的负载事件 |
ElectricHVACLoadEvent | 用于 HVAC 系统遥测的负载事件 |
ElectricalLoadDevice | iOS/iPadOS 27+ beta 中用于标识负载事件设备的类型 |
ElectricityInsightService | 查询能源/运行时洞察的服务 |
ElectricityInsightRecord | 历史能源或运行时数据,可按费率或 26.1+ 电网清洁度细分 |
ElectricityInsightQuery | 查询历史洞察数据的查询 |
建议操作
| 操作 | 使用场景 |
|---|---|
.shift | 可以将能耗调整到其他时段的设备(电动汽车充电) |
.reduce | 可以降低能耗但无需停止运行的设备(HVAC 调低设定值) |
查询用电指导
使用 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)")
// 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.Value 都包含一个时间间隔和一个
0.0 到 1.0 之间的评级。评级越低,表示用电时机越好。
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 }
}
在 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 "Good" }
if rating <= 0.6 { return "Fair" }
return "Avoid"
}
var body: some View {
Text(label)
.padding(.horizontal)
.padding(.vertical)
.background(color.opacity(0.2))
.foregroundStyle(color)
.clipShape(Capsule())
}
}
能源场所
EnergyVenue 表示注册用于能源管理的物理地点。
// 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)
}
场所属性
let venue = try await EnergyVenue.venue(for: venueID)
print("Venue ID: \(venue.id)")
print("Venue name: \(venue.name)")
提交负载事件
向系统报告设备耗电数据,帮助系统生成电力洞察。请求电力指导的同一台支持 EnergyKit 的设备/应用必须使用 EnergyKit 返回的指导令牌提交相应的负载事件。请勿伪造令牌。
EV 充电器负载事件
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 compatibility; deprecated in the iOS 27 SDK.
event = ElectricVehicleLoadEvent(
timestamp: Date(), measurement: measurement,
session: session, deviceID: deviceID
)
}
try await venue.submitEvents([event])
}
HVAC 负载事件
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 compatibility; deprecated in the iOS 27 SDK.
event = ElectricHVACLoadEvent(
timestamp: Date(), measurement: measurement,
session: session, deviceID: deviceID
)
}
try await venue.submitEvents([event])
}
会话状态
| 状态 | 使用时机 |
|---|---|
.begin | 设备开始耗电 |
.active | 设备正在耗电(定期更新) |
.end | 设备停止耗电 |
保持 .begin → .active → .end 的顺序,并及时提交事件,而不是长时间积攒批次。对于 EV 充电,以零功率和零能量提交 .begin;大约每 15 分钟提交一次 .active,并在发生重大变化时提交;以零功率和累计能量提交 .end。保留未获确认的事件,并以有界退避策略重试 EnergyKitError.rateLimitExceeded。请参阅 EV 会话管理器或 HVAC 会话管理器,了解针对特定设备的生命周期处理方式。
只有在 iOS/iPadOS 27+ 上同时具备基本 EnergyKit 和 EnergyKit LoadEvents 功能时,才可承诺支持“家庭”应用中的设备名称、能源上下文、活动日志、图表和趋势通知。
电力洞察
使用 ElectricityInsightService 查询设备的历史能耗和运行时长数据。空的 ElectricityInsightQuery.Options 选项集只会返回总计,不会填充清洁度或电价明细。只有在 UI 需要这些明细时,才请求 .cleanliness 和/或 .tariff。请勿用 MetricKit 应用耗电指标替代 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: \(total)") }
if #available(iOS 26.1, iPadOS 26.1, *),
let cleaner = record.dataByGridCleanliness?.cleaner {
print("Cleaner: \(cleaner)")
}
}
}
请使用 runtimeInsights(forDeviceID:using:atVenue:) 查询运行时长数据,而非能耗数据。粒度选项:.hourly、.daily、.weekly、.monthly、.yearly。选择符合 Apple 最低聚合时间窗口的范围:至少一个日历周使用每小时,至少一个日历月使用每日,至少六个月使用每周,至少一个日历年使用每月或每年。完整的洞察示例请参阅 references/energykit-patterns.md。
常见错误
| 错误 | 修复方法 |
|---|---|
| 在设置功能权限前查询 | 验证 EnergyKit 权限,并处理 .permissionDenied。 |
| 假定所有地区都提供指导 | Apple 目前仅记录了美国本土地区的指导;请处理不支持的地区以及场所/指导不可用的状态。 |
| 伪造或丢弃指导令牌 | 在请求设备上保存真实令牌,并在提交其负载事件时使用该令牌。 |
| 发送零散或延迟的负载样本 | 保持 .begin → .active → .end 的顺序,及时提交,并保留事件直至提交成功。 |
将 deviceID: 当作当前默认方式 | 使用 iOS/iPadOS 27+ 的 ElectricalLoadDevice 和 device:;仅在 26.x 运行时分支中保留 deviceID:。 |
| 使用硬编码的场所 ID | 使用 EnergyVenue.venues() 发现可用场所,并选择目标场所。 |
审核清单
- 已具备基础 EnergyKit 功能;iOS/iPadOS 27+ 的 Home 集成还具备 EnergyKit LoadEvents
- 已处理地区、权限、场所发现、不可用情况的指引和服务错误
- 真实指引令牌仅保留在发起请求的设备/应用及其提交的负载事件中
- iOS/iPadOS 27+ 使用
ElectricalLoadDevice/device:;deviceID:仅用于 26.x 兼容性 -
.begin → .active → .end事件遵循设备的时间节奏,及时提交,在失败时保留,并以有界退避方式重试速率限制 - 正确解读评级/操作;洞察选项、可用性、粒度和最小范围与 UI 一致
- 不以 MetricKit 遥测数据替代 EnergyKit 负载事件或洞察
参考资料
- 有关应用架构、EV/HVAC 会话时间节奏、仪表板呈现、洞察、错误和场所发现的扩展工作流: references/energykit-patterns.md
- EnergyKit framework
- ElectricityGuidance
- ElectricityGuidance.Service
- ElectricityGuidance.Query
- ElectricityGuidance.Value
- EnergyVenue
- ElectricalLoadDevice
- ElectricVehicleLoadEvent
- ElectricHVACLoadEvent
- ElectricityInsightService
- ElectricityInsightRecord
- ElectricityInsightQuery
- EnergyKitError
- EnergyKit entitlement
- EnergyKit LoadEvents entitlement
- 为电动汽车提供充电历史记录
- 优化家庭用电