@homebridge-plugins/homebridge-smarthq 0.5.0-beta.4 → 0.5.0-beta.40

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 (145) hide show
  1. package/.github/ISSUE_TEMPLATE/config.yml +1 -1
  2. package/.github/copilot-instructions.md +361 -0
  3. package/.github/scripts/branch-helper.sh +41 -0
  4. package/.github/workflows/beta-release.yml +1 -1
  5. package/.github/workflows/release.yml +53 -17
  6. package/CHANGELOG.md +563 -18
  7. package/MATTER.md +304 -0
  8. package/README.md +15 -0
  9. package/config.schema.json +24 -5
  10. package/dist/devices/OpalIceMaker/Managers/OpalDescaleSvcManager.d.ts.map +1 -1
  11. package/dist/devices/OpalIceMaker/Managers/OpalDescaleSvcManager.js +3 -1
  12. package/dist/devices/OpalIceMaker/Managers/OpalDescaleSvcManager.js.map +1 -1
  13. package/dist/devices/OpalIceMaker/Managers/OpalFilterMaintenanceSvcManager.d.ts.map +1 -1
  14. package/dist/devices/OpalIceMaker/Managers/OpalFilterMaintenanceSvcManager.js +3 -1
  15. package/dist/devices/OpalIceMaker/Managers/OpalFilterMaintenanceSvcManager.js.map +1 -1
  16. package/dist/devices/OpalIceMaker/Managers/OpalPowerSvcManager.js +1 -1
  17. package/dist/devices/OpalIceMaker/Managers/OpalPowerSvcManager.js.map +1 -1
  18. package/dist/devices/OpalIceMaker/Managers/OpalProgressSvcManager.d.ts +1 -1
  19. package/dist/devices/OpalIceMaker/Managers/OpalProgressSvcManager.d.ts.map +1 -1
  20. package/dist/devices/OpalIceMaker/Managers/OpalProgressSvcManager.js +12 -5
  21. package/dist/devices/OpalIceMaker/Managers/OpalProgressSvcManager.js.map +1 -1
  22. package/dist/devices/OpalIceMaker/Managers/StatusManagers/OpalStatusBase.d.ts.map +1 -1
  23. package/dist/devices/OpalIceMaker/Managers/StatusManagers/OpalStatusBase.js +4 -2
  24. package/dist/devices/OpalIceMaker/Managers/StatusManagers/OpalStatusBase.js.map +1 -1
  25. package/dist/devices/OpalIceMaker/OpalDeviceBase.d.ts +3 -4
  26. package/dist/devices/OpalIceMaker/OpalDeviceBase.d.ts.map +1 -1
  27. package/dist/devices/OpalIceMaker/OpalDeviceBase.js +3 -18
  28. package/dist/devices/OpalIceMaker/OpalDeviceBase.js.map +1 -1
  29. package/dist/devices/advantium.d.ts +10 -0
  30. package/dist/devices/advantium.d.ts.map +1 -0
  31. package/dist/devices/advantium.js +75 -0
  32. package/dist/devices/advantium.js.map +1 -0
  33. package/dist/devices/airConditioner.d.ts +11 -2
  34. package/dist/devices/airConditioner.d.ts.map +1 -1
  35. package/dist/devices/airConditioner.js +111 -17
  36. package/dist/devices/airConditioner.js.map +1 -1
  37. package/dist/devices/beverageCenter.d.ts +10 -0
  38. package/dist/devices/beverageCenter.d.ts.map +1 -0
  39. package/dist/devices/beverageCenter.js +125 -0
  40. package/dist/devices/beverageCenter.js.map +1 -0
  41. package/dist/devices/clothesDryer.d.ts +21 -0
  42. package/dist/devices/clothesDryer.d.ts.map +1 -0
  43. package/dist/devices/clothesDryer.js +273 -0
  44. package/dist/devices/clothesDryer.js.map +1 -0
  45. package/dist/devices/clothesWasher.d.ts +21 -0
  46. package/dist/devices/clothesWasher.d.ts.map +1 -0
  47. package/dist/devices/clothesWasher.js +284 -0
  48. package/dist/devices/clothesWasher.js.map +1 -0
  49. package/dist/devices/coffeeMaker.d.ts +10 -0
  50. package/dist/devices/coffeeMaker.d.ts.map +1 -0
  51. package/dist/devices/coffeeMaker.js +79 -0
  52. package/dist/devices/coffeeMaker.js.map +1 -0
  53. package/dist/devices/device.d.ts +108 -13
  54. package/dist/devices/device.d.ts.map +1 -1
  55. package/dist/devices/device.js +381 -60
  56. package/dist/devices/device.js.map +1 -1
  57. package/dist/devices/dishwasher.d.ts +9 -3
  58. package/dist/devices/dishwasher.d.ts.map +1 -1
  59. package/dist/devices/dishwasher.js +164 -29
  60. package/dist/devices/dishwasher.js.map +1 -1
  61. package/dist/devices/hood.d.ts +41 -0
  62. package/dist/devices/hood.d.ts.map +1 -0
  63. package/dist/devices/hood.js +336 -0
  64. package/dist/devices/hood.js.map +1 -0
  65. package/dist/devices/microwave.d.ts +19 -0
  66. package/dist/devices/microwave.d.ts.map +1 -0
  67. package/dist/devices/microwave.js +147 -0
  68. package/dist/devices/microwave.js.map +1 -0
  69. package/dist/devices/oven.d.ts +9 -2
  70. package/dist/devices/oven.d.ts.map +1 -1
  71. package/dist/devices/oven.js +267 -43
  72. package/dist/devices/oven.js.map +1 -1
  73. package/dist/devices/refrigerator.d.ts +37 -2
  74. package/dist/devices/refrigerator.d.ts.map +1 -1
  75. package/dist/devices/refrigerator.js +583 -34
  76. package/dist/devices/refrigerator.js.map +1 -1
  77. package/dist/devices/waterFilter.d.ts +10 -0
  78. package/dist/devices/waterFilter.d.ts.map +1 -0
  79. package/dist/devices/waterFilter.js +67 -0
  80. package/dist/devices/waterFilter.js.map +1 -0
  81. package/dist/devices/waterHeater.d.ts +19 -0
  82. package/dist/devices/waterHeater.d.ts.map +1 -0
  83. package/dist/devices/waterHeater.js +161 -0
  84. package/dist/devices/waterHeater.js.map +1 -0
  85. package/dist/devices/waterSoftener.d.ts +10 -0
  86. package/dist/devices/waterSoftener.d.ts.map +1 -0
  87. package/dist/devices/waterSoftener.js +67 -0
  88. package/dist/devices/waterSoftener.js.map +1 -0
  89. package/dist/getAccessToken.d.ts.map +1 -1
  90. package/dist/getAccessToken.js +190 -2
  91. package/dist/getAccessToken.js.map +1 -1
  92. package/dist/platform.d.ts +32 -1
  93. package/dist/platform.d.ts.map +1 -1
  94. package/dist/platform.js +853 -151
  95. package/dist/platform.js.map +1 -1
  96. package/dist/platform.test.d.ts +2 -0
  97. package/dist/platform.test.d.ts.map +1 -0
  98. package/dist/platform.test.js +80 -0
  99. package/dist/platform.test.js.map +1 -0
  100. package/dist/settings.d.ts +24 -0
  101. package/dist/settings.d.ts.map +1 -1
  102. package/dist/settings.js +13 -0
  103. package/dist/settings.js.map +1 -1
  104. package/docs/assets/highlight.css +7 -0
  105. package/docs/assets/main.js +5 -5
  106. package/docs/assets/navigation.js +1 -0
  107. package/docs/assets/search.js +1 -0
  108. package/docs/assets/style.css +248 -226
  109. package/docs/classes/SmartHQPlatform.html +54 -56
  110. package/docs/hierarchy.html +1 -0
  111. package/docs/index.html +22 -12
  112. package/docs/interfaces/DeviceOptions.html +2 -3
  113. package/docs/interfaces/SmartHQPlatformConfig.html +6 -17
  114. package/docs/interfaces/SmartHqContext.html +6 -4
  115. package/docs/interfaces/SmartHqERDResponse.html +7 -8
  116. package/docs/interfaces/credentials.html +3 -4
  117. package/docs/interfaces/devicesConfig.html +9 -8
  118. package/docs/interfaces/options.html +7 -8
  119. package/docs/media/copilot-instructions.md +361 -0
  120. package/docs/modules.html +1 -2
  121. package/docs/variables/API_URL.html +1 -2
  122. package/docs/variables/ERD_CODES.html +1 -2
  123. package/docs/variables/ERD_TYPES.html +1 -2
  124. package/docs/variables/KEEPALIVE_TIMEOUT.html +1 -2
  125. package/docs/variables/LOGIN_URL.html +2 -3
  126. package/docs/variables/OAUTH2_CLIENT_ID.html +1 -2
  127. package/docs/variables/OAUTH2_CLIENT_SECRET.html +1 -2
  128. package/docs/variables/OAUTH2_REDIRECT_URI.html +1 -2
  129. package/docs/variables/PLATFORM_NAME.html +2 -3
  130. package/docs/variables/PLUGIN_NAME.html +2 -3
  131. package/docs/variables/SECURE_URL.html +2 -3
  132. package/docs/variables/default.html +1 -0
  133. package/package.json +22 -33
  134. package/typedoc.json +0 -4
  135. package/vitest.config.ts +6 -5
  136. package/.github/workflows/build.yml +0 -18
  137. package/.github/workflows/changerelease.yml +0 -11
  138. package/.github/workflows/labeler.yml +0 -9
  139. package/.github/workflows/release-drafter.yml +0 -14
  140. package/docs/assets/dmt/dmt-component-data.js +0 -1
  141. package/docs/assets/dmt/dmt-components.css +0 -20
  142. package/docs/assets/dmt/dmt-components.js +0 -67
  143. package/docs/assets/dmt/dmt-search.cmp +0 -0
  144. package/docs/assets/dmt/dmt-theme.css +0 -1
  145. package/docs/functions/default.html +0 -2
package/dist/platform.js CHANGED
@@ -4,10 +4,20 @@ import { SmartHQIceMaker } from './devices/OpalIceMaker/index.js';
4
4
  import axios from 'axios';
5
5
  import pkg from 'lodash';
6
6
  import ws from 'ws';
7
+ import { SmartHQAdvantium } from './devices/advantium.js';
7
8
  import { SmartHQAirConditioner } from './devices/airConditioner.js';
9
+ import { SmartHQBeverageCenter } from './devices/beverageCenter.js';
10
+ import { SmartHQClothesDryer } from './devices/clothesDryer.js';
11
+ import { SmartHQClothesWasher } from './devices/clothesWasher.js';
12
+ import { SmartHQCoffeeMaker } from './devices/coffeeMaker.js';
8
13
  import { SmartHQDishWasher } from './devices/dishwasher.js';
14
+ import { SmartHQHood } from './devices/hood.js';
15
+ import { SmartHQMicrowave } from './devices/microwave.js';
9
16
  import { SmartHQOven } from './devices/oven.js';
10
17
  import { SmartHQRefrigerator } from './devices/refrigerator.js';
18
+ import { SmartHQWaterFilter } from './devices/waterFilter.js';
19
+ import { SmartHQWaterHeater } from './devices/waterHeater.js';
20
+ import { SmartHQWaterSoftener } from './devices/waterSoftener.js';
11
21
  import getAccessToken, { refreshAccessToken } from './getAccessToken.js';
12
22
  import { API_URL, ERD_CODES, ERD_TYPES, KEEPALIVE_TIMEOUT, PLATFORM_NAME, PLUGIN_NAME } from './settings.js';
13
23
  const { find } = pkg;
@@ -33,6 +43,9 @@ export class SmartHQPlatform {
33
43
  platformUpdateRate;
34
44
  debugMode;
35
45
  version;
46
+ // Matter support tracking
47
+ matterEnabled = false;
48
+ matterAvailable = false;
36
49
  constructor(log, config, api) {
37
50
  this.accessories = [];
38
51
  this.api = api;
@@ -60,6 +73,8 @@ export class SmartHQPlatform {
60
73
  this.Service = this.api.hap.Service;
61
74
  this.Characteristic = this.api.hap.Characteristic;
62
75
  this.debugLog(`Finished initializing platform: ${config.name}`);
76
+ // Check Matter availability and enabled status
77
+ this.checkMatterSupport();
63
78
  // verify the config
64
79
  (async () => {
65
80
  try {
@@ -112,17 +127,57 @@ export class SmartHQPlatform {
112
127
  }
113
128
  }
114
129
  async startRefreshTokenLogic() {
130
+ if (!this.tokenSet) {
131
+ throw new Error('Token set is undefined');
132
+ }
115
133
  if (this.tokenSet.refresh_token) {
116
134
  try {
117
135
  this.tokenSet = await refreshAccessToken(this.tokenSet.refresh_token);
118
136
  }
119
137
  catch (e) {
120
- await this.errorLog(`Failed to refresh Access Token, Error Message: ${e.message ?? e}, Submit Bugs Here: https://bit.ly/smarthq-bug-report`);
138
+ await this.errorLog(`Failed to refresh Access Token, Error Message: ${e.message ?? e}`);
139
+ // Handle invalid_grant error (expired/revoked refresh token)
140
+ if (e.error === 'invalid_grant' || e.message?.includes('invalid_grant') || e.message?.includes('Invalid refresh token')) {
141
+ await this.warnLog('Refresh token is invalid or expired. Attempting to re-authenticate with username and password...');
142
+ // Try to get a new token using username/password
143
+ const { username, password } = this.config.credentials ?? {};
144
+ if (username && password) {
145
+ try {
146
+ this.tokenSet = await getAccessToken(username, password);
147
+ await this.successLog('Successfully re-authenticated with credentials');
148
+ // Set up axios with new token
149
+ if (this.tokenSet.access_token) {
150
+ axios.defaults.headers.common = {
151
+ Authorization: `Bearer ${this.tokenSet.access_token}`,
152
+ };
153
+ // Schedule next refresh
154
+ if (this.tokenSet.expires_in) {
155
+ setTimeout(this.startRefreshTokenLogic.bind(this), 1000 * (this.tokenSet.expires_in - 2000));
156
+ }
157
+ return; // Successfully recovered
158
+ }
159
+ }
160
+ catch (reAuthError) {
161
+ await this.errorLog(`Failed to re-authenticate: ${reAuthError.message ?? reAuthError}`);
162
+ await this.errorLog('Please verify your SmartHQ credentials are correct in the Homebridge config');
163
+ await this.errorLog('You may need to log in to the GE SmartHQ app to ensure your account is active');
164
+ }
165
+ }
166
+ else {
167
+ await this.errorLog('No credentials available for re-authentication');
168
+ await this.errorLog('Please ensure username and password are set in your Homebridge config');
169
+ }
170
+ }
171
+ await this.errorLog('Submit Bugs Here: https://bit.ly/smarthq-bug-report');
172
+ throw e; // Re-throw to stop execution only if recovery failed
121
173
  }
122
174
  }
123
175
  else {
124
176
  throw new Error('Refresh token is undefined');
125
177
  }
178
+ if (!this.tokenSet.access_token) {
179
+ throw new Error('Access token is undefined after refresh');
180
+ }
126
181
  axios.defaults.headers.common = {
127
182
  Authorization: `Bearer ${this.tokenSet.access_token}`,
128
183
  };
@@ -148,12 +203,14 @@ export class SmartHQPlatform {
148
203
  }
149
204
  catch (e) {
150
205
  await this.errorLog(`discoverDevices, Failed to get Access Token, Error Message: ${e.message ?? e}, Submit Bugs Here: https://bit.ly/smarthq-bug-report`);
206
+ return; // Stop execution if authentication fails
151
207
  }
152
208
  try {
153
209
  await this.startRefreshTokenLogic();
154
210
  }
155
211
  catch (e) {
156
212
  await this.errorLog(`discoverDevices, Failed to start Refresh Token Logic, Error Message: ${e.message ?? e}, Submit Bugs Here: https://bit.ly/smarthq-bug-report`);
213
+ return; // Stop execution if token refresh setup fails
157
214
  }
158
215
  try {
159
216
  const wssData = await axios.get('/websocket');
@@ -226,6 +283,37 @@ export class SmartHQPlatform {
226
283
  case 'Split Air Conditioner':
227
284
  await this.createSmartHQAirConditioner(userId, device, details, features);
228
285
  break;
286
+ case 'Hood':
287
+ await this.createSmartHQHood(userId, device, details, features);
288
+ break;
289
+ case 'Clothes Washer':
290
+ await this.createSmartHQClothesWasher(userId, device, details, features);
291
+ break;
292
+ case 'Clothes Dryer':
293
+ await this.createSmartHQClothesDryer(userId, device, details, features);
294
+ break;
295
+ case 'Whole Home Water Filter':
296
+ await this.createSmartHQWaterFilter(userId, device, details, features);
297
+ break;
298
+ case 'Whole Home Water Softener':
299
+ await this.createSmartHQWaterSoftener(userId, device, details, features);
300
+ break;
301
+ case 'Whole Home Water Heater':
302
+ await this.createSmartHQWaterHeater(userId, device, details, features);
303
+ break;
304
+ case 'Advantium':
305
+ await this.createSmartHQAdvantium(userId, device, details, features);
306
+ break;
307
+ case 'Microwave':
308
+ await this.createSmartHQMicrowave(userId, device, details, features);
309
+ break;
310
+ case 'Coffee Maker':
311
+ case 'Espresso Maker':
312
+ await this.createSmartHQCoffeeMaker(userId, device, details, features);
313
+ break;
314
+ case 'Beverage Center':
315
+ await this.createSmartHQBeverageCenter(userId, device, details, features);
316
+ break;
229
317
  default:
230
318
  await this.warnLog(`Device Type Not Supported: ${device.type}`);
231
319
  break;
@@ -241,232 +329,786 @@ export class SmartHQPlatform {
241
329
  }
242
330
  }
243
331
  async createSmartHQDishWasher(userId, device, details, features) {
244
- const uuid = this.api.hap.uuid.generate(device.applianceId);
245
- // see if an accessory with the same uuid has already been registered and restored from
246
- // the cached devices we stored in the `configureAccessory` method above
332
+ // Merge device data
333
+ const deviceData = { brand: 'GE', ...details, ...features, ...device };
334
+ // Determine protocol (Matter or HAP)
335
+ deviceData.useMatter = this.shouldUseMatter(deviceData);
336
+ const uuid = this.api.hap.uuid.generate(deviceData.applianceId);
247
337
  const existingAccessory = this.accessories.find(accessory => accessory.UUID === uuid);
338
+ const protocol = deviceData.useMatter ? 'Matter' : 'HAP';
248
339
  if (existingAccessory) {
249
340
  // the accessory already exists
250
- if (!device.hide_device) {
251
- // if you need to update the accessory.context then you should run `api.updatePlatformAccessories`. eg.:
252
- existingAccessory.context.device = device;
253
- existingAccessory.context = { device: { brand: 'GE', ...details, ...features }, userId };
254
- existingAccessory.displayName = await this.validateAndCleanDisplayName(device.nickname, 'nickname', device.nickname);
255
- existingAccessory.context.device.firmware = device.firmware ?? await this.getVersion();
256
- this.api.updatePlatformAccessories([existingAccessory]);
257
- // Restore accessory
258
- this.infoLog(`Restoring existing accessory from cache: ${existingAccessory.displayName}`);
259
- // create the accessory handler for the restored accessory
260
- // this is imported from `platformAccessory.ts`
261
- new SmartHQDishWasher(this, existingAccessory, device);
262
- this.debugLog(`${device.nickname} uuid: ${device.applianceId}`);
341
+ if (!deviceData.hide_device) {
342
+ // Check if protocol changed to Matter - if so, remove from HAP bridge
343
+ if (this.shouldUnregisterForMatter(existingAccessory, deviceData)) {
344
+ // Device removed from HAP, will be registered as Matter accessory in device class
345
+ const accessory = new this.api.platformAccessory(deviceData.nickname, uuid);
346
+ accessory.context.device = deviceData;
347
+ accessory.context = { device: deviceData, userId };
348
+ accessory.displayName = await this.validateAndCleanDisplayName(deviceData.nickname, 'nickname', deviceData.nickname);
349
+ accessory.context.device.firmware = deviceData.firmware ?? await this.getVersion();
350
+ new SmartHQDishWasher(this, accessory, deviceData);
351
+ this.debugLog(`${deviceData.nickname} uuid: ${deviceData.applianceId}`);
352
+ }
353
+ else {
354
+ // Still using HAP, restore normally
355
+ existingAccessory.context.device = deviceData;
356
+ existingAccessory.context = { device: deviceData, userId };
357
+ existingAccessory.displayName = await this.validateAndCleanDisplayName(deviceData.nickname, 'nickname', deviceData.nickname);
358
+ existingAccessory.context.device.firmware = deviceData.firmware ?? await this.getVersion();
359
+ this.api.updatePlatformAccessories([existingAccessory]);
360
+ this.infoLog(`[${protocol}] Restoring existing accessory from cache: ${existingAccessory.displayName}`);
361
+ new SmartHQDishWasher(this, existingAccessory, deviceData);
362
+ await this.debugLog(`${deviceData.nickname} uuid: ${deviceData.applianceId}`);
363
+ }
263
364
  }
264
365
  else {
265
366
  this.unregisterPlatformAccessories(existingAccessory);
266
367
  }
267
368
  }
268
- else if (!device.hide_device && !existingAccessory) {
269
- this.infoLog(`Adding new accessory: ${device.nickname}`);
270
- const accessory = new this.api.platformAccessory(device.nickname, uuid);
271
- // store a copy of the device object in the `accessory.context`
272
- // the `context` property can be used to store any data about the accessory you may need
273
- accessory.context.device = device;
274
- accessory.context = { device: { brand: 'GE', ...details, ...features }, userId };
275
- accessory.displayName = await this.validateAndCleanDisplayName(device.nickname, 'nickname', device.nickname);
276
- accessory.context.device.firmware = device.firmware ?? await this.getVersion();
277
- // the accessory does not yet exist, so we need to create it
278
- // create the accessory handler for the newly create accessory
279
- // this is imported from `platformAccessory.ts`
280
- new SmartHQDishWasher(this, accessory, device);
281
- this.debugLog(`${device.nickname} uuid: ${device.applianceId}`);
282
- // link the accessory to your platform
369
+ else if (!deviceData.hide_device && !existingAccessory) {
370
+ this.infoLog(`[${protocol}] Adding new accessory: ${deviceData.nickname}`);
371
+ const accessory = new this.api.platformAccessory(deviceData.nickname, uuid);
372
+ accessory.context.device = deviceData;
373
+ accessory.context = { device: deviceData, userId };
374
+ accessory.displayName = await this.validateAndCleanDisplayName(deviceData.nickname, 'nickname', deviceData.nickname);
375
+ accessory.context.device.firmware = deviceData.firmware ?? await this.getVersion();
376
+ new SmartHQDishWasher(this, accessory, deviceData);
377
+ this.debugLog(`${deviceData.nickname} uuid: ${deviceData.applianceId}`);
283
378
  this.api.registerPlatformAccessories(PLUGIN_NAME, PLATFORM_NAME, [accessory]);
284
379
  this.accessories.push(accessory);
285
380
  }
286
381
  else {
287
- this.debugErrorLog(`Unable to Register new device: ${JSON.stringify(device.nickname)}`);
382
+ this.debugErrorLog(`Unable to Register new device: ${JSON.stringify(deviceData.nickname)}`);
288
383
  }
289
384
  }
290
385
  async createSmartHQOven(userId, device, details, features) {
291
- const uuid = this.api.hap.uuid.generate(device.applianceId);
292
- // see if an accessory with the same uuid has already been registered and restored from
293
- // the cached devices we stored in the `configureAccessory` method above
386
+ // Merge device data
387
+ const deviceData = { brand: 'GE', ...details, ...features, ...device };
388
+ // Determine protocol (Matter or HAP)
389
+ deviceData.useMatter = this.shouldUseMatter(deviceData);
390
+ const uuid = this.api.hap.uuid.generate(deviceData.applianceId);
294
391
  const existingAccessory = this.accessories.find(accessory => accessory.UUID === uuid);
392
+ const protocol = deviceData.useMatter ? 'Matter' : 'HAP';
295
393
  if (existingAccessory) {
296
394
  // the accessory already exists
297
- if (!device.hide_device) {
298
- // if you need to update the accessory.context then you should run `api.updatePlatformAccessories`. eg.:
299
- existingAccessory.context.device = device;
300
- existingAccessory.context = { device: { brand: 'GE', ...details, ...features }, userId };
301
- existingAccessory.displayName = await this.validateAndCleanDisplayName(device.nickname, 'nickname', device.nickname);
302
- existingAccessory.context.device.firmware = device.firmware ?? await this.getVersion();
303
- this.api.updatePlatformAccessories([existingAccessory]);
304
- // Restore accessory
305
- this.infoLog(`Restoring existing accessory from cache: ${existingAccessory.displayName}`);
306
- // create the accessory handler for the restored accessory
307
- // this is imported from `platformAccessory.ts`
308
- new SmartHQOven(this, existingAccessory, device);
309
- await this.debugLog(`${device.nickname} uuid: ${device.applianceId}`);
395
+ if (!deviceData.hide_device) {
396
+ // Check if protocol changed to Matter - if so, remove from HAP bridge
397
+ if (this.shouldUnregisterForMatter(existingAccessory, deviceData)) {
398
+ const accessory = new this.api.platformAccessory(deviceData.nickname, uuid);
399
+ accessory.context.device = deviceData;
400
+ accessory.context = { device: deviceData, userId };
401
+ accessory.displayName = await this.validateAndCleanDisplayName(deviceData.nickname, 'nickname', deviceData.nickname);
402
+ accessory.context.device.firmware = deviceData.firmware ?? await this.getVersion();
403
+ new SmartHQOven(this, accessory, deviceData);
404
+ this.debugLog(`${deviceData.nickname} uuid: ${deviceData.applianceId}`);
405
+ }
406
+ else {
407
+ existingAccessory.context.device = deviceData;
408
+ existingAccessory.context = { device: deviceData, userId };
409
+ existingAccessory.displayName = await this.validateAndCleanDisplayName(deviceData.nickname, 'nickname', deviceData.nickname);
410
+ existingAccessory.context.device.firmware = deviceData.firmware ?? await this.getVersion();
411
+ this.api.updatePlatformAccessories([existingAccessory]);
412
+ this.infoLog(`[${protocol}] Restoring existing accessory from cache: ${existingAccessory.displayName}`);
413
+ new SmartHQOven(this, existingAccessory, deviceData);
414
+ await this.debugLog(`${deviceData.nickname} uuid: ${deviceData.applianceId}`);
415
+ }
310
416
  }
311
417
  else {
312
418
  this.unregisterPlatformAccessories(existingAccessory);
313
419
  }
314
420
  }
315
- else if (!device.hide_device && !existingAccessory) {
316
- this.infoLog(`Adding new accessory: ${device.nickname}`);
317
- const accessory = new this.api.platformAccessory(device.nickname, uuid);
318
- // store a copy of the device object in the `accessory.context`
319
- // the `context` property can be used to store any data about the accessory you may need
320
- accessory.context.device = device;
321
- accessory.context = { device: { brand: 'GE', ...details, ...features }, userId };
322
- accessory.displayName = await this.validateAndCleanDisplayName(device.nickname, 'nickname', device.nickname);
323
- accessory.context.device.firmware = device.firmware ?? await this.getVersion();
324
- // the accessory does not yet exist, so we need to create it
325
- // create the accessory handler for the newly create accessory
326
- // this is imported from `platformAccessory.ts`
327
- new SmartHQOven(this, accessory, device);
328
- this.debugLog(`${device.nickname} uuid: ${device.applianceId}`);
329
- // link the accessory to your platform
421
+ else if (!deviceData.hide_device && !existingAccessory) {
422
+ this.infoLog(`[${protocol}] Adding new accessory: ${deviceData.nickname}`);
423
+ const accessory = new this.api.platformAccessory(deviceData.nickname, uuid);
424
+ accessory.context.device = deviceData;
425
+ accessory.context = { device: deviceData, userId };
426
+ accessory.displayName = await this.validateAndCleanDisplayName(deviceData.nickname, 'nickname', deviceData.nickname);
427
+ accessory.context.device.firmware = deviceData.firmware ?? await this.getVersion();
428
+ new SmartHQOven(this, accessory, deviceData);
429
+ this.debugLog(`${deviceData.nickname} uuid: ${deviceData.applianceId}`);
330
430
  this.api.registerPlatformAccessories(PLUGIN_NAME, PLATFORM_NAME, [accessory]);
331
431
  this.accessories.push(accessory);
332
432
  }
333
433
  else {
334
- this.debugErrorLog(`Unable to Register new device: ${JSON.stringify(device.nickname)}`);
434
+ this.debugErrorLog(`Unable to Register new device: ${JSON.stringify(deviceData.nickname)}`);
335
435
  }
336
436
  }
337
437
  async createSmartHQIceMaker(userId, device, details, features) {
338
- const uuid = this.api.hap.uuid.generate(device.applianceId);
438
+ // Merge device data
439
+ const deviceData = { brand: 'GE', ...details, ...features, ...device };
440
+ // Determine protocol (Matter or HAP)
441
+ deviceData.useMatter = this.shouldUseMatter(deviceData);
442
+ const uuid = this.api.hap.uuid.generate(deviceData.applianceId);
339
443
  // see if an accessory with the same uuid has already been registered and restored from
340
444
  // the cached devices we stored in the `configureAccessory` method above
341
445
  const existingAccessory = this.accessories.find(accessory => accessory.UUID === uuid);
446
+ const protocol = deviceData.useMatter ? 'Matter' : 'HAP';
342
447
  if (existingAccessory) {
343
448
  // the accessory already exists
344
- if (!device.hide_device) {
345
- // if you need to update the accessory.context then you should run `api.updatePlatformAccessories`. eg.:
346
- existingAccessory.context.device = device;
347
- existingAccessory.context = { device: { brand: 'GE', ...details, ...features }, userId };
348
- existingAccessory.displayName = await this.validateAndCleanDisplayName(device.nickname, 'nickname', device.nickname);
349
- existingAccessory.context.device.firmware = device.firmware ?? await this.getVersion();
350
- this.api.updatePlatformAccessories([existingAccessory]);
351
- // Restore accessory
352
- // create the accessory handler for the restored accessory
353
- // this is imported from `platformAccessory.ts`
354
- new SmartHQIceMaker(this, existingAccessory, device);
355
- this.debugLog(`${device.nickname} uuid: ${device.applianceId}`);
449
+ if (!deviceData.hide_device) {
450
+ // Check if protocol changed to Matter - if so, remove from HAP bridge
451
+ if (this.shouldUnregisterForMatter(existingAccessory, deviceData)) {
452
+ const accessory = new this.api.platformAccessory(deviceData.nickname, uuid);
453
+ accessory.context.device = deviceData;
454
+ accessory.context = { device: deviceData, userId };
455
+ accessory.displayName = await this.validateAndCleanDisplayName(deviceData.nickname, 'nickname', deviceData.nickname);
456
+ accessory.context.device.firmware = deviceData.firmware ?? await this.getVersion();
457
+ new SmartHQIceMaker(this, accessory, deviceData);
458
+ this.debugLog(`${deviceData.nickname} uuid: ${deviceData.applianceId}`);
459
+ }
460
+ else {
461
+ // if you need to update the accessory.context then you should run `api.updatePlatformAccessories`. eg.:
462
+ existingAccessory.context.device = deviceData;
463
+ existingAccessory.context = { device: deviceData, userId };
464
+ existingAccessory.displayName = await this.validateAndCleanDisplayName(deviceData.nickname, 'nickname', deviceData.nickname);
465
+ existingAccessory.context.device.firmware = deviceData.firmware ?? await this.getVersion();
466
+ this.api.updatePlatformAccessories([existingAccessory]);
467
+ // Restore accessory
468
+ // create the accessory handler for the restored accessory
469
+ // this is imported from `platformAccessory.ts`
470
+ this.infoLog(`[${protocol}] Restoring existing accessory from cache: ${existingAccessory.displayName}`);
471
+ new SmartHQIceMaker(this, existingAccessory, deviceData);
472
+ this.debugLog(`${deviceData.nickname} uuid: ${deviceData.applianceId}`);
473
+ }
356
474
  }
357
475
  else {
358
476
  this.unregisterPlatformAccessories(existingAccessory);
359
477
  }
360
478
  }
361
- else if (!device.hide_device && !existingAccessory) {
362
- this.infoLog(`Adding new accessory: ${device.nickname}`);
363
- const accessory = new this.api.platformAccessory(device.nickname, uuid);
479
+ else if (!deviceData.hide_device && !existingAccessory) {
480
+ this.infoLog(`[${protocol}] Adding new accessory: ${deviceData.nickname}`);
481
+ const accessory = new this.api.platformAccessory(deviceData.nickname, uuid);
364
482
  // store a copy of the device object in the `accessory.context`
365
483
  // the `context` property can be used to store any data about the accessory you may need
366
- accessory.context.device = device;
367
- accessory.context = { device: { brand: 'GE', ...details, ...features }, userId };
368
- accessory.displayName = await this.validateAndCleanDisplayName(device.nickname, 'nickname', device.nickname);
369
- accessory.context.device.firmware = device.firmware ?? await this.getVersion();
484
+ accessory.context.device = deviceData;
485
+ accessory.context = { device: deviceData, userId };
486
+ accessory.displayName = await this.validateAndCleanDisplayName(deviceData.nickname, 'nickname', deviceData.nickname);
487
+ accessory.context.device.firmware = deviceData.firmware ?? await this.getVersion();
370
488
  // the accessory does not yet exist, so we need to create it
371
489
  // create the accessory handler for the newly create accessory
372
490
  // this is imported from `platformAccessory.ts`
373
- new SmartHQIceMaker(this, accessory, device);
374
- this.debugLog(`${device.nickname} uuid: ${device.applianceId}`);
491
+ new SmartHQIceMaker(this, accessory, deviceData);
492
+ this.debugLog(`${deviceData.nickname} uuid: ${deviceData.applianceId}`);
375
493
  // link the accessory to your platform
376
494
  this.api.registerPlatformAccessories(PLUGIN_NAME, PLATFORM_NAME, [accessory]);
377
495
  this.accessories.push(accessory);
378
496
  }
379
497
  else {
380
- this.debugErrorLog(`Unable to Register new device: ${JSON.stringify(device.nickname)}`);
498
+ this.debugErrorLog(`Unable to Register new device: ${JSON.stringify(deviceData.nickname)}`);
381
499
  }
382
500
  }
501
+ /**
502
+ * Create Refrigerator accessory (unified HAP/Matter)
503
+ * The SmartHQRefrigerator class now handles both protocols internally
504
+ */
383
505
  async createSmartHQRefrigerator(userId, device, details, features) {
384
- const uuid = this.api.hap.uuid.generate(device.applianceId);
385
- // see if an accessory with the same uuid has already been registered and restored from
386
- // the cached devices we stored in the `configureAccessory` method above
506
+ // Merge device data
507
+ const deviceData = { brand: 'GE', ...details, ...features, ...device };
508
+ // Determine protocol (Matter or HAP)
509
+ deviceData.useMatter = this.shouldUseMatter(deviceData);
510
+ const uuid = this.api.hap.uuid.generate(deviceData.applianceId);
387
511
  const existingAccessory = this.accessories.find(accessory => accessory.UUID === uuid);
512
+ const protocol = deviceData.useMatter ? 'Matter' : 'HAP';
388
513
  if (existingAccessory) {
389
514
  // the accessory already exists
390
- if (!device.hide_device) {
391
- // if you need to update the accessory.context then you should run `api.updatePlatformAccessories`. eg.:
392
- existingAccessory.context.device = device;
393
- existingAccessory.context = { device: { brand: 'GE', ...details, ...features }, userId };
394
- existingAccessory.displayName = await this.validateAndCleanDisplayName(device.nickname, 'nickname', device.nickname);
395
- existingAccessory.context.device.firmware = device.firmware ?? await this.getVersion();
396
- this.api.updatePlatformAccessories([existingAccessory]);
397
- // Restore accessory
398
- this.infoLog(`Restoring existing accessory from cache: ${existingAccessory.displayName}`);
399
- // create the accessory handler for the restored accessory
400
- // this is imported from `platformAccessory.ts`
401
- new SmartHQRefrigerator(this, existingAccessory, device);
402
- await this.debugLog(`${device.nickname} uuid: ${device.applianceId}`);
515
+ if (!deviceData.hide_device) {
516
+ // Check if protocol changed to Matter - if so, remove from HAP bridge
517
+ if (this.shouldUnregisterForMatter(existingAccessory, deviceData)) {
518
+ const accessory = new this.api.platformAccessory(deviceData.nickname, uuid);
519
+ accessory.context.device = deviceData;
520
+ accessory.context = { device: deviceData, userId };
521
+ accessory.displayName = await this.validateAndCleanDisplayName(deviceData.nickname, 'nickname', deviceData.nickname);
522
+ accessory.context.device.firmware = deviceData.firmware ?? await this.getVersion();
523
+ new SmartHQRefrigerator(this, accessory, deviceData);
524
+ this.debugLog(`${deviceData.nickname} uuid: ${deviceData.applianceId}`);
525
+ }
526
+ else {
527
+ existingAccessory.context.device = deviceData;
528
+ existingAccessory.context = { device: deviceData, userId };
529
+ existingAccessory.displayName = await this.validateAndCleanDisplayName(deviceData.nickname, 'nickname', deviceData.nickname);
530
+ existingAccessory.context.device.firmware = deviceData.firmware ?? await this.getVersion();
531
+ this.api.updatePlatformAccessories([existingAccessory]);
532
+ this.infoLog(`[${protocol}] Restoring existing accessory from cache: ${existingAccessory.displayName}`);
533
+ new SmartHQRefrigerator(this, existingAccessory, deviceData);
534
+ await this.debugLog(`${deviceData.nickname} uuid: ${deviceData.applianceId}`);
535
+ }
403
536
  }
404
537
  else {
405
538
  this.unregisterPlatformAccessories(existingAccessory);
406
539
  }
407
540
  }
408
- else if (!device.hide_device && !existingAccessory) {
409
- this.infoLog(`Adding new accessory: ${device.nickname}`);
410
- const accessory = new this.api.platformAccessory(device.nickname, uuid);
411
- // store a copy of the device object in the `accessory.context`
412
- // the `context` property can be used to store any data about the accessory you may need
413
- accessory.context.device = device;
414
- accessory.context = { device: { brand: 'GE', ...details, ...features }, userId };
415
- accessory.displayName = await this.validateAndCleanDisplayName(device.nickname, 'nickname', device.nickname);
416
- accessory.context.device.firmware = device.firmware ?? await this.getVersion();
417
- // the accessory does not yet exist, so we need to create it
418
- // create the accessory handler for the newly create accessory
419
- // this is imported from `platformAccessory.ts`
420
- new SmartHQRefrigerator(this, accessory, device);
421
- this.debugLog(`${device.nickname} uuid: ${device.applianceId}`);
422
- // link the accessory to your platform
541
+ else if (!deviceData.hide_device && !existingAccessory) {
542
+ this.infoLog(`[${protocol}] Adding new accessory: ${deviceData.nickname}`);
543
+ const accessory = new this.api.platformAccessory(deviceData.nickname, uuid);
544
+ accessory.context.device = deviceData;
545
+ accessory.context = { device: deviceData, userId };
546
+ accessory.displayName = await this.validateAndCleanDisplayName(deviceData.nickname, 'nickname', deviceData.nickname);
547
+ accessory.context.device.firmware = deviceData.firmware ?? await this.getVersion();
548
+ new SmartHQRefrigerator(this, accessory, deviceData);
549
+ this.debugLog(`${deviceData.nickname} uuid: ${deviceData.applianceId}`);
423
550
  this.api.registerPlatformAccessories(PLUGIN_NAME, PLATFORM_NAME, [accessory]);
424
551
  this.accessories.push(accessory);
425
552
  }
426
553
  else {
427
- this.debugErrorLog(`Unable to Register new device: ${JSON.stringify(device.nickname)}`);
554
+ this.debugErrorLog(`Unable to Register new device: ${JSON.stringify(deviceData.nickname)}`);
428
555
  }
429
556
  }
430
557
  async createSmartHQAirConditioner(userId, device, details, features) {
431
- const uuid = this.api.hap.uuid.generate(device.applianceId);
432
- // see if an accessory with the same uuid has already been registered and restored from
433
- // the cached devices we stored in the `configureAccessory` method above
558
+ // Merge device data
559
+ const deviceData = { brand: 'GE', ...details, ...features, ...device };
560
+ // Determine protocol (Matter or HAP)
561
+ deviceData.useMatter = this.shouldUseMatter(deviceData);
562
+ const uuid = this.api.hap.uuid.generate(deviceData.applianceId);
434
563
  const existingAccessory = this.accessories.find(accessory => accessory.UUID === uuid);
564
+ const protocol = deviceData.useMatter ? 'Matter' : 'HAP';
435
565
  if (existingAccessory) {
436
566
  // the accessory already exists
437
- if (!device.hide_device) {
438
- // if you need to update the accessory.context then you should run `api.updatePlatformAccessories`. eg.:
439
- existingAccessory.context.device = device;
440
- existingAccessory.context = { device: { brand: 'GE', ...details, ...features }, userId };
441
- existingAccessory.displayName = await this.validateAndCleanDisplayName(device.nickname, 'nickname', device.nickname);
442
- existingAccessory.context.device.firmware = device.firmware ?? await this.getVersion();
443
- this.api.updatePlatformAccessories([existingAccessory]);
444
- // Restore accessory
445
- this.infoLog(`Restoring existing accessory from cache: ${existingAccessory.displayName}`);
446
- // create the accessory handler for the restored accessory
447
- // this is imported from `platformAccessory.ts`
448
- new SmartHQAirConditioner(this, existingAccessory, device);
449
- this.debugLog(`${device.nickname} uuid: ${device.applianceId}`);
567
+ if (!deviceData.hide_device) {
568
+ // Check if protocol changed to Matter - if so, remove from HAP bridge
569
+ if (this.shouldUnregisterForMatter(existingAccessory, deviceData)) {
570
+ const accessory = new this.api.platformAccessory(deviceData.nickname, uuid);
571
+ accessory.context.device = deviceData;
572
+ accessory.context = { device: deviceData, userId };
573
+ accessory.displayName = await this.validateAndCleanDisplayName(deviceData.nickname, 'nickname', deviceData.nickname);
574
+ accessory.context.device.firmware = deviceData.firmware ?? await this.getVersion();
575
+ new SmartHQAirConditioner(this, accessory, deviceData);
576
+ this.debugLog(`${deviceData.nickname} uuid: ${deviceData.applianceId}`);
577
+ }
578
+ else {
579
+ existingAccessory.context.device = deviceData;
580
+ existingAccessory.context = { device: deviceData, userId };
581
+ existingAccessory.displayName = await this.validateAndCleanDisplayName(deviceData.nickname, 'nickname', deviceData.nickname);
582
+ existingAccessory.context.device.firmware = deviceData.firmware ?? await this.getVersion();
583
+ this.api.updatePlatformAccessories([existingAccessory]);
584
+ this.infoLog(`[${protocol}] Restoring existing accessory from cache: ${existingAccessory.displayName}`);
585
+ new SmartHQAirConditioner(this, existingAccessory, deviceData);
586
+ this.debugLog(`${deviceData.nickname} uuid: ${deviceData.applianceId}`);
587
+ }
450
588
  }
451
589
  else {
452
590
  this.unregisterPlatformAccessories(existingAccessory);
453
591
  }
454
592
  }
455
- else if (!device.hide_device && !existingAccessory) {
456
- this.infoLog(`Adding new accessory: ${device.nickname}`);
457
- const accessory = new this.api.platformAccessory(device.nickname, uuid);
458
- // store a copy of the device object in the `accessory.context`
459
- // the `context` property can be used to store any data about the accessory you may need
460
- accessory.context.device = device;
461
- accessory.context = { device: { brand: 'GE', ...details, ...features }, userId };
462
- accessory.displayName = await this.validateAndCleanDisplayName(device.nickname, 'nickname', device.nickname);
463
- accessory.context.device.firmware = device.firmware ?? await this.getVersion();
464
- // the accessory does not yet exist, so we need to create it
465
- // create the accessory handler for the newly create accessory
466
- // this is imported from `platformAccessory.ts`
467
- new SmartHQAirConditioner(this, accessory, device);
468
- this.debugLog(`${device.nickname} uuid: ${device.applianceId}`);
469
- // link the accessory to your platform
593
+ else if (!deviceData.hide_device && !existingAccessory) {
594
+ this.infoLog(`[${protocol}] Adding new accessory: ${deviceData.nickname}`);
595
+ const accessory = new this.api.platformAccessory(deviceData.nickname, uuid);
596
+ accessory.context.device = deviceData;
597
+ accessory.context = { device: deviceData, userId };
598
+ accessory.displayName = await this.validateAndCleanDisplayName(deviceData.nickname, 'nickname', deviceData.nickname);
599
+ accessory.context.device.firmware = deviceData.firmware ?? await this.getVersion();
600
+ new SmartHQAirConditioner(this, accessory, deviceData);
601
+ this.debugLog(`${deviceData.nickname} uuid: ${deviceData.applianceId}`);
602
+ this.api.registerPlatformAccessories(PLUGIN_NAME, PLATFORM_NAME, [accessory]);
603
+ this.accessories.push(accessory);
604
+ }
605
+ else {
606
+ this.debugErrorLog(`Unable to Register new device: ${JSON.stringify(deviceData.nickname)}`);
607
+ }
608
+ }
609
+ async createSmartHQHood(userId, device, details, features) {
610
+ // Merge device data
611
+ const deviceData = { brand: 'GE', ...details, ...features, ...device };
612
+ // Determine protocol (Matter or HAP)
613
+ deviceData.useMatter = this.shouldUseMatter(deviceData);
614
+ const uuid = this.api.hap.uuid.generate(deviceData.applianceId);
615
+ const existingAccessory = this.accessories.find(accessory => accessory.UUID === uuid);
616
+ const protocol = deviceData.useMatter ? 'Matter' : 'HAP';
617
+ if (existingAccessory) {
618
+ if (!deviceData.hide_device) {
619
+ // Check if protocol changed to Matter - if so, remove from HAP bridge
620
+ if (this.shouldUnregisterForMatter(existingAccessory, deviceData)) {
621
+ const accessory = new this.api.platformAccessory(deviceData.nickname, uuid);
622
+ accessory.context.device = deviceData;
623
+ accessory.context = { device: deviceData, userId };
624
+ accessory.displayName = await this.validateAndCleanDisplayName(deviceData.nickname, 'nickname', deviceData.nickname);
625
+ accessory.context.device.firmware = deviceData.firmware ?? await this.getVersion();
626
+ new SmartHQHood(this, accessory, deviceData);
627
+ this.debugLog(`${deviceData.nickname} uuid: ${deviceData.applianceId}`);
628
+ }
629
+ else {
630
+ existingAccessory.context.device = deviceData;
631
+ existingAccessory.context = { device: deviceData, userId };
632
+ existingAccessory.displayName = await this.validateAndCleanDisplayName(deviceData.nickname, 'nickname', deviceData.nickname);
633
+ existingAccessory.context.device.firmware = deviceData.firmware ?? await this.getVersion();
634
+ this.api.updatePlatformAccessories([existingAccessory]);
635
+ this.infoLog(`[${protocol}] Restoring existing accessory from cache: ${existingAccessory.displayName}`);
636
+ new SmartHQHood(this, existingAccessory, deviceData);
637
+ this.debugLog(`${deviceData.nickname} uuid: ${deviceData.applianceId}`);
638
+ }
639
+ }
640
+ else {
641
+ this.unregisterPlatformAccessories(existingAccessory);
642
+ }
643
+ }
644
+ else if (!deviceData.hide_device && !existingAccessory) {
645
+ this.infoLog(`[${protocol}] Adding new accessory: ${deviceData.nickname}`);
646
+ const accessory = new this.api.platformAccessory(deviceData.nickname, uuid);
647
+ accessory.context.device = deviceData;
648
+ accessory.context = { device: deviceData, userId };
649
+ accessory.displayName = await this.validateAndCleanDisplayName(deviceData.nickname, 'nickname', deviceData.nickname);
650
+ accessory.context.device.firmware = deviceData.firmware ?? await this.getVersion();
651
+ new SmartHQHood(this, accessory, deviceData);
652
+ this.debugLog(`${deviceData.nickname} uuid: ${deviceData.applianceId}`);
653
+ this.api.registerPlatformAccessories(PLUGIN_NAME, PLATFORM_NAME, [accessory]);
654
+ this.accessories.push(accessory);
655
+ }
656
+ else {
657
+ this.debugErrorLog(`Unable to Register new device: ${JSON.stringify(deviceData.nickname)}`);
658
+ }
659
+ }
660
+ async createSmartHQClothesWasher(userId, device, details, features) {
661
+ // Merge device data
662
+ const deviceData = { brand: 'GE', ...details, ...features, ...device };
663
+ // Determine protocol (Matter or HAP)
664
+ deviceData.useMatter = this.shouldUseMatter(deviceData);
665
+ const uuid = this.api.hap.uuid.generate(deviceData.applianceId);
666
+ const existingAccessory = this.accessories.find(accessory => accessory.UUID === uuid);
667
+ const protocol = deviceData.useMatter ? 'Matter' : 'HAP';
668
+ if (existingAccessory) {
669
+ if (!deviceData.hide_device) {
670
+ // Check if protocol changed to Matter - if so, remove from HAP bridge
671
+ if (this.shouldUnregisterForMatter(existingAccessory, deviceData)) {
672
+ const accessory = new this.api.platformAccessory(deviceData.nickname, uuid);
673
+ accessory.context.device = deviceData;
674
+ accessory.context = { device: deviceData, userId };
675
+ accessory.displayName = await this.validateAndCleanDisplayName(deviceData.nickname, 'nickname', deviceData.nickname);
676
+ accessory.context.device.firmware = deviceData.firmware ?? await this.getVersion();
677
+ new SmartHQClothesWasher(this, accessory, deviceData);
678
+ this.debugLog(`${deviceData.nickname} uuid: ${deviceData.applianceId}`);
679
+ }
680
+ else {
681
+ existingAccessory.context.device = deviceData;
682
+ existingAccessory.context = { device: deviceData, userId };
683
+ existingAccessory.displayName = await this.validateAndCleanDisplayName(deviceData.nickname, 'nickname', deviceData.nickname);
684
+ existingAccessory.context.device.firmware = deviceData.firmware ?? await this.getVersion();
685
+ this.api.updatePlatformAccessories([existingAccessory]);
686
+ this.infoLog(`[${protocol}] Restoring existing accessory from cache: ${existingAccessory.displayName}`);
687
+ new SmartHQClothesWasher(this, existingAccessory, deviceData);
688
+ this.debugLog(`${deviceData.nickname} uuid: ${deviceData.applianceId}`);
689
+ }
690
+ }
691
+ else {
692
+ this.unregisterPlatformAccessories(existingAccessory);
693
+ }
694
+ }
695
+ else if (!deviceData.hide_device && !existingAccessory) {
696
+ this.infoLog(`[${protocol}] Adding new accessory: ${deviceData.nickname}`);
697
+ const accessory = new this.api.platformAccessory(deviceData.nickname, uuid);
698
+ accessory.context.device = deviceData;
699
+ accessory.context = { device: deviceData, userId };
700
+ accessory.displayName = await this.validateAndCleanDisplayName(deviceData.nickname, 'nickname', deviceData.nickname);
701
+ accessory.context.device.firmware = deviceData.firmware ?? await this.getVersion();
702
+ new SmartHQClothesWasher(this, accessory, deviceData);
703
+ this.debugLog(`${deviceData.nickname} uuid: ${deviceData.applianceId}`);
704
+ this.api.registerPlatformAccessories(PLUGIN_NAME, PLATFORM_NAME, [accessory]);
705
+ this.accessories.push(accessory);
706
+ }
707
+ else {
708
+ this.debugErrorLog(`Unable to Register new device: ${JSON.stringify(deviceData.nickname)}`);
709
+ }
710
+ }
711
+ async createSmartHQClothesDryer(userId, device, details, features) {
712
+ // Merge device data
713
+ const deviceData = { brand: 'GE', ...details, ...features, ...device };
714
+ // Determine protocol (Matter or HAP)
715
+ deviceData.useMatter = this.shouldUseMatter(deviceData);
716
+ const uuid = this.api.hap.uuid.generate(deviceData.applianceId);
717
+ const existingAccessory = this.accessories.find(accessory => accessory.UUID === uuid);
718
+ const protocol = deviceData.useMatter ? 'Matter' : 'HAP';
719
+ if (existingAccessory) {
720
+ if (!deviceData.hide_device) {
721
+ // Check if protocol changed to Matter - if so, remove from HAP bridge
722
+ if (this.shouldUnregisterForMatter(existingAccessory, deviceData)) {
723
+ const accessory = new this.api.platformAccessory(deviceData.nickname, uuid);
724
+ accessory.context.device = deviceData;
725
+ accessory.context = { device: deviceData, userId };
726
+ accessory.displayName = await this.validateAndCleanDisplayName(deviceData.nickname, 'nickname', deviceData.nickname);
727
+ accessory.context.device.firmware = deviceData.firmware ?? await this.getVersion();
728
+ new SmartHQClothesDryer(this, accessory, deviceData);
729
+ this.debugLog(`${deviceData.nickname} uuid: ${deviceData.applianceId}`);
730
+ }
731
+ else {
732
+ existingAccessory.context.device = deviceData;
733
+ existingAccessory.context = { device: deviceData, userId };
734
+ existingAccessory.displayName = await this.validateAndCleanDisplayName(deviceData.nickname, 'nickname', deviceData.nickname);
735
+ existingAccessory.context.device.firmware = deviceData.firmware ?? await this.getVersion();
736
+ this.api.updatePlatformAccessories([existingAccessory]);
737
+ this.infoLog(`[${protocol}] Restoring existing accessory from cache: ${existingAccessory.displayName}`);
738
+ new SmartHQClothesDryer(this, existingAccessory, deviceData);
739
+ this.debugLog(`${deviceData.nickname} uuid: ${deviceData.applianceId}`);
740
+ }
741
+ }
742
+ else {
743
+ this.unregisterPlatformAccessories(existingAccessory);
744
+ }
745
+ }
746
+ else if (!deviceData.hide_device && !existingAccessory) {
747
+ this.infoLog(`[${protocol}] Adding new accessory: ${deviceData.nickname}`);
748
+ const accessory = new this.api.platformAccessory(deviceData.nickname, uuid);
749
+ accessory.context.device = deviceData;
750
+ accessory.context = { device: deviceData, userId };
751
+ accessory.displayName = await this.validateAndCleanDisplayName(deviceData.nickname, 'nickname', deviceData.nickname);
752
+ accessory.context.device.firmware = deviceData.firmware ?? await this.getVersion();
753
+ new SmartHQClothesDryer(this, accessory, deviceData);
754
+ this.debugLog(`${deviceData.nickname} uuid: ${deviceData.applianceId}`);
755
+ this.api.registerPlatformAccessories(PLUGIN_NAME, PLATFORM_NAME, [accessory]);
756
+ this.accessories.push(accessory);
757
+ }
758
+ else {
759
+ this.debugErrorLog(`Unable to Register new device: ${JSON.stringify(deviceData.nickname)}`);
760
+ }
761
+ }
762
+ async createSmartHQWaterFilter(userId, device, details, features) {
763
+ // Merge device data
764
+ const deviceData = { brand: 'GE', ...details, ...features, ...device };
765
+ // Determine protocol (Matter or HAP)
766
+ deviceData.useMatter = this.shouldUseMatter(deviceData);
767
+ const uuid = this.api.hap.uuid.generate(deviceData.applianceId);
768
+ const existingAccessory = this.accessories.find(accessory => accessory.UUID === uuid);
769
+ const protocol = deviceData.useMatter ? 'Matter' : 'HAP';
770
+ if (existingAccessory) {
771
+ if (!deviceData.hide_device) {
772
+ // Check if protocol changed to Matter - if so, remove from HAP bridge
773
+ if (this.shouldUnregisterForMatter(existingAccessory, deviceData)) {
774
+ const accessory = new this.api.platformAccessory(deviceData.nickname, uuid);
775
+ accessory.context.device = deviceData;
776
+ accessory.context = { device: deviceData, userId };
777
+ accessory.displayName = await this.validateAndCleanDisplayName(deviceData.nickname, 'nickname', deviceData.nickname);
778
+ accessory.context.device.firmware = deviceData.firmware ?? await this.getVersion();
779
+ new SmartHQWaterFilter(this, accessory, deviceData);
780
+ this.debugLog(`${deviceData.nickname} uuid: ${deviceData.applianceId}`);
781
+ }
782
+ else {
783
+ existingAccessory.context.device = deviceData;
784
+ existingAccessory.context = { device: deviceData, userId };
785
+ existingAccessory.displayName = await this.validateAndCleanDisplayName(deviceData.nickname, 'nickname', deviceData.nickname);
786
+ existingAccessory.context.device.firmware = deviceData.firmware ?? await this.getVersion();
787
+ this.api.updatePlatformAccessories([existingAccessory]);
788
+ this.infoLog(`[${protocol}] Restoring existing accessory from cache: ${existingAccessory.displayName}`);
789
+ new SmartHQWaterFilter(this, existingAccessory, deviceData);
790
+ this.debugLog(`${deviceData.nickname} uuid: ${deviceData.applianceId}`);
791
+ }
792
+ }
793
+ else {
794
+ this.unregisterPlatformAccessories(existingAccessory);
795
+ }
796
+ }
797
+ else if (!deviceData.hide_device && !existingAccessory) {
798
+ this.infoLog(`[${protocol}] Adding new accessory: ${deviceData.nickname}`);
799
+ const accessory = new this.api.platformAccessory(deviceData.nickname, uuid);
800
+ accessory.context.device = deviceData;
801
+ accessory.context = { device: deviceData, userId };
802
+ accessory.displayName = await this.validateAndCleanDisplayName(deviceData.nickname, 'nickname', deviceData.nickname);
803
+ accessory.context.device.firmware = deviceData.firmware ?? await this.getVersion();
804
+ new SmartHQWaterFilter(this, accessory, deviceData);
805
+ this.debugLog(`${deviceData.nickname} uuid: ${deviceData.applianceId}`);
806
+ this.api.registerPlatformAccessories(PLUGIN_NAME, PLATFORM_NAME, [accessory]);
807
+ this.accessories.push(accessory);
808
+ }
809
+ else {
810
+ this.debugErrorLog(`Unable to Register new device: ${JSON.stringify(deviceData.nickname)}`);
811
+ }
812
+ }
813
+ async createSmartHQWaterSoftener(userId, device, details, features) {
814
+ // Merge device data
815
+ const deviceData = { brand: 'GE', ...details, ...features, ...device };
816
+ // Determine protocol (Matter or HAP)
817
+ deviceData.useMatter = this.shouldUseMatter(deviceData);
818
+ const uuid = this.api.hap.uuid.generate(deviceData.applianceId);
819
+ const existingAccessory = this.accessories.find(accessory => accessory.UUID === uuid);
820
+ const protocol = deviceData.useMatter ? 'Matter' : 'HAP';
821
+ if (existingAccessory) {
822
+ if (!deviceData.hide_device) {
823
+ // Check if protocol changed to Matter - if so, remove from HAP bridge
824
+ if (this.shouldUnregisterForMatter(existingAccessory, deviceData)) {
825
+ const accessory = new this.api.platformAccessory(deviceData.nickname, uuid);
826
+ accessory.context.device = deviceData;
827
+ accessory.context = { device: deviceData, userId };
828
+ accessory.displayName = await this.validateAndCleanDisplayName(deviceData.nickname, 'nickname', deviceData.nickname);
829
+ accessory.context.device.firmware = deviceData.firmware ?? await this.getVersion();
830
+ new SmartHQWaterSoftener(this, accessory, deviceData);
831
+ this.debugLog(`${deviceData.nickname} uuid: ${deviceData.applianceId}`);
832
+ }
833
+ else {
834
+ existingAccessory.context.device = deviceData;
835
+ existingAccessory.context = { device: deviceData, userId };
836
+ existingAccessory.displayName = await this.validateAndCleanDisplayName(deviceData.nickname, 'nickname', deviceData.nickname);
837
+ existingAccessory.context.device.firmware = deviceData.firmware ?? await this.getVersion();
838
+ this.api.updatePlatformAccessories([existingAccessory]);
839
+ this.infoLog(`[${protocol}] Restoring existing accessory from cache: ${existingAccessory.displayName}`);
840
+ new SmartHQWaterSoftener(this, existingAccessory, deviceData);
841
+ this.debugLog(`${deviceData.nickname} uuid: ${deviceData.applianceId}`);
842
+ }
843
+ }
844
+ else {
845
+ this.unregisterPlatformAccessories(existingAccessory);
846
+ }
847
+ }
848
+ else if (!deviceData.hide_device && !existingAccessory) {
849
+ this.infoLog(`[${protocol}] Adding new accessory: ${deviceData.nickname}`);
850
+ const accessory = new this.api.platformAccessory(deviceData.nickname, uuid);
851
+ accessory.context.device = deviceData;
852
+ accessory.context = { device: deviceData, userId };
853
+ accessory.displayName = await this.validateAndCleanDisplayName(deviceData.nickname, 'nickname', deviceData.nickname);
854
+ accessory.context.device.firmware = deviceData.firmware ?? await this.getVersion();
855
+ new SmartHQWaterSoftener(this, accessory, deviceData);
856
+ this.debugLog(`${deviceData.nickname} uuid: ${deviceData.applianceId}`);
857
+ this.api.registerPlatformAccessories(PLUGIN_NAME, PLATFORM_NAME, [accessory]);
858
+ this.accessories.push(accessory);
859
+ }
860
+ else {
861
+ this.debugErrorLog(`Unable to Register new device: ${JSON.stringify(deviceData.nickname)}`);
862
+ }
863
+ }
864
+ async createSmartHQWaterHeater(userId, device, details, features) {
865
+ // Merge device data
866
+ const deviceData = { brand: 'GE', ...details, ...features, ...device };
867
+ // Determine protocol (Matter or HAP)
868
+ deviceData.useMatter = this.shouldUseMatter(deviceData);
869
+ const uuid = this.api.hap.uuid.generate(deviceData.applianceId);
870
+ const existingAccessory = this.accessories.find(accessory => accessory.UUID === uuid);
871
+ const protocol = deviceData.useMatter ? 'Matter' : 'HAP';
872
+ if (existingAccessory) {
873
+ if (!deviceData.hide_device) {
874
+ // Check if protocol changed to Matter - if so, remove from HAP bridge
875
+ if (this.shouldUnregisterForMatter(existingAccessory, deviceData)) {
876
+ const accessory = new this.api.platformAccessory(deviceData.nickname, uuid);
877
+ accessory.context.device = deviceData;
878
+ accessory.context = { device: deviceData, userId };
879
+ accessory.displayName = await this.validateAndCleanDisplayName(deviceData.nickname, 'nickname', deviceData.nickname);
880
+ accessory.context.device.firmware = deviceData.firmware ?? await this.getVersion();
881
+ new SmartHQWaterHeater(this, accessory, deviceData);
882
+ this.debugLog(`${deviceData.nickname} uuid: ${deviceData.applianceId}`);
883
+ }
884
+ else {
885
+ existingAccessory.context.device = deviceData;
886
+ existingAccessory.context = { device: deviceData, userId };
887
+ existingAccessory.displayName = await this.validateAndCleanDisplayName(deviceData.nickname, 'nickname', deviceData.nickname);
888
+ existingAccessory.context.device.firmware = deviceData.firmware ?? await this.getVersion();
889
+ this.api.updatePlatformAccessories([existingAccessory]);
890
+ this.infoLog(`[${protocol}] Restoring existing accessory from cache: ${existingAccessory.displayName}`);
891
+ new SmartHQWaterHeater(this, existingAccessory, deviceData);
892
+ this.debugLog(`${deviceData.nickname} uuid: ${deviceData.applianceId}`);
893
+ }
894
+ }
895
+ else {
896
+ this.unregisterPlatformAccessories(existingAccessory);
897
+ }
898
+ }
899
+ else if (!deviceData.hide_device && !existingAccessory) {
900
+ this.infoLog(`[${protocol}] Adding new accessory: ${deviceData.nickname}`);
901
+ const accessory = new this.api.platformAccessory(deviceData.nickname, uuid);
902
+ accessory.context.device = deviceData;
903
+ accessory.context = { device: deviceData, userId };
904
+ accessory.displayName = await this.validateAndCleanDisplayName(deviceData.nickname, 'nickname', deviceData.nickname);
905
+ accessory.context.device.firmware = deviceData.firmware ?? await this.getVersion();
906
+ new SmartHQWaterHeater(this, accessory, deviceData);
907
+ this.debugLog(`${deviceData.nickname} uuid: ${deviceData.applianceId}`);
908
+ this.api.registerPlatformAccessories(PLUGIN_NAME, PLATFORM_NAME, [accessory]);
909
+ this.accessories.push(accessory);
910
+ }
911
+ else {
912
+ this.debugErrorLog(`Unable to Register new device: ${JSON.stringify(deviceData.nickname)}`);
913
+ }
914
+ }
915
+ async createSmartHQAdvantium(userId, device, details, features) {
916
+ // Merge device data
917
+ const deviceData = { brand: 'GE', ...details, ...features, ...device };
918
+ // Determine protocol (Matter or HAP)
919
+ deviceData.useMatter = this.shouldUseMatter(deviceData);
920
+ const uuid = this.api.hap.uuid.generate(deviceData.applianceId);
921
+ const existingAccessory = this.accessories.find(accessory => accessory.UUID === uuid);
922
+ const protocol = deviceData.useMatter ? 'Matter' : 'HAP';
923
+ if (existingAccessory) {
924
+ if (!deviceData.hide_device) {
925
+ // Check if protocol changed to Matter - if so, remove from HAP bridge
926
+ if (this.shouldUnregisterForMatter(existingAccessory, deviceData)) {
927
+ const accessory = new this.api.platformAccessory(deviceData.nickname, uuid);
928
+ accessory.context.device = deviceData;
929
+ accessory.context = { device: deviceData, userId };
930
+ accessory.displayName = await this.validateAndCleanDisplayName(deviceData.nickname, 'nickname', deviceData.nickname);
931
+ accessory.context.device.firmware = deviceData.firmware ?? await this.getVersion();
932
+ new SmartHQAdvantium(this, accessory, deviceData);
933
+ this.debugLog(`${deviceData.nickname} uuid: ${deviceData.applianceId}`);
934
+ }
935
+ else {
936
+ existingAccessory.context.device = deviceData;
937
+ existingAccessory.context = { device: deviceData, userId };
938
+ existingAccessory.displayName = await this.validateAndCleanDisplayName(deviceData.nickname, 'nickname', deviceData.nickname);
939
+ existingAccessory.context.device.firmware = deviceData.firmware ?? await this.getVersion();
940
+ this.api.updatePlatformAccessories([existingAccessory]);
941
+ this.infoLog(`[${protocol}] Restoring existing accessory from cache: ${existingAccessory.displayName}`);
942
+ new SmartHQAdvantium(this, existingAccessory, deviceData);
943
+ this.debugLog(`${deviceData.nickname} uuid: ${deviceData.applianceId}`);
944
+ }
945
+ }
946
+ else {
947
+ this.unregisterPlatformAccessories(existingAccessory);
948
+ }
949
+ }
950
+ else if (!deviceData.hide_device && !existingAccessory) {
951
+ this.infoLog(`[${protocol}] Adding new accessory: ${deviceData.nickname}`);
952
+ const accessory = new this.api.platformAccessory(deviceData.nickname, uuid);
953
+ accessory.context.device = deviceData;
954
+ accessory.context = { device: deviceData, userId };
955
+ accessory.displayName = await this.validateAndCleanDisplayName(deviceData.nickname, 'nickname', deviceData.nickname);
956
+ accessory.context.device.firmware = deviceData.firmware ?? await this.getVersion();
957
+ new SmartHQAdvantium(this, accessory, deviceData);
958
+ this.debugLog(`${deviceData.nickname} uuid: ${deviceData.applianceId}`);
959
+ this.api.registerPlatformAccessories(PLUGIN_NAME, PLATFORM_NAME, [accessory]);
960
+ this.accessories.push(accessory);
961
+ }
962
+ else {
963
+ this.debugErrorLog(`Unable to Register new device: ${JSON.stringify(device.nickname)}`);
964
+ }
965
+ }
966
+ async createSmartHQMicrowave(userId, device, details, features) {
967
+ // Merge device data
968
+ const deviceData = { brand: 'GE', ...details, ...features, ...device };
969
+ // Determine protocol (Matter or HAP)
970
+ deviceData.useMatter = this.shouldUseMatter(deviceData);
971
+ const uuid = this.api.hap.uuid.generate(deviceData.applianceId);
972
+ const existingAccessory = this.accessories.find(accessory => accessory.UUID === uuid);
973
+ const protocol = deviceData.useMatter ? 'Matter' : 'HAP';
974
+ if (existingAccessory) {
975
+ if (!deviceData.hide_device) {
976
+ // Check if protocol changed to Matter - if so, remove from HAP bridge
977
+ if (this.shouldUnregisterForMatter(existingAccessory, deviceData)) {
978
+ const accessory = new this.api.platformAccessory(deviceData.nickname, uuid);
979
+ accessory.context.device = deviceData;
980
+ accessory.context = { device: deviceData, userId };
981
+ accessory.displayName = await this.validateAndCleanDisplayName(deviceData.nickname, 'nickname', deviceData.nickname);
982
+ accessory.context.device.firmware = deviceData.firmware ?? await this.getVersion();
983
+ new SmartHQMicrowave(this, accessory, deviceData);
984
+ this.debugLog(`${deviceData.nickname} uuid: ${deviceData.applianceId}`);
985
+ }
986
+ else {
987
+ existingAccessory.context.device = deviceData;
988
+ existingAccessory.context = { device: deviceData, userId };
989
+ existingAccessory.displayName = await this.validateAndCleanDisplayName(deviceData.nickname, 'nickname', deviceData.nickname);
990
+ existingAccessory.context.device.firmware = deviceData.firmware ?? await this.getVersion();
991
+ this.api.updatePlatformAccessories([existingAccessory]);
992
+ this.infoLog(`[${protocol}] Restoring existing accessory from cache: ${existingAccessory.displayName}`);
993
+ new SmartHQMicrowave(this, existingAccessory, deviceData);
994
+ this.debugLog(`${deviceData.nickname} uuid: ${deviceData.applianceId}`);
995
+ }
996
+ }
997
+ else {
998
+ this.unregisterPlatformAccessories(existingAccessory);
999
+ }
1000
+ }
1001
+ else if (!deviceData.hide_device && !existingAccessory) {
1002
+ this.infoLog(`[${protocol}] Adding new accessory: ${deviceData.nickname}`);
1003
+ const accessory = new this.api.platformAccessory(deviceData.nickname, uuid);
1004
+ accessory.context.device = deviceData;
1005
+ accessory.context = { device: deviceData, userId };
1006
+ accessory.displayName = await this.validateAndCleanDisplayName(deviceData.nickname, 'nickname', deviceData.nickname);
1007
+ accessory.context.device.firmware = deviceData.firmware ?? await this.getVersion();
1008
+ new SmartHQMicrowave(this, accessory, deviceData);
1009
+ this.debugLog(`${deviceData.nickname} uuid: ${deviceData.applianceId}`);
1010
+ this.api.registerPlatformAccessories(PLUGIN_NAME, PLATFORM_NAME, [accessory]);
1011
+ this.accessories.push(accessory);
1012
+ }
1013
+ else {
1014
+ this.debugErrorLog(`Unable to Register new device: ${JSON.stringify(deviceData.nickname)}`);
1015
+ }
1016
+ }
1017
+ async createSmartHQCoffeeMaker(userId, device, details, features) {
1018
+ // Merge device data
1019
+ const deviceData = { brand: 'GE', ...details, ...features, ...device };
1020
+ // Determine protocol (Matter or HAP)
1021
+ deviceData.useMatter = this.shouldUseMatter(deviceData);
1022
+ const uuid = this.api.hap.uuid.generate(deviceData.applianceId);
1023
+ const existingAccessory = this.accessories.find(accessory => accessory.UUID === uuid);
1024
+ const protocol = deviceData.useMatter ? 'Matter' : 'HAP';
1025
+ if (existingAccessory) {
1026
+ if (!deviceData.hide_device) {
1027
+ // Check if protocol changed to Matter - if so, remove from HAP bridge
1028
+ if (this.shouldUnregisterForMatter(existingAccessory, deviceData)) {
1029
+ const accessory = new this.api.platformAccessory(deviceData.nickname, uuid);
1030
+ accessory.context.device = deviceData;
1031
+ accessory.context = { device: deviceData, userId };
1032
+ accessory.displayName = await this.validateAndCleanDisplayName(deviceData.nickname, 'nickname', deviceData.nickname);
1033
+ accessory.context.device.firmware = deviceData.firmware ?? await this.getVersion();
1034
+ new SmartHQCoffeeMaker(this, accessory, deviceData);
1035
+ this.debugLog(`${deviceData.nickname} uuid: ${deviceData.applianceId}`);
1036
+ }
1037
+ else {
1038
+ existingAccessory.context.device = deviceData;
1039
+ existingAccessory.context = { device: deviceData, userId };
1040
+ existingAccessory.displayName = await this.validateAndCleanDisplayName(deviceData.nickname, 'nickname', deviceData.nickname);
1041
+ existingAccessory.context.device.firmware = deviceData.firmware ?? await this.getVersion();
1042
+ this.api.updatePlatformAccessories([existingAccessory]);
1043
+ this.infoLog(`[${protocol}] Restoring existing accessory from cache: ${existingAccessory.displayName}`);
1044
+ new SmartHQCoffeeMaker(this, existingAccessory, deviceData);
1045
+ this.debugLog(`${deviceData.nickname} uuid: ${deviceData.applianceId}`);
1046
+ }
1047
+ }
1048
+ else {
1049
+ this.unregisterPlatformAccessories(existingAccessory);
1050
+ }
1051
+ }
1052
+ else if (!deviceData.hide_device && !existingAccessory) {
1053
+ this.infoLog(`[${protocol}] Adding new accessory: ${deviceData.nickname}`);
1054
+ const accessory = new this.api.platformAccessory(deviceData.nickname, uuid);
1055
+ accessory.context.device = deviceData;
1056
+ accessory.context = { device: deviceData, userId };
1057
+ accessory.displayName = await this.validateAndCleanDisplayName(deviceData.nickname, 'nickname', deviceData.nickname);
1058
+ accessory.context.device.firmware = deviceData.firmware ?? await this.getVersion();
1059
+ new SmartHQCoffeeMaker(this, accessory, deviceData);
1060
+ this.debugLog(`${deviceData.nickname} uuid: ${deviceData.applianceId}`);
1061
+ this.api.registerPlatformAccessories(PLUGIN_NAME, PLATFORM_NAME, [accessory]);
1062
+ this.accessories.push(accessory);
1063
+ }
1064
+ else {
1065
+ this.debugErrorLog(`Unable to Register new device: ${JSON.stringify(deviceData.nickname)}`);
1066
+ }
1067
+ }
1068
+ async createSmartHQBeverageCenter(userId, device, details, features) {
1069
+ // Merge device data
1070
+ const deviceData = { brand: 'GE', ...details, ...features, ...device };
1071
+ // Determine protocol (Matter or HAP)
1072
+ deviceData.useMatter = this.shouldUseMatter(deviceData);
1073
+ const uuid = this.api.hap.uuid.generate(deviceData.applianceId);
1074
+ const existingAccessory = this.accessories.find(accessory => accessory.UUID === uuid);
1075
+ const protocol = deviceData.useMatter ? 'Matter' : 'HAP';
1076
+ if (existingAccessory) {
1077
+ if (!deviceData.hide_device) {
1078
+ // Check if protocol changed to Matter - if so, remove from HAP bridge
1079
+ if (this.shouldUnregisterForMatter(existingAccessory, deviceData)) {
1080
+ const accessory = new this.api.platformAccessory(deviceData.nickname, uuid);
1081
+ accessory.context.device = deviceData;
1082
+ accessory.context = { device: deviceData, userId };
1083
+ accessory.displayName = await this.validateAndCleanDisplayName(deviceData.nickname, 'nickname', deviceData.nickname);
1084
+ accessory.context.device.firmware = deviceData.firmware ?? await this.getVersion();
1085
+ new SmartHQBeverageCenter(this, accessory, deviceData);
1086
+ this.debugLog(`${deviceData.nickname} uuid: ${deviceData.applianceId}`);
1087
+ }
1088
+ else {
1089
+ existingAccessory.context.device = deviceData;
1090
+ existingAccessory.context = { device: deviceData, userId };
1091
+ existingAccessory.displayName = await this.validateAndCleanDisplayName(deviceData.nickname, 'nickname', deviceData.nickname);
1092
+ existingAccessory.context.device.firmware = deviceData.firmware ?? await this.getVersion();
1093
+ this.api.updatePlatformAccessories([existingAccessory]);
1094
+ this.infoLog(`[${protocol}] Restoring existing accessory from cache: ${existingAccessory.displayName}`);
1095
+ new SmartHQBeverageCenter(this, existingAccessory, deviceData);
1096
+ this.debugLog(`${deviceData.nickname} uuid: ${deviceData.applianceId}`);
1097
+ }
1098
+ }
1099
+ else {
1100
+ this.unregisterPlatformAccessories(existingAccessory);
1101
+ }
1102
+ }
1103
+ else if (!deviceData.hide_device && !existingAccessory) {
1104
+ this.infoLog(`[${protocol}] Adding new accessory: ${deviceData.nickname}`);
1105
+ const accessory = new this.api.platformAccessory(deviceData.nickname, uuid);
1106
+ accessory.context.device = deviceData;
1107
+ accessory.context = { device: deviceData, userId };
1108
+ accessory.displayName = await this.validateAndCleanDisplayName(deviceData.nickname, 'nickname', deviceData.nickname);
1109
+ accessory.context.device.firmware = deviceData.firmware ?? await this.getVersion();
1110
+ new SmartHQBeverageCenter(this, accessory, deviceData);
1111
+ this.debugLog(`${deviceData.nickname} uuid: ${deviceData.applianceId}`);
470
1112
  this.api.registerPlatformAccessories(PLUGIN_NAME, PLATFORM_NAME, [accessory]);
471
1113
  this.accessories.push(accessory);
472
1114
  }
@@ -627,5 +1269,65 @@ export class SmartHQPlatform {
627
1269
  async enablingPlatformLogging() {
628
1270
  return this.platformLogging === 'debugMode' || this.platformLogging === 'debug' || this.platformLogging === 'standard';
629
1271
  }
1272
+ /**
1273
+ * Check if Matter is available and enabled in Homebridge
1274
+ */
1275
+ checkMatterSupport() {
1276
+ // Check if Matter API is available (Homebridge 2.0+)
1277
+ const api = this.api;
1278
+ if (typeof api.isMatterAvailable === 'function') {
1279
+ this.matterAvailable = api.isMatterAvailable();
1280
+ if (!this.matterAvailable) {
1281
+ this.log.warn('Matter is not available in this version of Homebridge. Please update to Homebridge 2.0.0-beta.63 or later to use Matter.');
1282
+ }
1283
+ }
1284
+ else {
1285
+ this.log.debug('Matter API not detected - running on Homebridge < 2.0.0');
1286
+ }
1287
+ // Check if Matter is enabled by user
1288
+ if (this.matterAvailable && typeof api.isMatterEnabled === 'function') {
1289
+ this.matterEnabled = api.isMatterEnabled();
1290
+ if (!this.matterEnabled) {
1291
+ this.log.warn('Matter is available but not enabled. Please enable Matter in Homebridge settings to use Matter devices.');
1292
+ }
1293
+ else {
1294
+ this.log.info('✓ Matter is available and enabled - devices will use Matter protocol');
1295
+ }
1296
+ }
1297
+ // Log final status
1298
+ if (this.matterAvailable && this.matterEnabled) {
1299
+ this.log.success('Matter support: ENABLED - Devices will register as Matter accessories');
1300
+ }
1301
+ else {
1302
+ this.log.info('Matter support: DISABLED - Devices will register as HAP accessories');
1303
+ }
1304
+ }
1305
+ /**
1306
+ * Determine if a device should use Matter based on platform and device config
1307
+ */
1308
+ shouldUseMatter(device) {
1309
+ // If Matter isn't available or enabled, use HAP
1310
+ if (!this.matterAvailable || !this.matterEnabled) {
1311
+ return false;
1312
+ }
1313
+ // Check per-device preference (if specified in config)
1314
+ if (device.useMatter !== undefined) {
1315
+ return device.useMatter;
1316
+ }
1317
+ // Default: use Matter when available
1318
+ return true;
1319
+ }
1320
+ /**
1321
+ * Handle accessory that needs to switch from HAP to Matter
1322
+ * Returns true if accessory was unregistered and needs to be recreated
1323
+ */
1324
+ shouldUnregisterForMatter(existingAccessory, deviceData) {
1325
+ if (deviceData.useMatter && existingAccessory) {
1326
+ this.infoLog(`Removing ${existingAccessory.displayName} from HAP bridge (switching to Matter)`);
1327
+ this.unregisterPlatformAccessories(existingAccessory);
1328
+ return true;
1329
+ }
1330
+ return false;
1331
+ }
630
1332
  }
631
1333
  //# sourceMappingURL=platform.js.map