mcpField
E
energykit

energykit

github.com/dpearson2699开发1.2k3.6k 次安装13 周前更新
代码仓库

关于 energykit

energykit 是面向 Apple 平台应用开发者的 EnergyKit 技能,介绍电力指导、设备负载事件和历史洞察的使用方式。内容涵盖 EV 充电与 HVAC 场景,并强调权限、区域和系统版本差异。

主要功能

  1. 01查询分时电力指导
  2. 02发现并选择能源场所
  3. 03提交 EV 与 HVAC 负载事件
  4. 04查询历史能耗与运行时洞察
  5. 05处理权限、区域及版本要求

适用场景

  1. 01安排 EV 充电时段
  2. 02根据电力指导调整 HVAC 负载
  3. 03为托管设备生成历史能耗洞察
  4. 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.xdeviceID: 兼容性初始化器EnergyKit
iOS/iPadOS 27+ beta使用 device: 初始化器的 ElectricalLoadDeviceEnergyKit;与 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 提供两项主要功能:

  1. 用电指导 -- 基于时间加权的预测,告知应用何时用电更清洁,以及在费率数据可用时何时 成本更低
  2. 负载事件 -- 由受管理设备(电动汽车充电器、HVAC)生成的遥测数据;需由请求用电指导的同一设备/应用提交,以便 EnergyKit 生成洞察
关键类型
类型作用
ElectricityGuidance包含加权时间间隔的预测数据
ElectricityGuidance.Service获取指导数据的接口
ElectricityGuidance.Query指定调整或减少操作的查询
ElectricityGuidance.Value包含评级(0.0-1.0)的时间间隔
EnergyVenue注册用于能源管理的物理地点(住宅)
ElectricVehicleLoadEvent用于电动汽车充电器遥测的负载事件
ElectricHVACLoadEvent用于 HVAC 系统遥测的负载事件
ElectricalLoadDeviceiOS/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 负载事件或洞察

参考资料

评论

支持 Markdown