@switchbot/homebridge-switchbot 5.0.0-beta.98 → 5.0.0
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/.changeset/config.json +14 -0
- package/.github/copilot-instructions.md +39 -0
- package/.github/workflows/ci.yml +4 -1
- package/.github/workflows/manual-e2e.yml +6 -3
- package/.github/workflows/release.yml +64 -15
- package/.github/workflows/stale.yml +2 -4
- package/.husky/pre-push +15 -0
- package/CHANGELOG.md +126 -134
- package/MIGRATION.md +16 -6
- package/README.md +84 -3
- package/TODO.md +263 -0
- package/config.schema.json +229 -36
- package/dist/SwitchBotHAPPlatform.d.ts +133 -0
- package/dist/SwitchBotHAPPlatform.d.ts.map +1 -0
- package/dist/SwitchBotHAPPlatform.js +555 -0
- package/dist/SwitchBotHAPPlatform.js.map +1 -0
- package/dist/SwitchBotMatterPlatform.d.ts +141 -0
- package/dist/SwitchBotMatterPlatform.d.ts.map +1 -0
- package/dist/SwitchBotMatterPlatform.js +536 -0
- package/dist/SwitchBotMatterPlatform.js.map +1 -0
- package/dist/device-types.d.ts +31 -0
- package/dist/device-types.d.ts.map +1 -0
- package/dist/device-types.js +246 -0
- package/dist/device-types.js.map +1 -0
- package/dist/deviceCommandMapper.d.ts +10 -0
- package/dist/deviceCommandMapper.d.ts.map +1 -0
- package/dist/deviceCommandMapper.js +319 -0
- package/dist/deviceCommandMapper.js.map +1 -0
- package/dist/deviceFactory.d.ts +3 -2
- package/dist/deviceFactory.d.ts.map +1 -1
- package/dist/deviceFactory.js +107 -29
- package/dist/deviceFactory.js.map +1 -1
- package/dist/devices/genericDevice.d.ts +59 -37
- package/dist/devices/genericDevice.d.ts.map +1 -1
- package/dist/devices/genericDevice.js +376 -78
- package/dist/devices/genericDevice.js.map +1 -1
- package/dist/errors.d.ts +38 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +32 -0
- package/dist/errors.js.map +1 -0
- package/dist/homebridge-ui/device-types.js +246 -0
- package/dist/homebridge-ui/device-types.js.map +1 -0
- package/dist/homebridge-ui/deviceCommandMapper.js +319 -0
- package/dist/homebridge-ui/deviceCommandMapper.js.map +1 -0
- package/dist/homebridge-ui/endpoints/config.d.ts +3 -0
- package/dist/homebridge-ui/endpoints/config.d.ts.map +1 -0
- package/dist/homebridge-ui/endpoints/config.js +90 -0
- package/dist/homebridge-ui/endpoints/config.js.map +1 -0
- package/dist/homebridge-ui/endpoints/devices.d.ts +6 -0
- package/dist/homebridge-ui/endpoints/devices.d.ts.map +1 -0
- package/dist/homebridge-ui/endpoints/devices.js +144 -0
- package/dist/homebridge-ui/endpoints/devices.js.map +1 -0
- package/dist/homebridge-ui/endpoints/discovery.d.ts +7 -0
- package/dist/homebridge-ui/endpoints/discovery.d.ts.map +1 -0
- package/dist/homebridge-ui/endpoints/discovery.js +219 -0
- package/dist/homebridge-ui/endpoints/discovery.js.map +1 -0
- package/dist/homebridge-ui/errors.js +32 -0
- package/dist/homebridge-ui/errors.js.map +1 -0
- package/dist/homebridge-ui/homebridge-ui/endpoints/config.js +90 -0
- package/dist/homebridge-ui/homebridge-ui/endpoints/config.js.map +1 -0
- package/dist/homebridge-ui/homebridge-ui/endpoints/devices.js +144 -0
- package/dist/homebridge-ui/homebridge-ui/endpoints/devices.js.map +1 -0
- package/dist/homebridge-ui/homebridge-ui/endpoints/discovery.js +219 -0
- package/dist/homebridge-ui/homebridge-ui/endpoints/discovery.js.map +1 -0
- package/dist/homebridge-ui/homebridge-ui/server.js +11 -0
- package/dist/homebridge-ui/homebridge-ui/server.js.map +1 -0
- package/dist/homebridge-ui/homebridge-ui/utils/config-parser.js +108 -0
- package/dist/homebridge-ui/homebridge-ui/utils/config-parser.js.map +1 -0
- package/dist/homebridge-ui/homebridge-ui/utils/device-migration.js +111 -0
- package/dist/homebridge-ui/homebridge-ui/utils/device-migration.js.map +1 -0
- package/dist/homebridge-ui/homebridge-ui/utils/logger.js +17 -0
- package/dist/homebridge-ui/homebridge-ui/utils/logger.js.map +1 -0
- package/dist/homebridge-ui/public/css/styles.css +483 -0
- package/dist/homebridge-ui/public/index.html +197 -621
- package/dist/homebridge-ui/public/js/advanced-settings.d.ts +3 -0
- package/dist/homebridge-ui/public/js/advanced-settings.d.ts.map +1 -0
- package/dist/homebridge-ui/public/js/advanced-settings.js +95 -0
- package/dist/homebridge-ui/public/js/advanced-settings.js.map +1 -0
- package/dist/homebridge-ui/public/js/advanced-settings.ts +94 -0
- package/dist/homebridge-ui/public/js/api.d.ts +66 -0
- package/dist/homebridge-ui/public/js/api.d.ts.map +1 -0
- package/dist/homebridge-ui/public/js/api.js +295 -0
- package/dist/homebridge-ui/public/js/api.js.map +1 -0
- package/dist/homebridge-ui/public/js/api.ts +355 -0
- package/dist/homebridge-ui/public/js/app.d.ts +2 -0
- package/dist/homebridge-ui/public/js/app.d.ts.map +1 -0
- package/dist/homebridge-ui/public/js/app.js +3722 -0
- package/dist/homebridge-ui/public/js/app.js.map +7 -0
- package/dist/homebridge-ui/public/js/app.ts +22 -0
- package/dist/homebridge-ui/public/js/constants.d.ts +2 -0
- package/dist/homebridge-ui/public/js/constants.d.ts.map +1 -0
- package/dist/homebridge-ui/public/js/constants.js +2 -0
- package/dist/homebridge-ui/public/js/constants.js.map +1 -0
- package/dist/homebridge-ui/public/js/constants.ts +1 -0
- package/dist/homebridge-ui/public/js/credentials.d.ts +3 -0
- package/dist/homebridge-ui/public/js/credentials.d.ts.map +1 -0
- package/dist/homebridge-ui/public/js/credentials.js +99 -0
- package/dist/homebridge-ui/public/js/credentials.js.map +1 -0
- package/dist/homebridge-ui/public/js/credentials.ts +105 -0
- package/dist/homebridge-ui/public/js/devices-delete.d.ts +3 -0
- package/dist/homebridge-ui/public/js/devices-delete.d.ts.map +1 -0
- package/dist/homebridge-ui/public/js/devices-delete.js +199 -0
- package/dist/homebridge-ui/public/js/devices-delete.js.map +1 -0
- package/dist/homebridge-ui/public/js/devices-delete.ts +227 -0
- package/dist/homebridge-ui/public/js/devices.d.ts +9 -0
- package/dist/homebridge-ui/public/js/devices.d.ts.map +1 -0
- package/dist/homebridge-ui/public/js/devices.js +98 -0
- package/dist/homebridge-ui/public/js/devices.js.map +1 -0
- package/dist/homebridge-ui/public/js/devices.ts +106 -0
- package/dist/homebridge-ui/public/js/discovery.d.ts +9 -0
- package/dist/homebridge-ui/public/js/discovery.d.ts.map +1 -0
- package/dist/homebridge-ui/public/js/discovery.js +1201 -0
- package/dist/homebridge-ui/public/js/discovery.js.map +1 -0
- package/dist/homebridge-ui/public/js/discovery.ts +1335 -0
- package/dist/homebridge-ui/public/js/logger.d.ts +7 -0
- package/dist/homebridge-ui/public/js/logger.d.ts.map +1 -0
- package/dist/homebridge-ui/public/js/logger.js +17 -0
- package/dist/homebridge-ui/public/js/logger.js.map +1 -0
- package/dist/homebridge-ui/public/js/logger.ts +17 -0
- package/dist/homebridge-ui/public/js/modal.d.ts +5 -0
- package/dist/homebridge-ui/public/js/modal.d.ts.map +1 -0
- package/dist/homebridge-ui/public/js/modal.js +35 -0
- package/dist/homebridge-ui/public/js/modal.js.map +1 -0
- package/dist/homebridge-ui/public/js/modal.ts +35 -0
- package/dist/homebridge-ui/public/js/modals.d.ts +15 -0
- package/dist/homebridge-ui/public/js/modals.d.ts.map +1 -0
- package/dist/homebridge-ui/public/js/modals.js +675 -0
- package/dist/homebridge-ui/public/js/modals.js.map +1 -0
- package/dist/homebridge-ui/public/js/modals.ts +765 -0
- package/dist/homebridge-ui/public/js/render.d.ts +71 -0
- package/dist/homebridge-ui/public/js/render.d.ts.map +1 -0
- package/dist/homebridge-ui/public/js/render.js +960 -0
- package/dist/homebridge-ui/public/js/render.js.map +1 -0
- package/dist/homebridge-ui/public/js/render.ts +1084 -0
- package/dist/homebridge-ui/public/js/toast.d.ts +6 -0
- package/dist/homebridge-ui/public/js/toast.d.ts.map +1 -0
- package/dist/homebridge-ui/public/js/toast.js +38 -0
- package/dist/homebridge-ui/public/js/toast.js.map +1 -0
- package/dist/homebridge-ui/public/js/toast.ts +44 -0
- package/dist/homebridge-ui/public/js/types.d.ts +23 -0
- package/dist/homebridge-ui/public/js/types.d.ts.map +1 -0
- package/dist/homebridge-ui/public/js/types.js +2 -0
- package/dist/homebridge-ui/public/js/types.js.map +1 -0
- package/dist/homebridge-ui/public/js/types.ts +26 -0
- package/dist/homebridge-ui/server.d.ts +1 -3
- package/dist/homebridge-ui/server.d.ts.map +1 -1
- package/dist/homebridge-ui/server.js +8 -450
- package/dist/homebridge-ui/server.js.map +1 -1
- package/dist/homebridge-ui/settings.js +8 -0
- package/dist/homebridge-ui/settings.js.map +1 -0
- package/dist/homebridge-ui/switchbotClient.js +247 -0
- package/dist/homebridge-ui/switchbotClient.js.map +1 -0
- package/dist/homebridge-ui/utils/config-parser.d.ts +39 -0
- package/dist/homebridge-ui/utils/config-parser.d.ts.map +1 -0
- package/dist/homebridge-ui/utils/config-parser.js +108 -0
- package/dist/homebridge-ui/utils/config-parser.js.map +1 -0
- package/dist/homebridge-ui/utils/device-migration.d.ts +35 -0
- package/dist/homebridge-ui/utils/device-migration.d.ts.map +1 -0
- package/dist/homebridge-ui/utils/device-migration.js +111 -0
- package/dist/homebridge-ui/utils/device-migration.js.map +1 -0
- package/dist/homebridge-ui/utils/logger.d.ts +7 -0
- package/dist/homebridge-ui/utils/logger.d.ts.map +1 -0
- package/dist/homebridge-ui/utils/logger.js +17 -0
- package/dist/homebridge-ui/utils/logger.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +12 -2
- package/dist/index.js.map +1 -1
- package/dist/settings.d.ts +1 -0
- package/dist/settings.d.ts.map +1 -1
- package/dist/settings.js +1 -0
- package/dist/settings.js.map +1 -1
- package/dist/switchbotClient.d.ts +12 -10
- package/dist/switchbotClient.d.ts.map +1 -1
- package/dist/switchbotClient.js +156 -103
- package/dist/switchbotClient.js.map +1 -1
- package/dist/utils.d.ts +76 -1
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +1121 -4
- package/dist/utils.js.map +1 -1
- package/docs/assets/highlight.css +16 -2
- package/docs/assets/main.js +1 -1
- package/docs/index.html +82 -5
- package/docs/variables/default.html +3 -1
- package/eslint.config.js +9 -5
- package/nodemon.json +2 -2
- package/package.json +34 -21
- package/scripts/build-ui.js +37 -0
- package/scripts/free-dev-ports.mjs +105 -0
- package/scripts/generate-matter-maps.js +34 -17
- package/scripts/sync-device-types.mjs +31 -0
- package/src/SwitchBotHAPPlatform.ts +558 -0
- package/src/SwitchBotMatterPlatform.ts +538 -0
- package/src/device-types.js +246 -0
- package/src/device-types.js.map +1 -0
- package/src/device-types.ts +261 -0
- package/src/deviceCommandMapper.js +319 -0
- package/src/deviceCommandMapper.js.map +1 -0
- package/src/deviceCommandMapper.ts +333 -0
- package/src/deviceFactory.ts +125 -45
- package/src/devices/genericDevice.ts +411 -69
- package/src/errors.js +32 -0
- package/src/errors.js.map +1 -0
- package/src/errors.ts +35 -0
- package/src/homebridge-ui/endpoints/config.ts +110 -0
- package/src/homebridge-ui/endpoints/devices.ts +153 -0
- package/src/homebridge-ui/endpoints/discovery.ts +240 -0
- package/src/homebridge-ui/public/css/styles.css +483 -0
- package/src/homebridge-ui/public/index.html +197 -621
- package/src/homebridge-ui/public/js/advanced-settings.ts +94 -0
- package/src/homebridge-ui/public/js/api.ts +355 -0
- package/src/homebridge-ui/public/js/app.ts +22 -0
- package/src/homebridge-ui/public/js/constants.ts +1 -0
- package/src/homebridge-ui/public/js/credentials.ts +105 -0
- package/src/homebridge-ui/public/js/devices-delete.ts +227 -0
- package/src/homebridge-ui/public/js/devices.ts +106 -0
- package/src/homebridge-ui/public/js/discovery.ts +1335 -0
- package/src/homebridge-ui/public/js/logger.ts +17 -0
- package/src/homebridge-ui/public/js/modal.ts +35 -0
- package/src/homebridge-ui/public/js/modals.ts +765 -0
- package/src/homebridge-ui/public/js/render.ts +1084 -0
- package/src/homebridge-ui/public/js/toast.ts +44 -0
- package/src/homebridge-ui/public/js/types.ts +26 -0
- package/src/homebridge-ui/server.ts +9 -526
- package/src/homebridge-ui/utils/config-parser.ts +125 -0
- package/src/homebridge-ui/utils/device-migration.ts +144 -0
- package/src/homebridge-ui/utils/logger.ts +17 -0
- package/src/index.ts +12 -2
- package/src/settings.js +8 -0
- package/src/settings.js.map +1 -0
- package/src/settings.ts +2 -0
- package/src/switchbotClient.js +247 -0
- package/src/switchbotClient.js.map +1 -0
- package/src/switchbotClient.ts +177 -114
- package/src/utils.ts +1133 -5
- package/test/client/switchbot-client-debounce.spec.ts +35 -0
- package/test/client/switchbot-client-openapi.spec.ts +19 -0
- package/test/client/switchbotClient.spec.ts +64 -0
- package/test/device/device-mapping.spec.ts +23 -0
- package/test/device/deviceBase.spec.ts +26 -0
- package/test/device/deviceFactory-edge.spec.ts +15 -0
- package/test/device/deviceFactory.spec.ts +33 -0
- package/test/device/fan-swing.spec.ts +34 -0
- package/test/device/genericDevice-blepoll.spec.ts +47 -0
- package/test/device/irdevice.spec.ts +9 -0
- package/test/device/lock-users.spec.ts +35 -0
- package/test/device/matter-descriptors.spec.ts +22 -0
- package/test/device/matter-device-state.spec.ts +37 -0
- package/test/e2e/run-e2e.spec.ts +18 -19
- package/test/errors/errors.spec.ts +10 -0
- package/test/helpers/matter-harness.ts +20 -9
- package/test/homebridge-ui/server.spec.ts +9 -0
- package/test/platform/accessory-restore.spec.ts +37 -0
- package/test/platform/matter-childbridge.spec.ts +34 -0
- package/test/platform/matter-integration.spec.ts +33 -0
- package/test/platform/platform-edge.spec.ts +73 -0
- package/test/platform/platform.integration.spec.ts +34 -0
- package/test/utils/utils-extra.spec.ts +10 -0
- package/test/utils/utils.spec.ts +53 -0
- package/todo/TODO.md +80 -0
- package/tsconfig.ui.json +11 -0
- package/.github/npm-version-script-esm.js +0 -97
- package/.github/workflows/beta-release.yml +0 -52
- package/dist/platform.d.ts +0 -35
- package/dist/platform.d.ts.map +0 -1
- package/dist/platform.js +0 -850
- package/dist/platform.js.map +0 -1
- package/src/platform.ts +0 -867
- package/test/accessory-restore.spec.ts +0 -73
- package/test/device-mapping.spec.ts +0 -37
- package/test/deviceFactory.spec.ts +0 -18
- package/test/fan-swing.spec.ts +0 -29
- package/test/lock-users.spec.ts +0 -44
- package/test/matter-childbridge.spec.ts +0 -55
- package/test/matter-descriptors.spec.ts +0 -97
- package/test/matter-device-state.spec.ts +0 -101
- package/test/matter-integration.spec.ts +0 -70
- package/test/platform.integration.spec.ts +0 -55
- package/test/switchbot-client-debounce.spec.ts +0 -131
- package/test/switchbot-client-openapi.spec.ts +0 -56
- package/test/switchbotClient.spec.ts +0 -10
- package/test/utils.spec.ts +0 -20
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ensure required fields are present on the SwitchBot platform config
|
|
3
|
+
*/
|
|
4
|
+
export function enforcePlatformConfigFields(platform: any): void {
|
|
5
|
+
if (!platform) return
|
|
6
|
+
if (!platform.platform) platform.platform = 'SwitchBot'
|
|
7
|
+
if (!platform.name) platform.name = 'SwitchBot'
|
|
8
|
+
if (!Array.isArray(platform.devices)) platform.devices = []
|
|
9
|
+
}
|
|
10
|
+
import type { HomebridgePluginUiServer } from '@homebridge/plugin-ui-utils'
|
|
11
|
+
|
|
12
|
+
import fs from 'node:fs/promises'
|
|
13
|
+
|
|
14
|
+
import { uiLog } from './logger.js'
|
|
15
|
+
|
|
16
|
+
// Module-scope regex pattern to avoid recompilation
|
|
17
|
+
export const SWITCHBOT_PLATFORM_REGEX = /switchbot/i
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Get reference to the devices array in platform config
|
|
21
|
+
*/
|
|
22
|
+
export function getDevicesRef(platform: any): any[] {
|
|
23
|
+
if (!platform || typeof platform !== 'object') {
|
|
24
|
+
return []
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if (Array.isArray(platform.devices)) {
|
|
28
|
+
return platform.devices
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
platform.devices = []
|
|
32
|
+
return platform.devices
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Get all device arrays from platform config
|
|
37
|
+
*/
|
|
38
|
+
export function getDeviceArrays(platform: any): any[][] {
|
|
39
|
+
const rootDevices = getDevicesRef(platform)
|
|
40
|
+
return [rootDevices]
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Get all unique devices from platform config
|
|
45
|
+
*/
|
|
46
|
+
export function getAllDevices(platform: any): any[] {
|
|
47
|
+
const all = getDevicesRef(platform)
|
|
48
|
+
const seen = new Set<string>()
|
|
49
|
+
|
|
50
|
+
return all.filter((d: any) => {
|
|
51
|
+
const id = String(d?.deviceId ?? d?.id ?? '').trim().toLowerCase()
|
|
52
|
+
if (!id || seen.has(id)) {
|
|
53
|
+
return false
|
|
54
|
+
}
|
|
55
|
+
seen.add(id)
|
|
56
|
+
return true
|
|
57
|
+
})
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Get credential from platform config
|
|
62
|
+
*/
|
|
63
|
+
export function getCredential(platform: any, key: 'openApiToken' | 'openApiSecret'): string | undefined {
|
|
64
|
+
return platform?.[key]
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Set credential in platform config
|
|
69
|
+
*/
|
|
70
|
+
export function setCredential(platform: any, key: 'openApiToken' | 'openApiSecret', value: string): void {
|
|
71
|
+
if (!platform || typeof platform !== 'object') {
|
|
72
|
+
return
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
platform[key] = value
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Find and parse the SwitchBot platform config from Homebridge config file
|
|
80
|
+
*/
|
|
81
|
+
export async function getSwitchBotPlatformConfig(server: HomebridgePluginUiServer) {
|
|
82
|
+
const cfgPath = server.homebridgeConfigPath
|
|
83
|
+
if (!cfgPath) {
|
|
84
|
+
throw new Error('HOMEBRIDGE_CONFIG_PATH not set')
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const raw = await fs.readFile(cfgPath, 'utf-8')
|
|
88
|
+
const cfg = JSON.parse(raw)
|
|
89
|
+
|
|
90
|
+
const platforms = Array.isArray(cfg.platforms) ? cfg.platforms : []
|
|
91
|
+
|
|
92
|
+
const candidates = platforms.filter((p: any) => {
|
|
93
|
+
const platformName = p.platform || p.name || ''
|
|
94
|
+
return !!platformName && (SWITCHBOT_PLATFORM_REGEX.test(String(platformName)) || String(platformName).toLowerCase() === '@switchbot/homebridge-switchbot')
|
|
95
|
+
})
|
|
96
|
+
|
|
97
|
+
uiLog.info(`Found ${candidates.length} SwitchBot candidate(s)`)
|
|
98
|
+
|
|
99
|
+
const platform = candidates.find((p: any) => {
|
|
100
|
+
const name = String(p.platform || '').toLowerCase()
|
|
101
|
+
return name === 'switchbot' || name.includes('switchbot')
|
|
102
|
+
})
|
|
103
|
+
|
|
104
|
+
if (!platform) {
|
|
105
|
+
throw new Error('SwitchBot platform not found in config. Please add the plugin configuration first.')
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
uiLog.info(`Using SwitchBot platform: ${platform.name || platform.platform || 'SwitchBot'}`)
|
|
109
|
+
return { platform, cfg, cfgPath }
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Save the Homebridge config file
|
|
114
|
+
*/
|
|
115
|
+
export async function saveConfig(cfgPath: string, cfg: any): Promise<void> {
|
|
116
|
+
// Defensive: enforce required fields on all SwitchBot platform blocks before saving
|
|
117
|
+
if (cfg && Array.isArray(cfg.platforms)) {
|
|
118
|
+
for (const p of cfg.platforms) {
|
|
119
|
+
if (p && (String(p.platform || '').toLowerCase() === 'switchbot' || String(p.name || '').toLowerCase() === 'switchbot')) {
|
|
120
|
+
enforcePlatformConfigFields(p)
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
await fs.writeFile(cfgPath, JSON.stringify(cfg, null, 2), 'utf-8')
|
|
125
|
+
}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import { DEVICE_TYPES, isValidDeviceType, normalizeDeviceType } from '../../device-types.js'
|
|
2
|
+
|
|
3
|
+
export interface DeviceMigrationResult {
|
|
4
|
+
migrated: boolean
|
|
5
|
+
originalType: string | undefined
|
|
6
|
+
correctedType: string | null
|
|
7
|
+
message: string
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Validate and optionally correct a device's configDeviceType
|
|
12
|
+
* @param device Device object from config
|
|
13
|
+
* @param autoCorrect Whether to auto-correct invalid types
|
|
14
|
+
* @returns Migration result with details
|
|
15
|
+
*/
|
|
16
|
+
export function validateAndMigrateDeviceType(
|
|
17
|
+
device: any,
|
|
18
|
+
autoCorrect = false,
|
|
19
|
+
): DeviceMigrationResult {
|
|
20
|
+
const originalType = device.configDeviceType
|
|
21
|
+
const deviceId = device.deviceId || device.id || 'unknown'
|
|
22
|
+
|
|
23
|
+
// Check if type is valid
|
|
24
|
+
if (isValidDeviceType(originalType)) {
|
|
25
|
+
return {
|
|
26
|
+
migrated: false,
|
|
27
|
+
originalType,
|
|
28
|
+
correctedType: originalType,
|
|
29
|
+
message: `Device "${device.configDeviceName || deviceId}" type is valid`,
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Try to find a valid mapping
|
|
34
|
+
const correctedType = normalizeDeviceType(originalType)
|
|
35
|
+
|
|
36
|
+
if (!correctedType) {
|
|
37
|
+
return {
|
|
38
|
+
migrated: false,
|
|
39
|
+
originalType,
|
|
40
|
+
correctedType: null,
|
|
41
|
+
message: `Device "${device.configDeviceName || deviceId}" has invalid type "${originalType}" with no valid mapping`,
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Found a valid mapping
|
|
46
|
+
if (autoCorrect) {
|
|
47
|
+
device.configDeviceType = correctedType
|
|
48
|
+
return {
|
|
49
|
+
migrated: true,
|
|
50
|
+
originalType,
|
|
51
|
+
correctedType,
|
|
52
|
+
message: `Device "${device.configDeviceName || deviceId}" type auto-corrected: "${originalType}" → "${correctedType}"`,
|
|
53
|
+
}
|
|
54
|
+
} else {
|
|
55
|
+
return {
|
|
56
|
+
migrated: false,
|
|
57
|
+
originalType,
|
|
58
|
+
correctedType,
|
|
59
|
+
message: `Device "${device.configDeviceName || deviceId}" requires migration: "${originalType}" → "${correctedType}"`,
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Validate all devices in config and optionally auto-correct invalid types
|
|
66
|
+
* @param config Configuration object containing devices array
|
|
67
|
+
* @param autoCorrect Whether to auto-correct invalid types
|
|
68
|
+
* @returns Array of migration results with statistics
|
|
69
|
+
*/
|
|
70
|
+
export function validateAndMigrateConfig(
|
|
71
|
+
config: any,
|
|
72
|
+
autoCorrect = false,
|
|
73
|
+
): {
|
|
74
|
+
results: DeviceMigrationResult[]
|
|
75
|
+
statistics: {
|
|
76
|
+
total: number
|
|
77
|
+
valid: number
|
|
78
|
+
corrected: number
|
|
79
|
+
invalid: number
|
|
80
|
+
}
|
|
81
|
+
warnings: string[]
|
|
82
|
+
} {
|
|
83
|
+
const results: DeviceMigrationResult[] = []
|
|
84
|
+
const warnings: string[] = []
|
|
85
|
+
let validCount = 0
|
|
86
|
+
let correctedCount = 0
|
|
87
|
+
let invalidCount = 0
|
|
88
|
+
|
|
89
|
+
if (!config?.devices || !Array.isArray(config.devices)) {
|
|
90
|
+
return {
|
|
91
|
+
results,
|
|
92
|
+
statistics: {
|
|
93
|
+
total: 0,
|
|
94
|
+
valid: 0,
|
|
95
|
+
corrected: 0,
|
|
96
|
+
invalid: 0,
|
|
97
|
+
},
|
|
98
|
+
warnings: ['No devices found in config'],
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
for (const device of config.devices) {
|
|
103
|
+
const result = validateAndMigrateDeviceType(device, autoCorrect)
|
|
104
|
+
results.push(result)
|
|
105
|
+
|
|
106
|
+
if (!result.originalType || isValidDeviceType(result.originalType)) {
|
|
107
|
+
validCount++
|
|
108
|
+
} else if (result.migrated) {
|
|
109
|
+
correctedCount++
|
|
110
|
+
} else if (result.correctedType) {
|
|
111
|
+
warnings.push(
|
|
112
|
+
`Device "${device.configDeviceName || device.deviceId}" needs correction: "${result.originalType}" → "${result.correctedType}"`,
|
|
113
|
+
)
|
|
114
|
+
invalidCount++
|
|
115
|
+
} else {
|
|
116
|
+
invalidCount++
|
|
117
|
+
warnings.push(
|
|
118
|
+
`Device "${device.configDeviceName || device.deviceId}" has INVALID type "${result.originalType}" with no mapping`,
|
|
119
|
+
)
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Validation complete - results and warnings are returned via return value
|
|
124
|
+
// Caller (config endpoint) will handle logging/reporting
|
|
125
|
+
|
|
126
|
+
return {
|
|
127
|
+
results,
|
|
128
|
+
statistics: {
|
|
129
|
+
total: config.devices.length,
|
|
130
|
+
valid: validCount,
|
|
131
|
+
corrected: correctedCount,
|
|
132
|
+
invalid: invalidCount,
|
|
133
|
+
},
|
|
134
|
+
warnings,
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Get list of all valid device types for error messages
|
|
140
|
+
* @returns Array of valid device types grouped by category
|
|
141
|
+
*/
|
|
142
|
+
export function getValidDeviceTypesList(): Record<string, readonly string[]> {
|
|
143
|
+
return DEVICE_TYPES
|
|
144
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/* eslint-disable no-console */
|
|
2
|
+
const PREFIX = '[SwitchBot UI/Server]'
|
|
3
|
+
|
|
4
|
+
export const uiLog = {
|
|
5
|
+
info: (message: string, ...parameters: any[]) => {
|
|
6
|
+
console.log(PREFIX, message, ...parameters)
|
|
7
|
+
},
|
|
8
|
+
warn: (message: string, ...parameters: any[]) => {
|
|
9
|
+
console.warn(PREFIX, message, ...parameters)
|
|
10
|
+
},
|
|
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)
|
|
16
|
+
},
|
|
17
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -4,11 +4,21 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import type { API } from 'homebridge'
|
|
6
6
|
|
|
7
|
-
import { SwitchBotHAPPlatform, SwitchBotMatterPlatform } from './platform.js'
|
|
8
7
|
import { PLATFORM_NAME, PLUGIN_NAME } from './settings.js'
|
|
8
|
+
import { SwitchBotHAPPlatform } from './SwitchBotHAPPlatform.js'
|
|
9
|
+
import { SwitchBotMatterPlatform } from './SwitchBotMatterPlatform.js'
|
|
9
10
|
import { createPlatformProxy } from './utils.js'
|
|
10
11
|
|
|
11
|
-
|
|
12
|
+
/**
|
|
13
|
+
* Registers the SwitchBot platform with Homebridge.
|
|
14
|
+
*
|
|
15
|
+
* @param api The Homebridge API instance.
|
|
16
|
+
* @property {string} PLATFORM_NAME - The Homebridge platform name.
|
|
17
|
+
* @property {string} PLUGIN_NAME - The Homebridge plugin name.
|
|
18
|
+
* @property {typeof SwitchBotHAPPlatform} SwitchBotHAPPlatform - The HAP platform class.
|
|
19
|
+
* @property {typeof SwitchBotMatterPlatform} SwitchBotMatterPlatform - The Matter platform class.
|
|
20
|
+
* @property {Function} createPlatformProxy - Factory for the platform proxy constructor.
|
|
21
|
+
*/
|
|
12
22
|
export default (api: API): void => {
|
|
13
23
|
const ProxyCtor = createPlatformProxy(SwitchBotHAPPlatform, SwitchBotMatterPlatform)
|
|
14
24
|
api.registerPlatform(PLUGIN_NAME, PLATFORM_NAME, ProxyCtor as any)
|
package/src/settings.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"settings.js","sourceRoot":"","sources":["settings.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,WAAW,GAAG,iCAAiC,CAAA;AAC5D,MAAM,CAAC,MAAM,aAAa,GAAG,WAAW,CAAA;AAYxC,MAAM,CAAC,MAAM,cAAc,GAAmC;IAC5D,YAAY,EAAE,IAAI;IAClB,YAAY,EAAE,IAAI;IAClB,SAAS,EAAE,IAAI;CAChB,CAAA"}
|
package/src/settings.ts
CHANGED
|
@@ -6,6 +6,7 @@ export interface SwitchBotPluginConfig {
|
|
|
6
6
|
openApiSecret?: string
|
|
7
7
|
preferMatter?: boolean
|
|
8
8
|
enableMatter?: boolean
|
|
9
|
+
enableBLE?: boolean // Enable or disable BLE support
|
|
9
10
|
// other plugin-specific configuration
|
|
10
11
|
[key: string]: any
|
|
11
12
|
}
|
|
@@ -13,6 +14,7 @@ export interface SwitchBotPluginConfig {
|
|
|
13
14
|
export const DEFAULT_CONFIG: Partial<SwitchBotPluginConfig> = {
|
|
14
15
|
preferMatter: true,
|
|
15
16
|
enableMatter: true,
|
|
17
|
+
enableBLE: true,
|
|
16
18
|
}
|
|
17
19
|
|
|
18
20
|
export type DeviceType = string
|
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
import { getDeviceCommandHandler } from './deviceCommandMapper.js';
|
|
2
|
+
import { CharacteristicMissingError, SwitchbotAuthenticationError, SwitchbotOperationError } from './errors.js';
|
|
3
|
+
/**
|
|
4
|
+
* Thin wrapper around node-switchbot v4.0.0+
|
|
5
|
+
* Leverages upstream resilience features (retry, circuit breaker, connection intelligence)
|
|
6
|
+
* while maintaining plugin-specific features like write debouncing and OpenAPI fallback.
|
|
7
|
+
*/
|
|
8
|
+
export class SwitchBotClient {
|
|
9
|
+
cfg;
|
|
10
|
+
client = null;
|
|
11
|
+
writeDebounceMs = 100;
|
|
12
|
+
discoveryCacheTtlMs = 30_000;
|
|
13
|
+
lastDiscoveryAt = 0;
|
|
14
|
+
logger;
|
|
15
|
+
pendingWrites = new Map();
|
|
16
|
+
constructor(cfg) {
|
|
17
|
+
this.cfg = cfg;
|
|
18
|
+
this.logger = cfg?.logger;
|
|
19
|
+
if (!this.logger) {
|
|
20
|
+
throw new Error('SwitchBotClient requires a logger (Homebridge logger) in config');
|
|
21
|
+
}
|
|
22
|
+
if (typeof cfg?.writeDebounceMs === 'number') {
|
|
23
|
+
this.writeDebounceMs = cfg.writeDebounceMs;
|
|
24
|
+
}
|
|
25
|
+
if (typeof cfg?.discoveryCacheTtlMs === 'number') {
|
|
26
|
+
this.discoveryCacheTtlMs = Math.max(0, cfg.discoveryCacheTtlMs);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
async init() {
|
|
30
|
+
if (this.client) {
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
try {
|
|
34
|
+
// Dynamic import of node-switchbot v4 with native resilience features
|
|
35
|
+
const { SwitchBot } = await import('node-switchbot');
|
|
36
|
+
const rawNodeClientConfig = typeof this.cfg?.nodeClientConfig === 'object' ? this.cfg.nodeClientConfig : {};
|
|
37
|
+
const scanTimeout = this.resolveScanTimeoutMs(rawNodeClientConfig);
|
|
38
|
+
this.client = new SwitchBot({
|
|
39
|
+
token: this.cfg.openApiToken,
|
|
40
|
+
secret: this.cfg.openApiSecret,
|
|
41
|
+
// Enable built-in resilience features from node-switchbot v4.
|
|
42
|
+
enableFallback: true, // Auto-fallback from BLE to API
|
|
43
|
+
enableRetry: true, // Retry with exponential backoff
|
|
44
|
+
enableCircuitBreaker: true, // Circuit breaker per connection type
|
|
45
|
+
enableConnectionIntelligence: true, // Connection tracking and route preference
|
|
46
|
+
enableBLE: this.cfg.enableBLE !== false, // Use config value, default true
|
|
47
|
+
scanTimeout,
|
|
48
|
+
...rawNodeClientConfig,
|
|
49
|
+
});
|
|
50
|
+
this.lastDiscoveryAt = 0;
|
|
51
|
+
this.logger?.info?.('SwitchBot client initialized with native resilience features');
|
|
52
|
+
}
|
|
53
|
+
catch (e) {
|
|
54
|
+
this.logger?.warn?.('Failed to load node-switchbot; will use OpenAPI fallback:', e);
|
|
55
|
+
this.client = null;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
async getDevice(id) {
|
|
59
|
+
if (this.client) {
|
|
60
|
+
try {
|
|
61
|
+
const fromManager = this.getManagedDevice(id);
|
|
62
|
+
if (fromManager) {
|
|
63
|
+
return fromManager;
|
|
64
|
+
}
|
|
65
|
+
const devices = await this.ensureDiscovered(false);
|
|
66
|
+
const fromDiscovery = devices.find((d) => d.id === id);
|
|
67
|
+
if (fromDiscovery) {
|
|
68
|
+
return fromDiscovery;
|
|
69
|
+
}
|
|
70
|
+
const refreshDevices = await this.ensureDiscovered(true);
|
|
71
|
+
return refreshDevices.find((d) => d.id === id);
|
|
72
|
+
}
|
|
73
|
+
catch (e) {
|
|
74
|
+
if (e instanceof SwitchbotAuthenticationError) {
|
|
75
|
+
this.logger?.error?.(`Authentication error for getDevice(${id}):`, e.message);
|
|
76
|
+
throw e;
|
|
77
|
+
}
|
|
78
|
+
else if (e instanceof SwitchbotOperationError) {
|
|
79
|
+
this.logger?.warn?.(`Operation error for getDevice(${id}):`, e.message, e.code);
|
|
80
|
+
throw e;
|
|
81
|
+
}
|
|
82
|
+
else if (e instanceof CharacteristicMissingError) {
|
|
83
|
+
this.logger?.warn?.(`Characteristic missing for getDevice(${id}):`, e.characteristic);
|
|
84
|
+
throw e;
|
|
85
|
+
}
|
|
86
|
+
else {
|
|
87
|
+
this.logger?.warn?.(`Client getDevice failed for ${id}:`, e);
|
|
88
|
+
throw e;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
throw new SwitchbotOperationError('No SwitchBot client available', 'no_client');
|
|
93
|
+
}
|
|
94
|
+
async getDevices() {
|
|
95
|
+
if (this.client) {
|
|
96
|
+
try {
|
|
97
|
+
const fromManager = this.getManagedDevices();
|
|
98
|
+
if (fromManager.length > 0) {
|
|
99
|
+
return fromManager;
|
|
100
|
+
}
|
|
101
|
+
return await this.ensureDiscovered(false);
|
|
102
|
+
}
|
|
103
|
+
catch (e) {
|
|
104
|
+
this.logger?.warn?.('Client getDevices failed:', e);
|
|
105
|
+
throw e;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
throw new SwitchbotOperationError('No SwitchBot client available', 'no_client');
|
|
109
|
+
}
|
|
110
|
+
async setDeviceState(id, body) {
|
|
111
|
+
// Plugin-level debounce: coalesce rapid writes per device
|
|
112
|
+
if (!this.writeDebounceMs || this.writeDebounceMs <= 0) {
|
|
113
|
+
return this._doSetDeviceState(id, body);
|
|
114
|
+
}
|
|
115
|
+
return new Promise((resolve, reject) => {
|
|
116
|
+
const existing = this.pendingWrites.get(id);
|
|
117
|
+
if (existing) {
|
|
118
|
+
existing.body = body;
|
|
119
|
+
existing.resolvers.push({ resolve, reject });
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
const resolvers = [{ resolve, reject }];
|
|
123
|
+
const timer = setTimeout(async () => {
|
|
124
|
+
const entry = this.pendingWrites.get(id);
|
|
125
|
+
if (!entry) {
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
this.pendingWrites.delete(id);
|
|
129
|
+
try {
|
|
130
|
+
const out = await this._doSetDeviceState(id, entry.body);
|
|
131
|
+
for (const r of entry.resolvers)
|
|
132
|
+
r.resolve(out);
|
|
133
|
+
}
|
|
134
|
+
catch (e) {
|
|
135
|
+
if (e instanceof SwitchbotAuthenticationError) {
|
|
136
|
+
this.logger?.error?.(`Authentication error for setDeviceState(${id}):`, e.message);
|
|
137
|
+
}
|
|
138
|
+
else if (e instanceof SwitchbotOperationError) {
|
|
139
|
+
this.logger?.warn?.(`Operation error for setDeviceState(${id}):`, e.message, e.code);
|
|
140
|
+
}
|
|
141
|
+
else if (e instanceof CharacteristicMissingError) {
|
|
142
|
+
this.logger?.warn?.(`Characteristic missing for setDeviceState(${id}):`, e.characteristic);
|
|
143
|
+
}
|
|
144
|
+
for (const r of entry.resolvers)
|
|
145
|
+
r.reject(e);
|
|
146
|
+
}
|
|
147
|
+
}, this.writeDebounceMs);
|
|
148
|
+
this.pendingWrites.set(id, { timer, body, resolvers });
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
async _doSetDeviceState(id, body) {
|
|
152
|
+
if (!this.client) {
|
|
153
|
+
throw new SwitchbotOperationError('No SwitchBot client available for setDeviceState', 'no_client');
|
|
154
|
+
}
|
|
155
|
+
try {
|
|
156
|
+
const device = await this.getDevice(id);
|
|
157
|
+
if (!device) {
|
|
158
|
+
throw new SwitchbotOperationError(`Device ${id} not found`, 'device_not_found');
|
|
159
|
+
}
|
|
160
|
+
const deviceType = (device.deviceType ?? '').toLowerCase();
|
|
161
|
+
const command = body?.command;
|
|
162
|
+
if (!command) {
|
|
163
|
+
throw new SwitchbotOperationError('No command specified in body', 'no_command');
|
|
164
|
+
}
|
|
165
|
+
const handler = getDeviceCommandHandler(deviceType, command);
|
|
166
|
+
if (!handler) {
|
|
167
|
+
throw new SwitchbotOperationError(`Unsupported command '${command}' for device type '${deviceType}'`, 'unsupported_command');
|
|
168
|
+
}
|
|
169
|
+
this.logger?.debug?.(`[${id}] Calling mapped command '${command}' for device type '${deviceType}'`);
|
|
170
|
+
return await handler(device, body);
|
|
171
|
+
}
|
|
172
|
+
catch (e) {
|
|
173
|
+
if (e instanceof SwitchbotAuthenticationError) {
|
|
174
|
+
this.logger?.error?.(`Authentication error for setDeviceState(${id}):`, e.message);
|
|
175
|
+
throw e;
|
|
176
|
+
}
|
|
177
|
+
else if (e instanceof SwitchbotOperationError) {
|
|
178
|
+
this.logger?.warn?.(`Operation error for setDeviceState(${id}):`, e.message, e.code);
|
|
179
|
+
throw e;
|
|
180
|
+
}
|
|
181
|
+
else if (e instanceof CharacteristicMissingError) {
|
|
182
|
+
this.logger?.warn?.(`Characteristic missing for setDeviceState(${id}):`, e.characteristic);
|
|
183
|
+
throw e;
|
|
184
|
+
}
|
|
185
|
+
else {
|
|
186
|
+
this.logger?.warn?.(`Device command failed for ${id}:`, e);
|
|
187
|
+
throw e;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
async destroy() {
|
|
192
|
+
for (const [, pending] of this.pendingWrites) {
|
|
193
|
+
clearTimeout(pending.timer);
|
|
194
|
+
const err = new SwitchbotOperationError('Client destroyed before pending write was sent', 'client_destroyed');
|
|
195
|
+
for (const r of pending.resolvers) {
|
|
196
|
+
r.reject(err);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
this.pendingWrites.clear();
|
|
200
|
+
if (this.client?.cleanup) {
|
|
201
|
+
await this.client.cleanup();
|
|
202
|
+
}
|
|
203
|
+
this.client = null;
|
|
204
|
+
this.lastDiscoveryAt = 0;
|
|
205
|
+
}
|
|
206
|
+
resolveScanTimeoutMs(rawNodeClientConfig) {
|
|
207
|
+
if (typeof rawNodeClientConfig.scanTimeout === 'number' && Number.isFinite(rawNodeClientConfig.scanTimeout)) {
|
|
208
|
+
return Math.max(500, rawNodeClientConfig.scanTimeout);
|
|
209
|
+
}
|
|
210
|
+
if (typeof rawNodeClientConfig.scanDuration === 'number' && Number.isFinite(rawNodeClientConfig.scanDuration)) {
|
|
211
|
+
return Math.max(500, rawNodeClientConfig.scanDuration);
|
|
212
|
+
}
|
|
213
|
+
if (typeof this.cfg?.bleScanDurationSeconds === 'number') {
|
|
214
|
+
return Math.max(500, this.cfg.bleScanDurationSeconds * 1000);
|
|
215
|
+
}
|
|
216
|
+
return 5000;
|
|
217
|
+
}
|
|
218
|
+
getManagedDevice(id) {
|
|
219
|
+
const manager = this.client?.devices;
|
|
220
|
+
if (manager?.get) {
|
|
221
|
+
return manager.get(id);
|
|
222
|
+
}
|
|
223
|
+
return undefined;
|
|
224
|
+
}
|
|
225
|
+
getManagedDevices() {
|
|
226
|
+
const manager = this.client?.devices;
|
|
227
|
+
if (manager?.list) {
|
|
228
|
+
const list = manager.list();
|
|
229
|
+
return Array.isArray(list) ? list : [];
|
|
230
|
+
}
|
|
231
|
+
return [];
|
|
232
|
+
}
|
|
233
|
+
async ensureDiscovered(force) {
|
|
234
|
+
if (!this.client) {
|
|
235
|
+
throw new SwitchbotOperationError('No SwitchBot client available', 'no_client');
|
|
236
|
+
}
|
|
237
|
+
const fromManager = this.getManagedDevices();
|
|
238
|
+
const cacheValid = this.discoveryCacheTtlMs > 0 && (Date.now() - this.lastDiscoveryAt) < this.discoveryCacheTtlMs;
|
|
239
|
+
if (!force && cacheValid && fromManager.length > 0) {
|
|
240
|
+
return fromManager;
|
|
241
|
+
}
|
|
242
|
+
const discovered = await this.client.discover();
|
|
243
|
+
this.lastDiscoveryAt = Date.now();
|
|
244
|
+
return discovered;
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
//# sourceMappingURL=switchbotClient.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"switchbotClient.js","sourceRoot":"","sources":["switchbotClient.ts"],"names":[],"mappings":"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"}
|