homebridge-smarthq-client 1.2.5-debug.9 → 1.2.5-test.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.
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { PlatformAccessory, Service, Characteristic } from 'homebridge';
|
|
2
|
-
import { DeviceService
|
|
2
|
+
import { DeviceService } from 'ge-smarthq-api';
|
|
3
3
|
import { SmartHqPlatform } from '../platform.js';
|
|
4
4
|
export declare class AirConditioner {
|
|
5
5
|
private readonly platform;
|
|
@@ -8,7 +8,7 @@ export declare class AirConditioner {
|
|
|
8
8
|
private readonly deviceId;
|
|
9
9
|
private readonly groupAccessory;
|
|
10
10
|
private lastActiveMode;
|
|
11
|
-
private
|
|
11
|
+
private lastActiveFanSpeed;
|
|
12
12
|
private lastActiveCelsius;
|
|
13
13
|
private isOn;
|
|
14
14
|
private physicalOnState;
|
|
@@ -17,20 +17,6 @@ export declare class AirConditioner {
|
|
|
17
17
|
private currentAmbientCelsius;
|
|
18
18
|
private coolCelsiusMin;
|
|
19
19
|
private coolCelsiusMax;
|
|
20
|
-
private readonly MODE_COOL;
|
|
21
|
-
private readonly MODE_FANONLY;
|
|
22
|
-
private readonly MODE_DRY;
|
|
23
|
-
private readonly MODE_ENERGY_SAVER;
|
|
24
|
-
private readonly MODE_COOL_QUIET;
|
|
25
|
-
private readonly MODE_COOL_TURBO;
|
|
26
|
-
private readonly MODE_HEAT;
|
|
27
|
-
private readonly MODE_OFF;
|
|
28
|
-
private readonly MODE_ON;
|
|
29
|
-
private readonly FAN_SPEED_LOW;
|
|
30
|
-
private readonly FAN_SPEED_MEDIUM;
|
|
31
|
-
private readonly FAN_SPEED_HIGH;
|
|
32
|
-
private readonly FAN_SPEED_AUTO;
|
|
33
|
-
private readonly FAN_SPEED_OFF;
|
|
34
20
|
private client;
|
|
35
21
|
private api;
|
|
36
22
|
Service: typeof Service;
|
|
@@ -44,11 +30,10 @@ export declare class AirConditioner {
|
|
|
44
30
|
constructor(platform: SmartHqPlatform, accessory: PlatformAccessory, // Parent: Air Conditioner
|
|
45
31
|
deviceServices: DeviceService[], deviceId: string, groupAccessory: PlatformAccessory[]);
|
|
46
32
|
private setupAccessories;
|
|
47
|
-
sendCommand
|
|
33
|
+
private sendCommand;
|
|
34
|
+
private executeSendCommand;
|
|
48
35
|
private handleUpdate;
|
|
49
36
|
private findService;
|
|
50
37
|
private getLastElementAndCapitalize;
|
|
51
38
|
private setupWebSocket;
|
|
52
|
-
private addDebugState;
|
|
53
|
-
setupService(serviceType: string, displayName: string, serviceIdSuffix: string): Service;
|
|
54
39
|
}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { SmartHQClient } from 'ge-smarthq-api';
|
|
2
|
-
import chalk from 'chalk';
|
|
3
2
|
export class AirConditioner {
|
|
4
3
|
platform;
|
|
5
4
|
accessory;
|
|
@@ -8,7 +7,7 @@ export class AirConditioner {
|
|
|
8
7
|
groupAccessory;
|
|
9
8
|
// State cache
|
|
10
9
|
lastActiveMode = 'cloud.smarthq.type.thermostatmode.cool';
|
|
11
|
-
|
|
10
|
+
lastActiveFanSpeed = 'cloud.smarthq.type.fanspeed.low';
|
|
12
11
|
lastActiveCelsius = 22.22;
|
|
13
12
|
isOn = false;
|
|
14
13
|
physicalOnState = false;
|
|
@@ -18,22 +17,6 @@ export class AirConditioner {
|
|
|
18
17
|
// Configuration thresholds
|
|
19
18
|
coolCelsiusMin = 17.77;
|
|
20
19
|
coolCelsiusMax = 30.0;
|
|
21
|
-
//======== Thermostat mode constants ========
|
|
22
|
-
MODE_COOL = "cloud.smarthq.type.thermostatmode.cool";
|
|
23
|
-
MODE_FANONLY = "cloud.smarthq.type.thermostatmode.fanonly";
|
|
24
|
-
MODE_DRY = "cloud.smarthq.type.thermostatmode.dry";
|
|
25
|
-
MODE_ENERGY_SAVER = "cloud.smarthq.type.thermostatmode.cool.energysaver";
|
|
26
|
-
MODE_COOL_QUIET = "cloud.smarthq.type.thermostatmode.cool.quiet";
|
|
27
|
-
MODE_COOL_TURBO = "cloud.smarthq.type.thermostatmode.cool.turbo";
|
|
28
|
-
MODE_HEAT = "cloud.smarthq.type.thermostatmode.heat";
|
|
29
|
-
MODE_OFF = "cloud.smarthq.type.thermostatmode.off";
|
|
30
|
-
MODE_ON = "cloud.smarthq.type.thermostatmode.on";
|
|
31
|
-
//======== Fan speed constants ========
|
|
32
|
-
FAN_SPEED_LOW = "cloud.smarthq.type.fanspeed.low";
|
|
33
|
-
FAN_SPEED_MEDIUM = "cloud.smarthq.type.fanspeed.medium";
|
|
34
|
-
FAN_SPEED_HIGH = "cloud.smarthq.type.fanspeed.high";
|
|
35
|
-
FAN_SPEED_AUTO = "cloud.smarthq.type.fanspeed.auto";
|
|
36
|
-
FAN_SPEED_OFF = "cloud.smarthq.type.fanspeed.off";
|
|
37
20
|
client;
|
|
38
21
|
api;
|
|
39
22
|
Service;
|
|
@@ -79,7 +62,7 @@ export class AirConditioner {
|
|
|
79
62
|
this.lastActiveMode = thermostatService.state.mode;
|
|
80
63
|
}
|
|
81
64
|
if (thermostatService.state.fanSpeed != null) {
|
|
82
|
-
this.
|
|
65
|
+
this.lastActiveFanSpeed = thermostatService.state.fanSpeed;
|
|
83
66
|
}
|
|
84
67
|
}
|
|
85
68
|
// Load Celsius min/max bounds from thermostat config
|
|
@@ -145,31 +128,14 @@ export class AirConditioner {
|
|
|
145
128
|
.onSet(async (value) => {
|
|
146
129
|
this.isOn = value === 1;
|
|
147
130
|
if (this.isOn) {
|
|
148
|
-
const stateVal = this.lastActiveMode ===
|
|
131
|
+
const stateVal = this.lastActiveMode === 'cloud.smarthq.type.thermostatmode.fanonly' ? 1 : 3;
|
|
149
132
|
this.acThermostat.updateCharacteristic(this.Characteristic.CurrentHeaterCoolerState, stateVal);
|
|
150
133
|
for (const [mode, service] of this.modeOutlets.entries()) {
|
|
151
134
|
service.updateCharacteristic(this.Characteristic.On, this.lastActiveMode === mode);
|
|
152
135
|
}
|
|
153
136
|
for (const [fanSpeed, service] of this.fanOutlets.entries()) {
|
|
154
|
-
service.updateCharacteristic(this.Characteristic.On, this.
|
|
137
|
+
service.updateCharacteristic(this.Characteristic.On, this.lastActiveFanSpeed === fanSpeed);
|
|
155
138
|
}
|
|
156
|
-
// Turned on, so set mode, fan speed and temperature to last active values. Default to Cool and Low and 22.22C if no last active values.
|
|
157
|
-
const cmdBody = {
|
|
158
|
-
command: {
|
|
159
|
-
mode: this.lastActiveMode || this.MODE_COOL,
|
|
160
|
-
fanSpeed: this.lastActiveFanSpeedMode || this.FAN_SPEED_LOW,
|
|
161
|
-
temperature: this.lastActiveCelsius || 22.22,
|
|
162
|
-
commandType: 'cloud.smarthq.command.thermostat.v1.set',
|
|
163
|
-
},
|
|
164
|
-
kind: 'service#command',
|
|
165
|
-
deviceId: this.deviceId,
|
|
166
|
-
serviceDeviceType: 'cloud.smarthq.device.airconditioner',
|
|
167
|
-
serviceType: 'cloud.smarthq.service.thermostat.v1',
|
|
168
|
-
domainType: 'cloud.smarthq.domain.thermostat',
|
|
169
|
-
};
|
|
170
|
-
this.client.debug(chalk.yellow(`## Turning on AC:`));
|
|
171
|
-
this.client.debug(chalk.yellow(JSON.stringify(cmdBody, null, 2)));
|
|
172
|
-
this.sendCommand(cmdBody);
|
|
173
139
|
}
|
|
174
140
|
else {
|
|
175
141
|
this.acThermostat.updateCharacteristic(this.Characteristic.CurrentHeaterCoolerState, 0);
|
|
@@ -179,29 +145,15 @@ export class AirConditioner {
|
|
|
179
145
|
for (const service of this.fanOutlets.values()) {
|
|
180
146
|
service.updateCharacteristic(this.Characteristic.On, false);
|
|
181
147
|
}
|
|
182
|
-
// Turned off, so send command to power down AC unit. Note: GE firmware does not support a "power off" command, so we just send the "on" parameter as false to turn off the unit.
|
|
183
|
-
const cmdBody = {
|
|
184
|
-
command: {
|
|
185
|
-
on: this.isOn,
|
|
186
|
-
commandType: 'cloud.smarthq.command.thermostat.v1.set',
|
|
187
|
-
},
|
|
188
|
-
kind: 'service#command',
|
|
189
|
-
deviceId: this.deviceId,
|
|
190
|
-
serviceDeviceType: 'cloud.smarthq.device.airconditioner',
|
|
191
|
-
serviceType: 'cloud.smarthq.service.thermostat.v1',
|
|
192
|
-
domainType: 'cloud.smarthq.domain.thermostat',
|
|
193
|
-
};
|
|
194
|
-
this.client.debug(chalk.yellow(`## Turning off AC:`));
|
|
195
|
-
this.client.debug(chalk.yellow(JSON.stringify(cmdBody, null, 2)));
|
|
196
|
-
this.sendCommand(cmdBody);
|
|
197
148
|
}
|
|
149
|
+
this.sendCommand();
|
|
198
150
|
});
|
|
199
151
|
this.acThermostat
|
|
200
152
|
.getCharacteristic(this.Characteristic.CurrentHeaterCoolerState)
|
|
201
153
|
.onGet(() => {
|
|
202
154
|
if (!this.isOn)
|
|
203
155
|
return 0;
|
|
204
|
-
return this.lastActiveMode ===
|
|
156
|
+
return this.lastActiveMode === 'cloud.smarthq.type.thermostatmode.fanonly' ? 1 : 3;
|
|
205
157
|
});
|
|
206
158
|
this.acThermostat
|
|
207
159
|
.getCharacteristic(this.Characteristic.TargetHeaterCoolerState)
|
|
@@ -220,24 +172,7 @@ export class AirConditioner {
|
|
|
220
172
|
.onGet(() => this.lastActiveCelsius)
|
|
221
173
|
.onSet(async (value) => {
|
|
222
174
|
this.lastActiveCelsius = value;
|
|
223
|
-
|
|
224
|
-
const clippedCelsius = Math.max(this.coolCelsiusMin, Math.min(this.coolCelsiusMax, this.lastActiveCelsius));
|
|
225
|
-
const coolFahrenheit = Math.round(clippedCelsius * 1.8 + 32);
|
|
226
|
-
// Set temperature on the AC unit via API command
|
|
227
|
-
const cmdBody = {
|
|
228
|
-
command: {
|
|
229
|
-
coolFahrenheit: coolFahrenheit,
|
|
230
|
-
commandType: 'cloud.smarthq.command.thermostat.v1.set',
|
|
231
|
-
},
|
|
232
|
-
kind: 'service#command',
|
|
233
|
-
deviceId: this.deviceId,
|
|
234
|
-
serviceDeviceType: 'cloud.smarthq.device.airconditioner',
|
|
235
|
-
serviceType: 'cloud.smarthq.service.thermostat.v1',
|
|
236
|
-
domainType: 'cloud.smarthq.domain.thermostat',
|
|
237
|
-
};
|
|
238
|
-
this.client.debug(chalk.yellow('## Setting AC Temperature:'));
|
|
239
|
-
this.client.debug(chalk.yellow(JSON.stringify(cmdBody, null, 2)));
|
|
240
|
-
this.sendCommand(cmdBody);
|
|
175
|
+
this.sendCommand();
|
|
241
176
|
});
|
|
242
177
|
this.acThermostat
|
|
243
178
|
.getCharacteristic(this.Characteristic.CurrentTemperature)
|
|
@@ -291,61 +226,12 @@ export class AirConditioner {
|
|
|
291
226
|
}
|
|
292
227
|
}
|
|
293
228
|
this.acThermostat.updateCharacteristic(this.Characteristic.Active, 1);
|
|
294
|
-
const stateVal = mode ===
|
|
229
|
+
const stateVal = mode === 'cloud.smarthq.type.thermostatmode.fanonly' ? 1 : 3;
|
|
295
230
|
this.acThermostat.updateCharacteristic(this.Characteristic.CurrentHeaterCoolerState, stateVal);
|
|
296
|
-
for (const [
|
|
297
|
-
service.updateCharacteristic(this.Characteristic.On, this.
|
|
298
|
-
}
|
|
299
|
-
// Handle behavior constraint: Fan Only mode does not support Auto Fan Speed - force Fan Speed to Low
|
|
300
|
-
// Dry mode sets fan speed to Auto, which is valid. Only Fan Only mode needs to be adjusted.
|
|
301
|
-
let cmdBody;
|
|
302
|
-
switch (mode) {
|
|
303
|
-
case this.MODE_FANONLY:
|
|
304
|
-
cmdBody = {
|
|
305
|
-
command: {
|
|
306
|
-
mode: this.MODE_FANONLY,
|
|
307
|
-
fanSpeed: this.FAN_SPEED_LOW,
|
|
308
|
-
commandType: 'cloud.smarthq.command.thermostat.v1.set',
|
|
309
|
-
},
|
|
310
|
-
kind: 'service#command',
|
|
311
|
-
deviceId: this.deviceId,
|
|
312
|
-
serviceDeviceType: 'cloud.smarthq.device.airconditioner',
|
|
313
|
-
serviceType: 'cloud.smarthq.service.thermostat.v1',
|
|
314
|
-
domainType: 'cloud.smarthq.domain.thermostat',
|
|
315
|
-
};
|
|
316
|
-
break;
|
|
317
|
-
case this.MODE_DRY:
|
|
318
|
-
cmdBody = {
|
|
319
|
-
command: {
|
|
320
|
-
mode: this.MODE_DRY,
|
|
321
|
-
// fanSpeed: this.FAN_SPEED_AUTO, set to Auto by default for Dry mode, which is valid
|
|
322
|
-
commandType: 'cloud.smarthq.command.thermostat.v1.set',
|
|
323
|
-
},
|
|
324
|
-
kind: 'service#command',
|
|
325
|
-
deviceId: this.deviceId,
|
|
326
|
-
serviceDeviceType: 'cloud.smarthq.device.airconditioner',
|
|
327
|
-
serviceType: 'cloud.smarthq.service.thermostat.v1',
|
|
328
|
-
domainType: 'cloud.smarthq.domain.thermostat',
|
|
329
|
-
};
|
|
330
|
-
break;
|
|
331
|
-
// Default case handles Cool and Heat modes, which support all(?) fan speeds
|
|
332
|
-
default:
|
|
333
|
-
// Set mode on the AC unit via API command with special handling for Fan Only and Dry modes
|
|
334
|
-
cmdBody = {
|
|
335
|
-
command: {
|
|
336
|
-
mode: mode,
|
|
337
|
-
commandType: 'cloud.smarthq.command.thermostat.v1.set',
|
|
338
|
-
},
|
|
339
|
-
kind: 'service#command',
|
|
340
|
-
deviceId: this.deviceId,
|
|
341
|
-
serviceDeviceType: 'cloud.smarthq.device.airconditioner',
|
|
342
|
-
serviceType: 'cloud.smarthq.service.thermostat.v1',
|
|
343
|
-
domainType: 'cloud.smarthq.domain.thermostat',
|
|
344
|
-
};
|
|
231
|
+
for (const [fanSpeed, service] of this.fanOutlets.entries()) {
|
|
232
|
+
service.updateCharacteristic(this.Characteristic.On, this.lastActiveFanSpeed === fanSpeed);
|
|
345
233
|
}
|
|
346
|
-
this.
|
|
347
|
-
this.client.debug(chalk.yellow(JSON.stringify(cmdBody, null, 2)));
|
|
348
|
-
this.sendCommand(cmdBody);
|
|
234
|
+
this.sendCommand();
|
|
349
235
|
}
|
|
350
236
|
else {
|
|
351
237
|
// Toggling active mode OFF powers down system
|
|
@@ -359,18 +245,7 @@ export class AirConditioner {
|
|
|
359
245
|
}
|
|
360
246
|
this.acThermostat.updateCharacteristic(this.Characteristic.Active, 0);
|
|
361
247
|
this.acThermostat.updateCharacteristic(this.Characteristic.CurrentHeaterCoolerState, 0);
|
|
362
|
-
|
|
363
|
-
command: {
|
|
364
|
-
on: this.isOn,
|
|
365
|
-
commandType: 'cloud.smarthq.command.thermostat.v1.set',
|
|
366
|
-
},
|
|
367
|
-
kind: 'service#command',
|
|
368
|
-
deviceId: this.deviceId,
|
|
369
|
-
serviceDeviceType: 'cloud.smarthq.device.airconditioner',
|
|
370
|
-
serviceType: 'cloud.smarthq.service.thermostat.v1',
|
|
371
|
-
domainType: 'cloud.smarthq.domain.thermostat',
|
|
372
|
-
};
|
|
373
|
-
this.sendCommand(cmdBody);
|
|
248
|
+
this.sendCommand();
|
|
374
249
|
}
|
|
375
250
|
}
|
|
376
251
|
});
|
|
@@ -385,16 +260,16 @@ export class AirConditioner {
|
|
|
385
260
|
.setCharacteristic(this.Characteristic.SerialNumber, this.parentAccessory.context.device.serial || 'Unknown');
|
|
386
261
|
// Clean up stale Fan Speed outlets from cache
|
|
387
262
|
const activeFanServiceUUIDs = new Set();
|
|
388
|
-
for (const
|
|
389
|
-
const [displayName] = this.getLastElementAndCapitalize(
|
|
263
|
+
for (const speed of supportedFanSpeeds) {
|
|
264
|
+
const [displayName] = this.getLastElementAndCapitalize(speed, '.');
|
|
390
265
|
const service = this.fanAccessory.getService(displayName) ||
|
|
391
|
-
this.fanAccessory.addService(this.Service.Outlet, displayName,
|
|
266
|
+
this.fanAccessory.addService(this.Service.Outlet, displayName, speed);
|
|
392
267
|
if (!service.getCharacteristic(this.Characteristic.ConfiguredName)) {
|
|
393
268
|
service.addOptionalCharacteristic(this.Characteristic.ConfiguredName);
|
|
394
269
|
}
|
|
395
270
|
service.setCharacteristic(this.Characteristic.Name, displayName);
|
|
396
271
|
service.setCharacteristic(this.Characteristic.ConfiguredName, displayName);
|
|
397
|
-
activeFanServiceUUIDs.add(service.UUID + (
|
|
272
|
+
activeFanServiceUUIDs.add(service.UUID + (speed || ''));
|
|
398
273
|
}
|
|
399
274
|
for (const service of [...this.fanAccessory.services]) {
|
|
400
275
|
if (service.UUID === this.Service.AccessoryInformation.UUID)
|
|
@@ -408,25 +283,21 @@ export class AirConditioner {
|
|
|
408
283
|
}
|
|
409
284
|
}
|
|
410
285
|
// Bind Fan Speed Outlets characteristics
|
|
411
|
-
for (const
|
|
412
|
-
const [displayName] = this.getLastElementAndCapitalize(
|
|
286
|
+
for (const speed of supportedFanSpeeds) {
|
|
287
|
+
const [displayName] = this.getLastElementAndCapitalize(speed, '.');
|
|
413
288
|
const service = this.fanAccessory.getService(displayName);
|
|
414
|
-
this.fanOutlets.set(
|
|
289
|
+
this.fanOutlets.set(speed, service);
|
|
415
290
|
service
|
|
416
291
|
.getCharacteristic(this.Characteristic.On)
|
|
417
|
-
.onGet(() => this.isOn && this.
|
|
292
|
+
.onGet(() => this.isOn && this.lastActiveFanSpeed === speed)
|
|
418
293
|
.onSet(async (value) => {
|
|
419
294
|
if (value) {
|
|
420
|
-
// If mode is Dry, then fan speed is automatically set to Auto and cannot be changed.
|
|
421
|
-
if (this.lastActiveMode === this.MODE_DRY) {
|
|
422
|
-
return;
|
|
423
|
-
}
|
|
424
295
|
// Handle behavior constraint: Fan Only mode does not support Auto Fan Speed
|
|
425
|
-
if (this.lastActiveMode ===
|
|
426
|
-
|
|
296
|
+
if (this.lastActiveMode === 'cloud.smarthq.type.thermostatmode.fanonly' &&
|
|
297
|
+
speed === 'cloud.smarthq.type.fanspeed.auto') {
|
|
427
298
|
setTimeout(() => {
|
|
428
299
|
service.updateCharacteristic(this.Characteristic.On, false);
|
|
429
|
-
const activeService = this.fanOutlets.get(this.
|
|
300
|
+
const activeService = this.fanOutlets.get(this.lastActiveFanSpeed);
|
|
430
301
|
if (activeService) {
|
|
431
302
|
activeService.updateCharacteristic(this.Characteristic.On, this.isOn);
|
|
432
303
|
}
|
|
@@ -434,38 +305,23 @@ export class AirConditioner {
|
|
|
434
305
|
return;
|
|
435
306
|
}
|
|
436
307
|
this.isOn = true;
|
|
437
|
-
this.
|
|
308
|
+
this.lastActiveFanSpeed = speed;
|
|
438
309
|
for (const [fanSpeed, service] of this.fanOutlets.entries()) {
|
|
439
|
-
if (fanSpeed !==
|
|
310
|
+
if (fanSpeed !== speed) {
|
|
440
311
|
service.updateCharacteristic(this.Characteristic.On, false);
|
|
441
312
|
}
|
|
442
313
|
}
|
|
443
314
|
this.acThermostat.updateCharacteristic(this.Characteristic.Active, 1);
|
|
444
|
-
const stateVal = this.lastActiveMode ===
|
|
315
|
+
const stateVal = this.lastActiveMode === 'cloud.smarthq.type.thermostatmode.fanonly' ? 1 : 3;
|
|
445
316
|
this.acThermostat.updateCharacteristic(this.Characteristic.CurrentHeaterCoolerState, stateVal);
|
|
446
317
|
for (const [modeKey, service] of this.modeOutlets.entries()) {
|
|
447
318
|
service.updateCharacteristic(this.Characteristic.On, this.lastActiveMode === modeKey);
|
|
448
319
|
}
|
|
449
|
-
|
|
450
|
-
command: {
|
|
451
|
-
fanspeed: this.lastActiveFanSpeedMode,
|
|
452
|
-
commandType: 'cloud.smarthq.command.thermostat.v1.set',
|
|
453
|
-
},
|
|
454
|
-
kind: 'service#command',
|
|
455
|
-
deviceId: this.deviceId,
|
|
456
|
-
serviceDeviceType: 'cloud.smarthq.device.airconditioner',
|
|
457
|
-
serviceType: 'cloud.smarthq.service.thermostat.v1',
|
|
458
|
-
domainType: 'cloud.smarthq.domain.thermostat',
|
|
459
|
-
};
|
|
460
|
-
this.client.debug(chalk.yellow('## Setting Fan Speed from mode:' + this.lastActiveMode));
|
|
461
|
-
this.client.debug(chalk.yellow(JSON.stringify(cmdBody, null, 2)));
|
|
462
|
-
this.sendCommand(cmdBody);
|
|
320
|
+
this.sendCommand();
|
|
463
321
|
}
|
|
464
322
|
else {
|
|
465
323
|
// Toggling active fan speed OFF powers down system
|
|
466
|
-
|
|
467
|
-
// note: don't think the else block is ever reached
|
|
468
|
-
if (this.lastActiveFanSpeedMode === fanspeedMode) {
|
|
324
|
+
if (this.lastActiveFanSpeed === speed) {
|
|
469
325
|
this.isOn = false;
|
|
470
326
|
for (const service of this.modeOutlets.values()) {
|
|
471
327
|
service.updateCharacteristic(this.Characteristic.On, false);
|
|
@@ -475,110 +331,76 @@ export class AirConditioner {
|
|
|
475
331
|
}
|
|
476
332
|
this.acThermostat.updateCharacteristic(this.Characteristic.Active, 0);
|
|
477
333
|
this.acThermostat.updateCharacteristic(this.Characteristic.CurrentHeaterCoolerState, 0);
|
|
478
|
-
|
|
479
|
-
command: {
|
|
480
|
-
on: this.isOn,
|
|
481
|
-
commandType: 'cloud.smarthq.command.thermostat.v1.set',
|
|
482
|
-
},
|
|
483
|
-
kind: 'service#command',
|
|
484
|
-
deviceId: this.deviceId,
|
|
485
|
-
serviceDeviceType: 'cloud.smarthq.device.airconditioner',
|
|
486
|
-
serviceType: 'cloud.smarthq.service.thermostat.v1',
|
|
487
|
-
domainType: 'cloud.smarthq.domain.thermostat',
|
|
488
|
-
};
|
|
489
|
-
this.sendCommand(cmdBody);
|
|
334
|
+
this.sendCommand();
|
|
490
335
|
}
|
|
491
336
|
}
|
|
492
337
|
});
|
|
493
338
|
}
|
|
494
339
|
}
|
|
495
|
-
// D. Add Debug State switch to parent accessory
|
|
496
|
-
this.addDebugState('cloud.smarthq.service.thermostat.v1');
|
|
497
340
|
}
|
|
498
341
|
// ---------------------------
|
|
499
342
|
// API COMMAND SENDER
|
|
500
343
|
// ---------------------------
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
344
|
+
sendCommand() {
|
|
345
|
+
if (this.debounceTimeout) {
|
|
346
|
+
clearTimeout(this.debounceTimeout);
|
|
347
|
+
}
|
|
348
|
+
this.debounceTimeout = setTimeout(() => {
|
|
349
|
+
this.debounceTimeout = null;
|
|
350
|
+
this.executeSendCommand();
|
|
351
|
+
}, 150);
|
|
352
|
+
}
|
|
353
|
+
async executeSendCommand() {
|
|
354
|
+
const service = this.findService('cloud.smarthq.service.thermostat.v1', 'cloud.smarthq.domain.thermostat');
|
|
355
|
+
if (!service)
|
|
356
|
+
return;
|
|
357
|
+
this.commandQueue = this.commandQueue.then(async () => {
|
|
358
|
+
const command = {
|
|
359
|
+
commandType: 'cloud.smarthq.command.thermostat.v1.set',
|
|
360
|
+
};
|
|
361
|
+
// Only send the 'on' parameter if it is transitioning (prevents GE firmware from resetting to ECO)
|
|
362
|
+
if (!this.isOn) {
|
|
363
|
+
command.on = false;
|
|
507
364
|
}
|
|
508
|
-
else {
|
|
509
|
-
|
|
510
|
-
return response.success;
|
|
365
|
+
else if (!this.physicalOnState) {
|
|
366
|
+
command.on = true;
|
|
511
367
|
}
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
368
|
+
if (this.isOn) {
|
|
369
|
+
command.mode = this.lastActiveMode;
|
|
370
|
+
// Handle behavior constraint: Fan Only mode does not support Auto Fan Speed
|
|
371
|
+
if (this.lastActiveMode === 'cloud.smarthq.type.thermostatmode.fanonly' &&
|
|
372
|
+
this.lastActiveFanSpeed === 'cloud.smarthq.type.fanspeed.auto') {
|
|
373
|
+
command.fanSpeed = 'cloud.smarthq.type.fanspeed.low';
|
|
374
|
+
}
|
|
375
|
+
else {
|
|
376
|
+
command.fanSpeed = this.lastActiveFanSpeed;
|
|
377
|
+
}
|
|
378
|
+
// Omit coolFahrenheit in Fan Only mode (thermostat has no cooling setpoint)
|
|
379
|
+
if (this.lastActiveMode !== 'cloud.smarthq.type.thermostatmode.fanonly') {
|
|
380
|
+
// Clip celsius to hardware bounds to prevent API errors
|
|
381
|
+
const clippedCelsius = Math.max(this.coolCelsiusMin, Math.min(this.coolCelsiusMax, this.lastActiveCelsius));
|
|
382
|
+
command.coolFahrenheit = Math.round(clippedCelsius * 1.8 + 32);
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
this.client.debug('## 1.2.5-test.0 -> command = ' + JSON.stringify(command, null, 2));
|
|
386
|
+
try {
|
|
387
|
+
await this.client.sendCommand({
|
|
388
|
+
command,
|
|
389
|
+
kind: 'service#command',
|
|
390
|
+
deviceId: this.deviceId,
|
|
391
|
+
serviceDeviceType: 'cloud.smarthq.device.airconditioner',
|
|
392
|
+
serviceType: 'cloud.smarthq.service.thermostat.v1',
|
|
393
|
+
domainType: 'cloud.smarthq.domain.thermostat',
|
|
394
|
+
});
|
|
395
|
+
}
|
|
396
|
+
catch (error) {
|
|
397
|
+
this.platform.log.error(`Error sending command to AC:`, error);
|
|
398
|
+
}
|
|
399
|
+
}).catch((err) => {
|
|
400
|
+
this.platform.log.error(`Error in AC command queue:`, err);
|
|
401
|
+
});
|
|
402
|
+
return this.commandQueue;
|
|
516
403
|
}
|
|
517
|
-
/*
|
|
518
|
-
private async executeSendCommand(command: SendCommandRequest) {
|
|
519
|
-
const service = this.findService(
|
|
520
|
-
'cloud.smarthq.service.thermostat.v1',
|
|
521
|
-
'cloud.smarthq.domain.thermostat',
|
|
522
|
-
);
|
|
523
|
-
|
|
524
|
-
if (!service) return;
|
|
525
|
-
|
|
526
|
-
//this.commandQueue = this.commandQueue.then(async () => {
|
|
527
|
-
|
|
528
|
-
const command: Record<string, unknown> = {
|
|
529
|
-
commandType: 'cloud.smarthq.command.thermostat.v1.set',
|
|
530
|
-
};
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
// Only send the 'on' parameter if it is transitioning (prevents GE firmware from resetting to ECO)
|
|
534
|
-
if (!this.isOn) {
|
|
535
|
-
command.on = false;
|
|
536
|
-
} else if (!this.physicalOnState) {
|
|
537
|
-
command.on = true;
|
|
538
|
-
}
|
|
539
|
-
|
|
540
|
-
if (this.isOn) {
|
|
541
|
-
command.mode = this.lastActiveMode;
|
|
542
|
-
|
|
543
|
-
// Handle behavior constraint: Fan Only mode does not support Auto Fan Speed
|
|
544
|
-
if (
|
|
545
|
-
this.lastActiveMode === this.MODE_FANONLY &&
|
|
546
|
-
this.lastActiveFanSpeedMode === this.FAN_SPEED_AUTO
|
|
547
|
-
) {
|
|
548
|
-
command.fanSpeed = this.FAN_SPEED_LOW;
|
|
549
|
-
} else {
|
|
550
|
-
command.fanSpeed = this.lastActiveFanSpeedMode;
|
|
551
|
-
}
|
|
552
|
-
|
|
553
|
-
// Omit coolFahrenheit in Fan Only mode (thermostat has no cooling setpoint)
|
|
554
|
-
if (this.lastActiveMode !== this.MODE_FANONLY) {
|
|
555
|
-
// Clip celsius to hardware bounds to prevent API errors
|
|
556
|
-
const clippedCelsius = Math.max(
|
|
557
|
-
this.coolCelsiusMin,
|
|
558
|
-
Math.min(this.coolCelsiusMax, this.lastActiveCelsius),
|
|
559
|
-
);
|
|
560
|
-
command.coolFahrenheit = Math.round(clippedCelsius * 1.8 + 32);
|
|
561
|
-
}
|
|
562
|
-
}
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
try {
|
|
566
|
-
const response = await this.client.sendCommand(command);
|
|
567
|
-
|
|
568
|
-
if (response == null) {
|
|
569
|
-
this.client.debug("No response from send command");
|
|
570
|
-
} else {
|
|
571
|
-
this.client.debug(
|
|
572
|
-
"#### Response from send command : " + response.outcome,
|
|
573
|
-
);
|
|
574
|
-
return response.success;
|
|
575
|
-
}
|
|
576
|
-
} catch (error) {
|
|
577
|
-
this.platform.log.error(`Error sending command to AC:`, error);
|
|
578
|
-
}
|
|
579
|
-
|
|
580
|
-
return true;
|
|
581
|
-
}*/
|
|
582
404
|
// ---------------------------
|
|
583
405
|
// WEBSOCKET UPDATES HANDLING
|
|
584
406
|
// ---------------------------
|
|
@@ -600,19 +422,19 @@ export class AirConditioner {
|
|
|
600
422
|
this.lastActiveMode = state.mode;
|
|
601
423
|
}
|
|
602
424
|
if (state.fanSpeed !== undefined) {
|
|
603
|
-
this.
|
|
425
|
+
this.lastActiveFanSpeed = state.fanSpeed;
|
|
604
426
|
}
|
|
605
427
|
// Update HeaterCooler state dynamically based on power and mode
|
|
606
428
|
const stateVal = !this.isOn
|
|
607
429
|
? 0
|
|
608
|
-
: (this.lastActiveMode ===
|
|
430
|
+
: (this.lastActiveMode === 'cloud.smarthq.type.thermostatmode.fanonly' ? 1 : 3);
|
|
609
431
|
this.acThermostat.updateCharacteristic(this.Characteristic.CurrentHeaterCoolerState, stateVal);
|
|
610
432
|
// Update dynamic outlets
|
|
611
433
|
for (const [mode, service] of this.modeOutlets.entries()) {
|
|
612
434
|
service.updateCharacteristic(this.Characteristic.On, this.isOn && this.lastActiveMode === mode);
|
|
613
435
|
}
|
|
614
436
|
for (const [fanSpeed, service] of this.fanOutlets.entries()) {
|
|
615
|
-
service.updateCharacteristic(this.Characteristic.On, this.isOn && this.
|
|
437
|
+
service.updateCharacteristic(this.Characteristic.On, this.isOn && this.lastActiveFanSpeed === fanSpeed);
|
|
616
438
|
}
|
|
617
439
|
}
|
|
618
440
|
else if (message.domainType === 'cloud.smarthq.domain.indoor.ambient') {
|
|
@@ -648,55 +470,5 @@ export class AirConditioner {
|
|
|
648
470
|
this.platform.log.error(`Failed to connect to SmartHQ WebSocket for AC ${this.deviceId}:`, error);
|
|
649
471
|
}
|
|
650
472
|
}
|
|
651
|
-
// ---------------------------
|
|
652
|
-
// HELPERS FOR DEBUGGING
|
|
653
|
-
// Adds a 'Debug State' switch to the parent accessory that logs the current state of the specified service type when toggled on
|
|
654
|
-
// ---------------------------
|
|
655
|
-
addDebugState(serviceType) {
|
|
656
|
-
const debugState = this.setupService('Switch', 'Debug State', `${this.deviceId}-debug-state`);
|
|
657
|
-
debugState
|
|
658
|
-
.getCharacteristic(this.Characteristic.On)
|
|
659
|
-
.onGet(() => {
|
|
660
|
-
return debugState.getCharacteristic(this.Characteristic.On).value;
|
|
661
|
-
})
|
|
662
|
-
.onSet(async (value) => {
|
|
663
|
-
if (value) {
|
|
664
|
-
const response = await this.client.getDevice(this.deviceId);
|
|
665
|
-
const deviceServices = response.services ?? [];
|
|
666
|
-
for (const service of deviceServices) {
|
|
667
|
-
if (service.serviceType === serviceType) {
|
|
668
|
-
this.client.debug('Debug State - Device Info: ' + JSON.stringify(service, null, 2));
|
|
669
|
-
}
|
|
670
|
-
}
|
|
671
|
-
setTimeout(() => {
|
|
672
|
-
debugState.updateCharacteristic(this.Characteristic.On, false);
|
|
673
|
-
}, 1000);
|
|
674
|
-
}
|
|
675
|
-
});
|
|
676
|
-
}
|
|
677
|
-
setupService(serviceType, displayName, serviceIdSuffix) {
|
|
678
|
-
let service;
|
|
679
|
-
switch (serviceType) {
|
|
680
|
-
case 'Outlet':
|
|
681
|
-
service =
|
|
682
|
-
this.accessory.getService(displayName) ||
|
|
683
|
-
this.accessory.addService(this.Service.Outlet, displayName, serviceIdSuffix);
|
|
684
|
-
break;
|
|
685
|
-
case 'Switch':
|
|
686
|
-
service =
|
|
687
|
-
this.accessory.getService(displayName) ||
|
|
688
|
-
this.accessory.addService(this.Service.Switch, displayName, serviceIdSuffix);
|
|
689
|
-
break;
|
|
690
|
-
default:
|
|
691
|
-
this.client.debug('Unknown service type: ' + serviceType + '');
|
|
692
|
-
service =
|
|
693
|
-
this.accessory.getService(displayName) ||
|
|
694
|
-
this.accessory.addService(this.Service.Fan, displayName, serviceIdSuffix);
|
|
695
|
-
}
|
|
696
|
-
service.setCharacteristic(this.Characteristic.Name, displayName);
|
|
697
|
-
service.addOptionalCharacteristic(this.Characteristic.ConfiguredName);
|
|
698
|
-
service.setCharacteristic(this.Characteristic.ConfiguredName, displayName);
|
|
699
|
-
return service;
|
|
700
|
-
}
|
|
701
473
|
}
|
|
702
474
|
//# sourceMappingURL=airConditioner.js.map
|