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.
Files changed (84) hide show
  1. package/LICENSE +21 -0
  2. package/Readme.md +138 -0
  3. package/config.schema.json +146 -0
  4. package/dist/index.d.ts +6 -0
  5. package/dist/index.js +9 -0
  6. package/dist/index.js.map +1 -0
  7. package/dist/platform.d.ts +40 -0
  8. package/dist/platform.js +243 -0
  9. package/dist/platform.js.map +1 -0
  10. package/dist/refrigerator/controlLock.d.ts +19 -0
  11. package/dist/refrigerator/controlLock.js +85 -0
  12. package/dist/refrigerator/controlLock.js.map +1 -0
  13. package/dist/refrigerator/convertibleDrawer.d.ts +27 -0
  14. package/dist/refrigerator/convertibleDrawer.js +317 -0
  15. package/dist/refrigerator/convertibleDrawer.js.map +1 -0
  16. package/dist/refrigerator/dispenserLight.d.ts +19 -0
  17. package/dist/refrigerator/dispenserLight.js +85 -0
  18. package/dist/refrigerator/dispenserLight.js.map +1 -0
  19. package/dist/refrigerator/energy.d.ts +19 -0
  20. package/dist/refrigerator/energy.js +93 -0
  21. package/dist/refrigerator/energy.js.map +1 -0
  22. package/dist/refrigerator/freezer.d.ts +41 -0
  23. package/dist/refrigerator/freezer.js +188 -0
  24. package/dist/refrigerator/freezer.js.map +1 -0
  25. package/dist/refrigerator/iceMaker.d.ts +19 -0
  26. package/dist/refrigerator/iceMaker.js +81 -0
  27. package/dist/refrigerator/iceMaker.js.map +1 -0
  28. package/dist/refrigerator/interiorLight.d.ts +21 -0
  29. package/dist/refrigerator/interiorLight.js +100 -0
  30. package/dist/refrigerator/interiorLight.js.map +1 -0
  31. package/dist/refrigerator/refrigerator.d.ts +41 -0
  32. package/dist/refrigerator/refrigerator.js +204 -0
  33. package/dist/refrigerator/refrigerator.js.map +1 -0
  34. package/dist/refrigerator/refrigeratorAlerts.d.ts +36 -0
  35. package/dist/refrigerator/refrigeratorAlerts.js +204 -0
  36. package/dist/refrigerator/refrigeratorAlerts.js.map +1 -0
  37. package/dist/refrigerator/sabbathMode.d.ts +20 -0
  38. package/dist/refrigerator/sabbathMode.js +91 -0
  39. package/dist/refrigerator/sabbathMode.js.map +1 -0
  40. package/dist/refrigerator/temperatureUnits.d.ts +21 -0
  41. package/dist/refrigerator/temperatureUnits.js +147 -0
  42. package/dist/refrigerator/temperatureUnits.js.map +1 -0
  43. package/dist/refrigerator/turboCoolMode.d.ts +23 -0
  44. package/dist/refrigerator/turboCoolMode.js +134 -0
  45. package/dist/refrigerator/turboCoolMode.js.map +1 -0
  46. package/dist/refrigerator/waterFilter.d.ts +19 -0
  47. package/dist/refrigerator/waterFilter.js +89 -0
  48. package/dist/refrigerator/waterFilter.js.map +1 -0
  49. package/dist/refrigeratorServices.d.ts +5 -0
  50. package/dist/refrigeratorServices.js +54 -0
  51. package/dist/refrigeratorServices.js.map +1 -0
  52. package/dist/settings.d.ts +12 -0
  53. package/dist/settings.js +13 -0
  54. package/dist/settings.js.map +1 -0
  55. package/dist/smartHqApi.d.ts +29 -0
  56. package/dist/smartHqApi.js +267 -0
  57. package/dist/smartHqApi.js.map +1 -0
  58. package/dist/smarthq-types.d.ts +29 -0
  59. package/dist/smarthq-types.js +2 -0
  60. package/dist/smarthq-types.js.map +1 -0
  61. package/eslint.config.js +12 -0
  62. package/images/homebridge.svg +1 -0
  63. package/package.json +61 -0
  64. package/src/@types/homebridge-lib.d.ts +14 -0
  65. package/src/index.ts +11 -0
  66. package/src/platform.ts +300 -0
  67. package/src/refrigerator/controlLock.ts +103 -0
  68. package/src/refrigerator/convertibleDrawer.ts +381 -0
  69. package/src/refrigerator/dispenserLight.ts +103 -0
  70. package/src/refrigerator/energy.ts +106 -0
  71. package/src/refrigerator/freezer.ts +227 -0
  72. package/src/refrigerator/iceMaker.ts +100 -0
  73. package/src/refrigerator/interiorLight.ts +118 -0
  74. package/src/refrigerator/refrigerator.ts +241 -0
  75. package/src/refrigerator/refrigeratorAlerts.ts +228 -0
  76. package/src/refrigerator/sabbathMode.ts +111 -0
  77. package/src/refrigerator/temperatureUnits.ts +178 -0
  78. package/src/refrigerator/turboCoolMode.ts +164 -0
  79. package/src/refrigerator/waterFilter.ts +107 -0
  80. package/src/refrigeratorServices.ts +60 -0
  81. package/src/settings.ts +14 -0
  82. package/src/smartHqApi.ts +305 -0
  83. package/src/smarthq-types.ts +31 -0
  84. package/tsconfig.json +24 -0
@@ -0,0 +1,107 @@
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 WaterFilter {
12
+
13
+ private readonly smartHqApi: SmartHqApi;
14
+ private log : Logging;
15
+
16
+ constructor(
17
+ private readonly platform: SmartHqPlatform,
18
+ private readonly accessory: PlatformAccessory,
19
+ public readonly deviceServices: DevService[],
20
+ public readonly deviceId: string
21
+ ) {
22
+ this.platform = platform;
23
+ this.accessory = accessory;
24
+ this.deviceServices = deviceServices;
25
+ this.deviceId = deviceId;
26
+ this.log = platform.log;
27
+
28
+ this.smartHqApi = new SmartHqApi(this.platform);
29
+
30
+ //=====================================================================================
31
+ // Check to see if the device has any supported FilterMaintenance services
32
+ // If not, then don't add services for device that doesn't support it
33
+ //=====================================================================================
34
+
35
+ if (!this.platform.deviceSupportsThisService(this.deviceServices,
36
+ 'cloud.smarthq.device.waterfilter',
37
+ 'cloud.smarthq.service.mode',
38
+ 'cloud.smarthq.domain.state')) {
39
+ this.log.info('No supported FilterMaintenance service found for device: ' + this.accessory.displayName);
40
+ return;
41
+ }
42
+ this.platform.debug('green', 'Adding a Water Filter Sensor');
43
+
44
+ //=====================================================================================
45
+ // create a new water FilterMaintenance service for the Refrigerator
46
+ // This works in Homebridge and HomeKit has a native FilterMaintenance service type but the Home app does not implement it yet
47
+ // so no sensor/accessory will show up in the Home app for this service type.
48
+ //=====================================================================================
49
+ const displayName = "Filter Life";
50
+ const refrigeratorWaterFilter = this.accessory.getService(displayName)
51
+ || this.accessory.addService(this.platform.Service.FilterMaintenance, displayName, 'filter-maintenance-1');
52
+ refrigeratorWaterFilter.addOptionalCharacteristic(this.platform.Characteristic.ConfiguredName)
53
+ refrigeratorWaterFilter.setCharacteristic(this.platform.Characteristic.ConfiguredName, displayName)
54
+
55
+ refrigeratorWaterFilter.getCharacteristic(this.platform.Characteristic.FilterChangeIndication)
56
+ .onGet(this.getWaterFilterChangeIndication.bind(this));
57
+ refrigeratorWaterFilter.getCharacteristic(this.platform.Characteristic.FilterLifeLevel)
58
+ .onGet(this.getWaterFilterLifeLevel.bind(this));
59
+
60
+
61
+
62
+ }
63
+ //=====================================================================================
64
+ async getWaterFilterChangeIndication(): Promise<CharacteristicValue> {
65
+ let filterStatus = 0
66
+ for (const service of this.deviceServices) {
67
+ if (service.serviceDeviceType === 'cloud.smarthq.device.waterfilter'
68
+ && service.serviceType === 'cloud.smarthq.service.mode') {
69
+
70
+ const state = await this.smartHqApi.getServiceState(this.deviceId, service.serviceId);
71
+ if (state?.mode == null) {
72
+ this.platform.debug('blue', 'No state.mode returned from getWaterFilterChangeIndication state');
73
+ return false;
74
+ }
75
+ if (state?.mode === 'cloud.smarthq.type.mode.good'
76
+ || state?.mode === 'cloud.smarthq.type.mode.bypass'
77
+ || state?.mode === 'cloud.smarthq.type.mode.expiringsoon'
78
+ ) {
79
+ filterStatus = this.platform.Characteristic.FilterChangeIndication.FILTER_OK;
80
+ } else {
81
+ filterStatus = this.platform.Characteristic.FilterChangeIndication.CHANGE_FILTER;
82
+ }
83
+ }
84
+ }
85
+ return filterStatus;
86
+ }
87
+
88
+ //=====================================================================================
89
+ async getWaterFilterLifeLevel(): Promise<CharacteristicValue> {
90
+
91
+ let filterRemaining = 0;
92
+ for (const service of this.deviceServices) {
93
+ if (service.serviceDeviceType === 'cloud.smarthq.device.waterfilter'
94
+ && service.serviceType === 'cloud.smarthq.service.integer') {
95
+
96
+ const state = await this.smartHqApi.getServiceState(this.deviceId, service.serviceId);
97
+ if (state?.value == null) {
98
+ this.platform.debug('blue', 'No value returned from getWaterFilterLifeLevel state');
99
+ return false;
100
+ }
101
+ filterRemaining = state?.value;
102
+ }
103
+ }
104
+ return filterRemaining;
105
+ }
106
+
107
+ }
@@ -0,0 +1,60 @@
1
+ // These functions setup the various services for a refrigerator appliance
2
+ // imported into platform.ts
3
+ // Refrigerator and Freezer are always added; the rest are optional based on config settings
4
+
5
+ import { Refrigerator } from './refrigerator/refrigerator.js';
6
+ import { Freezer } from './refrigerator/freezer.js';
7
+ import { ControlLock } from './refrigerator/controlLock.js';
8
+ import { ConvertibleDrawer } from './refrigerator/convertibleDrawer.js';
9
+ import { DispenserLight } from './refrigerator/dispenserLight.js';
10
+ import { EnergySensor } from './refrigerator/energy.js';
11
+ import { IceMaker } from './refrigerator/iceMaker.js';
12
+ import { InteriorLight } from './refrigerator/interiorLight.js';
13
+ import { SabbathMode } from './refrigerator/sabbathMode.js';
14
+ import { TemperatureUnits } from './refrigerator/temperatureUnits.js';
15
+ import { TurboCoolMode } from './refrigerator/turboCoolMode.js';
16
+ import { WaterFilter } from './refrigerator/waterFilter.js';
17
+ import { RefrigeratorAlerts } from './refrigerator/refrigeratorAlerts.js';
18
+ import { DevService } from './smarthq-types.js';
19
+ import { PlatformAccessory } from 'homebridge';
20
+ import { SmartHqPlatform } from './platform.js';
21
+ import { DevDevice } from './smarthq-types.js';
22
+
23
+
24
+ export function setupRefrigeratorServices(this: SmartHqPlatform, accessory: PlatformAccessory, device: DevDevice, deviceServices: DevService[]) {
25
+ new Refrigerator(this, accessory, deviceServices, device.deviceId);
26
+ new Freezer(this, accessory, deviceServices, device.deviceId);
27
+
28
+ if (this.config.addConvertibleDrawer) {
29
+ new ConvertibleDrawer(this, accessory, deviceServices, device.deviceId);}
30
+
31
+ if (this.config.addControlLock) {
32
+ new ControlLock(this, accessory, deviceServices, device.deviceId);}
33
+
34
+ if (this.config.addDispenserLight) {
35
+ new DispenserLight(this, accessory, deviceServices, device.deviceId);}
36
+
37
+ if (this.config.addEnergyMonitor) {
38
+ new EnergySensor(this, accessory, deviceServices, device.deviceId);}
39
+
40
+ if (this.config.addIceMaker) {
41
+ new IceMaker(this, accessory, deviceServices, device.deviceId);}
42
+
43
+ if (this.config.addInteriorLight) {
44
+ new InteriorLight(this, accessory, deviceServices, device.deviceId);}
45
+
46
+ if (this.config.addSabbathMode) {
47
+ new SabbathMode(this, accessory, deviceServices, device.deviceId);}
48
+
49
+ if (this.config.addTemperatureUnits) {
50
+ new TemperatureUnits(this, accessory, deviceServices, device.deviceId);}
51
+
52
+ if (this.config.addTurboCool) {
53
+ new TurboCoolMode(this, accessory, deviceServices, device.deviceId);}
54
+
55
+ if (this.config.addWaterFilter) {
56
+ new WaterFilter(this, accessory, deviceServices, device.deviceId); }
57
+
58
+ if (this.config.addAlerts) {
59
+ new RefrigeratorAlerts(this, accessory, deviceServices, device.deviceId); }
60
+ }
@@ -0,0 +1,14 @@
1
+ /**
2
+ * This is the name of the platform that users will use to register the plugin in the Homebridge config.json
3
+ */
4
+ export const PLATFORM_NAME = "SmartHqPlatform";
5
+
6
+ /**
7
+ * This must match the name of your plugin as defined the package.json `name` property
8
+ */
9
+ export const PLUGIN_NAME = 'homebridge-smarthq-client';
10
+ export const API_URL = 'https://client.mysmarthq.com';
11
+
12
+ export const AUTH_URL = 'https://accounts.brillion.geappliances.com/oauth2/auth';
13
+ export const ACCESSTOKEN_URL = 'https://accounts.brillion.geappliances.com/oauth2/token';
14
+ export const TOKEN_STORE = 'smarthq.tokens.json';
@@ -0,0 +1,305 @@
1
+ import axios, { AxiosResponse, isAxiosError } from 'axios';
2
+ import { writeFileSync, existsSync, readFileSync } from 'fs';
3
+ import { stringify } from 'querystring';
4
+ import chalk from 'chalk';
5
+
6
+ import { API_URL, AUTH_URL, ACCESSTOKEN_URL} from './settings.js';
7
+ import { SmartHqPlatform } from './platform.js';
8
+ import { DevService } from './smarthq-types.js';
9
+
10
+ /**
11
+ * SmartHq Api functions
12
+ * This class is the main constructor for your plugin, this is where you should
13
+ * parse the user config and discover/register accessories with Homebridge.
14
+ */
15
+ /* ---------- SmartHq Api ---------- */
16
+
17
+ export class SmartHqApi {
18
+ refresh_token: string;
19
+ expires: number;
20
+ access_token: string;
21
+
22
+ constructor(
23
+ private readonly platform: SmartHqPlatform,
24
+ ) {
25
+ this.refresh_token = '';
26
+ this.access_token = '';
27
+ this.expires = 0;
28
+ this.loadAccessToken();
29
+ }
30
+
31
+
32
+ //-------------------------------------------------------------
33
+ // Create the url to be displayed in Homebridge log for first time
34
+ // user to visit the link and login successfully.
35
+ // Response will be sent to redirect uri
36
+ //-------------------------------------------------------------
37
+ authUrl() {
38
+ return AUTH_URL + stringify({
39
+ response_type: 'code',
40
+ client_id: this.platform.config.clientId,
41
+ redirect_uri: this.platform.config.redirectUri
42
+ });
43
+ }
44
+
45
+ async getOauthAccessToken(dataForBody: Record<string, string>) {
46
+ try {
47
+ const response = await axios.post(
48
+ ACCESSTOKEN_URL,
49
+ stringify(dataForBody),
50
+ { headers: { 'Content-Type': 'application/x-www-form-urlencoded' } }
51
+ );
52
+
53
+ this.saveToken(response.data);
54
+ } catch (error) {
55
+ // Use isAxiosError type guard for safe property access
56
+ if (axios.isAxiosError(error)) {
57
+ if (error.response) {
58
+ switch (error.response.status) {
59
+ case 400:
60
+ this.platform.log.error('(400) Bad Request: The request is missing required parameters or includes invalid values.');
61
+ break;
62
+ case 401:
63
+ this.platform.log.error('(401) Unauthorized – The provided client credentials are invalid.: ');
64
+ break;
65
+ default:
66
+ this.platform.log.error('Error obtaining token: ', error.message);
67
+ break;
68
+ }
69
+ }
70
+ } else {
71
+ // Not an Axios error (e.g., a general JavaScript error)
72
+ console.error('Unexpected error:', error);
73
+ }
74
+ }
75
+ }
76
+
77
+ async exchangeCodeForToken(code: string) {
78
+ return this.getOauthAccessToken({
79
+ grant_type: 'authorization_code',
80
+ client_id: this.platform.config.clientId,
81
+ client_secret: this.platform.config.clientSecret,
82
+ redirect_uri: this.platform.config.redirectUri,
83
+ code: code
84
+ });
85
+ }
86
+
87
+ async refreshAccessToken() {
88
+ return this.getOauthAccessToken({
89
+ grant_type: 'refresh_token',
90
+ client_id: this.platform.config.clientId,
91
+ client_secret: this.platform.config.clientSecret,
92
+ refresh_token: this.refresh_token
93
+ });
94
+ }
95
+
96
+ saveToken(data: AxiosResponse['data']) {
97
+ Object.assign(this, data);
98
+
99
+ this.expires = Date.now() + data.expires_in * 1000;
100
+ try {
101
+ writeFileSync(this.platform.tokenPath, JSON.stringify(data, null, 2));
102
+ } catch (error) {
103
+ this.platform.log.error('Error saving token: ', error);
104
+ }
105
+ }
106
+
107
+ loadAccessToken() {
108
+ if (!existsSync(this.platform.tokenPath)) return;
109
+ const data: string = readFileSync(this.platform.tokenPath).toString('utf8');
110
+ const tokenData = JSON.parse(data);
111
+ Object.assign(this, tokenData);
112
+ this.expires = Date.now() + tokenData.expires_in * 1000;
113
+ }
114
+
115
+ async httpHeaders() {
116
+ if (!this.access_token || Date.now() > this.expires - 60000) {
117
+ await this.refreshAccessToken();
118
+ }
119
+ return { Authorization: `Bearer ${this.access_token}` };
120
+ }
121
+
122
+ async getAppliances() {
123
+ //======================================================
124
+ // Get list of devices for your account
125
+ //======================================================
126
+ this.debug('green', 'Entering getAppliances()');
127
+
128
+ const url = new URL('/v2/device', API_URL);
129
+ try {
130
+ const res = await axios.get(url.toString(),
131
+ { headers: await this.httpHeaders() }
132
+ );
133
+ return res.data.devices;
134
+ } catch (error) {
135
+ if (isAxiosError(error)) {
136
+ if (error.response) {
137
+ switch (error.response.status) {
138
+ case 400:
139
+ this.platform.log.error('(400) Bad Request: ', error.message);
140
+ break;
141
+ case 401:
142
+ this.refreshAccessToken();
143
+ //this.platform.log.error('(401) Unauthorized: ', error.message); token expired - refresh token instead of error
144
+ break;
145
+ case 403:
146
+ this.platform.log.error('(403) Forbidden client does not have permission to view device: ', error.message);
147
+ break;
148
+ default:
149
+ this.platform.log.error('Error in getAppliances(): ', error.message);
150
+ break;
151
+ }
152
+ } else {
153
+ this.debug('red', ('Still here too early.'));
154
+ }
155
+ }
156
+ }
157
+ }
158
+
159
+ async getServiceState(deviceId: string, serviceId: string) {
160
+ //======================================================
161
+ // Get the state of the service
162
+ //======================================================
163
+ const url = new URL(`/v2/device/${deviceId}/service/${serviceId}`, API_URL);
164
+ try {
165
+ const response = await axios.get(url.toString(),
166
+ { headers: await this.httpHeaders() }
167
+ );
168
+ if (!response.data) {
169
+ this.platform.log.error(
170
+ `Request ${url} failed with status ${response.status}`
171
+ );
172
+ return;
173
+ }
174
+ return response.data.state;
175
+
176
+ } catch (error) {
177
+ if (isAxiosError(error)) {
178
+ if (error.response && error.response.status === 401) {
179
+ await this.refreshAccessToken();
180
+ this.debug('red', 'Token refreshed in getServiceState.');
181
+ }
182
+ }
183
+ }
184
+ }
185
+
186
+
187
+
188
+
189
+
190
+ async getDeviceServices(deviceId: string) {
191
+ const url = new URL(`/v2/device/${deviceId}`, API_URL);
192
+ try {
193
+ const response = await axios.get(url.toString(),
194
+ { headers: await this.httpHeaders() }
195
+ );
196
+ const services: DevService[] = response.data.services;
197
+ const sortedServices = services.sort((a, b) => {
198
+ if (a.serviceDeviceType < b.serviceDeviceType) return -1;
199
+ if (a.serviceDeviceType > b.serviceDeviceType) return 1;
200
+ return 0;
201
+ });
202
+
203
+ if (this.platform.config.debugServices) {
204
+ this.platform.log.info(chalk.red("(The 'state' of each service can be queried using getServiceState() in smartHQApi.js)"));
205
+ for (const service of sortedServices) {
206
+ this.platform.log.info(chalk.yellow("ServiceDeviceType = " + service.serviceDeviceType));
207
+ this.platform.log.info(chalk.yellow("ServiceType = " + service.serviceType));
208
+ this.platform.log.info(chalk.yellow("Domain = " + service.domainType));
209
+ this.platform.log.info(chalk.yellow("Valid Commands = " + chalk.green(JSON.stringify(service.supportedCommands))));
210
+ this.platform.log.info(chalk.yellow("Config = " + chalk.green(JSON.stringify(service.config))));
211
+ this.platform.log.info(chalk.yellow("State = " + chalk.red(JSON.stringify(service.state))));
212
+ this.platform.log.info("------------------------------------------------------------------------");
213
+ }
214
+ }
215
+ if (!response.data) {
216
+ this.platform.log.error(
217
+ `Request ${url} failed with status ${response.status}`
218
+ );
219
+ return;
220
+ }
221
+ return response.data.services;
222
+ } catch (error) {
223
+ if (isAxiosError(error)) {
224
+ if (error.response && error.response.status === 401) {
225
+ await this.refreshAccessToken();
226
+ this.debug('red', 'Token refreshed in getDeviceServices.');
227
+ } else {
228
+ this.platform.log.error('Error from getDeviceServices():', error.message);
229
+ }
230
+ }
231
+ }
232
+ }
233
+ async getRecentAlerts() {
234
+ // Check for alerts every minute
235
+ const urlAlert = new URL(`/v2/alert/recent?after=1m`, API_URL);
236
+
237
+ try {
238
+ const res = await axios.get(urlAlert.toString(),
239
+ { headers: await this.httpHeaders() }
240
+ );
241
+ return res.data.alerts;
242
+
243
+ } catch (error) {
244
+ if (isAxiosError(error)) {
245
+ if (error.response && error.response.status === 401) {
246
+ await this.refreshAccessToken();
247
+ this.debug('red', 'Token refreshed in getRecentAlerts.');
248
+ } else {
249
+ this.platform.log.error('Error getting recent alerts:', + error.message);
250
+ }
251
+ }
252
+ }
253
+ }
254
+
255
+ async command(body: string) {
256
+
257
+ const url = new URL('/v2/command', API_URL);
258
+
259
+ try {
260
+ const response = await axios.post(url.toString(),
261
+ body,
262
+ { headers: await this.httpHeaders() }
263
+ );
264
+ return response.data;
265
+
266
+ } catch (error) {
267
+ if (isAxiosError(error)) {
268
+ if (error.response && error.response.status === 401) {
269
+ await this.refreshAccessToken();
270
+ this.debug('red', 'Token refreshed in command.');
271
+ } else {
272
+ this.platform.log.error('Error sending command:', body);
273
+ if (error.response) { // that falls out of the range of 2xx
274
+ this.platform.log(error.response.data);
275
+ }
276
+ }
277
+ }
278
+ }
279
+ }
280
+
281
+ // Use chalk to color debug messages (local to this plugin)
282
+ // only show debug messages if debugLogging is enabled in config
283
+
284
+ debug(color: string, message: string) {
285
+ if (this.platform.config.debugLogging) {
286
+ switch(color) {
287
+ case 'red':
288
+ this.platform.log.info(chalk.red('[SmartHQ] ' + message));
289
+ break;
290
+ case 'blue':
291
+ this.platform.log.info(chalk.blue('[SmartHQ] ' + message));
292
+ break;
293
+ case 'green':
294
+ this.platform.log.info(chalk.green('[SmartHQ] ' + message));
295
+ break;
296
+ case 'yellow':
297
+ this.platform.log.info(chalk.yellow('[SmartHQ] ' + message));
298
+ break;
299
+ default:
300
+ this.platform.log.info('[SmartHQ] ' + message);
301
+ }
302
+ }
303
+ }
304
+
305
+ }
@@ -0,0 +1,31 @@
1
+ export interface DevService {
2
+ serviceType: string;
3
+ lastSyncTime: string;
4
+ domainType: string;
5
+ supportedCommands: string[];
6
+ state: Record<string, unknown>;
7
+ serviceId: string;
8
+ serviceDeviceType: string;
9
+ config: Record<string, unknown>;
10
+ lastStateTime: string;
11
+ }
12
+
13
+ export interface DevDevice {
14
+ deviceType: string;
15
+ lastSyncTime: string;
16
+ roomNumber: string;
17
+ serial: string;
18
+ lastPresenceTime: string;
19
+ createdDateTime: string;
20
+ presence: string;
21
+ deviceId: string;
22
+ gatewayId: string;
23
+ room: string;
24
+ icon: string;
25
+ manufacturer: string;
26
+ nickname: string;
27
+ model: string;
28
+ floor: string;
29
+ macAddress: string;
30
+
31
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,24 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "lib": [
5
+ "DOM",
6
+ "ES2022"
7
+ ],
8
+ "rootDir": "src",
9
+ "module": "nodenext",
10
+ "moduleResolution": "nodenext",
11
+ "strict": true,
12
+ "declaration": true,
13
+ "outDir": "dist",
14
+ "sourceMap": true,
15
+ "allowSyntheticDefaultImports": true,
16
+ "esModuleInterop": true,
17
+ "forceConsistentCasingInFileNames": true
18
+ },
19
+ "include": [
20
+ "eslint.config.js",
21
+ "homebridge-ui",
22
+ "src"
23
+ ]
24
+ }