homebridge-smarthq-client 1.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/LICENSE +21 -0
- package/Readme.md +138 -0
- package/config.schema.json +146 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +9 -0
- package/dist/index.js.map +1 -0
- package/dist/platform.d.ts +40 -0
- package/dist/platform.js +243 -0
- package/dist/platform.js.map +1 -0
- package/dist/refrigerator/controlLock.d.ts +19 -0
- package/dist/refrigerator/controlLock.js +85 -0
- package/dist/refrigerator/controlLock.js.map +1 -0
- package/dist/refrigerator/convertibleDrawer.d.ts +27 -0
- package/dist/refrigerator/convertibleDrawer.js +317 -0
- package/dist/refrigerator/convertibleDrawer.js.map +1 -0
- package/dist/refrigerator/dispenserLight.d.ts +19 -0
- package/dist/refrigerator/dispenserLight.js +85 -0
- package/dist/refrigerator/dispenserLight.js.map +1 -0
- package/dist/refrigerator/energy.d.ts +19 -0
- package/dist/refrigerator/energy.js +93 -0
- package/dist/refrigerator/energy.js.map +1 -0
- package/dist/refrigerator/freezer.d.ts +41 -0
- package/dist/refrigerator/freezer.js +188 -0
- package/dist/refrigerator/freezer.js.map +1 -0
- package/dist/refrigerator/iceMaker.d.ts +19 -0
- package/dist/refrigerator/iceMaker.js +81 -0
- package/dist/refrigerator/iceMaker.js.map +1 -0
- package/dist/refrigerator/interiorLight.d.ts +21 -0
- package/dist/refrigerator/interiorLight.js +100 -0
- package/dist/refrigerator/interiorLight.js.map +1 -0
- package/dist/refrigerator/refrigerator.d.ts +41 -0
- package/dist/refrigerator/refrigerator.js +204 -0
- package/dist/refrigerator/refrigerator.js.map +1 -0
- package/dist/refrigerator/refrigeratorAlerts.d.ts +36 -0
- package/dist/refrigerator/refrigeratorAlerts.js +204 -0
- package/dist/refrigerator/refrigeratorAlerts.js.map +1 -0
- package/dist/refrigerator/sabbathMode.d.ts +20 -0
- package/dist/refrigerator/sabbathMode.js +91 -0
- package/dist/refrigerator/sabbathMode.js.map +1 -0
- package/dist/refrigerator/temperatureUnits.d.ts +21 -0
- package/dist/refrigerator/temperatureUnits.js +147 -0
- package/dist/refrigerator/temperatureUnits.js.map +1 -0
- package/dist/refrigerator/turboCoolMode.d.ts +23 -0
- package/dist/refrigerator/turboCoolMode.js +134 -0
- package/dist/refrigerator/turboCoolMode.js.map +1 -0
- package/dist/refrigerator/waterFilter.d.ts +19 -0
- package/dist/refrigerator/waterFilter.js +89 -0
- package/dist/refrigerator/waterFilter.js.map +1 -0
- package/dist/refrigeratorServices.d.ts +5 -0
- package/dist/refrigeratorServices.js +54 -0
- package/dist/refrigeratorServices.js.map +1 -0
- package/dist/settings.d.ts +12 -0
- package/dist/settings.js +13 -0
- package/dist/settings.js.map +1 -0
- package/dist/smartHqApi.d.ts +29 -0
- package/dist/smartHqApi.js +267 -0
- package/dist/smartHqApi.js.map +1 -0
- package/dist/smarthq-types.d.ts +29 -0
- package/dist/smarthq-types.js +2 -0
- package/dist/smarthq-types.js.map +1 -0
- package/eslint.config.js +12 -0
- package/images/homebridge.svg +1 -0
- package/package.json +61 -0
- package/src/@types/homebridge-lib.d.ts +14 -0
- package/src/index.ts +11 -0
- package/src/platform.ts +300 -0
- package/src/refrigerator/controlLock.ts +103 -0
- package/src/refrigerator/convertibleDrawer.ts +381 -0
- package/src/refrigerator/dispenserLight.ts +103 -0
- package/src/refrigerator/energy.ts +106 -0
- package/src/refrigerator/freezer.ts +227 -0
- package/src/refrigerator/iceMaker.ts +100 -0
- package/src/refrigerator/interiorLight.ts +118 -0
- package/src/refrigerator/refrigerator.ts +241 -0
- package/src/refrigerator/refrigeratorAlerts.ts +228 -0
- package/src/refrigerator/sabbathMode.ts +111 -0
- package/src/refrigerator/temperatureUnits.ts +178 -0
- package/src/refrigerator/turboCoolMode.ts +164 -0
- package/src/refrigerator/waterFilter.ts +107 -0
- package/src/refrigeratorServices.ts +60 -0
- package/src/settings.ts +14 -0
- package/src/smartHqApi.ts +305 -0
- package/src/smarthq-types.ts +31 -0
- package/tsconfig.json +24 -0
package/src/platform.ts
ADDED
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
|
|
2
|
+
import { API, Logging, PlatformConfig, PlatformAccessory, Service, Characteristic } from 'homebridge';
|
|
3
|
+
import chalk from 'chalk';
|
|
4
|
+
import express from 'express';
|
|
5
|
+
import fs from 'fs';
|
|
6
|
+
import path from 'path';
|
|
7
|
+
import { stringify, parse } from 'querystring';
|
|
8
|
+
import { SmartHqApi } from './smartHqApi.js';
|
|
9
|
+
import { EventEmitter } from 'node:events';
|
|
10
|
+
import { PLATFORM_NAME, PLUGIN_NAME, AUTH_URL, TOKEN_STORE } from './settings.js';
|
|
11
|
+
import { setupRefrigeratorServices } from './refrigeratorServices.js';
|
|
12
|
+
import { DevDevice, DevService } from './smarthq-types.js';
|
|
13
|
+
import { Server } from 'node:http';
|
|
14
|
+
|
|
15
|
+
export class SmartHqPlatform {
|
|
16
|
+
public readonly Service: typeof Service;
|
|
17
|
+
public readonly Characteristic: typeof Characteristic;
|
|
18
|
+
|
|
19
|
+
// this is used to track restored cached accessories
|
|
20
|
+
public readonly accessories: Map<string, PlatformAccessory> = new Map();
|
|
21
|
+
public readonly discoveredCacheUUIDs: string[] = [];
|
|
22
|
+
public readonly tokenPath: string;
|
|
23
|
+
private oauthServer?: Server;
|
|
24
|
+
public expires: number;
|
|
25
|
+
public readonly smartHqApi: SmartHqApi;
|
|
26
|
+
public authEmitter: EventEmitter
|
|
27
|
+
public serviceDeviceType: string = ''
|
|
28
|
+
public serviceType: string = ''
|
|
29
|
+
|
|
30
|
+
constructor(
|
|
31
|
+
public readonly log: Logging,
|
|
32
|
+
public readonly config: PlatformConfig,
|
|
33
|
+
private readonly api: API
|
|
34
|
+
) {
|
|
35
|
+
this.api = api;
|
|
36
|
+
this.Service = api.hap.Service;
|
|
37
|
+
this.Characteristic = api.hap.Characteristic;
|
|
38
|
+
this.expires = 0;
|
|
39
|
+
|
|
40
|
+
this.tokenPath = path.join(
|
|
41
|
+
this.api.user.persistPath(),
|
|
42
|
+
TOKEN_STORE
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
this.authEmitter = new EventEmitter();
|
|
47
|
+
|
|
48
|
+
chalk.level = 1; // Enable chalk colors
|
|
49
|
+
|
|
50
|
+
this.smartHqApi = new SmartHqApi(this);
|
|
51
|
+
|
|
52
|
+
// Validate required configuration parameters
|
|
53
|
+
if (!config.clientId || !config.clientSecret || !config.redirectUri) {
|
|
54
|
+
this.log.error('Missing required config parameter.');
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
this.api.on('didFinishLaunching', async () => {
|
|
59
|
+
try {
|
|
60
|
+
this.debug('red', '(SmartHQ OAuth2 authentication starting)');
|
|
61
|
+
await this.startOAuth();
|
|
62
|
+
this.debug('blue', '(SmartHQ OAuth2 authentication completed)');
|
|
63
|
+
} catch (error) {
|
|
64
|
+
this.log.error(chalk.red('SmartHQ OAuth2 authentication failed:'), error);
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
// Listen for authComplete event to start device discovery
|
|
69
|
+
this.authEmitter.on('authComplete', async () => {
|
|
70
|
+
this.debug('green', 'Auth complete event received, starting device discovery');
|
|
71
|
+
await this.discoverDevices();
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**================================================================
|
|
76
|
+
* If no token file exists, start the OAuth process.
|
|
77
|
+
* In this process, we start a local express server to handle the redirect
|
|
78
|
+
* from the SmartHQ authorization endpoint.
|
|
79
|
+
* When the user clicks the link in Homebridge UI, they will be directed to the /login route
|
|
80
|
+
* which will redirect to SmartHQ authorization endpoint to obtain authorization code.
|
|
81
|
+
* After user login, SmartHQ authorization endpoint will redirect to whatever url:port/path was configured
|
|
82
|
+
* with authorization code in the query parameter.
|
|
83
|
+
* We will use this code to obtain access token and refresh token
|
|
84
|
+
*================================================================*/
|
|
85
|
+
|
|
86
|
+
async startOAuth() {
|
|
87
|
+
const path: string = this.tokenPath;
|
|
88
|
+
|
|
89
|
+
if (!fs.existsSync(path)) {
|
|
90
|
+
this.debug('blue', '=== file does not exist starting OAuth process ===');
|
|
91
|
+
this.debug('red', ' Starting localhost server to handle OAuth redirects');
|
|
92
|
+
} else {
|
|
93
|
+
this.debug('red', ' Returning because token path exists, emitting authComplete');
|
|
94
|
+
this.authEmitter.emit('authComplete');
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const url = new URL(this.config.redirectUri);
|
|
99
|
+
const pathname: string = url.pathname;
|
|
100
|
+
const port: number = parseInt(url.port, 10);
|
|
101
|
+
this.debug('blue', ' Redirect URI port: ' + port + ' , ' + pathname);
|
|
102
|
+
|
|
103
|
+
const app = express();
|
|
104
|
+
|
|
105
|
+
// By clicking on the link in Homebridge UI (Logs), user will be directed to the /login route
|
|
106
|
+
// which will redirect to SmartHQ authorization endpoint to obtain authorization code
|
|
107
|
+
|
|
108
|
+
this.log.info(chalk.blue("======================================================================="));
|
|
109
|
+
this.log.info("Click to login for SmartHQ Auth setup ===>: " + chalk.red("http://localhost:" + port + "/login"));
|
|
110
|
+
this.log.info(chalk.blue("======================================================================="));
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
// Clicking above link in Homebridge UI will redirect to SmartHQ authorization endpoint
|
|
114
|
+
|
|
115
|
+
app.get('/login', async (_req, res) => {
|
|
116
|
+
|
|
117
|
+
try {
|
|
118
|
+
|
|
119
|
+
const url = AUTH_URL + '?' + stringify({
|
|
120
|
+
response_type: 'code',
|
|
121
|
+
client_id: this.config.clientId,
|
|
122
|
+
redirect_uri: this.config.redirectUri
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
this.debug('blue', "Redirecting to: " + url);
|
|
126
|
+
res.redirect(url); ///====> Redirect to SmartHQ authorization endpoint
|
|
127
|
+
|
|
128
|
+
} catch (error) {
|
|
129
|
+
this.log.error('SmartHQ /login error:', error);
|
|
130
|
+
res.status(500).send('Authentication /login failed: ' + error);};
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
// After user login, SmartHQ authorization endpoint will redirect to whatever url:port/path was configured
|
|
134
|
+
// with authorization code in the query parameter.
|
|
135
|
+
// We will use this code to obtain access token and refresh token
|
|
136
|
+
|
|
137
|
+
app.get(pathname, async (req, res) => {
|
|
138
|
+
try {
|
|
139
|
+
// Parse the request URL
|
|
140
|
+
const query = parse(req.url!.split('?')[1]);
|
|
141
|
+
|
|
142
|
+
const code = query.code?.toString() || '';
|
|
143
|
+
this.debug('blue', 'An authorization code was returned: ' + code);
|
|
144
|
+
this.debug('blue', 'Now exchanging code for access token...');
|
|
145
|
+
if (!code) {
|
|
146
|
+
this.log.error('No code found in callback URL for SmartHQ OAuth');
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
await this.smartHqApi.exchangeCodeForToken(code);
|
|
150
|
+
|
|
151
|
+
res.send(`
|
|
152
|
+
<h2>SmartHQ Connected</h2>
|
|
153
|
+
<p>Authorization token saved to file.</p>
|
|
154
|
+
<p>You may close this window.</p>
|
|
155
|
+
`);
|
|
156
|
+
// Emit event to indicate authentication is complete
|
|
157
|
+
this.authEmitter.emit('authComplete');
|
|
158
|
+
|
|
159
|
+
this.log.success('SmartHQ authentication completed');
|
|
160
|
+
|
|
161
|
+
setTimeout(() => this.oauthServer?.close(), 1000);
|
|
162
|
+
this.debug('blue', 'OAuth server has been closed.');
|
|
163
|
+
return;
|
|
164
|
+
|
|
165
|
+
} catch (error) {
|
|
166
|
+
this.log.error('SmartHQ OAuth failed', error);
|
|
167
|
+
res.status(500).send('Authentication failed: ' + error);
|
|
168
|
+
}
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
this.oauthServer = app.listen(port, () => {
|
|
172
|
+
this.log.info(`SmartHQ OAuth listening on ${port}`);
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// Extract port and pathname from redirect URI
|
|
177
|
+
|
|
178
|
+
extractPortFromRedirectUri(redirectUri: string): { port: number; pathname: string } {
|
|
179
|
+
const url = new URL(redirectUri);
|
|
180
|
+
const pathname: string = url.pathname;
|
|
181
|
+
const port: number = parseInt(url.port, 10);
|
|
182
|
+
return { port, pathname };
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
configureAccessory(accessory: PlatformAccessory) {
|
|
187
|
+
this.log.info('Loading accessory from cache:', accessory.displayName);
|
|
188
|
+
|
|
189
|
+
// add the restored accessory to the accessories cache, so we can track if it has already been registered
|
|
190
|
+
this.accessories.set(accessory.UUID, accessory);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
private async discoverDevices() {
|
|
195
|
+
this.debug('yellow', '(discoverDevices) Starting device discovery...');
|
|
196
|
+
let devices: DevDevice[] = [];
|
|
197
|
+
devices = await this.smartHqApi.getAppliances();
|
|
198
|
+
|
|
199
|
+
// loop over the discovered devices and register each one if it has not already been registered
|
|
200
|
+
|
|
201
|
+
if (!devices || devices.length === 0) {
|
|
202
|
+
this.log.warn(chalk.yellow('No SmartHQ devices found for this account.'));
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
for (const device of devices) {
|
|
206
|
+
this.log.info(chalk.yellow(`SmartHQ Discovered device: ${device.nickname} Model: ${device.model}`));
|
|
207
|
+
|
|
208
|
+
// Used to acquire service IDs and service deviceTypes for deviceServiceState queries
|
|
209
|
+
const deviceServices = await this.smartHqApi.getDeviceServices(device.deviceId);
|
|
210
|
+
|
|
211
|
+
let accessoryType: PlatformAccessory | undefined;
|
|
212
|
+
|
|
213
|
+
const uuid = this.api.hap.uuid.generate(device.deviceId);
|
|
214
|
+
const existingAccessory = this.accessories.get(uuid);
|
|
215
|
+
|
|
216
|
+
// for existing accessories restore from cache
|
|
217
|
+
|
|
218
|
+
if (existingAccessory) {
|
|
219
|
+
this.log.info('Restoring existing accessory from cache:', existingAccessory.displayName);
|
|
220
|
+
accessoryType = existingAccessory;
|
|
221
|
+
existingAccessory.context.device = device;
|
|
222
|
+
this.api.updatePlatformAccessories([existingAccessory]);
|
|
223
|
+
} else {
|
|
224
|
+
|
|
225
|
+
// create new accessory
|
|
226
|
+
|
|
227
|
+
this.log.info('Adding new accessory:', device.nickname);
|
|
228
|
+
const accessory = new this.api.platformAccessory(device.nickname, uuid);
|
|
229
|
+
accessoryType = accessory;
|
|
230
|
+
|
|
231
|
+
accessory.context.device = device;
|
|
232
|
+
this.api.registerPlatformAccessories(PLUGIN_NAME, PLATFORM_NAME, [accessory]);
|
|
233
|
+
}
|
|
234
|
+
this.discoveredCacheUUIDs.push(uuid);
|
|
235
|
+
|
|
236
|
+
// Setup services based on device type when there are multiple device types in account
|
|
237
|
+
// add more case statements e.g. Washer, Dryer, Oven, etc.
|
|
238
|
+
|
|
239
|
+
switch (device.nickname) {
|
|
240
|
+
case 'Refrigerator':
|
|
241
|
+
this.debug('green', `Setting up Refrigerator services for ${device.nickname}`);
|
|
242
|
+
setupRefrigeratorServices.call(this, accessoryType, device, deviceServices);
|
|
243
|
+
break;
|
|
244
|
+
case 'someNewAppliance':
|
|
245
|
+
this.debug('green', `Logic not implemented for ${device.nickname}`);
|
|
246
|
+
break;
|
|
247
|
+
default:
|
|
248
|
+
this.debug('red', `not implemented device : for device ${device.nickname}`);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// remove accessories from the cache which are no longer present
|
|
253
|
+
|
|
254
|
+
for (const [uuid, accessory] of this.accessories) {
|
|
255
|
+
if (!this.discoveredCacheUUIDs.includes(uuid)) {
|
|
256
|
+
this.log.info('Removing existing accessory from cache:', accessory.displayName);
|
|
257
|
+
this.api.unregisterPlatformAccessories(PLUGIN_NAME, PLATFORM_NAME, [accessory]);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
public debug(color: string, message: string) {
|
|
263
|
+
if (this.config.debugLogging) {
|
|
264
|
+
switch(color) {
|
|
265
|
+
case 'red':
|
|
266
|
+
this.log.info(chalk.red('[Smarthq] ' + message));
|
|
267
|
+
break;
|
|
268
|
+
case 'blue':
|
|
269
|
+
this.log.info(chalk.blue('[Smarthq] ' + message));
|
|
270
|
+
break;
|
|
271
|
+
case 'green':
|
|
272
|
+
this.log.info(chalk.green('[Smarthq] ' + message));
|
|
273
|
+
break;
|
|
274
|
+
case 'yellow':
|
|
275
|
+
this.log.info(chalk.yellow('[Smarthq] ' + message));
|
|
276
|
+
break;
|
|
277
|
+
default:
|
|
278
|
+
this.log.info('[Smarthq] ' + message);
|
|
279
|
+
}
|
|
280
|
+
} else {
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
// Some models may not have all services available so don't add service if not supported
|
|
286
|
+
|
|
287
|
+
public deviceSupportsThisService(deviceServices: DevService[],
|
|
288
|
+
serviceDeviceType: string,
|
|
289
|
+
serviceType: string,
|
|
290
|
+
domainType: string): boolean {
|
|
291
|
+
for (const service of deviceServices) {
|
|
292
|
+
if (service.serviceDeviceType === serviceDeviceType
|
|
293
|
+
&& service.serviceType === serviceType
|
|
294
|
+
&& service.domainType === domainType) {
|
|
295
|
+
return true;
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
return false;
|
|
299
|
+
}
|
|
300
|
+
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { CharacteristicValue, PlatformAccessory, Logging } from 'homebridge';
|
|
2
|
+
import { SmartHqPlatform } from '../platform.js';
|
|
3
|
+
import { SmartHqApi } from '../smartHqApi.js';
|
|
4
|
+
import { DevService } from '../smarthq-types.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Platform Accessory
|
|
8
|
+
* An instance of this class is created for each accessory your platform registers
|
|
9
|
+
* Each accessory may expose multiple services of different service types.
|
|
10
|
+
*/
|
|
11
|
+
export class ControlLock {
|
|
12
|
+
private readonly smartHqApi: SmartHqApi;
|
|
13
|
+
private log : Logging;
|
|
14
|
+
|
|
15
|
+
constructor(
|
|
16
|
+
private readonly platform: SmartHqPlatform,
|
|
17
|
+
private readonly accessory: PlatformAccessory,
|
|
18
|
+
public readonly deviceServices: DevService[],
|
|
19
|
+
public readonly deviceId: string
|
|
20
|
+
) {
|
|
21
|
+
this.platform = platform;
|
|
22
|
+
this.accessory = accessory;
|
|
23
|
+
this.deviceServices = deviceServices;
|
|
24
|
+
this.deviceId = deviceId;
|
|
25
|
+
this.log = platform.log;
|
|
26
|
+
|
|
27
|
+
this.smartHqApi = new SmartHqApi(this.platform);
|
|
28
|
+
//=====================================================================================
|
|
29
|
+
// Check to see if the device has any supported Convertible Drawer services
|
|
30
|
+
// If not, then don't add services for device that doesn't support it
|
|
31
|
+
//=====================================================================================
|
|
32
|
+
|
|
33
|
+
if (!this.platform.deviceSupportsThisService(this.deviceServices,
|
|
34
|
+
'cloud.smarthq.device.appliance',
|
|
35
|
+
'cloud.smarthq.service.toggle',
|
|
36
|
+
'cloud.smarthq.domain.controls.lock')) {
|
|
37
|
+
this.log.info('No supported Control Lock service found for device: ' + this.accessory.displayName);
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
this.platform.debug('green', 'Adding Controls Lock Switch');
|
|
41
|
+
|
|
42
|
+
//=====================================================================================
|
|
43
|
+
// create a Control Lock switch for the Refrigerator
|
|
44
|
+
//=====================================================================================
|
|
45
|
+
const displayName = "Controls Lock";
|
|
46
|
+
|
|
47
|
+
const controlsLock = this.accessory.getService(displayName)
|
|
48
|
+
|| this.accessory.addService(this.platform.Service.Switch, displayName, 'control-lock-123');
|
|
49
|
+
controlsLock.setCharacteristic(this.platform.Characteristic.Name, displayName);
|
|
50
|
+
|
|
51
|
+
controlsLock.addOptionalCharacteristic(this.platform.Characteristic.ConfiguredName)
|
|
52
|
+
controlsLock.setCharacteristic(this.platform.Characteristic.ConfiguredName, displayName)
|
|
53
|
+
|
|
54
|
+
controlsLock.getCharacteristic(this.platform.Characteristic.On)
|
|
55
|
+
.onGet(this.getcontrolsLock.bind(this))
|
|
56
|
+
.onSet(this.setcontrolsLock.bind(this));
|
|
57
|
+
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
//=====================================================================================
|
|
61
|
+
async getcontrolsLock(): Promise<CharacteristicValue> {
|
|
62
|
+
|
|
63
|
+
let isOn = false;
|
|
64
|
+
|
|
65
|
+
for (const service of this.deviceServices) {
|
|
66
|
+
if (service.serviceDeviceType === 'cloud.smarthq.device.appliance'
|
|
67
|
+
&& service.serviceType === 'cloud.smarthq.service.toggle'
|
|
68
|
+
&& service.domainType === 'cloud.smarthq.domain.controls.lock') {
|
|
69
|
+
const state = await this.smartHqApi.getServiceState(this.deviceId, service.serviceId);
|
|
70
|
+
if (state?.on == null) {
|
|
71
|
+
this.platform.debug('blue', 'No response from setcontrolsLock command');
|
|
72
|
+
return false;
|
|
73
|
+
}
|
|
74
|
+
isOn = state?.on;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return isOn;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
//=====================================================================================
|
|
81
|
+
async setcontrolsLock(value: CharacteristicValue) {
|
|
82
|
+
|
|
83
|
+
const cmdBody = {
|
|
84
|
+
command: {
|
|
85
|
+
commandType: 'cloud.smarthq.command.toggle.set',
|
|
86
|
+
on: value
|
|
87
|
+
},
|
|
88
|
+
kind: 'service#command',
|
|
89
|
+
deviceId: this.deviceId,
|
|
90
|
+
serviceDeviceType: 'cloud.smarthq.device.appliance',
|
|
91
|
+
serviceType: 'cloud.smarthq.service.toggle',
|
|
92
|
+
domainType: 'cloud.smarthq.domain.controls.lock'
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
const response = await this.smartHqApi.command(JSON.stringify(cmdBody));
|
|
96
|
+
|
|
97
|
+
if (response == null) {
|
|
98
|
+
this.platform.debug('blue', 'No response from setcontrolsLock command');
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
this.platform.debug('blue', 'setcontrolsLock response: ' + response.outcome);
|
|
102
|
+
}
|
|
103
|
+
}
|