@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/dist/homebridge-ui/public/index.html +182 -24
- package/dist/homebridge-ui/server.d.ts.map +1 -1
- package/dist/homebridge-ui/server.js +19 -6
- package/dist/homebridge-ui/server.js.map +1 -1
- package/dist/platform.d.ts +9 -1
- package/dist/platform.d.ts.map +1 -1
- package/dist/platform.js +98 -13
- package/dist/platform.js.map +1 -1
- package/docs/variables/default.html +1 -1
- package/package.json +1 -1
- package/src/homebridge-ui/public/index.html +182 -24
- package/src/homebridge-ui/server.ts +22 -6
- package/src/platform.ts +107 -15
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
|
-
|
|
3
|
+
|
|
3
4
|
import { createDevice } from './deviceFactory.js'
|
|
4
|
-
import
|
|
5
|
-
import {
|
|
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:
|
|
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
|
-
|
|
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
|
|
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 [
|
|
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)
|
|
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)
|
|
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
|
-
|
|
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.
|
|
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)
|
|
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)
|
|
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 [
|
|
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
|
}
|