@homebridge-plugins/homebridge-resideo 3.0.3-beta.6 → 3.0.3-beta.8

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.
@@ -41,13 +41,32 @@
41
41
  <button type="button" class="btn btn-primary" id="menuSettings">
42
42
  Settings
43
43
  </button>
44
- <button type="button" class="btn btn-primary" id="menuDevices">
44
+ <button type="button" class="btn btn-primary" id="menuFeatureOptions">
45
45
  Devices
46
46
  </button>
47
47
  <button type="button" class="btn btn-primary mr-0" id="menuHome">
48
48
  Support
49
49
  </button>
50
50
  </div>
51
+ <div id="pageFirstRun" class="mt-4" style="display: none;">
52
+ <div class="alert alert-info">
53
+ <h5>Getting Started</h5>
54
+ <p class="mb-2">Welcome to homebridge-resideo! Let's get your account configured.</p>
55
+ <p class="mb-0">Please enter your Resideo API credentials to continue.</p>
56
+ </div>
57
+ <div class="form-group">
58
+ <label for="firstRunKey">Consumer Key <span class="text-danger">*</span></label>
59
+ <input type="text" class="form-control" id="firstRunKey">
60
+ </div>
61
+ <div class="form-group">
62
+ <label for="firstRunSecret">Consumer Secret <span class="text-danger">*</span></label>
63
+ <input type="text" class="form-control" id="firstRunSecret">
64
+ </div>
65
+ <div id="loginError">&nbsp;</div>
66
+ <div class="form-group text-center">
67
+ <button type="button" class="btn btn-primary" id="firstRun">Setup Account &rarr;</button>
68
+ </div>
69
+ </div>
51
70
  <div id="pageAccount" class="mt-4" style="display: none;">
52
71
  <div class="alert alert-warning" style="display: none;" id="validateForm">
53
72
  Please enter both values
@@ -69,15 +88,40 @@
69
88
  <button type="button" class="btn btn-danger" id="unLinkButton">Unlink Account &rarr;</button>
70
89
  </div>
71
90
  </div>
91
+ <!-- Dynamic device configuration UI will be rendered here by webUI -->
92
+ <div id="pageFeatureOptions" class="mt-4" style="display: none;">
93
+ </div>
72
94
  <div id="pageDevices" class="mt-4" style="display: none;">
95
+ <div class="alert alert-info" id="deviceConfigInfo">
96
+ <h5>Device Configuration Made Easy</h5>
97
+ <p class="mb-2">This section shows devices available from your Resideo account and provides dynamic configuration options.</p>
98
+ <p class="mb-0">Select a device below to view its details and configure its settings using the feature options system.</p>
99
+ </div>
73
100
  <div id="deviceInfo">
74
101
  <h5>Cached Devices</h5>
75
102
  <p>These are devices already configured in Homebridge:</p>
76
103
  <form>
77
104
  <div class="form-group">
105
+ <label for="deviceSelect"><strong>Available Devices:</strong></label>
78
106
  <select class="form-control" id="deviceSelect"></select>
79
107
  </div>
80
108
  </form>
109
+ <div id="quickConfigSection" style="display: none;" class="mt-3">
110
+ <div class="alert alert-success">
111
+ <h6>Quick Configuration</h6>
112
+ <p class="mb-2">Copy the device ID below and paste it into the plugin settings:</p>
113
+ <div class="input-group">
114
+ <input type="text" class="form-control" id="quickDeviceID" readonly>
115
+ <div class="input-group-append">
116
+ <button class="btn btn-outline-secondary" type="button" id="copyDeviceID">Copy</button>
117
+ </div>
118
+ </div>
119
+ <small class="form-text text-muted mt-2">
120
+ Recommended device class: <span id="recommendedClass"></span><br>
121
+ Suggested device model: <span id="suggestedModel"></span>
122
+ </small>
123
+ </div>
124
+ </div>
81
125
  <table class="table w-100" id="deviceTable" style="display: none;">
82
126
  <thead>
83
127
  <tr class="table-active">
@@ -186,332 +230,172 @@
186
230
  </li>
187
231
  </ul>
188
232
  </div>
189
- <script>
190
- ; (async () => {
191
- try {
192
- const currentConfig = await homebridge.getPluginConfig()
193
- const hostname = window.location.hostname
194
- try {
195
- await homebridge.request('Start Resideo Login Server')
196
- } catch (err) {
197
- const msg = err.message
198
- homebridge.toast.error(msg, 'HTTP Server Not Started')
233
+ <script type="module">
234
+ import { webUi } from "./lib/webUi.mjs";
235
+
236
+ // Initialize the webUI with homebridge-plugin-utils
237
+ const ui = new webUi({
238
+ featureOptions: {
239
+ getFeatureOptions: () => homebridge.request('/getFeatureOptions'),
240
+ getDevices: () => homebridge.request('/getDevices'),
241
+ infoPanel: (device) => {
242
+ return `
243
+ <div class="mt-3">
244
+ <h6>Device Information</h6>
245
+ <p><strong>Name:</strong> ${device.name}</p>
246
+ <p><strong>Device ID:</strong> ${device.deviceID}</p>
247
+ <p><strong>Type:</strong> ${device.deviceType || 'Unknown'}</p>
248
+ <p><strong>Location:</strong> ${device.locationName || 'Unknown'}</p>
249
+ <p><strong>Model:</strong> ${device.model}</p>
250
+ <p><strong>Firmware:</strong> ${device.firmwareRevision}</p>
251
+ </div>
252
+ `;
199
253
  }
200
- homebridge.addEventListener('creds-received', async event => {
201
- try {
202
- if (event.data.access && event.data.refresh) {
203
- this.popup.close()
204
- currentConfig[0].credentials = {
205
- consumerKey: event.data.key,
206
- consumerSecret: event.data.secret,
207
- accessToken: event.data.access,
208
- refreshToken: event.data.refresh
209
- }
210
- await homebridge.updatePluginConfig(currentConfig)
211
- await homebridge.savePluginConfig()
212
- homebridge.toast.success("Successfully Linked Resideo Account", "homebridge-resideo")
213
- document.getElementById('backToIntro').style.display = 'none'
214
- document.getElementById('linkButton').style.display = 'none'
215
- document.getElementById('unLinkButton').style.display = 'none'
216
- document.getElementById('linkSuccess').style.display = 'block'
217
- } else {
218
- throw new Error('no access/token received from server')
219
- }
220
- } catch (err) {
221
- console.log(err)
222
- homebridge.toast.error('Try again, or see the console message for info.', 'Error')
254
+ },
255
+ firstRun: {
256
+ isRequired: () => {
257
+ // Check if we need to run first-time setup
258
+ const currentConfig = homebridge.getPluginConfig();
259
+ return !currentConfig[0]?.credentials?.consumerKey || !currentConfig[0]?.credentials?.consumerSecret;
260
+ },
261
+ onStart: () => {
262
+ // Initialize first run form
263
+ const currentConfig = homebridge.getPluginConfig();
264
+ if (currentConfig[0]?.credentials) {
265
+ document.getElementById('firstRunKey').value = currentConfig[0].credentials.consumerKey || '';
266
+ document.getElementById('firstRunSecret').value = currentConfig[0].credentials.consumerSecret || '';
223
267
  }
224
- })
225
- copyMyText = () => {
226
- var textToCopy = document.getElementById("copyMe");
227
- textToCopy.select();
228
- document.execCommand("copy");
229
- }
230
- linkAccount = async () => {
231
- document.getElementById('validateForm').style.display = 'none'
232
- if (
233
- !document.getElementById('inputConsumerKey').value ||
234
- !document.getElementById('inputConsumerSecret').value
235
- ) {
236
- document.getElementById('validateForm').style.display = 'block'
237
- return
268
+ return true;
269
+ },
270
+ onSubmit: async () => {
271
+ // Handle first run setup
272
+ const key = document.getElementById('firstRunKey').value;
273
+ const secret = document.getElementById('firstRunSecret').value;
274
+ const errorDiv = document.getElementById('loginError');
275
+
276
+ if (!key || !secret) {
277
+ errorDiv.innerHTML = '<div class="alert alert-danger">Please enter both Consumer Key and Consumer Secret.</div>';
278
+ return false;
238
279
  }
239
- const w = 450;
240
- const h = 700;
241
- const y = window.top.outerHeight / 2 + window.top.screenY - (h / 2);
242
- const x = window.top.outerWidth / 2 + window.top.screenX - (w / 2);
243
- const urlToOpen = 'http://' + hostname + ':8585/start?' +
244
- 'key=' + document.getElementById('inputConsumerKey').value +
245
- '&secret=' + document.getElementById('inputConsumerSecret').value +
246
- '&host=' + encodeURI(hostname);
247
- this.popup = window.open(
248
- urlToOpen, 'honeywell-auth',
249
- 'toolbar=no, location=no, directories=no, status=no, menubar=no scrollbars=no, resizable=no, copyhistory=no, ' +
250
- 'width=' + w + ', height=' + h + ', top=' + y + ', left=' + x,
251
- )
252
- }
253
- unLinkAccount = async () => {
280
+
254
281
  try {
255
- document.getElementById('validateForm').style.display = 'none'
256
- if (
257
- !document.getElementById('inputConsumerKey').value ||
258
- !document.getElementById('inputConsumerSecret').value
259
- ) {
260
- document.getElementById('validateForm').style.display = 'block'
261
- return
282
+ // Test the credentials by attempting to link the account
283
+ const currentConfig = homebridge.getPluginConfig();
284
+ if (!currentConfig[0]) {
285
+ currentConfig.push({ name: 'Resideo' });
262
286
  }
263
- delete currentConfig[0].credentials
264
- await homebridge.updatePluginConfig(currentConfig)
265
- await homebridge.savePluginConfig()
266
- document.getElementById('inputConsumerKey').value = ''
267
- document.getElementById('inputConsumerSecret').value = ''
268
- document.getElementById('unLinkButton').style.display = 'none'
269
- } catch (err) {
270
- console.error(err)
271
- homebridge.toast.error('Try again, or see the console message for info.', 'Error')
272
- }
273
- }
274
- showIntro = () => {
275
- const introLink = document.getElementById('introLink')
276
- document.getElementById('copyMe').value = 'http://' + hostname + ':8585/auth'
277
- introLink.addEventListener('click', () => {
278
- homebridge.showSpinner()
279
- document.getElementById('pageIntro').style.display = 'none'
280
- document.getElementById('menuWrapper').style.display = 'inline-flex'
281
- showAccount()
282
- homebridge.hideSpinner()
283
- })
284
- document.getElementById('pageAccount').style.display = 'none'
285
- document.getElementById('menuWrapper').style.display = 'none'
286
- document.getElementById('pageIntro').style.display = 'block'
287
- }
288
- showDevices = async () => {
289
- homebridge.showSpinner()
290
- homebridge.hideSchemaForm()
291
- document.getElementById('menuHome').classList.remove('btn-elegant')
292
- document.getElementById('menuHome').classList.add('btn-primary')
293
- document.getElementById('menuDevices').classList.add('btn-elegant')
294
- document.getElementById('menuDevices').classList.remove('btn-primary')
295
- document.getElementById('menuAccount').classList.remove('btn-elegant')
296
- document.getElementById('menuAccount').classList.add('btn-primary')
297
- document.getElementById('menuSettings').classList.remove('btn-elegant')
298
- document.getElementById('menuSettings').classList.add('btn-primary')
299
- document.getElementById('pageSupport').style.display = 'none'
300
- document.getElementById('pageAccount').style.display = 'none'
301
- document.getElementById('pageDevices').style.display = 'block'
302
- const cachedAccessories =
303
- typeof homebridge.getCachedAccessories === 'function'
304
- ? await homebridge.getCachedAccessories()
305
- : await homebridge.request('/getCachedAccessories')
306
- if (cachedAccessories.length > 0) {
307
- cachedAccessories.sort((a, b) => {
308
- return a.displayName.toLowerCase() > b.displayName.toLowerCase()
309
- ? 1
310
- : b.displayName.toLowerCase() > a.displayName.toLowerCase()
311
- ? -1
312
- : 0
313
- })
314
- }
315
- const deviceSelect = document.getElementById('deviceSelect')
316
- deviceSelect.innerHTML = ''
317
- cachedAccessories.forEach(a => {
318
- const option = document.createElement('option')
319
- option.text = a.displayName
320
- option.value = a.UUID
321
- deviceSelect.add(option)
322
- })
323
- showDeviceInfo = async UUID => {
324
- homebridge.showSpinner()
325
- const thisAcc = cachedAccessories.find(x => x.UUID === UUID)
326
- const context = thisAcc.context
327
- document.getElementById('displayName').innerHTML = thisAcc.displayName
328
- document.getElementById('deviceID').innerHTML = context.deviceID
329
- document.getElementById('model').innerHTML = context.model
330
- document.getElementById('firmwareRevision').innerHTML = context.firmwareRevision || 'N/A'
331
- document.getElementById('deviceTable').style.display = 'inline-table'
332
- homebridge.hideSpinner()
333
- }
334
- deviceSelect.addEventListener('change', event => showDeviceInfo(event.target.value))
335
- if (cachedAccessories.length > 0) {
336
- showDeviceInfo(cachedAccessories[0].UUID)
337
- } else {
338
- const option = document.createElement('option')
339
- option.text = 'No Devices'
340
- deviceSelect.add(option)
341
- deviceSelect.disabled = true
342
- }
343
- homebridge.hideSpinner()
344
-
345
- // Load available devices when the page loads
346
- loadAvailableDevices()
347
-
348
- // Set up refresh button
349
- document.getElementById('refreshDevicesButton').addEventListener('click', () => {
350
- loadAvailableDevices()
351
- })
352
- }
353
-
354
- loadAvailableDevices = async () => {
355
- const messageDiv = document.getElementById('availableDevicesMessage')
356
- const tableDiv = document.getElementById('availableDevicesTable')
357
- const tableBody = document.getElementById('availableDevicesTableBody')
358
-
359
- try {
360
- homebridge.showSpinner()
361
- messageDiv.style.display = 'none'
362
- tableDiv.style.display = 'none'
363
287
 
364
- const response = await homebridge.request('/getAvailableDevices')
288
+ currentConfig[0].credentials = {
289
+ consumerKey: key,
290
+ consumerSecret: secret
291
+ };
292
+
293
+ await homebridge.updatePluginConfig(currentConfig);
365
294
 
366
- if (response.status === 'error') {
367
- messageDiv.className = 'alert alert-danger'
368
- messageDiv.innerHTML = response.message || 'Failed to load devices'
369
- messageDiv.style.display = 'block'
370
- return
371
- }
372
-
373
- if (!response.data || response.data.length === 0) {
374
- messageDiv.className = 'alert alert-warning'
375
- messageDiv.innerHTML = 'No devices found in your Resideo account'
376
- messageDiv.style.display = 'block'
377
- return
378
- }
295
+ // Now trigger the OAuth flow
296
+ await homebridge.request('Start Resideo Login Server');
379
297
 
380
- // Clear existing table data
381
- tableBody.innerHTML = ''
382
-
383
- // Populate table with devices
384
- response.data.forEach(device => {
385
- const row = document.createElement('tr')
386
- row.innerHTML = `
387
- <td><strong>${device.deviceName}</strong></td>
388
- <td><code>${device.deviceID}</code></td>
389
- <td>${device.deviceClass}</td>
390
- <td>${device.deviceModel || 'N/A'}</td>
391
- <td>${device.locationName}</td>
392
- <td>
393
- <span class="badge badge-${device.isOnline ? 'success' : 'secondary'}">
394
- ${device.isOnline ? 'Online' : 'Offline'}
395
- </span>
396
- </td>
397
- `
398
- tableBody.appendChild(row)
399
- })
400
-
401
- tableDiv.style.display = 'table'
402
- messageDiv.className = 'alert alert-success'
403
- messageDiv.innerHTML = `Found ${response.data.length} device(s) in your Resideo account. Copy the Device ID to add devices to your configuration.`
404
- messageDiv.style.display = 'block'
298
+ // Open OAuth popup
299
+ const hostname = window.location.hostname;
300
+ const w = 450;
301
+ const h = 700;
302
+ const y = window.top.outerHeight / 2 + window.top.screenY - (h / 2);
303
+ const x = window.top.outerWidth / 2 + window.top.screenX - (w / 2);
304
+ const urlToOpen = 'http://' + hostname + ':8585/start?' +
305
+ 'key=' + encodeURIComponent(key) +
306
+ '&secret=' + encodeURIComponent(secret) +
307
+ '&host=' + encodeURIComponent(hostname);
405
308
 
309
+ const popup = window.open(
310
+ urlToOpen, 'resideo-auth',
311
+ 'toolbar=no, location=no, directories=no, status=no, menubar=no scrollbars=no, resizable=no, copyhistory=no, ' +
312
+ 'width=' + w + ', height=' + h + ', top=' + y + ', left=' + x,
313
+ );
314
+
315
+ return new Promise((resolve) => {
316
+ const checkClosed = setInterval(() => {
317
+ if (popup.closed) {
318
+ clearInterval(checkClosed);
319
+ // Check if credentials were updated
320
+ const updatedConfig = homebridge.getPluginConfig();
321
+ if (updatedConfig[0]?.credentials?.accessToken) {
322
+ resolve(true);
323
+ } else {
324
+ errorDiv.innerHTML = '<div class="alert alert-danger">Authentication failed. Please try again.</div>';
325
+ resolve(false);
326
+ }
327
+ }
328
+ }, 1000);
329
+ });
406
330
  } catch (error) {
407
- messageDiv.className = 'alert alert-danger'
408
- messageDiv.innerHTML = `Error loading devices: ${error.message}`
409
- messageDiv.style.display = 'block'
410
- } finally {
411
- homebridge.hideSpinner()
331
+ errorDiv.innerHTML = `<div class="alert alert-danger">Error: ${error.message}</div>`;
332
+ return false;
412
333
  }
413
334
  }
414
- showSupport = () => {
415
- homebridge.showSpinner()
416
- homebridge.hideSchemaForm()
417
- document.getElementById('menuHome').classList.add('btn-elegant')
418
- document.getElementById('menuHome').classList.remove('btn-primary')
419
- document.getElementById('menuDevices').classList.remove('btn-elegant')
420
- document.getElementById('menuDevices').classList.add('btn-primary')
421
- document.getElementById('menuSettings').classList.remove('btn-elegant')
422
- document.getElementById('menuSettings').classList.add('btn-primary')
423
- document.getElementById('menuAccount').classList.remove('btn-elegant')
424
- document.getElementById('menuAccount').classList.add('btn-primary')
425
- document.getElementById('pageSupport').style.display = 'block'
426
- document.getElementById('pageDevices').style.display = 'none'
427
- document.getElementById('pageAccount').style.display = 'none'
428
- homebridge.hideSpinner()
429
- }
430
- showSettings = () => {
431
- homebridge.showSpinner()
432
- document.getElementById('menuHome').classList.remove('btn-elegant')
433
- document.getElementById('menuHome').classList.add('btn-primary')
434
- document.getElementById('menuDevices').classList.remove('btn-elegant')
435
- document.getElementById('menuDevices').classList.add('btn-primary')
436
- document.getElementById('menuSettings').classList.add('btn-elegant')
437
- document.getElementById('menuSettings').classList.remove('btn-primary')
438
- document.getElementById('menuAccount').classList.remove('btn-elegant')
439
- document.getElementById('menuAccount').classList.add('btn-primary')
440
- document.getElementById('pageSupport').style.display = 'none'
441
- document.getElementById('pageDevices').style.display = 'none'
442
- document.getElementById('pageAccount').style.display = 'none'
443
- homebridge.showSchemaForm()
444
- homebridge.hideSpinner()
445
- }
446
- showAccount = () => {
447
- homebridge.showSpinner()
448
- const backToIntro = document.getElementById('backToIntro')
449
- backToIntro.addEventListener('click', () => {
450
- showIntro()
451
- })
452
- backToIntro.style.display = 'none'
453
- const linkButton = document.getElementById('linkButton')
454
- linkButton.addEventListener('click', () => {
455
- linkAccount()
456
- })
457
- const unLinkButton = document.getElementById('unLinkButton')
458
- unLinkButton.style.display = 'none'
459
- unLinkButton.addEventListener('click', () => {
460
- unLinkAccount()
461
- })
462
- document.getElementById('linkSuccess').style.display = 'none'
463
- homebridge.hideSchemaForm()
464
- document.getElementById('menuHome').classList.remove('btn-elegant')
465
- document.getElementById('menuHome').classList.add('btn-primary')
466
- document.getElementById('menuDevices').classList.remove('btn-elegant')
467
- document.getElementById('menuDevices').classList.add('btn-primary')
468
- document.getElementById('menuAccount').classList.add('btn-elegant')
469
- document.getElementById('menuAccount').classList.remove('btn-primary')
470
- document.getElementById('menuSettings').classList.remove('btn-elegant')
471
- document.getElementById('menuSettings').classList.add('btn-primary')
472
- document.getElementById('pageSupport').style.display = 'none'
473
- document.getElementById('pageDevices').style.display = 'none'
474
- document.getElementById('pageAccount').style.display = 'block'
475
- document.getElementById('linkButton').style.display = 'inline-block'
476
- let isRelink = false
477
- if (currentConfig[0] && currentConfig[0].credentials) {
478
- const key = currentConfig[0].credentials.consumerKey || ''
479
- const secret = currentConfig[0].credentials.consumerSecret || ''
480
- document.getElementById('inputConsumerKey').value = key
481
- document.getElementById('inputConsumerSecret').value = secret
482
- if (key && secret) {
483
- isRelink = true
484
- document.getElementById('unLinkButton').style.display = 'inline-block'
485
- } else {
486
- document.getElementById('unLinkButton').style.display = 'none'
487
- }
488
- }
489
- document.getElementById('linkButton').innerHTML = isRelink
490
- ? 'Relink Account &rarr;'
491
- : 'Link Account &rarr;'
492
- if (!isRelink) {
493
- document.getElementById('backToIntro').style.display = 'inline-block'
494
- } else {
495
- document.getElementById('backToIntro').style.display = 'none'
335
+ },
336
+ name: 'homebridge-resideo'
337
+ });
338
+
339
+ // Legacy functionality for existing UI elements
340
+ const setupLegacyHandlers = () => {
341
+ const currentConfig = homebridge.getPluginConfig();
342
+ const hostname = window.location.hostname;
343
+
344
+ homebridge.addEventListener('creds-received', async event => {
345
+ try {
346
+ if (event.data.access && event.data.refresh) {
347
+ currentConfig[0].credentials = {
348
+ consumerKey: event.data.key,
349
+ consumerSecret: event.data.secret,
350
+ accessToken: event.data.access,
351
+ refreshToken: event.data.refresh
352
+ };
353
+ await homebridge.updatePluginConfig(currentConfig);
354
+ await homebridge.savePluginConfig();
355
+ homebridge.toast.success("Successfully Linked Resideo Account", "homebridge-resideo");
496
356
  }
497
- homebridge.hideSpinner()
357
+ } catch (err) {
358
+ console.log(err);
359
+ homebridge.toast.error('Try again, or see the console message for info.', 'Error');
498
360
  }
499
- menuHome.addEventListener('click', () => showSupport())
500
- menuAccount.addEventListener('click', () => showAccount())
501
- menuDevices.addEventListener('click', () => showDevices())
502
- menuSettings.addEventListener('click', () => showSettings())
503
- if (currentConfig.length) {
504
- document.getElementById('menuWrapper').style.display = 'inline-flex'
505
- showAccount()
506
- } else {
507
- currentConfig.push({ name: 'Resideo' })
508
- await homebridge.updatePluginConfig(currentConfig)
509
- showIntro()
361
+ });
362
+
363
+ window.copyMyText = () => {
364
+ const textToCopy = document.getElementById("copyMe");
365
+ textToCopy.select();
366
+ document.execCommand("copy");
367
+ };
368
+
369
+ window.linkAccount = async () => {
370
+ document.getElementById('validateForm').style.display = 'none';
371
+ if (!document.getElementById('inputConsumerKey').value || !document.getElementById('inputConsumerSecret').value) {
372
+ document.getElementById('validateForm').style.display = 'block';
373
+ return;
510
374
  }
375
+ const w = 450;
376
+ const h = 700;
377
+ const y = window.top.outerHeight / 2 + window.top.screenY - (h / 2);
378
+ const x = window.top.outerWidth / 2 + window.top.screenX - (w / 2);
379
+ const urlToOpen = 'http://' + hostname + ':8585/start?' +
380
+ 'key=' + document.getElementById('inputConsumerKey').value +
381
+ '&secret=' + document.getElementById('inputConsumerSecret').value +
382
+ '&host=' + encodeURI(hostname);
383
+ window.popup = window.open(
384
+ urlToOpen, 'honeywell-auth',
385
+ 'toolbar=no, location=no, directories=no, status=no, menubar=no scrollbars=no, resizable=no, copyhistory=no, ' +
386
+ 'width=' + w + ', height=' + h + ', top=' + y + ', left=' + x,
387
+ );
388
+ };
389
+ };
390
+
391
+ // Show the webUI
392
+ (async () => {
393
+ try {
394
+ await homebridge.request('Start Resideo Login Server');
395
+ setupLegacyHandlers();
396
+ ui.show();
511
397
  } catch (err) {
512
- homebridge.toast.error(err.message, 'Error')
513
- } finally {
514
- homebridge.hideSpinner()
398
+ homebridge.toast.error(err.message, 'Error');
515
399
  }
516
- })()
400
+ })();
517
401
  </script>