homebridge-verano 2.1.4 → 3.0.1
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/.github/workflows/build-and-publish.yml +36 -0
- package/README.md +61 -3
- package/config.schema.json +48 -0
- package/dist/index.d.ts +19 -9
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +224 -156
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/index.ts +309 -181
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
name: Build and Publish
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches:
|
|
6
|
+
- main
|
|
7
|
+
- master
|
|
8
|
+
tags:
|
|
9
|
+
- 'v*'
|
|
10
|
+
pull_request:
|
|
11
|
+
branches:
|
|
12
|
+
- main
|
|
13
|
+
- master
|
|
14
|
+
|
|
15
|
+
jobs:
|
|
16
|
+
publish:
|
|
17
|
+
runs-on: ubuntu-latest
|
|
18
|
+
steps:
|
|
19
|
+
- uses: actions/checkout@v6
|
|
20
|
+
|
|
21
|
+
- name: Use Node.js
|
|
22
|
+
uses: actions/setup-node@v6
|
|
23
|
+
with:
|
|
24
|
+
node-version: '25.x'
|
|
25
|
+
registry-url: 'https://registry.npmjs.org'
|
|
26
|
+
|
|
27
|
+
- name: Install dependencies
|
|
28
|
+
run: npm ci
|
|
29
|
+
|
|
30
|
+
- name: Build
|
|
31
|
+
run: npm run build
|
|
32
|
+
|
|
33
|
+
- name: Publish to npm
|
|
34
|
+
run: npm publish
|
|
35
|
+
env:
|
|
36
|
+
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
package/README.md
CHANGED
|
@@ -1,6 +1,64 @@
|
|
|
1
|
+
# Homebridge Verano WiFi
|
|
1
2
|
|
|
2
|
-
|
|
3
|
+
Homebridge accessory plugin to control Verano VER-24 WiFi heaters via emodul.pl.
|
|
3
4
|
|
|
4
|
-
|
|
5
|
+
Features:
|
|
6
|
+
- Thermostat service (heat/off)
|
|
7
|
+
- Current and target temperature
|
|
8
|
+
- Periodic polling to keep HomeKit in sync
|
|
9
|
+
- Robust login with cookie reuse and auto-reauth
|
|
5
10
|
|
|
6
|
-
|
|
11
|
+
## Install
|
|
12
|
+
|
|
13
|
+
```sh
|
|
14
|
+
npm install -g homebridge-verano
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Configuration
|
|
18
|
+
|
|
19
|
+
Add an accessory block in your Homebridge config UI or JSON. Required fields: `username`, `password`.
|
|
20
|
+
|
|
21
|
+
Optional tuning parameters are shown with defaults.
|
|
22
|
+
|
|
23
|
+
```jsonc
|
|
24
|
+
{
|
|
25
|
+
"accessories": [
|
|
26
|
+
{
|
|
27
|
+
"accessory": "VeranoAccessoryPlugin",
|
|
28
|
+
"name": "Verano",
|
|
29
|
+
"username": "you@example.com",
|
|
30
|
+
"password": "••••••••",
|
|
31
|
+
// optional
|
|
32
|
+
"tileId": 58,
|
|
33
|
+
"setpointIdo": 139,
|
|
34
|
+
"temperatureDivider": 10,
|
|
35
|
+
"offThresholdC": 10,
|
|
36
|
+
"minC": 10,
|
|
37
|
+
"maxC": 30,
|
|
38
|
+
"stepC": 0.5,
|
|
39
|
+
"pollIntervalSec": 30
|
|
40
|
+
}
|
|
41
|
+
]
|
|
42
|
+
}
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Notes:
|
|
46
|
+
- Only HEAT/OFF modes are supported.
|
|
47
|
+
- Temperatures are in Celsius.
|
|
48
|
+
|
|
49
|
+
## Development
|
|
50
|
+
|
|
51
|
+
```sh
|
|
52
|
+
npm ci
|
|
53
|
+
npm run build
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Link locally into a dev Homebridge if desired:
|
|
57
|
+
|
|
58
|
+
```sh
|
|
59
|
+
npm link
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## License
|
|
63
|
+
|
|
64
|
+
Apache-2.0
|
package/config.schema.json
CHANGED
|
@@ -22,6 +22,54 @@
|
|
|
22
22
|
"type": "string",
|
|
23
23
|
"required": true,
|
|
24
24
|
"default": "Verano"
|
|
25
|
+
},
|
|
26
|
+
"tileId": {
|
|
27
|
+
"title": "Tile ID (target/current tile)",
|
|
28
|
+
"type": "number",
|
|
29
|
+
"required": false,
|
|
30
|
+
"default": 58
|
|
31
|
+
},
|
|
32
|
+
"setpointIdo": {
|
|
33
|
+
"title": "Setpoint IDO (control)",
|
|
34
|
+
"type": "number",
|
|
35
|
+
"required": false,
|
|
36
|
+
"default": 139
|
|
37
|
+
},
|
|
38
|
+
"temperatureDivider": {
|
|
39
|
+
"title": "Temperature Divider",
|
|
40
|
+
"type": "number",
|
|
41
|
+
"required": false,
|
|
42
|
+
"default": 10
|
|
43
|
+
},
|
|
44
|
+
"offThresholdC": {
|
|
45
|
+
"title": "Off Threshold (°C)",
|
|
46
|
+
"type": "number",
|
|
47
|
+
"required": false,
|
|
48
|
+
"default": 10
|
|
49
|
+
},
|
|
50
|
+
"minC": {
|
|
51
|
+
"title": "Minimum Temperature (°C)",
|
|
52
|
+
"type": "number",
|
|
53
|
+
"required": false,
|
|
54
|
+
"default": 10
|
|
55
|
+
},
|
|
56
|
+
"maxC": {
|
|
57
|
+
"title": "Maximum Temperature (°C)",
|
|
58
|
+
"type": "number",
|
|
59
|
+
"required": false,
|
|
60
|
+
"default": 30
|
|
61
|
+
},
|
|
62
|
+
"stepC": {
|
|
63
|
+
"title": "Temperature Step (°C)",
|
|
64
|
+
"type": "number",
|
|
65
|
+
"required": false,
|
|
66
|
+
"default": 0.5
|
|
67
|
+
},
|
|
68
|
+
"pollIntervalSec": {
|
|
69
|
+
"title": "Polling Interval (seconds)",
|
|
70
|
+
"type": "number",
|
|
71
|
+
"required": false,
|
|
72
|
+
"default": 30
|
|
25
73
|
}
|
|
26
74
|
}
|
|
27
75
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -3,22 +3,32 @@ export declare class VeranoAccessoryPlugin implements AccessoryPlugin {
|
|
|
3
3
|
private readonly log;
|
|
4
4
|
private readonly config;
|
|
5
5
|
private readonly api;
|
|
6
|
-
private readonly TEMPERATURE_DIVIDER;
|
|
7
|
-
private readonly TURN_ON_OFF_TEMPERATURE;
|
|
8
|
-
private readonly informationService;
|
|
9
|
-
private readonly name;
|
|
10
6
|
private readonly service;
|
|
11
|
-
private
|
|
12
|
-
private
|
|
7
|
+
private readonly informationService;
|
|
8
|
+
private readonly Characteristic;
|
|
9
|
+
private readonly cfg;
|
|
10
|
+
private axios;
|
|
13
11
|
private sessionCookie;
|
|
14
|
-
private
|
|
12
|
+
private isAuthorized;
|
|
13
|
+
private pollTimer?;
|
|
14
|
+
private writeInFlight;
|
|
15
|
+
private thermostatState;
|
|
16
|
+
private readonly credentials;
|
|
15
17
|
constructor(log: Logging, config: AccessoryConfig, api: API);
|
|
16
18
|
identify?(): void;
|
|
17
19
|
getServices(): Service[];
|
|
18
20
|
getControllers?(): Controller[];
|
|
19
|
-
private
|
|
20
|
-
private
|
|
21
|
+
private executeWithReauth;
|
|
22
|
+
private startPolling;
|
|
23
|
+
private stopPolling;
|
|
24
|
+
private clampAndStep;
|
|
25
|
+
private ensureSession;
|
|
21
26
|
private requestAuthorization;
|
|
27
|
+
private fetchDataTiles;
|
|
22
28
|
private requestTemperatureChange;
|
|
29
|
+
private requestThermostatState;
|
|
30
|
+
private refreshStateSafe;
|
|
31
|
+
private pushStateToHomeKit;
|
|
32
|
+
private setTargetTemperatureC;
|
|
23
33
|
}
|
|
24
34
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,GAAG,EAAE,UAAU,EAAE,OAAO,EAAE,OAAO,EAAkB,MAAM,YAAY,CAAC;AA4CjH,qBAAa,qBAAsB,YAAW,eAAe;IA+BvD,OAAO,CAAC,QAAQ,CAAC,GAAG;IACpB,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,GAAG;IAhCtB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAU;IAClC,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAU;IAC7C,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAwB;IAEvD,OAAO,CAAC,QAAQ,CAAC,GAAG,CAUjB;IAEH,OAAO,CAAC,KAAK,CAAgB;IAC7B,OAAO,CAAC,aAAa,CAAc;IACnC,OAAO,CAAC,YAAY,CAAS;IAC7B,OAAO,CAAC,SAAS,CAAC,CAAiB;IACnC,OAAO,CAAC,aAAa,CAAS;IAC9B,OAAO,CAAC,eAAe,CAIrB;IAEF,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAyC;gBAGlD,GAAG,EAAE,OAAO,EACZ,MAAM,EAAE,eAAe,EACvB,GAAG,EAAE,GAAG;IA+H3B,QAAQ,CAAC,IAAI,IAAI;IAIjB,WAAW,IAAI,OAAO,EAAE;IAIxB,cAAc,CAAC,IAAI,UAAU,EAAE;YAMjB,iBAAiB;IAc/B,OAAO,CAAC,YAAY;IAOpB,OAAO,CAAC,WAAW;IAOnB,OAAO,CAAC,YAAY;YAKN,aAAa;YAKb,oBAAoB;YA+BpB,cAAc;YAUd,wBAAwB;YAkBxB,sBAAsB;YAiBtB,gBAAgB;IAS9B,OAAO,CAAC,kBAAkB;YAaZ,qBAAqB;CAkBtC"}
|
package/dist/index.js
CHANGED
|
@@ -10,208 +10,276 @@ module.exports = (api) => {
|
|
|
10
10
|
};
|
|
11
11
|
class VeranoAccessoryPlugin {
|
|
12
12
|
constructor(log, config, api) {
|
|
13
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
13
14
|
this.log = log;
|
|
14
15
|
this.config = config;
|
|
15
16
|
this.api = api;
|
|
16
|
-
this.
|
|
17
|
-
this.
|
|
17
|
+
this.sessionCookie = '';
|
|
18
|
+
this.isAuthorized = false;
|
|
19
|
+
this.writeInFlight = false;
|
|
18
20
|
this.thermostatState = {
|
|
19
21
|
currentTemperature: 0,
|
|
20
22
|
targetTemperature: 0,
|
|
21
|
-
heating: false
|
|
23
|
+
heating: false,
|
|
22
24
|
};
|
|
23
|
-
this.log = log;
|
|
24
|
-
this.log.info('Initializing VeranoAccessoryPlugin');
|
|
25
|
-
this.config = config;
|
|
26
|
-
this.name = config.name;
|
|
27
|
-
this.api = api;
|
|
28
|
-
this.isAuthorized = false;
|
|
29
|
-
this.sessionCookie = '';
|
|
30
|
-
this.requestAuthorization();
|
|
31
25
|
this.Characteristic = this.api.hap.Characteristic;
|
|
26
|
+
// Extract credentials and validate presence
|
|
27
|
+
const raw = this.config;
|
|
28
|
+
if (!raw.username || !raw.password) {
|
|
29
|
+
throw new Error('Missing required config: username and/or password');
|
|
30
|
+
}
|
|
31
|
+
this.credentials = { username: raw.username, password: raw.password };
|
|
32
|
+
// Normalize config
|
|
33
|
+
this.cfg = {
|
|
34
|
+
tileId: (_a = raw.tileId) !== null && _a !== void 0 ? _a : 58,
|
|
35
|
+
setpointIdo: (_b = raw.setpointIdo) !== null && _b !== void 0 ? _b : 139,
|
|
36
|
+
temperatureDivider: (_c = raw.temperatureDivider) !== null && _c !== void 0 ? _c : 10,
|
|
37
|
+
offThresholdC: (_d = raw.offThresholdC) !== null && _d !== void 0 ? _d : 10,
|
|
38
|
+
minC: (_e = raw.minC) !== null && _e !== void 0 ? _e : 10,
|
|
39
|
+
maxC: (_f = raw.maxC) !== null && _f !== void 0 ? _f : 30,
|
|
40
|
+
stepC: (_g = raw.stepC) !== null && _g !== void 0 ? _g : 0.5,
|
|
41
|
+
pollIntervalSec: (_h = raw.pollIntervalSec) !== null && _h !== void 0 ? _h : 30,
|
|
42
|
+
};
|
|
43
|
+
// Axios instance with base URL and timeout
|
|
44
|
+
this.axios = axios_1.default.create({
|
|
45
|
+
baseURL: 'https://emodul.pl',
|
|
46
|
+
timeout: 8000,
|
|
47
|
+
withCredentials: true,
|
|
48
|
+
validateStatus: (s) => s >= 200 && s < 500, // handle 401/403 gracefully
|
|
49
|
+
});
|
|
50
|
+
// Attach cookie if available
|
|
51
|
+
this.axios.interceptors.request.use((req) => {
|
|
52
|
+
var _a;
|
|
53
|
+
if (this.sessionCookie) {
|
|
54
|
+
req.headers = (_a = req.headers) !== null && _a !== void 0 ? _a : {};
|
|
55
|
+
req.headers['Cookie'] = this.sessionCookie;
|
|
56
|
+
}
|
|
57
|
+
return req;
|
|
58
|
+
});
|
|
59
|
+
// Basic 401 reauth and retry once
|
|
60
|
+
this.axios.interceptors.response.use((res) => res, (err) => Promise.reject(err));
|
|
32
61
|
this.informationService = new this.api.hap.Service.AccessoryInformation()
|
|
33
|
-
.setCharacteristic(this.
|
|
34
|
-
.setCharacteristic(this.
|
|
35
|
-
this.service = new this.api.hap.Service.Thermostat(this.name);
|
|
36
|
-
//
|
|
62
|
+
.setCharacteristic(this.Characteristic.Manufacturer, 'Verano')
|
|
63
|
+
.setCharacteristic(this.Characteristic.Model, 'VER-24 WiFi');
|
|
64
|
+
this.service = new this.api.hap.Service.Thermostat(this.config.name);
|
|
65
|
+
// Current Heating/Cooling State
|
|
37
66
|
this.service.getCharacteristic(this.Characteristic.CurrentHeatingCoolingState)
|
|
38
|
-
.
|
|
39
|
-
this.
|
|
40
|
-
|
|
41
|
-
|
|
67
|
+
.onGet(() => {
|
|
68
|
+
return this.thermostatState.heating
|
|
69
|
+
? this.Characteristic.CurrentHeatingCoolingState.HEAT
|
|
70
|
+
: this.Characteristic.CurrentHeatingCoolingState.OFF;
|
|
42
71
|
});
|
|
43
|
-
//
|
|
72
|
+
// Target Heating/Cooling State (HEAT/OFF only)
|
|
44
73
|
this.service.getCharacteristic(this.Characteristic.TargetHeatingCoolingState)
|
|
45
|
-
.setProps({
|
|
46
|
-
validValues: [
|
|
74
|
+
.setProps({ validValues: [
|
|
47
75
|
this.Characteristic.TargetHeatingCoolingState.OFF,
|
|
48
|
-
this.Characteristic.TargetHeatingCoolingState.HEAT
|
|
49
|
-
]
|
|
76
|
+
this.Characteristic.TargetHeatingCoolingState.HEAT,
|
|
77
|
+
] })
|
|
78
|
+
.onGet(() => {
|
|
79
|
+
return this.thermostatState.heating
|
|
80
|
+
? this.Characteristic.TargetHeatingCoolingState.HEAT
|
|
81
|
+
: this.Characteristic.TargetHeatingCoolingState.OFF;
|
|
50
82
|
})
|
|
51
|
-
.
|
|
52
|
-
this.
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
callback(null, heatingState);
|
|
57
|
-
})
|
|
58
|
-
.catch(error => callback(error));
|
|
59
|
-
})
|
|
60
|
-
.on('set', (value, callback) => {
|
|
61
|
-
this.thermostatState.heating = value === this.Characteristic.TargetHeatingCoolingState.HEAT;
|
|
62
|
-
if (this.thermostatState.heating) {
|
|
63
|
-
callback(null);
|
|
64
|
-
return;
|
|
83
|
+
.onSet(async (value) => {
|
|
84
|
+
const heat = value === this.Characteristic.TargetHeatingCoolingState.HEAT;
|
|
85
|
+
// Turning off maps to sending the off threshold setpoint if device requires it
|
|
86
|
+
if (!heat) {
|
|
87
|
+
await this.setTargetTemperatureC(this.cfg.offThresholdC);
|
|
65
88
|
}
|
|
66
|
-
this.
|
|
67
|
-
this.
|
|
68
|
-
.then(() => {
|
|
69
|
-
this.log.info('Heating turned off');
|
|
70
|
-
callback(null);
|
|
71
|
-
})
|
|
72
|
-
.catch(error => callback(error));
|
|
89
|
+
this.thermostatState.heating = heat;
|
|
90
|
+
this.pushStateToHomeKit();
|
|
73
91
|
});
|
|
74
|
-
//
|
|
92
|
+
// Current Temperature
|
|
75
93
|
this.service.getCharacteristic(this.Characteristic.CurrentTemperature)
|
|
76
|
-
.
|
|
77
|
-
|
|
78
|
-
this.
|
|
79
|
-
.then(thermostatState => callback(null, thermostatState.currentTemperature))
|
|
80
|
-
.catch(error => callback(error));
|
|
94
|
+
.setProps({ minStep: 0.1 })
|
|
95
|
+
.onGet(() => {
|
|
96
|
+
return this.thermostatState.currentTemperature;
|
|
81
97
|
});
|
|
82
|
-
//
|
|
98
|
+
// Target Temperature
|
|
83
99
|
this.service.getCharacteristic(this.Characteristic.TargetTemperature)
|
|
84
|
-
.
|
|
85
|
-
this.
|
|
86
|
-
this.
|
|
87
|
-
|
|
88
|
-
.catch(error => callback(error));
|
|
100
|
+
.setProps({
|
|
101
|
+
minValue: this.cfg.minC,
|
|
102
|
+
maxValue: this.cfg.maxC,
|
|
103
|
+
minStep: this.cfg.stepC,
|
|
89
104
|
})
|
|
90
|
-
.
|
|
91
|
-
this.
|
|
92
|
-
this.requestTemperatureChange(value)
|
|
93
|
-
.then(() => callback(null))
|
|
94
|
-
.catch(error => callback(error));
|
|
105
|
+
.onGet(() => {
|
|
106
|
+
return this.thermostatState.targetTemperature;
|
|
95
107
|
})
|
|
96
|
-
.
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
108
|
+
.onSet(async (value) => {
|
|
109
|
+
const v = this.clampAndStep(Number(value), this.cfg.minC, this.cfg.maxC, this.cfg.stepC);
|
|
110
|
+
await this.setTargetTemperatureC(v);
|
|
111
|
+
this.thermostatState.heating = v > this.cfg.offThresholdC;
|
|
112
|
+
this.pushStateToHomeKit();
|
|
100
113
|
});
|
|
114
|
+
// Celsius only
|
|
101
115
|
this.service.getCharacteristic(this.Characteristic.TemperatureDisplayUnits)
|
|
102
|
-
.
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
116
|
+
.onGet(() => this.Characteristic.TemperatureDisplayUnits.CELSIUS)
|
|
117
|
+
.onSet(() => { });
|
|
118
|
+
// Defer network until platform is ready
|
|
119
|
+
this.api.on('didFinishLaunching', async () => {
|
|
120
|
+
var _a;
|
|
121
|
+
try {
|
|
122
|
+
await this.ensureSession();
|
|
123
|
+
await this.refreshStateSafe();
|
|
124
|
+
this.startPolling();
|
|
125
|
+
}
|
|
126
|
+
catch (e) {
|
|
127
|
+
this.log.error('Startup failed:', (_a = e === null || e === void 0 ? void 0 : e.message) !== null && _a !== void 0 ? _a : e);
|
|
128
|
+
}
|
|
129
|
+
});
|
|
130
|
+
// Cleanup on shutdown
|
|
131
|
+
this.api.on('shutdown', () => {
|
|
132
|
+
this.stopPolling();
|
|
106
133
|
});
|
|
107
134
|
}
|
|
108
135
|
identify() {
|
|
109
136
|
this.log('Identify!');
|
|
110
137
|
}
|
|
111
138
|
getServices() {
|
|
112
|
-
return [
|
|
113
|
-
this.informationService,
|
|
114
|
-
this.service,
|
|
115
|
-
];
|
|
139
|
+
return [this.informationService, this.service];
|
|
116
140
|
}
|
|
117
141
|
getControllers() {
|
|
118
142
|
return [];
|
|
119
143
|
}
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
this.thermostatState = {
|
|
126
|
-
currentTemperature: currentTemperature,
|
|
127
|
-
targetTemperature: targetTemperature,
|
|
128
|
-
heating: targetTemperature > this.TURN_ON_OFF_TEMPERATURE
|
|
129
|
-
};
|
|
130
|
-
this.log.info('Thermostat state:', this.thermostatState);
|
|
131
|
-
return this.thermostatState;
|
|
132
|
-
}
|
|
133
|
-
async fetchDataTiles() {
|
|
134
|
-
this.log.debug('Fetching data');
|
|
135
|
-
if (!this.isAuthorized) {
|
|
136
|
-
this.log.error('Not authorized, cannot get tiles, trying to authorize');
|
|
137
|
-
await this.requestAuthorization();
|
|
144
|
+
// --- Helpers ---
|
|
145
|
+
async executeWithReauth(operation) {
|
|
146
|
+
var _a, _b;
|
|
147
|
+
try {
|
|
148
|
+
return await operation();
|
|
138
149
|
}
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
return axios_1.default
|
|
146
|
-
.get('https://emodul.pl/frontend/module_data', config)
|
|
147
|
-
.then(response => {
|
|
148
|
-
this.log.debug('Successfully fetched data');
|
|
149
|
-
return response.data.tiles;
|
|
150
|
-
})
|
|
151
|
-
.catch(error => {
|
|
152
|
-
if (error.response) {
|
|
153
|
-
this.log.error("Error during tiles fetch");
|
|
154
|
-
this.log.error("Status:", error.response.status);
|
|
155
|
-
this.log.error("Response:");
|
|
156
|
-
this.log.error(error.response.data);
|
|
157
|
-
}
|
|
158
|
-
else {
|
|
159
|
-
this.log.error("Error during tiles fetch", error);
|
|
150
|
+
catch (err) {
|
|
151
|
+
const error = err;
|
|
152
|
+
if (((_a = error === null || error === void 0 ? void 0 : error.response) === null || _a === void 0 ? void 0 : _a.status) === 401 || ((_b = error === null || error === void 0 ? void 0 : error.response) === null || _b === void 0 ? void 0 : _b.status) === 403) {
|
|
153
|
+
this.isAuthorized = false;
|
|
154
|
+
await this.requestAuthorization();
|
|
155
|
+
return operation();
|
|
160
156
|
}
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
157
|
+
throw err;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
startPolling() {
|
|
161
|
+
if (this.pollTimer)
|
|
162
|
+
clearInterval(this.pollTimer);
|
|
163
|
+
this.pollTimer = setInterval(async () => {
|
|
164
|
+
await this.refreshStateSafe();
|
|
165
|
+
}, this.cfg.pollIntervalSec * 1000);
|
|
166
|
+
}
|
|
167
|
+
stopPolling() {
|
|
168
|
+
if (this.pollTimer) {
|
|
169
|
+
clearInterval(this.pollTimer);
|
|
170
|
+
this.pollTimer = undefined;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
clampAndStep(v, min, max, step) {
|
|
174
|
+
const clamped = Math.min(Math.max(v, min), max);
|
|
175
|
+
return Math.round(clamped / step) * step;
|
|
176
|
+
}
|
|
177
|
+
async ensureSession() {
|
|
178
|
+
if (this.isAuthorized && this.sessionCookie)
|
|
179
|
+
return;
|
|
180
|
+
await this.requestAuthorization();
|
|
164
181
|
}
|
|
165
|
-
;
|
|
166
182
|
async requestAuthorization() {
|
|
167
|
-
const
|
|
168
|
-
username: this.
|
|
169
|
-
password: this.
|
|
183
|
+
const body = {
|
|
184
|
+
username: this.credentials.username,
|
|
185
|
+
password: this.credentials.password,
|
|
170
186
|
rememberMe: true,
|
|
171
|
-
languageId: 'en'
|
|
187
|
+
languageId: 'en',
|
|
172
188
|
};
|
|
173
|
-
this.log.info('
|
|
174
|
-
|
|
175
|
-
.
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
this.sessionCookie = cookies.filter(cookie => cookie.includes('session'))[0];
|
|
180
|
-
this.isAuthorized = true;
|
|
181
|
-
return loginResponse;
|
|
182
|
-
})
|
|
183
|
-
.catch(error => {
|
|
184
|
-
this.log.error("Error during authorization", requestBody.username, error);
|
|
189
|
+
this.log.info('Authorizing user', this.credentials.username);
|
|
190
|
+
const res = await this.axios.post('/login', body).catch((e) => {
|
|
191
|
+
this.isAuthorized = false;
|
|
192
|
+
throw e;
|
|
193
|
+
});
|
|
194
|
+
if (res.status !== 200) {
|
|
185
195
|
this.isAuthorized = false;
|
|
186
|
-
throw
|
|
196
|
+
throw new Error(`Authorization failed: HTTP ${res.status}`);
|
|
197
|
+
}
|
|
198
|
+
const setCookie = res.headers['set-cookie'];
|
|
199
|
+
const session = setCookie === null || setCookie === void 0 ? void 0 : setCookie.find((c) => /session/i.test(c));
|
|
200
|
+
if (!session) {
|
|
201
|
+
this.isAuthorized = false;
|
|
202
|
+
throw new Error('Authorization failed: missing session cookie');
|
|
203
|
+
}
|
|
204
|
+
// Keep only the cookie key=value part
|
|
205
|
+
this.sessionCookie = session.split(';')[0];
|
|
206
|
+
this.isAuthorized = true;
|
|
207
|
+
this.log.info('Authorized as', this.credentials.username);
|
|
208
|
+
}
|
|
209
|
+
async fetchDataTiles() {
|
|
210
|
+
return this.executeWithReauth(async () => {
|
|
211
|
+
const res = await this.axios.get('/frontend/module_data');
|
|
212
|
+
if (res.status !== 200) {
|
|
213
|
+
throw new Error(`Tiles fetch failed: HTTP ${res.status}`);
|
|
214
|
+
}
|
|
215
|
+
return res.data.tiles;
|
|
187
216
|
});
|
|
188
217
|
}
|
|
189
|
-
async requestTemperatureChange(
|
|
190
|
-
|
|
191
|
-
const requestBody = [
|
|
218
|
+
async requestTemperatureChange(targetC) {
|
|
219
|
+
const body = [
|
|
192
220
|
{
|
|
193
|
-
ido:
|
|
194
|
-
params:
|
|
195
|
-
module_index: 0
|
|
196
|
-
}
|
|
197
|
-
];
|
|
198
|
-
const config = {
|
|
199
|
-
headers: {
|
|
200
|
-
'Cookie': this.sessionCookie
|
|
221
|
+
ido: this.cfg.setpointIdo,
|
|
222
|
+
params: Math.round(targetC * this.cfg.temperatureDivider),
|
|
223
|
+
module_index: 0,
|
|
201
224
|
},
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
return
|
|
209
|
-
})
|
|
210
|
-
.catch(error => {
|
|
211
|
-
this.log.error("Error during temperature change", error);
|
|
212
|
-
throw error;
|
|
225
|
+
];
|
|
226
|
+
return this.executeWithReauth(async () => {
|
|
227
|
+
const res = await this.axios.post('/send_control_data', body);
|
|
228
|
+
if (res.status < 200 || res.status >= 300) {
|
|
229
|
+
throw new Error(`Temperature change failed: HTTP ${res.status}`);
|
|
230
|
+
}
|
|
231
|
+
return res.data;
|
|
213
232
|
});
|
|
214
233
|
}
|
|
234
|
+
async requestThermostatState() {
|
|
235
|
+
const tiles = await this.fetchDataTiles();
|
|
236
|
+
const tile = tiles.find((t) => t.id === this.cfg.tileId);
|
|
237
|
+
if (!tile)
|
|
238
|
+
throw new Error(`Tile ${this.cfg.tileId} not found`);
|
|
239
|
+
const targetC = tile.params.widget1.value / this.cfg.temperatureDivider;
|
|
240
|
+
const currentC = tile.params.widget2.value / this.cfg.temperatureDivider;
|
|
241
|
+
this.thermostatState = {
|
|
242
|
+
currentTemperature: currentC,
|
|
243
|
+
targetTemperature: targetC,
|
|
244
|
+
heating: targetC > this.cfg.offThresholdC,
|
|
245
|
+
};
|
|
246
|
+
return this.thermostatState;
|
|
247
|
+
}
|
|
248
|
+
async refreshStateSafe() {
|
|
249
|
+
var _a;
|
|
250
|
+
try {
|
|
251
|
+
const state = await this.requestThermostatState();
|
|
252
|
+
this.pushStateToHomeKit(state);
|
|
253
|
+
}
|
|
254
|
+
catch (e) {
|
|
255
|
+
this.log.debug('Refresh failed:', (_a = e === null || e === void 0 ? void 0 : e.message) !== null && _a !== void 0 ? _a : e);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
pushStateToHomeKit(state = this.thermostatState) {
|
|
259
|
+
this.service.updateCharacteristic(this.Characteristic.CurrentTemperature, state.currentTemperature);
|
|
260
|
+
this.service.updateCharacteristic(this.Characteristic.CurrentHeatingCoolingState, state.heating ? this.Characteristic.CurrentHeatingCoolingState.HEAT : this.Characteristic.CurrentHeatingCoolingState.OFF);
|
|
261
|
+
this.service.updateCharacteristic(this.Characteristic.TargetTemperature, state.targetTemperature);
|
|
262
|
+
this.service.updateCharacteristic(this.Characteristic.TargetHeatingCoolingState, state.heating ? this.Characteristic.TargetHeatingCoolingState.HEAT : this.Characteristic.TargetHeatingCoolingState.OFF);
|
|
263
|
+
}
|
|
264
|
+
async setTargetTemperatureC(targetC) {
|
|
265
|
+
if (this.writeInFlight) {
|
|
266
|
+
// Simple debounce: skip if a write is in progress
|
|
267
|
+
this.log.debug('Skipping set, write in flight');
|
|
268
|
+
return;
|
|
269
|
+
}
|
|
270
|
+
this.writeInFlight = true;
|
|
271
|
+
const bounded = this.clampAndStep(targetC, this.cfg.minC, this.cfg.maxC, this.cfg.stepC);
|
|
272
|
+
this.log.info('Setting target temperature to', `${bounded}°C`);
|
|
273
|
+
try {
|
|
274
|
+
await this.ensureSession();
|
|
275
|
+
await this.requestTemperatureChange(bounded);
|
|
276
|
+
// Refresh from source of truth
|
|
277
|
+
await this.refreshStateSafe();
|
|
278
|
+
}
|
|
279
|
+
finally {
|
|
280
|
+
this.writeInFlight = false;
|
|
281
|
+
}
|
|
282
|
+
}
|
|
215
283
|
}
|
|
216
284
|
exports.VeranoAccessoryPlugin = VeranoAccessoryPlugin;
|
|
217
285
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -1,8 +1,39 @@
|
|
|
1
|
-
import {AccessoryConfig, AccessoryPlugin, API, Controller, Logging, Service} from 'homebridge';
|
|
2
|
-
import axios from
|
|
1
|
+
import { AccessoryConfig, AccessoryPlugin, API, Controller, Logging, Service, Characteristic } from 'homebridge';
|
|
2
|
+
import axios, { AxiosInstance } from 'axios';
|
|
3
3
|
|
|
4
4
|
module.exports = (api: API) => {
|
|
5
5
|
api.registerAccessory('VeranoAccessoryPlugin', VeranoAccessoryPlugin);
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
interface VeranoAccessoryConfig extends AccessoryConfig {
|
|
9
|
+
username: string;
|
|
10
|
+
password: string;
|
|
11
|
+
tileId?: number; // default: 58
|
|
12
|
+
setpointIdo?: number; // default: 139
|
|
13
|
+
temperatureDivider?: number; // default: 10
|
|
14
|
+
offThresholdC?: number; // default: 10
|
|
15
|
+
minC?: number; // default: 10
|
|
16
|
+
maxC?: number; // default: 30
|
|
17
|
+
stepC?: number; // default: 0.5
|
|
18
|
+
pollIntervalSec?: number; // default: 30
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
interface TileWidget {
|
|
22
|
+
value: number;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
interface TileParams {
|
|
26
|
+
widget1: TileWidget; // target
|
|
27
|
+
widget2: TileWidget; // current
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
interface Tile {
|
|
31
|
+
id: number;
|
|
32
|
+
params: TileParams;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
interface ModuleDataResponse {
|
|
36
|
+
tiles: Tile[];
|
|
6
37
|
}
|
|
7
38
|
|
|
8
39
|
interface ThermostatState {
|
|
@@ -12,121 +43,163 @@ interface ThermostatState {
|
|
|
12
43
|
}
|
|
13
44
|
|
|
14
45
|
export class VeranoAccessoryPlugin implements AccessoryPlugin {
|
|
46
|
+
private readonly service: Service;
|
|
47
|
+
private readonly informationService: Service;
|
|
48
|
+
private readonly Characteristic: typeof Characteristic;
|
|
15
49
|
|
|
16
|
-
private readonly
|
|
17
|
-
|
|
50
|
+
private readonly cfg: Required<Pick<
|
|
51
|
+
VeranoAccessoryConfig,
|
|
52
|
+
| 'tileId'
|
|
53
|
+
| 'setpointIdo'
|
|
54
|
+
| 'temperatureDivider'
|
|
55
|
+
| 'offThresholdC'
|
|
56
|
+
| 'minC'
|
|
57
|
+
| 'maxC'
|
|
58
|
+
| 'stepC'
|
|
59
|
+
| 'pollIntervalSec'
|
|
60
|
+
>>;
|
|
18
61
|
|
|
19
|
-
private
|
|
20
|
-
private
|
|
21
|
-
private
|
|
62
|
+
private axios: AxiosInstance;
|
|
63
|
+
private sessionCookie: string = '';
|
|
64
|
+
private isAuthorized = false;
|
|
65
|
+
private pollTimer?: NodeJS.Timeout;
|
|
66
|
+
private writeInFlight = false;
|
|
22
67
|
private thermostatState: ThermostatState = {
|
|
23
68
|
currentTemperature: 0,
|
|
24
69
|
targetTemperature: 0,
|
|
25
|
-
heating: false
|
|
70
|
+
heating: false,
|
|
26
71
|
};
|
|
27
72
|
|
|
28
|
-
private
|
|
29
|
-
private sessionCookie: string;
|
|
30
|
-
private Characteristic: any;
|
|
73
|
+
private readonly credentials: { username: string; password: string };
|
|
31
74
|
|
|
32
75
|
constructor(
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
76
|
+
private readonly log: Logging,
|
|
77
|
+
private readonly config: AccessoryConfig,
|
|
78
|
+
private readonly api: API,
|
|
36
79
|
) {
|
|
37
|
-
this.
|
|
38
|
-
this.log.info('Initializing VeranoAccessoryPlugin');
|
|
39
|
-
this.config = config;
|
|
40
|
-
this.name = config.name;
|
|
41
|
-
this.api = api;
|
|
42
|
-
this.isAuthorized = false;
|
|
43
|
-
this.sessionCookie = '';
|
|
80
|
+
this.Characteristic = this.api.hap.Characteristic;
|
|
44
81
|
|
|
45
|
-
|
|
82
|
+
// Extract credentials and validate presence
|
|
83
|
+
const raw = this.config as Partial<VeranoAccessoryConfig>;
|
|
84
|
+
if (!raw.username || !raw.password) {
|
|
85
|
+
throw new Error('Missing required config: username and/or password');
|
|
86
|
+
}
|
|
87
|
+
this.credentials = { username: raw.username, password: raw.password };
|
|
46
88
|
|
|
47
|
-
|
|
89
|
+
// Normalize config
|
|
90
|
+
this.cfg = {
|
|
91
|
+
tileId: raw.tileId ?? 58,
|
|
92
|
+
setpointIdo: raw.setpointIdo ?? 139,
|
|
93
|
+
temperatureDivider: raw.temperatureDivider ?? 10,
|
|
94
|
+
offThresholdC: raw.offThresholdC ?? 10,
|
|
95
|
+
minC: raw.minC ?? 10,
|
|
96
|
+
maxC: raw.maxC ?? 30,
|
|
97
|
+
stepC: raw.stepC ?? 0.5,
|
|
98
|
+
pollIntervalSec: raw.pollIntervalSec ?? 30,
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
// Axios instance with base URL and timeout
|
|
102
|
+
this.axios = axios.create({
|
|
103
|
+
baseURL: 'https://emodul.pl',
|
|
104
|
+
timeout: 8000,
|
|
105
|
+
withCredentials: true,
|
|
106
|
+
validateStatus: (s) => s >= 200 && s < 500, // handle 401/403 gracefully
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
// Attach cookie if available
|
|
110
|
+
this.axios.interceptors.request.use((req) => {
|
|
111
|
+
if (this.sessionCookie) {
|
|
112
|
+
req.headers = req.headers ?? {};
|
|
113
|
+
(req.headers as any)['Cookie'] = this.sessionCookie;
|
|
114
|
+
}
|
|
115
|
+
return req;
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
// Basic 401 reauth and retry once
|
|
119
|
+
this.axios.interceptors.response.use(
|
|
120
|
+
(res) => res,
|
|
121
|
+
(err) => Promise.reject(err),
|
|
122
|
+
);
|
|
48
123
|
|
|
49
124
|
this.informationService = new this.api.hap.Service.AccessoryInformation()
|
|
50
|
-
|
|
51
|
-
|
|
125
|
+
.setCharacteristic(this.Characteristic.Manufacturer, 'Verano')
|
|
126
|
+
.setCharacteristic(this.Characteristic.Model, 'VER-24 WiFi');
|
|
52
127
|
|
|
53
|
-
this.service = new this.api.hap.Service.Thermostat(this.name);
|
|
128
|
+
this.service = new this.api.hap.Service.Thermostat(this.config.name);
|
|
54
129
|
|
|
55
|
-
//
|
|
130
|
+
// Current Heating/Cooling State
|
|
56
131
|
this.service.getCharacteristic(this.Characteristic.CurrentHeatingCoolingState)
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
132
|
+
.onGet(() => {
|
|
133
|
+
return this.thermostatState.heating
|
|
134
|
+
? this.Characteristic.CurrentHeatingCoolingState.HEAT
|
|
135
|
+
: this.Characteristic.CurrentHeatingCoolingState.OFF;
|
|
136
|
+
});
|
|
62
137
|
|
|
63
|
-
//
|
|
138
|
+
// Target Heating/Cooling State (HEAT/OFF only)
|
|
64
139
|
this.service.getCharacteristic(this.Characteristic.TargetHeatingCoolingState)
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
this.
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
}
|
|
86
|
-
this.log.info('Turning off heating...');
|
|
87
|
-
this.requestTemperatureChange(this.TURN_ON_OFF_TEMPERATURE)
|
|
88
|
-
.then(() => {
|
|
89
|
-
this.log.info('Heating turned off');
|
|
90
|
-
callback(null);
|
|
91
|
-
})
|
|
92
|
-
.catch(error => callback(error));
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
// GET TEMPERATURE
|
|
140
|
+
.setProps({ validValues: [
|
|
141
|
+
this.Characteristic.TargetHeatingCoolingState.OFF,
|
|
142
|
+
this.Characteristic.TargetHeatingCoolingState.HEAT,
|
|
143
|
+
]})
|
|
144
|
+
.onGet(() => {
|
|
145
|
+
return this.thermostatState.heating
|
|
146
|
+
? this.Characteristic.TargetHeatingCoolingState.HEAT
|
|
147
|
+
: this.Characteristic.TargetHeatingCoolingState.OFF;
|
|
148
|
+
})
|
|
149
|
+
.onSet(async (value) => {
|
|
150
|
+
const heat = value === this.Characteristic.TargetHeatingCoolingState.HEAT;
|
|
151
|
+
// Turning off maps to sending the off threshold setpoint if device requires it
|
|
152
|
+
if (!heat) {
|
|
153
|
+
await this.setTargetTemperatureC(this.cfg.offThresholdC);
|
|
154
|
+
}
|
|
155
|
+
this.thermostatState.heating = heat;
|
|
156
|
+
this.pushStateToHomeKit();
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
// Current Temperature
|
|
96
160
|
this.service.getCharacteristic(this.Characteristic.CurrentTemperature)
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
// GET TARGET TEMPERATURE
|
|
161
|
+
.setProps({ minStep: 0.1 })
|
|
162
|
+
.onGet(() => {
|
|
163
|
+
return this.thermostatState.currentTemperature;
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
// Target Temperature
|
|
105
167
|
this.service.getCharacteristic(this.Characteristic.TargetTemperature)
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
maxValue: 30,
|
|
121
|
-
minStep: 0.5
|
|
122
|
-
});
|
|
168
|
+
.setProps({
|
|
169
|
+
minValue: this.cfg.minC,
|
|
170
|
+
maxValue: this.cfg.maxC,
|
|
171
|
+
minStep: this.cfg.stepC,
|
|
172
|
+
})
|
|
173
|
+
.onGet(() => {
|
|
174
|
+
return this.thermostatState.targetTemperature;
|
|
175
|
+
})
|
|
176
|
+
.onSet(async (value) => {
|
|
177
|
+
const v = this.clampAndStep(Number(value), this.cfg.minC, this.cfg.maxC, this.cfg.stepC);
|
|
178
|
+
await this.setTargetTemperatureC(v);
|
|
179
|
+
this.thermostatState.heating = v > this.cfg.offThresholdC;
|
|
180
|
+
this.pushStateToHomeKit();
|
|
181
|
+
});
|
|
123
182
|
|
|
183
|
+
// Celsius only
|
|
124
184
|
this.service.getCharacteristic(this.Characteristic.TemperatureDisplayUnits)
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
185
|
+
.onGet(() => this.Characteristic.TemperatureDisplayUnits.CELSIUS)
|
|
186
|
+
.onSet(() => { /* no-op, force Celsius */ });
|
|
187
|
+
|
|
188
|
+
// Defer network until platform is ready
|
|
189
|
+
this.api.on('didFinishLaunching', async () => {
|
|
190
|
+
try {
|
|
191
|
+
await this.ensureSession();
|
|
192
|
+
await this.refreshStateSafe();
|
|
193
|
+
this.startPolling();
|
|
194
|
+
} catch (e) {
|
|
195
|
+
this.log.error('Startup failed:', (e as Error)?.message ?? e);
|
|
196
|
+
}
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
// Cleanup on shutdown
|
|
200
|
+
this.api.on('shutdown', () => {
|
|
201
|
+
this.stopPolling();
|
|
202
|
+
});
|
|
130
203
|
}
|
|
131
204
|
|
|
132
205
|
identify?(): void {
|
|
@@ -134,112 +207,167 @@ export class VeranoAccessoryPlugin implements AccessoryPlugin {
|
|
|
134
207
|
}
|
|
135
208
|
|
|
136
209
|
getServices(): Service[] {
|
|
137
|
-
return [
|
|
138
|
-
this.informationService,
|
|
139
|
-
this.service,
|
|
140
|
-
];
|
|
210
|
+
return [this.informationService, this.service];
|
|
141
211
|
}
|
|
142
212
|
|
|
143
213
|
getControllers?(): Controller[] {
|
|
144
214
|
return [];
|
|
145
215
|
}
|
|
146
216
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
217
|
+
// --- Helpers ---
|
|
218
|
+
|
|
219
|
+
private async executeWithReauth<T>(operation: () => Promise<T>): Promise<T> {
|
|
220
|
+
try {
|
|
221
|
+
return await operation();
|
|
222
|
+
} catch (err) {
|
|
223
|
+
const error = err as any;
|
|
224
|
+
if (error?.response?.status === 401 || error?.response?.status === 403) {
|
|
225
|
+
this.isAuthorized = false;
|
|
226
|
+
await this.requestAuthorization();
|
|
227
|
+
return operation();
|
|
228
|
+
}
|
|
229
|
+
throw err;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
private startPolling() {
|
|
234
|
+
if (this.pollTimer) clearInterval(this.pollTimer);
|
|
235
|
+
this.pollTimer = setInterval(async () => {
|
|
236
|
+
await this.refreshStateSafe();
|
|
237
|
+
}, this.cfg.pollIntervalSec * 1000);
|
|
159
238
|
}
|
|
160
239
|
|
|
161
|
-
private
|
|
162
|
-
this.
|
|
163
|
-
|
|
164
|
-
this.
|
|
165
|
-
await this.requestAuthorization();
|
|
240
|
+
private stopPolling() {
|
|
241
|
+
if (this.pollTimer) {
|
|
242
|
+
clearInterval(this.pollTimer);
|
|
243
|
+
this.pollTimer = undefined;
|
|
166
244
|
}
|
|
245
|
+
}
|
|
167
246
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
return
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
this.log.debug('Successfully fetched data');
|
|
178
|
-
return response.data.tiles;
|
|
179
|
-
})
|
|
180
|
-
.catch(error => {
|
|
181
|
-
if (error.response) {
|
|
182
|
-
this.log.error("Error during tiles fetch");
|
|
183
|
-
this.log.error("Status:", error.response.status);
|
|
184
|
-
this.log.error("Response:");
|
|
185
|
-
this.log.error(error.response.data);
|
|
186
|
-
} else {
|
|
187
|
-
this.log.error("Error during tiles fetch", error);
|
|
188
|
-
}
|
|
189
|
-
this.isAuthorized = false;
|
|
190
|
-
throw error;
|
|
191
|
-
});
|
|
192
|
-
};
|
|
247
|
+
private clampAndStep(v: number, min: number, max: number, step: number) {
|
|
248
|
+
const clamped = Math.min(Math.max(v, min), max);
|
|
249
|
+
return Math.round(clamped / step) * step;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
private async ensureSession() {
|
|
253
|
+
if (this.isAuthorized && this.sessionCookie) return;
|
|
254
|
+
await this.requestAuthorization();
|
|
255
|
+
}
|
|
193
256
|
|
|
194
257
|
private async requestAuthorization() {
|
|
195
|
-
const
|
|
196
|
-
username: this.
|
|
197
|
-
password: this.
|
|
258
|
+
const body = {
|
|
259
|
+
username: this.credentials.username,
|
|
260
|
+
password: this.credentials.password,
|
|
198
261
|
rememberMe: true,
|
|
199
|
-
languageId: 'en'
|
|
262
|
+
languageId: 'en',
|
|
200
263
|
};
|
|
201
|
-
this.log.info('
|
|
202
|
-
|
|
203
|
-
.
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
264
|
+
this.log.info('Authorizing user', this.credentials.username);
|
|
265
|
+
const res = await this.axios.post('/login', body).catch((e) => {
|
|
266
|
+
this.isAuthorized = false;
|
|
267
|
+
throw e;
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
if (res.status !== 200) {
|
|
271
|
+
this.isAuthorized = false;
|
|
272
|
+
throw new Error(`Authorization failed: HTTP ${res.status}`);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
const setCookie = res.headers['set-cookie'] as string[] | undefined;
|
|
276
|
+
const session = setCookie?.find((c) => /session/i.test(c));
|
|
277
|
+
if (!session) {
|
|
278
|
+
this.isAuthorized = false;
|
|
279
|
+
throw new Error('Authorization failed: missing session cookie');
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
// Keep only the cookie key=value part
|
|
283
|
+
this.sessionCookie = session.split(';')[0];
|
|
284
|
+
this.isAuthorized = true;
|
|
285
|
+
this.log.info('Authorized as', this.credentials.username);
|
|
216
286
|
}
|
|
217
287
|
|
|
218
|
-
private async
|
|
219
|
-
this.
|
|
220
|
-
|
|
221
|
-
{
|
|
222
|
-
|
|
223
|
-
params: targetTemperature * this.TEMPERATURE_DIVIDER,
|
|
224
|
-
module_index: 0
|
|
288
|
+
private async fetchDataTiles(): Promise<Tile[]> {
|
|
289
|
+
return this.executeWithReauth(async () => {
|
|
290
|
+
const res = await this.axios.get<ModuleDataResponse>('/frontend/module_data');
|
|
291
|
+
if (res.status !== 200) {
|
|
292
|
+
throw new Error(`Tiles fetch failed: HTTP ${res.status}`);
|
|
225
293
|
}
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
294
|
+
return res.data.tiles;
|
|
295
|
+
});
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
private async requestTemperatureChange(targetC: number) {
|
|
299
|
+
const body = [
|
|
300
|
+
{
|
|
301
|
+
ido: this.cfg.setpointIdo,
|
|
302
|
+
params: Math.round(targetC * this.cfg.temperatureDivider),
|
|
303
|
+
module_index: 0,
|
|
230
304
|
},
|
|
231
|
-
|
|
305
|
+
];
|
|
306
|
+
|
|
307
|
+
return this.executeWithReauth(async () => {
|
|
308
|
+
const res = await this.axios.post('/send_control_data', body);
|
|
309
|
+
if (res.status < 200 || res.status >= 300) {
|
|
310
|
+
throw new Error(`Temperature change failed: HTTP ${res.status}`);
|
|
311
|
+
}
|
|
312
|
+
return res.data;
|
|
313
|
+
});
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
private async requestThermostatState(): Promise<ThermostatState> {
|
|
317
|
+
const tiles = await this.fetchDataTiles();
|
|
318
|
+
const tile = tiles.find((t) => t.id === this.cfg.tileId);
|
|
319
|
+
if (!tile) throw new Error(`Tile ${this.cfg.tileId} not found`);
|
|
320
|
+
|
|
321
|
+
const targetC = tile.params.widget1.value / this.cfg.temperatureDivider;
|
|
322
|
+
const currentC = tile.params.widget2.value / this.cfg.temperatureDivider;
|
|
323
|
+
|
|
324
|
+
this.thermostatState = {
|
|
325
|
+
currentTemperature: currentC,
|
|
326
|
+
targetTemperature: targetC,
|
|
327
|
+
heating: targetC > this.cfg.offThresholdC,
|
|
232
328
|
};
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
.then(response => {
|
|
236
|
-
this.log.info('Successfully changed temperature to', targetTemperature + '°C');
|
|
237
|
-
return response?.data;
|
|
238
|
-
})
|
|
239
|
-
.catch(error => {
|
|
240
|
-
this.log.error("Error during temperature change", error);
|
|
241
|
-
throw error;
|
|
242
|
-
})
|
|
329
|
+
|
|
330
|
+
return this.thermostatState;
|
|
243
331
|
}
|
|
244
332
|
|
|
333
|
+
private async refreshStateSafe() {
|
|
334
|
+
try {
|
|
335
|
+
const state = await this.requestThermostatState();
|
|
336
|
+
this.pushStateToHomeKit(state);
|
|
337
|
+
} catch (e) {
|
|
338
|
+
this.log.debug('Refresh failed:', (e as Error)?.message ?? e);
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
private pushStateToHomeKit(state: ThermostatState = this.thermostatState) {
|
|
343
|
+
this.service.updateCharacteristic(this.Characteristic.CurrentTemperature, state.currentTemperature);
|
|
344
|
+
this.service.updateCharacteristic(
|
|
345
|
+
this.Characteristic.CurrentHeatingCoolingState,
|
|
346
|
+
state.heating ? this.Characteristic.CurrentHeatingCoolingState.HEAT : this.Characteristic.CurrentHeatingCoolingState.OFF,
|
|
347
|
+
);
|
|
348
|
+
this.service.updateCharacteristic(this.Characteristic.TargetTemperature, state.targetTemperature);
|
|
349
|
+
this.service.updateCharacteristic(
|
|
350
|
+
this.Characteristic.TargetHeatingCoolingState,
|
|
351
|
+
state.heating ? this.Characteristic.TargetHeatingCoolingState.HEAT : this.Characteristic.TargetHeatingCoolingState.OFF,
|
|
352
|
+
);
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
private async setTargetTemperatureC(targetC: number) {
|
|
356
|
+
if (this.writeInFlight) {
|
|
357
|
+
// Simple debounce: skip if a write is in progress
|
|
358
|
+
this.log.debug('Skipping set, write in flight');
|
|
359
|
+
return;
|
|
360
|
+
}
|
|
361
|
+
this.writeInFlight = true;
|
|
362
|
+
const bounded = this.clampAndStep(targetC, this.cfg.minC, this.cfg.maxC, this.cfg.stepC);
|
|
363
|
+
this.log.info('Setting target temperature to', `${bounded}°C`);
|
|
364
|
+
try {
|
|
365
|
+
await this.ensureSession();
|
|
366
|
+
await this.requestTemperatureChange(bounded);
|
|
367
|
+
// Refresh from source of truth
|
|
368
|
+
await this.refreshStateSafe();
|
|
369
|
+
} finally {
|
|
370
|
+
this.writeInFlight = false;
|
|
371
|
+
}
|
|
372
|
+
}
|
|
245
373
|
}
|