mcpField
E
energykit

energykit

github.com/dpearson2699開発1.2k3.6k 回インストール13 週間前に更新
リポジトリ

energykit について

energykitは、EnergyKitを使うAppleプラットフォームのアプリ開発者向けスキルです。電力ガイダンス、デバイスの負荷イベント、履歴インサイトを扱い、EV充電やHVACの例と権限・地域・OSバージョンの違いを解説します。

主な機能

  1. 01時間帯別の電力ガイダンス取得
  2. 02エネルギー施設の検索と選択
  3. 03EV・HVAC負荷イベントの送信
  4. 04エネルギー・稼働時間の履歴インサイト取得
  5. 05権限・地域・バージョン要件への対応

活用シーン

  1. 01EV充電時間のスケジューリング
  2. 02電力ガイダンスに基づくHVAC負荷の調整
  3. 03管理対象デバイスの履歴エネルギー分析
  4. 04Homeアプリの負荷イベント体験の実装

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/dpearson2699/swift-ios-skills --skill energykit

インストール後、Claude Code で/energykitを入力して読み込まれたか確認します。

同梱ファイルevals/evals.jsonreferences/energykit-patterns.md

詳細

EnergyKit

電力網のクリーン度とコストのガイダンスを活用して、管理対象デバイスの負荷を時間移動または削減します。 管理対象デバイスのインサイトを得るには、デバイスの実際の負荷イベントを速やかに送信してください。

ベータ版に関する注意。 EnergyKit のコア機能は iOS/iPadOS 26 に含まれています。iOS/iPadOS 27 の ElectricalLoadDevice と Home 向けの LoadEvents エクスペリエンスはベータ版です。これらの API を利用する前に、 最新の Apple ドキュメントを再確認してください。

目次

セットアップ

エンタイトルメントとバージョンの違い
ランタイム負荷イベント用デバイス API機能
iOS/iPadOS 26.xdeviceID: 互換イニシャライザーEnergyKit
iOS/iPadOS 27+ betadevice: イニシャライザーを使う 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 は、主に次の 2 つの機能を提供します。

  1. 電力ガイダンス -- 電力がよりクリーンで、料金データが利用可能な場合はより安価になる時間帯をアプリに知らせる、時間で重み付けされた予測
  2. 負荷イベント -- EnergyKit がインサイトを生成できるよう、ガイダンスを要求したものと同じデバイス/アプリから送信される、管理対象デバイス(EV 充電器、HVAC)のテレメトリ
主な型
型役割
ElectricityGuidance時間で重み付けされた間隔を含む予測データ
ElectricityGuidance.Serviceガイダンスデータを取得するためのインターフェイス
ElectricityGuidance.Query時間移動または削減のアクションを指定するクエリ
ElectricityGuidance.Value評価値(0.0-1.0)を持つ時間間隔
EnergyVenueエネルギー管理用に登録された物理的な場所(自宅)
ElectricVehicleLoadEventEV 充電器のテレメトリ用負荷イベント
ElectricHVACLoadEventHVAC システムのテレメトリ用負荷イベント
ElectricalLoadDevice負荷イベント用の iOS/iPadOS 27+ ベータ版デバイス識別情報
ElectricityInsightServiceエネルギー/稼働時間のインサイトを取得するサービス
ElectricityInsightRecord料金区分、または 26.1+ の電力網のクリーン度別に分類できる、エネルギーまたは稼働時間の履歴データ
ElectricityInsightQuery履歴インサイトデータ用のクエリ
推奨アクション
アクションユースケース
.shift消費する時間帯を変更できるデバイス(EV 充電)
.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 を電力とエネルギーがゼロの状態で送信し、.active は約15分ごと、および大きな変化があったときに送信します。.end は電力をゼロにし、累積エネルギーを含めて送信します。未確認のイベントを保持し、EnergyKitError.rateLimitExceeded が発生した場合は、上限を設けたバックオフで再試行してください。デバイス固有のライフサイクル処理については、EVセッションマネージャーまたはHVACセッションマネージャーを参照してください。

Homeアプリのデバイス名、エネルギーコンテキスト、アクティビティログ、グラフ、傾向の通知を利用できると案内するのは、iOS/iPadOS 27以降で、EnergyKitの基本機能とEnergyKit LoadEvents機能の両方がある場合に限ってください。

電力に関するインサイト

ElectricityInsightServiceを使って、デバイスの過去のエネルギーおよび稼働時間データをクエリします。空のElectricityInsightQuery.Optionsオプションセットでは合計値のみが返され、クリーン度や料金の内訳は取得されません。UIで内訳が必要な場合に限り、.cleanlinessや.tariffをリクエストしてください。EnergyKitのインサイトの代わりにMetricKitのアプリ電力メトリクスを使用しないでください。EnergyKitのインサイトは、管理対象デバイスから送信されたEnergyKitの負荷イベントに依存します。

リクエストされた期間に合わせて、インサイトの粒度を選択してください。7日間の表示では.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が定める最小集計期間に合った期間を選択してください。.hourlyは暦週以上、.dailyは暦月以上、.weeklyは6か月以上、.monthlyまたは.yearlyは暦年を対象とする場合に使用します。インサイトの完全な例については、references/energykit-patterns.mdを参照してください。

よくある間違い

間違い修正方法
機能を設定する前にクエリするEnergyKitのエンタイトルメントを確認し、.permissionDeniedを処理してください。
すべての地域でガイダンスが利用できると思い込むAppleが現在ガイダンスを案内しているのは米国本土のみです。未対応の地域や、利用できない会場/ガイダンスの状態を処理してください。
ガイダンストークンを捏造または破棄するリクエスト元のデバイスに実際のトークンを保存し、そのトークンを負荷イベントとともに送信してください。
単発または遅延した負荷サンプルを送信する.begin → .active → .end の順序を維持し、速やかに送信して、送信に成功するまでイベントを保持してください。
現在のデフォルトとしてdeviceID:を使用するiOS/iPadOS 27以降ではElectricalLoadDeviceとdevice:を使用し、deviceID:は26.xのランタイム分岐でのみ使用してください。
ハードコードした会場IDを使用するEnergyVenue.venues()で会場を検出し、目的の会場を選択してください。

レビューチェックリスト

  • 基本的な EnergyKit の機能が備わっている。iOS/iPadOS 27 以降の Home 連携には EnergyKit LoadEvents も備わっている
  • 地域、権限、施設の検出、利用不可の場合の案内、サービスエラーが適切に処理されている
  • 実際のガイダンストークンは、要求元のデバイス/アプリと、そのアプリが送信した負荷イベントに紐付けられている
  • iOS/iPadOS 27 以降では ElectricalLoadDevice/device: を使用し、deviceID: は 26.x との互換性のためだけに使用している
  • .begin → .active → .end イベントはデバイスの間隔に従い、速やかに送信され、失敗時にも保持され、上限付きバックオフでレート制限を再試行する
  • 評価/アクションが正しく解釈され、インサイトのオプション、利用可能性、粒度、最小範囲が UI と一致している
  • MetricKit のテレメトリを EnergyKit の負荷イベントやインサイトの代わりに使用していない

参考資料

コメント