homebridge-rainforest-eagle3 1.0.0 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +119 -15
- package/config.schema.json +78 -0
- package/dist/eveCharacteristics.d.ts +7 -0
- package/dist/eveCharacteristics.d.ts.map +1 -1
- package/dist/eveCharacteristics.js +12 -0
- package/dist/eveCharacteristics.js.map +1 -1
- package/dist/exportMeterAccessory.d.ts +2 -0
- package/dist/exportMeterAccessory.d.ts.map +1 -1
- package/dist/exportMeterAccessory.js +13 -6
- package/dist/exportMeterAccessory.js.map +1 -1
- package/dist/gridMeterAccessory.d.ts +2 -0
- package/dist/gridMeterAccessory.d.ts.map +1 -1
- package/dist/gridMeterAccessory.js +13 -6
- package/dist/gridMeterAccessory.js.map +1 -1
- package/dist/matterEnergy.d.ts +176 -0
- package/dist/matterEnergy.d.ts.map +1 -0
- package/dist/matterEnergy.js +332 -0
- package/dist/matterEnergy.js.map +1 -0
- package/dist/platform.d.ts +13 -0
- package/dist/platform.d.ts.map +1 -1
- package/dist/platform.js +64 -2
- package/dist/platform.js.map +1 -1
- package/dist/powerSwitch.d.ts +24 -0
- package/dist/powerSwitch.d.ts.map +1 -0
- package/dist/powerSwitch.js +70 -0
- package/dist/powerSwitch.js.map +1 -0
- package/dist/settings.d.ts +7 -0
- package/dist/settings.d.ts.map +1 -1
- package/dist/updatePolicy.d.ts +43 -0
- package/dist/updatePolicy.d.ts.map +1 -0
- package/dist/updatePolicy.js +115 -0
- package/dist/updatePolicy.js.map +1 -0
- package/package.json +7 -6
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* matterEnergy.ts
|
|
3
|
+
*
|
|
4
|
+
* Publishes the EAGLE grid meter to Matter controllers as a single
|
|
5
|
+
* bidirectional ElectricalSensor reporting live power and cumulative /
|
|
6
|
+
* periodic energy, so it feeds the Apple Home Energy view (iOS 27+).
|
|
7
|
+
*
|
|
8
|
+
* Background
|
|
9
|
+
* ----------
|
|
10
|
+
* Apple Home's Energy view is driven by Matter electrical-measurement
|
|
11
|
+
* clusters, not by classic HomeKit/HAP characteristics. HAP has no native
|
|
12
|
+
* power/energy characteristic, so the Eve custom characteristics this plugin
|
|
13
|
+
* also exposes (see eveCharacteristics.ts) are only ever read by Eve-class
|
|
14
|
+
* apps and never populate the native Energy tile.
|
|
15
|
+
*
|
|
16
|
+
* Device type: ElectricalSensor, not OnOffOutlet
|
|
17
|
+
* -----------------------------------------------
|
|
18
|
+
* A pure metering device type like ElectricalSensor has no actionable
|
|
19
|
+
* primary cluster, so its tile in the Home app shows "Not Supported" as its
|
|
20
|
+
* headline status. That's an accepted tradeoff here: the meter is not
|
|
21
|
+
* actually controllable, and an on/off state would only ever be a workaround
|
|
22
|
+
* for the tile headline — the wattage still rolls into the Home aggregate
|
|
23
|
+
* and (via PeriodicEnergy, below) into this accessory's own Energy-view
|
|
24
|
+
* attribution either way.
|
|
25
|
+
*
|
|
26
|
+
* powerW -> electricalPowerMeasurement.activePower (mW, signed)
|
|
27
|
+
* energyKWh -> electricalEnergyMeasurement.cumulativeEnergyImported.energy (mWh)
|
|
28
|
+
* and .cumulativeEnergyExported.energy, since the grid meter is
|
|
29
|
+
* bidirectional (see EnergyDirection below).
|
|
30
|
+
*
|
|
31
|
+
* Matter expresses power in milliwatts and energy in milliwatt-hours, hence
|
|
32
|
+
* the x1000 / x1,000,000 conversions. cumulativeEnergyImported/Exported are
|
|
33
|
+
* themselves structs (EnergyMeasurementStruct — { energy, startTimestamp?,
|
|
34
|
+
* endTimestamp?, ... }), not plain numbers: Homebridge's updateAccessoryState()
|
|
35
|
+
* accepts and normalizes a flat number for convenience, but the *initial*
|
|
36
|
+
* state passed at registration goes straight to matter.js's struct-typed
|
|
37
|
+
* attribute and must already be an object, or registration fails with
|
|
38
|
+
* "Cannot manage number because it is not a struct".
|
|
39
|
+
*
|
|
40
|
+
* Homebridge derives the mandatory cluster attributes (powerMode, accuracy,
|
|
41
|
+
* numberOfMeasurementTypes) and the feature-gated ElectricalEnergyMeasurement
|
|
42
|
+
* features from the declared state — declaring `cumulativeEnergyImported`
|
|
43
|
+
* selects the ImportedEnergy + CumulativeEnergy features, `cumulativeEnergyExported`
|
|
44
|
+
* selects ExportedEnergy + CumulativeEnergy, and both together (for a
|
|
45
|
+
* bidirectional meter) select both pairs at once. No voltage/current data is
|
|
46
|
+
* available from the EAGLE's local API, so only activePower is declared;
|
|
47
|
+
* voltage/activeCurrent are optional per the Matter spec and are simply
|
|
48
|
+
* omitted.
|
|
49
|
+
*
|
|
50
|
+
* Imported vs. exported energy is always two separate attributes
|
|
51
|
+
* ------------------------------------------------------------
|
|
52
|
+
* `activePower` is a signed attribute (positive = importing, negative =
|
|
53
|
+
* exporting), so this single bidirectional meter can report live direction
|
|
54
|
+
* with one accessory. Cumulative/periodic *energy* has no equivalent signed
|
|
55
|
+
* "net" attribute, though: Imported and Exported are distinct lifetime
|
|
56
|
+
* running totals per the Matter spec (you don't want a day's export and
|
|
57
|
+
* import cancelling each other out), so the meter declares both
|
|
58
|
+
* `cumulativeEnergyImported` and `cumulativeEnergyExported` (and their
|
|
59
|
+
* periodic counterparts) together on the same cluster. This is how the grid
|
|
60
|
+
* meter is published as a single Matter accessory even though it remains two
|
|
61
|
+
* separate accessories in Eve/HomeKit (Grid Meter - Import / Grid Meter -
|
|
62
|
+
* Export) when `showExportMeter` is enabled, since that split is purely
|
|
63
|
+
* about Eve's inability to represent a negative watt value, not a Matter
|
|
64
|
+
* limitation.
|
|
65
|
+
*
|
|
66
|
+
* Cumulative vs. periodic energy
|
|
67
|
+
* ------------------------------
|
|
68
|
+
* Alongside the cumulative (lifetime) total, this module also declares the
|
|
69
|
+
* PeriodicEnergy feature (`periodicEnergyImported`/`periodicEnergyExported`)
|
|
70
|
+
* — a per-interval delta with its own start/end timestamps. Per prior art in
|
|
71
|
+
* homebridge-shelly-matter (github.com/keremerkan/homebridge-shelly-matter,
|
|
72
|
+
* shellyAccessory.ts), this is what drives Apple Home's per-device energy
|
|
73
|
+
* attribution in the Energy view, not the cumulative total alone. Matter
|
|
74
|
+
* features compose once at registration, so PeriodicEnergy must already be
|
|
75
|
+
* present in the *initial* cluster state passed to registerPlatformAccessories()
|
|
76
|
+
* — declaring it for the first time in a later updateAccessoryState() call
|
|
77
|
+
* would not retroactively add the feature. register() therefore seeds a
|
|
78
|
+
* zero-energy periodic fragment (no timestamps yet).
|
|
79
|
+
*
|
|
80
|
+
* Event rate
|
|
81
|
+
* ----------
|
|
82
|
+
* Homebridge routes every energy field passed to updateAccessoryState() through
|
|
83
|
+
* matter.js setMeasurement(), which records a CumulativeEnergyMeasured or
|
|
84
|
+
* PeriodicEnergyMeasured event for each one. Sending both on every poll
|
|
85
|
+
* (default 15s) produced events that almost always repeated the previous
|
|
86
|
+
* total or reported 0 energy, so update() includes each field only when it
|
|
87
|
+
* carries news:
|
|
88
|
+
*
|
|
89
|
+
* - Cumulative: when the total (in mWh) has changed since the last one sent,
|
|
90
|
+
* or CUMULATIVE_HEARTBEAT_S has passed, so controllers still get a periodic
|
|
91
|
+
* heartbeat while the meter is idle.
|
|
92
|
+
* - Periodic: energy is accumulated across polls (per-poll deltas, so a meter
|
|
93
|
+
* counter that steps backwards doesn't produce a negative period) and
|
|
94
|
+
* emitted once per PERIODIC_INTERVAL_S, with start/end timestamps covering
|
|
95
|
+
* the whole period. If the previous period was idle (zero energy) and energy
|
|
96
|
+
* starts flowing, the period is emitted immediately and a new one starts, so
|
|
97
|
+
* a new session starts reporting promptly instead of up to 15 minutes later.
|
|
98
|
+
*
|
|
99
|
+
* The periodic baseline is taken from the first real poll, not from the
|
|
100
|
+
* placeholder readings register() is called with. The first update() after
|
|
101
|
+
* registration always sends both fields, as before.
|
|
102
|
+
*
|
|
103
|
+
* Requirements
|
|
104
|
+
* ------------
|
|
105
|
+
* - Homebridge 2.4.0+
|
|
106
|
+
* - Matter enabled on this plugin's child bridge (Homebridge UI ->
|
|
107
|
+
* plugin settings -> Bridge Settings -> enable Matter)
|
|
108
|
+
* - iOS/iPadOS 27+ on the device used to view the Apple Home Energy view
|
|
109
|
+
*
|
|
110
|
+
* Everything here is feature-detected and guarded: on a Homebridge build
|
|
111
|
+
* without the Matter API, or with Matter disabled, isSupported() returns
|
|
112
|
+
* false and the plugin runs HAP/Eve-only exactly as before.
|
|
113
|
+
*/
|
|
114
|
+
import type { API, Logger } from 'homebridge';
|
|
115
|
+
export type EnergyDirection = 'imported' | 'exported' | 'bidirectional';
|
|
116
|
+
export interface EnergyReadings {
|
|
117
|
+
/** Signed for a 'bidirectional' meter (positive = importing, negative = exporting). */
|
|
118
|
+
powerW: number;
|
|
119
|
+
/** Required when direction is 'imported' or 'bidirectional'. */
|
|
120
|
+
importedEnergyKWh?: number;
|
|
121
|
+
/** Required when direction is 'exported' or 'bidirectional'. */
|
|
122
|
+
exportedEnergyKWh?: number;
|
|
123
|
+
}
|
|
124
|
+
export declare class MatterEnergyBridge {
|
|
125
|
+
private readonly log;
|
|
126
|
+
private readonly direction;
|
|
127
|
+
private readonly nowMs;
|
|
128
|
+
/** Re-send an unchanged cumulative total at least this often. */
|
|
129
|
+
static readonly CUMULATIVE_HEARTBEAT_S: number;
|
|
130
|
+
/** Length of one PeriodicEnergyMeasured period. */
|
|
131
|
+
static readonly PERIODIC_INTERVAL_S: number;
|
|
132
|
+
private readonly api;
|
|
133
|
+
private readonly kinds;
|
|
134
|
+
private uuid;
|
|
135
|
+
private registered;
|
|
136
|
+
private warnedUpdate;
|
|
137
|
+
private sentFirstUpdate;
|
|
138
|
+
private lastCumulative;
|
|
139
|
+
private lastReadingKWh;
|
|
140
|
+
private periodStartS;
|
|
141
|
+
private periodKWh;
|
|
142
|
+
private lastPeriodic;
|
|
143
|
+
private idle;
|
|
144
|
+
constructor(api: API, log: Logger, direction: EnergyDirection, nowMs?: () => number);
|
|
145
|
+
/**
|
|
146
|
+
* Whether this Homebridge build exposes everything needed to publish this
|
|
147
|
+
* meter. Logs at debug level so unsupported builds stay quiet.
|
|
148
|
+
*/
|
|
149
|
+
isSupported(): boolean;
|
|
150
|
+
private nowS;
|
|
151
|
+
private static readingsByKind;
|
|
152
|
+
/** Initial cluster state for register(): every energy field, periodic seeded at zero. */
|
|
153
|
+
private buildInitialClusters;
|
|
154
|
+
private energyFields;
|
|
155
|
+
/**
|
|
156
|
+
* Decide which energy fields this poll should send, updating the cumulative
|
|
157
|
+
* and periodic bookkeeping. Returns an empty object when nothing is due.
|
|
158
|
+
*/
|
|
159
|
+
private buildEnergyUpdate;
|
|
160
|
+
/**
|
|
161
|
+
* Register this meter as a Matter electrical sensor.
|
|
162
|
+
*
|
|
163
|
+
* @param seedKey - unique per-meter key used to derive this accessory's
|
|
164
|
+
* Matter UUID, distinct from the HAP accessory's UUID
|
|
165
|
+
* @param displayName
|
|
166
|
+
* @param serialNumber
|
|
167
|
+
* @param readings - initial readings to seed the clusters with
|
|
168
|
+
*/
|
|
169
|
+
register(seedKey: string, displayName: string, serialNumber: string, readings: EnergyReadings): Promise<boolean>;
|
|
170
|
+
/**
|
|
171
|
+
* Push fresh readings to the registered Matter accessory. No-op until
|
|
172
|
+
* registration has succeeded.
|
|
173
|
+
*/
|
|
174
|
+
update(readings: EnergyReadings): Promise<void>;
|
|
175
|
+
}
|
|
176
|
+
//# sourceMappingURL=matterEnergy.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"matterEnergy.d.ts","sourceRoot":"","sources":["../src/matterEnergy.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgHG;AAEH,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AAkB9C,MAAM,MAAM,eAAe,GAAG,UAAU,GAAG,UAAU,GAAG,eAAe,CAAC;AAExE,MAAM,WAAW,cAAc;IAC7B,uFAAuF;IACvF,MAAM,EAAE,MAAM,CAAC;IACf,gEAAgE;IAChE,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,gEAAgE;IAChE,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B;AAyDD,qBAAa,kBAAkB;IA6B3B,OAAO,CAAC,QAAQ,CAAC,GAAG;IACpB,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,KAAK;IA9BxB,iEAAiE;IACjE,MAAM,CAAC,QAAQ,CAAC,sBAAsB,SAAW;IACjD,mDAAmD;IACnD,MAAM,CAAC,QAAQ,CAAC,mBAAmB,SAAW;IAE9C,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAgB;IACpC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAe;IACrC,OAAO,CAAC,IAAI,CAAuB;IACnC,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,YAAY,CAAS;IAC7B,OAAO,CAAC,eAAe,CAAS;IAGhC,OAAO,CAAC,cAAc,CAAmE;IAGzF,OAAO,CAAC,cAAc,CAA2C;IACjE,OAAO,CAAC,YAAY,CAAK;IACzB,OAAO,CAAC,SAAS,CAA4D;IAC7E,OAAO,CAAC,YAAY,CAGlB;IAEF,OAAO,CAAC,IAAI,CAAQ;gBAGlB,GAAG,EAAE,GAAG,EACS,GAAG,EAAE,MAAM,EACX,SAAS,EAAE,eAAe,EAC1B,KAAK,GAAE,MAAM,MAAiB;IAMjD;;;OAGG;IACH,WAAW,IAAI,OAAO;IAiBtB,OAAO,CAAC,IAAI;IAIZ,OAAO,CAAC,MAAM,CAAC,cAAc;IAI7B,yFAAyF;IACzF,OAAO,CAAC,oBAAoB;IAU5B,OAAO,CAAC,YAAY;IAiBpB;;;OAGG;IACH,OAAO,CAAC,iBAAiB;IAgDzB;;;;;;;;OAQG;IACG,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,QAAQ,EAAE,cAAc,GAAG,OAAO,CAAC,OAAO,CAAC;IA2BtH;;;OAGG;IACG,MAAM,CAAC,QAAQ,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC;CA+BtD"}
|
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* matterEnergy.ts
|
|
4
|
+
*
|
|
5
|
+
* Publishes the EAGLE grid meter to Matter controllers as a single
|
|
6
|
+
* bidirectional ElectricalSensor reporting live power and cumulative /
|
|
7
|
+
* periodic energy, so it feeds the Apple Home Energy view (iOS 27+).
|
|
8
|
+
*
|
|
9
|
+
* Background
|
|
10
|
+
* ----------
|
|
11
|
+
* Apple Home's Energy view is driven by Matter electrical-measurement
|
|
12
|
+
* clusters, not by classic HomeKit/HAP characteristics. HAP has no native
|
|
13
|
+
* power/energy characteristic, so the Eve custom characteristics this plugin
|
|
14
|
+
* also exposes (see eveCharacteristics.ts) are only ever read by Eve-class
|
|
15
|
+
* apps and never populate the native Energy tile.
|
|
16
|
+
*
|
|
17
|
+
* Device type: ElectricalSensor, not OnOffOutlet
|
|
18
|
+
* -----------------------------------------------
|
|
19
|
+
* A pure metering device type like ElectricalSensor has no actionable
|
|
20
|
+
* primary cluster, so its tile in the Home app shows "Not Supported" as its
|
|
21
|
+
* headline status. That's an accepted tradeoff here: the meter is not
|
|
22
|
+
* actually controllable, and an on/off state would only ever be a workaround
|
|
23
|
+
* for the tile headline — the wattage still rolls into the Home aggregate
|
|
24
|
+
* and (via PeriodicEnergy, below) into this accessory's own Energy-view
|
|
25
|
+
* attribution either way.
|
|
26
|
+
*
|
|
27
|
+
* powerW -> electricalPowerMeasurement.activePower (mW, signed)
|
|
28
|
+
* energyKWh -> electricalEnergyMeasurement.cumulativeEnergyImported.energy (mWh)
|
|
29
|
+
* and .cumulativeEnergyExported.energy, since the grid meter is
|
|
30
|
+
* bidirectional (see EnergyDirection below).
|
|
31
|
+
*
|
|
32
|
+
* Matter expresses power in milliwatts and energy in milliwatt-hours, hence
|
|
33
|
+
* the x1000 / x1,000,000 conversions. cumulativeEnergyImported/Exported are
|
|
34
|
+
* themselves structs (EnergyMeasurementStruct — { energy, startTimestamp?,
|
|
35
|
+
* endTimestamp?, ... }), not plain numbers: Homebridge's updateAccessoryState()
|
|
36
|
+
* accepts and normalizes a flat number for convenience, but the *initial*
|
|
37
|
+
* state passed at registration goes straight to matter.js's struct-typed
|
|
38
|
+
* attribute and must already be an object, or registration fails with
|
|
39
|
+
* "Cannot manage number because it is not a struct".
|
|
40
|
+
*
|
|
41
|
+
* Homebridge derives the mandatory cluster attributes (powerMode, accuracy,
|
|
42
|
+
* numberOfMeasurementTypes) and the feature-gated ElectricalEnergyMeasurement
|
|
43
|
+
* features from the declared state — declaring `cumulativeEnergyImported`
|
|
44
|
+
* selects the ImportedEnergy + CumulativeEnergy features, `cumulativeEnergyExported`
|
|
45
|
+
* selects ExportedEnergy + CumulativeEnergy, and both together (for a
|
|
46
|
+
* bidirectional meter) select both pairs at once. No voltage/current data is
|
|
47
|
+
* available from the EAGLE's local API, so only activePower is declared;
|
|
48
|
+
* voltage/activeCurrent are optional per the Matter spec and are simply
|
|
49
|
+
* omitted.
|
|
50
|
+
*
|
|
51
|
+
* Imported vs. exported energy is always two separate attributes
|
|
52
|
+
* ------------------------------------------------------------
|
|
53
|
+
* `activePower` is a signed attribute (positive = importing, negative =
|
|
54
|
+
* exporting), so this single bidirectional meter can report live direction
|
|
55
|
+
* with one accessory. Cumulative/periodic *energy* has no equivalent signed
|
|
56
|
+
* "net" attribute, though: Imported and Exported are distinct lifetime
|
|
57
|
+
* running totals per the Matter spec (you don't want a day's export and
|
|
58
|
+
* import cancelling each other out), so the meter declares both
|
|
59
|
+
* `cumulativeEnergyImported` and `cumulativeEnergyExported` (and their
|
|
60
|
+
* periodic counterparts) together on the same cluster. This is how the grid
|
|
61
|
+
* meter is published as a single Matter accessory even though it remains two
|
|
62
|
+
* separate accessories in Eve/HomeKit (Grid Meter - Import / Grid Meter -
|
|
63
|
+
* Export) when `showExportMeter` is enabled, since that split is purely
|
|
64
|
+
* about Eve's inability to represent a negative watt value, not a Matter
|
|
65
|
+
* limitation.
|
|
66
|
+
*
|
|
67
|
+
* Cumulative vs. periodic energy
|
|
68
|
+
* ------------------------------
|
|
69
|
+
* Alongside the cumulative (lifetime) total, this module also declares the
|
|
70
|
+
* PeriodicEnergy feature (`periodicEnergyImported`/`periodicEnergyExported`)
|
|
71
|
+
* — a per-interval delta with its own start/end timestamps. Per prior art in
|
|
72
|
+
* homebridge-shelly-matter (github.com/keremerkan/homebridge-shelly-matter,
|
|
73
|
+
* shellyAccessory.ts), this is what drives Apple Home's per-device energy
|
|
74
|
+
* attribution in the Energy view, not the cumulative total alone. Matter
|
|
75
|
+
* features compose once at registration, so PeriodicEnergy must already be
|
|
76
|
+
* present in the *initial* cluster state passed to registerPlatformAccessories()
|
|
77
|
+
* — declaring it for the first time in a later updateAccessoryState() call
|
|
78
|
+
* would not retroactively add the feature. register() therefore seeds a
|
|
79
|
+
* zero-energy periodic fragment (no timestamps yet).
|
|
80
|
+
*
|
|
81
|
+
* Event rate
|
|
82
|
+
* ----------
|
|
83
|
+
* Homebridge routes every energy field passed to updateAccessoryState() through
|
|
84
|
+
* matter.js setMeasurement(), which records a CumulativeEnergyMeasured or
|
|
85
|
+
* PeriodicEnergyMeasured event for each one. Sending both on every poll
|
|
86
|
+
* (default 15s) produced events that almost always repeated the previous
|
|
87
|
+
* total or reported 0 energy, so update() includes each field only when it
|
|
88
|
+
* carries news:
|
|
89
|
+
*
|
|
90
|
+
* - Cumulative: when the total (in mWh) has changed since the last one sent,
|
|
91
|
+
* or CUMULATIVE_HEARTBEAT_S has passed, so controllers still get a periodic
|
|
92
|
+
* heartbeat while the meter is idle.
|
|
93
|
+
* - Periodic: energy is accumulated across polls (per-poll deltas, so a meter
|
|
94
|
+
* counter that steps backwards doesn't produce a negative period) and
|
|
95
|
+
* emitted once per PERIODIC_INTERVAL_S, with start/end timestamps covering
|
|
96
|
+
* the whole period. If the previous period was idle (zero energy) and energy
|
|
97
|
+
* starts flowing, the period is emitted immediately and a new one starts, so
|
|
98
|
+
* a new session starts reporting promptly instead of up to 15 minutes later.
|
|
99
|
+
*
|
|
100
|
+
* The periodic baseline is taken from the first real poll, not from the
|
|
101
|
+
* placeholder readings register() is called with. The first update() after
|
|
102
|
+
* registration always sends both fields, as before.
|
|
103
|
+
*
|
|
104
|
+
* Requirements
|
|
105
|
+
* ------------
|
|
106
|
+
* - Homebridge 2.4.0+
|
|
107
|
+
* - Matter enabled on this plugin's child bridge (Homebridge UI ->
|
|
108
|
+
* plugin settings -> Bridge Settings -> enable Matter)
|
|
109
|
+
* - iOS/iPadOS 27+ on the device used to view the Apple Home Energy view
|
|
110
|
+
*
|
|
111
|
+
* Everything here is feature-detected and guarded: on a Homebridge build
|
|
112
|
+
* without the Matter API, or with Matter disabled, isSupported() returns
|
|
113
|
+
* false and the plugin runs HAP/Eve-only exactly as before.
|
|
114
|
+
*/
|
|
115
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
116
|
+
exports.MatterEnergyBridge = void 0;
|
|
117
|
+
const settings_1 = require("./settings");
|
|
118
|
+
/** Matter expresses power in milliwatts. */
|
|
119
|
+
function wToMilliW(value) {
|
|
120
|
+
const n = Number(value);
|
|
121
|
+
return Number.isFinite(n) ? Math.round(n * 1000) : 0;
|
|
122
|
+
}
|
|
123
|
+
/** Matter expresses energy in milliwatt-hours; EAGLE readings are in kWh. */
|
|
124
|
+
function kWhToMilliWh(value) {
|
|
125
|
+
const n = Number(value);
|
|
126
|
+
return Number.isFinite(n) ? Math.round(Math.max(0, n) * 1000000) : 0;
|
|
127
|
+
}
|
|
128
|
+
const ENERGY_KINDS = {
|
|
129
|
+
imported: ['imported'],
|
|
130
|
+
exported: ['exported'],
|
|
131
|
+
bidirectional: ['imported', 'exported'],
|
|
132
|
+
};
|
|
133
|
+
class MatterEnergyBridge {
|
|
134
|
+
constructor(api, log, direction, nowMs = Date.now) {
|
|
135
|
+
this.log = log;
|
|
136
|
+
this.direction = direction;
|
|
137
|
+
this.nowMs = nowMs;
|
|
138
|
+
this.uuid = null;
|
|
139
|
+
this.registered = false;
|
|
140
|
+
this.warnedUpdate = false;
|
|
141
|
+
this.sentFirstUpdate = false;
|
|
142
|
+
// Cumulative energy last sent (mWh) and when.
|
|
143
|
+
this.lastCumulative = null;
|
|
144
|
+
// Periodic-energy bookkeeping — see "Event rate" at the top of this file.
|
|
145
|
+
this.lastReadingKWh = null;
|
|
146
|
+
this.periodStartS = 0;
|
|
147
|
+
this.periodKWh = { imported: 0, exported: 0 };
|
|
148
|
+
this.lastPeriodic = {
|
|
149
|
+
imported: { energy: 0 },
|
|
150
|
+
exported: { energy: 0 },
|
|
151
|
+
};
|
|
152
|
+
// True while the last emitted period carried no energy (and at startup).
|
|
153
|
+
this.idle = true;
|
|
154
|
+
this.api = api;
|
|
155
|
+
this.kinds = ENERGY_KINDS[direction];
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Whether this Homebridge build exposes everything needed to publish this
|
|
159
|
+
* meter. Logs at debug level so unsupported builds stay quiet.
|
|
160
|
+
*/
|
|
161
|
+
isSupported() {
|
|
162
|
+
const matter = this.api.matter;
|
|
163
|
+
if (!matter) {
|
|
164
|
+
this.log.debug('[matter] api.matter unavailable — Matter energy export disabled. Requires Homebridge 2.4.0+ with Matter enabled on this plugin\'s child bridge.');
|
|
165
|
+
return false;
|
|
166
|
+
}
|
|
167
|
+
if (!matter.deviceTypes?.ElectricalSensor) {
|
|
168
|
+
this.log.debug('[matter] api.matter.deviceTypes.ElectricalSensor unavailable — Matter energy export disabled. Requires a newer Homebridge build.');
|
|
169
|
+
return false;
|
|
170
|
+
}
|
|
171
|
+
if (typeof matter.registerPlatformAccessories !== 'function' || typeof matter.updateAccessoryState !== 'function') {
|
|
172
|
+
this.log.debug('[matter] Matter registration/update API unavailable — Matter energy export disabled.');
|
|
173
|
+
return false;
|
|
174
|
+
}
|
|
175
|
+
return true;
|
|
176
|
+
}
|
|
177
|
+
nowS() {
|
|
178
|
+
return Math.floor(this.nowMs() / 1000);
|
|
179
|
+
}
|
|
180
|
+
static readingsByKind(r) {
|
|
181
|
+
return { imported: r.importedEnergyKWh ?? 0, exported: r.exportedEnergyKWh ?? 0 };
|
|
182
|
+
}
|
|
183
|
+
/** Initial cluster state for register(): every energy field, periodic seeded at zero. */
|
|
184
|
+
buildInitialClusters(r) {
|
|
185
|
+
const kWh = MatterEnergyBridge.readingsByKind(r);
|
|
186
|
+
const mWh = { imported: kWhToMilliWh(kWh.imported), exported: kWhToMilliWh(kWh.exported) };
|
|
187
|
+
this.lastCumulative = { mWh, timeS: this.nowS() };
|
|
188
|
+
return {
|
|
189
|
+
electricalPowerMeasurement: { activePower: wToMilliW(r.powerW) },
|
|
190
|
+
electricalEnergyMeasurement: this.energyFields(mWh, true),
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
energyFields(cumulativeMWh, includePeriodic) {
|
|
194
|
+
const out = {};
|
|
195
|
+
for (const kind of this.kinds) {
|
|
196
|
+
const suffix = kind === 'imported' ? 'Imported' : 'Exported';
|
|
197
|
+
if (cumulativeMWh) {
|
|
198
|
+
out[`cumulativeEnergy${suffix}`] = { energy: cumulativeMWh[kind] };
|
|
199
|
+
}
|
|
200
|
+
if (includePeriodic) {
|
|
201
|
+
out[`periodicEnergy${suffix}`] = this.lastPeriodic[kind];
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
return out;
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* Decide which energy fields this poll should send, updating the cumulative
|
|
208
|
+
* and periodic bookkeeping. Returns an empty object when nothing is due.
|
|
209
|
+
*/
|
|
210
|
+
buildEnergyUpdate(r) {
|
|
211
|
+
const nowS = this.nowS();
|
|
212
|
+
const kWh = MatterEnergyBridge.readingsByKind(r);
|
|
213
|
+
const first = !this.sentFirstUpdate;
|
|
214
|
+
this.sentFirstUpdate = true;
|
|
215
|
+
// Periodic: accumulate this poll's delta, emit at the end of a period or
|
|
216
|
+
// as soon as energy starts flowing after an idle period.
|
|
217
|
+
let emitPeriodic = false;
|
|
218
|
+
if (this.lastReadingKWh === null) {
|
|
219
|
+
this.periodStartS = nowS;
|
|
220
|
+
}
|
|
221
|
+
else {
|
|
222
|
+
for (const kind of this.kinds) {
|
|
223
|
+
this.periodKWh[kind] += Math.max(0, kWh[kind] - this.lastReadingKWh[kind]);
|
|
224
|
+
}
|
|
225
|
+
const hasEnergy = this.kinds.some((k) => kWhToMilliWh(this.periodKWh[k]) > 0);
|
|
226
|
+
const periodOver = nowS - this.periodStartS >= MatterEnergyBridge.PERIODIC_INTERVAL_S;
|
|
227
|
+
if (periodOver || (this.idle && hasEnergy)) {
|
|
228
|
+
emitPeriodic = true;
|
|
229
|
+
for (const kind of this.kinds) {
|
|
230
|
+
this.lastPeriodic[kind] = {
|
|
231
|
+
energy: kWhToMilliWh(this.periodKWh[kind]),
|
|
232
|
+
startTimestamp: this.periodStartS,
|
|
233
|
+
endTimestamp: nowS,
|
|
234
|
+
};
|
|
235
|
+
this.periodKWh[kind] = 0;
|
|
236
|
+
}
|
|
237
|
+
this.idle = !hasEnergy;
|
|
238
|
+
this.periodStartS = nowS;
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
this.lastReadingKWh = kWh;
|
|
242
|
+
// Cumulative: send on change or heartbeat.
|
|
243
|
+
const mWh = { imported: kWhToMilliWh(kWh.imported), exported: kWhToMilliWh(kWh.exported) };
|
|
244
|
+
const last = this.lastCumulative;
|
|
245
|
+
const sendCumulative = first ||
|
|
246
|
+
last === null ||
|
|
247
|
+
this.kinds.some((k) => mWh[k] !== last.mWh[k]) ||
|
|
248
|
+
nowS - last.timeS >= MatterEnergyBridge.CUMULATIVE_HEARTBEAT_S;
|
|
249
|
+
if (sendCumulative) {
|
|
250
|
+
this.lastCumulative = { mWh, timeS: nowS };
|
|
251
|
+
}
|
|
252
|
+
return this.energyFields(sendCumulative ? mWh : null, first || emitPeriodic);
|
|
253
|
+
}
|
|
254
|
+
/**
|
|
255
|
+
* Register this meter as a Matter electrical sensor.
|
|
256
|
+
*
|
|
257
|
+
* @param seedKey - unique per-meter key used to derive this accessory's
|
|
258
|
+
* Matter UUID, distinct from the HAP accessory's UUID
|
|
259
|
+
* @param displayName
|
|
260
|
+
* @param serialNumber
|
|
261
|
+
* @param readings - initial readings to seed the clusters with
|
|
262
|
+
*/
|
|
263
|
+
async register(seedKey, displayName, serialNumber, readings) {
|
|
264
|
+
if (!this.isSupported())
|
|
265
|
+
return false;
|
|
266
|
+
const matter = this.api.matter;
|
|
267
|
+
this.uuid = matter.uuid.generate(`${settings_1.PLUGIN_NAME}:matter:${seedKey}`);
|
|
268
|
+
const accessory = {
|
|
269
|
+
UUID: this.uuid,
|
|
270
|
+
displayName,
|
|
271
|
+
deviceType: matter.deviceTypes.ElectricalSensor,
|
|
272
|
+
serialNumber,
|
|
273
|
+
manufacturer: 'Rainforest Automation',
|
|
274
|
+
model: 'EAGLE-3',
|
|
275
|
+
clusters: this.buildInitialClusters(readings),
|
|
276
|
+
};
|
|
277
|
+
try {
|
|
278
|
+
await matter.registerPlatformAccessories(settings_1.PLUGIN_NAME, settings_1.PLATFORM_NAME, [accessory]);
|
|
279
|
+
this.registered = true;
|
|
280
|
+
this.log.info(`[matter] Published "${displayName}" as a Matter electrical sensor — live power should feed the Apple Home Energy view.`);
|
|
281
|
+
return true;
|
|
282
|
+
}
|
|
283
|
+
catch (err) {
|
|
284
|
+
this.log.warn(`[matter] Failed to register Matter accessory for "${displayName}" (${err instanceof Error ? err.message : err}). Continuing with HomeKit/Eve only.`);
|
|
285
|
+
this.registered = false;
|
|
286
|
+
return false;
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
/**
|
|
290
|
+
* Push fresh readings to the registered Matter accessory. No-op until
|
|
291
|
+
* registration has succeeded.
|
|
292
|
+
*/
|
|
293
|
+
async update(readings) {
|
|
294
|
+
if (!this.registered || !this.uuid)
|
|
295
|
+
return;
|
|
296
|
+
const matter = this.api.matter;
|
|
297
|
+
if (!matter)
|
|
298
|
+
return;
|
|
299
|
+
const energy = this.buildEnergyUpdate(readings);
|
|
300
|
+
try {
|
|
301
|
+
const updates = [
|
|
302
|
+
matter.updateAccessoryState(this.uuid, 'electricalPowerMeasurement', {
|
|
303
|
+
activePower: wToMilliW(readings.powerW),
|
|
304
|
+
}),
|
|
305
|
+
];
|
|
306
|
+
// Every energy field sent records a Matter event, so skip the call
|
|
307
|
+
// entirely when nothing is due.
|
|
308
|
+
if (Object.keys(energy).length > 0) {
|
|
309
|
+
updates.push(matter.updateAccessoryState(this.uuid, 'electricalEnergyMeasurement', energy));
|
|
310
|
+
}
|
|
311
|
+
await Promise.all(updates);
|
|
312
|
+
}
|
|
313
|
+
catch (err) {
|
|
314
|
+
// Log the first failure at warn, the rest at debug, so a persistently
|
|
315
|
+
// unhappy Matter server can't flood the log on every poll.
|
|
316
|
+
const message = `[matter] Failed to update Matter state: ${err instanceof Error ? err.message : err}`;
|
|
317
|
+
if (!this.warnedUpdate) {
|
|
318
|
+
this.warnedUpdate = true;
|
|
319
|
+
this.log.warn(message);
|
|
320
|
+
}
|
|
321
|
+
else {
|
|
322
|
+
this.log.debug(message);
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
exports.MatterEnergyBridge = MatterEnergyBridge;
|
|
328
|
+
/** Re-send an unchanged cumulative total at least this often. */
|
|
329
|
+
MatterEnergyBridge.CUMULATIVE_HEARTBEAT_S = 15 * 60;
|
|
330
|
+
/** Length of one PeriodicEnergyMeasured period. */
|
|
331
|
+
MatterEnergyBridge.PERIODIC_INTERVAL_S = 15 * 60;
|
|
332
|
+
//# sourceMappingURL=matterEnergy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"matterEnergy.js","sourceRoot":"","sources":["../src/matterEnergy.ts"],"names":[],"mappings":"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"}
|
package/dist/platform.d.ts
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { API, Characteristic, DynamicPlatformPlugin, Logger, PlatformAccessory, PlatformConfig, Service } from 'homebridge';
|
|
2
2
|
import { EveChars } from './eveCharacteristics';
|
|
3
|
+
import { EveUnit, EveUpdateThrottle, UpdatePolicyOptions } from './updatePolicy';
|
|
4
|
+
import { PowerSwitch } from './powerSwitch';
|
|
5
|
+
import { EveCharClass } from './eveCharacteristics';
|
|
3
6
|
export declare class EAGLEPlatform implements DynamicPlatformPlugin {
|
|
4
7
|
readonly log: Logger;
|
|
5
8
|
readonly api: API;
|
|
@@ -10,14 +13,24 @@ export declare class EAGLEPlatform implements DynamicPlatformPlugin {
|
|
|
10
13
|
readonly meterName: string;
|
|
11
14
|
readonly showExportMeter: boolean;
|
|
12
15
|
readonly exportMeterName: string;
|
|
16
|
+
readonly matterEnabled: boolean;
|
|
17
|
+
readonly updatePolicy: UpdatePolicyOptions;
|
|
18
|
+
readonly eveThrottle: EveUpdateThrottle;
|
|
19
|
+
private readonly switchStats;
|
|
13
20
|
private readonly client;
|
|
14
21
|
private readonly pollIntervalMs;
|
|
15
22
|
private gridMeterAccessory?;
|
|
16
23
|
private exportMeterAccessory?;
|
|
24
|
+
private gridMatterBridge?;
|
|
17
25
|
private pollTimer?;
|
|
26
|
+
private throttleStatsTimer?;
|
|
18
27
|
private pollInFlight;
|
|
19
28
|
private backedOff;
|
|
20
29
|
constructor(log: Logger, config: PlatformConfig, api: API);
|
|
30
|
+
/** Push an Eve power/energy value, subject to the evePowerUpdates mode. */
|
|
31
|
+
updateEve(service: Service, characteristic: EveCharClass, key: string, value: number, unit: EveUnit): void;
|
|
32
|
+
/** On/off state for a power-derived On characteristic, with deadband and hold. */
|
|
33
|
+
createPowerSwitch(key: string): PowerSwitch;
|
|
21
34
|
configureAccessory(accessory: PlatformAccessory): void;
|
|
22
35
|
private discoverDevices;
|
|
23
36
|
private registerOrRestoreAccessory;
|
package/dist/platform.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"platform.d.ts","sourceRoot":"","sources":["../src/platform.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,GAAG,EACH,cAAc,EACd,qBAAqB,EACrB,MAAM,EACN,iBAAiB,EACjB,cAAc,EACd,OAAO,EACR,MAAM,YAAY,CAAC;AAMpB,OAAO,EAA4B,QAAQ,EAAE,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"platform.d.ts","sourceRoot":"","sources":["../src/platform.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,GAAG,EACH,cAAc,EACd,qBAAqB,EACrB,MAAM,EACN,iBAAiB,EACjB,cAAc,EACd,OAAO,EACR,MAAM,YAAY,CAAC;AAMpB,OAAO,EAA4B,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAE1E,OAAO,EAAE,OAAO,EAAE,iBAAiB,EAAE,mBAAmB,EAAoC,MAAM,gBAAgB,CAAC;AACnH,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAQpD,qBAAa,aAAc,YAAW,qBAAqB;aAgCvC,GAAG,EAAE,MAAM;aAEX,GAAG,EAAE,GAAG;IAjC1B,SAAgB,OAAO,EAAE,OAAO,OAAO,CAAC;IACxC,SAAgB,cAAc,EAAE,OAAO,cAAc,CAAC;IAEtD,SAAgB,WAAW,EAAE,iBAAiB,EAAE,CAAM;IACtD,SAAgB,QAAQ,EAAE,QAAQ,CAAC;IACnC,SAAgB,SAAS,EAAE,MAAM,CAAC;IAClC,SAAgB,eAAe,EAAE,OAAO,CAAC;IACzC,SAAgB,eAAe,EAAE,MAAM,CAAC;IACxC,SAAgB,aAAa,EAAE,OAAO,CAAC;IAEvC,SAAgB,YAAY,EAAE,mBAAmB,CAAC;IAClD,SAAgB,WAAW,EAAE,iBAAiB,CAAC;IAC/C,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAqB;IAEjD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAc;IACrC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAS;IAExC,OAAO,CAAC,kBAAkB,CAAC,CAAqB;IAChD,OAAO,CAAC,oBAAoB,CAAC,CAAuB;IAMpD,OAAO,CAAC,gBAAgB,CAAC,CAAqB;IAC9C,OAAO,CAAC,SAAS,CAAC,CAAiC;IACnD,OAAO,CAAC,kBAAkB,CAAC,CAAiC;IAC5D,OAAO,CAAC,YAAY,CAAS;IAC7B,OAAO,CAAC,SAAS,CAAS;gBAGR,GAAG,EAAE,MAAM,EAC3B,MAAM,EAAE,cAAc,EACN,GAAG,EAAE,GAAG;IAoE1B,2EAA2E;IAC3E,SAAS,CAAC,OAAO,EAAE,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,IAAI;IAM1G,kFAAkF;IAClF,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,WAAW;IAS3C,kBAAkB,CAAC,SAAS,EAAE,iBAAiB,GAAG,IAAI;IAKtD,OAAO,CAAC,eAAe;IAgBvB,OAAO,CAAC,0BAA0B;IAuElC,OAAO,CAAC,YAAY;IAyEpB,OAAO,CAAC,YAAY;CAOrB"}
|