@switchbot/homebridge-switchbot 5.0.0-beta.112 → 5.0.0-beta.113

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 (82) hide show
  1. package/MIGRATION.md +10 -0
  2. package/README.md +43 -0
  3. package/TODO.md +263 -0
  4. package/config.schema.json +27 -10
  5. package/dist/device-types.d.ts.map +1 -1
  6. package/dist/device-types.js +93 -63
  7. package/dist/device-types.js.map +1 -1
  8. package/dist/deviceFactory.d.ts.map +1 -1
  9. package/dist/deviceFactory.js +55 -33
  10. package/dist/deviceFactory.js.map +1 -1
  11. package/dist/devices/genericDevice.d.ts +12 -1
  12. package/dist/devices/genericDevice.d.ts.map +1 -1
  13. package/dist/devices/genericDevice.js +121 -1
  14. package/dist/devices/genericDevice.js.map +1 -1
  15. package/dist/errors.d.ts +5 -0
  16. package/dist/errors.d.ts.map +1 -0
  17. package/dist/errors.js +51 -0
  18. package/dist/errors.js.map +1 -0
  19. package/dist/homebridge-ui/endpoints/devices.d.ts.map +1 -1
  20. package/dist/homebridge-ui/endpoints/devices.js +80 -1
  21. package/dist/homebridge-ui/endpoints/devices.js.map +1 -1
  22. package/dist/homebridge-ui/endpoints/discovery.d.ts.map +1 -1
  23. package/dist/homebridge-ui/endpoints/discovery.js +73 -41
  24. package/dist/homebridge-ui/endpoints/discovery.js.map +1 -1
  25. package/dist/homebridge-ui/public/css/styles.css +98 -0
  26. package/dist/homebridge-ui/public/index.html +57 -0
  27. package/dist/homebridge-ui/public/js/api.d.ts +24 -1
  28. package/dist/homebridge-ui/public/js/api.d.ts.map +1 -1
  29. package/dist/homebridge-ui/public/js/api.js +33 -2
  30. package/dist/homebridge-ui/public/js/api.js.map +1 -1
  31. package/dist/homebridge-ui/public/js/api.ts +52 -3
  32. package/dist/homebridge-ui/public/js/app.js +1503 -104
  33. package/dist/homebridge-ui/public/js/app.js.map +3 -3
  34. package/dist/homebridge-ui/public/js/app.ts +2 -1
  35. package/dist/homebridge-ui/public/js/devices.d.ts.map +1 -1
  36. package/dist/homebridge-ui/public/js/devices.js +2 -0
  37. package/dist/homebridge-ui/public/js/devices.js.map +1 -1
  38. package/dist/homebridge-ui/public/js/devices.ts +13 -1
  39. package/dist/homebridge-ui/public/js/discovery.d.ts +1 -0
  40. package/dist/homebridge-ui/public/js/discovery.d.ts.map +1 -1
  41. package/dist/homebridge-ui/public/js/discovery.js +1223 -126
  42. package/dist/homebridge-ui/public/js/discovery.js.map +1 -1
  43. package/dist/homebridge-ui/public/js/discovery.ts +1323 -145
  44. package/dist/homebridge-ui/public/js/modals.d.ts.map +1 -1
  45. package/dist/homebridge-ui/public/js/modals.js +42 -0
  46. package/dist/homebridge-ui/public/js/modals.js.map +1 -1
  47. package/dist/homebridge-ui/public/js/modals.ts +50 -1
  48. package/dist/homebridge-ui/public/js/render.d.ts +3 -0
  49. package/dist/homebridge-ui/public/js/render.d.ts.map +1 -1
  50. package/dist/homebridge-ui/public/js/render.js +311 -1
  51. package/dist/homebridge-ui/public/js/render.js.map +1 -1
  52. package/dist/homebridge-ui/public/js/render.ts +341 -2
  53. package/dist/platform.d.ts +1 -0
  54. package/dist/platform.d.ts.map +1 -1
  55. package/dist/platform.js +6 -2
  56. package/dist/platform.js.map +1 -1
  57. package/dist/switchbotClient.d.ts.map +1 -1
  58. package/dist/switchbotClient.js +51 -5
  59. package/dist/switchbotClient.js.map +1 -1
  60. package/docs/assets/highlight.css +9 -2
  61. package/docs/index.html +28 -2
  62. package/docs/variables/default.html +1 -1
  63. package/package.json +2 -2
  64. package/src/device-types.ts +95 -64
  65. package/src/deviceFactory.ts +55 -33
  66. package/src/devices/genericDevice.ts +113 -1
  67. package/src/errors.ts +43 -0
  68. package/src/homebridge-ui/endpoints/devices.ts +82 -1
  69. package/src/homebridge-ui/endpoints/discovery.ts +78 -44
  70. package/src/homebridge-ui/public/css/styles.css +98 -0
  71. package/src/homebridge-ui/public/index.html +57 -0
  72. package/src/homebridge-ui/public/js/api.ts +52 -3
  73. package/src/homebridge-ui/public/js/app.ts +2 -1
  74. package/src/homebridge-ui/public/js/devices.ts +13 -1
  75. package/src/homebridge-ui/public/js/discovery.ts +1323 -145
  76. package/src/homebridge-ui/public/js/modals.ts +50 -1
  77. package/src/homebridge-ui/public/js/render.ts +341 -2
  78. package/src/platform.ts +6 -2
  79. package/src/switchbotClient.ts +44 -9
  80. package/test/matter-integration.spec.ts +21 -0
  81. package/todo/COMPLETED.md +300 -0
  82. package/todo/TODO.md +80 -66
@@ -10,9 +10,37 @@ import { uiLog } from '../utils/logger.js'
10
10
  * Discovers SwitchBot devices via BLE and OpenAPI
11
11
  */
12
12
  export function registerDiscoveryEndpoint(server: HomebridgePluginUiServer) {
13
- server.onRequest('/discover', async () => {
13
+ server.onRequest('/ble-status', async () => {
14
14
  try {
15
15
  const { platform } = await getSwitchBotPlatformConfig(server)
16
+ const token = getCredential(platform, 'openApiToken') || platform.token
17
+ const secret = getCredential(platform, 'openApiSecret') || platform.secret
18
+ const { SwitchBot } = await import('node-switchbot')
19
+ const switchbot = new SwitchBot({
20
+ token: token || undefined,
21
+ secret: secret || undefined,
22
+ enableBLE: true,
23
+ enableFallback: true,
24
+ enableRetry: true,
25
+ })
26
+
27
+ await switchbot.discover({ timeout: 1000 })
28
+ return { success: true, data: { available: true, message: 'adapter ready' } }
29
+ } catch (e) {
30
+ const message = e instanceof Error ? e.message : String(e)
31
+ uiLog.warn(`GET /ble-status failed: ${message}`)
32
+ return { success: true, data: { available: false, message } }
33
+ }
34
+ })
35
+
36
+ server.onRequest('/discover', async (payload?: any) => {
37
+ try {
38
+ const { platform } = await getSwitchBotPlatformConfig(server)
39
+ const mode = String(payload?.mode || 'all').toLowerCase()
40
+ const runBle = mode === 'all' || mode === 'ble'
41
+ const runOpenApi = mode === 'all' || mode === 'openapi'
42
+ const bleScanDurationSeconds = Math.max(3, Math.min(15, Number(payload?.bleScanDurationSeconds || 5)))
43
+ const bleTimeoutSeconds = Math.max(3, Math.min(30, Number(payload?.bleTimeoutSeconds || 8)))
16
44
 
17
45
  uiLog.debug(`GET /discover - Platform config keys: ${Object.keys(platform).join(', ')}`)
18
46
 
@@ -40,55 +68,57 @@ export function registerDiscoveryEndpoint(server: HomebridgePluginUiServer) {
40
68
  const deviceMap = new Map<string, any>()
41
69
 
42
70
  // 1. Try BLE discovery first (with timeout)
43
- uiLog.info('GET /discover - Starting BLE scan...')
44
- try {
45
- const bleTimeout = 8000 // 8 second timeout for BLE scan
46
- const bleDiscoveryPromise = switchbot.discover({ timeout: 5000 }) // 5 second scan timeout
47
- const bleDevices = await Promise.race([
48
- bleDiscoveryPromise,
49
- new Promise<any[]>(resolve => setTimeout(resolve, bleTimeout, [])),
50
- ])
51
-
52
- if (Array.isArray(bleDevices) && bleDevices.length > 0) {
53
- uiLog.info(`GET /discover - Found ${bleDevices.length} BLE devices`)
54
- for (const [index, d] of bleDevices.entries()) {
55
- const info = typeof d?.getInfo === 'function' ? d.getInfo() : undefined
56
- const id = d?.id
57
- || (typeof d?.getId === 'function' ? d.getId() : undefined)
58
- || info?.id
59
-
60
- const mac = d?.mac
61
- || (typeof d?.getMAC === 'function' ? d.getMAC() : undefined)
62
- || info?.mac
71
+ if (runBle) {
72
+ uiLog.info('GET /discover - Starting BLE scan...')
73
+ try {
74
+ const bleTimeout = bleTimeoutSeconds * 1000
75
+ const bleDiscoveryPromise = switchbot.discover({ timeout: bleScanDurationSeconds * 1000 })
76
+ const bleDevices = await Promise.race([
77
+ bleDiscoveryPromise,
78
+ new Promise<any[]>(resolve => setTimeout(resolve, bleTimeout, [])),
79
+ ])
80
+
81
+ if (Array.isArray(bleDevices) && bleDevices.length > 0) {
82
+ uiLog.info(`GET /discover - Found ${bleDevices.length} BLE devices`)
83
+ for (const [index, d] of bleDevices.entries()) {
84
+ const info = typeof d?.getInfo === 'function' ? d.getInfo() : undefined
85
+ const id = d?.id
86
+ || (typeof d?.getId === 'function' ? d.getId() : undefined)
87
+ || info?.id
88
+
89
+ const mac = d?.mac
90
+ || (typeof d?.getMAC === 'function' ? d.getMAC() : undefined)
91
+ || info?.mac
92
+
93
+ if (!id) {
94
+ uiLog.warn(`GET /discover - BLE device at index ${index} has no id, skipping`)
95
+ continue
96
+ }
97
+
98
+ const connectionTypes = Array.isArray(info?.connectionTypes) ? info.connectionTypes : []
99
+ const isHybrid = connectionTypes.includes('api')
63
100
 
64
- if (!id) {
65
- uiLog.warn(`GET /discover - BLE device at index ${index} has no id, skipping`)
66
- continue
101
+ deviceMap.set(id, {
102
+ id,
103
+ name: d?.name || (typeof d?.getName === 'function' ? d.getName() : undefined) || info?.name || d?.deviceName || id,
104
+ type: d?.deviceType || (typeof d?.getDeviceType === 'function' ? d.getDeviceType() : undefined) || info?.deviceType || d?.type || d?.model || 'unknown',
105
+ model: info?.model || d?.model || d?.deviceModel,
106
+ address: mac,
107
+ connectionType: isHybrid ? 'Both' : 'BLE',
108
+ rssi: info?.rssi || d?.rssi,
109
+ })
67
110
  }
68
-
69
- const connectionTypes = Array.isArray(info?.connectionTypes) ? info.connectionTypes : []
70
- const isHybrid = connectionTypes.includes('api')
71
-
72
- deviceMap.set(id, {
73
- id,
74
- name: d?.name || (typeof d?.getName === 'function' ? d.getName() : undefined) || info?.name || d?.deviceName || id,
75
- type: d?.deviceType || (typeof d?.getDeviceType === 'function' ? d.getDeviceType() : undefined) || info?.deviceType || d?.type || d?.model || 'unknown',
76
- model: info?.model || d?.model || d?.deviceModel,
77
- address: mac,
78
- connectionType: isHybrid ? 'Both' : 'BLE',
79
- rssi: info?.rssi || d?.rssi,
80
- })
111
+ } else {
112
+ uiLog.info('GET /discover - No BLE devices found or scan timed out')
81
113
  }
82
- } else {
83
- uiLog.info('GET /discover - No BLE devices found or scan timed out')
114
+ } catch (bleErr) {
115
+ uiLog.warn(`GET /discover - BLE discovery failed: ${bleErr instanceof Error ? bleErr.message : String(bleErr)}`)
116
+ // Continue with OpenAPI even if BLE fails
84
117
  }
85
- } catch (bleErr) {
86
- uiLog.warn(`GET /discover - BLE discovery failed: ${bleErr instanceof Error ? bleErr.message : String(bleErr)}`)
87
- // Continue with OpenAPI even if BLE fails
88
118
  }
89
119
 
90
120
  // 2. Get devices from OpenAPI (only if credentials are available)
91
- if (hasOpenAPICredentials) {
121
+ if (runOpenApi && hasOpenAPICredentials) {
92
122
  uiLog.info('GET /discover - Fetching devices from OpenAPI...')
93
123
  try {
94
124
  const apiClient = switchbot.getAPIClient()
@@ -157,7 +187,7 @@ export function registerDiscoveryEndpoint(server: HomebridgePluginUiServer) {
157
187
  throw apiErr
158
188
  }
159
189
  }
160
- } else {
190
+ } else if (runOpenApi) {
161
191
  uiLog.info('GET /discover - Skipping OpenAPI discovery (no credentials configured)')
162
192
  }
163
193
 
@@ -166,6 +196,10 @@ export function registerDiscoveryEndpoint(server: HomebridgePluginUiServer) {
166
196
  uiLog.info(`GET /discover - Normalized BLE devices: ${normalizedBleDevices.length} (firstId: ${firstNormalizedBleId})`)
167
197
 
168
198
  // Check if we found any devices
199
+ if (deviceMap.size === 0 && mode === 'openapi' && !hasOpenAPICredentials) {
200
+ return { success: true, data: [] }
201
+ }
202
+
169
203
  if (deviceMap.size === 0) {
170
204
  const errorMsg = hasOpenAPICredentials
171
205
  ? 'No devices found via BLE or OpenAPI. Make sure devices are powered on and in range.'
@@ -310,6 +310,92 @@ select:focus {
310
310
  gap: 6px;
311
311
  }
312
312
 
313
+ .discover-phase-progress {
314
+ margin-bottom: 8px;
315
+ }
316
+
317
+ .discovery-settings-row {
318
+ display: flex;
319
+ flex-wrap: wrap;
320
+ gap: 8px;
321
+ align-items: center;
322
+ }
323
+
324
+ .discover-phase-track {
325
+ width: 100%;
326
+ height: 5px;
327
+ border-radius: 999px;
328
+ background: rgba(148, 163, 184, 0.35);
329
+ overflow: hidden;
330
+ }
331
+
332
+ .discover-phase-fill {
333
+ width: 0%;
334
+ height: 100%;
335
+ border-radius: 999px;
336
+ background: var(--switchbot-red, #ef4444);
337
+ transition: width 0.25s ease;
338
+ }
339
+
340
+ .discover-phase-label {
341
+ margin-top: 3px;
342
+ font-size: 10px;
343
+ opacity: 0.75;
344
+ }
345
+
346
+ .discovery-groups {
347
+ display: block;
348
+ max-height: 400px;
349
+ overflow-y: auto;
350
+ margin-top: 10px;
351
+ }
352
+
353
+ .discovery-group {
354
+ margin-top: 8px;
355
+ }
356
+
357
+ .discovery-group:first-child {
358
+ margin-top: 0;
359
+ }
360
+
361
+ .discovery-group-header {
362
+ margin: 0 0 4px;
363
+ font-size: 11px;
364
+ font-weight: 600;
365
+ opacity: 0.85;
366
+ }
367
+
368
+ .discovery-group-header-btn {
369
+ width: 100%;
370
+ text-align: left;
371
+ margin: 0 0 4px;
372
+ padding: 2px 0;
373
+ border: 0;
374
+ background: transparent;
375
+ color: inherit;
376
+ font-size: 11px;
377
+ font-weight: 600;
378
+ opacity: 0.9;
379
+ cursor: pointer;
380
+ border-radius: 0;
381
+ box-shadow: none;
382
+ transform: none;
383
+ }
384
+
385
+ .discovery-group-header-btn:hover,
386
+ .discovery-group-header-btn:active {
387
+ background: transparent;
388
+ color: inherit;
389
+ box-shadow: none;
390
+ transform: none;
391
+ }
392
+
393
+ .discovery-group-empty {
394
+ font-size: 11px;
395
+ opacity: 0.75;
396
+ padding: 4px 0;
397
+ }
398
+
313
399
  #devices.device-grid,
314
400
  #discoveredDevices.device-grid,
315
401
  ul.device-grid {
@@ -347,3 +433,15 @@ ul.device-grid {
347
433
  .card:hover {
348
434
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
349
435
  }
436
+ @keyframes pulse {
437
+ 0%, 100% {
438
+ opacity: 1;
439
+ }
440
+ 50% {
441
+ opacity: 0.5;
442
+ }
443
+ }
444
+
445
+ .discovery-scanning-pulse {
446
+ animation: pulse 1.5s ease-in-out infinite;
447
+ }
@@ -42,10 +42,66 @@
42
42
  Adds all discovered devices automatically; already configured devices are skipped.
43
43
  </div>
44
44
  </div>
45
+ <div class="discovery-settings-row" style="margin-bottom: 2px">
46
+ <label id="bleScanSetting" style="display: inline-flex; align-items: center; gap: 4px; font-size: 11px">
47
+ <span>Scan:</span>
48
+ <select id="bleScanDurationSelect" style="font-size: 11px; padding: 2px 6px">
49
+ <option value="3">3s</option>
50
+ <option value="5" selected>5s</option>
51
+ <option value="10">10s</option>
52
+ <option value="15">15s</option>
53
+ </select>
54
+ </label>
55
+ <label id="bleTimeoutSetting" style="display: inline-flex; align-items: center; gap: 4px; font-size: 11px">
56
+ <span>Timeout:</span>
57
+ <input
58
+ id="bleTimeoutInput"
59
+ type="number"
60
+ min="3"
61
+ max="30"
62
+ step="1"
63
+ value="8"
64
+ style="width: 56px; font-size: 11px; padding: 2px 4px"
65
+ />
66
+ <span>s</span>
67
+ </label>
68
+ <label style="display: flex; align-items: center; gap: 4px; cursor: pointer; font-size: 11px">
69
+ <input type="checkbox" id="disableBleScanCheckbox" style="width: auto; margin: 0" />
70
+ <span>Disable BLE scan</span>
71
+ </label>
72
+ </div>
73
+ <div id="bluetoothStatus" style="margin-bottom: 4px; font-size: 10px; opacity: 0.8">Bluetooth: checking...</div>
74
+ <div class="discovery-settings-row" style="margin-bottom: 8px">
75
+ <div id="lastScannedStatus" style="font-size: 10px; opacity: 0.8; flex: 1 1 auto">Last scanned: never</div>
76
+ <label style="font-size: 11px">
77
+ Auto-refresh:
78
+ <select id="autoRefreshIntervalSelect" style="margin-left: 4px; font-size: 11px; padding: 2px 6px">
79
+ <option value="0" selected>Off</option>
80
+ <option value="30">30s</option>
81
+ <option value="60">1m</option>
82
+ <option value="300">5m</option>
83
+ </select>
84
+ </label>
85
+ <button id="refreshDiscoverBtn" class="secondary" style="padding: 4px 9px; font-size: 11px">Refresh</button>
86
+ </div>
45
87
  <button id="discoverBtn" onclick="discoverDevices()" style="margin-bottom: 8px">
46
88
  🔍 Discover
47
89
  </button>
90
+ <button
91
+ id="cancelDiscoverBtn"
92
+ style="margin-left: 6px; margin-bottom: 8px; display: none; padding: 6px 10px; font-size: 11px"
93
+ class="secondary"
94
+ >
95
+ Cancel
96
+ </button>
48
97
  <div id="discoverStatus" style="margin-bottom: 6px; font-size: 11px"></div>
98
+ <div id="discoverDevicesFound" style="margin-bottom: 6px; font-size: 11px; display: none"></div>
99
+ <div id="discoverPhaseProgress" class="discover-phase-progress" style="display: none">
100
+ <div class="discover-phase-track">
101
+ <div id="discoverPhaseFill" class="discover-phase-fill"></div>
102
+ </div>
103
+ <div id="discoverPhaseLabel" class="discover-phase-label"></div>
104
+ </div>
49
105
  <div id="discoveredList" style="display: none">
50
106
  <h3 style="margin-top: 0; font-size: 12px; margin-bottom: 6px; font-weight: 600">Available Devices</h3>
51
107
  <ul
@@ -59,6 +115,7 @@
59
115
  <h2>Configured Devices</h2>
60
116
  <div id="status">Loading…</div>
61
117
  <ul id="devices"></ul>
118
+ <!-- BLE encryption key fields will be rendered dynamically per device in the UI (see app.ts/devices.js) -->
62
119
  <div
63
120
  id="removeAllContainer"
64
121
  style="
@@ -100,9 +100,18 @@ export async function fetchDevices(): Promise<any[]> {
100
100
  }
101
101
  }
102
102
 
103
- export async function discoverDevices(): Promise<any[]> {
103
+ export type DiscoverRequestOptions = {
104
+ bleEnabled?: boolean
105
+ bleScanDurationSeconds?: number
106
+ bleTimeoutSeconds?: number
107
+ }
108
+
109
+ export async function discoverDevices(
110
+ mode: 'all' | 'ble' | 'openapi' = 'all',
111
+ options?: DiscoverRequestOptions,
112
+ ): Promise<any[]> {
104
113
  try {
105
- const resp = await homebridge.request('/discover', {})
114
+ const resp = await homebridge.request('/discover', { mode, ...options })
106
115
  uiLog.info('Discover response:', resp)
107
116
 
108
117
  if (!resp || resp.success === false) {
@@ -115,17 +124,57 @@ export async function discoverDevices(): Promise<any[]> {
115
124
  }
116
125
  }
117
126
 
127
+ export async function fetchBluetoothStatus(): Promise<{ available: boolean, message: string }> {
128
+ try {
129
+ const resp = await homebridge.request('/ble-status', {})
130
+ if (!resp || resp.success === false) {
131
+ return { available: false, message: 'Bluetooth status unavailable' }
132
+ }
133
+ return resp.data || { available: false, message: 'Bluetooth status unavailable' }
134
+ } catch (_e) {
135
+ return { available: false, message: 'Bluetooth status unavailable' }
136
+ }
137
+ }
138
+
139
+ export async function testDeviceConnection(payload: {
140
+ deviceId: string
141
+ connectionType?: string
142
+ address?: string
143
+ }): Promise<{
144
+ success: boolean
145
+ deviceId: string
146
+ method: string
147
+ latencyMs: number
148
+ message: string
149
+ state?: Record<string, any>
150
+ }> {
151
+ const resp = await homebridge.request('/test-connection', payload)
152
+ if (!resp || resp.success === false) {
153
+ throw new Error(resp?.data?.message || 'Connection test failed')
154
+ }
155
+
156
+ return resp.data || {
157
+ success: false,
158
+ deviceId: payload.deviceId,
159
+ method: 'Auto',
160
+ latencyMs: 0,
161
+ message: 'Connection test failed',
162
+ }
163
+ }
164
+
118
165
  export async function addDevice(
119
166
  deviceId: string,
120
167
  name: string,
121
168
  type: string,
122
- options?: { address?: string, model?: string, rssi?: number },
169
+ options?: { address?: string, model?: string, rssi?: number, encryptionKey?: string, keyId?: string },
123
170
  ): Promise<any> {
124
171
  try {
125
172
  const payload: any = { deviceId, name, type }
126
173
  if (options?.address) { payload.address = options.address }
127
174
  if (options?.model) { payload.model = options.model }
128
175
  if (options?.rssi !== undefined && options?.rssi !== null && options?.rssi !== 0) { payload.rssi = options.rssi }
176
+ if (options?.encryptionKey) { payload.encryptionKey = options.encryptionKey }
177
+ if (options?.keyId) { payload.keyId = options.keyId }
129
178
 
130
179
  uiLog.info('Adding device to config:', payload)
131
180
  const resp = await homebridge.request('/add-device', payload)
@@ -1,7 +1,7 @@
1
1
  import { loadCredentialStatus, saveCredentials } from './credentials.js'
2
2
  import { loadConfiguredDevices, initRemoveAllButton } from './devices.js'
3
3
  // Wire up global event handlers
4
- import { discoverDevices } from './discovery.js';
4
+ import { discoverDevices, initializeDiscoverySettings } from './discovery.js';
5
5
 
6
6
  (window as any).loadCredentialStatus = loadCredentialStatus
7
7
  ;(window as any).saveCredentials = saveCredentials
@@ -10,6 +10,7 @@ import { discoverDevices } from './discovery.js';
10
10
  // Initialize on page load
11
11
  async function init(): Promise<void> {
12
12
  await loadCredentialStatus()
13
+ await initializeDiscoverySettings()
13
14
  const list = await loadConfiguredDevices()
14
15
  await initRemoveAllButton()
15
16
  }
@@ -1,15 +1,25 @@
1
+
1
2
  import { addDevice, fetchDevices, syncParentPluginConfigFromDisk } from './api.js'
2
3
  import { uiLog } from './logger.js'
3
4
  import { hideBusyUi, showBusyUi } from './modal.js'
4
5
  import { renderDeviceList } from './render.js'
5
6
  import { toastError, toastInfo, toastSuccess, toastWarning } from './toast.js'
6
7
 
8
+ type ImportDiscoveredDeviceResult = {
9
+ configDeviceName: string
10
+ configDeviceType: string
11
+ address?: string
12
+ encryptionKey?: string
13
+ keyId?: string
14
+ } | null
15
+
7
16
  export async function addDeviceToConfig(device: any, options: { refresh?: boolean, showStatus?: boolean } = {}): Promise<{ added: boolean }> {
8
17
  const { refresh = true, showStatus = true } = options
9
18
 
10
19
  try {
11
20
  const { importDiscoveredDevice } = await import('./modals.js')
12
- const importValues = await importDiscoveredDevice(device)
21
+
22
+ const importValues: ImportDiscoveredDeviceResult = await importDiscoveredDevice(device)
13
23
  if (!importValues) {
14
24
  return { added: false }
15
25
  }
@@ -20,6 +30,8 @@ export async function addDeviceToConfig(device: any, options: { refresh?: boolea
20
30
  address: importValues.address,
21
31
  model: device.model,
22
32
  rssi: device.rssi,
33
+ encryptionKey: importValues.encryptionKey,
34
+ keyId: importValues.keyId,
23
35
  })
24
36
  uiLog.info('Add device response:', resp)
25
37