@switchbot/homebridge-switchbot 5.0.0-beta.99 → 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 -471
- 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 -554
- 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 -945
- package/dist/platform.js.map +0 -1
- package/src/platform.ts +0 -963
- 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
package/dist/platform.js
DELETED
|
@@ -1,945 +0,0 @@
|
|
|
1
|
-
import { createDevice } from './deviceFactory.js';
|
|
2
|
-
import { PLATFORM_NAME, PLUGIN_NAME } from './settings.js';
|
|
3
|
-
import { SwitchBotClient } from './switchbotClient.js';
|
|
4
|
-
// Which device types should prefer Matter if available
|
|
5
|
-
// Based on HAP service mappings: device implementations use specific HomeKit services
|
|
6
|
-
// that map to corresponding Matter clusters when Matter is enabled
|
|
7
|
-
const DEVICE_MATTER_SUPPORTED = {
|
|
8
|
-
// Core devices
|
|
9
|
-
'bot': true, // Switch → OnOff
|
|
10
|
-
'curtain': true, // WindowCovering → WindowCovering
|
|
11
|
-
'fan': true, // Fan → FanControl
|
|
12
|
-
'light': true, // Lightbulb → OnOff + LevelControl
|
|
13
|
-
'lightstrip': true, // Lightbulb (color) → OnOff + LevelControl + ColorControl
|
|
14
|
-
'motion': true, // MotionSensor → OccupancySensing
|
|
15
|
-
'contact': true, // ContactSensor → BooleanState
|
|
16
|
-
'vacuum': true, // Switch → RobotVacuumCleaner
|
|
17
|
-
'lock': true, // LockMechanism → DoorLock
|
|
18
|
-
'humidifier': true, // Fan + Humidity → OnOff + FanControl + RelativeHumidityMeasurement
|
|
19
|
-
'temperature': true, // TemperatureSensor → TemperatureMeasurement
|
|
20
|
-
// Switch devices
|
|
21
|
-
'relay': true, // Switch → OnOff
|
|
22
|
-
'relay switch 1': true, // Switch → OnOff
|
|
23
|
-
'relay switch 1pm': true, // Switch → OnOff
|
|
24
|
-
'plug': true, // Outlet → OnOff
|
|
25
|
-
'plug mini (jp)': true, // Outlet → OnOff
|
|
26
|
-
'plug mini (us)': true, // Outlet → OnOff
|
|
27
|
-
// Window covering variants
|
|
28
|
-
'blindtilt': true, // WindowCovering → WindowCovering
|
|
29
|
-
'blind tilt': true, // WindowCovering → WindowCovering
|
|
30
|
-
'curtain3': true, // WindowCovering → WindowCovering
|
|
31
|
-
'rollershade': true, // WindowCovering → WindowCovering
|
|
32
|
-
'roller shade': true, // WindowCovering → WindowCovering
|
|
33
|
-
'worollershade': true, // WindowCovering → WindowCovering
|
|
34
|
-
'wo rollershade': true, // WindowCovering → WindowCovering
|
|
35
|
-
// Vacuum variants (normalized to 'vacuum' before lookup)
|
|
36
|
-
'wosweeper': true, // VacuumDevice → RobotVacuumCleaner
|
|
37
|
-
'wosweepermini': true, // VacuumDevice → RobotVacuumCleaner
|
|
38
|
-
'wosweeperminipro': true, // VacuumDevice → RobotVacuumCleaner
|
|
39
|
-
'k10+': true, // VacuumDevice → RobotVacuumCleaner
|
|
40
|
-
'k10+ pro': true, // VacuumDevice → RobotVacuumCleaner
|
|
41
|
-
// Sensors
|
|
42
|
-
'meter': true, // TemperatureSensor + HumiditySensor → TemperatureMeasurement + RelativeHumidityMeasurement
|
|
43
|
-
'meterplus': true, // TemperatureSensor + HumiditySensor → TemperatureMeasurement + RelativeHumidityMeasurement
|
|
44
|
-
'meter plus (jp)': true, // TemperatureSensor + HumiditySensor → TemperatureMeasurement + RelativeHumidityMeasurement
|
|
45
|
-
'meterpro': true, // TemperatureSensor + HumiditySensor → TemperatureMeasurement + RelativeHumidityMeasurement
|
|
46
|
-
'meterpro(co2)': true, // TemperatureSensor + HumiditySensor → TemperatureMeasurement + RelativeHumidityMeasurement
|
|
47
|
-
'waterdetector': true, // LeakSensor → BooleanState
|
|
48
|
-
'water detector': true, // LeakSensor → BooleanState
|
|
49
|
-
// Other devices
|
|
50
|
-
'smart fan': true, // Fan → FanControl
|
|
51
|
-
'strip light': true, // Lightbulb (color) → OnOff + LevelControl + ColorControl
|
|
52
|
-
'hub 2': false, // Hub device - not exposed as accessory
|
|
53
|
-
'walletfinder': false, // Button device - Matter support TBD
|
|
54
|
-
};
|
|
55
|
-
// Default Matter cluster configurations by device type
|
|
56
|
-
// Maps device types to their Matter cluster states (used when device doesn't provide clusters)
|
|
57
|
-
// Note: wosweeper/curtain/plug variants are normalized before cluster lookup (see loadDevices)
|
|
58
|
-
const DEVICE_MATTER_CLUSTERS = {
|
|
59
|
-
// Core devices - aligned with HAP service implementations
|
|
60
|
-
bot: { onOff: { onOff: false } }, // Switch → OnOff
|
|
61
|
-
vacuum: {
|
|
62
|
-
rvcRunMode: {
|
|
63
|
-
supportedModes: [
|
|
64
|
-
{ label: 'Idle', mode: 0, modeTags: [{ value: 16384 }] },
|
|
65
|
-
{ label: 'Cleaning', mode: 1, modeTags: [{ value: 16385 }] },
|
|
66
|
-
],
|
|
67
|
-
currentMode: 0,
|
|
68
|
-
},
|
|
69
|
-
rvcCleanMode: {
|
|
70
|
-
supportedModes: [
|
|
71
|
-
{ label: 'Vacuum', mode: 0, modeTags: [{ value: 16385 }] },
|
|
72
|
-
],
|
|
73
|
-
currentMode: 0,
|
|
74
|
-
},
|
|
75
|
-
rvcOperationalState: {
|
|
76
|
-
operationalStateList: [
|
|
77
|
-
{ operationalStateId: 0 }, // Stopped
|
|
78
|
-
{ operationalStateId: 1 }, // Running
|
|
79
|
-
{ operationalStateId: 2 }, // Paused
|
|
80
|
-
{ operationalStateId: 3 }, // Error (required)
|
|
81
|
-
{ operationalStateId: 64 }, // Seeking charger
|
|
82
|
-
{ operationalStateId: 65 }, // Charging
|
|
83
|
-
{ operationalStateId: 66 }, // Docked
|
|
84
|
-
],
|
|
85
|
-
operationalState: 66,
|
|
86
|
-
},
|
|
87
|
-
}, // Switch in HAP, RobotVacuumCleaner in Matter
|
|
88
|
-
curtain: {
|
|
89
|
-
windowCovering: {
|
|
90
|
-
currentPositionLiftPercent100ths: 0,
|
|
91
|
-
targetPositionLiftPercent100ths: 0,
|
|
92
|
-
operationalStatus: 0,
|
|
93
|
-
},
|
|
94
|
-
}, // WindowCovering → WindowCovering (includes curtain3, rollershade variants via normalization)
|
|
95
|
-
blindtilt: {
|
|
96
|
-
windowCovering: {
|
|
97
|
-
currentPositionLiftPercent100ths: 0,
|
|
98
|
-
targetPositionLiftPercent100ths: 0,
|
|
99
|
-
currentPositionTiltPercent100ths: 0,
|
|
100
|
-
targetPositionTiltPercent100ths: 0,
|
|
101
|
-
operationalStatus: 0,
|
|
102
|
-
},
|
|
103
|
-
}, // WindowCovering with tilt → WindowCovering
|
|
104
|
-
fan: {
|
|
105
|
-
onOff: { onOff: false },
|
|
106
|
-
fanControl: {
|
|
107
|
-
fanMode: 0,
|
|
108
|
-
percentCurrent: 0,
|
|
109
|
-
percentSetting: 0,
|
|
110
|
-
speedCurrent: 0,
|
|
111
|
-
speedMax: 100,
|
|
112
|
-
},
|
|
113
|
-
}, // Fan → OnOff + FanControl
|
|
114
|
-
light: {
|
|
115
|
-
onOff: { onOff: false },
|
|
116
|
-
levelControl: {
|
|
117
|
-
currentLevel: 0,
|
|
118
|
-
minLevel: 0,
|
|
119
|
-
maxLevel: 254,
|
|
120
|
-
},
|
|
121
|
-
}, // Lightbulb → OnOff + LevelControl
|
|
122
|
-
lightstrip: {
|
|
123
|
-
onOff: { onOff: false },
|
|
124
|
-
levelControl: {
|
|
125
|
-
currentLevel: 0,
|
|
126
|
-
minLevel: 0,
|
|
127
|
-
maxLevel: 254,
|
|
128
|
-
},
|
|
129
|
-
colorControl: {
|
|
130
|
-
currentHue: 0,
|
|
131
|
-
currentSaturation: 0,
|
|
132
|
-
colorTemperatureMireds: 400,
|
|
133
|
-
colorMode: 0,
|
|
134
|
-
},
|
|
135
|
-
}, // Lightbulb with color → OnOff + LevelControl + ColorControl
|
|
136
|
-
lock: {
|
|
137
|
-
doorLock: {
|
|
138
|
-
lockState: 0,
|
|
139
|
-
lockType: 0,
|
|
140
|
-
actuatorEnabled: true,
|
|
141
|
-
operatingMode: 0,
|
|
142
|
-
},
|
|
143
|
-
}, // LockMechanism → DoorLock
|
|
144
|
-
motion: {
|
|
145
|
-
occupancySensing: {
|
|
146
|
-
occupancy: 0,
|
|
147
|
-
occupancySensorType: 0,
|
|
148
|
-
},
|
|
149
|
-
}, // MotionSensor → OccupancySensing
|
|
150
|
-
contact: {
|
|
151
|
-
booleanState: {
|
|
152
|
-
stateValue: false,
|
|
153
|
-
},
|
|
154
|
-
}, // ContactSensor → BooleanState
|
|
155
|
-
humidifier: {
|
|
156
|
-
onOff: { onOff: false },
|
|
157
|
-
fanControl: {
|
|
158
|
-
fanMode: 0,
|
|
159
|
-
percentCurrent: 0,
|
|
160
|
-
},
|
|
161
|
-
relativeHumidityMeasurement: {
|
|
162
|
-
measuredValue: 0,
|
|
163
|
-
minMeasuredValue: 0,
|
|
164
|
-
maxMeasuredValue: 100,
|
|
165
|
-
},
|
|
166
|
-
}, // HumidifierDehumidifier → OnOff + FanControl + RelativeHumidityMeasurement
|
|
167
|
-
temperature: {
|
|
168
|
-
temperatureMeasurement: {
|
|
169
|
-
measuredValue: 0,
|
|
170
|
-
minMeasuredValue: -27315,
|
|
171
|
-
maxMeasuredValue: 32767,
|
|
172
|
-
},
|
|
173
|
-
}, // TemperatureSensor → TemperatureMeasurement
|
|
174
|
-
// Switch/Outlet devices
|
|
175
|
-
relay: { onOff: { onOff: false } }, // Switch → OnOff
|
|
176
|
-
plug: {
|
|
177
|
-
onOff: { onOff: false },
|
|
178
|
-
electricalMeasurement: {
|
|
179
|
-
activePower: 0,
|
|
180
|
-
rmsCurrent: 0,
|
|
181
|
-
rmsVoltage: 0,
|
|
182
|
-
},
|
|
183
|
-
}, // Outlet → OnOff + ElectricalMeasurement (for PM models)
|
|
184
|
-
// Sensors
|
|
185
|
-
meter: {
|
|
186
|
-
temperatureMeasurement: {
|
|
187
|
-
measuredValue: 0,
|
|
188
|
-
minMeasuredValue: -27315,
|
|
189
|
-
maxMeasuredValue: 32767,
|
|
190
|
-
},
|
|
191
|
-
relativeHumidityMeasurement: {
|
|
192
|
-
measuredValue: 0,
|
|
193
|
-
minMeasuredValue: 0,
|
|
194
|
-
maxMeasuredValue: 100,
|
|
195
|
-
},
|
|
196
|
-
}, // TemperatureSensor + HumiditySensor → TemperatureMeasurement + RelativeHumidityMeasurement
|
|
197
|
-
waterdetector: {
|
|
198
|
-
booleanState: {
|
|
199
|
-
stateValue: false,
|
|
200
|
-
},
|
|
201
|
-
}, // LeakSensor → BooleanState
|
|
202
|
-
};
|
|
203
|
-
const DEVICE_MATTER_DEVICE_TYPE_KEYS = {
|
|
204
|
-
bot: 'OnOffSwitch',
|
|
205
|
-
vacuum: 'RoboticVacuumCleaner',
|
|
206
|
-
curtain: 'WindowCovering',
|
|
207
|
-
blindtilt: 'WindowCovering',
|
|
208
|
-
fan: 'Fan',
|
|
209
|
-
light: 'DimmableLight',
|
|
210
|
-
lightstrip: 'ExtendedColorLight',
|
|
211
|
-
lock: 'DoorLock',
|
|
212
|
-
motion: 'MotionSensor',
|
|
213
|
-
contact: 'ContactSensor',
|
|
214
|
-
humidifier: 'Fan',
|
|
215
|
-
temperature: 'TemperatureSensor',
|
|
216
|
-
relay: 'OnOffSwitch',
|
|
217
|
-
plug: 'OnOffOutlet',
|
|
218
|
-
meter: 'TemperatureSensor',
|
|
219
|
-
waterdetector: 'LeakSensor',
|
|
220
|
-
};
|
|
221
|
-
// Default Matter cluster handlers by device type
|
|
222
|
-
// Provides handler stubs for devices with interactive clusters
|
|
223
|
-
const DEVICE_MATTER_HANDLERS = {
|
|
224
|
-
vacuum: {
|
|
225
|
-
rvcRunMode: {
|
|
226
|
-
// Handle changing vacuum run mode (Idle, Cleaning, Mapping)
|
|
227
|
-
changeToMode: async (request) => {
|
|
228
|
-
const modeNames = ['Idle', 'Cleaning', 'Mapping'];
|
|
229
|
-
const modeName = modeNames[request?.newMode] || `Unknown (${request?.newMode})`;
|
|
230
|
-
console.log('[SwitchBot] RVC run mode change requested:', modeName);
|
|
231
|
-
// TODO: Send API command to SwitchBot device to change run mode
|
|
232
|
-
// await this.client.setDeviceState(deviceId, { command: 'setMode', parameter: modeName })
|
|
233
|
-
},
|
|
234
|
-
},
|
|
235
|
-
rvcCleanMode: {
|
|
236
|
-
// Handle changing vacuum clean mode (Vacuum, Mop, etc.)
|
|
237
|
-
changeToMode: async (request) => {
|
|
238
|
-
const modeName = request?.newMode !== undefined ? `Mode ${request.newMode}` : 'Unknown';
|
|
239
|
-
console.log('[SwitchBot] RVC clean mode change requested:', modeName);
|
|
240
|
-
// TODO: Send API command to SwitchBot device to change clean mode
|
|
241
|
-
},
|
|
242
|
-
},
|
|
243
|
-
rvcOperationalState: {
|
|
244
|
-
// Handle pause command
|
|
245
|
-
pause: async () => {
|
|
246
|
-
console.log('[SwitchBot] RVC pause command received');
|
|
247
|
-
// TODO: Send pause command to SwitchBot device
|
|
248
|
-
},
|
|
249
|
-
// Handle resume command
|
|
250
|
-
resume: async () => {
|
|
251
|
-
console.log('[SwitchBot] RVC resume command received');
|
|
252
|
-
// TODO: Send resume command to SwitchBot device
|
|
253
|
-
},
|
|
254
|
-
// Handle return to dock command
|
|
255
|
-
goHome: async () => {
|
|
256
|
-
console.log('[SwitchBot] RVC goHome command received');
|
|
257
|
-
// TODO: Send return-to-dock command to SwitchBot device
|
|
258
|
-
},
|
|
259
|
-
},
|
|
260
|
-
},
|
|
261
|
-
bot: {
|
|
262
|
-
onOff: {
|
|
263
|
-
// Handle on/off switch commands
|
|
264
|
-
on: async () => {
|
|
265
|
-
console.log('[SwitchBot] Bot ON command received');
|
|
266
|
-
// TODO: Send ON command to SwitchBot device
|
|
267
|
-
},
|
|
268
|
-
off: async () => {
|
|
269
|
-
console.log('[SwitchBot] Bot OFF command received');
|
|
270
|
-
// TODO: Send OFF command to SwitchBot device
|
|
271
|
-
},
|
|
272
|
-
},
|
|
273
|
-
},
|
|
274
|
-
curtain: {
|
|
275
|
-
windowCovering: {
|
|
276
|
-
// Handle curtain position changes
|
|
277
|
-
goToLiftPercentage: async (request) => {
|
|
278
|
-
console.log('[SwitchBot] Curtain position change requested:', request?.liftPercent100thsValue);
|
|
279
|
-
// TODO: Send position command to SwitchBot device
|
|
280
|
-
},
|
|
281
|
-
upOrOpen: async () => {
|
|
282
|
-
console.log('[SwitchBot] Curtain open command received');
|
|
283
|
-
// TODO: Send open command to SwitchBot device
|
|
284
|
-
},
|
|
285
|
-
downOrClose: async () => {
|
|
286
|
-
console.log('[SwitchBot] Curtain close command received');
|
|
287
|
-
// TODO: Send close command to SwitchBot device
|
|
288
|
-
},
|
|
289
|
-
stopMotion: async () => {
|
|
290
|
-
console.log('[SwitchBot] Curtain stop command received');
|
|
291
|
-
// TODO: Send stop command to SwitchBot device
|
|
292
|
-
},
|
|
293
|
-
},
|
|
294
|
-
},
|
|
295
|
-
plug: {
|
|
296
|
-
onOff: {
|
|
297
|
-
on: async () => {
|
|
298
|
-
console.log('[SwitchBot] Plug ON command received');
|
|
299
|
-
},
|
|
300
|
-
off: async () => {
|
|
301
|
-
console.log('[SwitchBot] Plug OFF command received');
|
|
302
|
-
},
|
|
303
|
-
},
|
|
304
|
-
},
|
|
305
|
-
lock: {
|
|
306
|
-
doorLock: {
|
|
307
|
-
setLockState: async (request) => {
|
|
308
|
-
const state = request?.lockState === 1 ? 'LOCKED' : 'UNLOCKED';
|
|
309
|
-
console.log('[SwitchBot] Lock state change requested:', state);
|
|
310
|
-
},
|
|
311
|
-
},
|
|
312
|
-
},
|
|
313
|
-
};
|
|
314
|
-
function resolveMatterDeviceType(matterApi, type, createdDeviceType, clusters) {
|
|
315
|
-
if (createdDeviceType && typeof createdDeviceType === 'object' && typeof createdDeviceType.with === 'function') {
|
|
316
|
-
return createdDeviceType;
|
|
317
|
-
}
|
|
318
|
-
const lowerType = (typeof createdDeviceType === 'string' && createdDeviceType) ? createdDeviceType.toLowerCase() : (type || '').toLowerCase();
|
|
319
|
-
// Cluster-based upgrade for color lights if descriptor omitted device type.
|
|
320
|
-
const hasColorControl = !!clusters?.colorControl;
|
|
321
|
-
const inferredType = hasColorControl && lowerType === 'light'
|
|
322
|
-
? 'lightstrip'
|
|
323
|
-
: lowerType;
|
|
324
|
-
const mappedKey = DEVICE_MATTER_DEVICE_TYPE_KEYS[inferredType] || 'OnOffSwitch';
|
|
325
|
-
return matterApi?.deviceTypes?.[mappedKey] || matterApi?.deviceTypes?.OnOffSwitch;
|
|
326
|
-
}
|
|
327
|
-
export class SwitchBotHAPPlatform {
|
|
328
|
-
api;
|
|
329
|
-
log;
|
|
330
|
-
config;
|
|
331
|
-
devices = [];
|
|
332
|
-
// cached accessories restored by Homebridge
|
|
333
|
-
accessories;
|
|
334
|
-
// Track last loaded config to detect changes
|
|
335
|
-
lastConfigHash = '';
|
|
336
|
-
configReloadInterval = null;
|
|
337
|
-
constructor(log, config, api) {
|
|
338
|
-
this.log = log;
|
|
339
|
-
this.config = config ?? {};
|
|
340
|
-
this.api = api;
|
|
341
|
-
this.accessories = new Map();
|
|
342
|
-
this.log.info('SwitchBot HAP platform initialized');
|
|
343
|
-
// Create/shared SwitchBot client and attach to config so child devices reuse it.
|
|
344
|
-
try {
|
|
345
|
-
const client = new SwitchBotClient(this.config);
|
|
346
|
-
void client.init();
|
|
347
|
-
this.config._client = client;
|
|
348
|
-
}
|
|
349
|
-
catch (e) {
|
|
350
|
-
this.log.debug('Failed to create shared SwitchBot client', e);
|
|
351
|
-
}
|
|
352
|
-
// Create/shared SwitchBot client and attach to config so child devices reuse it.
|
|
353
|
-
try {
|
|
354
|
-
const client = new SwitchBotClient(this.config);
|
|
355
|
-
void client.init();
|
|
356
|
-
this.config._client = client;
|
|
357
|
-
}
|
|
358
|
-
catch (e) {
|
|
359
|
-
this.log.debug('Failed to create shared SwitchBot client', e);
|
|
360
|
-
}
|
|
361
|
-
// Wait for Homebridge to finish launching to create/register accessories
|
|
362
|
-
if (this.api && typeof this.api.on === 'function') {
|
|
363
|
-
;
|
|
364
|
-
this.api.on('didFinishLaunching', () => {
|
|
365
|
-
void this.loadDevices();
|
|
366
|
-
// Start periodic config reload to pick up UI changes
|
|
367
|
-
this.configReloadInterval = setInterval(() => {
|
|
368
|
-
void this.checkAndReloadDevices();
|
|
369
|
-
}, 10000); // Check every 10 seconds
|
|
370
|
-
});
|
|
371
|
-
}
|
|
372
|
-
else {
|
|
373
|
-
void this.loadDevices();
|
|
374
|
-
// Start periodic config reload to pick up UI changes
|
|
375
|
-
this.configReloadInterval = setInterval(() => {
|
|
376
|
-
void this.checkAndReloadDevices();
|
|
377
|
-
}, 10000); // Check every 10 seconds
|
|
378
|
-
}
|
|
379
|
-
}
|
|
380
|
-
getConfigHash() {
|
|
381
|
-
// Create a simple hash of current device config to detect changes
|
|
382
|
-
const devices = this.config?.devices ?? [];
|
|
383
|
-
return JSON.stringify(devices.map((d) => ({
|
|
384
|
-
id: d.deviceId ?? d.id,
|
|
385
|
-
type: d.configDeviceType ?? d.type,
|
|
386
|
-
name: d.configDeviceName ?? d.name,
|
|
387
|
-
})));
|
|
388
|
-
}
|
|
389
|
-
async checkAndReloadDevices() {
|
|
390
|
-
const currentHash = this.getConfigHash();
|
|
391
|
-
if (currentHash !== this.lastConfigHash) {
|
|
392
|
-
this.log.info('[SwitchBot] Detected config changes, reloading devices...');
|
|
393
|
-
// Clear existing devices
|
|
394
|
-
this.devices = [];
|
|
395
|
-
await this.loadDevices();
|
|
396
|
-
}
|
|
397
|
-
}
|
|
398
|
-
async loadDevices() {
|
|
399
|
-
const devices = this.config?.devices ?? [];
|
|
400
|
-
for (const raw of devices) {
|
|
401
|
-
// Normalize config keys from UI schema to internal shape
|
|
402
|
-
const d = {
|
|
403
|
-
id: raw.deviceId ?? raw.id,
|
|
404
|
-
name: raw.configDeviceName ?? raw.name,
|
|
405
|
-
type: raw.configDeviceType ?? raw.type ?? raw.deviceType ?? 'unknown',
|
|
406
|
-
_raw: raw,
|
|
407
|
-
};
|
|
408
|
-
let type = d.type;
|
|
409
|
-
// Normalize device type variants for consistent Matter cluster lookup
|
|
410
|
-
// Vacuum variants
|
|
411
|
-
if (['wosweeper', 'wosweepermini', 'wosweeperminipro', 'k10+', 'k10+ pro'].includes((type || '').toLowerCase())) {
|
|
412
|
-
type = 'vacuum';
|
|
413
|
-
}
|
|
414
|
-
// Window covering variants
|
|
415
|
-
if (['blindtilt', 'blind tilt', 'curtain3', 'rollershade', 'roller shade', 'worollershade', 'wo rollershade'].includes((type || '').toLowerCase())) {
|
|
416
|
-
type = 'curtain';
|
|
417
|
-
}
|
|
418
|
-
// Plug variants
|
|
419
|
-
if (['plug mini (jp)', 'plug mini (us)'].includes((type || '').toLowerCase())) {
|
|
420
|
-
type = 'plug';
|
|
421
|
-
}
|
|
422
|
-
// Meter variants
|
|
423
|
-
if (['meterplus', 'meter plus (jp)', 'meterpro', 'meterpro(co2)'].includes((type || '').toLowerCase())) {
|
|
424
|
-
type = 'meter';
|
|
425
|
-
}
|
|
426
|
-
// Relay switch variants
|
|
427
|
-
if (['relay switch 1', 'relay switch 1pm'].includes((type || '').toLowerCase())) {
|
|
428
|
-
type = 'relay';
|
|
429
|
-
}
|
|
430
|
-
// Water detector variants
|
|
431
|
-
if (['water detector'].includes((type || '').toLowerCase())) {
|
|
432
|
-
type = 'waterdetector';
|
|
433
|
-
}
|
|
434
|
-
// Fan variants
|
|
435
|
-
if (['smart fan'].includes((type || '').toLowerCase())) {
|
|
436
|
-
type = 'fan';
|
|
437
|
-
}
|
|
438
|
-
// Light variants
|
|
439
|
-
if (['strip light'].includes((type || '').toLowerCase())) {
|
|
440
|
-
type = 'lightstrip';
|
|
441
|
-
}
|
|
442
|
-
const matterSupported = !!DEVICE_MATTER_SUPPORTED[(type || '').toLowerCase()];
|
|
443
|
-
// Auto-detect Matter from Homebridge API, allow manual override via config
|
|
444
|
-
const matterAvailable = !!(this.api?.isMatterAvailable?.() && this.api?.isMatterEnabled?.());
|
|
445
|
-
const matterEnabled = matterAvailable || !!this.config.enableMatter;
|
|
446
|
-
const useMatter = !!(matterEnabled && matterSupported && (!!this.config.preferMatter || matterAvailable));
|
|
447
|
-
try {
|
|
448
|
-
const created = await createDevice({ id: d.id, type, name: d.name }, this.config, useMatter);
|
|
449
|
-
this.devices.push(created);
|
|
450
|
-
// Prefer Matter: try registering to the Matter child bridge first.
|
|
451
|
-
let matterRegistered = false;
|
|
452
|
-
if (useMatter) {
|
|
453
|
-
this.log.info(`Attempting Matter registration for ${d.id} (${type})`);
|
|
454
|
-
// If Homebridge Matter APIs are available, register the accessory
|
|
455
|
-
try {
|
|
456
|
-
const matterApi = this.api?.matter;
|
|
457
|
-
if (this.api?.isMatterAvailable?.() && this.api?.isMatterEnabled?.() && matterApi && typeof matterApi.registerPlatformAccessories === 'function') {
|
|
458
|
-
const createdDesc = created.createAccessory?.(this.api) || { id: d.id, name: d.name || type };
|
|
459
|
-
const uuid = matterApi.uuid.generate(`${d.id}`);
|
|
460
|
-
const defaultClusters = DEVICE_MATTER_CLUSTERS[type.toLowerCase()] || { onOff: { onOff: false } };
|
|
461
|
-
const clusters = createdDesc.clusters || defaultClusters;
|
|
462
|
-
const deviceType = resolveMatterDeviceType(matterApi, type, createdDesc.deviceType, clusters);
|
|
463
|
-
const accessory = {
|
|
464
|
-
UUID: uuid,
|
|
465
|
-
displayName: createdDesc.name || d.name || type,
|
|
466
|
-
deviceType,
|
|
467
|
-
manufacturer: createdDesc.manufacturer || 'SwitchBot',
|
|
468
|
-
model: createdDesc.model || type,
|
|
469
|
-
serialNumber: createdDesc.serialNumber || d.id,
|
|
470
|
-
reachable: createdDesc.reachable !== false,
|
|
471
|
-
firmwareRevision: createdDesc.firmwareRevision || '1.0.0',
|
|
472
|
-
hardwareRevision: createdDesc.hardwareRevision || '',
|
|
473
|
-
clusters,
|
|
474
|
-
context: { deviceId: d.id, type, _created: true },
|
|
475
|
-
};
|
|
476
|
-
try {
|
|
477
|
-
await matterApi.registerPlatformAccessories(PLUGIN_NAME, PLATFORM_NAME, [accessory]);
|
|
478
|
-
this.accessories.set(uuid, accessory);
|
|
479
|
-
matterRegistered = true;
|
|
480
|
-
this.log.info(`Registered Matter accessory ${d.id} (${type}) with uuid=${uuid}`);
|
|
481
|
-
}
|
|
482
|
-
catch (e) {
|
|
483
|
-
this.log.warn('Failed to register Matter accessory with Homebridge matter API', e);
|
|
484
|
-
}
|
|
485
|
-
}
|
|
486
|
-
else {
|
|
487
|
-
this.log.info('Homebridge Matter API not available or not enabled; will fallback to HAP');
|
|
488
|
-
}
|
|
489
|
-
}
|
|
490
|
-
catch (e) {
|
|
491
|
-
this.log.warn('Matter registration attempt failed', e);
|
|
492
|
-
}
|
|
493
|
-
}
|
|
494
|
-
// If Matter wasn't registered (either not supported, API missing, or registration failed), fall back to HAP registration.
|
|
495
|
-
if (!matterRegistered && this.api && this.api.hap) {
|
|
496
|
-
// Basic HAP accessory creation using homebridge API when available
|
|
497
|
-
try {
|
|
498
|
-
const hap = this.api.hap;
|
|
499
|
-
const uuid = hap.uuid.generate(`${d.id}`);
|
|
500
|
-
// Reuse cached accessory if available by uuid
|
|
501
|
-
let accessory = this.accessories.get(uuid);
|
|
502
|
-
// If not found by uuid, attempt to find by stored deviceId in accessory.context
|
|
503
|
-
if (!accessory) {
|
|
504
|
-
for (const [, a] of Array.from(this.accessories.entries())) {
|
|
505
|
-
try {
|
|
506
|
-
if (a && a.context && a.context.deviceId === d.id) {
|
|
507
|
-
accessory = a;
|
|
508
|
-
break;
|
|
509
|
-
}
|
|
510
|
-
}
|
|
511
|
-
catch (e) {
|
|
512
|
-
// ignore
|
|
513
|
-
}
|
|
514
|
-
}
|
|
515
|
-
}
|
|
516
|
-
if (!accessory) {
|
|
517
|
-
accessory = new this.api.platformAccessory(d.name || type, uuid);
|
|
518
|
-
// Store device metadata on accessory.context for persistence across restarts
|
|
519
|
-
try {
|
|
520
|
-
accessory.context = accessory.context || {};
|
|
521
|
-
accessory.context.deviceId = d.id;
|
|
522
|
-
accessory.context.type = type;
|
|
523
|
-
}
|
|
524
|
-
catch (e) {
|
|
525
|
-
// ignore context failures
|
|
526
|
-
}
|
|
527
|
-
// Register new accessory with Homebridge so it's cached
|
|
528
|
-
try {
|
|
529
|
-
;
|
|
530
|
-
this.api.registerPlatformAccessories(PLUGIN_NAME, PLATFORM_NAME, [accessory]);
|
|
531
|
-
}
|
|
532
|
-
catch (e) {
|
|
533
|
-
// older API variations may require different registration; ignore if unavailable
|
|
534
|
-
}
|
|
535
|
-
this.accessories.set(uuid, accessory);
|
|
536
|
-
}
|
|
537
|
-
else {
|
|
538
|
-
// ensure context includes deviceId (in case restored accessory lacked it)
|
|
539
|
-
try {
|
|
540
|
-
accessory.context = accessory.context || {};
|
|
541
|
-
accessory.context.deviceId = accessory.context.deviceId || d.id;
|
|
542
|
-
accessory.context.type = accessory.context.type || type;
|
|
543
|
-
}
|
|
544
|
-
catch (e) {
|
|
545
|
-
// ignore
|
|
546
|
-
}
|
|
547
|
-
}
|
|
548
|
-
// Add basic service descriptor from device
|
|
549
|
-
const accDesc = created.createAccessory?.(this.api);
|
|
550
|
-
if (accDesc && accDesc.services) {
|
|
551
|
-
for (const s of accDesc.services) {
|
|
552
|
-
const Service = hap.Service[s.type] || hap.Service[s.type];
|
|
553
|
-
if (!Service) {
|
|
554
|
-
continue;
|
|
555
|
-
}
|
|
556
|
-
const service = accessory.getService(Service) || accessory.addService(Service);
|
|
557
|
-
for (const [charName, getterSetterRaw] of Object.entries(s.characteristics || {})) {
|
|
558
|
-
const getterSetter = getterSetterRaw;
|
|
559
|
-
const Characteristic = hap.Characteristic[charName];
|
|
560
|
-
if (!Characteristic) {
|
|
561
|
-
continue;
|
|
562
|
-
}
|
|
563
|
-
// Apply characteristic props if provided (min/max/step)
|
|
564
|
-
if (getterSetter && getterSetter.props) {
|
|
565
|
-
try {
|
|
566
|
-
service.getCharacteristic(Characteristic).setProps(getterSetter.props);
|
|
567
|
-
}
|
|
568
|
-
catch (e) {
|
|
569
|
-
// ignore setProps failures on older HAP implementations
|
|
570
|
-
}
|
|
571
|
-
}
|
|
572
|
-
// Wire simple get/set handlers if provided
|
|
573
|
-
if (getterSetter && typeof getterSetter.get === 'function') {
|
|
574
|
-
service.getCharacteristic(Characteristic).onGet(getterSetter.get);
|
|
575
|
-
}
|
|
576
|
-
if (getterSetter && typeof getterSetter.set === 'function') {
|
|
577
|
-
service.getCharacteristic(Characteristic).onSet(getterSetter.set);
|
|
578
|
-
}
|
|
579
|
-
}
|
|
580
|
-
}
|
|
581
|
-
}
|
|
582
|
-
this.log.info(`Created/updated HAP accessory ${d.id} (${type})`);
|
|
583
|
-
}
|
|
584
|
-
catch (e) {
|
|
585
|
-
this.log.warn('HAP accessory creation failed', e);
|
|
586
|
-
}
|
|
587
|
-
}
|
|
588
|
-
else if (!matterRegistered) {
|
|
589
|
-
this.log.info(`Created HAP descriptor for ${d.id} (${type}) (API not available to register)`);
|
|
590
|
-
}
|
|
591
|
-
}
|
|
592
|
-
catch (e) {
|
|
593
|
-
this.log.error(`Failed to create device ${d.id}:`, e);
|
|
594
|
-
}
|
|
595
|
-
}
|
|
596
|
-
// Update hash after successfully loading devices
|
|
597
|
-
this.lastConfigHash = this.getConfigHash();
|
|
598
|
-
}
|
|
599
|
-
// Example lifecycle method called by Homebridge
|
|
600
|
-
async configureAccessory(accessory) {
|
|
601
|
-
// Homebridge calls this for restored cached accessories — keep a reference.
|
|
602
|
-
try {
|
|
603
|
-
const uuid = accessory.UUID || accessory.UUID;
|
|
604
|
-
this.accessories.set(uuid, accessory);
|
|
605
|
-
this.log.info(`Restored cached accessory ${accessory.displayName || uuid}`);
|
|
606
|
-
}
|
|
607
|
-
catch (e) {
|
|
608
|
-
this.log.warn('configureAccessory failed to restore accessory', e);
|
|
609
|
-
}
|
|
610
|
-
}
|
|
611
|
-
// Called by Homebridge when a cached Matter accessory is restored
|
|
612
|
-
configureMatterAccessory(accessory) {
|
|
613
|
-
try {
|
|
614
|
-
const uuid = accessory.uuid || accessory.UUID || accessory.uuid;
|
|
615
|
-
this.accessories.set(uuid, accessory);
|
|
616
|
-
this.log.info(`Restored cached Matter accessory ${accessory.displayName || uuid}`);
|
|
617
|
-
}
|
|
618
|
-
catch (e) {
|
|
619
|
-
this.log.warn('configureMatterAccessory failed to restore accessory', e);
|
|
620
|
-
}
|
|
621
|
-
}
|
|
622
|
-
}
|
|
623
|
-
// Matter platform implementation (placeholder)
|
|
624
|
-
export class SwitchBotMatterPlatform {
|
|
625
|
-
api;
|
|
626
|
-
log;
|
|
627
|
-
config;
|
|
628
|
-
devices = [];
|
|
629
|
-
accessories;
|
|
630
|
-
// Track last loaded config to detect changes
|
|
631
|
-
lastConfigHash = '';
|
|
632
|
-
configReloadInterval = null;
|
|
633
|
-
constructor(log, config, api) {
|
|
634
|
-
this.log = log;
|
|
635
|
-
this.config = config ?? {};
|
|
636
|
-
this.api = api;
|
|
637
|
-
this.accessories = new Map();
|
|
638
|
-
this.log.info('SwitchBot Matter platform initialized');
|
|
639
|
-
if (this.api && typeof this.api.on === 'function') {
|
|
640
|
-
;
|
|
641
|
-
this.api.on('didFinishLaunching', () => {
|
|
642
|
-
;
|
|
643
|
-
(async () => {
|
|
644
|
-
// After launch, perform discovery (if any) and register Matter accessories
|
|
645
|
-
try {
|
|
646
|
-
await this.loadDevices();
|
|
647
|
-
if (this.api.isMatterAvailable?.() && this.api.isMatterEnabled?.() && this.api.matter && typeof this.api.matter.registerPlatformAccessories === 'function') {
|
|
648
|
-
try {
|
|
649
|
-
await this.registerMatterAccessories?.();
|
|
650
|
-
}
|
|
651
|
-
catch (e) {
|
|
652
|
-
this.log.warn('registerMatterAccessories failed', e);
|
|
653
|
-
}
|
|
654
|
-
}
|
|
655
|
-
}
|
|
656
|
-
catch (e) {
|
|
657
|
-
this.log.warn('Error during Matter platform startup', e);
|
|
658
|
-
}
|
|
659
|
-
})();
|
|
660
|
-
// Start periodic config reload to pick up UI changes
|
|
661
|
-
this.configReloadInterval = setInterval(() => {
|
|
662
|
-
void this.checkAndReloadDevices();
|
|
663
|
-
}, 10000); // Check every 10 seconds
|
|
664
|
-
});
|
|
665
|
-
}
|
|
666
|
-
else {
|
|
667
|
-
void this.loadDevices();
|
|
668
|
-
// Start periodic config reload to pick up UI changes
|
|
669
|
-
this.configReloadInterval = setInterval(() => {
|
|
670
|
-
void this.checkAndReloadDevices();
|
|
671
|
-
}, 10000); // Check every 10 seconds
|
|
672
|
-
}
|
|
673
|
-
// Create/shared SwitchBot client and attach to config so child devices reuse it.
|
|
674
|
-
try {
|
|
675
|
-
const client = new SwitchBotClient(this.config);
|
|
676
|
-
void client.init();
|
|
677
|
-
this.config._client = client;
|
|
678
|
-
}
|
|
679
|
-
catch (e) {
|
|
680
|
-
this.log.debug('Failed to create shared SwitchBot client', e);
|
|
681
|
-
}
|
|
682
|
-
}
|
|
683
|
-
async loadDevices() {
|
|
684
|
-
const devices = this.config?.devices ?? [];
|
|
685
|
-
for (const raw of devices) {
|
|
686
|
-
// Normalize config keys produced by the UI schema
|
|
687
|
-
const d = {
|
|
688
|
-
id: raw.deviceId ?? raw.id,
|
|
689
|
-
name: raw.configDeviceName ?? raw.name,
|
|
690
|
-
type: raw.configDeviceType ?? raw.type ?? raw.deviceType ?? 'unknown',
|
|
691
|
-
_raw: raw,
|
|
692
|
-
};
|
|
693
|
-
let type = d.type;
|
|
694
|
-
// Normalize device type variants for consistent Matter cluster lookup
|
|
695
|
-
// Vacuum variants
|
|
696
|
-
if (['wosweeper', 'wosweepermini', 'wosweeperminipro', 'k10+', 'k10+ pro'].includes((type || '').toLowerCase())) {
|
|
697
|
-
type = 'vacuum';
|
|
698
|
-
}
|
|
699
|
-
// Window covering variants
|
|
700
|
-
if (['blindtilt', 'blind tilt', 'curtain3', 'rollershade', 'roller shade', 'worollershade', 'wo rollershade'].includes((type || '').toLowerCase())) {
|
|
701
|
-
type = 'curtain';
|
|
702
|
-
}
|
|
703
|
-
// Plug variants
|
|
704
|
-
if (['plug mini (jp)', 'plug mini (us)'].includes((type || '').toLowerCase())) {
|
|
705
|
-
type = 'plug';
|
|
706
|
-
}
|
|
707
|
-
// Meter variants
|
|
708
|
-
if (['meterplus', 'meter plus (jp)', 'meterpro', 'meterpro(co2)'].includes((type || '').toLowerCase())) {
|
|
709
|
-
type = 'meter';
|
|
710
|
-
}
|
|
711
|
-
// Relay switch variants
|
|
712
|
-
if (['relay switch 1', 'relay switch 1pm'].includes((type || '').toLowerCase())) {
|
|
713
|
-
type = 'relay';
|
|
714
|
-
}
|
|
715
|
-
// Water detector variants
|
|
716
|
-
if (['water detector'].includes((type || '').toLowerCase())) {
|
|
717
|
-
type = 'waterdetector';
|
|
718
|
-
}
|
|
719
|
-
// Fan variants
|
|
720
|
-
if (['smart fan'].includes((type || '').toLowerCase())) {
|
|
721
|
-
type = 'fan';
|
|
722
|
-
}
|
|
723
|
-
// Light variants
|
|
724
|
-
if (['strip light'].includes((type || '').toLowerCase())) {
|
|
725
|
-
type = 'lightstrip';
|
|
726
|
-
}
|
|
727
|
-
const matterSupported = !!DEVICE_MATTER_SUPPORTED[(type || '').toLowerCase()];
|
|
728
|
-
// Auto-detect Matter from Homebridge API, allow manual override via config
|
|
729
|
-
const matterAvailable = this.api?.isMatterAvailable?.() && this.api?.isMatterEnabled?.();
|
|
730
|
-
const matterEnabled = matterAvailable || !!this.config.enableMatter;
|
|
731
|
-
const useMatter = matterEnabled && matterSupported;
|
|
732
|
-
try {
|
|
733
|
-
const created = await createDevice({ id: d.id, type, name: d.name }, this.config, useMatter);
|
|
734
|
-
this.devices.push(created);
|
|
735
|
-
if (useMatter) {
|
|
736
|
-
this.log.info(`Prepared Matter accessory for ${d.id} (${type})${matterAvailable ? ' (auto-detected)' : ' (manually enabled)'}`);
|
|
737
|
-
}
|
|
738
|
-
else {
|
|
739
|
-
if (!matterEnabled) {
|
|
740
|
-
this.log.info(`Skipping Matter for ${d.id} (${type}) - Matter not available on this bridge`);
|
|
741
|
-
}
|
|
742
|
-
else if (!matterSupported) {
|
|
743
|
-
this.log.info(`Skipping Matter for ${d.id} (${type}) - device type not supported`);
|
|
744
|
-
}
|
|
745
|
-
else {
|
|
746
|
-
this.log.info(`Skipping Matter for ${d.id} (${type}) - not supported`);
|
|
747
|
-
}
|
|
748
|
-
}
|
|
749
|
-
}
|
|
750
|
-
catch (e) {
|
|
751
|
-
this.log.error(`Failed to create Matter device ${d.id}:`, e);
|
|
752
|
-
}
|
|
753
|
-
}
|
|
754
|
-
// Update hash after successfully loading devices
|
|
755
|
-
this.lastConfigHash = this.getConfigHash();
|
|
756
|
-
}
|
|
757
|
-
getConfigHash() {
|
|
758
|
-
// Create a simple hash of current device config to detect changes
|
|
759
|
-
const devices = this.config?.devices ?? [];
|
|
760
|
-
return JSON.stringify(devices.map((d) => ({
|
|
761
|
-
id: d.deviceId ?? d.id,
|
|
762
|
-
type: d.configDeviceType ?? d.type,
|
|
763
|
-
name: d.configDeviceName ?? d.name,
|
|
764
|
-
})));
|
|
765
|
-
}
|
|
766
|
-
async checkAndReloadDevices() {
|
|
767
|
-
const currentHash = this.getConfigHash();
|
|
768
|
-
if (currentHash !== this.lastConfigHash) {
|
|
769
|
-
this.log.info('[SwitchBot] Detected config changes, reloading devices...');
|
|
770
|
-
// Clear existing devices
|
|
771
|
-
this.devices = [];
|
|
772
|
-
await this.loadDevices();
|
|
773
|
-
}
|
|
774
|
-
}
|
|
775
|
-
async configureAccessory(accessory) {
|
|
776
|
-
try {
|
|
777
|
-
const uuid = accessory.UUID || accessory.UUID;
|
|
778
|
-
this.accessories.set(uuid, accessory);
|
|
779
|
-
this.log.info(`Restored cached Matter accessory ${accessory.displayName || uuid}`);
|
|
780
|
-
}
|
|
781
|
-
catch (e) {
|
|
782
|
-
this.log.warn('configureAccessory failed to restore Matter accessory', e);
|
|
783
|
-
}
|
|
784
|
-
}
|
|
785
|
-
// Homebridge calls this when restoring cached Matter accessories
|
|
786
|
-
configureMatterAccessory(accessory) {
|
|
787
|
-
try {
|
|
788
|
-
const uuid = accessory.uuid || accessory.UUID || accessory.uuid;
|
|
789
|
-
this.accessories.set(uuid, accessory);
|
|
790
|
-
this.log.info(`Restored cached Matter accessory ${accessory.displayName || uuid}`);
|
|
791
|
-
}
|
|
792
|
-
catch (e) {
|
|
793
|
-
this.log.warn('configureMatterAccessory failed to restore Matter accessory', e);
|
|
794
|
-
}
|
|
795
|
-
}
|
|
796
|
-
// Register serialized Matter accessories via Homebridge Matter API
|
|
797
|
-
async registerMatterAccessories() {
|
|
798
|
-
if (!this.api) {
|
|
799
|
-
return;
|
|
800
|
-
}
|
|
801
|
-
const matterApi = this.api.matter;
|
|
802
|
-
if (!matterApi || typeof matterApi.registerPlatformAccessories !== 'function') {
|
|
803
|
-
this.log.info('Homebridge Matter API not available; skipping Matter accessory registration');
|
|
804
|
-
return;
|
|
805
|
-
}
|
|
806
|
-
const devices = this.config?.devices ?? [];
|
|
807
|
-
const accessoriesToRegister = [];
|
|
808
|
-
// Auto-detect Matter from Homebridge API
|
|
809
|
-
const matterAvailable = this.api?.isMatterAvailable?.() && this.api?.isMatterEnabled?.();
|
|
810
|
-
const matterEnabled = matterAvailable || !!this.config.enableMatter;
|
|
811
|
-
for (const raw of devices) {
|
|
812
|
-
const d = {
|
|
813
|
-
id: raw.deviceId ?? raw.id,
|
|
814
|
-
name: raw.configDeviceName ?? raw.name,
|
|
815
|
-
type: raw.configDeviceType ?? raw.type ?? raw.deviceType ?? 'unknown',
|
|
816
|
-
};
|
|
817
|
-
if (!d.id) {
|
|
818
|
-
continue;
|
|
819
|
-
}
|
|
820
|
-
let type = d.type;
|
|
821
|
-
// Normalize device type variants for consistent Matter cluster lookup
|
|
822
|
-
// Vacuum variants
|
|
823
|
-
if (['wosweeper', 'wosweepermini', 'wosweeperminipro', 'k10+', 'k10+ pro'].includes((type || '').toLowerCase())) {
|
|
824
|
-
type = 'vacuum';
|
|
825
|
-
}
|
|
826
|
-
// Window covering variants
|
|
827
|
-
if (['blindtilt', 'blind tilt', 'curtain3', 'rollershade', 'roller shade', 'worollershade', 'wo rollershade'].includes((type || '').toLowerCase())) {
|
|
828
|
-
type = 'curtain';
|
|
829
|
-
}
|
|
830
|
-
// Plug variants
|
|
831
|
-
if (['plug mini (jp)', 'plug mini (us)'].includes((type || '').toLowerCase())) {
|
|
832
|
-
type = 'plug';
|
|
833
|
-
}
|
|
834
|
-
// Meter variants
|
|
835
|
-
if (['meterplus', 'meter plus (jp)', 'meterpro', 'meterpro(co2)'].includes((type || '').toLowerCase())) {
|
|
836
|
-
type = 'meter';
|
|
837
|
-
}
|
|
838
|
-
// Relay switch variants
|
|
839
|
-
if (['relay switch 1', 'relay switch 1pm'].includes((type || '').toLowerCase())) {
|
|
840
|
-
type = 'relay';
|
|
841
|
-
}
|
|
842
|
-
// Water detector variants
|
|
843
|
-
if (['water detector'].includes((type || '').toLowerCase())) {
|
|
844
|
-
type = 'waterdetector';
|
|
845
|
-
}
|
|
846
|
-
// Fan variants
|
|
847
|
-
if (['smart fan'].includes((type || '').toLowerCase())) {
|
|
848
|
-
type = 'fan';
|
|
849
|
-
}
|
|
850
|
-
// Light variants
|
|
851
|
-
if (['strip light'].includes((type || '').toLowerCase())) {
|
|
852
|
-
type = 'lightstrip';
|
|
853
|
-
}
|
|
854
|
-
const matterSupported = !!DEVICE_MATTER_SUPPORTED[(type || '').toLowerCase()];
|
|
855
|
-
const useMatter = matterEnabled && matterSupported;
|
|
856
|
-
if (!useMatter) {
|
|
857
|
-
continue;
|
|
858
|
-
}
|
|
859
|
-
try {
|
|
860
|
-
const created = await createDevice({ id: d.id, type, name: d.name }, this.config, true);
|
|
861
|
-
const createdDesc = created.createAccessory?.(this.api) || { id: d.id, name: d.name || type };
|
|
862
|
-
const uuid = matterApi.uuid.generate(`${d.id}`);
|
|
863
|
-
// Try to find existing restored accessory by deviceId
|
|
864
|
-
let existing;
|
|
865
|
-
for (const [, a] of Array.from(this.accessories.entries())) {
|
|
866
|
-
try {
|
|
867
|
-
if (a && a.context && a.context.deviceId === d.id) {
|
|
868
|
-
existing = a;
|
|
869
|
-
break;
|
|
870
|
-
}
|
|
871
|
-
}
|
|
872
|
-
catch (e) {
|
|
873
|
-
// ignore
|
|
874
|
-
}
|
|
875
|
-
}
|
|
876
|
-
if (existing) {
|
|
877
|
-
// Ensure context and displayName are up to date
|
|
878
|
-
// Prioritize device-specific Matter clusters (e.g., RVC for vacuum) over generic HAP-derived clusters
|
|
879
|
-
let clusters = DEVICE_MATTER_CLUSTERS[type.toLowerCase()];
|
|
880
|
-
if (!clusters) {
|
|
881
|
-
clusters = existing.clusters || createdDesc.clusters || { onOff: { onOff: false } };
|
|
882
|
-
}
|
|
883
|
-
const deviceType = resolveMatterDeviceType(matterApi, type, existing.deviceType || createdDesc.deviceType, clusters);
|
|
884
|
-
existing.context = existing.context || {};
|
|
885
|
-
existing.context.deviceId = existing.context.deviceId || d.id;
|
|
886
|
-
existing.context.type = existing.context.type || type;
|
|
887
|
-
existing.deviceType = deviceType;
|
|
888
|
-
existing.manufacturer = existing.manufacturer || createdDesc.manufacturer || 'SwitchBot';
|
|
889
|
-
existing.model = existing.model || createdDesc.model || type;
|
|
890
|
-
existing.serialNumber = existing.serialNumber || createdDesc.serialNumber || d.id;
|
|
891
|
-
existing.reachable = existing.reachable !== false;
|
|
892
|
-
existing.firmwareRevision = existing.firmwareRevision || createdDesc.firmwareRevision || '1.0.0';
|
|
893
|
-
existing.hardwareRevision = existing.hardwareRevision || createdDesc.hardwareRevision || '';
|
|
894
|
-
existing.clusters = clusters;
|
|
895
|
-
existing.handlers = createdDesc.handlers || DEVICE_MATTER_HANDLERS[type.toLowerCase()] || undefined;
|
|
896
|
-
existing.displayName = createdDesc.name || d.name || type;
|
|
897
|
-
existing.UUID = existing.UUID || existing.uuid || uuid;
|
|
898
|
-
accessoriesToRegister.push(existing);
|
|
899
|
-
this.accessories.set(existing.UUID || uuid, existing);
|
|
900
|
-
}
|
|
901
|
-
else {
|
|
902
|
-
// Prioritize device-specific Matter clusters (e.g., RVC for vacuum) over generic HAP-derived clusters
|
|
903
|
-
let clusters = DEVICE_MATTER_CLUSTERS[type.toLowerCase()];
|
|
904
|
-
if (!clusters) {
|
|
905
|
-
clusters = createdDesc.clusters || { onOff: { onOff: false } };
|
|
906
|
-
}
|
|
907
|
-
const deviceType = resolveMatterDeviceType(matterApi, type, createdDesc.deviceType, clusters);
|
|
908
|
-
const serialized = {
|
|
909
|
-
UUID: uuid,
|
|
910
|
-
displayName: createdDesc.name || d.name || type,
|
|
911
|
-
deviceType,
|
|
912
|
-
manufacturer: createdDesc.manufacturer || 'SwitchBot',
|
|
913
|
-
model: createdDesc.model || type,
|
|
914
|
-
serialNumber: createdDesc.serialNumber || d.id,
|
|
915
|
-
reachable: createdDesc.reachable !== false,
|
|
916
|
-
firmwareRevision: createdDesc.firmwareRevision || '1.0.0',
|
|
917
|
-
hardwareRevision: createdDesc.hardwareRevision || '',
|
|
918
|
-
clusters,
|
|
919
|
-
handlers: createdDesc.handlers || DEVICE_MATTER_HANDLERS[type.toLowerCase()] || undefined,
|
|
920
|
-
context: { deviceId: d.id, type, created: true },
|
|
921
|
-
};
|
|
922
|
-
accessoriesToRegister.push(serialized);
|
|
923
|
-
this.accessories.set(uuid, serialized);
|
|
924
|
-
}
|
|
925
|
-
}
|
|
926
|
-
catch (e) {
|
|
927
|
-
this.log.warn(`Failed to prepare Matter accessory for ${d.id} (${type})`, e);
|
|
928
|
-
}
|
|
929
|
-
}
|
|
930
|
-
if (accessoriesToRegister.length > 0) {
|
|
931
|
-
try {
|
|
932
|
-
await matterApi.registerPlatformAccessories(PLUGIN_NAME, PLATFORM_NAME, accessoriesToRegister);
|
|
933
|
-
this.log.info(`Registered ${accessoriesToRegister.length} Matter accessory(ies) with Homebridge`);
|
|
934
|
-
}
|
|
935
|
-
catch (e) {
|
|
936
|
-
this.log.warn('Failed to register Matter accessories', e);
|
|
937
|
-
}
|
|
938
|
-
}
|
|
939
|
-
else {
|
|
940
|
-
this.log.info('No Matter accessories to register');
|
|
941
|
-
}
|
|
942
|
-
}
|
|
943
|
-
}
|
|
944
|
-
export default SwitchBotHAPPlatform;
|
|
945
|
-
//# sourceMappingURL=platform.js.map
|