homebridge-plugin-klares4 2.1.3-rc.6 → 2.1.3
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 +78 -3
- package/dist/websocket-client/thermostat-status-updater.d.ts +37 -1
- package/dist/websocket-client/thermostat-status-updater.d.ts.map +1 -1
- package/dist/websocket-client/thermostat-status-updater.js +65 -15
- package/dist/websocket-client/thermostat-status-updater.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,7 +7,82 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
-
## [2.1.3
|
|
10
|
+
## [2.1.3] - 2026-05-23
|
|
11
|
+
|
|
12
|
+
Stable release of the **`2.1.3-rc.1` … `2.1.3-rc.7`** cycle, validated on a
|
|
13
|
+
production Matter-only child bridge running Node 24 with 109 cached
|
|
14
|
+
accessories. Aggregates the fixes shipped over the seven release candidates
|
|
15
|
+
below; per-rc detail is preserved further down for archival reference.
|
|
16
|
+
|
|
17
|
+
### Fixed — Thermostat command routing (critical, verified in production)
|
|
18
|
+
|
|
19
|
+
- **`STATUS_TEMPERATURES` cross-pollination on swapped `(cfg, DOMUS sensor)` pairs.** On installations where two thermostats had numerically-swapped `(cfg-id, sensor-id)` pairs — in the reference setup *Matrimoniale* `(cfg 3, sensor 4)` and *Bagno* `(cfg 4, sensor 3)` — a setpoint or mode change on one was silently applied to the *other*: change Matrimoniale to 25 °C and Bagno's `targetTemperature` ended up at 25 °C too. Root cause inchiodato sul debug capture `klares4-debug-2026-05-23T07-29-03.json`: the Lares4 panel keys `STATUS_TEMPERATURES` broadcasts by **DOMUS sensor id**, not by cfg/program id. The previous resolver compared the broadcast id against each thermostat's cfg ids, producing a numeric collision for swapped pairs. Fix: `resolveThermostatOutputIds` now matches strictly against each thermostat's **expected DOMUS sensor id** (via `state.thermostatToDomus`, falling back to `domusSensorIdByThermostatProgramId[programId]`). The degraded path is preserved but no longer mixes cfg ids into the candidate set. Secondary fix: removed a stale `thermostatCommandIdByOutputId[outputId] = entry.ID` write from `updateTemperatureStatuses` that was poisoning the command-resolver cache with a sensor id. Setups whose `cfg` and `sensor` ids coincide numerically (e.g. Sala `(1,1)`, Cameretta `(2,2)`, Studio `(5,5)`) were invisibly correct before and remain a no-op after.
|
|
20
|
+
|
|
21
|
+
- **Self-sustaining thermostat command loop on Matter (P0).** A single setpoint change from Apple Home on `thermostat_21` would trigger repeated `WRITE_CFG` commands at 2-3 s intervals until the user stopped Homebridge. Root cause: matter.js re-fires attribute-change handlers for the plugin's own pushes via `api.matter.updateAccessoryState`; the closure-scoped `ThermostatEchoGuard` from the abandoned rc.2 was recreated on every `refreshAccessoryMetadata`, losing state across re-mappings. Replaced with a plugin-scoped `MatterThermostatEchoTracker` (one per `MatterAccessoryRegistry`) that records every cluster value before pushing it, recognises matter.js re-fires as echoes, and covers `occupiedHeatingSetpoint`, `occupiedCoolingSetpoint` and `systemMode`. Added idempotency guards on all four thermostat handlers, swallowed WS errors so timeouts don't promote to matter.js retry loops, and made heating-only cooling-setpoint writes read/state-only.
|
|
22
|
+
|
|
23
|
+
### Fixed — Matter accessory registration
|
|
24
|
+
|
|
25
|
+
- **Matter accessory rejected for `nodeLabel` > 32 chars.** Matter spec §1.7.7.1 caps `BridgedDeviceBasicInformation.nodeLabel` at 32 characters but the plugin's sanitiser used 64. Real-world failure on `scenario_12 "Inserisci Tapparelle+Volumetrici"` (32 chars → ` e ` expansion → 34 chars → rejected with `String length of 34 is not within bounds`). `MAX_NAME_LENGTH` lowered to 32; the collision-suffix path was already bound to the same constant.
|
|
26
|
+
- **Stale matter.js endpoint after the 32-char fix.** Following the sanitisation tightening above, `scenario_12` still failed registration because matter.js had persisted the previous (over-limit) endpoint for that UUID. New register call succeeded but `getAccessoryState` kept reading the stale record. The recovery path now performs an `unregister` + `register` purge on the second recovery attempt — same UUID is reused so Apple Home rooms / automations survive — forcing matter.js to recreate the endpoint with the current sanitised displayName.
|
|
27
|
+
|
|
28
|
+
### Fixed — WebSocket / connectivity
|
|
29
|
+
|
|
30
|
+
- **Node.js 24 / OpenSSL 3 — `unsafe legacy renegotiation disabled` blocks connection to the Lares4 panel.** On Homebridge containers upgraded to Node 24 the WebSocket handshake to `wss://<panel>:443/KseniaWsock/` aborted at TLS level. The plugin already set `SSL_OP_LEGACY_SERVER_CONNECT` but the Lares4 firmware also triggers a TLS renegotiation later in the session that needs the separate `SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION` flag (OpenSSL 3 default-denies it). Both flags are now OR-ed together in the `https.Agent` `secureOptions`. No change to the `allowInsecureTls=false` strict path.
|
|
31
|
+
|
|
32
|
+
### Added — Diagnostics
|
|
33
|
+
|
|
34
|
+
- **Configurable debug-capture duration** (`debugCaptureDurationMs`, default 60_000 ms, clamp 10s..30min, exposed in the Homebridge UI under "Diagnostica & Debug"). The previous fixed 60 s window often closed before the user could reproduce the failing scenario from HomeKit, because on Matter-only setups Apple Home can take several minutes to re-sync the mesh after a child-bridge restart. Setting `debugCaptureDurationMs: 600000` (10 minutes) keeps the WebSocket trace alive long enough.
|
|
35
|
+
- **Register-request log line** now includes the sanitised displayName, its character count and the UUID: `register requested: <original> -> "<sanitised>" [Nch] (<type>, uuid=<uuid>)` — makes register failures diagnosable directly from the log.
|
|
36
|
+
|
|
37
|
+
### Observability — Apple Home / HomeKit side-effects
|
|
38
|
+
|
|
39
|
+
- As a beneficial side-effect of the `STATUS_TEMPERATURES` fix, the two channels that update a thermostat's `currentTemperature` and `humidity` (broadcast on `STATUS_BUS_HA_SENSORS` for DOMUS readings, and `STATUS_TEMPERATURES` for the thermostat program) now patch the *same* device coherently on swapped-pair installations. Before, they could fight each other and present "ballerine" temperature readings in HomeKit.
|
|
40
|
+
|
|
41
|
+
### Refactored
|
|
42
|
+
|
|
43
|
+
- Extracted the four Matter Thermostat handlers into `src/platform/matter-thermostat-handlers.ts` to keep `matter-device-mapper.ts` under the 350-line repo limit and co-locate the loop-prevention logic with the echo tracker.
|
|
44
|
+
|
|
45
|
+
### Tests
|
|
46
|
+
|
|
47
|
+
- **149/149 passing.** New regression suite for the production scenarios:
|
|
48
|
+
- `STATUS_TEMPERATURES regression: crossed (cfg,sensor) pairs do NOT cross-pollinate` — reproduces the exact Matrimoniale↔Bagno setup in both directions.
|
|
49
|
+
- `STATUS_TEMPERATURES.ID is the DOMUS sensor id (4 for Matrimoniale), patch goes to thermostat_21` — rewritten from the old test that wrongly assumed `ID = cfg id`.
|
|
50
|
+
- `matter-thermostat-echo-tracker.test.js` — unit coverage for the new echo tracker (per-UUID scoping, TTL expiry, record / consume / clear).
|
|
51
|
+
- `matter-thermostat-echo-loop.test.js` — integration scenario for the matter.js handler-rewrite loop.
|
|
52
|
+
- `matter-name-sanitizer.test.js` — regression on the two real-world failing scenario names and the long-name collision-suffix path under the 32-char limit.
|
|
53
|
+
- `matter-accessory-registry.test.js` — stale-endpoint recovery (unregister-then-register) and register-request log format.
|
|
54
|
+
|
|
55
|
+
### Notes
|
|
56
|
+
|
|
57
|
+
- The KSA-cache pre-population path is unchanged: setups where the Lares4 panel rejects `READ PRG_THERMOSTATS` with `CMD_NOT_AVAILABLE` (firmware-dependent) still operate correctly because the plugin pre-loads `thermostatProgramById` / `thermostatProgramIdByOutputId` from the on-disk KSA cache (`klares4-ksa-cache.json`).
|
|
58
|
+
- Apple Home rooms and automations survive all the register/unregister cycles because the same accessory UUID is reused throughout.
|
|
59
|
+
|
|
60
|
+
---
|
|
61
|
+
|
|
62
|
+
### Per-RC archival history
|
|
63
|
+
|
|
64
|
+
#### [2.1.3-rc.7] - 2026-05-23
|
|
65
|
+
|
|
66
|
+
### Fixed (Thermostat, critical)
|
|
67
|
+
|
|
68
|
+
- **Cross-pollination between thermostats with "swapped" `(cfg, DOMUS sensor)` pairs.** In production: user changes setpoint on "Riscaldamento Matrimoniale" (output 21, cfg 3, DOMUS sensor 4) → "Riscaldamento Bagno" (output 20, cfg 4, DOMUS sensor 3) also flips to the same setpoint and turns ON. Inverse on the other direction. Other thermostats (Studio cfg=5/sensor=5, Cameretta cfg=2/sensor=2) unaffected.
|
|
69
|
+
|
|
70
|
+
**Root cause (verified via debug capture `klares4-debug-2026-05-23T07-29-03.json`)**: `STATUS_TEMPERATURES.ID` from the Lares4 panel is the **DOMUS sensor id**, NOT the cfg/program id. The previous `resolveThermostatOutputIds` (in `src/websocket-client/thermostat-status-updater.ts`) compared the broadcast `ID` against each thermostat's `manualCommandId` / `programCommandId` / `cachedCommandId` — all of which are **cfg ids**. When two thermostats had numerically-swapped pairs (cfg=3/sensor=4 vs cfg=4/sensor=3), the candidate list of one thermostat numerically matched the sensor id of the other, so the patch landed on the wrong device. Pairs where cfg and sensor coincide numerically (cfg=5/sensor=5, etc.) were spared by accident, which is why the bug was invisible on most installations.
|
|
71
|
+
|
|
72
|
+
**Fix**: `resolveThermostatOutputIds` now computes each thermostat's **expected DOMUS sensor id** (from `state.thermostatToDomus`, with a fallback to `state.domusSensorIdByThermostatProgramId[programCommandId]`) and matches `STATUS_TEMPERATURES.ID` strictly against that. The degraded-mode path (no DOMUS mapping at all) is preserved but no longer mixes cfg ids into the candidate set.
|
|
73
|
+
|
|
74
|
+
**Secondary fix**: removed a stale write `state.thermostatCommandIdByOutputId.set(outputId, entry.ID)` from `updateTemperatureStatuses`. That line was overwriting the cache used by the command resolver (`command-service.ts`) and config-sync path with a sensor id instead of a cfg id, poisoning subsequent WRITE_CFG routing decisions. Only `command-service.ts` and `thermostat-config-sync.ts` write that map now, and they always write the cfg id.
|
|
75
|
+
|
|
76
|
+
### Tests
|
|
77
|
+
|
|
78
|
+
- New regression test `STATUS_TEMPERATURES regression: crossed (cfg,sensor) pairs do NOT cross-pollinate` reproduces the exact Matrimoniale↔Bagno scenario and asserts that an `ID=4` broadcast patches only `thermostat_21` (Matrimoniale, sensor 4), leaves `thermostat_20` (Bagno, sensor 3) untouched, and vice-versa for `ID=3`.
|
|
79
|
+
- Updated `STATUS_TEMPERATURES.ID is the DOMUS sensor id ...` (renamed from the old `PRG_THERMOSTATS routes thermostat output 21 to cfg id 3 and sensor 4`) to feed the realistic `ID=sensor_id=4` payload instead of the unreal `ID=cfg_id=3` previously assumed.
|
|
80
|
+
|
|
81
|
+
### Notes
|
|
82
|
+
|
|
83
|
+
- Setups where, for every thermostat, the cfg id and the DOMUS sensor id are the same number (Sala, Cameretta, Studio in our reference install) saw no symptom because the candidate set "by accident" contained the correct id. The fix is a no-op for those.
|
|
84
|
+
|
|
85
|
+
#### [2.1.3-rc.6] - 2026-05-23
|
|
11
86
|
|
|
12
87
|
### Added (Diagnostics)
|
|
13
88
|
|
|
@@ -17,7 +92,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
17
92
|
|
|
18
93
|
- The debug capture itself does NOT cause accessories to disappear from Apple Home — the delay is the legitimate Matter mesh resync that follows every child-bridge restart (the Matter spec doesn't bound this on the controller side). Increasing the capture duration is the right knob.
|
|
19
94
|
|
|
20
|
-
|
|
95
|
+
#### [2.1.3-rc.5] - 2026-05-23
|
|
21
96
|
|
|
22
97
|
### Fixed (WebSocket, critical)
|
|
23
98
|
|
|
@@ -38,7 +113,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
38
113
|
- New regression in `matter-accessory-registry.test.js` covering the stale-endpoint recovery path (verifies the `unregister` precedes the second-attempt `register`, and that the UUID is preserved across all attempts).
|
|
39
114
|
- New test asserting the register-request log line includes the sanitised name, the char-count annotation, and the UUID.
|
|
40
115
|
|
|
41
|
-
|
|
116
|
+
#### [2.1.3-rc.3] - 2026-05-22
|
|
42
117
|
|
|
43
118
|
### Fixed (Matter, critical)
|
|
44
119
|
|
|
@@ -9,8 +9,44 @@ export declare class ThermostatStatusUpdater {
|
|
|
9
9
|
constructor(deps: ThermostatStatusUpdaterDeps);
|
|
10
10
|
updateTemperatureStatuses(entries: KseniaTemperatureStatusRaw[]): void;
|
|
11
11
|
applyPendingTemperatureStatuses(): void;
|
|
12
|
+
/**
|
|
13
|
+
* Resolve which Lares4 output-thermostat(s) a `STATUS_TEMPERATURES.ID` refers to.
|
|
14
|
+
*
|
|
15
|
+
* IMPORTANT (verified via debug capture 2026-05-23T07-29-03):
|
|
16
|
+
* `STATUS_TEMPERATURES.ID` is the **DOMUS sensor id**, NOT the program/cfg id.
|
|
17
|
+
* The Lares4 panel broadcasts thermostat state indexed by the DOMUS probe that
|
|
18
|
+
* measures the room temperature, not by the cronotermostato program number.
|
|
19
|
+
*
|
|
20
|
+
* The previous implementation compared `statusId` against the thermostat's
|
|
21
|
+
* `manualCommandId` / `programCommandId` / `cachedCommandId` — all of which are
|
|
22
|
+
* cfg ids — producing a numeric collision in setups where two thermostats have
|
|
23
|
+
* "swapped" sensor/program pairs (e.g. Matrimoniale: cfg=3 sensor=4, Bagno:
|
|
24
|
+
* cfg=4 sensor=3). On those setups, a STATUS_TEMPERATURES targeted at one
|
|
25
|
+
* thermostat would silently patch the *other*: change Matrimoniale setpoint to
|
|
26
|
+
* 25 °C and Bagno's targetTemperature ended up at 25 °C too. Devices whose
|
|
27
|
+
* (cfg, sensor) pair coincides numerically (e.g. Studio cfg=5 sensor=5) were
|
|
28
|
+
* spared by accident, masking the bug.
|
|
29
|
+
*
|
|
30
|
+
* The fix: for each thermostat compute its **expected DOMUS sensor id** and
|
|
31
|
+
* match `statusId` against that. Sources, in priority order:
|
|
32
|
+
* 1. `state.thermostatToDomus` — runtime DOMUS thermostat mapping (manual /
|
|
33
|
+
* auto / program), the canonical source whenever it's populated.
|
|
34
|
+
* 2. `state.domusSensorIdByThermostatProgramId[programCommandId]` —
|
|
35
|
+
* derived from PRG_THERMOSTATS at runtime or from the KSA cache; needed
|
|
36
|
+
* when `thermostatToDomus` hasn't been built yet (first STATUS_TEMPERATURES
|
|
37
|
+
* can arrive before the DOMUS mapping refresh).
|
|
38
|
+
*
|
|
39
|
+
* Degraded path (no PRG_THERMOSTATS and no DOMUS mapping at all): match
|
|
40
|
+
* exactly on `outputThermostatId` and fall through. We do NOT include the cfg
|
|
41
|
+
* ids in candidates — that was the root cause of the cross-pollination bug.
|
|
42
|
+
*/
|
|
12
43
|
private resolveThermostatOutputIds;
|
|
13
|
-
|
|
44
|
+
/**
|
|
45
|
+
* Returns the DOMUS sensor id we expect `STATUS_TEMPERATURES.ID` to carry for
|
|
46
|
+
* a given output thermostat, or `undefined` if no DOMUS association is known
|
|
47
|
+
* (caller falls back to the degraded path).
|
|
48
|
+
*/
|
|
49
|
+
private resolveExpectedSensorId;
|
|
14
50
|
private recordRealtimeSnapshot;
|
|
15
51
|
}
|
|
16
52
|
export declare function hasFreshThermostatRealtimeState(state: WebSocketClientState, outputThermostatId: string): boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"thermostat-status-updater.d.ts","sourceRoot":"","sources":["../../src/websocket-client/thermostat-status-updater.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACR,YAAY,EACZ,0BAA0B,EAC7B,MAAM,UAAU,CAAC;AAElB,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,SAAS,CAAC;AAEpD,UAAU,2BAA2B;IACjC,KAAK,EAAE,oBAAoB,CAAC;IAC5B,sBAAsB,EAAE,CAAC,MAAM,EAAE,YAAY,KAAK,IAAI,CAAC;CAC1D;AAED,qBAAa,uBAAuB;IACpB,OAAO,CAAC,QAAQ,CAAC,IAAI;gBAAJ,IAAI,EAAE,2BAA2B;IAEvD,yBAAyB,CAAC,OAAO,EAAE,0BAA0B,EAAE,GAAG,IAAI;
|
|
1
|
+
{"version":3,"file":"thermostat-status-updater.d.ts","sourceRoot":"","sources":["../../src/websocket-client/thermostat-status-updater.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACR,YAAY,EACZ,0BAA0B,EAC7B,MAAM,UAAU,CAAC;AAElB,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,SAAS,CAAC;AAEpD,UAAU,2BAA2B;IACjC,KAAK,EAAE,oBAAoB,CAAC;IAC5B,sBAAsB,EAAE,CAAC,MAAM,EAAE,YAAY,KAAK,IAAI,CAAC;CAC1D;AAED,qBAAa,uBAAuB;IACpB,OAAO,CAAC,QAAQ,CAAC,IAAI;gBAAJ,IAAI,EAAE,2BAA2B;IAEvD,yBAAyB,CAAC,OAAO,EAAE,0BAA0B,EAAE,GAAG,IAAI;IAkCtE,+BAA+B,IAAI,IAAI;IAQ9C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA8BG;IACH,OAAO,CAAC,0BAA0B;IA2BlC;;;;OAIG;IACH,OAAO,CAAC,uBAAuB;IAa/B,OAAO,CAAC,sBAAsB;CAkBjC;AAED,wBAAgB,+BAA+B,CAC3C,KAAK,EAAE,oBAAoB,EAC3B,kBAAkB,EAAE,MAAM,GAC3B,OAAO,CAMT"}
|
|
@@ -25,7 +25,14 @@ class ThermostatStatusUpdater {
|
|
|
25
25
|
continue;
|
|
26
26
|
}
|
|
27
27
|
const changed = (0, thermostat_state_1.updateThermostatStatus)(thermostatDevice, patch);
|
|
28
|
-
|
|
28
|
+
// NOTE: previously we wrote `thermostatCommandIdByOutputId[outputId] = entry.ID`
|
|
29
|
+
// here, but `entry.ID` from STATUS_TEMPERATURES is the DOMUS sensor id,
|
|
30
|
+
// NOT the cfg/program id that `thermostatCommandIdByOutputId` is supposed
|
|
31
|
+
// to hold. That write poisoned the cache used by the command resolver
|
|
32
|
+
// (command-service.ts) and the config-sync path, which then routed
|
|
33
|
+
// WRITE_CFG to a stale/sensor id. Removed entirely: the only legitimate
|
|
34
|
+
// writers of this map are `command-service.ts` and `thermostat-config-sync.ts`,
|
|
35
|
+
// both of which already write the cfg id.
|
|
29
36
|
this.deps.state.thermostatRealtimeByOutputId.set(outputThermostatId, Date.now());
|
|
30
37
|
if (changed) {
|
|
31
38
|
this.deps.emitDeviceStatusUpdate(thermostatDevice);
|
|
@@ -40,6 +47,37 @@ class ThermostatStatusUpdater {
|
|
|
40
47
|
}
|
|
41
48
|
this.updateTemperatureStatuses(pendingEntries);
|
|
42
49
|
}
|
|
50
|
+
/**
|
|
51
|
+
* Resolve which Lares4 output-thermostat(s) a `STATUS_TEMPERATURES.ID` refers to.
|
|
52
|
+
*
|
|
53
|
+
* IMPORTANT (verified via debug capture 2026-05-23T07-29-03):
|
|
54
|
+
* `STATUS_TEMPERATURES.ID` is the **DOMUS sensor id**, NOT the program/cfg id.
|
|
55
|
+
* The Lares4 panel broadcasts thermostat state indexed by the DOMUS probe that
|
|
56
|
+
* measures the room temperature, not by the cronotermostato program number.
|
|
57
|
+
*
|
|
58
|
+
* The previous implementation compared `statusId` against the thermostat's
|
|
59
|
+
* `manualCommandId` / `programCommandId` / `cachedCommandId` — all of which are
|
|
60
|
+
* cfg ids — producing a numeric collision in setups where two thermostats have
|
|
61
|
+
* "swapped" sensor/program pairs (e.g. Matrimoniale: cfg=3 sensor=4, Bagno:
|
|
62
|
+
* cfg=4 sensor=3). On those setups, a STATUS_TEMPERATURES targeted at one
|
|
63
|
+
* thermostat would silently patch the *other*: change Matrimoniale setpoint to
|
|
64
|
+
* 25 °C and Bagno's targetTemperature ended up at 25 °C too. Devices whose
|
|
65
|
+
* (cfg, sensor) pair coincides numerically (e.g. Studio cfg=5 sensor=5) were
|
|
66
|
+
* spared by accident, masking the bug.
|
|
67
|
+
*
|
|
68
|
+
* The fix: for each thermostat compute its **expected DOMUS sensor id** and
|
|
69
|
+
* match `statusId` against that. Sources, in priority order:
|
|
70
|
+
* 1. `state.thermostatToDomus` — runtime DOMUS thermostat mapping (manual /
|
|
71
|
+
* auto / program), the canonical source whenever it's populated.
|
|
72
|
+
* 2. `state.domusSensorIdByThermostatProgramId[programCommandId]` —
|
|
73
|
+
* derived from PRG_THERMOSTATS at runtime or from the KSA cache; needed
|
|
74
|
+
* when `thermostatToDomus` hasn't been built yet (first STATUS_TEMPERATURES
|
|
75
|
+
* can arrive before the DOMUS mapping refresh).
|
|
76
|
+
*
|
|
77
|
+
* Degraded path (no PRG_THERMOSTATS and no DOMUS mapping at all): match
|
|
78
|
+
* exactly on `outputThermostatId` and fall through. We do NOT include the cfg
|
|
79
|
+
* ids in candidates — that was the root cause of the cross-pollination bug.
|
|
80
|
+
*/
|
|
43
81
|
resolveThermostatOutputIds(statusId) {
|
|
44
82
|
const matches = new Set();
|
|
45
83
|
for (const device of this.deps.state.devices.values()) {
|
|
@@ -47,26 +85,38 @@ class ThermostatStatusUpdater {
|
|
|
47
85
|
continue;
|
|
48
86
|
}
|
|
49
87
|
const outputThermostatId = (0, device_id_1.stripDevicePrefix)(device.id);
|
|
50
|
-
const
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
];
|
|
57
|
-
if (this.deps.state.thermostatProgramById.size === 0) {
|
|
58
|
-
candidates.push(this.deps.state.thermostatToDomus.get(outputThermostatId));
|
|
59
|
-
candidates.push(outputThermostatId);
|
|
88
|
+
const expectedSensorId = this.resolveExpectedSensorId(outputThermostatId);
|
|
89
|
+
if (expectedSensorId !== undefined) {
|
|
90
|
+
if (expectedSensorId === statusId) {
|
|
91
|
+
matches.add(outputThermostatId);
|
|
92
|
+
}
|
|
93
|
+
continue;
|
|
60
94
|
}
|
|
61
|
-
|
|
95
|
+
// Fully degraded: no DOMUS mapping at all. Last-resort match by
|
|
96
|
+
// outputThermostatId (some firmwares broadcast that way for
|
|
97
|
+
// standalone thermostats without a DOMUS probe).
|
|
98
|
+
if (statusId === outputThermostatId) {
|
|
62
99
|
matches.add(outputThermostatId);
|
|
63
100
|
}
|
|
64
101
|
}
|
|
65
102
|
return [...matches];
|
|
66
103
|
}
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
104
|
+
/**
|
|
105
|
+
* Returns the DOMUS sensor id we expect `STATUS_TEMPERATURES.ID` to carry for
|
|
106
|
+
* a given output thermostat, or `undefined` if no DOMUS association is known
|
|
107
|
+
* (caller falls back to the degraded path).
|
|
108
|
+
*/
|
|
109
|
+
resolveExpectedSensorId(outputThermostatId) {
|
|
110
|
+
const direct = this.deps.state.thermostatToDomus.get(outputThermostatId);
|
|
111
|
+
if (direct !== undefined)
|
|
112
|
+
return direct;
|
|
113
|
+
const programId = this.deps.state.thermostatProgramIdByOutputId.get(outputThermostatId);
|
|
114
|
+
if (programId !== undefined) {
|
|
115
|
+
const sensorId = this.deps.state.domusSensorIdByThermostatProgramId.get(programId);
|
|
116
|
+
if (sensorId !== undefined)
|
|
117
|
+
return sensorId;
|
|
118
|
+
}
|
|
119
|
+
return undefined;
|
|
70
120
|
}
|
|
71
121
|
recordRealtimeSnapshot(entry) {
|
|
72
122
|
const previous = this.deps.state.thermostatRealtimeSnapshotById.get(entry.ID);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"thermostat-status-updater.js","sourceRoot":"","sources":["../../src/websocket-client/thermostat-status-updater.ts"],"names":[],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"thermostat-status-updater.js","sourceRoot":"","sources":["../../src/websocket-client/thermostat-status-updater.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED