homebridge-warmup4ie-v2 3.2.0 → 3.3.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/CHANGELOG.md +48 -0
- package/package.json +1 -1
- package/src/index.js +23 -0
- package/src/lib/state.js +23 -4
- package/src/lib/warmup4ie.js +9 -3
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,54 @@ This package is a maintained fork of [`homebridge-warmup4ie`](https://github.com
|
|
|
7
7
|
|
|
8
8
|
---
|
|
9
9
|
|
|
10
|
+
## [3.3.0] — 2026-05-05
|
|
11
|
+
|
|
12
|
+
Roadmap [Milestone 6](ROADMAP.md) batch 1 — incremental polish. No GraphQL
|
|
13
|
+
changes; everything uses data we already fetch. `config.json` unchanged.
|
|
14
|
+
|
|
15
|
+
### Added
|
|
16
|
+
- **`StatusFault` characteristic** on each Thermostat. Reads
|
|
17
|
+
`room.isFaultAir | isFaultFloor1 | isFaultFloor2` (data already in the
|
|
18
|
+
normalized room shape from v3.0). Surfaces sensor disconnects in
|
|
19
|
+
HomeKit's accessory diagnostics — better than mysterious wrong
|
|
20
|
+
readings. NO_FAULT (0) when clean, GENERAL_FAULT (1) when any flag set.
|
|
21
|
+
- **`runMode` edge-case handling** in `state.js` for the rare modes the
|
|
22
|
+
GraphQL schema documents (`anti_frost`, `holiday`, `gradual`,
|
|
23
|
+
`fil_pilote`, `relay`, `previous`, `not_set`):
|
|
24
|
+
- `holiday` → `TargetHeatingCoolingState = OFF` (location-wide vacation
|
|
25
|
+
mode; user expectation = "off"). `Current` still tracks `currentTemp <
|
|
26
|
+
targetTemp` because heating to the holiday setpoint *is* heating.
|
|
27
|
+
- `anti_frost` → `Target = OFF` (frost protection is passive).
|
|
28
|
+
`Current` still tracks the temp delta.
|
|
29
|
+
- `gradual` (early-start ramp-up) → `Target = AUTO`.
|
|
30
|
+
- The four `not_set | fil_pilote | relay | previous` rare modes →
|
|
31
|
+
`Target = HEAT` (safe fallback; better than OFF when heating may be
|
|
32
|
+
happening).
|
|
33
|
+
|
|
34
|
+
### Fixed
|
|
35
|
+
- **Defensive guard against transient empty rooms.** If Warmup returns
|
|
36
|
+
`owned[0].rooms = []` (a transient API hiccup; the user almost
|
|
37
|
+
certainly didn't actually delete every thermostat in the seconds
|
|
38
|
+
between polls), `reconcileAccessories` now logs a warning and skips
|
|
39
|
+
the unregister-stale step. Without this guard, a single bad poll
|
|
40
|
+
would rip every cached accessory out of the user's HomeKit rooms /
|
|
41
|
+
scenes / automations.
|
|
42
|
+
|
|
43
|
+
### Documentation
|
|
44
|
+
- Per-thermostat metadata fields in `normalizeRoom` get inline comments
|
|
45
|
+
explaining what each is for and which roadmap milestone surfaces it.
|
|
46
|
+
|
|
47
|
+
### Tests
|
|
48
|
+
- 89 total, 86 offline + 2 live + 1 destructive (skipped). Up from 75
|
|
49
|
+
in 3.2.0. Notable additions:
|
|
50
|
+
- `state-derivers.test.js`: 8 new test cases covering every documented
|
|
51
|
+
`runMode` value (and a few unknown-mode fallbacks).
|
|
52
|
+
- `platform-state.test.js`: `StatusFault` flow (NO_FAULT initially,
|
|
53
|
+
GENERAL_FAULT after a sensor flag flips on the next poll); empty-rooms
|
|
54
|
+
defensive guard (cached accessory survives a 0-room poll response).
|
|
55
|
+
|
|
56
|
+
---
|
|
57
|
+
|
|
10
58
|
## [3.2.0] — 2026-05-05
|
|
11
59
|
|
|
12
60
|
Roadmap [Milestone 5](ROADMAP.md) — **Eve / fakegato history graphs.**
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "homebridge-warmup4ie-v2",
|
|
3
3
|
"displayName": "Homebridge Warmup Wi-Fi Thermostats",
|
|
4
|
-
"version": "3.
|
|
4
|
+
"version": "3.3.0",
|
|
5
5
|
"description": "Homebridge plugin for Warmup Wi-Fi underfloor-heating thermostats (4iE, 6iE, 7iE, Element, Terra). Maintained fork of homebridge-warmup4ie with fixes for Homebridge 2.0 and the broken Off control in 0.1.0–0.1.1.",
|
|
6
6
|
"main": "src/index.js",
|
|
7
7
|
"scripts": {
|
package/src/index.js
CHANGED
|
@@ -123,6 +123,16 @@ warmup4iePlatform.prototype = {
|
|
|
123
123
|
// register/unregister deltas to Homebridge so the on-disk cache + HomeKit
|
|
124
124
|
// bridge match the Warmup account.
|
|
125
125
|
reconcileAccessories: function (rooms) {
|
|
126
|
+
// Defensive: an empty rooms array is *probably* a transient Warmup
|
|
127
|
+
// hiccup (the user didn't actually delete every thermostat). Skip
|
|
128
|
+
// unregistering cached accessories — they'll show "Not Responding"
|
|
129
|
+
// until the next poll, but we don't rip their HomeKit tiles out of
|
|
130
|
+
// rooms / scenes / automations on a glitch.
|
|
131
|
+
if (rooms.length === 0 && this.accessories.size > 0) {
|
|
132
|
+
this.log.warn('Warmup returned 0 rooms but %s cached accessories exist — keeping cache untouched', this.accessories.size);
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
|
|
126
136
|
const seen = new Set();
|
|
127
137
|
const toRegister = [];
|
|
128
138
|
|
|
@@ -298,6 +308,11 @@ function pushRoomState(accessory, room) {
|
|
|
298
308
|
.updateValue(heatingState);
|
|
299
309
|
thermo.getCharacteristic(Characteristic.TargetHeatingCoolingState)
|
|
300
310
|
.updateValue(deriveTargetHeatingState(room));
|
|
311
|
+
// StatusFault: NO_FAULT (0) when sensors look fine, GENERAL_FAULT (1)
|
|
312
|
+
// when any of the per-thermostat fault flags is set. Calling
|
|
313
|
+
// .getCharacteristic on an optional characteristic auto-adds it.
|
|
314
|
+
thermo.getCharacteristic(Characteristic.StatusFault)
|
|
315
|
+
.updateValue(deriveStatusFault(room));
|
|
301
316
|
temp.getCharacteristic(Characteristic.CurrentTemperature)
|
|
302
317
|
.updateValue(Number(room.airTemp / 10));
|
|
303
318
|
|
|
@@ -410,6 +425,14 @@ function effectiveTargetTemp(room) {
|
|
|
410
425
|
return room.targetTemp > room.minTemp ? room.targetTemp : room.minTemp;
|
|
411
426
|
}
|
|
412
427
|
|
|
428
|
+
// HAP StatusFault: NO_FAULT (0) | GENERAL_FAULT (1). Surfaces sensor
|
|
429
|
+
// disconnects (air or floor probes) so the user gets a visible "fault" badge
|
|
430
|
+
// in HomeKit's accessory diagnostics rather than mysterious wrong readings.
|
|
431
|
+
function deriveStatusFault(room) {
|
|
432
|
+
const anyFault = room.isFaultAir || room.isFaultFloor1 || room.isFaultFloor2;
|
|
433
|
+
return anyFault ? Characteristic.StatusFault.GENERAL_FAULT : Characteristic.StatusFault.NO_FAULT;
|
|
434
|
+
}
|
|
435
|
+
|
|
413
436
|
function uuidForRoom(roomId) {
|
|
414
437
|
return uuid.generate(`warmup4ie:${roomId}`);
|
|
415
438
|
}
|
package/src/lib/state.js
CHANGED
|
@@ -5,19 +5,38 @@
|
|
|
5
5
|
// HAP-NodeJS. Numeric values are HAP characteristic values:
|
|
6
6
|
// CurrentHeatingCoolingState: 0=OFF, 1=HEAT, 2=COOL (we only emit 0 and 1)
|
|
7
7
|
// TargetHeatingCoolingState: 0=OFF, 1=HEAT, 2=COOL, 3=AUTO (we only emit 0, 1, 3)
|
|
8
|
+
//
|
|
9
|
+
// `runMode` enum (per the GraphQL schema): not_set | off | schedule | override
|
|
10
|
+
// | fixed | anti_frost | holiday |
|
|
11
|
+
// | fil_pilote | gradual | relay | previous
|
|
8
12
|
|
|
9
13
|
function deriveCurrentHeatingState(room) {
|
|
14
|
+
// OFF only when the device is genuinely off. For every other mode (including
|
|
15
|
+
// anti_frost and holiday — both still drive heating to a low setpoint) the
|
|
16
|
+
// signal is whether currentTemp is below targetTemp. Roadmap M6 will swap
|
|
17
|
+
// this for the more accurate `Thermostat4iE.parameters.outputStatus` relay
|
|
18
|
+
// signal once we re-add `parameters` to the GraphQL query.
|
|
10
19
|
if (room.runMode === 'off') return 0;
|
|
11
20
|
return room.currentTemp < room.targetTemp ? 1 : 0;
|
|
12
21
|
}
|
|
13
22
|
|
|
14
23
|
function deriveTargetHeatingState(room) {
|
|
15
24
|
switch (room.runMode) {
|
|
16
|
-
case 'off':
|
|
25
|
+
case 'off':
|
|
26
|
+
case 'holiday': // Location-wide vacation mode — user expectation: "off"
|
|
27
|
+
case 'anti_frost': // Frost protection — passive, low setpoint
|
|
28
|
+
return 0; // OFF
|
|
17
29
|
case 'fixed':
|
|
18
|
-
case 'override':
|
|
19
|
-
|
|
20
|
-
|
|
30
|
+
case 'override':
|
|
31
|
+
return 1; // HEAT (user-set fixed temperature)
|
|
32
|
+
case 'schedule':
|
|
33
|
+
case 'gradual': // Early-start ramp-up to the next scheduled slot
|
|
34
|
+
return 3; // AUTO
|
|
35
|
+
default:
|
|
36
|
+
// Includes `not_set`, `fil_pilote`, `relay`, `previous` — rare modes
|
|
37
|
+
// we have no real signal on. HEAT is the safe fallback (better than
|
|
38
|
+
// showing OFF when heating is actually happening).
|
|
39
|
+
return 1;
|
|
21
40
|
}
|
|
22
41
|
}
|
|
23
42
|
|
package/src/lib/warmup4ie.js
CHANGED
|
@@ -270,9 +270,15 @@ function normalizeRoom(r) {
|
|
|
270
270
|
fixedTemp: r.fixedTemp,
|
|
271
271
|
energy: r.energy,
|
|
272
272
|
cost: r.cost,
|
|
273
|
-
//
|
|
274
|
-
// M6
|
|
275
|
-
//
|
|
273
|
+
// Per-thermostat metadata. Fault flags drive the platform's `StatusFault`
|
|
274
|
+
// characteristic (M6 batch 1). The rest are queued for follow-up:
|
|
275
|
+
// - `floor1Temp`/`floor2Temp` — exposed as a separate sensor in M6+.
|
|
276
|
+
// - `deviceSN` — could replace `warmup4ie-<roomId>` as SerialNumber, but
|
|
277
|
+
// would force HomeKit to re-pair existing accessories. Not worth it.
|
|
278
|
+
// - `lastPoll` — drives `StatusActive` (offline detection) in M6+.
|
|
279
|
+
// - `parameters { outputStatus }` (relay state) is not in the GraphQL
|
|
280
|
+
// query yet — re-adding caused a 409 during v3 development; M6 will
|
|
281
|
+
// try again carefully and use it for `CurrentHeatingCoolingState`.
|
|
276
282
|
floor1Temp: t.floor1Temp,
|
|
277
283
|
floor2Temp: t.floor2Temp,
|
|
278
284
|
isFaultAir: t.isFaultAir,
|