homebridge-tesy-heater-api-v4 0.0.3 → 0.0.4

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.
Files changed (3) hide show
  1. package/index.js +385 -65
  2. package/index.js-backup +137 -0
  3. package/package.json +1 -1
package/index.js CHANGED
@@ -1,3 +1,4 @@
1
+ // index.js
1
2
  const axios = require("axios");
2
3
  const _http_base = require("homebridge-http-base");
3
4
  const PullTimer = _http_base.PullTimer;
@@ -7,12 +8,15 @@ let Service, Characteristic;
7
8
  module.exports = function (homebridge) {
8
9
  Service = homebridge.hap.Service;
9
10
  Characteristic = homebridge.hap.Characteristic;
10
- homebridge.registerAccessory("homebridge-tesy-heater-v2", "TesyHeater", TesyHeater);
11
+ // Keep the package name here in case you want to fully-qualify in config:
12
+ homebridge.registerAccessory("homebridge-tesy-heater-api-v4", "TesyHeater", TesyHeater);
11
13
  };
12
14
 
13
15
  class TesyHeater {
14
16
  constructor(log, config) {
15
17
  this.log = log;
18
+
19
+ // ---- Config ----
16
20
  this.name = config.name;
17
21
  this.manufacturer = config.manufacturer || "Tesy";
18
22
  this.model = config.model || "Convector (Heater)";
@@ -20,118 +24,434 @@ class TesyHeater {
20
24
  this.pullInterval = config.pullInterval || 10000;
21
25
  this.maxTemp = config.maxTemp || 30;
22
26
  this.minTemp = config.minTemp || 10;
27
+
28
+ this.userid = config.userid || null; // optional; some accounts have it
23
29
  this.username = config.username || null;
24
30
  this.password = config.password || null;
25
- this.session = "";
26
- this.alt = "";
27
31
 
28
- if (this.username && this.password) {
29
- this.authenticate();
30
- }
32
+ // ---- Session state returned by login ----
33
+ this.session = ""; // PHPSESSID / acc_session
34
+ this.alt = ""; // ALT / acc_alt
31
35
 
36
+ // ---- HomeKit Service ----
32
37
  this.service = new Service.HeaterCooler(this.name);
38
+
39
+ // Default initial states so Home app doesn't show "No Response" before first refresh
40
+ this.service
41
+ .getCharacteristic(Characteristic.CurrentHeaterCoolerState)
42
+ .updateValue(Characteristic.CurrentHeaterCoolerState.INACTIVE);
43
+
44
+ this.service
45
+ .getCharacteristic(Characteristic.Active)
46
+ .updateValue(Characteristic.Active.INACTIVE);
47
+
48
+ this.service
49
+ .getCharacteristic(Characteristic.CurrentTemperature)
50
+ .updateValue(this.minTemp);
51
+
52
+ this.service
53
+ .getCharacteristic(Characteristic.HeatingThresholdTemperature)
54
+ .setProps({
55
+ minValue: this.minTemp,
56
+ maxValue: this.maxTemp,
57
+ minStep: 0.5,
58
+ })
59
+ .updateValue(this.minTemp);
60
+
61
+ // Add CoolingThresholdTemperature so Home shows the marker on the wheel
62
+ this.service
63
+ .getCharacteristic(Characteristic.CoolingThresholdTemperature)
64
+ .setProps({
65
+ minValue: this.minTemp,
66
+ maxValue: this.maxTemp,
67
+ minStep: 0.5,
68
+ })
69
+ .updateValue(this.minTemp);
70
+
71
+ // Only support HEAT for TargetHeaterCoolerState
72
+ this.service
73
+ .getCharacteristic(Characteristic.TargetHeaterCoolerState)
74
+ .setProps({
75
+ validValues: [Characteristic.TargetHeaterCoolerState.HEAT],
76
+ });
77
+
78
+ // ---- Bind getters/setters ----
79
+ this.service
80
+ .getCharacteristic(Characteristic.Active)
81
+ .on("get", this.getActive.bind(this))
82
+ .on("set", this.setActive.bind(this));
83
+
84
+ this.service
85
+ .getCharacteristic(Characteristic.CurrentTemperature)
86
+ .setProps({ minStep: 0.1 })
87
+ .on("get", this.getCurrentTemperature.bind(this));
88
+
89
+ this.service
90
+ .getCharacteristic(Characteristic.HeatingThresholdTemperature)
91
+ .on("set", this.setHeatingThresholdTemperature.bind(this));
92
+
93
+ this.service
94
+ .getCharacteristic(Characteristic.TargetHeaterCoolerState)
95
+ .on("get", this.getTargetHeaterCoolerState.bind(this));
96
+
97
+ this.service
98
+ .getCharacteristic(Characteristic.Name)
99
+ .on("get", this.getName.bind(this));
100
+
101
+ // ---- Accessory Information ----
102
+ this.informationService = new Service.AccessoryInformation();
103
+ this.informationService
104
+ .setCharacteristic(Characteristic.Manufacturer, this.manufacturer)
105
+ .setCharacteristic(Characteristic.Model, this.model)
106
+ .setCharacteristic(Characteristic.SerialNumber, this.device_id);
107
+
108
+ // ---- Timer (created now, started after login) ----
33
109
  this.pullTimer = new PullTimer(
34
110
  this.log,
35
111
  this.pullInterval,
36
112
  this.refreshTesyHeaterStatus.bind(this),
37
113
  () => {}
38
114
  );
39
- this.pullTimer.start();
115
+
116
+ // ---- Kick off login, then start polling ----
117
+ if (this.username && this.password) {
118
+ this.authenticate()
119
+ .then(() => {
120
+ this.pullTimer.start();
121
+ // Do an immediate first refresh
122
+ this.refreshTesyHeaterStatus();
123
+ })
124
+ .catch((e) => {
125
+ this.log.error("Initial authentication failed. Will still start timer and retry on next ticks.");
126
+ this.pullTimer.start();
127
+ });
128
+ } else {
129
+ this.log.warn("Username/password not provided; device will remain INACTIVE.");
130
+ this.pullTimer.start();
131
+ }
132
+
133
+ this.log.info(this.name);
134
+ }
135
+
136
+ // ---- HomeKit identity ----
137
+ identify(callback) {
138
+ this.log.info("Hi, I'm", this.name);
139
+ callback();
40
140
  }
41
141
 
142
+ getName(callback) {
143
+ callback(null, this.name);
144
+ }
145
+
146
+ getTargetHeaterCoolerState(callback) {
147
+ callback(null, Characteristic.TargetHeaterCoolerState.HEAT);
148
+ }
149
+
150
+ // ---- Helpers to map Tesy fields ----
151
+ getTesyHeaterActiveState(state) {
152
+ if (!state) return Characteristic.Active.INACTIVE;
153
+ return state.toLowerCase() === "on"
154
+ ? Characteristic.Active.ACTIVE
155
+ : Characteristic.Active.INACTIVE;
156
+ }
157
+
158
+ getTesyHeaterCurrentHeaterCoolerState(state) {
159
+ // READY = IDLE; otherwise assume heating when active
160
+ if (!state) return Characteristic.CurrentHeaterCoolerState.INACTIVE;
161
+ return state.toUpperCase() === "READY"
162
+ ? Characteristic.CurrentHeaterCoolerState.IDLE
163
+ : Characteristic.CurrentHeaterCoolerState.HEATING;
164
+ }
165
+
166
+ // ---- API calls (axios) ----
42
167
  async authenticate() {
43
168
  try {
44
- const response = await axios.post("https://v4.mytesy.com/auth/login", {
45
- email: this.username,
46
- password: this.password,
47
- });
48
- this.session = response.data.session_token;
49
- this.alt = response.data.alt_key;
50
- this.log.info("Successfully authenticated with Tesy API");
169
+ const { data } = await axios.post(
170
+ "https://ad.mytesy.com/rest/old-app-login",
171
+ {
172
+ email: this.username,
173
+ password: this.password,
174
+ userID: this.userid,
175
+ userEmail: this.username,
176
+ userPass: this.password,
177
+ lang: "en",
178
+ },
179
+ { headers: { "content-type": "application/json" }, timeout: 10000 }
180
+ );
181
+
182
+ // Expected fields from Tesy "old-app" login:
183
+ this.session = data.acc_session || data.PHPSESSID || "";
184
+ this.alt = data.acc_alt || data.ALT || "";
185
+
186
+ if (!this.session || !this.alt) {
187
+ throw new Error("Missing session/alt in login response");
188
+ }
189
+
190
+ this.log.info("Authenticated to Tesy (old-app).");
51
191
  } catch (error) {
52
- this.log.error("Authentication failed:", error.response?.data || error.message);
192
+ const msg = error?.response?.data || error.message;
193
+ this.log.error("Authentication failed:", msg);
194
+ throw error;
53
195
  }
54
196
  }
55
197
 
56
198
  async refreshTesyHeaterStatus() {
57
- this.log.debug("Refreshing heater status");
199
+ this.log.debug("Executing refreshTesyHeaterStatus");
200
+
201
+ // Avoid overlapping polls
202
+ this.pullTimer.stop();
203
+
58
204
  try {
59
- const response = await axios.post("https://v4.mytesy.com/devices/status", {
60
- session_token: this.session,
61
- device_id: this.device_id,
62
- });
205
+ if (!this.session || !this.alt) {
206
+ this.log.warn("No session/alt yet; re-authenticating...");
207
+ await this.authenticate();
208
+ }
209
+
210
+ const { data } = await axios.post(
211
+ "https://ad.mytesy.com/rest/old-app-devices",
212
+ {
213
+ ALT: this.alt,
214
+ CURRENT_SESSION: null,
215
+ PHPSESSID: this.session,
216
+ last_login_username: this.username,
217
+ userID: this.userid,
218
+ userEmail: this.username,
219
+ userPass: this.password,
220
+ lang: "en",
221
+ },
222
+ { headers: { "content-type": "application/json" }, timeout: 10000 }
223
+ );
224
+
225
+ if (!data?.device || Object.keys(data.device).length === 0) {
226
+ throw new Error("No devices in response");
227
+ }
228
+
229
+ const firstKey = Object.keys(data.device)[0];
230
+ const status = data.device[firstKey]?.DeviceStatus;
231
+
232
+ if (!status) throw new Error("DeviceStatus missing");
63
233
 
64
- const status = response.data.device_status;
65
234
  this.updateDeviceStatus(status);
66
235
  } catch (error) {
67
- this.log.error("Failed to refresh heater status:", error.response?.data || error.message);
236
+ const msg = error?.response?.data || error.message;
237
+ this.log.error("Failed to refresh heater status:", msg);
238
+
239
+ // Set to INACTIVE on failure to avoid stale "active" UI
240
+ try {
241
+ this.service
242
+ .getCharacteristic(Characteristic.Active)
243
+ .updateValue(Characteristic.Active.INACTIVE);
244
+ } catch (_) {}
245
+ } finally {
246
+ this.pullTimer.start();
68
247
  }
69
248
  }
70
249
 
71
250
  updateDeviceStatus(status) {
72
- const newCurrentTemperature = parseFloat(status.temperature);
73
- this.service.getCharacteristic(Characteristic.CurrentTemperature).updateValue(newCurrentTemperature);
251
+ // Current temperature: status.gradus
252
+ const newCurrentTemperature = parseFloat(status.gradus);
253
+ const oldCurrentTemperature =
254
+ this.service.getCharacteristic(Characteristic.CurrentTemperature).value;
255
+
256
+ if (
257
+ Number.isFinite(newCurrentTemperature) &&
258
+ newCurrentTemperature !== oldCurrentTemperature &&
259
+ newCurrentTemperature >= this.minTemp &&
260
+ newCurrentTemperature <= this.maxTemp
261
+ ) {
262
+ this.service
263
+ .getCharacteristic(Characteristic.CurrentTemperature)
264
+ .updateValue(newCurrentTemperature);
265
+ this.log.info(
266
+ "Changing CurrentTemperature from %s to %s",
267
+ oldCurrentTemperature,
268
+ newCurrentTemperature
269
+ );
270
+ }
271
+
272
+ // Target temp: status.ref_gradus
273
+ const newHeatingThresholdTemperature = parseFloat(status.ref_gradus);
274
+ const oldHeatingThresholdTemperature =
275
+ this.service.getCharacteristic(Characteristic.HeatingThresholdTemperature).value;
276
+
277
+ if (
278
+ Number.isFinite(newHeatingThresholdTemperature) &&
279
+ newHeatingThresholdTemperature !== oldHeatingThresholdTemperature &&
280
+ newHeatingThresholdTemperature >= this.minTemp &&
281
+ newHeatingThresholdTemperature <= this.maxTemp
282
+ ) {
283
+ this.service
284
+ .getCharacteristic(Characteristic.HeatingThresholdTemperature)
285
+ .updateValue(newHeatingThresholdTemperature);
286
+ this.log.info(
287
+ "Changing HeatingThresholdTemperature from %s to %s",
288
+ oldHeatingThresholdTemperature,
289
+ newHeatingThresholdTemperature
290
+ );
291
+ }
292
+
293
+ // Power state: status.power_sw => "on" / "off"
294
+ const newHeaterActiveStatus = this.getTesyHeaterActiveState(status.power_sw);
295
+ const oldHeaterActiveStatus =
296
+ this.service.getCharacteristic(Characteristic.Active).value;
297
+
298
+ if (
299
+ newHeaterActiveStatus !== undefined &&
300
+ newHeaterActiveStatus !== oldHeaterActiveStatus
301
+ ) {
302
+ this.service
303
+ .getCharacteristic(Characteristic.Active)
304
+ .updateValue(newHeaterActiveStatus);
305
+ this.log.info(
306
+ "Changing ActiveStatus from %s to %s",
307
+ oldHeaterActiveStatus,
308
+ newHeaterActiveStatus
309
+ );
310
+ }
74
311
 
75
- const newHeatingThresholdTemperature = parseFloat(status.target_temperature);
76
- this.service.getCharacteristic(Characteristic.HeatingThresholdTemperature).updateValue(newHeatingThresholdTemperature);
312
+ // Heating state: status.heater_state => READY / HEATING
313
+ const newCurrentHeaterCoolerState = this.getTesyHeaterCurrentHeaterCoolerState(
314
+ status.heater_state
315
+ );
316
+ const oldCurrentHeaterCoolerState =
317
+ this.service.getCharacteristic(Characteristic.CurrentHeaterCoolerState).value;
318
+
319
+ if (newCurrentHeaterCoolerState !== oldCurrentHeaterCoolerState) {
320
+ this.service
321
+ .getCharacteristic(Characteristic.CurrentHeaterCoolerState)
322
+ .updateValue(newCurrentHeaterCoolerState);
323
+ this.log.info(
324
+ "Changing CurrentHeaterCoolerState from %s to %s",
325
+ oldCurrentHeaterCoolerState,
326
+ newCurrentHeaterCoolerState
327
+ );
328
+ }
329
+ }
330
+
331
+ // ---- HomeKit characteristic handlers ----
332
+ getActive(callback) {
333
+ // Return current cached value immediately
334
+ try {
335
+ const v = this.service.getCharacteristic(Characteristic.Active).value;
336
+ callback(null, v);
337
+ } catch (e) {
338
+ callback(e);
339
+ }
77
340
 
78
- const isActive = status.power === "on" ? Characteristic.Active.ACTIVE : Characteristic.Active.INACTIVE;
79
- this.service.getCharacteristic(Characteristic.Active).updateValue(isActive);
341
+ // Also trigger a background refresh (not blocking callback)
342
+ this.refreshTesyHeaterStatus().catch(() => {});
80
343
  }
81
344
 
82
345
  async setActive(value, callback) {
83
- this.log.info("Setting heater state:", value);
346
+ this.log.info("[+] Changing Active status to value:", value);
347
+
348
+ this.pullTimer.stop();
349
+ const newValue = value === 0 ? "off" : "on";
350
+
84
351
  try {
85
- await axios.post("https://v4.mytesy.com/devices/set", {
86
- session_token: this.session,
87
- device_id: this.device_id,
88
- command: "power",
89
- value: value === 0 ? "off" : "on",
90
- });
352
+ if (!this.session || !this.alt) {
353
+ await this.authenticate();
354
+ }
355
+
356
+ await axios.post(
357
+ "https://ad.mytesy.com/rest/old-app-set-device-status",
358
+ {
359
+ ALT: this.alt,
360
+ CURRENT_SESSION: null,
361
+ PHPSESSID: this.session,
362
+ last_login_username: this.username,
363
+ id: this.device_id,
364
+ apiVersion: "apiv1",
365
+ command: "power_sw",
366
+ value: newValue,
367
+ userID: this.userid,
368
+ userEmail: this.username,
369
+ userPass: this.password,
370
+ lang: "en",
371
+ },
372
+ { headers: { "content-type": "application/json" }, timeout: 10000 }
373
+ );
374
+
375
+ // Optimistically update
376
+ this.service
377
+ .getCharacteristic(Characteristic.Active)
378
+ .updateValue(value);
379
+
91
380
  callback(null, value);
92
381
  } catch (error) {
93
- this.log.error("Failed to set heater state:", error.response?.data || error.message);
382
+ const msg = error?.response?.data || error.message;
383
+ this.log.error("Failed to set Active:", msg);
94
384
  callback(error);
385
+ } finally {
386
+ this.pullTimer.start();
95
387
  }
96
388
  }
97
389
 
98
- async setHeatingThresholdTemperature(value, callback) {
99
- this.log.info("Setting target temperature to:", value);
390
+ getCurrentTemperature(callback) {
391
+ // Return cached value
100
392
  try {
101
- await axios.post("https://v4.mytesy.com/devices/set", {
102
- session_token: this.session,
103
- device_id: this.device_id,
104
- command: "target_temperature",
105
- value: value,
106
- });
107
- callback(null, value);
108
- } catch (error) {
109
- this.log.error("Failed to set target temperature:", error.response?.data || error.message);
110
- callback(error);
393
+ const v =
394
+ this.service.getCharacteristic(Characteristic.CurrentTemperature).value;
395
+ callback(null, v);
396
+ } catch (e) {
397
+ callback(e);
111
398
  }
399
+
400
+ // Kick a refresh to keep it warm
401
+ this.refreshTesyHeaterStatus().catch(() => {});
112
402
  }
113
403
 
114
- getServices() {
115
- this.informationService = new Service.AccessoryInformation();
116
- this.informationService
117
- .setCharacteristic(Characteristic.Manufacturer, this.manufacturer)
118
- .setCharacteristic(Characteristic.Model, this.model)
119
- .setCharacteristic(Characteristic.SerialNumber, this.device_id);
404
+ async setHeatingThresholdTemperature(value, callback) {
405
+ let v = value;
406
+ if (v < this.minTemp) v = this.minTemp;
407
+ if (v > this.maxTemp) v = this.maxTemp;
408
+ this.log.info("[+] Changing HeatingThresholdTemperature to:", v);
120
409
 
121
- this.service.getCharacteristic(Characteristic.Active)
122
- .on("set", this.setActive.bind(this));
410
+ this.pullTimer.stop();
123
411
 
124
- this.service.getCharacteristic(Characteristic.CurrentTemperature)
125
- .setProps({ minStep: 0.1 });
412
+ try {
413
+ if (!this.session || !this.alt) {
414
+ await this.authenticate();
415
+ }
126
416
 
127
- this.service.getCharacteristic(Characteristic.HeatingThresholdTemperature)
128
- .setProps({
129
- minValue: this.minTemp,
130
- maxValue: this.maxTemp,
131
- minStep: 0.5,
132
- })
133
- .on("set", this.setHeatingThresholdTemperature.bind(this));
417
+ await axios.post(
418
+ "https://ad.mytesy.com/rest/old-app-set-device-status",
419
+ {
420
+ ALT: this.alt,
421
+ CURRENT_SESSION: null,
422
+ PHPSESSID: this.session,
423
+ last_login_username: this.username,
424
+ id: this.device_id,
425
+ apiVersion: "apiv1",
426
+ command: "tmpT",
427
+ value: v,
428
+ userID: this.userid,
429
+ userEmail: this.username,
430
+ userPass: this.password,
431
+ lang: "en",
432
+ },
433
+ { headers: { "content-type": "application/json" }, timeout: 10000 }
434
+ );
435
+
436
+ // Optimistically update
437
+ this.service
438
+ .getCharacteristic(Characteristic.HeatingThresholdTemperature)
439
+ .updateValue(v);
134
440
 
441
+ callback(null, v);
442
+ } catch (error) {
443
+ const msg = error?.response?.data || error.message;
444
+ this.log.error("Failed to set target temperature:", msg);
445
+ callback(error);
446
+ } finally {
447
+ this.pullTimer.start();
448
+ }
449
+ }
450
+
451
+ // ---- Services ----
452
+ getServices() {
453
+ // Trigger an initial refresh (non-blocking)
454
+ this.refreshTesyHeaterStatus().catch(() => {});
135
455
  return [this.informationService, this.service];
136
456
  }
137
457
  }
@@ -0,0 +1,137 @@
1
+ const axios = require("axios");
2
+ const _http_base = require("homebridge-http-base");
3
+ const PullTimer = _http_base.PullTimer;
4
+
5
+ let Service, Characteristic;
6
+
7
+ module.exports = function (homebridge) {
8
+ Service = homebridge.hap.Service;
9
+ Characteristic = homebridge.hap.Characteristic;
10
+ homebridge.registerAccessory("homebridge-tesy-heater-v2", "TesyHeater", TesyHeater);
11
+ };
12
+
13
+ class TesyHeater {
14
+ constructor(log, config) {
15
+ this.log = log;
16
+ this.name = config.name;
17
+ this.manufacturer = config.manufacturer || "Tesy";
18
+ this.model = config.model || "Convector (Heater)";
19
+ this.device_id = config.device_id;
20
+ this.pullInterval = config.pullInterval || 10000;
21
+ this.maxTemp = config.maxTemp || 30;
22
+ this.minTemp = config.minTemp || 10;
23
+ this.username = config.username || null;
24
+ this.password = config.password || null;
25
+ this.session = "";
26
+ this.alt = "";
27
+
28
+ if (this.username && this.password) {
29
+ this.authenticate();
30
+ }
31
+
32
+ this.service = new Service.HeaterCooler(this.name);
33
+ this.pullTimer = new PullTimer(
34
+ this.log,
35
+ this.pullInterval,
36
+ this.refreshTesyHeaterStatus.bind(this),
37
+ () => {}
38
+ );
39
+ this.pullTimer.start();
40
+ }
41
+
42
+ async authenticate() {
43
+ try {
44
+ const response = await axios.post("https://v4.mytesy.com/auth/login", {
45
+ email: this.username,
46
+ password: this.password,
47
+ });
48
+ this.session = response.data.session_token;
49
+ this.alt = response.data.alt_key;
50
+ this.log.info("Successfully authenticated with Tesy API");
51
+ } catch (error) {
52
+ this.log.error("Authentication failed:", error.response?.data || error.message);
53
+ }
54
+ }
55
+
56
+ async refreshTesyHeaterStatus() {
57
+ this.log.debug("Refreshing heater status");
58
+ try {
59
+ const response = await axios.post("https://v4.mytesy.com/devices/status", {
60
+ session_token: this.session,
61
+ device_id: this.device_id,
62
+ });
63
+
64
+ const status = response.data.device_status;
65
+ this.updateDeviceStatus(status);
66
+ } catch (error) {
67
+ this.log.error("Failed to refresh heater status:", error.response?.data || error.message);
68
+ }
69
+ }
70
+
71
+ updateDeviceStatus(status) {
72
+ const newCurrentTemperature = parseFloat(status.temperature);
73
+ this.service.getCharacteristic(Characteristic.CurrentTemperature).updateValue(newCurrentTemperature);
74
+
75
+ const newHeatingThresholdTemperature = parseFloat(status.target_temperature);
76
+ this.service.getCharacteristic(Characteristic.HeatingThresholdTemperature).updateValue(newHeatingThresholdTemperature);
77
+
78
+ const isActive = status.power === "on" ? Characteristic.Active.ACTIVE : Characteristic.Active.INACTIVE;
79
+ this.service.getCharacteristic(Characteristic.Active).updateValue(isActive);
80
+ }
81
+
82
+ async setActive(value, callback) {
83
+ this.log.info("Setting heater state:", value);
84
+ try {
85
+ await axios.post("https://v4.mytesy.com/devices/set", {
86
+ session_token: this.session,
87
+ device_id: this.device_id,
88
+ command: "power",
89
+ value: value === 0 ? "off" : "on",
90
+ });
91
+ callback(null, value);
92
+ } catch (error) {
93
+ this.log.error("Failed to set heater state:", error.response?.data || error.message);
94
+ callback(error);
95
+ }
96
+ }
97
+
98
+ async setHeatingThresholdTemperature(value, callback) {
99
+ this.log.info("Setting target temperature to:", value);
100
+ try {
101
+ await axios.post("https://v4.mytesy.com/devices/set", {
102
+ session_token: this.session,
103
+ device_id: this.device_id,
104
+ command: "target_temperature",
105
+ value: value,
106
+ });
107
+ callback(null, value);
108
+ } catch (error) {
109
+ this.log.error("Failed to set target temperature:", error.response?.data || error.message);
110
+ callback(error);
111
+ }
112
+ }
113
+
114
+ getServices() {
115
+ this.informationService = new Service.AccessoryInformation();
116
+ this.informationService
117
+ .setCharacteristic(Characteristic.Manufacturer, this.manufacturer)
118
+ .setCharacteristic(Characteristic.Model, this.model)
119
+ .setCharacteristic(Characteristic.SerialNumber, this.device_id);
120
+
121
+ this.service.getCharacteristic(Characteristic.Active)
122
+ .on("set", this.setActive.bind(this));
123
+
124
+ this.service.getCharacteristic(Characteristic.CurrentTemperature)
125
+ .setProps({ minStep: 0.1 });
126
+
127
+ this.service.getCharacteristic(Characteristic.HeatingThresholdTemperature)
128
+ .setProps({
129
+ minValue: this.minTemp,
130
+ maxValue: this.maxTemp,
131
+ minStep: 0.5,
132
+ })
133
+ .on("set", this.setHeatingThresholdTemperature.bind(this));
134
+
135
+ return [this.informationService, this.service];
136
+ }
137
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "homebridge-tesy-heater-api-v4",
3
- "version": "0.0.3",
3
+ "version": "0.0.4",
4
4
  "description": "Homebridge plugin for Tesy Heater (updated APIv4)",
5
5
  "main": "index.js",
6
6
  "keywords": [