@switchbot/homebridge-switchbot 5.0.0-beta.80 → 5.0.0-beta.82

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.
package/src/platform.ts CHANGED
@@ -1,8 +1,9 @@
1
+ import type { SwitchBotPluginConfig } from './settings.js'
1
2
  import type { API, Logger, PlatformConfig } from 'homebridge'
2
- import { SwitchBotClient } from './switchbotClient.js'
3
+
3
4
  import { createDevice } from './deviceFactory.js'
4
- import type { SwitchBotPluginConfig } from './settings.js'
5
- import { PLUGIN_NAME, PLATFORM_NAME } from './settings.js'
5
+ import { PLATFORM_NAME, PLUGIN_NAME } from './settings.js'
6
+ import { SwitchBotClient } from './switchbotClient.js'
6
7
 
7
8
  // Which device types should prefer Matter if available
8
9
  const DEVICE_MATTER_SUPPORTED: Record<string, boolean> = {
@@ -13,10 +14,13 @@ const DEVICE_MATTER_SUPPORTED: Record<string, boolean> = {
13
14
  lightstrip: true,
14
15
  motion: true,
15
16
  contact: true,
16
- vacuum: false,
17
+ vacuum: true,
17
18
  lock: true,
18
19
  humidifier: true,
19
20
  temperature: true,
21
+ wosweeper: true,
22
+ wosweepermini: true,
23
+ wosweeperminipro: true,
20
24
  }
21
25
 
22
26
  export class SwitchBotHAPPlatform {
@@ -26,6 +30,9 @@ export class SwitchBotHAPPlatform {
26
30
  devices: any[] = []
27
31
  // cached accessories restored by Homebridge
28
32
  accessories: Map<string, any>
33
+ // Track last loaded config to detect changes
34
+ private lastConfigHash: string = ''
35
+ private configReloadInterval: NodeJS.Timeout | null = null
29
36
 
30
37
  constructor(log: Logger, config: PlatformConfig, api?: API) {
31
38
  this.log = log
@@ -56,9 +63,37 @@ export class SwitchBotHAPPlatform {
56
63
  if (this.api && typeof (this.api as any).on === 'function') {
57
64
  ;(this.api as any).on('didFinishLaunching', () => {
58
65
  void this.loadDevices()
66
+ // Start periodic config reload to pick up UI changes
67
+ this.configReloadInterval = setInterval(() => {
68
+ void this.checkAndReloadDevices()
69
+ }, 10000) // Check every 10 seconds
59
70
  })
60
71
  } else {
61
72
  void this.loadDevices()
73
+ // Start periodic config reload to pick up UI changes
74
+ this.configReloadInterval = setInterval(() => {
75
+ void this.checkAndReloadDevices()
76
+ }, 10000) // Check every 10 seconds
77
+ }
78
+ }
79
+
80
+ private getConfigHash(): string {
81
+ // Create a simple hash of current device config to detect changes
82
+ const devices = (this.config as any)?.devices ?? []
83
+ return JSON.stringify(devices.map((d: any) => ({
84
+ id: d.deviceId ?? d.id,
85
+ type: d.configDeviceType ?? d.type,
86
+ name: d.configDeviceName ?? d.name,
87
+ })))
88
+ }
89
+
90
+ private async checkAndReloadDevices() {
91
+ const currentHash = this.getConfigHash()
92
+ if (currentHash !== this.lastConfigHash) {
93
+ this.log.info('[SwitchBot] Detected config changes, reloading devices...')
94
+ // Clear existing devices
95
+ this.devices = []
96
+ await this.loadDevices()
62
97
  }
63
98
  }
64
99
 
@@ -73,7 +108,11 @@ export class SwitchBotHAPPlatform {
73
108
  _raw: raw,
74
109
  }
75
110
 
76
- const type: string = d.type
111
+ let type: string = d.type
112
+ // Normalize wosweeper variants to vacuum
113
+ if (['wosweeper', 'wosweepermini', 'wosweeperminipro'].includes((type || '').toLowerCase())) {
114
+ type = 'vacuum'
115
+ }
77
116
  const matterSupported = !!DEVICE_MATTER_SUPPORTED[(type || '').toLowerCase()]
78
117
  const useMatter = !!this.config.enableMatter && matterSupported && !!this.config.preferMatter
79
118
 
@@ -93,7 +132,7 @@ export class SwitchBotHAPPlatform {
93
132
  const accessory: any = {
94
133
  uuid,
95
134
  displayName: createdDesc.name || d.name || type,
96
- context: { deviceId: d.id, type: type, _created: true },
135
+ context: { deviceId: d.id, type, _created: true },
97
136
  }
98
137
  try {
99
138
  await matterApi.registerPlatformAccessories(PLUGIN_NAME, PLATFORM_NAME, [accessory])
@@ -121,7 +160,7 @@ export class SwitchBotHAPPlatform {
121
160
  let accessory: any = this.accessories.get(uuid)
122
161
  // If not found by uuid, attempt to find by stored deviceId in accessory.context
123
162
  if (!accessory) {
124
- for (const [k, a] of Array.from(this.accessories.entries())) {
163
+ for (const [, a] of Array.from(this.accessories.entries())) {
125
164
  try {
126
165
  if (a && a.context && a.context.deviceId === d.id) {
127
166
  accessory = a
@@ -165,12 +204,16 @@ export class SwitchBotHAPPlatform {
165
204
  if (accDesc && accDesc.services) {
166
205
  for (const s of accDesc.services) {
167
206
  const Service = hap.Service[s.type] || hap.Service[s.type]
168
- if (!Service) continue
207
+ if (!Service) {
208
+ continue
209
+ }
169
210
  const service = accessory.getService(Service) || accessory.addService(Service)
170
211
  for (const [charName, getterSetterRaw] of Object.entries(s.characteristics || {})) {
171
212
  const getterSetter: any = getterSetterRaw
172
213
  const Characteristic = (hap.Characteristic as any)[charName]
173
- if (!Characteristic) continue
214
+ if (!Characteristic) {
215
+ continue
216
+ }
174
217
  // Apply characteristic props if provided (min/max/step)
175
218
  if (getterSetter && getterSetter.props) {
176
219
  try {
@@ -201,6 +244,8 @@ export class SwitchBotHAPPlatform {
201
244
  this.log.error(`Failed to create device ${d.id}:`, e as any)
202
245
  }
203
246
  }
247
+ // Update hash after successfully loading devices
248
+ this.lastConfigHash = this.getConfigHash()
204
249
  }
205
250
 
206
251
  // Example lifecycle method called by Homebridge
@@ -234,6 +279,9 @@ export class SwitchBotMatterPlatform {
234
279
  config: SwitchBotPluginConfig
235
280
  devices: any[] = []
236
281
  accessories: Map<string, any>
282
+ // Track last loaded config to detect changes
283
+ private lastConfigHash: string = ''
284
+ private configReloadInterval: NodeJS.Timeout | null = null
237
285
 
238
286
  constructor(log: Logger, config: PlatformConfig, api?: API) {
239
287
  this.log = log
@@ -259,9 +307,17 @@ export class SwitchBotMatterPlatform {
259
307
  this.log.warn('Error during Matter platform startup', e)
260
308
  }
261
309
  })()
310
+ // Start periodic config reload to pick up UI changes
311
+ this.configReloadInterval = setInterval(() => {
312
+ void this.checkAndReloadDevices()
313
+ }, 10000) // Check every 10 seconds
262
314
  })
263
315
  } else {
264
316
  void this.loadDevices()
317
+ // Start periodic config reload to pick up UI changes
318
+ this.configReloadInterval = setInterval(() => {
319
+ void this.checkAndReloadDevices()
320
+ }, 10000) // Check every 10 seconds
265
321
  }
266
322
  // Create/shared SwitchBot client and attach to config so child devices reuse it.
267
323
  try {
@@ -284,7 +340,11 @@ export class SwitchBotMatterPlatform {
284
340
  _raw: raw,
285
341
  }
286
342
 
287
- const type: string = d.type
343
+ let type: string = d.type
344
+ // Normalize wosweeper variants to vacuum
345
+ if (['wosweeper', 'wosweepermini', 'wosweeperminipro'].includes((type || '').toLowerCase())) {
346
+ type = 'vacuum'
347
+ }
288
348
  const matterSupported = !!DEVICE_MATTER_SUPPORTED[(type || '').toLowerCase()]
289
349
  const useMatter = !!this.config.enableMatter && matterSupported
290
350
  try {
@@ -293,12 +353,40 @@ export class SwitchBotMatterPlatform {
293
353
  if (useMatter) {
294
354
  this.log.info(`Prepared Matter accessory for ${d.id} (${type})`)
295
355
  } else {
296
- this.log.info(`Skipping Matter for ${d.id} (${type}) - not supported`)
356
+ if (!this.config.enableMatter) {
357
+ this.log.info(`Skipping Matter for ${d.id} (${type}) - Matter not enabled in plugin config`)
358
+ } else if (!matterSupported) {
359
+ this.log.info(`Skipping Matter for ${d.id} (${type}) - device type not supported`)
360
+ } else {
361
+ this.log.info(`Skipping Matter for ${d.id} (${type}) - not supported`)
362
+ }
297
363
  }
298
364
  } catch (e) {
299
365
  this.log.error(`Failed to create Matter device ${d.id}:`, e as any)
300
366
  }
301
367
  }
368
+ // Update hash after successfully loading devices
369
+ this.lastConfigHash = this.getConfigHash()
370
+ }
371
+
372
+ private getConfigHash(): string {
373
+ // Create a simple hash of current device config to detect changes
374
+ const devices = (this.config as any)?.devices ?? []
375
+ return JSON.stringify(devices.map((d: any) => ({
376
+ id: d.deviceId ?? d.id,
377
+ type: d.configDeviceType ?? d.type,
378
+ name: d.configDeviceName ?? d.name,
379
+ })))
380
+ }
381
+
382
+ private async checkAndReloadDevices() {
383
+ const currentHash = this.getConfigHash()
384
+ if (currentHash !== this.lastConfigHash) {
385
+ this.log.info('[SwitchBot] Detected config changes, reloading devices...')
386
+ // Clear existing devices
387
+ this.devices = []
388
+ await this.loadDevices()
389
+ }
302
390
  }
303
391
 
304
392
  async configureAccessory(accessory: any) {
@@ -324,7 +412,9 @@ export class SwitchBotMatterPlatform {
324
412
 
325
413
  // Register serialized Matter accessories via Homebridge Matter API
326
414
  async registerMatterAccessories() {
327
- if (!this.api) return
415
+ if (!this.api) {
416
+ return
417
+ }
328
418
  const matterApi = (this.api as any).matter
329
419
  if (!matterApi || typeof matterApi.registerPlatformAccessories !== 'function') {
330
420
  this.log.info('Homebridge Matter API not available; skipping Matter accessory registration')
@@ -338,7 +428,9 @@ export class SwitchBotMatterPlatform {
338
428
  const type: string = d.type || d.deviceType || 'unknown'
339
429
  const matterSupported = !!DEVICE_MATTER_SUPPORTED[(type || '').toLowerCase()]
340
430
  const useMatter = !!this.config.enableMatter && matterSupported
341
- if (!useMatter) continue
431
+ if (!useMatter) {
432
+ continue
433
+ }
342
434
 
343
435
  try {
344
436
  const created = await createDevice({ id: d.id, type, name: d.name }, this.config, true)
@@ -346,7 +438,7 @@ export class SwitchBotMatterPlatform {
346
438
  const uuid = matterApi.uuid.generate(`${d.id}`)
347
439
  // Try to find existing restored accessory by deviceId
348
440
  let existing: any | undefined
349
- for (const [k, a] of Array.from(this.accessories.entries())) {
441
+ for (const [, a] of Array.from(this.accessories.entries())) {
350
442
  try {
351
443
  if (a && a.context && a.context.deviceId === d.id) {
352
444
  existing = a
@@ -382,7 +474,7 @@ export class SwitchBotMatterPlatform {
382
474
  if (accessoriesToRegister.length > 0) {
383
475
  try {
384
476
  await matterApi.registerPlatformAccessories(PLUGIN_NAME, PLATFORM_NAME, accessoriesToRegister)
385
- this.log.info(`Registered ${accessoriesToRegister.length} Matter accessory(ies) with Homebridge`)
477
+ this.log.info(`Registered ${accessoriesToRegister.length} Matter accessory(ies) with Homebridge`)
386
478
  } catch (e) {
387
479
  this.log.warn('Failed to register Matter accessories', e)
388
480
  }