homebridge-viessmann-vicare 2.0.40 → 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 +9 -0
- package/SETUP-GUIDE.md +10 -3
- package/dist/platform.d.ts.map +1 -1
- package/dist/platform.js +6 -2
- package/dist/settings.d.ts +1 -1
- package/dist/settings.js +1 -1
- package/package.json +1 -1
- package/viessmann-report.js +214 -0
package/README.md
CHANGED
|
@@ -1033,6 +1033,15 @@ 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
|
+
|
|
1036
1045
|
### [2.0.40] - 2026-03-15
|
|
1037
1046
|
#### Fixed
|
|
1038
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.
|
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,13 @@ 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
|
+
|
|
1231
1238
|
### v2.0.40 (2026-03-15)
|
|
1232
1239
|
**Critical bug fix + log noise reduction:**
|
|
1233
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.
|
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
|
}
|
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": {
|
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
|
|