homebridge-verano 2.1.3 → 3.0.0
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 +18 -9
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +229 -156
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/index.ts +322 -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,31 @@ 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 startPolling;
|
|
22
|
+
private stopPolling;
|
|
23
|
+
private clampAndStep;
|
|
24
|
+
private ensureSession;
|
|
21
25
|
private requestAuthorization;
|
|
26
|
+
private fetchDataTiles;
|
|
22
27
|
private requestTemperatureChange;
|
|
28
|
+
private requestThermostatState;
|
|
29
|
+
private refreshStateSafe;
|
|
30
|
+
private pushStateToHomeKit;
|
|
31
|
+
private setTargetTemperatureC;
|
|
23
32
|
}
|
|
24
33
|
//# 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;IAqI3B,QAAQ,CAAC,IAAI,IAAI;IAIjB,WAAW,IAAI,OAAO,EAAE;IAIxB,cAAc,CAAC,IAAI,UAAU,EAAE;IAM/B,OAAO,CAAC,YAAY;IAOpB,OAAO,CAAC,WAAW;IAOnB,OAAO,CAAC,YAAY;YAKN,aAAa;YAKb,oBAAoB;YA+BpB,cAAc;YAoBd,wBAAwB;YA4BxB,sBAAsB;YAiBtB,gBAAgB;IAS9B,OAAO,CAAC,kBAAkB;YAaZ,qBAAqB;CAkBtC"}
|
package/dist/index.js
CHANGED
|
@@ -10,207 +10,280 @@ 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('Verano accessory plugin initializing');
|
|
25
|
-
this.config = config;
|
|
26
|
-
this.name = config.name;
|
|
27
|
-
this.api = api;
|
|
28
|
-
this.isAuthorized = false;
|
|
29
|
-
this.sessionCookie = '';
|
|
30
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));
|
|
31
61
|
this.informationService = new this.api.hap.Service.AccessoryInformation()
|
|
32
|
-
.setCharacteristic(this.
|
|
33
|
-
.setCharacteristic(this.
|
|
34
|
-
this.service = new this.api.hap.Service.Thermostat(this.name);
|
|
35
|
-
//
|
|
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
|
|
36
66
|
this.service.getCharacteristic(this.Characteristic.CurrentHeatingCoolingState)
|
|
37
|
-
.
|
|
38
|
-
this.
|
|
39
|
-
|
|
40
|
-
|
|
67
|
+
.onGet(async () => {
|
|
68
|
+
return this.thermostatState.heating
|
|
69
|
+
? this.Characteristic.CurrentHeatingCoolingState.HEAT
|
|
70
|
+
: this.Characteristic.CurrentHeatingCoolingState.OFF;
|
|
41
71
|
});
|
|
42
|
-
//
|
|
72
|
+
// Target Heating/Cooling State (HEAT/OFF only)
|
|
43
73
|
this.service.getCharacteristic(this.Characteristic.TargetHeatingCoolingState)
|
|
44
|
-
.setProps({
|
|
45
|
-
validValues: [
|
|
74
|
+
.setProps({ validValues: [
|
|
46
75
|
this.Characteristic.TargetHeatingCoolingState.OFF,
|
|
47
|
-
this.Characteristic.TargetHeatingCoolingState.HEAT
|
|
48
|
-
]
|
|
49
|
-
|
|
50
|
-
.
|
|
51
|
-
this.
|
|
52
|
-
this.
|
|
53
|
-
.
|
|
54
|
-
|
|
55
|
-
callback(null, heatingState);
|
|
56
|
-
})
|
|
57
|
-
.catch(error => callback(error));
|
|
76
|
+
this.Characteristic.TargetHeatingCoolingState.HEAT,
|
|
77
|
+
] })
|
|
78
|
+
.onGet(async () => {
|
|
79
|
+
await this.ensureSession();
|
|
80
|
+
await this.refreshStateSafe();
|
|
81
|
+
return this.thermostatState.heating
|
|
82
|
+
? this.Characteristic.TargetHeatingCoolingState.HEAT
|
|
83
|
+
: this.Characteristic.TargetHeatingCoolingState.OFF;
|
|
58
84
|
})
|
|
59
|
-
.
|
|
60
|
-
|
|
61
|
-
if
|
|
62
|
-
|
|
63
|
-
|
|
85
|
+
.onSet(async (value) => {
|
|
86
|
+
const heat = value === this.Characteristic.TargetHeatingCoolingState.HEAT;
|
|
87
|
+
// Turning off maps to sending the off threshold setpoint if device requires it
|
|
88
|
+
if (!heat) {
|
|
89
|
+
await this.setTargetTemperatureC(this.cfg.offThresholdC);
|
|
64
90
|
}
|
|
65
|
-
this.
|
|
66
|
-
this.
|
|
67
|
-
.then(() => {
|
|
68
|
-
this.log.info('Heating turned off');
|
|
69
|
-
callback(null);
|
|
70
|
-
})
|
|
71
|
-
.catch(error => callback(error));
|
|
91
|
+
this.thermostatState.heating = heat;
|
|
92
|
+
this.pushStateToHomeKit();
|
|
72
93
|
});
|
|
73
|
-
//
|
|
94
|
+
// Current Temperature
|
|
74
95
|
this.service.getCharacteristic(this.Characteristic.CurrentTemperature)
|
|
75
|
-
.
|
|
76
|
-
|
|
77
|
-
this.
|
|
78
|
-
|
|
79
|
-
|
|
96
|
+
.setProps({ minStep: 0.1 })
|
|
97
|
+
.onGet(async () => {
|
|
98
|
+
await this.ensureSession();
|
|
99
|
+
await this.refreshStateSafe();
|
|
100
|
+
return this.thermostatState.currentTemperature;
|
|
80
101
|
});
|
|
81
|
-
//
|
|
102
|
+
// Target Temperature
|
|
82
103
|
this.service.getCharacteristic(this.Characteristic.TargetTemperature)
|
|
83
|
-
.
|
|
84
|
-
this.
|
|
85
|
-
this.
|
|
86
|
-
|
|
87
|
-
.catch(error => callback(error));
|
|
104
|
+
.setProps({
|
|
105
|
+
minValue: this.cfg.minC,
|
|
106
|
+
maxValue: this.cfg.maxC,
|
|
107
|
+
minStep: this.cfg.stepC,
|
|
88
108
|
})
|
|
89
|
-
.
|
|
90
|
-
this.
|
|
91
|
-
this.
|
|
92
|
-
|
|
93
|
-
.catch(error => callback(error));
|
|
109
|
+
.onGet(async () => {
|
|
110
|
+
await this.ensureSession();
|
|
111
|
+
await this.refreshStateSafe();
|
|
112
|
+
return this.thermostatState.targetTemperature;
|
|
94
113
|
})
|
|
95
|
-
.
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
114
|
+
.onSet(async (value) => {
|
|
115
|
+
const v = this.clampAndStep(Number(value), this.cfg.minC, this.cfg.maxC, this.cfg.stepC);
|
|
116
|
+
await this.setTargetTemperatureC(v);
|
|
117
|
+
this.thermostatState.heating = v > this.cfg.offThresholdC;
|
|
118
|
+
this.pushStateToHomeKit();
|
|
99
119
|
});
|
|
120
|
+
// Celsius only
|
|
100
121
|
this.service.getCharacteristic(this.Characteristic.TemperatureDisplayUnits)
|
|
101
|
-
.
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
122
|
+
.onGet(async () => this.Characteristic.TemperatureDisplayUnits.CELSIUS)
|
|
123
|
+
.onSet(async () => { });
|
|
124
|
+
// Defer network until platform is ready
|
|
125
|
+
this.api.on('didFinishLaunching', async () => {
|
|
126
|
+
var _a;
|
|
127
|
+
try {
|
|
128
|
+
await this.ensureSession();
|
|
129
|
+
await this.refreshStateSafe();
|
|
130
|
+
this.startPolling();
|
|
131
|
+
}
|
|
132
|
+
catch (e) {
|
|
133
|
+
this.log.error('Startup failed:', (_a = e === null || e === void 0 ? void 0 : e.message) !== null && _a !== void 0 ? _a : e);
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
// Cleanup on shutdown
|
|
137
|
+
this.api.on('shutdown', () => {
|
|
138
|
+
this.stopPolling();
|
|
105
139
|
});
|
|
106
|
-
this.log.info('Verano accessory plugin initialized');
|
|
107
140
|
}
|
|
108
141
|
identify() {
|
|
109
142
|
this.log('Identify!');
|
|
110
143
|
}
|
|
111
144
|
getServices() {
|
|
112
|
-
return [
|
|
113
|
-
this.informationService,
|
|
114
|
-
this.service,
|
|
115
|
-
];
|
|
145
|
+
return [this.informationService, this.service];
|
|
116
146
|
}
|
|
117
147
|
getControllers() {
|
|
118
148
|
return [];
|
|
119
149
|
}
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
targetTemperature: targetTemperature,
|
|
128
|
-
heating: targetTemperature > this.TURN_ON_OFF_TEMPERATURE
|
|
129
|
-
};
|
|
130
|
-
this.log.info('Thermostat state:', this.thermostatState);
|
|
131
|
-
return this.thermostatState;
|
|
150
|
+
// --- Helpers ---
|
|
151
|
+
startPolling() {
|
|
152
|
+
if (this.pollTimer)
|
|
153
|
+
clearInterval(this.pollTimer);
|
|
154
|
+
this.pollTimer = setInterval(async () => {
|
|
155
|
+
await this.refreshStateSafe();
|
|
156
|
+
}, this.cfg.pollIntervalSec * 1000);
|
|
132
157
|
}
|
|
133
|
-
|
|
134
|
-
this.
|
|
135
|
-
|
|
136
|
-
this.
|
|
137
|
-
await this.requestAuthorization();
|
|
158
|
+
stopPolling() {
|
|
159
|
+
if (this.pollTimer) {
|
|
160
|
+
clearInterval(this.pollTimer);
|
|
161
|
+
this.pollTimer = undefined;
|
|
138
162
|
}
|
|
139
|
-
const config = {
|
|
140
|
-
headers: {
|
|
141
|
-
'Cookie': this.sessionCookie
|
|
142
|
-
},
|
|
143
|
-
withCredentials: true,
|
|
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);
|
|
160
|
-
}
|
|
161
|
-
this.isAuthorized = false;
|
|
162
|
-
throw error;
|
|
163
|
-
});
|
|
164
163
|
}
|
|
165
|
-
|
|
164
|
+
clampAndStep(v, min, max, step) {
|
|
165
|
+
const clamped = Math.min(Math.max(v, min), max);
|
|
166
|
+
return Math.round(clamped / step) * step;
|
|
167
|
+
}
|
|
168
|
+
async ensureSession() {
|
|
169
|
+
if (this.isAuthorized && this.sessionCookie)
|
|
170
|
+
return;
|
|
171
|
+
await this.requestAuthorization();
|
|
172
|
+
}
|
|
166
173
|
async requestAuthorization() {
|
|
167
|
-
const
|
|
168
|
-
username: this.
|
|
169
|
-
password: this.
|
|
174
|
+
const body = {
|
|
175
|
+
username: this.credentials.username,
|
|
176
|
+
password: this.credentials.password,
|
|
170
177
|
rememberMe: true,
|
|
171
|
-
languageId: 'en'
|
|
178
|
+
languageId: 'en',
|
|
172
179
|
};
|
|
173
|
-
this.log.info('
|
|
174
|
-
|
|
175
|
-
.post('https://emodul.pl/login', requestBody)
|
|
176
|
-
.then(loginResponse => {
|
|
177
|
-
this.log.info('Successfully authorized', requestBody.username);
|
|
178
|
-
const cookies = loginResponse.headers['set-cookie'];
|
|
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);
|
|
180
|
+
this.log.info('Authorizing user', this.credentials.username);
|
|
181
|
+
const res = await this.axios.post('/login', body).catch((e) => {
|
|
185
182
|
this.isAuthorized = false;
|
|
186
|
-
throw
|
|
183
|
+
throw e;
|
|
187
184
|
});
|
|
185
|
+
if (res.status !== 200) {
|
|
186
|
+
this.isAuthorized = false;
|
|
187
|
+
throw new Error(`Authorization failed: HTTP ${res.status}`);
|
|
188
|
+
}
|
|
189
|
+
const setCookie = res.headers['set-cookie'];
|
|
190
|
+
const session = setCookie === null || setCookie === void 0 ? void 0 : setCookie.find((c) => /session/i.test(c));
|
|
191
|
+
if (!session) {
|
|
192
|
+
this.isAuthorized = false;
|
|
193
|
+
throw new Error('Authorization failed: missing session cookie');
|
|
194
|
+
}
|
|
195
|
+
// Keep only the cookie key=value part
|
|
196
|
+
this.sessionCookie = session.split(';')[0];
|
|
197
|
+
this.isAuthorized = true;
|
|
198
|
+
this.log.info('Authorized as', this.credentials.username);
|
|
188
199
|
}
|
|
189
|
-
async
|
|
190
|
-
this.
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
200
|
+
async fetchDataTiles() {
|
|
201
|
+
const res = await this.axios.get('/frontend/module_data');
|
|
202
|
+
if (res.status === 401 || res.status === 403) {
|
|
203
|
+
this.isAuthorized = false;
|
|
204
|
+
await this.requestAuthorization();
|
|
205
|
+
const retry = await this.axios.get('/frontend/module_data');
|
|
206
|
+
if (retry.status !== 200) {
|
|
207
|
+
throw new Error(`Tiles fetch failed after reauth: HTTP ${retry.status}`);
|
|
196
208
|
}
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
209
|
+
return retry.data.tiles;
|
|
210
|
+
}
|
|
211
|
+
if (res.status !== 200) {
|
|
212
|
+
throw new Error(`Tiles fetch failed: HTTP ${res.status}`);
|
|
213
|
+
}
|
|
214
|
+
return res.data.tiles;
|
|
215
|
+
}
|
|
216
|
+
async requestTemperatureChange(targetC) {
|
|
217
|
+
const body = [
|
|
218
|
+
{
|
|
219
|
+
ido: this.cfg.setpointIdo,
|
|
220
|
+
params: Math.round(targetC * this.cfg.temperatureDivider),
|
|
221
|
+
module_index: 0,
|
|
201
222
|
},
|
|
202
|
-
|
|
223
|
+
];
|
|
224
|
+
const res = await this.axios.post('/send_control_data', body);
|
|
225
|
+
if (res.status === 401 || res.status === 403) {
|
|
226
|
+
this.isAuthorized = false;
|
|
227
|
+
await this.requestAuthorization();
|
|
228
|
+
const retry = await this.axios.post('/send_control_data', body);
|
|
229
|
+
if (retry.status < 200 || retry.status >= 300) {
|
|
230
|
+
throw new Error(`Temperature change failed after reauth: HTTP ${retry.status}`);
|
|
231
|
+
}
|
|
232
|
+
return retry.data;
|
|
233
|
+
}
|
|
234
|
+
if (res.status < 200 || res.status >= 300) {
|
|
235
|
+
throw new Error(`Temperature change failed: HTTP ${res.status}`);
|
|
236
|
+
}
|
|
237
|
+
return res.data;
|
|
238
|
+
}
|
|
239
|
+
async requestThermostatState() {
|
|
240
|
+
const tiles = await this.fetchDataTiles();
|
|
241
|
+
const tile = tiles.find((t) => t.id === this.cfg.tileId);
|
|
242
|
+
if (!tile)
|
|
243
|
+
throw new Error(`Tile ${this.cfg.tileId} not found`);
|
|
244
|
+
const targetC = tile.params.widget1.value / this.cfg.temperatureDivider;
|
|
245
|
+
const currentC = tile.params.widget2.value / this.cfg.temperatureDivider;
|
|
246
|
+
this.thermostatState = {
|
|
247
|
+
currentTemperature: currentC,
|
|
248
|
+
targetTemperature: targetC,
|
|
249
|
+
heating: targetC > this.cfg.offThresholdC,
|
|
203
250
|
};
|
|
204
|
-
return
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
.
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
251
|
+
return this.thermostatState;
|
|
252
|
+
}
|
|
253
|
+
async refreshStateSafe() {
|
|
254
|
+
var _a;
|
|
255
|
+
try {
|
|
256
|
+
const state = await this.requestThermostatState();
|
|
257
|
+
this.pushStateToHomeKit(state);
|
|
258
|
+
}
|
|
259
|
+
catch (e) {
|
|
260
|
+
this.log.debug('Refresh failed:', (_a = e === null || e === void 0 ? void 0 : e.message) !== null && _a !== void 0 ? _a : e);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
pushStateToHomeKit(state = this.thermostatState) {
|
|
264
|
+
this.service.updateCharacteristic(this.Characteristic.CurrentTemperature, state.currentTemperature);
|
|
265
|
+
this.service.updateCharacteristic(this.Characteristic.CurrentHeatingCoolingState, state.heating ? this.Characteristic.CurrentHeatingCoolingState.HEAT : this.Characteristic.CurrentHeatingCoolingState.OFF);
|
|
266
|
+
this.service.updateCharacteristic(this.Characteristic.TargetTemperature, state.targetTemperature);
|
|
267
|
+
this.service.updateCharacteristic(this.Characteristic.TargetHeatingCoolingState, state.heating ? this.Characteristic.TargetHeatingCoolingState.HEAT : this.Characteristic.TargetHeatingCoolingState.OFF);
|
|
268
|
+
}
|
|
269
|
+
async setTargetTemperatureC(targetC) {
|
|
270
|
+
if (this.writeInFlight) {
|
|
271
|
+
// Simple debounce: skip if a write is in progress
|
|
272
|
+
this.log.debug('Skipping set, write in flight');
|
|
273
|
+
return;
|
|
274
|
+
}
|
|
275
|
+
this.writeInFlight = true;
|
|
276
|
+
const bounded = this.clampAndStep(targetC, this.cfg.minC, this.cfg.maxC, this.cfg.stepC);
|
|
277
|
+
this.log.info('Setting target temperature to', `${bounded}°C`);
|
|
278
|
+
try {
|
|
279
|
+
await this.ensureSession();
|
|
280
|
+
await this.requestTemperatureChange(bounded);
|
|
281
|
+
// Refresh from source of truth
|
|
282
|
+
await this.refreshStateSafe();
|
|
283
|
+
}
|
|
284
|
+
finally {
|
|
285
|
+
this.writeInFlight = false;
|
|
286
|
+
}
|
|
214
287
|
}
|
|
215
288
|
}
|
|
216
289
|
exports.VeranoAccessoryPlugin = VeranoAccessoryPlugin;
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;AACA,kDAA0B;AAE1B,MAAM,CAAC,OAAO,GAAG,CAAC,GAAQ,EAAE,EAAE;IAC1B,GAAG,CAAC,iBAAiB,CAAC,uBAAuB,EAAE,qBAAqB,CAAC,CAAC;AAC1E,CAAC,CAAA;AAQD,MAAa,qBAAqB;IAkB9B,YACqB,GAAY,EACZ,MAAuB,EACvB,GAAQ;QAFR,QAAG,GAAH,GAAG,CAAS;QACZ,WAAM,GAAN,MAAM,CAAiB;QACvB,QAAG,GAAH,GAAG,CAAK;QAnBZ,wBAAmB,GAAW,EAAE,CAAC;QACjC,4BAAuB,GAAW,EAAE,CAAC;QAK9C,oBAAe,GAAoB;YACvC,kBAAkB,EAAE,CAAC;YACrB,iBAAiB,EAAE,CAAC;YACpB,OAAO,EAAE,KAAK;SACjB,CAAC;QAWE,IAAI,CAAC,GAAG,GAAG,GAAG,CAAC;QACf,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,sCAAsC,CAAC,CAAC;QACtD,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC;QACxB,IAAI,CAAC,GAAG,GAAG,GAAG,CAAC;QACf,IAAI,CAAC,YAAY,GAAG,KAAK,CAAC;QAC1B,IAAI,CAAC,aAAa,GAAG,EAAE,CAAC;QAExB,IAAI,CAAC,cAAc,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,cAAc,CAAC;QAElD,IAAI,CAAC,kBAAkB,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,oBAAoB,EAAE;aACpE,iBAAiB,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,cAAc,CAAC,YAAY,EAAE,QAAQ,CAAC;aACrE,iBAAiB,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,cAAc,CAAC,KAAK,EAAE,aAAa,CAAC,CAAC;QAEzE,IAAI,CAAC,OAAO,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAE9D,oBAAoB;QACpB,IAAI,CAAC,OAAO,CAAC,iBAAiB,CAAC,IAAI,CAAC,cAAc,CAAC,0BAA0B,CAAC;aACzE,EAAE,CAAC,KAAK,EAAE,CAAC,QAAQ,EAAE,EAAE;YACpB,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,gCAAgC,CAAC,CAAC;YACjD,MAAM,YAAY,GAAG,IAAI,CAAC,eAAe,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,cAAc,CAAC,0BAA0B,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,cAAc,CAAC,0BAA0B,CAAC,GAAG,CAAC;YAC7J,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAC,CAAC;QACjC,CAAC,CAAC,CAAC;QAEP,mBAAmB;QACnB,IAAI,CAAC,OAAO,CAAC,iBAAiB,CAAC,IAAI,CAAC,cAAc,CAAC,yBAAyB,CAAC;aACxE,QAAQ,CAAC;YACN,WAAW,EAAE;gBACT,IAAI,CAAC,cAAc,CAAC,yBAAyB,CAAC,GAAG;gBACjD,IAAI,CAAC,cAAc,CAAC,yBAAyB,CAAC,IAAI;aACrD;SACJ,CAAC;aACD,EAAE,CAAC,KAAK,EAAE,CAAC,QAAQ,EAAE,EAAE;YACpB,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,+BAA+B,CAAC,CAAC;YAChD,IAAI,CAAC,sBAAsB,EAAE;iBACxB,IAAI,CAAC,eAAe,CAAC,EAAE;gBACpB,MAAM,YAAY,GAAG,eAAe,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,cAAc,CAAC,yBAAyB,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,cAAc,CAAC,yBAAyB,CAAC,GAAG,CAAC;gBACtJ,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAC,CAAC;YACjC,CAAC,CAAC;iBACD,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC;QACzC,CAAC,CAAC;aACD,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,EAAE,QAAQ,EAAE,EAAE;YAC3B,IAAI,CAAC,eAAe,CAAC,OAAO,GAAG,KAAK,KAAK,IAAI,CAAC,cAAc,CAAC,yBAAyB,CAAC,IAAI,CAAC;YAC5F,IAAI,IAAI,CAAC,eAAe,CAAC,OAAO,EAAE;gBAC9B,QAAQ,CAAC,IAAI,CAAC,CAAC;gBACf,OAAO;aACV;YACD,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,wBAAwB,CAAC,CAAC;YACxC,IAAI,CAAC,wBAAwB,CAAC,IAAI,CAAC,uBAAuB,CAAC;iBACtD,IAAI,CAAC,GAAG,EAAE;gBACP,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,oBAAoB,CAAC,CAAC;gBACpC,QAAQ,CAAC,IAAI,CAAC,CAAC;YACnB,CAAC,CAAC;iBACD,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC;QACzC,CAAC,CAAC,CAAC;QAEP,kBAAkB;QAClB,IAAI,CAAC,OAAO,CAAC,iBAAiB,CAAC,IAAI,CAAC,cAAc,CAAC,kBAAkB,CAAC;aACjE,EAAE,CAAC,KAAK,EAAE,CAAC,QAAQ,EAAE,EAAE;YACpB,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,wBAAwB,CAAC,CAAC;YACzC,IAAI,CAAC,sBAAsB,EAAE;iBACxB,IAAI,CAAC,eAAe,CAAC,EAAE,CAAC,QAAQ,CAAC,IAAI,EAAE,eAAe,CAAC,kBAAkB,CAAC,CAAC;iBAC3E,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC;QACzC,CAAC,CAAC,CAAC;QAEP,yBAAyB;QACzB,IAAI,CAAC,OAAO,CAAC,iBAAiB,CAAC,IAAI,CAAC,cAAc,CAAC,iBAAiB,CAAC;aAChE,EAAE,CAAC,KAAK,EAAE,CAAC,QAAQ,EAAE,EAAE;YACpB,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,uBAAuB,CAAC,CAAC;YACxC,IAAI,CAAC,sBAAsB,EAAE;iBACxB,IAAI,CAAC,eAAe,CAAC,EAAE,CAAC,QAAQ,CAAC,IAAI,EAAE,eAAe,CAAC,iBAAiB,CAAC,CAAC;iBAC1E,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC;QACzC,CAAC,CAAC;aACD,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,EAAE,QAAQ,EAAE,EAAE;YAC3B,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,wBAAwB,EAAE,KAAK,CAAC,CAAC;YAChD,IAAI,CAAC,wBAAwB,CAAC,KAAe,CAAC;iBACzC,IAAI,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;iBAC1B,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC;QACzC,CAAC,CAAC;aACD,QAAQ,CAAC;YACN,QAAQ,EAAE,EAAE;YACZ,QAAQ,EAAE,EAAE;YACZ,OAAO,EAAE,GAAG;SACf,CAAC,CAAC;QAEP,IAAI,CAAC,OAAO,CAAC,iBAAiB,CAAC,IAAI,CAAC,cAAc,CAAC,uBAAuB,CAAC;aACtE,EAAE,CAAC,KAAK,EAAE,CAAC,QAAQ,EAAE,EAAE;YACpB,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC,cAAc,CAAC,uBAAuB,CAAC,OAAO,CAAC,CAAC;QACxE,CAAC,CAAC;aACD,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,EAAE,QAAQ,EAAE,EAAE;QAC/B,CAAC,CAAC,CAAC;QACP,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,qCAAqC,CAAC,CAAC;IACzD,CAAC;IAED,QAAQ;QACJ,IAAI,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;IAC1B,CAAC;IAED,WAAW;QACP,OAAO;YACH,IAAI,CAAC,kBAAkB;YACvB,IAAI,CAAC,OAAO;SACf,CAAC;IACN,CAAC;IAED,cAAc;QACV,OAAO,EAAE,CAAC;IACd,CAAC;IAEO,KAAK,CAAC,sBAAsB;QAChC,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,cAAc,EAAE,CAAC;QAC1C,MAAM,SAAS,GAAG,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;QAC1D,MAAM,iBAAiB,GAAG,SAAS,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,GAAG,IAAI,CAAC,mBAAmB,CAAC;QACpF,MAAM,kBAAkB,GAAG,SAAS,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,GAAG,IAAI,CAAC,mBAAmB,CAAC;QACrF,IAAI,CAAC,eAAe,GAAG;YACnB,kBAAkB,EAAE,kBAAkB;YACtC,iBAAiB,EAAE,iBAAiB;YACpC,OAAO,EAAE,iBAAiB,GAAG,IAAI,CAAC,uBAAuB;SAC5D,CAAC;QACF,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,mBAAmB,EAAE,IAAI,CAAC,eAAe,CAAC,CAAC;QACzD,OAAO,IAAI,CAAC,eAAe,CAAC;IAChC,CAAC;IAEO,KAAK,CAAC,cAAc;QACxB,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,eAAe,CAAC,CAAC;QAChC,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE;YACpB,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,uDAAuD,CAAC,CAAC;YACxE,MAAM,IAAI,CAAC,oBAAoB,EAAE,CAAC;SACrC;QAED,MAAM,MAAM,GAAG;YACX,OAAO,EAAE;gBACL,QAAQ,EAAE,IAAI,CAAC,aAAa;aAC/B;YACD,eAAe,EAAE,IAAI;SACxB,CAAC;QACF,OAAO,eAAK;aACP,GAAG,CAAC,wCAAwC,EAAE,MAAM,CAAC;aACrD,IAAI,CAAC,QAAQ,CAAC,EAAE;YACb,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,2BAA2B,CAAC,CAAC;YAC5C,OAAO,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC;QAC/B,CAAC,CAAC;aACD,KAAK,CAAC,KAAK,CAAC,EAAE;YACX,IAAI,KAAK,CAAC,QAAQ,EAAE;gBAChB,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,0BAA0B,CAAC,CAAC;gBAC3C,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,SAAS,EAAE,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;gBACjD,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;gBAC5B,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;aACvC;iBAAM;gBACH,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,0BAA0B,EAAE,KAAK,CAAC,CAAC;aACrD;YACD,IAAI,CAAC,YAAY,GAAG,KAAK,CAAC;YAC1B,MAAM,KAAK,CAAC;QAChB,CAAC,CAAC,CAAC;IACX,CAAC;IAAA,CAAC;IAEM,KAAK,CAAC,oBAAoB;QAC9B,MAAM,WAAW,GAAG;YAChB,QAAQ,EAAE,IAAI,CAAC,MAAM,CAAC,QAAQ;YAC9B,QAAQ,EAAE,IAAI,CAAC,MAAM,CAAC,QAAQ;YAC9B,UAAU,EAAE,IAAI;YAChB,UAAU,EAAE,IAAI;SACnB,CAAC;QACF,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,+BAA+B,EAAE,WAAW,CAAC,QAAQ,CAAC,CAAC;QACrE,OAAO,eAAK;aACP,IAAI,CAAC,yBAAyB,EAAE,WAAW,CAAC;aAC5C,IAAI,CAAC,aAAa,CAAC,EAAE;YAClB,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,yBAAyB,EAAE,WAAW,CAAC,QAAQ,CAAC,CAAC;YAC/D,MAAM,OAAO,GAAG,aAAa,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;YACpD,IAAI,CAAC,aAAa,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;YAC7E,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC;YACzB,OAAO,aAAa,CAAC;QACzB,CAAC,CAAC;aACD,KAAK,CAAC,KAAK,CAAC,EAAE;YACX,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,4BAA4B,EAAE,WAAW,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;YAC1E,IAAI,CAAC,YAAY,GAAG,KAAK,CAAC;YAC1B,MAAM,KAAK,CAAC;QAChB,CAAC,CAAC,CAAC;IACX,CAAC;IAEO,KAAK,CAAC,wBAAwB,CAAC,iBAAyB;QAC5D,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,yBAAyB,EAAE,iBAAiB,GAAG,IAAI,CAAC,CAAC;QACnE,MAAM,WAAW,GAAG;YAChB;gBACI,GAAG,EAAE,GAAG;gBACR,MAAM,EAAE,iBAAiB,GAAG,IAAI,CAAC,mBAAmB;gBACpD,YAAY,EAAE,CAAC;aAClB;SACJ,CAAA;QACD,MAAM,MAAM,GAAG;YACX,OAAO,EAAE;gBACL,QAAQ,EAAE,IAAI,CAAC,aAAa;aAC/B;YACD,eAAe,EAAE,IAAI;SACxB,CAAC;QACF,OAAO,eAAK;aACP,IAAI,CAAC,qCAAqC,EAAE,WAAW,EAAE,MAAM,CAAC;aAChE,IAAI,CAAC,QAAQ,CAAC,EAAE;YACb,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,qCAAqC,EAAE,iBAAiB,GAAG,IAAI,CAAC,CAAC;YAC/E,OAAO,QAAQ,aAAR,QAAQ,uBAAR,QAAQ,CAAE,IAAI,CAAC;QAC1B,CAAC,CAAC;aACD,KAAK,CAAC,KAAK,CAAC,EAAE;YACX,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,iCAAiC,EAAE,KAAK,CAAC,CAAC;YACzD,MAAM,KAAK,CAAC;QAChB,CAAC,CAAC,CAAA;IACV,CAAC;CAEJ;AAtOD,sDAsOC"}
|
|
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,120 +43,169 @@ 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.log = log;
|
|
38
|
-
this.log.info('Verano accessory plugin initializing');
|
|
39
|
-
this.config = config;
|
|
40
|
-
this.name = config.name;
|
|
41
|
-
this.api = api;
|
|
42
|
-
this.isAuthorized = false;
|
|
43
|
-
this.sessionCookie = '';
|
|
44
|
-
|
|
45
80
|
this.Characteristic = this.api.hap.Characteristic;
|
|
46
81
|
|
|
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 };
|
|
88
|
+
|
|
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
|
+
);
|
|
123
|
+
|
|
47
124
|
this.informationService = new this.api.hap.Service.AccessoryInformation()
|
|
48
|
-
|
|
49
|
-
|
|
125
|
+
.setCharacteristic(this.Characteristic.Manufacturer, 'Verano')
|
|
126
|
+
.setCharacteristic(this.Characteristic.Model, 'VER-24 WiFi');
|
|
50
127
|
|
|
51
|
-
this.service = new this.api.hap.Service.Thermostat(this.name);
|
|
128
|
+
this.service = new this.api.hap.Service.Thermostat(this.config.name);
|
|
52
129
|
|
|
53
|
-
//
|
|
130
|
+
// Current Heating/Cooling State
|
|
54
131
|
this.service.getCharacteristic(this.Characteristic.CurrentHeatingCoolingState)
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
132
|
+
.onGet(async () => {
|
|
133
|
+
return this.thermostatState.heating
|
|
134
|
+
? this.Characteristic.CurrentHeatingCoolingState.HEAT
|
|
135
|
+
: this.Characteristic.CurrentHeatingCoolingState.OFF;
|
|
136
|
+
});
|
|
60
137
|
|
|
61
|
-
//
|
|
138
|
+
// Target Heating/Cooling State (HEAT/OFF only)
|
|
62
139
|
this.service.getCharacteristic(this.Characteristic.TargetHeatingCoolingState)
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
this.
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
this.requestTemperatureChange(this.TURN_ON_OFF_TEMPERATURE)
|
|
86
|
-
.then(() => {
|
|
87
|
-
this.log.info('Heating turned off');
|
|
88
|
-
callback(null);
|
|
89
|
-
})
|
|
90
|
-
.catch(error => callback(error));
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
// GET TEMPERATURE
|
|
140
|
+
.setProps({ validValues: [
|
|
141
|
+
this.Characteristic.TargetHeatingCoolingState.OFF,
|
|
142
|
+
this.Characteristic.TargetHeatingCoolingState.HEAT,
|
|
143
|
+
]})
|
|
144
|
+
.onGet(async () => {
|
|
145
|
+
await this.ensureSession();
|
|
146
|
+
await this.refreshStateSafe();
|
|
147
|
+
return this.thermostatState.heating
|
|
148
|
+
? this.Characteristic.TargetHeatingCoolingState.HEAT
|
|
149
|
+
: this.Characteristic.TargetHeatingCoolingState.OFF;
|
|
150
|
+
})
|
|
151
|
+
.onSet(async (value) => {
|
|
152
|
+
const heat = value === this.Characteristic.TargetHeatingCoolingState.HEAT;
|
|
153
|
+
// Turning off maps to sending the off threshold setpoint if device requires it
|
|
154
|
+
if (!heat) {
|
|
155
|
+
await this.setTargetTemperatureC(this.cfg.offThresholdC);
|
|
156
|
+
}
|
|
157
|
+
this.thermostatState.heating = heat;
|
|
158
|
+
this.pushStateToHomeKit();
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
// Current Temperature
|
|
94
162
|
this.service.getCharacteristic(this.Characteristic.CurrentTemperature)
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
//
|
|
163
|
+
.setProps({ minStep: 0.1 })
|
|
164
|
+
.onGet(async () => {
|
|
165
|
+
await this.ensureSession();
|
|
166
|
+
await this.refreshStateSafe();
|
|
167
|
+
return this.thermostatState.currentTemperature;
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
// Target Temperature
|
|
103
171
|
this.service.getCharacteristic(this.Characteristic.TargetTemperature)
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
});
|
|
172
|
+
.setProps({
|
|
173
|
+
minValue: this.cfg.minC,
|
|
174
|
+
maxValue: this.cfg.maxC,
|
|
175
|
+
minStep: this.cfg.stepC,
|
|
176
|
+
})
|
|
177
|
+
.onGet(async () => {
|
|
178
|
+
await this.ensureSession();
|
|
179
|
+
await this.refreshStateSafe();
|
|
180
|
+
return this.thermostatState.targetTemperature;
|
|
181
|
+
})
|
|
182
|
+
.onSet(async (value) => {
|
|
183
|
+
const v = this.clampAndStep(Number(value), this.cfg.minC, this.cfg.maxC, this.cfg.stepC);
|
|
184
|
+
await this.setTargetTemperatureC(v);
|
|
185
|
+
this.thermostatState.heating = v > this.cfg.offThresholdC;
|
|
186
|
+
this.pushStateToHomeKit();
|
|
187
|
+
});
|
|
121
188
|
|
|
189
|
+
// Celsius only
|
|
122
190
|
this.service.getCharacteristic(this.Characteristic.TemperatureDisplayUnits)
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
191
|
+
.onGet(async () => this.Characteristic.TemperatureDisplayUnits.CELSIUS)
|
|
192
|
+
.onSet(async () => { /* no-op, force Celsius */ });
|
|
193
|
+
|
|
194
|
+
// Defer network until platform is ready
|
|
195
|
+
this.api.on('didFinishLaunching', async () => {
|
|
196
|
+
try {
|
|
197
|
+
await this.ensureSession();
|
|
198
|
+
await this.refreshStateSafe();
|
|
199
|
+
this.startPolling();
|
|
200
|
+
} catch (e) {
|
|
201
|
+
this.log.error('Startup failed:', (e as Error)?.message ?? e);
|
|
202
|
+
}
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
// Cleanup on shutdown
|
|
206
|
+
this.api.on('shutdown', () => {
|
|
207
|
+
this.stopPolling();
|
|
208
|
+
});
|
|
129
209
|
}
|
|
130
210
|
|
|
131
211
|
identify?(): void {
|
|
@@ -133,112 +213,173 @@ export class VeranoAccessoryPlugin implements AccessoryPlugin {
|
|
|
133
213
|
}
|
|
134
214
|
|
|
135
215
|
getServices(): Service[] {
|
|
136
|
-
return [
|
|
137
|
-
this.informationService,
|
|
138
|
-
this.service,
|
|
139
|
-
];
|
|
216
|
+
return [this.informationService, this.service];
|
|
140
217
|
}
|
|
141
218
|
|
|
142
219
|
getControllers?(): Controller[] {
|
|
143
220
|
return [];
|
|
144
221
|
}
|
|
145
222
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
targetTemperature: targetTemperature,
|
|
154
|
-
heating: targetTemperature > this.TURN_ON_OFF_TEMPERATURE
|
|
155
|
-
};
|
|
156
|
-
this.log.info('Thermostat state:', this.thermostatState);
|
|
157
|
-
return this.thermostatState;
|
|
223
|
+
// --- Helpers ---
|
|
224
|
+
|
|
225
|
+
private startPolling() {
|
|
226
|
+
if (this.pollTimer) clearInterval(this.pollTimer);
|
|
227
|
+
this.pollTimer = setInterval(async () => {
|
|
228
|
+
await this.refreshStateSafe();
|
|
229
|
+
}, this.cfg.pollIntervalSec * 1000);
|
|
158
230
|
}
|
|
159
231
|
|
|
160
|
-
private
|
|
161
|
-
this.
|
|
162
|
-
|
|
163
|
-
this.
|
|
164
|
-
await this.requestAuthorization();
|
|
232
|
+
private stopPolling() {
|
|
233
|
+
if (this.pollTimer) {
|
|
234
|
+
clearInterval(this.pollTimer);
|
|
235
|
+
this.pollTimer = undefined;
|
|
165
236
|
}
|
|
237
|
+
}
|
|
166
238
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
return
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
this.log.debug('Successfully fetched data');
|
|
177
|
-
return response.data.tiles;
|
|
178
|
-
})
|
|
179
|
-
.catch(error => {
|
|
180
|
-
if (error.response) {
|
|
181
|
-
this.log.error("Error during tiles fetch");
|
|
182
|
-
this.log.error("Status:", error.response.status);
|
|
183
|
-
this.log.error("Response:");
|
|
184
|
-
this.log.error(error.response.data);
|
|
185
|
-
} else {
|
|
186
|
-
this.log.error("Error during tiles fetch", error);
|
|
187
|
-
}
|
|
188
|
-
this.isAuthorized = false;
|
|
189
|
-
throw error;
|
|
190
|
-
});
|
|
191
|
-
};
|
|
239
|
+
private clampAndStep(v: number, min: number, max: number, step: number) {
|
|
240
|
+
const clamped = Math.min(Math.max(v, min), max);
|
|
241
|
+
return Math.round(clamped / step) * step;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
private async ensureSession() {
|
|
245
|
+
if (this.isAuthorized && this.sessionCookie) return;
|
|
246
|
+
await this.requestAuthorization();
|
|
247
|
+
}
|
|
192
248
|
|
|
193
249
|
private async requestAuthorization() {
|
|
194
|
-
const
|
|
195
|
-
username: this.
|
|
196
|
-
password: this.
|
|
250
|
+
const body = {
|
|
251
|
+
username: this.credentials.username,
|
|
252
|
+
password: this.credentials.password,
|
|
197
253
|
rememberMe: true,
|
|
198
|
-
languageId: 'en'
|
|
254
|
+
languageId: 'en',
|
|
199
255
|
};
|
|
200
|
-
this.log.info('
|
|
201
|
-
|
|
202
|
-
.
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
256
|
+
this.log.info('Authorizing user', this.credentials.username);
|
|
257
|
+
const res = await this.axios.post('/login', body).catch((e) => {
|
|
258
|
+
this.isAuthorized = false;
|
|
259
|
+
throw e;
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
if (res.status !== 200) {
|
|
263
|
+
this.isAuthorized = false;
|
|
264
|
+
throw new Error(`Authorization failed: HTTP ${res.status}`);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
const setCookie = res.headers['set-cookie'] as string[] | undefined;
|
|
268
|
+
const session = setCookie?.find((c) => /session/i.test(c));
|
|
269
|
+
if (!session) {
|
|
270
|
+
this.isAuthorized = false;
|
|
271
|
+
throw new Error('Authorization failed: missing session cookie');
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// Keep only the cookie key=value part
|
|
275
|
+
this.sessionCookie = session.split(';')[0];
|
|
276
|
+
this.isAuthorized = true;
|
|
277
|
+
this.log.info('Authorized as', this.credentials.username);
|
|
215
278
|
}
|
|
216
279
|
|
|
217
|
-
private async
|
|
218
|
-
this.
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
280
|
+
private async fetchDataTiles(): Promise<Tile[]> {
|
|
281
|
+
const res = await this.axios.get<ModuleDataResponse>('/frontend/module_data');
|
|
282
|
+
|
|
283
|
+
if (res.status === 401 || res.status === 403) {
|
|
284
|
+
this.isAuthorized = false;
|
|
285
|
+
await this.requestAuthorization();
|
|
286
|
+
const retry = await this.axios.get<ModuleDataResponse>('/frontend/module_data');
|
|
287
|
+
if (retry.status !== 200) {
|
|
288
|
+
throw new Error(`Tiles fetch failed after reauth: HTTP ${retry.status}`);
|
|
224
289
|
}
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
290
|
+
return retry.data.tiles;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
if (res.status !== 200) {
|
|
294
|
+
throw new Error(`Tiles fetch failed: HTTP ${res.status}`);
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
return res.data.tiles;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
private async requestTemperatureChange(targetC: number) {
|
|
301
|
+
const body = [
|
|
302
|
+
{
|
|
303
|
+
ido: this.cfg.setpointIdo,
|
|
304
|
+
params: Math.round(targetC * this.cfg.temperatureDivider),
|
|
305
|
+
module_index: 0,
|
|
229
306
|
},
|
|
230
|
-
|
|
307
|
+
];
|
|
308
|
+
|
|
309
|
+
const res = await this.axios.post('/send_control_data', body);
|
|
310
|
+
|
|
311
|
+
if (res.status === 401 || res.status === 403) {
|
|
312
|
+
this.isAuthorized = false;
|
|
313
|
+
await this.requestAuthorization();
|
|
314
|
+
const retry = await this.axios.post('/send_control_data', body);
|
|
315
|
+
if (retry.status < 200 || retry.status >= 300) {
|
|
316
|
+
throw new Error(`Temperature change failed after reauth: HTTP ${retry.status}`);
|
|
317
|
+
}
|
|
318
|
+
return retry.data;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
if (res.status < 200 || res.status >= 300) {
|
|
322
|
+
throw new Error(`Temperature change failed: HTTP ${res.status}`);
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
return res.data;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
private async requestThermostatState(): Promise<ThermostatState> {
|
|
329
|
+
const tiles = await this.fetchDataTiles();
|
|
330
|
+
const tile = tiles.find((t) => t.id === this.cfg.tileId);
|
|
331
|
+
if (!tile) throw new Error(`Tile ${this.cfg.tileId} not found`);
|
|
332
|
+
|
|
333
|
+
const targetC = tile.params.widget1.value / this.cfg.temperatureDivider;
|
|
334
|
+
const currentC = tile.params.widget2.value / this.cfg.temperatureDivider;
|
|
335
|
+
|
|
336
|
+
this.thermostatState = {
|
|
337
|
+
currentTemperature: currentC,
|
|
338
|
+
targetTemperature: targetC,
|
|
339
|
+
heating: targetC > this.cfg.offThresholdC,
|
|
231
340
|
};
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
.
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
341
|
+
|
|
342
|
+
return this.thermostatState;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
private async refreshStateSafe() {
|
|
346
|
+
try {
|
|
347
|
+
const state = await this.requestThermostatState();
|
|
348
|
+
this.pushStateToHomeKit(state);
|
|
349
|
+
} catch (e) {
|
|
350
|
+
this.log.debug('Refresh failed:', (e as Error)?.message ?? e);
|
|
351
|
+
}
|
|
242
352
|
}
|
|
243
353
|
|
|
354
|
+
private pushStateToHomeKit(state: ThermostatState = this.thermostatState) {
|
|
355
|
+
this.service.updateCharacteristic(this.Characteristic.CurrentTemperature, state.currentTemperature);
|
|
356
|
+
this.service.updateCharacteristic(
|
|
357
|
+
this.Characteristic.CurrentHeatingCoolingState,
|
|
358
|
+
state.heating ? this.Characteristic.CurrentHeatingCoolingState.HEAT : this.Characteristic.CurrentHeatingCoolingState.OFF,
|
|
359
|
+
);
|
|
360
|
+
this.service.updateCharacteristic(this.Characteristic.TargetTemperature, state.targetTemperature);
|
|
361
|
+
this.service.updateCharacteristic(
|
|
362
|
+
this.Characteristic.TargetHeatingCoolingState,
|
|
363
|
+
state.heating ? this.Characteristic.TargetHeatingCoolingState.HEAT : this.Characteristic.TargetHeatingCoolingState.OFF,
|
|
364
|
+
);
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
private async setTargetTemperatureC(targetC: number) {
|
|
368
|
+
if (this.writeInFlight) {
|
|
369
|
+
// Simple debounce: skip if a write is in progress
|
|
370
|
+
this.log.debug('Skipping set, write in flight');
|
|
371
|
+
return;
|
|
372
|
+
}
|
|
373
|
+
this.writeInFlight = true;
|
|
374
|
+
const bounded = this.clampAndStep(targetC, this.cfg.minC, this.cfg.maxC, this.cfg.stepC);
|
|
375
|
+
this.log.info('Setting target temperature to', `${bounded}°C`);
|
|
376
|
+
try {
|
|
377
|
+
await this.ensureSession();
|
|
378
|
+
await this.requestTemperatureChange(bounded);
|
|
379
|
+
// Refresh from source of truth
|
|
380
|
+
await this.refreshStateSafe();
|
|
381
|
+
} finally {
|
|
382
|
+
this.writeInFlight = false;
|
|
383
|
+
}
|
|
384
|
+
}
|
|
244
385
|
}
|