@switchbot/homebridge-switchbot 5.0.0-beta.147 → 5.0.0-beta.149

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 (94) hide show
  1. package/.github/workflows/ci.yml +1 -1
  2. package/.github/workflows/manual-e2e.yml +3 -3
  3. package/config.schema.json +6 -0
  4. package/dist/SwitchBotHAPPlatform.d.ts +133 -0
  5. package/dist/SwitchBotHAPPlatform.d.ts.map +1 -0
  6. package/dist/SwitchBotHAPPlatform.js +555 -0
  7. package/dist/SwitchBotHAPPlatform.js.map +1 -0
  8. package/dist/SwitchBotMatterPlatform.d.ts +141 -0
  9. package/dist/SwitchBotMatterPlatform.d.ts.map +1 -0
  10. package/dist/SwitchBotMatterPlatform.js +536 -0
  11. package/dist/SwitchBotMatterPlatform.js.map +1 -0
  12. package/dist/deviceFactory.d.ts +2 -3
  13. package/dist/deviceFactory.d.ts.map +1 -1
  14. package/dist/deviceFactory.js +12 -3
  15. package/dist/deviceFactory.js.map +1 -1
  16. package/dist/homebridge-ui/endpoints/config.d.ts.map +1 -1
  17. package/dist/homebridge-ui/endpoints/config.js +1 -18
  18. package/dist/homebridge-ui/endpoints/config.js.map +1 -1
  19. package/dist/homebridge-ui/endpoints/devices.d.ts.map +1 -1
  20. package/dist/homebridge-ui/endpoints/devices.js +1 -350
  21. package/dist/homebridge-ui/endpoints/devices.js.map +1 -1
  22. package/dist/homebridge-ui/public/index.html +6 -0
  23. package/dist/homebridge-ui/public/js/advanced-settings.d.ts.map +1 -1
  24. package/dist/homebridge-ui/public/js/advanced-settings.js +22 -12
  25. package/dist/homebridge-ui/public/js/advanced-settings.js.map +1 -1
  26. package/dist/homebridge-ui/public/js/advanced-settings.ts +36 -25
  27. package/dist/homebridge-ui/public/js/api.d.ts +1 -1
  28. package/dist/homebridge-ui/public/js/api.d.ts.map +1 -1
  29. package/dist/homebridge-ui/public/js/api.js +111 -52
  30. package/dist/homebridge-ui/public/js/api.js.map +1 -1
  31. package/dist/homebridge-ui/public/js/api.ts +112 -53
  32. package/dist/homebridge-ui/public/js/app.js +234 -177
  33. package/dist/homebridge-ui/public/js/app.js.map +4 -4
  34. package/dist/homebridge-ui/public/js/credentials.d.ts.map +1 -1
  35. package/dist/homebridge-ui/public/js/credentials.js +26 -23
  36. package/dist/homebridge-ui/public/js/credentials.js.map +1 -1
  37. package/dist/homebridge-ui/public/js/credentials.ts +26 -25
  38. package/dist/homebridge-ui/public/js/devices-delete.d.ts.map +1 -1
  39. package/dist/homebridge-ui/public/js/devices-delete.js +3 -10
  40. package/dist/homebridge-ui/public/js/devices-delete.js.map +1 -1
  41. package/dist/homebridge-ui/public/js/devices-delete.ts +3 -12
  42. package/dist/homebridge-ui/public/js/devices.d.ts.map +1 -1
  43. package/dist/homebridge-ui/public/js/devices.js +0 -3
  44. package/dist/homebridge-ui/public/js/devices.js.map +1 -1
  45. package/dist/homebridge-ui/public/js/devices.ts +5 -9
  46. package/dist/homebridge-ui/server.js +0 -2
  47. package/dist/homebridge-ui/server.js.map +1 -1
  48. package/dist/index.d.ts +10 -0
  49. package/dist/index.d.ts.map +1 -1
  50. package/dist/index.js +12 -2
  51. package/dist/index.js.map +1 -1
  52. package/dist/settings.d.ts +1 -0
  53. package/dist/settings.d.ts.map +1 -1
  54. package/dist/settings.js +1 -0
  55. package/dist/settings.js.map +1 -1
  56. package/dist/switchbotClient.js +1 -1
  57. package/dist/switchbotClient.js.map +1 -1
  58. package/dist/utils.d.ts +76 -1
  59. package/dist/utils.d.ts.map +1 -1
  60. package/dist/utils.js +1117 -2
  61. package/dist/utils.js.map +1 -1
  62. package/docs/variables/default.html +3 -1
  63. package/package.json +1 -1
  64. package/src/SwitchBotHAPPlatform.ts +558 -0
  65. package/src/SwitchBotMatterPlatform.ts +538 -0
  66. package/src/deviceFactory.ts +14 -6
  67. package/src/homebridge-ui/endpoints/config.ts +2 -18
  68. package/src/homebridge-ui/endpoints/devices.ts +1 -392
  69. package/src/homebridge-ui/public/index.html +6 -0
  70. package/src/homebridge-ui/public/js/advanced-settings.ts +36 -25
  71. package/src/homebridge-ui/public/js/api.ts +112 -53
  72. package/src/homebridge-ui/public/js/credentials.ts +26 -25
  73. package/src/homebridge-ui/public/js/devices-delete.ts +3 -12
  74. package/src/homebridge-ui/public/js/devices.ts +5 -9
  75. package/src/homebridge-ui/server.ts +0 -2
  76. package/src/index.ts +12 -2
  77. package/src/settings.ts +2 -0
  78. package/src/switchbotClient.ts +1 -1
  79. package/src/utils.ts +1128 -3
  80. package/test/platform/accessory-restore.spec.ts +2 -1
  81. package/test/platform/matter-childbridge.spec.ts +1 -1
  82. package/test/platform/matter-integration.spec.ts +1 -1
  83. package/test/platform/platform-edge.spec.ts +2 -1
  84. package/test/platform/platform.integration.spec.ts +1 -1
  85. package/dist/homebridge-ui/endpoints/credentials.d.ts +0 -6
  86. package/dist/homebridge-ui/endpoints/credentials.d.ts.map +0 -1
  87. package/dist/homebridge-ui/endpoints/credentials.js +0 -65
  88. package/dist/homebridge-ui/endpoints/credentials.js.map +0 -1
  89. package/dist/platform.d.ts +0 -48
  90. package/dist/platform.d.ts.map +0 -1
  91. package/dist/platform.js +0 -1733
  92. package/dist/platform.js.map +0 -1
  93. package/src/homebridge-ui/endpoints/credentials.ts +0 -74
  94. package/src/platform.ts +0 -1750
@@ -1,7 +1,26 @@
1
+ // Fetch the list of configured devices from the Homebridge UI API
1
2
  import { isValidDeviceType, normalizeDeviceType } from '../../../device-types.js'
2
3
  import './types.js'
3
4
  import { uiLog } from './logger.js'
4
5
 
6
+ export async function fetchDevices(): Promise<any[]> {
7
+ try {
8
+ if (typeof homebridge.getPluginConfig !== 'function') {
9
+ throw new TypeError('Homebridge UI API not available')
10
+ }
11
+ const configArr = await homebridge.getPluginConfig()
12
+ const config = Array.isArray(configArr) && configArr.length > 0 ? configArr.find(isSwitchBotPlatformConfig) : null
13
+ if (!config || !Array.isArray(config.devices)) {
14
+ return []
15
+ }
16
+ return config.devices
17
+ } catch (e) {
18
+ const msg = e instanceof Error ? e.message : String(e)
19
+ uiLog.error('Error fetching devices:', msg)
20
+ return []
21
+ }
22
+ }
23
+
5
24
  /**
6
25
  * Validate and auto-correct device types in the config array before saving.
7
26
  * Returns an array of errors for devices that cannot be fixed.
@@ -114,24 +133,6 @@ export async function saveCredentials(token: string, secret: string): Promise<an
114
133
  return resp.data || resp
115
134
  }
116
135
 
117
- export async function fetchDevices(): Promise<any[]> {
118
- try {
119
- const resp = await homebridge.request('/devices', {})
120
- if (!resp || resp.success === false) {
121
- // Prefer backend error message if available
122
- const backendMsg = resp?.data?.message || resp?.message
123
- throw new Error(backendMsg || 'request failed')
124
- }
125
- return resp.data || []
126
- } catch (e) {
127
- // Show the real error to the user
128
- const msg = e instanceof Error ? e.message : String(e)
129
- uiLog.error('Error fetching devices:', msg)
130
- // Optionally, show a toast or UI error here if needed
131
- return []
132
- }
133
- }
134
-
135
136
  export interface DiscoverRequestOptions {
136
137
  bleEnabled?: boolean
137
138
  bleScanDurationSeconds?: number
@@ -194,29 +195,45 @@ export async function addDevice(
194
195
  type: string,
195
196
  options?: { address?: string, model?: string, rssi?: number, encryptionKey?: string, keyId?: string },
196
197
  ): Promise<any> {
197
- const payload: any = { deviceId, name, type }
198
+ if (typeof homebridge.getPluginConfig !== 'function' || typeof homebridge.updatePluginConfig !== 'function') {
199
+ throw new TypeError('Homebridge UI API not available')
200
+ }
201
+ const configArr = await homebridge.getPluginConfig()
202
+ const idx = Array.isArray(configArr) ? configArr.findIndex(isSwitchBotPlatformConfig) : -1
203
+ if (idx === -1) {
204
+ throw new Error('SwitchBot config not found')
205
+ }
206
+ const config = configArr[idx]
207
+ if (!Array.isArray(config.devices)) {
208
+ config.devices = []
209
+ }
210
+ const normalizedDeviceId = String(deviceId).trim().toLowerCase()
211
+ const exists = config.devices.some((d: any) => String(d.deviceId ?? d.id ?? '').trim().toLowerCase() === normalizedDeviceId)
212
+ if (exists) {
213
+ return { alreadyExists: true, message: 'Device already in config' }
214
+ }
215
+ const newDevice: any = { deviceId, configDeviceName: name, configDeviceType: type }
198
216
  if (options?.address) {
199
- payload.address = options.address
217
+ newDevice.address = options.address
200
218
  }
201
219
  if (options?.model) {
202
- payload.model = options.model
220
+ newDevice.model = options.model
203
221
  }
204
222
  if (options?.rssi !== undefined && options?.rssi !== null && options?.rssi !== 0) {
205
- payload.rssi = options.rssi
223
+ newDevice.rssi = options.rssi
206
224
  }
207
225
  if (options?.encryptionKey) {
208
- payload.encryptionKey = options.encryptionKey
226
+ newDevice.encryptionKey = options.encryptionKey
209
227
  }
210
228
  if (options?.keyId) {
211
- payload.keyId = options.keyId
229
+ newDevice.keyId = options.keyId
212
230
  }
213
- uiLog.info('Adding device to config:', payload)
214
- const resp = await homebridge.request('/add-device', payload)
215
- uiLog.info('Add device response:', resp)
216
- if (!resp || resp.success === false) {
217
- throw new Error(resp?.data?.message || 'Failed to add device')
231
+ config.devices.push(newDevice)
232
+ await homebridge.updatePluginConfig(configArr)
233
+ if (typeof homebridge.savePluginConfig === 'function') {
234
+ await homebridge.savePluginConfig()
218
235
  }
219
- return resp.data || resp
236
+ return { added: true, message: `Device "${name}" added successfully` }
220
237
  }
221
238
 
222
239
  export async function addDevicesInBulk(
@@ -241,43 +258,85 @@ export async function updateDevice(
241
258
  keyId?: string
242
259
  room?: string
243
260
  [key: string]: any
244
- }
261
+ },
245
262
  ): Promise<any> {
246
- const params: any = { deviceId }
263
+ if (typeof homebridge.getPluginConfig !== 'function' || typeof homebridge.updatePluginConfig !== 'function') {
264
+ throw new TypeError('Homebridge UI API not available')
265
+ }
266
+ const configArr = await homebridge.getPluginConfig()
267
+ const idx = Array.isArray(configArr) ? configArr.findIndex(isSwitchBotPlatformConfig) : -1
268
+ if (idx === -1) {
269
+ throw new Error('SwitchBot config not found')
270
+ }
271
+ const config = configArr[idx]
272
+ if (!Array.isArray(config.devices)) {
273
+ throw new TypeError('No devices array in config')
274
+ }
275
+ const normalizedDeviceId = String(deviceId).trim().toLowerCase()
276
+ const device = config.devices.find((d: any) => String(d.deviceId ?? d.id ?? '').trim().toLowerCase() === normalizedDeviceId)
277
+ if (!device) {
278
+ throw new Error('Device not found in config')
279
+ }
247
280
  if (configDeviceName) {
248
- params.configDeviceName = configDeviceName
281
+ device.configDeviceName = configDeviceName
249
282
  }
250
283
  if (configDeviceType) {
251
- params.configDeviceType = configDeviceType
284
+ device.configDeviceType = configDeviceType
252
285
  }
253
286
  if (options) {
254
- Object.assign(params, options)
287
+ Object.assign(device, options)
255
288
  }
256
- uiLog.info('[Update Device] Sending update request with params:', params)
257
- const resp = await homebridge.request('/update-device', params)
258
- uiLog.info('[Update Device] Update response:', resp)
259
- if (!resp || resp.success === false) {
260
- throw new Error(resp?.data?.message || 'Failed to update device')
289
+ await homebridge.updatePluginConfig(configArr)
290
+ if (typeof homebridge.savePluginConfig === 'function') {
291
+ await homebridge.savePluginConfig()
261
292
  }
262
- return resp.data || resp
293
+ return { updated: true, message: `Device updated successfully` }
263
294
  }
264
295
 
265
296
  export async function deleteDevice(deviceId: string): Promise<any> {
266
- uiLog.info('Sending delete request for deviceId:', deviceId)
267
- const resp = await homebridge.request('/delete-device', { deviceId })
268
- uiLog.info('Delete response:', resp)
269
- if (!resp || resp.success === false) {
270
- throw new Error(resp?.data?.message || 'Failed to delete device')
297
+ if (typeof homebridge.getPluginConfig !== 'function' || typeof homebridge.updatePluginConfig !== 'function') {
298
+ throw new TypeError('Homebridge UI API not available')
271
299
  }
272
- return resp.data || resp
300
+ const configArr = await homebridge.getPluginConfig()
301
+ const idx = Array.isArray(configArr) ? configArr.findIndex(isSwitchBotPlatformConfig) : -1
302
+ if (idx === -1) {
303
+ throw new Error('SwitchBot config not found')
304
+ }
305
+ const config = configArr[idx]
306
+ if (!Array.isArray(config.devices)) {
307
+ throw new TypeError('No devices array in config')
308
+ }
309
+ const normalizedDeviceId = String(deviceId).trim().toLowerCase()
310
+ const before = config.devices.length
311
+ config.devices = config.devices.filter((d: any) => String(d.deviceId ?? d.id ?? '').trim().toLowerCase() !== normalizedDeviceId)
312
+ if (config.devices.length === before) {
313
+ throw new Error('Device not found in config')
314
+ }
315
+ await homebridge.updatePluginConfig(configArr)
316
+ if (typeof homebridge.savePluginConfig === 'function') {
317
+ await homebridge.savePluginConfig()
318
+ }
319
+ return { deleted: true, message: `Device removed from config` }
273
320
  }
274
321
 
275
322
  export async function deleteAllDevices(): Promise<any> {
276
- uiLog.info('Sending delete all devices request')
277
- const resp = await homebridge.request('/delete-all-devices', {})
278
- uiLog.info('Delete all response:', resp)
279
- if (!resp || resp.success === false) {
280
- throw new Error(resp?.data?.message || 'Failed to delete all devices')
323
+ if (typeof homebridge.getPluginConfig !== 'function' || typeof homebridge.updatePluginConfig !== 'function') {
324
+ throw new TypeError('Homebridge UI API not available')
281
325
  }
282
- return resp.data || resp
326
+ const configArr = await homebridge.getPluginConfig()
327
+ const idx = Array.isArray(configArr) ? configArr.findIndex(isSwitchBotPlatformConfig) : -1
328
+ if (idx === -1) {
329
+ throw new Error('SwitchBot config not found')
330
+ }
331
+ const config = configArr[idx]
332
+ if (!Array.isArray(config.devices)) {
333
+ throw new TypeError('No devices array in config')
334
+ }
335
+ const deletedCount = config.devices.length
336
+ config.devices = []
337
+ await homebridge.updatePluginConfig(configArr)
338
+ if (typeof homebridge.savePluginConfig === 'function') {
339
+ await homebridge.savePluginConfig()
340
+ }
341
+ return { deleted: true, deletedCount, message: `Removed ${deletedCount} device(s) from config` }
283
342
  }