homebridge-viessmann-vicare 2.0.76 → 2.0.78
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 +17 -0
- package/README.md +25 -0
- package/SETUP-GUIDE.md +22 -3
- package/config.schema.json +20 -2
- package/dist/accessories/dhw-accessory.d.ts +5 -0
- package/dist/accessories/dhw-accessory.d.ts.map +1 -1
- package/dist/accessories/dhw-accessory.js +86 -1
- package/dist/accessories/room-sensor-accessory.d.ts.map +1 -1
- package/dist/accessories/room-sensor-accessory.js +4 -1
- package/dist/accessories/rooms-accessory.d.ts +37 -0
- package/dist/accessories/rooms-accessory.d.ts.map +1 -0
- package/dist/accessories/rooms-accessory.js +116 -0
- package/dist/auth-manager.d.ts.map +1 -1
- package/dist/auth-manager.js +18 -22
- package/dist/platform.d.ts +8 -0
- package/dist/platform.d.ts.map +1 -1
- package/dist/platform.js +111 -5
- package/dist/settings.d.ts +2 -1
- package/dist/settings.d.ts.map +1 -1
- package/dist/settings.js +3 -1
- package/grafana/viessmann-dashboard.json +507 -110
- package/package.json +1 -1
- package/viessmann-report-server.js +77 -12
- package/viessmann-report.js +61 -29
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,23 @@
|
|
|
3
3
|
All notable changes to homebridge-viessmann-vicare.
|
|
4
4
|
|
|
5
5
|
|
|
6
|
+
### [2.0.78] - 2026-09-26
|
|
7
|
+
- fix: **report "Device messages" were frozen**: the plugin stopped writing `viessmann-messages-<installation>-<device>.json` in 2.0.50, so the report showed months-old codes. The writer is back, now also includes fault codes (`device.messages.errors.raw`) and keeps a history (the API only returns current messages)
|
|
8
|
+
- fix: report heating-curve analysis uses only heating-season samples (outdoor < 16 °C): no more "weather compensation not active" advice in summer
|
|
9
|
+
- fix: report annual gas estimate requires ~a full year of data (a few summer weeks gave e.g. 52 m³/year)
|
|
10
|
+
- fix: report comfort-vs-efficiency is not evaluated when there is no space-heating gas in the period (it claimed "system optimisation is working" in summer)
|
|
11
|
+
- fix: report burner runtime shows one decimal below 1 %
|
|
12
|
+
|
|
13
|
+
### [2.0.77] - 2026-09-26
|
|
14
|
+
- fix: **30/90/365-day reports failed with "Load failed"**: report generation was O(n²) (90 days took ~3 min on a Raspberry Pi, now a few seconds), the browser request stayed open for minutes, and the report tab was opened after the wait (blocked as a pop-up by Safari). Reports now run as background jobs polled by the page, and the tab is opened at click time
|
|
15
|
+
- fix: report analysis: no false "short cycling" alarm when burner hours changed by less than 2 h (integer counter), heating-curve check uses only samples with the circuit in heating mode, 30-day gas forecast uses the period average with fewer than 14 days of data
|
|
16
|
+
- fix (#3): the OAuth authorization URL is now always printed in the log (Docker/Umbrel installs never showed it)
|
|
17
|
+
- fix (#6): no more empty extra "Boiler" accessory for energy devices (e.g. VitoCharge); a stale one is removed automatically
|
|
18
|
+
- fix: room-sensor discovery accessories were never refreshed, and could show a non-temperature value (e.g. 0 °C from heatingCircuitId)
|
|
19
|
+
- feat (#4): **ViCare Smart Climate rooms**: one HomeKit accessory per room (temperature, humidity when connected, window open/closed) with `features.enableRoomSensors: true`; names via `customNames.roomNames`
|
|
20
|
+
- feat (#8): DHW modes other than comfort/eco/off (e.g. `balanced`, `efficient`, `efficientWithMinComfort`) get their own switch; new **one-time hot water charge** switch ("Warm water once") when the device supports `heating.dhw.oneTimeCharge`
|
|
21
|
+
- feat: Grafana dashboard: text tiles (burner, program, modes, status code) no longer show "No data", state timelines stop at "now", sparse series show points
|
|
22
|
+
|
|
6
23
|
### [2.0.76] - 2026-09-26
|
|
7
24
|
- fix: report web server (`reportServerPort`) could stop silently: it was started with `execFile`, which buffers the child output (1 MB max) and kills it when the buffer is full, especially with `debug: true`. It could also fail with the port still held by an orphan process after a restart. The server now runs as a supervised child process: output goes to the Homebridge log, errors and exits are logged, it restarts automatically with back-off (max 5 attempts) and it is stopped when Homebridge shuts down
|
|
8
25
|
|
package/README.md
CHANGED
|
@@ -859,6 +859,8 @@ The plugin automatically creates these accessories (configurable via feature fla
|
|
|
859
859
|
- `[Installation] [Custom DHW Name] [Custom Comfort Name]` (Switch)
|
|
860
860
|
- `[Installation] [Custom DHW Name] [Custom Eco Name]` (Switch)
|
|
861
861
|
- `[Installation] [Custom DHW Name] [Custom Off Name]` (Switch)
|
|
862
|
+
- one extra switch for every other mode the device reports, e.g. `balanced`, `efficient`, `efficientWithMinComfort` *(v2.0.77+)*
|
|
863
|
+
- **One-time charge** *(v2.0.77+, if the device supports it)*: `[Installation] [Custom DHW Name] Once` (Switch), the "Warm water once" function of the ViCare app. It switches off by itself when the cylinder is charged.
|
|
862
864
|
- **Sensors**: Current DHW temperature, heating state
|
|
863
865
|
|
|
864
866
|
### 🏠 Heating Circuits
|
|
@@ -875,6 +877,12 @@ The plugin automatically creates these accessories (configurable via feature fla
|
|
|
875
877
|
- `[Installation] [Custom Heating Circuit Name] X [Custom Extended Heating Name]` (Switch)
|
|
876
878
|
- **Sensors**: Room temperature, supply temperature
|
|
877
879
|
|
|
880
|
+
### 🏠 ViCare Smart Climate rooms *(v2.0.77+, opt-in)*
|
|
881
|
+
With a ViCare **RoomControl** and smart radiator valves, set `"features": { "enableRoomSensors": true }` to get one accessory per room:
|
|
882
|
+
- `[Installation] Room N` (or the name from `customNames.roomNames`, in room order)
|
|
883
|
+
- **TemperatureSensor** (room temperature), **HumiditySensor** (only if the room humidity sensor is connected), **ContactSensor** for the window (if available)
|
|
884
|
+
- Room temperature and heating setpoint are logged to CSV/MySQL
|
|
885
|
+
|
|
878
886
|
## 🎯 Advanced Features
|
|
879
887
|
|
|
880
888
|
### Temperature Programs
|
|
@@ -1095,6 +1103,23 @@ For issues and questions:
|
|
|
1095
1103
|
|
|
1096
1104
|
## 📈 Changelog
|
|
1097
1105
|
|
|
1106
|
+
### [2.0.78] - 2026-09-26
|
|
1107
|
+
- fix: **report "Device messages" were frozen**: the plugin stopped writing `viessmann-messages-<installation>-<device>.json` in 2.0.50, so the report showed months-old codes. The writer is back, now also includes fault codes (`device.messages.errors.raw`) and keeps a history (the API only returns current messages)
|
|
1108
|
+
- fix: report heating-curve analysis uses only heating-season samples (outdoor < 16 °C): no more "weather compensation not active" advice in summer
|
|
1109
|
+
- fix: report annual gas estimate requires ~a full year of data (a few summer weeks gave e.g. 52 m³/year)
|
|
1110
|
+
- fix: report comfort-vs-efficiency is not evaluated when there is no space-heating gas in the period (it claimed "system optimisation is working" in summer)
|
|
1111
|
+
- fix: report burner runtime shows one decimal below 1 %
|
|
1112
|
+
|
|
1113
|
+
### [2.0.77] - 2026-09-26
|
|
1114
|
+
- fix: **30/90/365-day reports failed with "Load failed"**: report generation was O(n²) (90 days took ~3 min on a Raspberry Pi, now a few seconds), the browser request stayed open for minutes, and the report tab was opened after the wait (blocked as a pop-up by Safari). Reports now run as background jobs polled by the page, and the tab is opened at click time
|
|
1115
|
+
- fix: report analysis: no false "short cycling" alarm when burner hours changed by less than 2 h (integer counter), heating-curve check uses only samples with the circuit in heating mode, 30-day gas forecast uses the period average with fewer than 14 days of data
|
|
1116
|
+
- fix (#3): the OAuth authorization URL is now always printed in the log (Docker/Umbrel installs never showed it)
|
|
1117
|
+
- fix (#6): no more empty extra "Boiler" accessory for energy devices (e.g. VitoCharge); a stale one is removed automatically
|
|
1118
|
+
- fix: room-sensor discovery accessories were never refreshed, and could show a non-temperature value (e.g. 0 °C from heatingCircuitId)
|
|
1119
|
+
- feat (#4): **ViCare Smart Climate rooms**: one HomeKit accessory per room (temperature, humidity when connected, window open/closed) with `features.enableRoomSensors: true`; names via `customNames.roomNames`
|
|
1120
|
+
- feat (#8): DHW modes other than comfort/eco/off (e.g. `balanced`, `efficient`, `efficientWithMinComfort`) get their own switch; new **one-time hot water charge** switch ("Warm water once") when the device supports `heating.dhw.oneTimeCharge`
|
|
1121
|
+
- feat: Grafana dashboard: text tiles (burner, program, modes, status code) no longer show "No data", state timelines stop at "now", sparse series show points
|
|
1122
|
+
|
|
1098
1123
|
### [2.0.76] - 2026-09-26
|
|
1099
1124
|
- fix: report web server (`reportServerPort`) could stop silently: it was started with `execFile`, which buffers the child output (1 MB max) and kills it when the buffer is full, especially with `debug: true`. It could also fail with the port still held by an orphan process after a restart. The server now runs as a supervised child process: output goes to the Homebridge log, errors and exits are logged, it restarts automatically with back-off (max 5 attempts) and it is stopped when Homebridge shuts down
|
|
1100
1125
|
|
package/SETUP-GUIDE.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
# Complete Setup Guide - v2.0.
|
|
1
|
+
# Complete Setup Guide - v2.0.78
|
|
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.78 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
|
|
|
@@ -760,7 +760,16 @@ All energy data (PV production, battery level, charge/discharge, grid, wallbox)
|
|
|
760
760
|
|
|
761
761
|
> ⚠️ **Beta**: Energy accessory support and history logging are functional but depend on Viessmann API path variants that differ across VitoCharge generations. Enable `debug: true` and share logs if data appears missing.
|
|
762
762
|
|
|
763
|
-
###
|
|
763
|
+
### 🏠 ViCare Smart Climate rooms *(v2.0.77+)*
|
|
764
|
+
|
|
765
|
+
For installations with a ViCare **RoomControl** and smart radiator valves, enable:
|
|
766
|
+
```json
|
|
767
|
+
"features": { "enableRoomSensors": true },
|
|
768
|
+
"customNames": { "roomNames": ["Living room", "Kitchen", "Bedroom"] }
|
|
769
|
+
```
|
|
770
|
+
One accessory per room is created: **temperature** sensor, **humidity** sensor (only when the room humidity sensor is connected) and **window** contact sensor (when available). Without `roomNames` rooms are called "Room 1", "Room 2", … Room temperatures and setpoints are logged to CSV/MySQL (`room0`, `room1`, …).
|
|
771
|
+
|
|
772
|
+
### 🌡️ TRV / Room Sensor discovery *(diagnostics)*
|
|
764
773
|
|
|
765
774
|
Enable with `features.enableRoomSensorDiscovery: true` in plugin config. The plugin scans all non-main gateway devices and creates a `TemperatureSensor` accessory for each device with a temperature reading (ViCare Smart Climate TRVs, zone sensors, etc.).
|
|
766
775
|
|
|
@@ -780,6 +789,8 @@ Temperature history is logged to CSV (`room-<deviceId>` column) and MySQL on eve
|
|
|
780
789
|
- `[Installation Prefix] [Custom DHW Name] [Custom Comfort Name]` - Comfort Mode Switch
|
|
781
790
|
- `[Installation Prefix] [Custom DHW Name] [Custom Eco Name]` - Eco Mode Switch
|
|
782
791
|
- `[Installation Prefix] [Custom DHW Name] [Custom Off Name]` - Off Mode Switch
|
|
792
|
+
- Other modes reported by the device (e.g. `balanced`, `efficient`, `efficientWithMinComfort`) get their own switch *(v2.0.77+)*
|
|
793
|
+
- **One-time charge** *(v2.0.77+, if supported)*: `[Installation Prefix] [Custom DHW Name] Once` - Switch that starts a single hot-water charge ("Warm water once" in the ViCare app); it turns off by itself when the cylinder is charged
|
|
783
794
|
|
|
784
795
|
### 🏠 Heating Circuit Accessories
|
|
785
796
|
- **Main Circuit Control**: `[Installation Prefix] [Custom Heating Circuit Name] X` - HeaterCooler service for circuit temperature
|
|
@@ -1281,6 +1292,14 @@ sudo systemctl restart homebridge
|
|
|
1281
1292
|
|
|
1282
1293
|
## Changelog
|
|
1283
1294
|
|
|
1295
|
+
### v2.0.78 (2026-09-26)
|
|
1296
|
+
- fix: report device messages up to date again (with fault codes and history); seasonal-aware heating-curve, annual gas and comfort analyses
|
|
1297
|
+
|
|
1298
|
+
### v2.0.77 (2026-09-26)
|
|
1299
|
+
- fix: long reports (30/90/365 days) — much faster, generated in background, no more "Load failed"
|
|
1300
|
+
- fix: OAuth URL always logged (Docker), no empty extra Boiler for energy devices, report analysis false alarms
|
|
1301
|
+
- feat: ViCare Smart Climate rooms (`features.enableRoomSensors`), extra DHW modes and one-time hot water charge switch
|
|
1302
|
+
|
|
1284
1303
|
### v2.0.76 (2026-09-26)
|
|
1285
1304
|
- fix: report web server supervised (no more silent stop / "Load failed"): logs, automatic restart, clean stop on shutdown
|
|
1286
1305
|
|
package/config.schema.json
CHANGED
|
@@ -271,6 +271,15 @@
|
|
|
271
271
|
"type": "string",
|
|
272
272
|
"placeholder": "Riscaldamento Extra",
|
|
273
273
|
"description": "Custom name for extended heating mode (comfort boost). Default: 'Extended Heating'. Italian example: 'Riscaldamento Extra'"
|
|
274
|
+
},
|
|
275
|
+
"roomNames": {
|
|
276
|
+
"title": "Room names (ViCare Smart Climate)",
|
|
277
|
+
"type": "array",
|
|
278
|
+
"items": {
|
|
279
|
+
"type": "string",
|
|
280
|
+
"title": "Room name"
|
|
281
|
+
},
|
|
282
|
+
"description": "Names for the ViCare rooms in order (room 1, room 2, …). Leave empty to use \"Room 1\", \"Room 2\", …"
|
|
274
283
|
}
|
|
275
284
|
}
|
|
276
285
|
},
|
|
@@ -439,6 +448,12 @@
|
|
|
439
448
|
"type": "boolean",
|
|
440
449
|
"default": false,
|
|
441
450
|
"description": "Scan all gateway devices for thermostatic radiator valves (TRVs) or per-room temperature sensors. Creates a HomeKit TemperatureSensor for each discovered device and dumps all API feature paths to the Homebridge log. Enable this if you have Viessmann ViCare Smart Climate and want to help implement full TRV support. Disable in production — generates verbose logs."
|
|
451
|
+
},
|
|
452
|
+
"enableRoomSensors": {
|
|
453
|
+
"title": "Room sensors (ViCare Smart Climate)",
|
|
454
|
+
"type": "boolean",
|
|
455
|
+
"default": false,
|
|
456
|
+
"description": "Create one HomeKit accessory per ViCare room (temperature, humidity when a sensor is connected, window open/closed) for installations with a ViCare RoomControl and smart radiator valves. Room names can be set in Custom Names → Room names."
|
|
442
457
|
}
|
|
443
458
|
}
|
|
444
459
|
},
|
|
@@ -688,6 +703,7 @@
|
|
|
688
703
|
"customNames.holiday",
|
|
689
704
|
"customNames.holidayAtHome",
|
|
690
705
|
"customNames.extendedHeating",
|
|
706
|
+
"customNames.roomNames",
|
|
691
707
|
"forceServiceRecreation"
|
|
692
708
|
]
|
|
693
709
|
},
|
|
@@ -737,7 +753,9 @@
|
|
|
737
753
|
"features.enableHeatingCircuitAccessories",
|
|
738
754
|
"features.enableTemperaturePrograms",
|
|
739
755
|
"features.enableQuickSelections",
|
|
740
|
-
"features.enableBurnerStatus"
|
|
756
|
+
"features.enableBurnerStatus",
|
|
757
|
+
"features.enableRoomSensors",
|
|
758
|
+
"features.enableRoomSensorDiscovery"
|
|
741
759
|
]
|
|
742
760
|
},
|
|
743
761
|
{
|
|
@@ -792,4 +810,4 @@
|
|
|
792
810
|
]
|
|
793
811
|
}
|
|
794
812
|
]
|
|
795
|
-
}
|
|
813
|
+
}
|
|
@@ -11,6 +11,10 @@ export declare class ViessmannDHWAccessory {
|
|
|
11
11
|
private comfortService?;
|
|
12
12
|
private ecoService?;
|
|
13
13
|
private offService?;
|
|
14
|
+
private extraModeServices;
|
|
15
|
+
private oneTimeChargeService?;
|
|
16
|
+
private supportsOneTimeCharge;
|
|
17
|
+
private oneTimeChargeActive;
|
|
14
18
|
private availableModes;
|
|
15
19
|
private supportsTemperatureControl;
|
|
16
20
|
private temperatureConstraints;
|
|
@@ -33,6 +37,7 @@ export declare class ViessmannDHWAccessory {
|
|
|
33
37
|
private setupHeaterCoolerService;
|
|
34
38
|
private setupModeServices;
|
|
35
39
|
private removeAllModeServices;
|
|
40
|
+
setOneTimeCharge(value: CharacteristicValue): Promise<void>;
|
|
36
41
|
setActive(value: CharacteristicValue): Promise<void>;
|
|
37
42
|
setComfortMode(value: CharacteristicValue): Promise<void>;
|
|
38
43
|
setEcoMode(value: CharacteristicValue): Promise<void>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dhw-accessory.d.ts","sourceRoot":"","sources":["../../src/accessories/dhw-accessory.ts"],"names":[],"mappings":"AAAA,OAAO,EAAW,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAE7E,OAAO,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,gBAAgB,EAAE,eAAe,EAA2B,MAAM,aAAa,CAAC;AAGnI,qBAAa,qBAAqB;
|
|
1
|
+
{"version":3,"file":"dhw-accessory.d.ts","sourceRoot":"","sources":["../../src/accessories/dhw-accessory.ts"],"names":[],"mappings":"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"}
|
|
@@ -14,6 +14,12 @@ class ViessmannDHWAccessory {
|
|
|
14
14
|
comfortService;
|
|
15
15
|
ecoService;
|
|
16
16
|
offService;
|
|
17
|
+
// Extra DHW modes exposed by some devices (e.g. balanced, efficient) — issue #8
|
|
18
|
+
extraModeServices = new Map();
|
|
19
|
+
// One-time DHW charge ("Warm water once" in the ViCare app) — issue #8
|
|
20
|
+
oneTimeChargeService;
|
|
21
|
+
supportsOneTimeCharge = false;
|
|
22
|
+
oneTimeChargeActive = false;
|
|
17
23
|
availableModes = [];
|
|
18
24
|
supportsTemperatureControl = false;
|
|
19
25
|
temperatureConstraints = { min: 30, max: 60 }; // Default DHW temperature range
|
|
@@ -140,7 +146,11 @@ class ViessmannDHWAccessory {
|
|
|
140
146
|
this.states.HeatingThresholdTemperature = 50;
|
|
141
147
|
}
|
|
142
148
|
}
|
|
143
|
-
|
|
149
|
+
// One-time charge ("Warm water once")
|
|
150
|
+
const otc = features.find(f => f.feature === 'heating.dhw.oneTimeCharge');
|
|
151
|
+
this.supportsOneTimeCharge = !!(otc?.isEnabled !== false && otc?.commands?.activate);
|
|
152
|
+
this.oneTimeChargeActive = otc?.properties?.active?.value === true;
|
|
153
|
+
this.platform.log.info(`DHW Capabilities - Modes: [${this.availableModes.join(', ')}], Temperature: ${this.supportsTemperatureControl ? 'Yes' : 'No'}, One-time charge: ${this.supportsOneTimeCharge ? 'Yes' : 'No'}`);
|
|
144
154
|
}
|
|
145
155
|
setupCharacteristics() {
|
|
146
156
|
// Remove any existing conflicting services
|
|
@@ -308,6 +318,46 @@ class ViessmannDHWAccessory {
|
|
|
308
318
|
.onGet(() => this.currentMode === 'off')
|
|
309
319
|
.onSet(this.setOffMode.bind(this));
|
|
310
320
|
}
|
|
321
|
+
// Extra modes (balanced, efficient, efficientWithMinComfort, …): one switch each
|
|
322
|
+
const EXTRA_LABELS = {
|
|
323
|
+
balanced: 'Balanced', efficient: 'Efficient', efficientWithMinComfort: 'Efficient+',
|
|
324
|
+
};
|
|
325
|
+
for (const mode of this.availableModes.filter(m => !['comfort', 'eco', 'off'].includes(m))) {
|
|
326
|
+
const label = config.customNames?.[`dhw_${mode}`] || EXTRA_LABELS[mode] || mode;
|
|
327
|
+
const name = `${installationName} ${dhwName} ${label}`;
|
|
328
|
+
this.platform.log.info(`🏷️ Creating ${mode} service: "${name}"`);
|
|
329
|
+
const svc = this.accessory.addService(this.platform.Service.Switch, name, `dhw-${mode}-${subtypeVersion}`);
|
|
330
|
+
svc.setCharacteristic(this.platform.Characteristic.Name, name);
|
|
331
|
+
svc.displayName = name;
|
|
332
|
+
svc.getCharacteristic(this.platform.Characteristic.On)
|
|
333
|
+
.onGet(() => this.currentMode === mode)
|
|
334
|
+
.onSet(async (value) => {
|
|
335
|
+
if (this._updatingCharacteristics)
|
|
336
|
+
return;
|
|
337
|
+
if (value && this.currentMode !== mode) {
|
|
338
|
+
await this.setMode(mode);
|
|
339
|
+
}
|
|
340
|
+
else if (!value && this.currentMode === mode && this.availableModes.includes('off')) {
|
|
341
|
+
await this.setMode('off');
|
|
342
|
+
}
|
|
343
|
+
else if (!value) {
|
|
344
|
+
setImmediate(() => this.updateAllCharacteristics());
|
|
345
|
+
}
|
|
346
|
+
});
|
|
347
|
+
this.extraModeServices.set(mode, svc);
|
|
348
|
+
}
|
|
349
|
+
// One-time charge switch ("Warm water once")
|
|
350
|
+
if (this.supportsOneTimeCharge) {
|
|
351
|
+
const label = config.customNames?.dhw_oneTimeCharge || 'Once';
|
|
352
|
+
const name = `${installationName} ${dhwName} ${label}`;
|
|
353
|
+
this.platform.log.info(`🏷️ Creating one-time charge service: "${name}"`);
|
|
354
|
+
this.oneTimeChargeService = this.accessory.addService(this.platform.Service.Switch, name, `dhw-onetimecharge-${subtypeVersion}`);
|
|
355
|
+
this.oneTimeChargeService.setCharacteristic(this.platform.Characteristic.Name, name);
|
|
356
|
+
this.oneTimeChargeService.displayName = name;
|
|
357
|
+
this.oneTimeChargeService.getCharacteristic(this.platform.Characteristic.On)
|
|
358
|
+
.onGet(() => this.oneTimeChargeActive)
|
|
359
|
+
.onSet(this.setOneTimeCharge.bind(this));
|
|
360
|
+
}
|
|
311
361
|
this.platform.log.info(`✅ DHW mode services setup completed for modes: [${this.availableModes.join(', ')}] with subtype version: ${subtypeVersion}`);
|
|
312
362
|
}
|
|
313
363
|
removeAllModeServices() {
|
|
@@ -326,6 +376,26 @@ class ViessmannDHWAccessory {
|
|
|
326
376
|
this.comfortService = undefined;
|
|
327
377
|
this.ecoService = undefined;
|
|
328
378
|
this.offService = undefined;
|
|
379
|
+
this.extraModeServices.clear();
|
|
380
|
+
this.oneTimeChargeService = undefined;
|
|
381
|
+
}
|
|
382
|
+
// 🚿 One-time DHW charge ("Warm water once") — heating.dhw.oneTimeCharge activate/deactivate
|
|
383
|
+
async setOneTimeCharge(value) {
|
|
384
|
+
if (this._updatingCharacteristics)
|
|
385
|
+
return;
|
|
386
|
+
const on = value;
|
|
387
|
+
try {
|
|
388
|
+
const ok = await this.platform.viessmannAPI.executeCommand(this.installation.id, this.gateway.serial, this.device.id, 'heating.dhw.oneTimeCharge', on ? 'activate' : 'deactivate', {});
|
|
389
|
+
if (!ok)
|
|
390
|
+
throw new Error('command rejected');
|
|
391
|
+
this.oneTimeChargeActive = on;
|
|
392
|
+
this.platform.log.info(`🚿 DHW one-time charge ${on ? 'started' : 'stopped'}`);
|
|
393
|
+
}
|
|
394
|
+
catch (error) {
|
|
395
|
+
this.platform.log.error(`Failed to ${on ? 'start' : 'stop'} DHW one-time charge:`, error instanceof Error ? error.message : error);
|
|
396
|
+
setImmediate(() => this.oneTimeChargeService?.updateCharacteristic(this.platform.Characteristic.On, this.oneTimeChargeActive));
|
|
397
|
+
throw new this.platform.api.hap.HapStatusError(-70402 /* this.platform.api.hap.HAPStatus.SERVICE_COMMUNICATION_FAILURE */);
|
|
398
|
+
}
|
|
329
399
|
}
|
|
330
400
|
async setActive(value) {
|
|
331
401
|
const active = value;
|
|
@@ -475,6 +545,12 @@ class ViessmannDHWAccessory {
|
|
|
475
545
|
if (this.offService) {
|
|
476
546
|
this.offService.updateCharacteristic(this.platform.Characteristic.On, isOff);
|
|
477
547
|
}
|
|
548
|
+
for (const [mode, svc] of this.extraModeServices) {
|
|
549
|
+
svc.updateCharacteristic(this.platform.Characteristic.On, this.currentMode === mode);
|
|
550
|
+
}
|
|
551
|
+
if (this.oneTimeChargeService) {
|
|
552
|
+
this.oneTimeChargeService.updateCharacteristic(this.platform.Characteristic.On, this.oneTimeChargeActive);
|
|
553
|
+
}
|
|
478
554
|
this.platform.log.debug(`DHW States - Mode: ${this.currentMode.toUpperCase()}, Active: ${isActive}, Comfort: ${isComfort}, Eco: ${isEco}, Off: ${isOff}`);
|
|
479
555
|
}
|
|
480
556
|
finally {
|
|
@@ -640,6 +716,15 @@ class ViessmannDHWAccessory {
|
|
|
640
716
|
}
|
|
641
717
|
async updateFromFeatures(features) {
|
|
642
718
|
let changed = false;
|
|
719
|
+
// One-time charge state (ends automatically when the cylinder is charged)
|
|
720
|
+
const otcFeature = features.find(f => f.feature === 'heating.dhw.oneTimeCharge');
|
|
721
|
+
if (otcFeature?.properties?.active?.value !== undefined) {
|
|
722
|
+
const otc = otcFeature.properties.active.value === true;
|
|
723
|
+
if (otc !== this.oneTimeChargeActive) {
|
|
724
|
+
this.oneTimeChargeActive = otc;
|
|
725
|
+
changed = true;
|
|
726
|
+
}
|
|
727
|
+
}
|
|
643
728
|
// Update DHW current temperature
|
|
644
729
|
const dhwTempFeature = features.find(f => f.feature === 'heating.dhw.sensors.temperature.dhwCylinder' ||
|
|
645
730
|
f.feature === 'heating.dhw.sensors.temperature.hotWaterStorage' ||
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"room-sensor-accessory.d.ts","sourceRoot":"","sources":["../../src/accessories/room-sensor-accessory.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAEH,OAAO,EAAE,iBAAiB,EAAW,MAAM,YAAY,CAAC;AACxD,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,EAAE,gBAAgB,EAAE,eAAe,EAAE,qBAAqB,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAC;AAkCxH,MAAM,WAAW,yBAAyB;IACxC,MAAM,EAAE,eAAe,CAAC;IACxB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,eAAe,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACpF,eAAe,EAAE,MAAM,CAAC;IACxB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,CACpC,QAAQ,EAAE,iBAAiB,EAC3B,MAAM,EAAE,eAAe,EACvB,QAAQ,EAAE,gBAAgB,EAAE,GAC3B,yBAAyB,
|
|
1
|
+
{"version":3,"file":"room-sensor-accessory.d.ts","sourceRoot":"","sources":["../../src/accessories/room-sensor-accessory.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAEH,OAAO,EAAE,iBAAiB,EAAW,MAAM,YAAY,CAAC;AACxD,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,EAAE,gBAAgB,EAAE,eAAe,EAAE,qBAAqB,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAC;AAkCxH,MAAM,WAAW,yBAAyB;IACxC,MAAM,EAAE,eAAe,CAAC;IACxB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,eAAe,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACpF,eAAe,EAAE,MAAM,CAAC;IACxB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,CACpC,QAAQ,EAAE,iBAAiB,EAC3B,MAAM,EAAE,eAAe,EACvB,QAAQ,EAAE,gBAAgB,EAAE,GAC3B,yBAAyB,CAyG3B;AAID,qBAAa,4BAA4B;IASrC,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,YAAY;IAC7B,OAAO,CAAC,QAAQ,CAAC,OAAO;IAX1B,OAAO,CAAC,kBAAkB,CAAU;IACpC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAkB;IACtC,OAAO,CAAC,aAAa,CAAC,CAAyB;IAG/C,OAAO,CAAC,eAAe,CAA4B;gBAGhC,QAAQ,EAAE,iBAAiB,EAC3B,SAAS,EAAE,iBAAiB,EAC5B,YAAY,EAAE,qBAAqB,EACnC,OAAO,EAAE,gBAAgB,EAC1C,MAAM,EAAE,eAAe,EACvB,QAAQ,EAAE,gBAAgB,EAAE;IA8C9B;;OAEG;IACI,MAAM,CAAC,QAAQ,EAAE,gBAAgB,EAAE,GAAG,IAAI;CAsClD"}
|
|
@@ -95,7 +95,10 @@ function discoverRoomSensorData(platform, device, features) {
|
|
|
95
95
|
const type = p?.type ?? typeof val;
|
|
96
96
|
if (val !== undefined && val !== null) {
|
|
97
97
|
// Collect numeric temperature-range values
|
|
98
|
-
|
|
98
|
+
// Only real temperatures: unit celsius/fahrenheit or a "temperature" path.
|
|
99
|
+
// (Before 2.0.77 any number matched, e.g. device.heatingCircuitId = 0 → "0 °C".)
|
|
100
|
+
const isTemp = /celsius|fahrenheit/i.test(unit) || /temperature/i.test(f.feature);
|
|
101
|
+
if (typeof val === 'number' && val > -30 && val < 100 && unit !== '%' && isTemp) {
|
|
99
102
|
allTempFeatures.push({ path: f.feature, prop: pk, value: val, unit });
|
|
100
103
|
}
|
|
101
104
|
const display = Array.isArray(val)
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ViessmannRoomAccessory — one HomeKit accessory per room of a ViCare Smart Climate
|
|
3
|
+
* "RoomControl" device (issue #4).
|
|
4
|
+
*
|
|
5
|
+
* Feature paths (confirmed on E3_RoomControl_One_05 with E3_RadiatorActuator TRVs):
|
|
6
|
+
* rooms.N.sensors.temperature value (°C), status connected/notConnected
|
|
7
|
+
* rooms.N.sensors.humidity value (%), status connected/notConnected
|
|
8
|
+
* rooms.N.sensors.window.openState value (boolean) — optional
|
|
9
|
+
* rooms.N.temperature.levels.heating temperature (°C) — current heating setpoint
|
|
10
|
+
*
|
|
11
|
+
* Services per room:
|
|
12
|
+
* - TemperatureSensor (always)
|
|
13
|
+
* - HumiditySensor (only when the room humidity sensor is connected)
|
|
14
|
+
* - ContactSensor (window open/closed, only when the feature exists)
|
|
15
|
+
*
|
|
16
|
+
* Enable with: "features": { "enableRoomSensors": true }
|
|
17
|
+
* Room names: "customNames": { "rooms": { "0": "Living room", "1": "Bedroom" } }
|
|
18
|
+
*/
|
|
19
|
+
import { PlatformAccessory } from 'homebridge';
|
|
20
|
+
import { ViessmannPlatform } from '../platform';
|
|
21
|
+
import { ViessmannFeature, ViessmannDevice, ViessmannInstallation, ViessmannGateway } from '../viessmann-api-endpoints';
|
|
22
|
+
/** Room indexes exposed by a device (rooms.N.sensors.temperature present and enabled). */
|
|
23
|
+
export declare function detectRoomIndexes(features: ViessmannFeature[]): number[];
|
|
24
|
+
export declare class ViessmannRoomAccessory {
|
|
25
|
+
private readonly platform;
|
|
26
|
+
private readonly accessory;
|
|
27
|
+
private readonly roomIndex;
|
|
28
|
+
private readonly tempService;
|
|
29
|
+
private humidityService?;
|
|
30
|
+
private windowService?;
|
|
31
|
+
private historyLogger?;
|
|
32
|
+
private readonly TAG;
|
|
33
|
+
constructor(platform: ViessmannPlatform, accessory: PlatformAccessory, installation: ViessmannInstallation, gateway: ViessmannGateway, device: ViessmannDevice, roomIndex: number, features: ViessmannFeature[]);
|
|
34
|
+
private featureGetter;
|
|
35
|
+
update(features: ViessmannFeature[]): void;
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=rooms-accessory.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rooms-accessory.d.ts","sourceRoot":"","sources":["../../src/accessories/rooms-accessory.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,EAAE,iBAAiB,EAAW,MAAM,YAAY,CAAC;AACxD,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,EAAE,gBAAgB,EAAE,eAAe,EAAE,qBAAqB,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAC;AAGxH,0FAA0F;AAC1F,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,gBAAgB,EAAE,GAAG,MAAM,EAAE,CAOxE;AAED,qBAAa,sBAAsB;IAQ/B,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,SAAS;IAI1B,OAAO,CAAC,QAAQ,CAAC,SAAS;IAZ5B,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAU;IACtC,OAAO,CAAC,eAAe,CAAC,CAAU;IAClC,OAAO,CAAC,aAAa,CAAC,CAAU;IAChC,OAAO,CAAC,aAAa,CAAC,CAAyB;IAC/C,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAS;gBAGV,QAAQ,EAAE,iBAAiB,EAC3B,SAAS,EAAE,iBAAiB,EAC7C,YAAY,EAAE,qBAAqB,EACnC,OAAO,EAAE,gBAAgB,EACzB,MAAM,EAAE,eAAe,EACN,SAAS,EAAE,MAAM,EAClC,QAAQ,EAAE,gBAAgB,EAAE;IAoC9B,OAAO,CAAC,aAAa;IAKd,MAAM,CAAC,QAAQ,EAAE,gBAAgB,EAAE,GAAG,IAAI;CAsClD"}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* ViessmannRoomAccessory — one HomeKit accessory per room of a ViCare Smart Climate
|
|
4
|
+
* "RoomControl" device (issue #4).
|
|
5
|
+
*
|
|
6
|
+
* Feature paths (confirmed on E3_RoomControl_One_05 with E3_RadiatorActuator TRVs):
|
|
7
|
+
* rooms.N.sensors.temperature value (°C), status connected/notConnected
|
|
8
|
+
* rooms.N.sensors.humidity value (%), status connected/notConnected
|
|
9
|
+
* rooms.N.sensors.window.openState value (boolean) — optional
|
|
10
|
+
* rooms.N.temperature.levels.heating temperature (°C) — current heating setpoint
|
|
11
|
+
*
|
|
12
|
+
* Services per room:
|
|
13
|
+
* - TemperatureSensor (always)
|
|
14
|
+
* - HumiditySensor (only when the room humidity sensor is connected)
|
|
15
|
+
* - ContactSensor (window open/closed, only when the feature exists)
|
|
16
|
+
*
|
|
17
|
+
* Enable with: "features": { "enableRoomSensors": true }
|
|
18
|
+
* Room names: "customNames": { "rooms": { "0": "Living room", "1": "Bedroom" } }
|
|
19
|
+
*/
|
|
20
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
21
|
+
exports.ViessmannRoomAccessory = void 0;
|
|
22
|
+
exports.detectRoomIndexes = detectRoomIndexes;
|
|
23
|
+
const history_logger_1 = require("./history-logger");
|
|
24
|
+
/** Room indexes exposed by a device (rooms.N.sensors.temperature present and enabled). */
|
|
25
|
+
function detectRoomIndexes(features) {
|
|
26
|
+
const idx = new Set();
|
|
27
|
+
for (const f of features) {
|
|
28
|
+
const m = /^rooms\.(\d+)\.sensors\.temperature$/.exec(f.feature);
|
|
29
|
+
if (m && f.isEnabled !== false)
|
|
30
|
+
idx.add(parseInt(m[1], 10));
|
|
31
|
+
}
|
|
32
|
+
return [...idx].sort((a, b) => a - b);
|
|
33
|
+
}
|
|
34
|
+
class ViessmannRoomAccessory {
|
|
35
|
+
platform;
|
|
36
|
+
accessory;
|
|
37
|
+
roomIndex;
|
|
38
|
+
tempService;
|
|
39
|
+
humidityService;
|
|
40
|
+
windowService;
|
|
41
|
+
historyLogger;
|
|
42
|
+
TAG;
|
|
43
|
+
constructor(platform, accessory, installation, gateway, device, roomIndex, features) {
|
|
44
|
+
this.platform = platform;
|
|
45
|
+
this.accessory = accessory;
|
|
46
|
+
this.roomIndex = roomIndex;
|
|
47
|
+
const { Service: Svc, Characteristic: Char } = platform;
|
|
48
|
+
this.TAG = `[Room ${roomIndex}]`;
|
|
49
|
+
accessory.getService(Svc.AccessoryInformation)
|
|
50
|
+
.setCharacteristic(Char.Manufacturer, 'Viessmann')
|
|
51
|
+
.setCharacteristic(Char.Model, device.modelId || 'ViCare Room')
|
|
52
|
+
.setCharacteristic(Char.SerialNumber, `${installation.id}-${device.id}-room${roomIndex}`);
|
|
53
|
+
this.tempService = accessory.getService(Svc.TemperatureSensor)
|
|
54
|
+
|| accessory.addService(Svc.TemperatureSensor, accessory.displayName);
|
|
55
|
+
this.tempService.getCharacteristic(Char.CurrentTemperature).setProps({ minValue: -30, maxValue: 60 });
|
|
56
|
+
const feat = this.featureGetter(features);
|
|
57
|
+
// Humidity only if a sensor is actually connected
|
|
58
|
+
if (feat('sensors.humidity')?.properties?.status?.value === 'connected') {
|
|
59
|
+
this.humidityService = accessory.getService(Svc.HumiditySensor)
|
|
60
|
+
|| accessory.addService(Svc.HumiditySensor, `${accessory.displayName} Humidity`);
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
const old = accessory.getService(Svc.HumiditySensor);
|
|
64
|
+
if (old)
|
|
65
|
+
accessory.removeService(old);
|
|
66
|
+
}
|
|
67
|
+
// Window open/closed
|
|
68
|
+
if (feat('sensors.window.openState')?.properties?.value?.value !== undefined) {
|
|
69
|
+
this.windowService = accessory.getService(Svc.ContactSensor)
|
|
70
|
+
|| accessory.addService(Svc.ContactSensor, `${accessory.displayName} Window`);
|
|
71
|
+
}
|
|
72
|
+
this.historyLogger = new history_logger_1.ViessmannHistoryLogger(platform, accessory, 'thermo', `Room${roomIndex}`, installation.id, gateway.serial);
|
|
73
|
+
// Hook into the platform refresh loop (it calls accessory.context.updateHandler)
|
|
74
|
+
accessory.context.updateHandler = (feats) => this.update(feats);
|
|
75
|
+
this.update(features);
|
|
76
|
+
}
|
|
77
|
+
featureGetter(features) {
|
|
78
|
+
const prefix = `rooms.${this.roomIndex}.`;
|
|
79
|
+
return (suffix) => features.find(f => f.feature === prefix + suffix && f.isEnabled !== false);
|
|
80
|
+
}
|
|
81
|
+
update(features) {
|
|
82
|
+
const Char = this.platform.Characteristic;
|
|
83
|
+
const feat = this.featureGetter(features);
|
|
84
|
+
const t = feat('sensors.temperature');
|
|
85
|
+
const temp = t?.properties?.value?.value;
|
|
86
|
+
const connected = t?.properties?.status?.value !== 'notConnected';
|
|
87
|
+
this.tempService.getCharacteristic(Char.StatusActive).updateValue(connected && typeof temp === 'number');
|
|
88
|
+
if (typeof temp === 'number') {
|
|
89
|
+
this.tempService.getCharacteristic(Char.CurrentTemperature).updateValue(temp);
|
|
90
|
+
}
|
|
91
|
+
if (this.humidityService) {
|
|
92
|
+
const h = feat('sensors.humidity')?.properties?.value?.value;
|
|
93
|
+
if (typeof h === 'number')
|
|
94
|
+
this.humidityService.getCharacteristic(Char.CurrentRelativeHumidity).updateValue(Math.max(0, Math.min(100, h)));
|
|
95
|
+
}
|
|
96
|
+
if (this.windowService) {
|
|
97
|
+
const open = feat('sensors.window.openState')?.properties?.value?.value === true;
|
|
98
|
+
this.windowService.getCharacteristic(Char.ContactSensorState).updateValue(open
|
|
99
|
+
? Char.ContactSensorState.CONTACT_NOT_DETECTED
|
|
100
|
+
: Char.ContactSensorState.CONTACT_DETECTED);
|
|
101
|
+
}
|
|
102
|
+
const setpoint = feat('temperature.levels.heating')?.properties?.temperature?.value;
|
|
103
|
+
this.platform.log.debug(`${this.TAG} temp=${temp}°C setpoint=${setpoint ?? '-'}°C`);
|
|
104
|
+
if (typeof temp === 'number' && this.historyLogger) {
|
|
105
|
+
this.historyLogger.addThermoEntry({ currentTemp: temp, setTemp: typeof setpoint === 'number' ? setpoint : temp });
|
|
106
|
+
this.historyLogger.appendRow({
|
|
107
|
+
timestamp: new Date().toISOString(),
|
|
108
|
+
accessory: `room${this.roomIndex}`,
|
|
109
|
+
event_type: 'snapshot',
|
|
110
|
+
room_temp: temp,
|
|
111
|
+
target_temp: typeof setpoint === 'number' ? setpoint : undefined,
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
exports.ViessmannRoomAccessory = ViessmannRoomAccessory;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth-manager.d.ts","sourceRoot":"","sources":["../src/auth-manager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AACpC,OAAO,EAAE,aAAa,EAAiB,MAAM,OAAO,CAAC;AAOrD,MAAM,WAAW,UAAU;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,GAAG,QAAQ,CAAC;IAC/B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,sBAAsB,CAAC,EAAE,OAAO,CAAC;CAClC;AAkBD,qBAAa,WAAW;IA2BpB,OAAO,CAAC,QAAQ,CAAC,GAAG;IACpB,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,UAAU;IAC3B,OAAO,CAAC,QAAQ,CAAC,MAAM;IA7BzB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAuD;IAC/E,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAS;IAG1C,OAAO,CAAC,QAAQ,CAAC,sBAAsB,CAAS;IAChD,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAe;IACjD,OAAO,CAAC,QAAQ,CAAC,wBAAwB,CAAW;IAEpD,OAAO,CAAC,WAAW,CAAC,CAAS;IAC7B,OAAO,CAAC,YAAY,CAAC,CAAS;IAC9B,OAAO,CAAC,cAAc,CAAC,CAAS;IAChC,OAAO,CAAC,aAAa,CAAC,CAAS;IAC/B,OAAO,CAAC,UAAU,CAAC,CAAS;IAC5B,OAAO,CAAC,qBAAqB,CAAC,CAAS;IACvC,OAAO,CAAC,YAAY,CAAC,CAAS;IAC9B,OAAO,CAAC,aAAa,CAAC,CAAS;IAC/B,OAAO,CAAC,UAAU,CAAC,CAAc;IACjC,OAAO,CAAC,WAAW,CAAC,CAAiB;IACrC,OAAO,CAAC,iBAAiB,CAAC,CAAiB;IAC3C,OAAO,CAAC,oBAAoB,CAAS;IAErC,OAAO,CAAC,cAAc,CAAgB;IACtC,OAAO,CAAC,mBAAmB,CAAC,CAAyC;gBAGlD,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,UAAU,EAClB,UAAU,EAAE,aAAa,EACzB,MAAM,EAAE,MAAM,EAC/B,WAAW,CAAC,EAAE,MAAM;IAiBtB,OAAO,CAAC,yBAAyB;IAiCjC,OAAO,CAAC,iBAAiB;IAoBzB,OAAO,CAAC,gBAAgB;IAiBxB,OAAO,CAAC,gBAAgB;IA0CxB,OAAO,CAAC,UAAU;IAkDlB,OAAO,CAAC,iBAAiB;IA8BzB,OAAO,CAAC,oBAAoB;IAkCf,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC;IAgD1C,OAAO,CAAC,mBAAmB;YAKb,eAAe;YAKf,gBAAgB;YAsChB,eAAe;IA+B7B,OAAO,CAAC,YAAY;IAYpB,OAAO,CAAC,YAAY;IAkBpB,OAAO,CAAC,yBAAyB;IAmIjC,OAAO,CAAC,mBAAmB;IAkH3B,OAAO,CAAC,cAAc;IAcxB,OAAO,CAAC,WAAW;
|
|
1
|
+
{"version":3,"file":"auth-manager.d.ts","sourceRoot":"","sources":["../src/auth-manager.ts"],"names":[],"mappings":"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"}
|
package/dist/auth-manager.js
CHANGED
|
@@ -675,31 +675,27 @@ class AuthManager {
|
|
|
675
675
|
}
|
|
676
676
|
}
|
|
677
677
|
openBrowser(url) {
|
|
678
|
-
//
|
|
679
|
-
//
|
|
678
|
+
// Always log the URL first (issue #3): in Docker/Umbrel/other containers the
|
|
679
|
+
// process runs as root without systemd variables, xdg-open does not exist and the
|
|
680
|
+
// URL was never shown, making the OAuth flow unrecoverable.
|
|
681
|
+
this.log.info('='.repeat(80));
|
|
682
|
+
this.log.info('🔐 AUTHENTICATION REQUIRED');
|
|
683
|
+
this.log.info('='.repeat(80));
|
|
684
|
+
this.log.info('');
|
|
685
|
+
this.log.info('📱 Open this URL from ANY device on your network:');
|
|
686
|
+
this.log.info('');
|
|
687
|
+
this.log.info(` ${url}`);
|
|
688
|
+
this.log.info('');
|
|
689
|
+
this.log.info(`🌐 Or open the status page: http://${this.hostIp}:${this.config.redirectPort || 4200}`);
|
|
690
|
+
this.log.info('⏳ Waiting for authentication...');
|
|
691
|
+
this.log.info('='.repeat(80));
|
|
692
|
+
// Try to open a local browser only on interactive desktop installs
|
|
680
693
|
const isSystemdService = !!(process.env.SYSTEMD_EXEC_PID || process.env.INVOCATION_ID);
|
|
681
694
|
const isHomebridge = process.env.USER === 'homebridge';
|
|
682
|
-
|
|
683
|
-
if (isSystemdService || isHomebridge) {
|
|
684
|
-
this.log.info('='.repeat(80));
|
|
685
|
-
this.log.info('🔐 AUTHENTICATION REQUIRED');
|
|
686
|
-
this.log.info('='.repeat(80));
|
|
687
|
-
this.log.info('');
|
|
688
|
-
this.log.info('📱 Open this URL from ANY device on your network:');
|
|
689
|
-
this.log.info('');
|
|
690
|
-
this.log.info(` ${url}`);
|
|
691
|
-
this.log.info('');
|
|
692
|
-
this.log.info('✅ You can open it from:');
|
|
693
|
-
this.log.info(' • Your computer/laptop');
|
|
694
|
-
this.log.info(' • Your smartphone/tablet');
|
|
695
|
-
this.log.info(' • This Raspberry Pi (if you have a browser)');
|
|
696
|
-
this.log.info('');
|
|
697
|
-
this.log.info(`🌐 Auth server is listening on: ${this.hostIp}:${this.config.redirectPort || 4200}`);
|
|
698
|
-
this.log.info('⏳ Waiting for authentication...');
|
|
699
|
-
this.log.info('='.repeat(80));
|
|
695
|
+
const isContainer = require('fs').existsSync('/.dockerenv') || !!process.env.container;
|
|
696
|
+
if (isSystemdService || isHomebridge || isContainer) {
|
|
700
697
|
return;
|
|
701
698
|
}
|
|
702
|
-
// Solo per installazioni non-systemd (es. macOS, sviluppo locale)
|
|
703
699
|
this.tryOpenBrowserDirect(url);
|
|
704
700
|
}
|
|
705
701
|
tryOpenBrowserDirect(url) {
|
|
@@ -717,7 +713,7 @@ class AuthManager {
|
|
|
717
713
|
}
|
|
718
714
|
exec(command, (error) => {
|
|
719
715
|
if (error) {
|
|
720
|
-
this.log.info(
|
|
716
|
+
this.log.info(`📱 Could not open a browser automatically — open the URL above manually: ${url}`);
|
|
721
717
|
}
|
|
722
718
|
else {
|
|
723
719
|
this.log.info('🌐 Opening browser...');
|
package/dist/platform.d.ts
CHANGED
|
@@ -61,7 +61,15 @@ export declare class ViessmannPlatform implements DynamicPlatformPlugin {
|
|
|
61
61
|
setupHeatingCircuitAccessories(installation: ViessmannInstallation, gateway: ViessmannGateway, device: ViessmannDevice, features: ViessmannFeature[]): Promise<void>;
|
|
62
62
|
setupEnergyAccessory(installation: ViessmannInstallation, gateway: ViessmannGateway, device: ViessmannDevice, features: ViessmannFeature[]): Promise<void>;
|
|
63
63
|
private setupRoomSensorDiscovery;
|
|
64
|
+
private setupRoomAccessories;
|
|
64
65
|
updateAllDevices(): Promise<void>;
|
|
66
|
+
/**
|
|
67
|
+
* Writes device.messages.* entries (status S.xx, info I.xx, service, errors F.xx) to
|
|
68
|
+
* viessmann-messages-<installationId>-<deviceId>.json, used by viessmann-report.js.
|
|
69
|
+
* The API only returns the CURRENT messages, so entries are merged with the existing
|
|
70
|
+
* file (deduplicated by code+timestamp, newest first, max 200) to keep a history.
|
|
71
|
+
*/
|
|
72
|
+
private writeDeviceMessages;
|
|
65
73
|
private updateCycleComplete;
|
|
66
74
|
private writeApiStatusFile;
|
|
67
75
|
private adjustRefreshInterval;
|
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;
|
|
1
|
+
{"version":3,"file":"platform.d.ts","sourceRoot":"","sources":["../src/platform.ts"],"names":[],"mappings":"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"}
|