homebridge-viessmann-vicare 2.0.39 → 2.0.41
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 +21 -0
- package/SETUP-GUIDE.md +18 -3
- package/dist/accessories/energy-accessory.d.ts.map +1 -1
- package/dist/accessories/energy-accessory.js +27 -18
- package/dist/platform.d.ts.map +1 -1
- package/dist/platform.js +19 -3
- package/dist/settings.d.ts +1 -1
- package/dist/settings.js +1 -1
- package/package.json +2 -2
- package/viessmann-report.js +214 -0
package/README.md
CHANGED
|
@@ -1033,6 +1033,27 @@ For issues and questions:
|
|
|
1033
1033
|
|
|
1034
1034
|
## 📈 Changelog
|
|
1035
1035
|
|
|
1036
|
+
### [2.0.41] - 2026-03-15
|
|
1037
|
+
#### Fixed
|
|
1038
|
+
- **Duplicate Boiler accessory on heat pump installations** — `setupBoilerAccessory` was matching `heating.boiler.serial` which is present on VitoCharge and other gen3 devices as a system identifier. Filter now requires actual burner/boiler operation features (`heating.burners.*`, `heating.boiler.temperature.current`, etc.). Fixes "Boiler 2" / "Energy 2" confusion reported on Windows installations with Vitocal 250A.
|
|
1039
|
+
|
|
1040
|
+
#### Added — `viessmann-report.js`
|
|
1041
|
+
- **Gas forecast section** — projects next-30-day and annual gas consumption using linear regression on historical CSV data. Shows cost estimate in € with configurable tariff via `--gasPriceEur` (default: 0.90 €/m³). Includes trend indicator (rising/stable/falling).
|
|
1042
|
+
- **Device messages section** — reads `viessmann-messages-<ID>.json` (written by plugin, future) and displays S./F./I. codes with English translations from Viessmann service documentation (80+ codes covered).
|
|
1043
|
+
- **`--gasPriceEur`** CLI parameter for gas cost calculation.
|
|
1044
|
+
|
|
1045
|
+
### [2.0.40] - 2026-03-15
|
|
1046
|
+
#### Fixed
|
|
1047
|
+
- **Critical: Accessories not updating after Homebridge restart** — when restoring accessories from cache, `device`, `installation`, and `gateway` were not written to `accessory.context`. The update loop silently skipped all accessories on every subsequent restart, showing `0 device(s) fetched, 0/0 accessories updated`. All four restore-from-cache paths (Boiler, DHW, Heating Circuit, Energy/Heat Pump) are now fixed.
|
|
1048
|
+
|
|
1049
|
+
#### Changed
|
|
1050
|
+
- **Full feature dump** — moved from `INFO` to `DEBUG` level; only visible when `debug: true` is set in plugin config.
|
|
1051
|
+
- **Capability detail log** — resolved HP paths and capability breakdown moved to `DEBUG`; single compact `INFO` line now summarises detected capabilities (e.g. `Capabilities detected: HeatPump`).
|
|
1052
|
+
- **`updateHandler not set` warning** — downgraded from `WARN` to `DEBUG`. Per-device spam eliminated; update cycle summary still shows the count when non-zero.
|
|
1053
|
+
|
|
1054
|
+
#### Notes
|
|
1055
|
+
- Users upgrading from ≤ v2.0.38 with a heat pump may see ghost "Heat Pump" accessories in Homebridge cache. Remove via Homebridge UI → Settings → Remove Single Accessory.
|
|
1056
|
+
|
|
1036
1057
|
### [2.0.39] - 2026-03-11
|
|
1037
1058
|
#### Fixed
|
|
1038
1059
|
- **Critical: Heat pump device detection** — `isHeatPumpDevice()` was incorrectly matching ALL Viessmann gen3 devices because `type:E3` is a gen3 architecture marker present on every device (TCU gateway, TRVs, room sensors, repeaters, VitoCharge, HEMS, wallbox, etc.). Detection now requires `type:heatpump` (exact role) or modelId containing `vitocal`. This was causing spurious "Adding new energy accessory: … Heat Pump" log entries for every device.
|
package/SETUP-GUIDE.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
# Complete Setup Guide - v2.0.
|
|
1
|
+
# Complete Setup Guide - v2.0.41
|
|
2
2
|
|
|
3
3
|
## Overview
|
|
4
4
|
|
|
5
|
-
This guide will walk you through setting up the Viessmann ViCare plugin v2.0.
|
|
5
|
+
This guide will walk you through setting up the Viessmann ViCare plugin v2.0.41 for Homebridge, including all the advanced features like intelligent caching, rate limiting protection, comprehensive configuration options, **complete localization support with custom names**, **CSV history logging**, **HTML diagnostic reports**, **energy system monitoring** (PV, battery, wallbox), and **heating schedule awareness** with visual bands in the HTML report, and **heat pump (Wärmepumpe) support** with automatic device detection.
|
|
6
6
|
|
|
7
7
|
## Prerequisites
|
|
8
8
|
|
|
@@ -1157,7 +1157,7 @@ When reporting issues, include:
|
|
|
1157
1157
|
|
|
1158
1158
|
```json
|
|
1159
1159
|
{
|
|
1160
|
-
"plugin_version": "2.0.
|
|
1160
|
+
"plugin_version": "2.0.41",
|
|
1161
1161
|
"homebridge_version": "1.8.x",
|
|
1162
1162
|
"node_version": "18.x.x",
|
|
1163
1163
|
"heating_system": "Viessmann Model",
|
|
@@ -1228,6 +1228,21 @@ sudo systemctl restart homebridge
|
|
|
1228
1228
|
|
|
1229
1229
|
## Changelog
|
|
1230
1230
|
|
|
1231
|
+
### v2.0.41 (2026-03-15)
|
|
1232
|
+
**Fix duplicate Boiler accessory + gas forecast + error code translations:**
|
|
1233
|
+
- **Fixed: duplicate "Boiler 2" on heat pump installations** — `setupBoilerAccessory` was matching `heating.boiler.serial` present on VitoCharge and other gen3 devices. Filter now requires actual burner operation features. Fixes accessory naming confusion on Windows installs with Vitocal 250A.
|
|
1234
|
+
- **Report: Gas forecast** — new section projecting 30-day and annual gas consumption via linear regression on CSV history. Cost estimate in € with `--gasPriceEur` parameter (default 0.90 €/m³). Trend indicator (rising/stable/falling).
|
|
1235
|
+
- **Report: Device messages** — new section with 80+ Viessmann S./F./I. code translations (English). Reads `viessmann-messages-<ID>.json` when available.
|
|
1236
|
+
- **`--gasPriceEur <price>`** new CLI parameter for `viessmann-report.js`.
|
|
1237
|
+
|
|
1238
|
+
### v2.0.40 (2026-03-15)
|
|
1239
|
+
**Critical bug fix + log noise reduction:**
|
|
1240
|
+
- **Fixed: accessories not updating after restart** — all four restore-from-cache paths (Boiler, DHW, HC, Energy) were missing `context.device/installation/gateway` assignment. This caused `updateAllDevices` to skip every accessory silently on every restart after the first, showing `0 device(s) fetched`. Affected all users.
|
|
1241
|
+
- Full feature dump moved to `DEBUG` level (only shown when `debug: true`).
|
|
1242
|
+
- Capability detail and resolved HP paths moved to `DEBUG`; single compact `INFO` line shows detected capabilities.
|
|
1243
|
+
- `updateHandler not set` per-device warning downgraded to `DEBUG`; update cycle summary count retained.
|
|
1244
|
+
- **Note:** Users upgrading from ≤v2.0.38 with a heat pump may have ghost "Heat Pump" accessories in cache — remove via Homebridge UI → Settings → Remove Single Accessory.
|
|
1245
|
+
|
|
1231
1246
|
### v2.0.39 (2026-03-11)
|
|
1232
1247
|
**Critical fix** — Heat pump device detection overhaul:
|
|
1233
1248
|
- Fixed `isHeatPumpDevice()`: removed `type:E3` which matched ALL gen3 devices. Now requires `type:heatpump` (exact) or Vitocal modelId.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"energy-accessory.d.ts","sourceRoot":"","sources":["../../src/accessories/energy-accessory.ts"],"names":[],"mappings":"AAAA,OAAO,EAAW,iBAAiB,EAAuB,MAAM,YAAY,CAAC;AAC7E,OAAO,EACL,iBAAiB,EACjB,qBAAqB,EACrB,gBAAgB,EAChB,eAAe,EAChB,MAAM,aAAa,CAAC;AAErB;;;;;;;;;;;;;;;;;GAiBG;AACH,qBAAa,wBAAwB;IAwFjC,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,YAAY;IAC7B,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,MAAM;IAxFzB,OAAO,CAAC,kBAAkB,CAAU;IAGpC,OAAO,CAAC,eAAe,CAAC,CAAU;IAClC,OAAO,CAAC,kBAAkB,CAAC,CAAU;IAGrC,OAAO,CAAC,mBAAmB,CAAC,CAAU;IACtC,OAAO,CAAC,eAAe,CAAC,CAAU;IAGlC,OAAO,CAAC,cAAc,CAAC,CAAU;IACjC,OAAO,CAAC,mBAAmB,CAAC,CAAU;IAGtC,OAAO,CAAC,cAAc,CAAC,CAAU;IACjC,OAAO,CAAC,oBAAoB,CAAC,CAAU;IAGvC,OAAO,CAAC,kBAAkB,CAAC,CAAU;IAIrC,OAAO,CAAC,UAAU,CAAc;IAChC,OAAO,CAAC,KAAK,CAAmB;IAChC,OAAO,CAAC,UAAU,CAAc;IAChC,OAAO,CAAC,UAAU,CAAc;IAChC,OAAO,CAAC,cAAc,CAAU;IAIhC,OAAO,CAAC,OAAO,CAOb;IAGF,OAAO,CAAC,MAAM,CAwCZ;gBAGiB,QAAQ,EAAE,iBAAiB,EAC3B,SAAS,EAAE,iBAAiB,EAC5B,YAAY,EAAE,qBAAqB,EACnC,OAAO,EAAE,gBAAgB,EACzB,MAAM,EAAE,eAAe;IAoB1C,OAAO,CAAC,gBAAgB;YAUV,sBAAsB;IAuBpC,OAAO,CAAC,cAAc;
|
|
1
|
+
{"version":3,"file":"energy-accessory.d.ts","sourceRoot":"","sources":["../../src/accessories/energy-accessory.ts"],"names":[],"mappings":"AAAA,OAAO,EAAW,iBAAiB,EAAuB,MAAM,YAAY,CAAC;AAC7E,OAAO,EACL,iBAAiB,EACjB,qBAAqB,EACrB,gBAAgB,EAChB,eAAe,EAChB,MAAM,aAAa,CAAC;AAErB;;;;;;;;;;;;;;;;;GAiBG;AACH,qBAAa,wBAAwB;IAwFjC,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,YAAY;IAC7B,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,MAAM;IAxFzB,OAAO,CAAC,kBAAkB,CAAU;IAGpC,OAAO,CAAC,eAAe,CAAC,CAAU;IAClC,OAAO,CAAC,kBAAkB,CAAC,CAAU;IAGrC,OAAO,CAAC,mBAAmB,CAAC,CAAU;IACtC,OAAO,CAAC,eAAe,CAAC,CAAU;IAGlC,OAAO,CAAC,cAAc,CAAC,CAAU;IACjC,OAAO,CAAC,mBAAmB,CAAC,CAAU;IAGtC,OAAO,CAAC,cAAc,CAAC,CAAU;IACjC,OAAO,CAAC,oBAAoB,CAAC,CAAU;IAGvC,OAAO,CAAC,kBAAkB,CAAC,CAAU;IAIrC,OAAO,CAAC,UAAU,CAAc;IAChC,OAAO,CAAC,KAAK,CAAmB;IAChC,OAAO,CAAC,UAAU,CAAc;IAChC,OAAO,CAAC,UAAU,CAAc;IAChC,OAAO,CAAC,cAAc,CAAU;IAIhC,OAAO,CAAC,OAAO,CAOb;IAGF,OAAO,CAAC,MAAM,CAwCZ;gBAGiB,QAAQ,EAAE,iBAAiB,EAC3B,SAAS,EAAE,iBAAiB,EAC5B,YAAY,EAAE,qBAAqB,EACnC,OAAO,EAAE,gBAAgB,EACzB,MAAM,EAAE,eAAe;IAoB1C,OAAO,CAAC,gBAAgB;YAUV,sBAAsB;IAuBpC,OAAO,CAAC,cAAc;IAoCtB,OAAO,CAAC,kBAAkB;IAyD1B;;;;OAIG;IACH,OAAO,CAAC,oBAAoB;IAqD5B,OAAO,CAAC,aAAa;IAoBrB,OAAO,CAAC,qBAAqB;IAwF7B,OAAO,CAAC,eAAe;IAkCvB,OAAO,CAAC,oBAAoB;IAgE5B,OAAO,CAAC,oBAAoB;IAwC5B,OAAO,CAAC,uBAAuB;IAwDlB,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC;YAa5B,kBAAkB;YAalB,cAAc;YA2Hd,mBAAmB;YAuInB,kBAAkB;YAmBlB,oBAAoB;YAmBpB,yBAAyB;CAgBxC"}
|
|
@@ -134,14 +134,16 @@ class ViessmannEnergyAccessory {
|
|
|
134
134
|
this.setupServices();
|
|
135
135
|
}
|
|
136
136
|
}
|
|
137
|
-
// ── Full feature dump (
|
|
137
|
+
// ── Full feature dump (debug only) ────────────────────────────────────────
|
|
138
138
|
logAllFeatures(features) {
|
|
139
|
+
if (!this.platform.config.debug)
|
|
140
|
+
return;
|
|
139
141
|
const tag = '[EnergyAccessory]';
|
|
140
142
|
const deviceLabel = `device=${this.device.id} model=${this.device.modelId ?? '?'} roles=${JSON.stringify(this.device.roles ?? [])}`;
|
|
141
|
-
this.platform.log.
|
|
142
|
-
this.platform.log.
|
|
143
|
-
this.platform.log.
|
|
144
|
-
this.platform.log.
|
|
143
|
+
this.platform.log.debug(`${tag} ════════════════════════════════════════════════`);
|
|
144
|
+
this.platform.log.debug(`${tag} FULL FEATURE DUMP — ${deviceLabel}`);
|
|
145
|
+
this.platform.log.debug(`${tag} Total features: ${features.length}`);
|
|
146
|
+
this.platform.log.debug(`${tag} ────────────────────────────────────────────────`);
|
|
145
147
|
// Sort features alphabetically for readability
|
|
146
148
|
const sorted = [...features].sort((a, b) => a.feature.localeCompare(b.feature));
|
|
147
149
|
for (const f of sorted) {
|
|
@@ -150,13 +152,13 @@ class ViessmannEnergyAccessory {
|
|
|
150
152
|
const enabled = f.isEnabled ? '✓' : '✗';
|
|
151
153
|
const ready = f.isReady ? '✓' : '✗';
|
|
152
154
|
const propVals = JSON.stringify(f.properties ?? {});
|
|
153
|
-
this.platform.log.
|
|
154
|
-
this.platform.log.
|
|
155
|
+
this.platform.log.debug(`${tag} [${enabled}${ready}] ${f.feature}`);
|
|
156
|
+
this.platform.log.debug(`${tag} props=(${propKeys}) values=${propVals.substring(0, 120)}`);
|
|
155
157
|
if (cmdKeys !== '—') {
|
|
156
|
-
this.platform.log.
|
|
158
|
+
this.platform.log.debug(`${tag} commands=(${cmdKeys})`);
|
|
157
159
|
}
|
|
158
160
|
}
|
|
159
|
-
this.platform.log.
|
|
161
|
+
this.platform.log.debug(`${tag} ════════════════════════════════════════════════`);
|
|
160
162
|
}
|
|
161
163
|
// ── Capability detection ──────────────────────────────────────────────────
|
|
162
164
|
detectCapabilities(features) {
|
|
@@ -196,12 +198,19 @@ class ViessmannEnergyAccessory {
|
|
|
196
198
|
names.some(n => n.startsWith('heating.dhw.pumps.primary')) ||
|
|
197
199
|
names.some(n => n === 'heating.dhw.operating.modes.electricBoost');
|
|
198
200
|
}
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
201
|
+
const caps = [
|
|
202
|
+
this.isHeatPump ? 'HeatPump' : null,
|
|
203
|
+
this.hasPV ? 'PV' : null,
|
|
204
|
+
this.hasBattery ? 'Battery' : null,
|
|
205
|
+
this.hasWallbox ? 'Wallbox' : null,
|
|
206
|
+
this.hasElectricDHW ? 'ElecDHW' : null,
|
|
207
|
+
].filter(Boolean).join(', ') || 'none';
|
|
208
|
+
this.platform.log.info(`${tag} Capabilities detected: ${caps}`);
|
|
209
|
+
this.platform.log.debug(`${tag} isHeatPump : ${this.isHeatPump}`);
|
|
210
|
+
this.platform.log.debug(`${tag} PV/Solar : ${this.hasPV}`);
|
|
211
|
+
this.platform.log.debug(`${tag} Battery : ${this.hasBattery}`);
|
|
212
|
+
this.platform.log.debug(`${tag} Wallbox/EV : ${this.hasWallbox}`);
|
|
213
|
+
this.platform.log.debug(`${tag} Electric DHW : ${this.hasElectricDHW}`);
|
|
205
214
|
}
|
|
206
215
|
/**
|
|
207
216
|
* Tries multiple known path variants for heat pump features.
|
|
@@ -217,10 +226,10 @@ class ViessmannEnergyAccessory {
|
|
|
217
226
|
this.hpPaths.supplyTemp = pick('heating.circuits.0.sensors.temperature.supply', 'heating.primaryCircuit.sensors.temperature.supply', 'heating.heatpump.sensors.temperature.flow');
|
|
218
227
|
this.hpPaths.returnTemp = pick('heating.sensors.temperature.return', 'heating.secondaryCircuit.sensors.temperature.return', 'heating.primaryCircuit.sensors.temperature.return', 'heating.heatpump.sensors.temperature.return', 'heating.circuits.0.sensors.temperature.return');
|
|
219
228
|
this.hpPaths.cop = pick('heating.scop.heating', 'heating.spf.heating', 'heating.scop.total', 'heating.heatpump.cop', 'heating.compressor.0.cop');
|
|
220
|
-
this.platform.log.
|
|
229
|
+
this.platform.log.debug(`${tag} Resolved paths:`);
|
|
221
230
|
for (const [key, val] of Object.entries(this.hpPaths)) {
|
|
222
|
-
const status = val ? `✓ ${val}` : '✗ not found
|
|
223
|
-
this.platform.log.
|
|
231
|
+
const status = val ? `✓ ${val}` : '✗ not found';
|
|
232
|
+
this.platform.log.debug(`${tag} ${key.padEnd(18)}: ${status}`);
|
|
224
233
|
}
|
|
225
234
|
}
|
|
226
235
|
// ── Service creation ───────────────────────────────────────────────────────
|
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,EAGH,qBAAqB,EACrB,MAAM,EACN,iBAAiB,EACjB,cAAc,EACd,OAAO,EACP,cAAc,EACf,MAAM,YAAY,CAAC;AAGpB,OAAO,EAAE,YAAY,EAAE,uBAAuB,EAAE,MAAM,iBAAiB,CAAC;AAExE,OAAO,EAAE,qBAAqB,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AACrI,OAAO,EAAE,uBAAuB,EAAE,MAAM,iBAAiB,CAAC;AAC1D,OAAO,EAAE,qBAAqB,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,eAAe,EAAgB,MAAM,2BAA2B,CAAC;AAOrI,qBAAa,iBAAkB,YAAW,qBAAqB;aA2B3C,GAAG,EAAE,MAAM;aACX,MAAM,EAAE,uBAAuB,GAAG,cAAc;aAChD,GAAG,EAAE,GAAG;IA5B1B,SAAgB,OAAO,EAAE,OAAO,OAAO,CAAC;IACxC,SAAgB,cAAc,EAAE,OAAO,cAAc,CAAC;IAEtD,SAAgB,WAAW,EAAE,iBAAiB,EAAE,CAAM;IACtD,SAAgB,YAAY,EAAG,YAAY,CAAC;IAC5C,OAAO,CAAC,WAAW,CAAS;IAE5B,OAAO,CAAC,aAAa,CAA+B;IACpD,OAAO,CAAC,YAAY,CAAC,CAAiB;IACtC,OAAO,CAAC,qBAAqB,CAAC,CAAiB;IAC/C,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,iBAAiB,CAAK;IAC9B,OAAO,CAAC,oBAAoB,CAAK;IACjC,OAAO,CAAC,iBAAiB,CAAK;IAG9B,OAAO,CAAC,oBAAoB,CAA0C;IACtE,OAAO,CAAC,iBAAiB,CAMvB;gBAGgB,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,uBAAuB,GAAG,cAAc,EAChD,GAAG,EAAE,GAAG;IAqD1B,OAAO,CAAC,cAAc;IAmCtB,OAAO,CAAC,uBAAuB;YAQjB,wBAAwB;YA8CxB,iCAAiC;YA+CjC,iCAAiC;YAoHjC,8BAA8B;YA2D9B,yCAAyC;IA4BhD,4BAA4B,CACjC,cAAc,EAAE,MAAM,EACtB,aAAa,EAAE,MAAM,EACrB,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,EACd,OAAO,GAAE,MAAkD,GAC1D,IAAI;IAgBP,OAAO,CAAC,yBAAyB;IAW1B,oBAAoB,IAAI;QAC7B,YAAY,EAAE,MAAM,CAAC;QACrB,iBAAiB,EAAE,MAAM,CAAC;QAC1B,aAAa,EAAE,MAAM,CAAC;QACtB,WAAW,EAAE,MAAM,CAAC;QACpB,cAAc,EAAE,MAAM,CAAC;QACvB,cAAc,EAAE,MAAM,CAAC;QACvB,aAAa,EAAE,MAAM,CAAC;KACvB;IAWD,kBAAkB,CAAC,SAAS,EAAE,iBAAiB;IAKzC,eAAe;IAuDrB,OAAO,CAAC,oBAAoB;IA8B5B,OAAO,CAAC,sBAAsB;IAO9B,OAAO,CAAC,iBAAiB;IAiBzB;;OAEG;IACH,OAAO,CAAC,mBAAmB;IA+CrB,sBAAsB,CAAC,YAAY,EAAE,qBAAqB,EAAE,OAAO,EAAE,gBAAgB,EAAE,MAAM,EAAE,eAAe;IAyB9G,oBAAoB,CACxB,YAAY,EAAE,qBAAqB,EACnC,OAAO,EAAE,gBAAgB,EACzB,MAAM,EAAE,eAAe,EACvB,QAAQ,EAAE,gBAAgB,EAAE;
|
|
1
|
+
{"version":3,"file":"platform.d.ts","sourceRoot":"","sources":["../src/platform.ts"],"names":[],"mappings":"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"}
|
package/dist/platform.js
CHANGED
|
@@ -511,8 +511,12 @@ class ViessmannPlatform {
|
|
|
511
511
|
}
|
|
512
512
|
}
|
|
513
513
|
async setupBoilerAccessory(installation, gateway, device, features) {
|
|
514
|
-
|
|
515
|
-
|
|
514
|
+
// Require actual burner/boiler operation features — not just heating.boiler.serial
|
|
515
|
+
// which is also present on VitoCharge and other gen3 devices as a system identifier.
|
|
516
|
+
const boilerFeatures = features.filter(f => f.feature.startsWith('heating.burners') ||
|
|
517
|
+
f.feature === 'heating.boiler.temperature.current' ||
|
|
518
|
+
f.feature === 'heating.boiler.sensors.temperature.commonSupply' ||
|
|
519
|
+
f.feature === 'heating.boiler.pumps.internal');
|
|
516
520
|
if (boilerFeatures.length === 0) {
|
|
517
521
|
return;
|
|
518
522
|
}
|
|
@@ -523,6 +527,9 @@ class ViessmannPlatform {
|
|
|
523
527
|
const existingAccessory = this.accessories.find(accessory => accessory.UUID === uuid);
|
|
524
528
|
if (existingAccessory) {
|
|
525
529
|
this.log.info('Restoring existing accessory from cache:', existingAccessory.displayName);
|
|
530
|
+
existingAccessory.context.device = device;
|
|
531
|
+
existingAccessory.context.installation = installation;
|
|
532
|
+
existingAccessory.context.gateway = gateway;
|
|
526
533
|
new boiler_accessory_1.ViessmannBoilerAccessory(this, existingAccessory, installation, gateway, device);
|
|
527
534
|
}
|
|
528
535
|
else {
|
|
@@ -548,6 +555,9 @@ class ViessmannPlatform {
|
|
|
548
555
|
const existingAccessory = this.accessories.find(accessory => accessory.UUID === uuid);
|
|
549
556
|
if (existingAccessory) {
|
|
550
557
|
this.log.info('Restoring existing accessory from cache:', existingAccessory.displayName);
|
|
558
|
+
existingAccessory.context.device = device;
|
|
559
|
+
existingAccessory.context.installation = installation;
|
|
560
|
+
existingAccessory.context.gateway = gateway;
|
|
551
561
|
new dhw_accessory_1.ViessmannDHWAccessory(this, existingAccessory, installation, gateway, device);
|
|
552
562
|
}
|
|
553
563
|
else {
|
|
@@ -577,6 +587,9 @@ class ViessmannPlatform {
|
|
|
577
587
|
const existingAccessory = this.accessories.find(accessory => accessory.UUID === uuid);
|
|
578
588
|
if (existingAccessory) {
|
|
579
589
|
this.log.info('Restoring existing accessory from cache:', existingAccessory.displayName);
|
|
590
|
+
existingAccessory.context.device = device;
|
|
591
|
+
existingAccessory.context.installation = installation;
|
|
592
|
+
existingAccessory.context.gateway = gateway;
|
|
580
593
|
new heating_circuit_accessory_1.ViessmannHeatingCircuitAccessory(this, existingAccessory, installation, gateway, device, circuitNumber);
|
|
581
594
|
}
|
|
582
595
|
else {
|
|
@@ -628,6 +641,9 @@ class ViessmannPlatform {
|
|
|
628
641
|
const existingAccessory = this.accessories.find(a => a.UUID === uuid);
|
|
629
642
|
if (existingAccessory) {
|
|
630
643
|
this.log.info('Restoring existing energy accessory from cache:', existingAccessory.displayName);
|
|
644
|
+
existingAccessory.context.device = device;
|
|
645
|
+
existingAccessory.context.installation = installation;
|
|
646
|
+
existingAccessory.context.gateway = gateway;
|
|
631
647
|
new energy_accessory_1.ViessmannEnergyAccessory(this, existingAccessory, installation, gateway, device);
|
|
632
648
|
}
|
|
633
649
|
else {
|
|
@@ -714,7 +730,7 @@ class ViessmannPlatform {
|
|
|
714
730
|
}
|
|
715
731
|
else {
|
|
716
732
|
handlersSkipped++;
|
|
717
|
-
this.log.
|
|
733
|
+
this.log.debug(`⚙️ "${accessory.displayName}" — updateHandler not set, skipping`);
|
|
718
734
|
}
|
|
719
735
|
}
|
|
720
736
|
catch (error) {
|
package/dist/settings.d.ts
CHANGED
|
@@ -9,7 +9,7 @@ export declare const PLUGIN_NAME = "homebridge-viessmann-vicare";
|
|
|
9
9
|
/**
|
|
10
10
|
* Plugin version for User-Agent and logging
|
|
11
11
|
*/
|
|
12
|
-
export declare const PLUGIN_VERSION = "2.0.
|
|
12
|
+
export declare const PLUGIN_VERSION = "2.0.41";
|
|
13
13
|
/**
|
|
14
14
|
* Default configuration values
|
|
15
15
|
*/
|
package/dist/settings.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "homebridge-viessmann-vicare",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.41",
|
|
4
4
|
"description": "Complete Homebridge plugin for Viessmann ViCare heating systems with full control capabilities, advanced rate limiting protection, and intelligent cache management",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -59,4 +59,4 @@
|
|
|
59
59
|
"optionalDependencies": {
|
|
60
60
|
"fakegato-history": "^0.6.7"
|
|
61
61
|
}
|
|
62
|
-
}
|
|
62
|
+
}
|
package/viessmann-report.js
CHANGED
|
@@ -581,6 +581,165 @@ const ovOutsideHum = overviewTimes.map(ts => interpolate(boilerRows, ts, 'outsid
|
|
|
581
581
|
const ovMod = overviewTimes.map(ts => interpolate(boilerRows,ts, 'modulation'));
|
|
582
582
|
const ovBurner = overviewTimes.map(ts => lookupBurner(boilerRows, ts));
|
|
583
583
|
|
|
584
|
+
|
|
585
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
586
|
+
// GAS FORECAST
|
|
587
|
+
// Uses gasPerDay (already computed above) to project monthly/annual consumption.
|
|
588
|
+
// Strategy: linear regression on last N days → extrapolate to 30/365 days.
|
|
589
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
590
|
+
let gasForecast = null;
|
|
591
|
+
if (gasDays.length >= 3) {
|
|
592
|
+
// Use all available days; total = heating + dhw
|
|
593
|
+
const totalPerDay = gasDays.map(d => gasPerDay[d].heating + gasPerDay[d].dhw);
|
|
594
|
+
const n = totalPerDay.length;
|
|
595
|
+
// Simple linear regression: y = a + b*x (x = day index)
|
|
596
|
+
const xMean = (n - 1) / 2;
|
|
597
|
+
const yMean = totalPerDay.reduce((s, v) => s + v, 0) / n;
|
|
598
|
+
let num = 0, den = 0;
|
|
599
|
+
totalPerDay.forEach((y, i) => { num += (i - xMean) * (y - yMean); den += (i - xMean) ** 2; });
|
|
600
|
+
const slope = den !== 0 ? num / den : 0;
|
|
601
|
+
const intercept = yMean - slope * xMean;
|
|
602
|
+
// Project from today (index = n-1) forward
|
|
603
|
+
const projectDay = (offset) => Math.max(0, intercept + slope * (n - 1 + offset));
|
|
604
|
+
// Monthly: sum of next 30 days
|
|
605
|
+
let monthSum = 0;
|
|
606
|
+
for (let i = 1; i <= 30; i++) monthSum += projectDay(i);
|
|
607
|
+
// Annual: sum of next 365 days (approximated as 30-day avg × 12 with seasonal note)
|
|
608
|
+
// For simplicity: annualise the period avg × 365 (more stable than long regression)
|
|
609
|
+
const periodAvgPerDay = yMean;
|
|
610
|
+
const annualEst = periodAvgPerDay * 365;
|
|
611
|
+
// Cost estimate (€): use --gasPriceEur param or env (default 0.90 €/m³ — Italian average)
|
|
612
|
+
const GAS_PRICE = parseFloat(getArg('--gasPriceEur', process.env.GAS_PRICE_EUR || '0.90'));
|
|
613
|
+
gasForecast = {
|
|
614
|
+
avgPerDay: yMean.toFixed(2),
|
|
615
|
+
trend: slope > 0.05 ? 'rising' : slope < -0.05 ? 'falling' : 'stable',
|
|
616
|
+
trendSlope: slope.toFixed(3),
|
|
617
|
+
month30: monthSum.toFixed(1),
|
|
618
|
+
annualEst: annualEst.toFixed(0),
|
|
619
|
+
costMonth: (monthSum * GAS_PRICE).toFixed(2),
|
|
620
|
+
costAnnual: (annualEst * GAS_PRICE).toFixed(2),
|
|
621
|
+
gasPrice: GAS_PRICE.toFixed(2),
|
|
622
|
+
daysUsed: n,
|
|
623
|
+
};
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
627
|
+
// VIESSMANN STATUS / ERROR CODE TRANSLATIONS
|
|
628
|
+
// Source: Viessmann service documentation + vieventlog error_codes.go
|
|
629
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
630
|
+
const VIESSMANN_CODES = {
|
|
631
|
+
// ── Status codes (S.) ──
|
|
632
|
+
'S.0': { en: 'Standby', de: 'Bereitschaft' },
|
|
633
|
+
'S.1': { en: 'DHW heating', de: 'Warmwasserbereitung' },
|
|
634
|
+
'S.2': { en: 'Central heating', de: 'Heizbetrieb' },
|
|
635
|
+
'S.3': { en: 'Burner on', de: 'Brenner ein' },
|
|
636
|
+
'S.4': { en: 'Burner off', de: 'Brenner aus' },
|
|
637
|
+
'S.5': { en: 'Fan pre-purge', de: 'Lüfter Vorspülung' },
|
|
638
|
+
'S.6': { en: 'Ignition', de: 'Zündung' },
|
|
639
|
+
'S.7': { en: 'Flame detected', de: 'Flamme erkannt' },
|
|
640
|
+
'S.8': { en: 'Burner post-purge', de: 'Lüfter Nachspülung' },
|
|
641
|
+
'S.9': { en: 'Cooling mode', de: 'Kühlbetrieb' },
|
|
642
|
+
'S.10': { en: 'Frost protection active', de: 'Frostschutz aktiv' },
|
|
643
|
+
'S.12': { en: 'Circulation pump active', de: 'Umwälzpumpe aktiv' },
|
|
644
|
+
'S.17': { en: 'Flue gas test', de: 'Abgastest' },
|
|
645
|
+
'S.19': { en: 'Chimney sweep mode', de: 'Schornsteinfegerbetrieb' },
|
|
646
|
+
'S.20': { en: 'Boiler protection (overtemp)', de: 'Kesselschutz (Übertemperatur)' },
|
|
647
|
+
'S.22': { en: 'External demand active', de: 'Externe Anforderung aktiv' },
|
|
648
|
+
'S.24': { en: 'Pump overrun', de: 'Pumpennachlauf' },
|
|
649
|
+
'S.31': { en: 'Summer eco mode', de: 'Sommer-Eco-Betrieb' },
|
|
650
|
+
'S.32': { en: 'Heating circuit standby', de: 'Heizkreis Standby' },
|
|
651
|
+
'S.40': { en: 'Heat pump heating', de: 'Wärmepumpe Heizbetrieb' },
|
|
652
|
+
'S.41': { en: 'Heat pump DHW', de: 'Wärmepumpe Warmwasser' },
|
|
653
|
+
'S.42': { en: 'Heat pump defrost', de: 'Wärmepumpe Abtauung' },
|
|
654
|
+
'S.43': { en: 'Heat pump cooling', de: 'Wärmepumpe Kühlung' },
|
|
655
|
+
'S.44': { en: 'Compressor starting', de: 'Verdichter startet' },
|
|
656
|
+
'S.45': { en: 'Compressor running', de: 'Verdichter läuft' },
|
|
657
|
+
'S.46': { en: 'Compressor stopping', de: 'Verdichter stoppt' },
|
|
658
|
+
'S.100': { en: 'Heating mode', de: 'Heizbetrieb' },
|
|
659
|
+
'S.109': { en: 'Heating circuit active', de: 'Heizkreis aktiv' },
|
|
660
|
+
'S.111': { en: 'Normal heating program', de: 'Normalbetrieb Heizung' },
|
|
661
|
+
'S.112': { en: 'Reduced heating program', de: 'Absenkbetrieb Heizung' },
|
|
662
|
+
'S.113': { en: 'Comfort heating program', de: 'Komfortbetrieb Heizung' },
|
|
663
|
+
'S.114': { en: 'DHW demand', de: 'Warmwasseranforderung' },
|
|
664
|
+
'S.118': { en: 'Primary circuit active', de: 'Primärkreis aktiv' },
|
|
665
|
+
'S.119': { en: 'Secondary circuit active', de: 'Sekundärkreis aktiv' },
|
|
666
|
+
'S.120': { en: 'Circulation pump running', de: 'Umwälzpumpe läuft' },
|
|
667
|
+
'S.123': { en: 'Heat pump standby', de: 'Wärmepumpe Bereitschaft' },
|
|
668
|
+
'S.124': { en: 'Heat pump grid lock', de: 'Wärmepumpe Netzsperrzeit' },
|
|
669
|
+
'S.125': { en: 'Heat pump demand pending', de: 'Wärmepumpe Anforderung ausstehend' },
|
|
670
|
+
'S.126': { en: 'Heat pump frost protection', de: 'Wärmepumpe Frostschutz' },
|
|
671
|
+
'S.130': { en: 'Defrost active', de: 'Abtauung aktiv' },
|
|
672
|
+
'S.131': { en: 'Defrost completed', de: 'Abtauung abgeschlossen' },
|
|
673
|
+
'S.134': { en: 'Heat pump heating active', de: 'Wärmepumpe Heizbetrieb aktiv' },
|
|
674
|
+
'S.140': { en: 'Smart grid active', de: 'Smart Grid aktiv' },
|
|
675
|
+
'S.200': { en: 'Legionella protection', de: 'Legionellenschutz' },
|
|
676
|
+
'S.201': { en: 'DHW efficient mode', de: 'Warmwasser Effizienzbetrieb' },
|
|
677
|
+
'S.202': { en: 'DHW comfort mode', de: 'Warmwasser Komfortbetrieb' },
|
|
678
|
+
// ── Info codes (I.) ──
|
|
679
|
+
'I.0': { en: 'System OK', de: 'System OK' },
|
|
680
|
+
'I.1': { en: 'Maintenance due', de: 'Wartung fällig' },
|
|
681
|
+
'I.2': { en: 'Filter replacement due', de: 'Filterwechsel fällig' },
|
|
682
|
+
'I.10': { en: 'External temperature sensor fault', de: 'Außentemperaturfühler Fehler' },
|
|
683
|
+
'I.11': { en: 'Return temperature sensor fault', de: 'Rücklauftemperaturfühler Fehler' },
|
|
684
|
+
'I.12': { en: 'DHW sensor fault', de: 'Warmwasserfühler Fehler' },
|
|
685
|
+
'I.20': { en: 'Low water pressure warning', de: 'Niederdruck Warnung' },
|
|
686
|
+
'I.100': { en: 'System info', de: 'Systeminformation' },
|
|
687
|
+
'I.113': { en: 'Heating curve optimisation info', de: 'Heizkurvenoptimierung Info' },
|
|
688
|
+
'I.114': { en: 'Energy balance info', de: 'Energiebilanz Info' },
|
|
689
|
+
'I.115': { en: 'Runtime statistics info', de: 'Laufzeitstatistik Info' },
|
|
690
|
+
// ── Fault codes (F.) ──
|
|
691
|
+
'F.0': { en: 'No fault', de: 'Kein Fehler' },
|
|
692
|
+
'F.1': { en: 'Burner fault — no ignition', de: 'Brennerstörung — keine Zündung' },
|
|
693
|
+
'F.2': { en: 'Flame signal lost', de: 'Flammensignal verloren' },
|
|
694
|
+
'F.3': { en: 'Ignition fault', de: 'Zündfehler' },
|
|
695
|
+
'F.4': { en: 'Safety chain open', de: 'Sicherheitskette offen' },
|
|
696
|
+
'F.5': { en: 'Gas valve fault', de: 'Gasventil Fehler' },
|
|
697
|
+
'F.7': { en: 'Flue gas sensor fault', de: 'Abgastemperaturfühler Fehler' },
|
|
698
|
+
'F.9': { en: 'Supply sensor fault', de: 'Vorlauftemperaturfühler Fehler' },
|
|
699
|
+
'F.10': { en: 'External sensor fault', de: 'Außenfühler Fehler' },
|
|
700
|
+
'F.11': { en: 'Return sensor fault', de: 'Rücklauftemperaturfühler Fehler' },
|
|
701
|
+
'F.12': { en: 'DHW sensor fault', de: 'Warmwasserfühler Fehler' },
|
|
702
|
+
'F.20': { en: 'Safety temperature limiter', de: 'Sicherheitstemperaturbegrenzer' },
|
|
703
|
+
'F.22': { en: 'Low water pressure fault', de: 'Wassermangel' },
|
|
704
|
+
'F.23': { en: 'Pump fault — overtemp', de: 'Pumpe Fehler — Übertemperatur' },
|
|
705
|
+
'F.24': { en: 'Pump circulation fault', de: 'Pumpe Zirkulationsfehler' },
|
|
706
|
+
'F.28': { en: 'Ignition fault (gas)', de: 'Zündstörung (Gas)' },
|
|
707
|
+
'F.29': { en: 'Flame fault after ignition', de: 'Flammenfehler nach Zündung' },
|
|
708
|
+
'F.30': { en: 'STB safety shutdown', de: 'STB Sicherheitsabschaltung' },
|
|
709
|
+
'F.31': { en: 'Boiler overtemperature', de: 'Kesselübertemperatur' },
|
|
710
|
+
'F.32': { en: 'Flue overtemperature', de: 'Abgasübertemperatur' },
|
|
711
|
+
'F.33': { en: 'Draft fault', de: 'Zugfehler' },
|
|
712
|
+
'F.36': { en: 'Fan fault', de: 'Lüfter Fehler' },
|
|
713
|
+
'F.40': { en: 'Compressor fault', de: 'Verdichter Fehler' },
|
|
714
|
+
'F.41': { en: 'Refrigerant circuit fault', de: 'Kältemittelkreis Fehler' },
|
|
715
|
+
'F.42': { en: 'Defrost fault', de: 'Abtaufehler' },
|
|
716
|
+
'F.50': { en: 'Communication fault gateway', de: 'Kommunikationsfehler Gateway' },
|
|
717
|
+
'F.51': { en: 'Communication fault controller', de: 'Kommunikationsfehler Regler' },
|
|
718
|
+
'F.52': { en: 'Bus fault', de: 'Busfehler' },
|
|
719
|
+
'F.60': { en: 'Expansion vessel fault', de: 'Ausdehnungsgefäß Fehler' },
|
|
720
|
+
'F.73': { en: 'Water pressure too high', de: 'Wasserdruck zu hoch' },
|
|
721
|
+
'F.74': { en: 'Water pressure too low', de: 'Wasserdruck zu niedrig' },
|
|
722
|
+
'F.75': { en: 'Pump speed sensor fault', de: 'Pumpendrehzahlsensor Fehler' },
|
|
723
|
+
};
|
|
724
|
+
|
|
725
|
+
function translateCode(code) {
|
|
726
|
+
if (!code) return null;
|
|
727
|
+
const entry = VIESSMANN_CODES[code];
|
|
728
|
+
if (entry) return entry.en;
|
|
729
|
+
// Partial match fallback (e.g. S.134 → try S.13 → S.1)
|
|
730
|
+
const parts = code.split('.');
|
|
731
|
+
if (parts.length === 2) {
|
|
732
|
+
const shorter = parts[0] + '.' + parts[1].slice(0, -1);
|
|
733
|
+
if (VIESSMANN_CODES[shorter]) return VIESSMANN_CODES[shorter].en + ' (variant)';
|
|
734
|
+
}
|
|
735
|
+
return null;
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
// Extract device messages from the latest API data snapshot (if available via CSV extension)
|
|
739
|
+
// For now we show the codes from the last row that has error_code-like fields
|
|
740
|
+
// (future: read from a separate messages JSON file written by the plugin)
|
|
741
|
+
const lastBoiler = boilerRows.length ? boilerRows[boilerRows.length - 1] : null;
|
|
742
|
+
|
|
584
743
|
const sc = (l,v,u='',badge='') => `<div class="sc"><div class="sl">${l}</div><div class="sv">${v!==null?v+u:'<span class="na">N/A</span>'} ${badge}</div></div>`;
|
|
585
744
|
const badge = (cls,txt) => `<span class="badge badge-${cls}">${txt}</span>`;
|
|
586
745
|
const genAt = new Date().toLocaleString('en-GB');
|
|
@@ -721,6 +880,18 @@ footer{text-align:center;font-size:10px;color:#bbb;padding:16px}
|
|
|
721
880
|
${dhwRows.length >= 2 ? `<div class="ch-tall"><canvas id="cDhw"></canvas></div>` : ''}
|
|
722
881
|
</div>
|
|
723
882
|
|
|
883
|
+
${gasForecast ? `
|
|
884
|
+
<div class="box">
|
|
885
|
+
<h2>⛽ Gas Forecast</h2>
|
|
886
|
+
<p class="note" style="margin-bottom:14px">Projection based on last ${gasForecast.daysUsed} day(s) of data · gas price: €${gasForecast.gasPrice}/m³ · trend: <strong>${gasForecast.trend === 'rising' ? '↑ Rising' : gasForecast.trend === 'falling' ? '↓ Falling' : '→ Stable'}</strong></p>
|
|
887
|
+
<div class="grid">
|
|
888
|
+
${sc('Avg consumption/day', gasForecast.avgPerDay, ' m³')}
|
|
889
|
+
${sc('Projected next 30 days', gasForecast.month30, ' m³', badge(gasForecast.trend === 'rising' ? 'warn' : 'good', '≈ €' + gasForecast.costMonth))}
|
|
890
|
+
${sc('Annual estimate', gasForecast.annualEst, ' m³', badge('neutral', '≈ €' + gasForecast.costAnnual))}
|
|
891
|
+
</div>
|
|
892
|
+
<p class="note" style="margin-top:10px">⚠️ Annual estimate uses period average × 365 — add more days for seasonal accuracy. Use <code>--gasPriceEur</code> to set your tariff.</p>
|
|
893
|
+
</div>` : ''}
|
|
894
|
+
|
|
724
895
|
${energyRows.length >= 1 ? `
|
|
725
896
|
<div class="box">
|
|
726
897
|
<h2>Energy System</h2>
|
|
@@ -737,6 +908,49 @@ ${energyRows.length >= 1 ? `
|
|
|
737
908
|
${hasWallbox && energyRows.length >= 2 ? `<div class="ch" style="margin-top:14px"><canvas id="cWallbox"></canvas></div>` : ''}
|
|
738
909
|
</div>` : ''}
|
|
739
910
|
|
|
911
|
+
</div>
|
|
912
|
+
|
|
913
|
+
<div class="box" id="device-messages">
|
|
914
|
+
<h2>🔔 Device Messages</h2>
|
|
915
|
+
<p class="note" style="margin-bottom:12px">Status, info and fault codes reported by the device. Translated from Viessmann service documentation.</p>
|
|
916
|
+
<div id="msg-list">
|
|
917
|
+
${(() => {
|
|
918
|
+
// Read messages from viessmann-messages-<ID>.json if available (written by plugin)
|
|
919
|
+
const msgFile = INSTALLATION_ID
|
|
920
|
+
? require('path').join(HB_PATH, 'viessmann-messages-' + INSTALLATION_ID + '.json')
|
|
921
|
+
: require('path').join(HB_PATH, 'viessmann-messages.json');
|
|
922
|
+
let messages = [];
|
|
923
|
+
try {
|
|
924
|
+
if (require('fs').existsSync(msgFile)) {
|
|
925
|
+
messages = JSON.parse(require('fs').readFileSync(msgFile, 'utf8'));
|
|
926
|
+
}
|
|
927
|
+
} catch(_) {}
|
|
928
|
+
|
|
929
|
+
if (!messages.length) {
|
|
930
|
+
return `<p class="note">No messages file found yet. Messages will appear here once the plugin writes <code>viessmann-messages-${INSTALLATION_ID || ''}.json</code>.</p>`;
|
|
931
|
+
}
|
|
932
|
+
|
|
933
|
+
return messages.slice(0, 20).map(m => {
|
|
934
|
+
const code = m.errorCode || m.code || '';
|
|
935
|
+
const desc = translateCode(code);
|
|
936
|
+
const ts = m.timestamp ? new Date(m.timestamp).toLocaleString('en-GB') : '';
|
|
937
|
+
const type = code.startsWith('F.') ? 'fault' : code.startsWith('I.') ? 'info' : 'status';
|
|
938
|
+
const bg = type === 'fault' ? '#fff3e0' : type === 'info' ? '#e8f4fd' : '#f1f8f1';
|
|
939
|
+
const br = type === 'fault' ? '#ffb74d' : type === 'info' ? '#90caf9' : '#a5d6a7';
|
|
940
|
+
const icon = type === 'fault' ? '⚠️' : type === 'info' ? 'ℹ️' : '✅';
|
|
941
|
+
return `<div style="display:flex;gap:12px;align-items:flex-start;padding:10px 12px;background:${bg};border-left:3px solid ${br};border-radius:4px;margin-bottom:6px;font-size:13px">
|
|
942
|
+
<span style="flex-shrink:0">${icon}</span>
|
|
943
|
+
<div style="flex:1">
|
|
944
|
+
<strong>${code}</strong>${desc ? ` — ${desc}` : ' — (unknown code)'}
|
|
945
|
+
${ts ? `<span style="color:#888;font-size:11px;margin-left:8px">${ts}</span>` : ''}
|
|
946
|
+
${m.busAddress ? `<span style="color:#aaa;font-size:11px;margin-left:8px">bus: ${m.busAddress}</span>` : ''}
|
|
947
|
+
</div>
|
|
948
|
+
</div>`;
|
|
949
|
+
}).join('');
|
|
950
|
+
})()}
|
|
951
|
+
</div>
|
|
952
|
+
</div>
|
|
953
|
+
|
|
740
954
|
</div>
|
|
741
955
|
<footer>homebridge-viessmann-vicare | ${CSV_FILE}</footer>
|
|
742
956
|
|