@switchbot/homebridge-switchbot 5.0.0-beta.134 → 5.0.0-beta.136

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 (93) hide show
  1. package/config.schema.json +0 -11
  2. package/dist/deviceFactory.d.ts +3 -1
  3. package/dist/deviceFactory.d.ts.map +1 -1
  4. package/dist/deviceFactory.js +3 -18
  5. package/dist/deviceFactory.js.map +1 -1
  6. package/dist/devices/genericDevice.d.ts +1 -1
  7. package/dist/devices/genericDevice.d.ts.map +1 -1
  8. package/dist/devices/genericDevice.js +79 -159
  9. package/dist/devices/genericDevice.js.map +1 -1
  10. package/dist/homebridge-ui/public/index.html +107 -124
  11. package/dist/homebridge-ui/public/js/api.d.ts +1 -4
  12. package/dist/homebridge-ui/public/js/api.d.ts.map +1 -1
  13. package/dist/homebridge-ui/public/js/api.js +1 -7
  14. package/dist/homebridge-ui/public/js/api.js.map +1 -1
  15. package/dist/homebridge-ui/public/js/api.ts +1 -8
  16. package/dist/homebridge-ui/public/js/app.js +201 -90
  17. package/dist/homebridge-ui/public/js/app.js.map +2 -2
  18. package/dist/homebridge-ui/public/js/app.ts +5 -6
  19. package/dist/homebridge-ui/public/js/credentials.d.ts.map +1 -1
  20. package/dist/homebridge-ui/public/js/credentials.js +1 -1
  21. package/dist/homebridge-ui/public/js/credentials.js.map +1 -1
  22. package/dist/homebridge-ui/public/js/credentials.ts +7 -3
  23. package/dist/homebridge-ui/public/js/devices-delete.js +1 -1
  24. package/dist/homebridge-ui/public/js/devices-delete.js.map +1 -1
  25. package/dist/homebridge-ui/public/js/devices-delete.ts +1 -1
  26. package/dist/homebridge-ui/public/js/devices.d.ts.map +1 -1
  27. package/dist/homebridge-ui/public/js/devices.js +0 -1
  28. package/dist/homebridge-ui/public/js/devices.js.map +1 -1
  29. package/dist/homebridge-ui/public/js/devices.ts +0 -2
  30. package/dist/homebridge-ui/public/js/discovery.d.ts.map +1 -1
  31. package/dist/homebridge-ui/public/js/discovery.js +6 -3
  32. package/dist/homebridge-ui/public/js/discovery.js.map +1 -1
  33. package/dist/homebridge-ui/public/js/discovery.ts +9 -3
  34. package/dist/homebridge-ui/public/js/logger.d.ts +1 -0
  35. package/dist/homebridge-ui/public/js/logger.d.ts.map +1 -1
  36. package/dist/homebridge-ui/public/js/logger.js +8 -3
  37. package/dist/homebridge-ui/public/js/logger.js.map +1 -1
  38. package/dist/homebridge-ui/public/js/logger.ts +12 -6
  39. package/dist/homebridge-ui/public/js/modals.d.ts +4 -0
  40. package/dist/homebridge-ui/public/js/modals.d.ts.map +1 -1
  41. package/dist/homebridge-ui/public/js/modals.js +152 -59
  42. package/dist/homebridge-ui/public/js/modals.js.map +1 -1
  43. package/dist/homebridge-ui/public/js/modals.ts +187 -60
  44. package/dist/homebridge-ui/public/js/render.d.ts +3 -0
  45. package/dist/homebridge-ui/public/js/render.d.ts.map +1 -1
  46. package/dist/homebridge-ui/public/js/render.js +63 -24
  47. package/dist/homebridge-ui/public/js/render.js.map +1 -1
  48. package/dist/homebridge-ui/public/js/render.ts +102 -51
  49. package/dist/homebridge-ui/utils/logger.js +1 -1
  50. package/dist/homebridge-ui/utils/logger.js.map +1 -1
  51. package/dist/platform.d.ts.map +1 -1
  52. package/dist/platform.js +40 -32
  53. package/dist/platform.js.map +1 -1
  54. package/dist/switchbotClient.d.ts +5 -5
  55. package/dist/switchbotClient.d.ts.map +1 -1
  56. package/dist/switchbotClient.js +21 -12
  57. package/dist/switchbotClient.js.map +1 -1
  58. package/docs/variables/default.html +1 -1
  59. package/package.json +1 -1
  60. package/scripts/build-ui.js +4 -3
  61. package/scripts/generate-matter-maps.js +34 -17
  62. package/scripts/sync-device-types.mjs +4 -1
  63. package/src/deviceFactory.ts +3 -18
  64. package/src/devices/genericDevice.ts +77 -164
  65. package/src/homebridge-ui/public/index.html +107 -124
  66. package/src/homebridge-ui/public/js/api.ts +1 -8
  67. package/src/homebridge-ui/public/js/app.ts +5 -6
  68. package/src/homebridge-ui/public/js/credentials.ts +7 -3
  69. package/src/homebridge-ui/public/js/devices-delete.ts +1 -1
  70. package/src/homebridge-ui/public/js/devices.ts +0 -2
  71. package/src/homebridge-ui/public/js/discovery.ts +9 -3
  72. package/src/homebridge-ui/public/js/logger.ts +12 -6
  73. package/src/homebridge-ui/public/js/modals.ts +187 -60
  74. package/src/homebridge-ui/public/js/render.ts +102 -51
  75. package/src/homebridge-ui/utils/logger.ts +1 -1
  76. package/src/platform.ts +40 -31
  77. package/src/switchbotClient.ts +42 -27
  78. package/test/accessory-restore.spec.ts +9 -8
  79. package/test/device-mapping.spec.ts +4 -3
  80. package/test/deviceFactory.spec.ts +4 -3
  81. package/test/e2e/run-e2e.spec.ts +6 -10
  82. package/test/fan-swing.spec.ts +7 -6
  83. package/test/helpers/matter-harness.ts +18 -7
  84. package/test/lock-users.spec.ts +5 -5
  85. package/test/matter-childbridge.spec.ts +10 -7
  86. package/test/matter-descriptors.spec.ts +8 -8
  87. package/test/matter-device-state.spec.ts +52 -36
  88. package/test/matter-integration.spec.ts +53 -6
  89. package/test/platform.integration.spec.ts +7 -5
  90. package/test/switchbot-client-debounce.spec.ts +36 -40
  91. package/test/switchbot-client-openapi.spec.ts +22 -15
  92. package/test/switchbotClient.spec.ts +4 -3
  93. package/test/utils.spec.ts +2 -1
@@ -15,7 +15,9 @@ export async function loadCredentialStatus(): Promise<void> {
15
15
  const tokenStatus = document.getElementById('tokenStatus')
16
16
  const secretStatus = document.getElementById('secretStatus')
17
17
 
18
- if (!tokenStatus || !secretStatus) { return }
18
+ if (!tokenStatus || !secretStatus) {
19
+ return
20
+ }
19
21
 
20
22
  if (creds.hasToken) {
21
23
  tokenStatus.textContent = `✓ Configured (${creds.tokenLength} characters)`
@@ -43,7 +45,9 @@ export async function saveCredentials(): Promise<void> {
43
45
  const saveStatus = document.getElementById('saveStatus')
44
46
  const saveBtn = document.getElementById('saveBtn') as HTMLButtonElement
45
47
 
46
- if (!saveStatus || !saveBtn) { return }
48
+ if (!saveStatus || !saveBtn) {
49
+ return
50
+ }
47
51
 
48
52
  if (!token || !secret) {
49
53
  saveStatus.textContent = 'Please enter both token and secret'
@@ -80,7 +84,7 @@ export async function saveCredentials(): Promise<void> {
80
84
  ;(document.getElementById('secret') as HTMLInputElement).value = ''
81
85
 
82
86
  // Reload status to verify save
83
- setTimeout(() => loadCredentialStatus(), 500)
87
+ setTimeout(loadCredentialStatus, 500)
84
88
 
85
89
  // Clear status message
86
90
  setTimeout(() => {
@@ -1,4 +1,4 @@
1
- import { deleteDevice as apiDeleteDevice, deleteAllDevices as apiDeleteAllDevices, syncParentPluginConfigFromDisk, fetchDevices } from './api.js'
1
+ import { deleteAllDevices as apiDeleteAllDevices, deleteDevice as apiDeleteDevice, fetchDevices, syncParentPluginConfigFromDisk } from './api.js'
2
2
  import { uiLog } from './logger.js'
3
3
  import { hideBusyUi, showBusyUi } from './modal.js'
4
4
  import { renderDeviceList } from './render.js'
@@ -10,7 +10,6 @@ type ImportDiscoveredDeviceResult = {
10
10
  address?: string
11
11
  encryptionKey?: string
12
12
  keyId?: string
13
- matterType?: number
14
13
  } | null
15
14
 
16
15
  export async function addDeviceToConfig(device: any, options: { refresh?: boolean, showStatus?: boolean } = {}): Promise<{ added: boolean }> {
@@ -32,7 +31,6 @@ export async function addDeviceToConfig(device: any, options: { refresh?: boolea
32
31
  rssi: device.rssi,
33
32
  encryptionKey: importValues.encryptionKey,
34
33
  keyId: importValues.keyId,
35
- matterType: importValues.matterType,
36
34
  })
37
35
  uiLog.info('Add device response:', resp)
38
36
 
@@ -1301,9 +1301,15 @@ async function updateDiscoveryView(
1301
1301
  const enableSelectedBtn = document.querySelector('button')?.parentElement?.querySelector('button[aria-label="Enable Selected"]') as HTMLButtonElement | null
1302
1302
  const disableSelectedBtn = document.querySelector('button')?.parentElement?.querySelector('button[aria-label="Disable Selected"]') as HTMLButtonElement | null
1303
1303
  const hasSelection = selectedIds.size > 0
1304
- if (addSelectedBtn) addSelectedBtn.disabled = !hasSelection
1305
- if (enableSelectedBtn) enableSelectedBtn.disabled = !hasSelection
1306
- if (disableSelectedBtn) disableSelectedBtn.disabled = !hasSelection
1304
+ if (addSelectedBtn) {
1305
+ addSelectedBtn.disabled = !hasSelection
1306
+ }
1307
+ if (enableSelectedBtn) {
1308
+ enableSelectedBtn.disabled = !hasSelection
1309
+ }
1310
+ if (disableSelectedBtn) {
1311
+ disableSelectedBtn.disabled = !hasSelection
1312
+ }
1307
1313
  }
1308
1314
  window.removeEventListener('discovery-selection-changed', updateBatchButtonStates)
1309
1315
  window.addEventListener('discovery-selection-changed', updateBatchButtonStates)
@@ -1,11 +1,17 @@
1
+ /* eslint-disable no-console */
2
+ const PREFIX = '[SwitchBot UI/html]'
3
+
1
4
  export const uiLog = {
2
- info: (message: string, ...parameters: any[]): void => {
3
- console.warn('[SwitchBot UI]', message, ...parameters)
5
+ info: (message: string, ...parameters: any[]) => {
6
+ console.log(PREFIX, message, ...parameters)
4
7
  },
5
- warn: (message: string, ...parameters: any[]): void => {
6
- console.warn('[SwitchBot UI]', message, ...parameters)
8
+ warn: (message: string, ...parameters: any[]) => {
9
+ console.warn(PREFIX, message, ...parameters)
7
10
  },
8
- error: (message: string, ...parameters: any[]): void => {
9
- console.error('[SwitchBot UI]', message, ...parameters)
11
+ error: (message: string, ...parameters: any[]) => {
12
+ console.error(PREFIX, message, ...parameters)
13
+ },
14
+ debug: (message: string, ...parameters: any[]) => {
15
+ console.debug(PREFIX, message, ...parameters)
10
16
  },
11
17
  }
@@ -1,7 +1,15 @@
1
1
  import { DEVICE_TYPES } from './constants.js'
2
2
  import { uiLog } from './logger.js'
3
3
 
4
- export async function importDiscoveredDevice(device: any): Promise<{ configDeviceName: string, configDeviceType: string, address?: string } | null> {
4
+ export async function importDiscoveredDevice(device: any): Promise<{
5
+ configDeviceName: string
6
+ configDeviceType: string
7
+ address?: string
8
+ connectionPreference?: string
9
+ room?: string
10
+ encryptionKey?: string
11
+ keyId?: string
12
+ } | null> {
5
13
  return new Promise((resolve) => {
6
14
  const div = document.createElement('div')
7
15
  div.style.position = 'fixed'
@@ -91,6 +99,51 @@ export async function importDiscoveredDevice(device: any): Promise<{ configDevic
91
99
  typeSelect.appendChild(optgroup)
92
100
  })
93
101
 
102
+ // --- Connection Preference ---
103
+ const connectionPrefLabel = document.createElement('label')
104
+ connectionPrefLabel.textContent = 'Connection Preference'
105
+ connectionPrefLabel.style.display = 'block'
106
+ connectionPrefLabel.style.marginBottom = '6px'
107
+ connectionPrefLabel.style.fontWeight = '500'
108
+ connectionPrefLabel.style.fontSize = '12px'
109
+ connectionPrefLabel.style.color = '#6b7280'
110
+
111
+ const connectionPrefSelect = document.createElement('select')
112
+ connectionPrefSelect.style.width = '100%'
113
+ connectionPrefSelect.style.marginBottom = '12px'
114
+ connectionPrefSelect.style.padding = '8px 10px'
115
+ connectionPrefSelect.style.borderRadius = '6px'
116
+ connectionPrefSelect.style.fontSize = '14px'
117
+ connectionPrefSelect.style.boxSizing = 'border-box';
118
+ ['auto', 'ble', 'openapi'].forEach((val) => {
119
+ const opt = document.createElement('option')
120
+ opt.value = val
121
+ opt.text = val.charAt(0).toUpperCase() + val.slice(1)
122
+ opt.selected = (device.connectionPreference || 'auto') === val
123
+ connectionPrefSelect.appendChild(opt)
124
+ })
125
+
126
+ // --- Room ---
127
+ const roomLabel = document.createElement('label')
128
+ roomLabel.textContent = 'Room'
129
+ roomLabel.style.display = 'block'
130
+ roomLabel.style.marginBottom = '6px'
131
+ roomLabel.style.fontWeight = '500'
132
+ roomLabel.style.fontSize = '12px'
133
+ roomLabel.style.color = '#6b7280'
134
+
135
+ const roomInput = document.createElement('input')
136
+ roomInput.type = 'text'
137
+ roomInput.value = device.room || ''
138
+ roomInput.placeholder = 'Optional room/location metadata'
139
+ roomInput.style.width = '100%'
140
+ roomInput.style.marginBottom = '12px'
141
+ roomInput.style.padding = '8px 10px'
142
+ roomInput.style.borderRadius = '6px'
143
+ roomInput.style.fontSize = '14px'
144
+ roomInput.style.boxSizing = 'border-box'
145
+
146
+ // --- BLE MAC Address ---
94
147
  const macLabel = document.createElement('label')
95
148
  macLabel.textContent = 'BLE MAC Address (optional)'
96
149
  macLabel.style.display = 'block'
@@ -110,7 +163,7 @@ export async function importDiscoveredDevice(device: any): Promise<{ configDevic
110
163
  macInput.style.fontSize = '14px'
111
164
  macInput.style.boxSizing = 'border-box'
112
165
 
113
- // BLE Encryption Key
166
+ // --- Encryption Key ---
114
167
  const encryptionKeyLabel = document.createElement('label')
115
168
  encryptionKeyLabel.textContent = 'BLE Encryption Key (optional)'
116
169
  encryptionKeyLabel.style.display = 'block'
@@ -130,7 +183,7 @@ export async function importDiscoveredDevice(device: any): Promise<{ configDevic
130
183
  encryptionKeyInput.style.fontSize = '14px'
131
184
  encryptionKeyInput.style.boxSizing = 'border-box'
132
185
 
133
- // BLE Key ID
186
+ // --- Key ID ---
134
187
  const keyIdLabel = document.createElement('label')
135
188
  keyIdLabel.textContent = 'BLE Key ID (optional)'
136
189
  keyIdLabel.style.display = 'block'
@@ -171,7 +224,15 @@ export async function importDiscoveredDevice(device: any): Promise<{ configDevic
171
224
  importBtn.style.padding = '8px 20px'
172
225
  importBtn.style.fontSize = '13px'
173
226
 
174
- const cleanup = (result: { configDeviceName: string, configDeviceType: string, address?: string, encryptionKey?: string, keyId?: string } | null) => {
227
+ const cleanup = (result: {
228
+ configDeviceName: string
229
+ configDeviceType: string
230
+ address?: string
231
+ connectionPreference?: string
232
+ room?: string
233
+ encryptionKey?: string
234
+ keyId?: string
235
+ } | null) => {
175
236
  div.remove()
176
237
  resolve(result)
177
238
  }
@@ -181,6 +242,8 @@ export async function importDiscoveredDevice(device: any): Promise<{ configDevic
181
242
  configDeviceName: nameInput.value || device.name || device.id,
182
243
  configDeviceType: typeSelect.value || device.type,
183
244
  address: macInput.value || undefined,
245
+ connectionPreference: connectionPrefSelect.value || undefined,
246
+ room: roomInput.value || undefined,
184
247
  encryptionKey: encryptionKeyInput.value || undefined,
185
248
  keyId: keyIdInput.value || undefined,
186
249
  })
@@ -197,6 +260,10 @@ export async function importDiscoveredDevice(device: any): Promise<{ configDevic
197
260
  contentDiv.appendChild(nameInput)
198
261
  contentDiv.appendChild(typeLabel)
199
262
  contentDiv.appendChild(typeSelect)
263
+ contentDiv.appendChild(connectionPrefLabel)
264
+ contentDiv.appendChild(connectionPrefSelect)
265
+ contentDiv.appendChild(roomLabel)
266
+ contentDiv.appendChild(roomInput)
200
267
  contentDiv.appendChild(macLabel)
201
268
  contentDiv.appendChild(macInput)
202
269
  contentDiv.appendChild(encryptionKeyLabel)
@@ -250,58 +317,6 @@ export async function editDevice(device: any): Promise<void> {
250
317
  typeSelect.appendChild(optgroup)
251
318
  })
252
319
 
253
- // --- Matter Type Advanced Field (only for window covering types) ---
254
- const windowCoveringTypes = ['Roller Shade', 'Curtain', 'Curtain3', 'Blind Tilt']
255
- const showMatterType = windowCoveringTypes.includes(device.configDeviceType || device.deviceType || device.type || '')
256
- const matterTypeLabel = document.createElement('label')
257
- matterTypeLabel.textContent = 'Matter Window Covering Type (Advanced)'
258
- matterTypeLabel.style.display = showMatterType ? 'block' : 'none'
259
- matterTypeLabel.style.marginBottom = '6px'
260
- matterTypeLabel.style.fontWeight = '500'
261
- matterTypeLabel.style.fontSize = '12px'
262
- matterTypeLabel.style.color = '#6b7280'
263
- matterTypeLabel.style.marginTop = '2px'
264
- const matterTypes = [
265
- { value: '', label: 'Auto-detect (recommended)' },
266
- { value: 0, label: 'Roller Shade (0)' },
267
- { value: 1, label: 'Roman Shade (1)' },
268
- { value: 2, label: 'Balloon Shade (2)' },
269
- { value: 3, label: 'Woven Wood (3)' },
270
- { value: 4, label: 'Drapery (4)' },
271
- { value: 5, label: 'Awning (5)' },
272
- { value: 6, label: 'Shutter (6)' },
273
- { value: 7, label: 'Tilt Blind (7)' },
274
- { value: 8, label: 'Blind Tilt (8)' },
275
- { value: 9, label: 'Projector Screen (9)' },
276
- { value: 15, label: 'Other (15)' },
277
- ]
278
- const matterTypeSelect = document.createElement('select')
279
- matterTypeSelect.style.width = '100%'
280
- matterTypeSelect.style.marginBottom = '12px'
281
- matterTypeSelect.style.padding = '8px 10px'
282
- matterTypeSelect.style.borderRadius = '6px'
283
- matterTypeSelect.style.fontSize = '14px'
284
- matterTypeSelect.style.boxSizing = 'border-box'
285
- matterTypes.forEach((opt) => {
286
- const option = document.createElement('option')
287
- option.value = String(opt.value)
288
- option.textContent = opt.label
289
- if (device.matterType != null && String(opt.value) === String(device.matterType)) {
290
- option.selected = true
291
- }
292
- matterTypeSelect.appendChild(option)
293
- })
294
- function updateMatterTypeVisibility() {
295
- if (windowCoveringTypes.includes(typeSelect.value)) {
296
- matterTypeLabel.style.display = 'block'
297
- matterTypeSelect.style.display = 'block'
298
- } else {
299
- matterTypeLabel.style.display = 'none'
300
- matterTypeSelect.style.display = 'none'
301
- }
302
- }
303
- typeSelect.addEventListener('change', updateMatterTypeVisibility)
304
- updateMatterTypeVisibility()
305
320
  // Create modal dialog for editing
306
321
  const div = document.createElement('div')
307
322
  div.style.position = 'fixed'
@@ -402,6 +417,90 @@ export async function editDevice(device: any): Promise<void> {
402
417
  typeSelect.appendChild(optgroup)
403
418
  })
404
419
 
420
+ // --- Connection Preference ---
421
+ const connectionPrefLabel = document.createElement('label')
422
+ connectionPrefLabel.textContent = 'Connection Preference'
423
+ connectionPrefLabel.style.display = 'block'
424
+ connectionPrefLabel.style.marginBottom = '6px'
425
+ connectionPrefLabel.style.fontWeight = '500'
426
+ connectionPrefLabel.style.fontSize = '12px'
427
+ connectionPrefLabel.style.color = '#6b7280'
428
+
429
+ const connectionPrefSelect = document.createElement('select')
430
+ connectionPrefSelect.style.width = '100%'
431
+ connectionPrefSelect.style.marginBottom = '12px'
432
+ connectionPrefSelect.style.padding = '8px 10px'
433
+ connectionPrefSelect.style.borderRadius = '6px'
434
+ connectionPrefSelect.style.fontSize = '14px'
435
+ connectionPrefSelect.style.boxSizing = 'border-box'
436
+ ;['auto', 'ble', 'openapi'].forEach((val) => {
437
+ const opt = document.createElement('option')
438
+ opt.value = val
439
+ opt.text = val.charAt(0).toUpperCase() + val.slice(1)
440
+ opt.selected = (device.connectionPreference || 'auto') === val
441
+ connectionPrefSelect.appendChild(opt)
442
+ })
443
+
444
+ // --- Room ---
445
+ const roomLabel = document.createElement('label')
446
+ roomLabel.textContent = 'Room'
447
+ roomLabel.style.display = 'block'
448
+ roomLabel.style.marginBottom = '6px'
449
+ roomLabel.style.fontWeight = '500'
450
+ roomLabel.style.fontSize = '12px'
451
+ roomLabel.style.color = '#6b7280'
452
+
453
+ const roomInput = document.createElement('input')
454
+ roomInput.type = 'text'
455
+ roomInput.value = device.room || ''
456
+ roomInput.placeholder = 'Optional room/location metadata'
457
+ roomInput.style.width = '100%'
458
+ roomInput.style.marginBottom = '12px'
459
+ roomInput.style.padding = '8px 10px'
460
+ roomInput.style.borderRadius = '6px'
461
+ roomInput.style.fontSize = '14px'
462
+ roomInput.style.boxSizing = 'border-box'
463
+
464
+ // --- Encryption Key ---
465
+ const encryptionKeyLabel = document.createElement('label')
466
+ encryptionKeyLabel.textContent = 'BLE Encryption Key (optional)'
467
+ encryptionKeyLabel.style.display = 'block'
468
+ encryptionKeyLabel.style.marginBottom = '6px'
469
+ encryptionKeyLabel.style.fontWeight = '500'
470
+ encryptionKeyLabel.style.fontSize = '12px'
471
+ encryptionKeyLabel.style.color = '#6b7280'
472
+
473
+ const encryptionKeyInput = document.createElement('input')
474
+ encryptionKeyInput.type = 'password'
475
+ encryptionKeyInput.value = device.encryptionKey || ''
476
+ encryptionKeyInput.placeholder = 'Paste device BLE encryption key'
477
+ encryptionKeyInput.style.width = '100%'
478
+ encryptionKeyInput.style.marginBottom = '12px'
479
+ encryptionKeyInput.style.padding = '8px 10px'
480
+ encryptionKeyInput.style.borderRadius = '6px'
481
+ encryptionKeyInput.style.fontSize = '14px'
482
+ encryptionKeyInput.style.boxSizing = 'border-box'
483
+
484
+ // --- Key ID ---
485
+ const keyIdLabel = document.createElement('label')
486
+ keyIdLabel.textContent = 'BLE Key ID (optional)'
487
+ keyIdLabel.style.display = 'block'
488
+ keyIdLabel.style.marginBottom = '6px'
489
+ keyIdLabel.style.fontWeight = '500'
490
+ keyIdLabel.style.fontSize = '12px'
491
+ keyIdLabel.style.color = '#6b7280'
492
+
493
+ const keyIdInput = document.createElement('input')
494
+ keyIdInput.type = 'text'
495
+ keyIdInput.value = device.keyId || ''
496
+ keyIdInput.placeholder = 'e.g. ff'
497
+ keyIdInput.style.width = '100%'
498
+ keyIdInput.style.marginBottom = '12px'
499
+ keyIdInput.style.padding = '8px 10px'
500
+ keyIdInput.style.borderRadius = '6px'
501
+ keyIdInput.style.fontSize = '14px'
502
+ keyIdInput.style.boxSizing = 'border-box'
503
+
405
504
  const errorMessage = document.createElement('div')
406
505
  errorMessage.style.color = 'var(--switchbot-red, #ef4444)'
407
506
  errorMessage.style.marginBottom = '12px'
@@ -442,10 +541,32 @@ export async function editDevice(device: any): Promise<void> {
442
541
  deviceId: device.id,
443
542
  configDeviceName: nameInput.value || undefined,
444
543
  configDeviceType: typeSelect.value,
445
- matterType: windowCoveringTypes.includes(typeSelect.value) && matterTypeSelect.value !== '' ? Number(matterTypeSelect.value) : undefined,
544
+ connectionPreference: connectionPrefSelect.value,
545
+ room: roomInput.value || undefined,
546
+ encryptionKey: encryptionKeyInput.value || undefined,
547
+ keyId: keyIdInput.value || undefined,
548
+ }
549
+
550
+ // Only include defined properties in the options object
551
+ const options: any = {}
552
+ if (params.connectionPreference !== undefined) {
553
+ options.connectionPreference = params.connectionPreference
554
+ }
555
+ if (params.room !== undefined) {
556
+ options.room = params.room
557
+ }
558
+ if (params.encryptionKey !== undefined) {
559
+ options.encryptionKey = params.encryptionKey
560
+ }
561
+ if (params.keyId !== undefined) {
562
+ options.keyId = params.keyId
446
563
  }
447
564
 
448
- await updateDevice(params.deviceId, params.configDeviceName, params.configDeviceType, { matterType: params.matterType })
565
+ await updateDevice(
566
+ params.deviceId,
567
+ params.configDeviceName,
568
+ params.configDeviceType,
569
+ )
449
570
  await syncParentPluginConfigFromDisk()
450
571
 
451
572
  // Refresh device list
@@ -469,8 +590,14 @@ export async function editDevice(device: any): Promise<void> {
469
590
  contentDiv.appendChild(apiTypeInput)
470
591
  contentDiv.appendChild(typeLabel)
471
592
  contentDiv.appendChild(typeSelect)
472
- contentDiv.appendChild(matterTypeLabel)
473
- contentDiv.appendChild(matterTypeSelect)
593
+ contentDiv.appendChild(connectionPrefLabel)
594
+ contentDiv.appendChild(connectionPrefSelect)
595
+ contentDiv.appendChild(roomLabel)
596
+ contentDiv.appendChild(roomInput)
597
+ contentDiv.appendChild(encryptionKeyLabel)
598
+ contentDiv.appendChild(encryptionKeyInput)
599
+ contentDiv.appendChild(keyIdLabel)
600
+ contentDiv.appendChild(keyIdInput)
474
601
  contentDiv.appendChild(errorMessage)
475
602
  contentDiv.appendChild(buttons)
476
603