@homebridge-plugins/homebridge-resideo 2.1.7-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. package/.github/FUNDING.yml +4 -0
  2. package/.github/ISSUE_TEMPLATE/bug-report.yml +97 -0
  3. package/.github/ISSUE_TEMPLATE/config.yml +8 -0
  4. package/.github/ISSUE_TEMPLATE/feature-request.yml +38 -0
  5. package/.github/ISSUE_TEMPLATE/support-request.yml +85 -0
  6. package/.github/ISSUE_TEMPLATE.md +52 -0
  7. package/.github/PULL_REQUEST_TEMPLATE/pull_request.md +27 -0
  8. package/.github/auto-merge.yml +3 -0
  9. package/.github/dependabot.yml +17 -0
  10. package/.github/labeler.yml +38 -0
  11. package/.github/npm-version-script-esm.js +85 -0
  12. package/.github/release-drafter.yml +33 -0
  13. package/.github/workflows/beta-release.yml +55 -0
  14. package/.github/workflows/build.yml +18 -0
  15. package/.github/workflows/changerelease.yml +11 -0
  16. package/.github/workflows/labeler.yml +9 -0
  17. package/.github/workflows/release-drafter.yml +14 -0
  18. package/.github/workflows/release.yml +35 -0
  19. package/.github/workflows/stale.yml +12 -0
  20. package/CHANGELOG.md +161 -0
  21. package/LICENSE +14 -0
  22. package/README.md +67 -0
  23. package/SECURITY.md +19 -0
  24. package/branding/Homebridge_x_Resideo.svg +28 -0
  25. package/branding/honeywell/Homebridge_x_Honeywell.svg +1 -0
  26. package/branding/honeywell/homebridge-honeywell-home-changelog.md +851 -0
  27. package/branding/icon.png +0 -0
  28. package/config.schema.json +611 -0
  29. package/dist/devices/device.d.ts +41 -0
  30. package/dist/devices/device.d.ts.map +1 -0
  31. package/dist/devices/device.js +279 -0
  32. package/dist/devices/device.js.map +1 -0
  33. package/dist/devices/leaksensors.d.ts +34 -0
  34. package/dist/devices/leaksensors.d.ts.map +1 -0
  35. package/dist/devices/leaksensors.js +300 -0
  36. package/dist/devices/leaksensors.js.map +1 -0
  37. package/dist/devices/roomsensors.d.ts +38 -0
  38. package/dist/devices/roomsensors.d.ts.map +1 -0
  39. package/dist/devices/roomsensors.js +274 -0
  40. package/dist/devices/roomsensors.js.map +1 -0
  41. package/dist/devices/roomsensorthermostats.d.ts +57 -0
  42. package/dist/devices/roomsensorthermostats.d.ts.map +1 -0
  43. package/dist/devices/roomsensorthermostats.js +700 -0
  44. package/dist/devices/roomsensorthermostats.js.map +1 -0
  45. package/dist/devices/thermostats.d.ts +73 -0
  46. package/dist/devices/thermostats.d.ts.map +1 -0
  47. package/dist/devices/thermostats.js +930 -0
  48. package/dist/devices/thermostats.js.map +1 -0
  49. package/dist/devices/valve.d.ts +21 -0
  50. package/dist/devices/valve.d.ts.map +1 -0
  51. package/dist/devices/valve.js +151 -0
  52. package/dist/devices/valve.js.map +1 -0
  53. package/dist/homebridge-ui/public/index.html +423 -0
  54. package/dist/homebridge-ui/server.d.ts +2 -0
  55. package/dist/homebridge-ui/server.d.ts.map +1 -0
  56. package/dist/homebridge-ui/server.js +131 -0
  57. package/dist/homebridge-ui/server.js.map +1 -0
  58. package/dist/index.d.ts +4 -0
  59. package/dist/index.d.ts.map +1 -0
  60. package/dist/index.js +7 -0
  61. package/dist/index.js.map +1 -0
  62. package/dist/platform.d.ts +94 -0
  63. package/dist/platform.d.ts.map +1 -0
  64. package/dist/platform.js +890 -0
  65. package/dist/platform.js.map +1 -0
  66. package/dist/settings.d.ts +409 -0
  67. package/dist/settings.d.ts.map +1 -0
  68. package/dist/settings.js +25 -0
  69. package/dist/settings.js.map +1 -0
  70. package/dist/utils.d.ts +21 -0
  71. package/dist/utils.d.ts.map +1 -0
  72. package/dist/utils.js +58 -0
  73. package/dist/utils.js.map +1 -0
  74. package/docs/.nojekyll +1 -0
  75. package/docs/assets/highlight.css +22 -0
  76. package/docs/assets/icons.js +18 -0
  77. package/docs/assets/icons.svg +1 -0
  78. package/docs/assets/main.js +60 -0
  79. package/docs/assets/navigation.js +1 -0
  80. package/docs/assets/search.js +1 -0
  81. package/docs/assets/style.css +1493 -0
  82. package/docs/functions/default.html +1 -0
  83. package/docs/index.html +73 -0
  84. package/docs/modules.html +2 -0
  85. package/eslint.config.js +50 -0
  86. package/package.json +102 -0
  87. package/typedoc.json +22 -0
@@ -0,0 +1,423 @@
1
+ <p class="text-center">
2
+ <img
3
+ src="https://raw.githubusercontent.com/homebridge-plugins/homebridge-resideo/latest/branding/Homebridge_x_Resideo.svg"
4
+ alt="homebridge-resideo logo" style="width: 40%;" />
5
+ </p>
6
+ <div id="pageIntro" style="display: none;">
7
+ <p class="lead text-center">Thank you for installing <strong>homebridge-resideo</strong></p>
8
+ <p class="lead text-center">Before continuing:</p>
9
+ <ol>
10
+ <li class="mb-3">Login / create an account at <a href="https://developer.honeywellhome.com/user" target="_blank"
11
+ rel="noreferrer noopener">https://developer.honeywellhome.com/user</a>.</li>
12
+ <li class="mb-3">Click <strong>Create New App</strong>.</li>
13
+ <li class="mb-3">
14
+ Give your application a name, and enter the 'Callback URL' exactly as it is displayed below.
15
+ <br>
16
+ <div class="input-group mt-3">
17
+ <input type="text" class="form-control" disabled id="copyMe">
18
+ <div class="input-group-append">
19
+ <button class="btn btn-primary m-0 py-0 px-3" onclick="copyMyText()" type="button">
20
+ <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" fill="currentColor"
21
+ class="mb-1 bi bi-clipboard" viewBox="0 0 16 16">
22
+ <path
23
+ d="M4 1.5H3a2 2 0 0 0-2 2V14a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V3.5a2 2 0 0 0-2-2h-1v1h1a1 1 0 0 1 1 1V14a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V3.5a1 1 0 0 1 1-1h1v-1z" />
24
+ <path
25
+ d="M9.5 1a.5.5 0 0 1 .5.5v1a.5.5 0 0 1-.5.5h-3a.5.5 0 0 1-.5-.5v-1a.5.5 0 0 1 .5-.5h3zm-3-1A1.5 1.5 0 0 0 5 1.5v1A1.5 1.5 0 0 0 6.5 4h3A1.5 1.5 0 0 0 11 2.5v-1A1.5 1.5 0 0 0 9.5 0h-3z" />
26
+ </svg>
27
+ </button>
28
+ </div>
29
+ </div>
30
+ </li>
31
+ <li>Enter the generated 'Consumer Key' and 'Consumer Secret' on the next page.</li>
32
+ </ol>
33
+ <div class="text-center">
34
+ <button type="button" class="btn btn-primary" id="introLink">Continue &rarr;</button>
35
+ </div>
36
+ </div>
37
+ <div id="menuWrapper" class="btn-group w-100 mb-0" role="group" aria-label="UI Menu" style="display: none;">
38
+ <button type="button" class="btn btn-primary ml-0" id="menuAccount">
39
+ Account
40
+ </button>
41
+ <button type="button" class="btn btn-primary" id="menuSettings">
42
+ Settings
43
+ </button>
44
+ <button type="button" class="btn btn-primary" id="menuDevices">
45
+ Devices
46
+ </button>
47
+ <button type="button" class="btn btn-primary mr-0" id="menuHome">
48
+ Support
49
+ </button>
50
+ </div>
51
+ <div id="pageAccount" class="mt-4" style="display: none;">
52
+ <div class="alert alert-warning" style="display: none;" id="validateForm">
53
+ Please enter both values
54
+ </div>
55
+ <div class="alert alert-success" style="display: none;" id="linkSuccess">
56
+ Config saved, please close this window and restart Homebridge
57
+ </div>
58
+ <div class="form-group">
59
+ <label for="inputConsumerKey">Consumer Key <span class="text-danger">*</span></label>
60
+ <input type="text" class="form-control" id="inputConsumerKey">
61
+ </div>
62
+ <div class="form-group">
63
+ <label for="inputConsumerSecret">Consumer Secret <span class="text-danger">*</span></label>
64
+ <input type="text" class="form-control" id="inputConsumerSecret">
65
+ </div>
66
+ <div class="form-group text-center">
67
+ <button type="button" class="btn btn-primary" id="backToIntro">&larr;</button>
68
+ <button type="button" class="btn btn-primary" id="linkButton">Continue &rarr;</button>
69
+ <button type="button" class="btn btn-danger" id="unLinkButton">Unlink Account &rarr;</button>
70
+ </div>
71
+ </div>
72
+ <div id="pageDevices" class="mt-4" style="display: none;">
73
+ <div id="deviceInfo">
74
+ <form>
75
+ <div class="form-group">
76
+ <select class="form-control" id="deviceSelect"></select>
77
+ </div>
78
+ </form>
79
+ <table class="table w-100" id="deviceTable" style="display: none;">
80
+ <thead>
81
+ <tr class="table-active">
82
+ <th scope="col" style="width: 40%;">Device Name</th>
83
+ <th scope="col" style="width: 60%;" id="displayName"></th>
84
+ </tr>
85
+ </thead>
86
+ <tbody>
87
+ <tr>
88
+ <th scope="row">Device ID</th>
89
+ <td id="deviceID"></td>
90
+ </tr>
91
+ <tr>
92
+ <th scope="row">Model</th>
93
+ <td id="model"></td>
94
+ </tr>
95
+ <tr>
96
+ <th scope="row">Firmware Version</th>
97
+ <td id="firmwareRevision"></td>
98
+ </tr>
99
+ </tbody>
100
+ </table>
101
+ </div>
102
+ </div>
103
+ <div id="pageSupport" class="mt-4" style="display: none;">
104
+ <p class="text-center lead">Thank you for using <strong>homebridge-resideo</strong></p>
105
+ <p class="text-center">The links below will take you to our GitHub wiki</p>
106
+ <h5>Setup</h5>
107
+ <ul>
108
+ <li>
109
+ <a href="https://github.com/homebridge-plugins/homebridge-resideo/wiki" target="_blank">Wiki Home</a>
110
+ </li>
111
+ <li>
112
+ <a href="https://github.com/homebridge-plugins/homebridge-resideo/wiki/Configuration"
113
+ target="_blank">Configuration</a>
114
+ </li>
115
+ <li>
116
+ <a href="https://github.com/homebridge-plugins/homebridge-resideo/wiki/Beta-Version" target="_blank">Beta
117
+ Version</a>
118
+ </li>
119
+ <li>
120
+ <a href="https://github.com/homebridge-plugins/homebridge-resideo/wiki/Node-Version" target="_blank">Node
121
+ Version</a>
122
+ </li>
123
+ <li>
124
+ <a href="https://github.com/homebridge-plugins/homebridge-resideo/wiki/Uninstallation"
125
+ target="_blank">Uninstallation</a>
126
+ </li>
127
+ </ul>
128
+ <h5>Features</h5>
129
+ <ul>
130
+ <li>
131
+ <a href="https://github.com/homebridge-plugins/homebridge-resideo/wiki/Supported-Devices" target="_blank">Supported
132
+ Devices</a>
133
+ </li>
134
+ <li>
135
+ <a href="https://github.com/homebridge-plugins/homebridge-resideo/wiki/Fan-Modes" target="_blank">Fan
136
+ Modes</a>
137
+ </li>
138
+ </ul>
139
+ <h5>Help/About</h5>
140
+ <ul>
141
+ <li>
142
+ <a href="https://github.com/homebridge-plugins/homebridge-resideo/issues/new/choose" target="_blank">Support
143
+ Request</a>
144
+ </li>
145
+ <li>
146
+ <a href="https://github.com/homebridge-plugins/homebridge-resideo/blob/latest/CHANGELOG.md"
147
+ target="_blank">Changelog</a>
148
+ </li>
149
+ <li>
150
+ <a href="https://github.com/sponsors/donavanbecker" target="_blank">About Me</a>
151
+ </li>
152
+ </ul>
153
+ <h5>Disclaimer</h5>
154
+ <ul>
155
+ <li>
156
+ I am in no way affiliated with Resideo and this plugin is a personal project that I maintain in
157
+ my free time.
158
+ </li>
159
+ <li>
160
+ Use this plugin entirely at your own risk - please see licence for more information.
161
+ </li>
162
+ </ul>
163
+ </div>
164
+ <script>
165
+ ; (async () => {
166
+ try {
167
+ const currentConfig = await homebridge.getPluginConfig()
168
+ const hostname = window.location.hostname
169
+ try {
170
+ await homebridge.request('Start Resideo Login Server')
171
+ } catch (err) {
172
+ const msg = err.message
173
+ homebridge.toast.error(msg, 'HTTP Server Not Started')
174
+ }
175
+ homebridge.addEventListener('creds-received', async event => {
176
+ try {
177
+ if (event.data.access && event.data.refresh) {
178
+ this.popup.close()
179
+ currentConfig[0].credentials = {
180
+ consumerKey: event.data.key,
181
+ consumerSecret: event.data.secret,
182
+ accessToken: event.data.access,
183
+ refreshToken: event.data.refresh
184
+ }
185
+ await homebridge.updatePluginConfig(currentConfig)
186
+ await homebridge.savePluginConfig()
187
+ homebridge.toast.success("Successfully Linked Resideo Account", "homebridge-resideo")
188
+ document.getElementById('backToIntro').style.display = 'none'
189
+ document.getElementById('linkButton').style.display = 'none'
190
+ document.getElementById('unLinkButton').style.display = 'none'
191
+ document.getElementById('linkSuccess').style.display = 'block'
192
+ } else {
193
+ throw new Error('no access/token received from server')
194
+ }
195
+ } catch (err) {
196
+ console.log(err)
197
+ homebridge.toast.error('Try again, or see the console message for info.', 'Error')
198
+ }
199
+ })
200
+ copyMyText = () => {
201
+ var textToCopy = document.getElementById("copyMe");
202
+ textToCopy.select();
203
+ document.execCommand("copy");
204
+ }
205
+ linkAccount = async () => {
206
+ document.getElementById('validateForm').style.display = 'none'
207
+ if (
208
+ !document.getElementById('inputConsumerKey').value ||
209
+ !document.getElementById('inputConsumerSecret').value
210
+ ) {
211
+ document.getElementById('validateForm').style.display = 'block'
212
+ return
213
+ }
214
+ const w = 450;
215
+ const h = 700;
216
+ const y = window.top.outerHeight / 2 + window.top.screenY - (h / 2);
217
+ const x = window.top.outerWidth / 2 + window.top.screenX - (w / 2);
218
+ const urlToOpen = 'http://' + hostname + ':8585/start?' +
219
+ 'key=' + document.getElementById('inputConsumerKey').value +
220
+ '&secret=' + document.getElementById('inputConsumerSecret').value +
221
+ '&host=' + encodeURI(hostname);
222
+ this.popup = window.open(
223
+ urlToOpen, 'honeywell-auth',
224
+ 'toolbar=no, location=no, directories=no, status=no, menubar=no scrollbars=no, resizable=no, copyhistory=no, ' +
225
+ 'width=' + w + ', height=' + h + ', top=' + y + ', left=' + x,
226
+ )
227
+ }
228
+ unLinkAccount = async () => {
229
+ try {
230
+ document.getElementById('validateForm').style.display = 'none'
231
+ if (
232
+ !document.getElementById('inputConsumerKey').value ||
233
+ !document.getElementById('inputConsumerSecret').value
234
+ ) {
235
+ document.getElementById('validateForm').style.display = 'block'
236
+ return
237
+ }
238
+ delete currentConfig[0].credentials
239
+ await homebridge.updatePluginConfig(currentConfig)
240
+ await homebridge.savePluginConfig()
241
+ document.getElementById('inputConsumerKey').value = ''
242
+ document.getElementById('inputConsumerSecret').value = ''
243
+ document.getElementById('unLinkButton').style.display = 'none'
244
+ } catch (err) {
245
+ console.error(err)
246
+ homebridge.toast.error('Try again, or see the console message for info.', 'Error')
247
+ }
248
+ }
249
+ showIntro = () => {
250
+ const introLink = document.getElementById('introLink')
251
+ document.getElementById('copyMe').value = 'http://' + hostname + ':8585/auth'
252
+ introLink.addEventListener('click', () => {
253
+ homebridge.showSpinner()
254
+ document.getElementById('pageIntro').style.display = 'none'
255
+ document.getElementById('menuWrapper').style.display = 'inline-flex'
256
+ showAccount()
257
+ homebridge.hideSpinner()
258
+ })
259
+ document.getElementById('pageAccount').style.display = 'none'
260
+ document.getElementById('menuWrapper').style.display = 'none'
261
+ document.getElementById('pageIntro').style.display = 'block'
262
+ }
263
+ showDevices = async () => {
264
+ homebridge.showSpinner()
265
+ homebridge.hideSchemaForm()
266
+ document.getElementById('menuHome').classList.remove('btn-elegant')
267
+ document.getElementById('menuHome').classList.add('btn-primary')
268
+ document.getElementById('menuDevices').classList.add('btn-elegant')
269
+ document.getElementById('menuDevices').classList.remove('btn-primary')
270
+ document.getElementById('menuAccount').classList.remove('btn-elegant')
271
+ document.getElementById('menuAccount').classList.add('btn-primary')
272
+ document.getElementById('menuSettings').classList.remove('btn-elegant')
273
+ document.getElementById('menuSettings').classList.add('btn-primary')
274
+ document.getElementById('pageSupport').style.display = 'none'
275
+ document.getElementById('pageAccount').style.display = 'none'
276
+ document.getElementById('pageDevices').style.display = 'block'
277
+ const cachedAccessories =
278
+ typeof homebridge.getCachedAccessories === 'function'
279
+ ? await homebridge.getCachedAccessories()
280
+ : await homebridge.request('/getCachedAccessories')
281
+ if (cachedAccessories.length > 0) {
282
+ cachedAccessories.sort((a, b) => {
283
+ return a.displayName.toLowerCase() > b.displayName.toLowerCase()
284
+ ? 1
285
+ : b.displayName.toLowerCase() > a.displayName.toLowerCase()
286
+ ? -1
287
+ : 0
288
+ })
289
+ }
290
+ const deviceSelect = document.getElementById('deviceSelect')
291
+ deviceSelect.innerHTML = ''
292
+ cachedAccessories.forEach(a => {
293
+ const option = document.createElement('option')
294
+ option.text = a.displayName
295
+ option.value = a.UUID
296
+ deviceSelect.add(option)
297
+ })
298
+ showDeviceInfo = async UUID => {
299
+ homebridge.showSpinner()
300
+ const thisAcc = cachedAccessories.find(x => x.UUID === UUID)
301
+ const context = thisAcc.context
302
+ document.getElementById('displayName').innerHTML = thisAcc.displayName
303
+ document.getElementById('deviceID').innerHTML = context.deviceID
304
+ document.getElementById('model').innerHTML = context.model
305
+ document.getElementById('firmwareRevision').innerHTML = context.firmwareRevision || 'N/A'
306
+ document.getElementById('deviceTable').style.display = 'inline-table'
307
+ homebridge.hideSpinner()
308
+ }
309
+ deviceSelect.addEventListener('change', event => showDeviceInfo(event.target.value))
310
+ if (cachedAccessories.length > 0) {
311
+ showDeviceInfo(cachedAccessories[0].UUID)
312
+ } else {
313
+ const option = document.createElement('option')
314
+ option.text = 'No Devices'
315
+ deviceSelect.add(option)
316
+ deviceSelect.disabled = true
317
+ }
318
+ homebridge.hideSpinner()
319
+ }
320
+ showSupport = () => {
321
+ homebridge.showSpinner()
322
+ homebridge.hideSchemaForm()
323
+ document.getElementById('menuHome').classList.add('btn-elegant')
324
+ document.getElementById('menuHome').classList.remove('btn-primary')
325
+ document.getElementById('menuDevices').classList.remove('btn-elegant')
326
+ document.getElementById('menuDevices').classList.add('btn-primary')
327
+ document.getElementById('menuSettings').classList.remove('btn-elegant')
328
+ document.getElementById('menuSettings').classList.add('btn-primary')
329
+ document.getElementById('menuAccount').classList.remove('btn-elegant')
330
+ document.getElementById('menuAccount').classList.add('btn-primary')
331
+ document.getElementById('pageSupport').style.display = 'block'
332
+ document.getElementById('pageDevices').style.display = 'none'
333
+ document.getElementById('pageAccount').style.display = 'none'
334
+ homebridge.hideSpinner()
335
+ }
336
+ showSettings = () => {
337
+ homebridge.showSpinner()
338
+ document.getElementById('menuHome').classList.remove('btn-elegant')
339
+ document.getElementById('menuHome').classList.add('btn-primary')
340
+ document.getElementById('menuDevices').classList.remove('btn-elegant')
341
+ document.getElementById('menuDevices').classList.add('btn-primary')
342
+ document.getElementById('menuSettings').classList.add('btn-elegant')
343
+ document.getElementById('menuSettings').classList.remove('btn-primary')
344
+ document.getElementById('menuAccount').classList.remove('btn-elegant')
345
+ document.getElementById('menuAccount').classList.add('btn-primary')
346
+ document.getElementById('pageSupport').style.display = 'none'
347
+ document.getElementById('pageDevices').style.display = 'none'
348
+ document.getElementById('pageAccount').style.display = 'none'
349
+ homebridge.showSchemaForm()
350
+ homebridge.hideSpinner()
351
+ }
352
+ showAccount = () => {
353
+ homebridge.showSpinner()
354
+ const backToIntro = document.getElementById('backToIntro')
355
+ backToIntro.addEventListener('click', () => {
356
+ showIntro()
357
+ })
358
+ backToIntro.style.display = 'none'
359
+ const linkButton = document.getElementById('linkButton')
360
+ linkButton.addEventListener('click', () => {
361
+ linkAccount()
362
+ })
363
+ const unLinkButton = document.getElementById('unLinkButton')
364
+ unLinkButton.style.display = 'none'
365
+ unLinkButton.addEventListener('click', () => {
366
+ unLinkAccount()
367
+ })
368
+ document.getElementById('linkSuccess').style.display = 'none'
369
+ homebridge.hideSchemaForm()
370
+ document.getElementById('menuHome').classList.remove('btn-elegant')
371
+ document.getElementById('menuHome').classList.add('btn-primary')
372
+ document.getElementById('menuDevices').classList.remove('btn-elegant')
373
+ document.getElementById('menuDevices').classList.add('btn-primary')
374
+ document.getElementById('menuAccount').classList.add('btn-elegant')
375
+ document.getElementById('menuAccount').classList.remove('btn-primary')
376
+ document.getElementById('menuSettings').classList.remove('btn-elegant')
377
+ document.getElementById('menuSettings').classList.add('btn-primary')
378
+ document.getElementById('pageSupport').style.display = 'none'
379
+ document.getElementById('pageDevices').style.display = 'none'
380
+ document.getElementById('pageAccount').style.display = 'block'
381
+ document.getElementById('linkButton').style.display = 'inline-block'
382
+ let isRelink = false
383
+ if (currentConfig[0] && currentConfig[0].credentials) {
384
+ const key = currentConfig[0].credentials.consumerKey || ''
385
+ const secret = currentConfig[0].credentials.consumerSecret || ''
386
+ document.getElementById('inputConsumerKey').value = key
387
+ document.getElementById('inputConsumerSecret').value = secret
388
+ if (key && secret) {
389
+ isRelink = true
390
+ document.getElementById('unLinkButton').style.display = 'inline-block'
391
+ } else {
392
+ document.getElementById('unLinkButton').style.display = 'none'
393
+ }
394
+ }
395
+ document.getElementById('linkButton').innerHTML = isRelink
396
+ ? 'Relink Account &rarr;'
397
+ : 'Link Account &rarr;'
398
+ if (!isRelink) {
399
+ document.getElementById('backToIntro').style.display = 'inline-block'
400
+ } else {
401
+ document.getElementById('backToIntro').style.display = 'none'
402
+ }
403
+ homebridge.hideSpinner()
404
+ }
405
+ menuHome.addEventListener('click', () => showSupport())
406
+ menuAccount.addEventListener('click', () => showAccount())
407
+ menuDevices.addEventListener('click', () => showDevices())
408
+ menuSettings.addEventListener('click', () => showSettings())
409
+ if (currentConfig.length) {
410
+ document.getElementById('menuWrapper').style.display = 'inline-flex'
411
+ showAccount()
412
+ } else {
413
+ currentConfig.push({ name: 'Resideo' })
414
+ await homebridge.updatePluginConfig(currentConfig)
415
+ showIntro()
416
+ }
417
+ } catch (err) {
418
+ homebridge.toast.error(err.message, 'Error')
419
+ } finally {
420
+ homebridge.hideSpinner()
421
+ }
422
+ })()
423
+ </script>
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/homebridge-ui/server.ts"],"names":[],"mappings":""}
@@ -0,0 +1,131 @@
1
+ import { Buffer } from 'node:buffer';
2
+ import { exec as execCb } from 'node:child_process';
3
+ import fs from 'node:fs';
4
+ import http from 'node:http';
5
+ import util from 'node:util';
6
+ /* eslint-disable no-console */
7
+ import { HomebridgePluginUiServer } from '@homebridge/plugin-ui-utils';
8
+ const exec = util.promisify(execCb);
9
+ class PluginUiServer extends HomebridgePluginUiServer {
10
+ key;
11
+ secret;
12
+ hostname;
13
+ constructor() {
14
+ super();
15
+ this.onRequest('Start Resideo Login Server', () => {
16
+ const runningServer = http.createServer(async (req, res) => {
17
+ try {
18
+ res.writeHead(200, { 'Content-Type': 'text/html' });
19
+ const reqUrl = new URL(req.url ?? '', `http://${req.headers.host}`);
20
+ const action = reqUrl.pathname.replace('/', '');
21
+ const query = reqUrl.searchParams;
22
+ switch (action) {
23
+ case 'start': {
24
+ this.key = query.get('key');
25
+ this.secret = query.get('secret');
26
+ this.hostname = query.get('host');
27
+ const redirectUrl = `http://${this.hostname}:8585/auth`;
28
+ const authUrl = `https://api.honeywell.com/oauth2/authorize?response_type=code&appSelect=1&redirect_uri=${encodeURI(redirectUrl)}&client_id=${query.get('key')}`;
29
+ res.end(`<script>window.location.replace('${authUrl}');</script>`);
30
+ break;
31
+ }
32
+ case 'auth': {
33
+ if (query.get('code')) {
34
+ const code = query.get('code');
35
+ const auth = Buffer.from(`${this.key}:${this.secret}`).toString('base64');
36
+ let curlString = '';
37
+ curlString += 'curl -X POST ';
38
+ curlString += `--header "Authorization: Basic ${auth}" `;
39
+ curlString += '--header "Accept: application/json" ';
40
+ curlString += '--header "Content-Type: application/x-www-form-urlencoded" ';
41
+ curlString += '-d "';
42
+ curlString += 'grant_type=authorization_code&';
43
+ curlString += `code=${code}&`;
44
+ curlString += `redirect_uri=${encodeURI(`http://${this.hostname}:8585/auth`)}`;
45
+ curlString += '" ';
46
+ curlString += '"https://api.honeywell.com/oauth2/token"';
47
+ try {
48
+ const { stdout } = await exec(curlString);
49
+ const response = JSON.parse(stdout);
50
+ if (response.access_token) {
51
+ this.pushEvent('creds-received', {
52
+ key: this.key,
53
+ secret: this.secret,
54
+ access: response.access_token,
55
+ refresh: response.refresh_token,
56
+ });
57
+ res.end('Success. You can close this window now.');
58
+ }
59
+ else {
60
+ res.end('oops.');
61
+ }
62
+ }
63
+ catch (err) {
64
+ res.end(`<strong>An error occurred:</strong><br>${JSON.stringify(err)}<br><br>Close this window and start again`);
65
+ }
66
+ }
67
+ else {
68
+ res.end('<strong>An error occurred:</strong><br>no code received<br><br>Close this window and start again');
69
+ }
70
+ break;
71
+ }
72
+ default: {
73
+ // should never happen
74
+ res.end('welcome to the server');
75
+ break;
76
+ }
77
+ }
78
+ }
79
+ catch (err) {
80
+ console.log(err);
81
+ }
82
+ });
83
+ runningServer.listen(8585, (err) => {
84
+ if (err) {
85
+ console.log(err);
86
+ }
87
+ else {
88
+ console.log('Server is running');
89
+ }
90
+ });
91
+ setTimeout(() => {
92
+ runningServer.close();
93
+ }, 300000);
94
+ // Return a response to satisfy the expected return type
95
+ return { status: 'ok' };
96
+ });
97
+ /*
98
+ A native method getCachedAccessories() was introduced in config-ui-x v4.37.0
99
+ The following is for users who have a lower version of config-ui-x
100
+ */
101
+ this.onRequest('/getCachedAccessories', async () => {
102
+ try {
103
+ // Define the plugin and create the array to return
104
+ const plugin = 'homebridge-resideo';
105
+ const devicesToReturn = [];
106
+ // The path and file of the cached accessories
107
+ const accFile = `${this.homebridgeStoragePath}/accessories/cachedAccessories`;
108
+ // Check the file exists
109
+ if (fs.existsSync(accFile)) {
110
+ // Read the cached accessories file
111
+ const cachedAccessoriesData = await fs.promises.readFile(accFile, 'utf8');
112
+ // Parse the JSON
113
+ const cachedAccessories = JSON.parse(cachedAccessoriesData);
114
+ // We only want the accessories for this plugin
115
+ cachedAccessories
116
+ .filter((accessory) => accessory.plugin === plugin)
117
+ .forEach((accessory) => devicesToReturn.push(accessory));
118
+ }
119
+ // Return the array
120
+ return { status: 'ok', data: devicesToReturn };
121
+ }
122
+ catch (err) {
123
+ // Just return an empty accessory list in case of any errors
124
+ return { status: 'error', data: [] };
125
+ }
126
+ });
127
+ this.ready();
128
+ }
129
+ }
130
+ (() => new PluginUiServer())();
131
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/homebridge-ui/server.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import type { API } from 'homebridge';
2
+ declare const _default: (api: API) => void;
3
+ export default _default;
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,YAAY,CAAA;8BAMhB,GAAG,KAAG,IAAI;AAA/B,wBAEC"}
package/dist/index.js ADDED
@@ -0,0 +1,7 @@
1
+ import { ResideoPlatform } from './platform.js';
2
+ import { PLATFORM_NAME, PLUGIN_NAME } from './settings.js';
3
+ // Register our platform with homebridge.
4
+ export default (api) => {
5
+ api.registerPlatform(PLUGIN_NAME, PLATFORM_NAME, ResideoPlatform);
6
+ };
7
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;AAC/C,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA;AAE1D,yCAAyC;AACzC,eAAe,CAAC,GAAQ,EAAQ,EAAE;IAChC,GAAG,CAAC,gBAAgB,CAAC,WAAW,EAAE,aAAa,EAAE,eAAe,CAAC,CAAA;AACnE,CAAC,CAAA"}