@switchbot/homebridge-switchbot 5.0.0-beta.1 → 5.0.0-beta.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/devices-matter/ColorLightAccessory.d.ts +3 -1
- package/dist/devices-matter/ColorLightAccessory.d.ts.map +1 -1
- package/dist/devices-matter/ColorLightAccessory.js +35 -36
- package/dist/devices-matter/ColorLightAccessory.js.map +1 -1
- package/dist/devices-matter/ColorTemperatureLightAccessory.d.ts +3 -1
- package/dist/devices-matter/ColorTemperatureLightAccessory.d.ts.map +1 -1
- package/dist/devices-matter/ColorTemperatureLightAccessory.js +32 -37
- package/dist/devices-matter/ColorTemperatureLightAccessory.js.map +1 -1
- package/dist/devices-matter/ContactSensorAccessory.d.ts +3 -1
- package/dist/devices-matter/ContactSensorAccessory.d.ts.map +1 -1
- package/dist/devices-matter/ContactSensorAccessory.js +16 -13
- package/dist/devices-matter/ContactSensorAccessory.js.map +1 -1
- package/dist/devices-matter/DimmableLightAccessory.d.ts +3 -1
- package/dist/devices-matter/DimmableLightAccessory.d.ts.map +1 -1
- package/dist/devices-matter/DimmableLightAccessory.js +24 -27
- package/dist/devices-matter/DimmableLightAccessory.js.map +1 -1
- package/dist/devices-matter/DoorLockAccessory.d.ts +3 -1
- package/dist/devices-matter/DoorLockAccessory.d.ts.map +1 -1
- package/dist/devices-matter/DoorLockAccessory.js +25 -22
- package/dist/devices-matter/DoorLockAccessory.js.map +1 -1
- package/dist/devices-matter/ExtendedColorLightAccessory.d.ts +3 -1
- package/dist/devices-matter/ExtendedColorLightAccessory.d.ts.map +1 -1
- package/dist/devices-matter/ExtendedColorLightAccessory.js +40 -41
- package/dist/devices-matter/ExtendedColorLightAccessory.js.map +1 -1
- package/dist/devices-matter/FanAccessory.d.ts +3 -1
- package/dist/devices-matter/FanAccessory.d.ts.map +1 -1
- package/dist/devices-matter/FanAccessory.js +31 -23
- package/dist/devices-matter/FanAccessory.js.map +1 -1
- package/dist/devices-matter/HumiditySensorAccessory.d.ts +3 -1
- package/dist/devices-matter/HumiditySensorAccessory.d.ts.map +1 -1
- package/dist/devices-matter/HumiditySensorAccessory.js +16 -15
- package/dist/devices-matter/HumiditySensorAccessory.js.map +1 -1
- package/dist/devices-matter/LeakSensorAccessory.d.ts +3 -1
- package/dist/devices-matter/LeakSensorAccessory.d.ts.map +1 -1
- package/dist/devices-matter/LeakSensorAccessory.js +16 -13
- package/dist/devices-matter/LeakSensorAccessory.js.map +1 -1
- package/dist/devices-matter/LightSensorAccessory.d.ts +3 -1
- package/dist/devices-matter/LightSensorAccessory.d.ts.map +1 -1
- package/dist/devices-matter/LightSensorAccessory.js +16 -15
- package/dist/devices-matter/LightSensorAccessory.js.map +1 -1
- package/dist/devices-matter/OccupancySensorAccessory.d.ts +3 -1
- package/dist/devices-matter/OccupancySensorAccessory.d.ts.map +1 -1
- package/dist/devices-matter/OccupancySensorAccessory.js +17 -21
- package/dist/devices-matter/OccupancySensorAccessory.js.map +1 -1
- package/dist/devices-matter/OnOffLightAccessory.d.ts +3 -1
- package/dist/devices-matter/OnOffLightAccessory.d.ts.map +1 -1
- package/dist/devices-matter/OnOffLightAccessory.js +22 -21
- package/dist/devices-matter/OnOffLightAccessory.js.map +1 -1
- package/dist/devices-matter/OnOffOutletAccessory.d.ts +3 -3
- package/dist/devices-matter/OnOffOutletAccessory.d.ts.map +1 -1
- package/dist/devices-matter/OnOffOutletAccessory.js +24 -27
- package/dist/devices-matter/OnOffOutletAccessory.js.map +1 -1
- package/dist/devices-matter/OnOffSwitchAccessory.d.ts +3 -1
- package/dist/devices-matter/OnOffSwitchAccessory.d.ts.map +1 -1
- package/dist/devices-matter/OnOffSwitchAccessory.js +18 -19
- package/dist/devices-matter/OnOffSwitchAccessory.js.map +1 -1
- package/dist/devices-matter/RoboticVacuumAccessory.d.ts +3 -1
- package/dist/devices-matter/RoboticVacuumAccessory.d.ts.map +1 -1
- package/dist/devices-matter/RoboticVacuumAccessory.js +77 -145
- package/dist/devices-matter/RoboticVacuumAccessory.js.map +1 -1
- package/dist/devices-matter/TemperatureSensorAccessory.d.ts +3 -1
- package/dist/devices-matter/TemperatureSensorAccessory.d.ts.map +1 -1
- package/dist/devices-matter/TemperatureSensorAccessory.js +18 -15
- package/dist/devices-matter/TemperatureSensorAccessory.js.map +1 -1
- package/dist/devices-matter/ThermostatAccessory.d.ts +3 -1
- package/dist/devices-matter/ThermostatAccessory.d.ts.map +1 -1
- package/dist/devices-matter/ThermostatAccessory.js +37 -29
- package/dist/devices-matter/ThermostatAccessory.js.map +1 -1
- package/dist/devices-matter/VenetianBlindAccessory.d.ts +3 -1
- package/dist/devices-matter/VenetianBlindAccessory.d.ts.map +1 -1
- package/dist/devices-matter/VenetianBlindAccessory.js +39 -40
- package/dist/devices-matter/VenetianBlindAccessory.js.map +1 -1
- package/dist/devices-matter/WindowBlindAccessory.d.ts +3 -1
- package/dist/devices-matter/WindowBlindAccessory.d.ts.map +1 -1
- package/dist/devices-matter/WindowBlindAccessory.js +36 -37
- package/dist/devices-matter/WindowBlindAccessory.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +63 -5
- package/dist/index.js.map +1 -1
- package/dist/index.test.js +7 -2
- package/dist/index.test.js.map +1 -1
- package/dist/platform-hap.d.ts.map +1 -1
- package/dist/platform-hap.js +13 -1
- package/dist/platform-hap.js.map +1 -1
- package/dist/platform-matter.d.ts +45 -0
- package/dist/platform-matter.d.ts.map +1 -1
- package/dist/platform-matter.js +607 -2
- package/dist/platform-matter.js.map +1 -1
- package/docs/variables/default.html +1 -1
- package/package.json +1 -1
- package/src/devices-matter/ColorLightAccessory.ts +37 -40
- package/src/devices-matter/ColorTemperatureLightAccessory.ts +35 -41
- package/src/devices-matter/ContactSensorAccessory.ts +18 -14
- package/src/devices-matter/DimmableLightAccessory.ts +26 -30
- package/src/devices-matter/DoorLockAccessory.ts +28 -24
- package/src/devices-matter/ExtendedColorLightAccessory.ts +43 -47
- package/src/devices-matter/FanAccessory.ts +33 -25
- package/src/devices-matter/HumiditySensorAccessory.ts +18 -16
- package/src/devices-matter/LeakSensorAccessory.ts +18 -14
- package/src/devices-matter/LightSensorAccessory.ts +18 -16
- package/src/devices-matter/OccupancySensorAccessory.ts +18 -22
- package/src/devices-matter/OnOffLightAccessory.ts +23 -23
- package/src/devices-matter/OnOffOutletAccessory.ts +25 -31
- package/src/devices-matter/OnOffSwitchAccessory.ts +20 -21
- package/src/devices-matter/RoboticVacuumAccessory.ts +79 -156
- package/src/devices-matter/TemperatureSensorAccessory.ts +20 -16
- package/src/devices-matter/ThermostatAccessory.ts +39 -34
- package/src/devices-matter/VenetianBlindAccessory.ts +41 -44
- package/src/devices-matter/WindowBlindAccessory.ts +37 -39
- package/src/index.test.ts +7 -2
- package/src/index.ts +64 -5
- package/src/platform-hap.ts +12 -1
- package/src/platform-matter.ts +647 -2
|
@@ -30,165 +30,88 @@ import { BaseMatterAccessory } from './BaseMatterAccessory.js'
|
|
|
30
30
|
export class RoboticVacuumAccessory extends BaseMatterAccessory {
|
|
31
31
|
private activeTimers: NodeJS.Timeout[] = []
|
|
32
32
|
|
|
33
|
-
constructor(api: API, log: Logger) {
|
|
34
|
-
const serialNumber = 'VACUUM-001'
|
|
33
|
+
constructor(api: API, log: Logger, opts?: Partial<import('./BaseMatterAccessory').BaseMatterAccessoryConfig & { deviceId?: string }>) {
|
|
34
|
+
const serialNumber = opts?.serialNumber ?? 'VACUUM-001'
|
|
35
|
+
const clusters = opts?.clusters ?? {
|
|
36
|
+
powerSource: {
|
|
37
|
+
status: 0,
|
|
38
|
+
order: 0,
|
|
39
|
+
description: 'Battery',
|
|
40
|
+
batPercentRemaining: 100,
|
|
41
|
+
batChargeLevel: 2,
|
|
42
|
+
batReplaceability: 1,
|
|
43
|
+
},
|
|
44
|
+
rvcRunMode: {
|
|
45
|
+
supportedModes: [
|
|
46
|
+
{ label: 'Idle', mode: 0, modeTags: [{ value: 16384 }] },
|
|
47
|
+
{ label: 'Cleaning', mode: 1, modeTags: [{ value: 16385 }] },
|
|
48
|
+
{ label: 'Mapping', mode: 2, modeTags: [{ value: 16386 }] },
|
|
49
|
+
],
|
|
50
|
+
currentMode: 0,
|
|
51
|
+
},
|
|
52
|
+
rvcCleanMode: {
|
|
53
|
+
supportedModes: [
|
|
54
|
+
{ label: 'Vacuum', mode: 0, modeTags: [{ value: 16385 }] },
|
|
55
|
+
{ label: 'Mop', mode: 1, modeTags: [{ value: 16386 }] },
|
|
56
|
+
{ label: 'Vacuum & Mop', mode: 2, modeTags: [{ value: 16385 }, { value: 16386 }] },
|
|
57
|
+
{ label: 'Deep Clean', mode: 3, modeTags: [{ value: 16384 }] },
|
|
58
|
+
{ label: 'Deep Vacuum', mode: 4, modeTags: [{ value: 16384 }, { value: 16385 }] },
|
|
59
|
+
{ label: 'Deep Mop', mode: 5, modeTags: [{ value: 16384 }, { value: 16386 }] },
|
|
60
|
+
{ label: 'Quick Clean', mode: 6, modeTags: [{ value: 1 }, { value: 16385 }] },
|
|
61
|
+
{ label: 'Max Clean', mode: 7, modeTags: [{ value: 7 }, { value: 16385 }] },
|
|
62
|
+
{ label: 'Min Clean', mode: 8, modeTags: [{ value: 6 }, { value: 16385 }] },
|
|
63
|
+
{ label: 'Quiet Vacuum', mode: 9, modeTags: [{ value: 2 }, { value: 16385 }] },
|
|
64
|
+
{ label: 'Quiet Mop', mode: 10, modeTags: [{ value: 2 }, { value: 16386 }] },
|
|
65
|
+
{ label: 'Night Mode', mode: 11, modeTags: [{ value: 8 }, { value: 16385 }] },
|
|
66
|
+
{ label: 'Eco Vacuum', mode: 12, modeTags: [{ value: 4 }, { value: 16385 }] },
|
|
67
|
+
{ label: 'Eco Mop', mode: 13, modeTags: [{ value: 4 }, { value: 16386 }] },
|
|
68
|
+
{ label: 'Auto', mode: 14, modeTags: [{ value: 0 }, { value: 16385 }] },
|
|
69
|
+
],
|
|
70
|
+
currentMode: 0,
|
|
71
|
+
},
|
|
72
|
+
rvcOperationalState: {
|
|
73
|
+
operationalStateList: [
|
|
74
|
+
{ operationalStateId: 0 },
|
|
75
|
+
{ operationalStateId: 1 },
|
|
76
|
+
{ operationalStateId: 2 },
|
|
77
|
+
{ operationalStateId: 3 },
|
|
78
|
+
{ operationalStateId: 64 },
|
|
79
|
+
{ operationalStateId: 65 },
|
|
80
|
+
{ operationalStateId: 66 },
|
|
81
|
+
],
|
|
82
|
+
operationalState: 66,
|
|
83
|
+
},
|
|
84
|
+
serviceArea: {
|
|
85
|
+
supportedMaps: [],
|
|
86
|
+
supportedAreas: [
|
|
87
|
+
{ areaId: 0, mapId: null, areaInfo: { locationInfo: { locationName: 'Living Room', floorNumber: 0, areaType: 7 }, landmarkInfo: null } },
|
|
88
|
+
{ areaId: 1, mapId: null, areaInfo: { locationInfo: { locationName: 'Kitchen', floorNumber: 0, areaType: 10 }, landmarkInfo: null } },
|
|
89
|
+
{ areaId: 2, mapId: null, areaInfo: { locationInfo: { locationName: 'Bedroom', floorNumber: 0, areaType: 2 }, landmarkInfo: null } },
|
|
90
|
+
{ areaId: 3, mapId: null, areaInfo: { locationInfo: { locationName: 'Bathroom', floorNumber: 0, areaType: 6 }, landmarkInfo: null } },
|
|
91
|
+
],
|
|
92
|
+
selectedAreas: [0, 1, 2, 3],
|
|
93
|
+
},
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const handlers = opts?.handlers ?? {
|
|
97
|
+
rvcRunMode: { changeToMode: async (request: MatterRequests.ChangeToMode) => this.handleChangeRunMode(request) },
|
|
98
|
+
rvcCleanMode: { changeToMode: async (request: MatterRequests.ChangeToMode) => this.handleChangeCleanMode(request) },
|
|
99
|
+
rvcOperationalState: { pause: async () => this.handlePause(), stop: async () => this.handleStop(), start: async () => this.handleStart(), resume: async () => this.handleResume(), goHome: async () => this.handleGoHome() },
|
|
100
|
+
serviceArea: { selectAreas: async (request: MatterRequests.SelectAreas) => this.handleSelectAreas(request), skipArea: async (request: MatterRequests.SkipArea) => this.handleSkipArea(request) },
|
|
101
|
+
}
|
|
102
|
+
|
|
35
103
|
super(api, log, {
|
|
36
|
-
uuid: api.matter.uuid.generate(serialNumber),
|
|
37
|
-
displayName: 'Robot Vacuum',
|
|
104
|
+
uuid: opts?.uuid ?? api.matter.uuid.generate(serialNumber),
|
|
105
|
+
displayName: opts?.displayName ?? 'Robot Vacuum',
|
|
38
106
|
deviceType: api.matter.deviceTypes.RoboticVacuumCleaner,
|
|
39
107
|
serialNumber,
|
|
40
|
-
manufacturer: 'Homebridge Matter',
|
|
41
|
-
model: 'HB-MATTER-VACUUM-ROBOTIC',
|
|
42
|
-
firmwareRevision: '2.0.0',
|
|
43
|
-
hardwareRevision: '1.0.0',
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
powerSource: {
|
|
48
|
-
status: 0, // 0 = Active
|
|
49
|
-
order: 0, // Primary power source
|
|
50
|
-
description: 'Battery',
|
|
51
|
-
batPercentRemaining: 100, // 0-200, where 200 = 100% (0.5% increments)
|
|
52
|
-
batChargeLevel: 2, // 0 = Ok, 1 = Warning, 2 = Critical
|
|
53
|
-
batReplaceability: 1, // 0 = Unspecified, 1 = Not replaceable, 2 = User replaceable, 3 = Factory replaceable
|
|
54
|
-
},
|
|
55
|
-
|
|
56
|
-
// Run Mode: Controls what the vacuum is doing (Idle, Cleaning, Mapping)
|
|
57
|
-
rvcRunMode: {
|
|
58
|
-
supportedModes: [
|
|
59
|
-
{ label: 'Idle', mode: 0, modeTags: [{ value: 16384 }] }, // RvcRunMode.ModeTag.Idle
|
|
60
|
-
{ label: 'Cleaning', mode: 1, modeTags: [{ value: 16385 }] }, // RvcRunMode.ModeTag.Cleaning
|
|
61
|
-
{ label: 'Mapping', mode: 2, modeTags: [{ value: 16386 }] }, // RvcRunMode.ModeTag.Mapping
|
|
62
|
-
],
|
|
63
|
-
currentMode: 0,
|
|
64
|
-
},
|
|
65
|
-
|
|
66
|
-
// Clean Mode: Controls HOW the vacuum cleans
|
|
67
|
-
// You can combine semantic tags (0-9) with functional tags (16384-16386)
|
|
68
|
-
// Available semantic tags: Auto, Quick, Quiet, LowNoise, LowEnergy, Vacation, Min, Max, Night, Day
|
|
69
|
-
// Available functional tags: DeepClean, Vacuum, Mop
|
|
70
|
-
rvcCleanMode: {
|
|
71
|
-
supportedModes: [
|
|
72
|
-
// Basic functional modes
|
|
73
|
-
{ label: 'Vacuum', mode: 0, modeTags: [{ value: 16385 }] }, // Vacuum
|
|
74
|
-
{ label: 'Mop', mode: 1, modeTags: [{ value: 16386 }] }, // Mop
|
|
75
|
-
{ label: 'Vacuum & Mop', mode: 2, modeTags: [{ value: 16385 }, { value: 16386 }] }, // Both
|
|
76
|
-
|
|
77
|
-
// Deep clean modes
|
|
78
|
-
{ label: 'Deep Clean', mode: 3, modeTags: [{ value: 16384 }] }, // DeepClean
|
|
79
|
-
{ label: 'Deep Vacuum', mode: 4, modeTags: [{ value: 16384 }, { value: 16385 }] }, // DeepClean + Vacuum
|
|
80
|
-
{ label: 'Deep Mop', mode: 5, modeTags: [{ value: 16384 }, { value: 16386 }] }, // DeepClean + Mop
|
|
81
|
-
|
|
82
|
-
// Intensity modes
|
|
83
|
-
{ label: 'Quick Clean', mode: 6, modeTags: [{ value: 1 }, { value: 16385 }] }, // Quick + Vacuum
|
|
84
|
-
{ label: 'Max Clean', mode: 7, modeTags: [{ value: 7 }, { value: 16385 }] }, // Max + Vacuum
|
|
85
|
-
{ label: 'Min Clean', mode: 8, modeTags: [{ value: 6 }, { value: 16385 }] }, // Min + Vacuum
|
|
86
|
-
|
|
87
|
-
// Quiet modes
|
|
88
|
-
{ label: 'Quiet Vacuum', mode: 9, modeTags: [{ value: 2 }, { value: 16385 }] }, // Quiet + Vacuum
|
|
89
|
-
{ label: 'Quiet Mop', mode: 10, modeTags: [{ value: 2 }, { value: 16386 }] }, // Quiet + Mop
|
|
90
|
-
{ label: 'Night Mode', mode: 11, modeTags: [{ value: 8 }, { value: 16385 }] }, // Night + Vacuum
|
|
91
|
-
|
|
92
|
-
// Energy efficient
|
|
93
|
-
{ label: 'Eco Vacuum', mode: 12, modeTags: [{ value: 4 }, { value: 16385 }] }, // LowEnergy + Vacuum
|
|
94
|
-
{ label: 'Eco Mop', mode: 13, modeTags: [{ value: 4 }, { value: 16386 }] }, // LowEnergy + Mop
|
|
95
|
-
|
|
96
|
-
// Auto mode
|
|
97
|
-
{ label: 'Auto', mode: 14, modeTags: [{ value: 0 }, { value: 16385 }] }, // Auto + Vacuum
|
|
98
|
-
],
|
|
99
|
-
currentMode: 0, // start with basic Vacuum
|
|
100
|
-
},
|
|
101
|
-
|
|
102
|
-
// Operational State: Current state (Stopped, Running, Paused, Error, etc.)
|
|
103
|
-
rvcOperationalState: {
|
|
104
|
-
operationalStateList: [
|
|
105
|
-
{ operationalStateId: 0 }, // stopped (standard label from Matter spec)
|
|
106
|
-
{ operationalStateId: 1 }, // running
|
|
107
|
-
{ operationalStateId: 2 }, // paused
|
|
108
|
-
{ operationalStateId: 3 }, // error
|
|
109
|
-
{ operationalStateId: 64 }, // seeking charger
|
|
110
|
-
{ operationalStateId: 65 }, // charging
|
|
111
|
-
{ operationalStateId: 66 }, // docked
|
|
112
|
-
],
|
|
113
|
-
operationalState: 66, // start docked
|
|
114
|
-
},
|
|
115
|
-
|
|
116
|
-
// Service Area: Room/zone selection for targeted cleaning
|
|
117
|
-
serviceArea: {
|
|
118
|
-
supportedMaps: [], // empty array - we don't use map features
|
|
119
|
-
supportedAreas: [
|
|
120
|
-
{
|
|
121
|
-
areaId: 0,
|
|
122
|
-
mapId: null, // required: must be null when supportedMaps is empty
|
|
123
|
-
areaInfo: {
|
|
124
|
-
locationInfo: {
|
|
125
|
-
locationName: 'Living Room',
|
|
126
|
-
floorNumber: 0,
|
|
127
|
-
areaType: 7, // AreaNamespaceTag.LivingRoom
|
|
128
|
-
},
|
|
129
|
-
landmarkInfo: null,
|
|
130
|
-
},
|
|
131
|
-
},
|
|
132
|
-
{
|
|
133
|
-
areaId: 1,
|
|
134
|
-
mapId: null,
|
|
135
|
-
areaInfo: {
|
|
136
|
-
locationInfo: {
|
|
137
|
-
locationName: 'Kitchen',
|
|
138
|
-
floorNumber: 0,
|
|
139
|
-
areaType: 10, // AreaNamespaceTag.Kitchen
|
|
140
|
-
},
|
|
141
|
-
landmarkInfo: null,
|
|
142
|
-
},
|
|
143
|
-
},
|
|
144
|
-
{
|
|
145
|
-
areaId: 2,
|
|
146
|
-
mapId: null,
|
|
147
|
-
areaInfo: {
|
|
148
|
-
locationInfo: {
|
|
149
|
-
locationName: 'Bedroom',
|
|
150
|
-
floorNumber: 0,
|
|
151
|
-
areaType: 2, // AreaNamespaceTag.Bedroom
|
|
152
|
-
},
|
|
153
|
-
landmarkInfo: null,
|
|
154
|
-
},
|
|
155
|
-
},
|
|
156
|
-
{
|
|
157
|
-
areaId: 3,
|
|
158
|
-
mapId: null,
|
|
159
|
-
areaInfo: {
|
|
160
|
-
locationInfo: {
|
|
161
|
-
locationName: 'Bathroom',
|
|
162
|
-
floorNumber: 0,
|
|
163
|
-
areaType: 6, // AreaNamespaceTag.Bathroom
|
|
164
|
-
},
|
|
165
|
-
landmarkInfo: null,
|
|
166
|
-
},
|
|
167
|
-
},
|
|
168
|
-
],
|
|
169
|
-
selectedAreas: [0, 1, 2, 3], // all areas selected by default
|
|
170
|
-
},
|
|
171
|
-
},
|
|
172
|
-
|
|
173
|
-
handlers: {
|
|
174
|
-
rvcRunMode: {
|
|
175
|
-
changeToMode: async (request: MatterRequests.ChangeToMode) => this.handleChangeRunMode(request),
|
|
176
|
-
},
|
|
177
|
-
rvcCleanMode: {
|
|
178
|
-
changeToMode: async (request: MatterRequests.ChangeToMode) => this.handleChangeCleanMode(request),
|
|
179
|
-
},
|
|
180
|
-
rvcOperationalState: {
|
|
181
|
-
pause: async () => this.handlePause(),
|
|
182
|
-
stop: async () => this.handleStop(),
|
|
183
|
-
start: async () => this.handleStart(),
|
|
184
|
-
resume: async () => this.handleResume(),
|
|
185
|
-
goHome: async () => this.handleGoHome(),
|
|
186
|
-
},
|
|
187
|
-
serviceArea: {
|
|
188
|
-
selectAreas: async (request: MatterRequests.SelectAreas) => this.handleSelectAreas(request),
|
|
189
|
-
skipArea: async (request: MatterRequests.SkipArea) => this.handleSkipArea(request),
|
|
190
|
-
},
|
|
191
|
-
},
|
|
108
|
+
manufacturer: opts?.manufacturer ?? 'Homebridge Matter',
|
|
109
|
+
model: opts?.model ?? 'HB-MATTER-VACUUM-ROBOTIC',
|
|
110
|
+
firmwareRevision: opts?.firmwareRevision ?? '2.0.0',
|
|
111
|
+
hardwareRevision: opts?.hardwareRevision ?? '1.0.0',
|
|
112
|
+
clusters,
|
|
113
|
+
handlers,
|
|
114
|
+
context: { deviceId: opts?.deviceId, ...(opts?.context ?? {}) },
|
|
192
115
|
})
|
|
193
116
|
|
|
194
117
|
this.logInfo('initialized and ready.')
|
|
@@ -7,25 +7,29 @@ import type { API, Logger } from 'homebridge'
|
|
|
7
7
|
import { BaseMatterAccessory } from './BaseMatterAccessory.js'
|
|
8
8
|
|
|
9
9
|
export class TemperatureSensorAccessory extends BaseMatterAccessory {
|
|
10
|
-
constructor(api: API, log: Logger) {
|
|
11
|
-
const serialNumber = 'SENSOR-004'
|
|
10
|
+
constructor(api: API, log: Logger, opts?: Partial<import('./BaseMatterAccessory').BaseMatterAccessoryConfig & { deviceId?: string }>) {
|
|
11
|
+
const serialNumber = opts?.serialNumber ?? 'SENSOR-004'
|
|
12
|
+
const displayName = opts?.displayName ?? 'Temperature Sensor'
|
|
13
|
+
const manufacturer = opts?.manufacturer ?? 'Homebridge Matter'
|
|
14
|
+
const model = opts?.model ?? 'HB-MATTER-SENSOR-TEMPERATURE'
|
|
15
|
+
const firmwareRevision = opts?.firmwareRevision ?? '2.0.0'
|
|
16
|
+
const hardwareRevision = opts?.hardwareRevision ?? '1.0.0'
|
|
17
|
+
|
|
18
|
+
const clusters = opts?.clusters ?? {
|
|
19
|
+
temperatureMeasurement: { measuredValue: 2100, minMeasuredValue: -5000, maxMeasuredValue: 10000 },
|
|
20
|
+
}
|
|
21
|
+
|
|
12
22
|
super(api, log, {
|
|
13
|
-
uuid: api.matter.uuid.generate(serialNumber),
|
|
14
|
-
displayName
|
|
23
|
+
uuid: opts?.uuid ?? api.matter.uuid.generate(serialNumber),
|
|
24
|
+
displayName,
|
|
15
25
|
deviceType: api.matter.deviceTypes.TemperatureSensor,
|
|
16
26
|
serialNumber,
|
|
17
|
-
manufacturer
|
|
18
|
-
model
|
|
19
|
-
firmwareRevision
|
|
20
|
-
hardwareRevision
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
temperatureMeasurement: {
|
|
24
|
-
measuredValue: 2100, // 21.00°C (in hundredths)
|
|
25
|
-
minMeasuredValue: -5000,
|
|
26
|
-
maxMeasuredValue: 10000,
|
|
27
|
-
},
|
|
28
|
-
},
|
|
27
|
+
manufacturer,
|
|
28
|
+
model,
|
|
29
|
+
firmwareRevision,
|
|
30
|
+
hardwareRevision,
|
|
31
|
+
clusters,
|
|
32
|
+
context: { deviceId: opts?.deviceId, ...(opts?.context ?? {}) },
|
|
29
33
|
})
|
|
30
34
|
|
|
31
35
|
this.logInfo('initialized.')
|
|
@@ -7,44 +7,49 @@ import type { API, Logger, MatterRequests } from 'homebridge'
|
|
|
7
7
|
import { BaseMatterAccessory } from './BaseMatterAccessory.js'
|
|
8
8
|
|
|
9
9
|
export class ThermostatAccessory extends BaseMatterAccessory {
|
|
10
|
-
constructor(api: API, log: Logger) {
|
|
11
|
-
const serialNumber = 'THERMOSTAT-001'
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
manufacturer: 'Homebridge Matter',
|
|
18
|
-
model: 'HB-MATTER-THERMOSTAT',
|
|
19
|
-
firmwareRevision: '2.0.0',
|
|
20
|
-
hardwareRevision: '1.0.0',
|
|
10
|
+
constructor(api: API, log: Logger, opts?: Partial<import('./BaseMatterAccessory').BaseMatterAccessoryConfig & { deviceId?: string }>) {
|
|
11
|
+
const serialNumber = opts?.serialNumber ?? 'THERMOSTAT-001'
|
|
12
|
+
const displayName = opts?.displayName ?? 'Thermostat'
|
|
13
|
+
const manufacturer = opts?.manufacturer ?? 'Homebridge Matter'
|
|
14
|
+
const model = opts?.model ?? 'HB-MATTER-THERMOSTAT'
|
|
15
|
+
const firmwareRevision = opts?.firmwareRevision ?? '2.0.0'
|
|
16
|
+
const hardwareRevision = opts?.hardwareRevision ?? '1.0.0'
|
|
21
17
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
},
|
|
18
|
+
const clusters = opts?.clusters ?? {
|
|
19
|
+
thermostat: {
|
|
20
|
+
localTemperature: 2100,
|
|
21
|
+
occupiedHeatingSetpoint: 2000,
|
|
22
|
+
occupiedCoolingSetpoint: 2400,
|
|
23
|
+
minHeatSetpointLimit: 700,
|
|
24
|
+
maxHeatSetpointLimit: 3000,
|
|
25
|
+
minCoolSetpointLimit: 1600,
|
|
26
|
+
maxCoolSetpointLimit: 3200,
|
|
27
|
+
controlSequenceOfOperation: 4,
|
|
28
|
+
systemMode: 0,
|
|
34
29
|
},
|
|
30
|
+
}
|
|
35
31
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
occupiedHeatingSetpointChange: async (request: { occupiedHeatingSetpoint: number, oldOccupiedHeatingSetpoint: number }) =>
|
|
43
|
-
this.handleOccupiedHeatingSetpointChange(request),
|
|
44
|
-
occupiedCoolingSetpointChange: async (request: { occupiedCoolingSetpoint: number, oldOccupiedCoolingSetpoint: number }) =>
|
|
45
|
-
this.handleOccupiedCoolingSetpointChange(request),
|
|
46
|
-
},
|
|
32
|
+
const handlers = opts?.handlers ?? {
|
|
33
|
+
thermostat: {
|
|
34
|
+
setpointRaiseLower: async (request: MatterRequests.SetpointRaiseLower) => this.handleSetpointRaiseLower(request),
|
|
35
|
+
systemModeChange: async (request: { systemMode: number, oldSystemMode: number }) => this.handleSystemModeChange(request),
|
|
36
|
+
occupiedHeatingSetpointChange: async (request: { occupiedHeatingSetpoint: number, oldOccupiedHeatingSetpoint: number }) => this.handleOccupiedHeatingSetpointChange(request),
|
|
37
|
+
occupiedCoolingSetpointChange: async (request: { occupiedCoolingSetpoint: number, oldOccupiedCoolingSetpoint: number }) => this.handleOccupiedCoolingSetpointChange(request),
|
|
47
38
|
},
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
super(api, log, {
|
|
42
|
+
uuid: opts?.uuid ?? api.matter.uuid.generate(serialNumber),
|
|
43
|
+
displayName,
|
|
44
|
+
deviceType: api.matter.deviceTypes.Thermostat,
|
|
45
|
+
serialNumber,
|
|
46
|
+
manufacturer,
|
|
47
|
+
model,
|
|
48
|
+
firmwareRevision,
|
|
49
|
+
hardwareRevision,
|
|
50
|
+
clusters,
|
|
51
|
+
handlers,
|
|
52
|
+
context: { deviceId: opts?.deviceId, ...(opts?.context ?? {}) },
|
|
48
53
|
})
|
|
49
54
|
|
|
50
55
|
this.logInfo('initialized.')
|
|
@@ -8,54 +8,51 @@ import type { API, Logger, MatterRequests } from 'homebridge'
|
|
|
8
8
|
import { BaseMatterAccessory } from './BaseMatterAccessory.js'
|
|
9
9
|
|
|
10
10
|
export class VenetianBlindAccessory extends BaseMatterAccessory {
|
|
11
|
-
constructor(api: API, log: Logger) {
|
|
12
|
-
const serialNumber = 'BLIND-002'
|
|
11
|
+
constructor(api: API, log: Logger, opts?: Partial<import('./BaseMatterAccessory').BaseMatterAccessoryConfig & { deviceId?: string }>) {
|
|
12
|
+
const serialNumber = opts?.serialNumber ?? 'BLIND-002'
|
|
13
|
+
|
|
14
|
+
const clusters = opts?.clusters ?? {
|
|
15
|
+
windowCovering: {
|
|
16
|
+
targetPositionLiftPercent100ths: 5000,
|
|
17
|
+
currentPositionLiftPercent100ths: 5000,
|
|
18
|
+
targetPositionTiltPercent100ths: 5000,
|
|
19
|
+
currentPositionTiltPercent100ths: 5000,
|
|
20
|
+
operationalStatus: { global: 0, lift: 0, tilt: 0 },
|
|
21
|
+
endProductType: 8,
|
|
22
|
+
configStatus: {
|
|
23
|
+
operational: true,
|
|
24
|
+
onlineReserved: true,
|
|
25
|
+
liftMovementReversed: false,
|
|
26
|
+
liftPositionAware: true,
|
|
27
|
+
tiltPositionAware: true,
|
|
28
|
+
liftEncoderControlled: true,
|
|
29
|
+
tiltEncoderControlled: true,
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const handlers = opts?.handlers ?? {
|
|
35
|
+
windowCovering: {
|
|
36
|
+
goToLiftPercentage: async (request: MatterRequests.GoToLiftPercentage) => this.handleGoToLift(request),
|
|
37
|
+
goToTiltPercentage: async (request: MatterRequests.GoToTiltPercentage) => this.handleGoToTilt(request),
|
|
38
|
+
upOrOpen: async () => this.handleUpOrOpen(),
|
|
39
|
+
downOrClose: async () => this.handleDownOrClose(),
|
|
40
|
+
stopMotion: async () => this.handleStop(),
|
|
41
|
+
},
|
|
42
|
+
}
|
|
13
43
|
|
|
14
44
|
super(api, log, {
|
|
15
|
-
uuid: api.matter.uuid.generate(serialNumber),
|
|
16
|
-
displayName: 'Venetian Blind (Tilt)',
|
|
45
|
+
uuid: opts?.uuid ?? api.matter.uuid.generate(serialNumber),
|
|
46
|
+
displayName: opts?.displayName ?? 'Venetian Blind (Tilt)',
|
|
17
47
|
deviceType: api.matter.deviceTypes.WindowCovering,
|
|
18
48
|
serialNumber,
|
|
19
|
-
manufacturer: 'Homebridge Matter',
|
|
20
|
-
model: 'HB-MATTER-BLIND-VENETIAN',
|
|
21
|
-
firmwareRevision: '2.0.0',
|
|
22
|
-
hardwareRevision: '1.0.0',
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
targetPositionLiftPercent100ths: 5000,
|
|
27
|
-
currentPositionLiftPercent100ths: 5000,
|
|
28
|
-
targetPositionTiltPercent100ths: 5000,
|
|
29
|
-
currentPositionTiltPercent100ths: 5000,
|
|
30
|
-
operationalStatus: {
|
|
31
|
-
global: 0,
|
|
32
|
-
lift: 0,
|
|
33
|
-
tilt: 0,
|
|
34
|
-
},
|
|
35
|
-
endProductType: 8,
|
|
36
|
-
configStatus: {
|
|
37
|
-
operational: true,
|
|
38
|
-
onlineReserved: true,
|
|
39
|
-
liftMovementReversed: false,
|
|
40
|
-
liftPositionAware: true,
|
|
41
|
-
tiltPositionAware: true,
|
|
42
|
-
liftEncoderControlled: true,
|
|
43
|
-
tiltEncoderControlled: true,
|
|
44
|
-
},
|
|
45
|
-
},
|
|
46
|
-
},
|
|
47
|
-
|
|
48
|
-
handlers: {
|
|
49
|
-
windowCovering: {
|
|
50
|
-
goToLiftPercentage: async (request: MatterRequests.GoToLiftPercentage) =>
|
|
51
|
-
this.handleGoToLift(request),
|
|
52
|
-
goToTiltPercentage: async (request: MatterRequests.GoToTiltPercentage) =>
|
|
53
|
-
this.handleGoToTilt(request),
|
|
54
|
-
upOrOpen: async () => this.handleUpOrOpen(),
|
|
55
|
-
downOrClose: async () => this.handleDownOrClose(),
|
|
56
|
-
stopMotion: async () => this.handleStop(),
|
|
57
|
-
},
|
|
58
|
-
},
|
|
49
|
+
manufacturer: opts?.manufacturer ?? 'Homebridge Matter',
|
|
50
|
+
model: opts?.model ?? 'HB-MATTER-BLIND-VENETIAN',
|
|
51
|
+
firmwareRevision: opts?.firmwareRevision ?? '2.0.0',
|
|
52
|
+
hardwareRevision: opts?.hardwareRevision ?? '1.0.0',
|
|
53
|
+
clusters,
|
|
54
|
+
handlers,
|
|
55
|
+
context: { deviceId: opts?.deviceId, ...(opts?.context ?? {}) },
|
|
59
56
|
})
|
|
60
57
|
|
|
61
58
|
this.logInfo('initialized.')
|
|
@@ -8,50 +8,48 @@ import type { API, Logger, MatterRequests } from 'homebridge'
|
|
|
8
8
|
import { BaseMatterAccessory } from './BaseMatterAccessory.js'
|
|
9
9
|
|
|
10
10
|
export class WindowBlindAccessory extends BaseMatterAccessory {
|
|
11
|
-
constructor(api: API, log: Logger) {
|
|
12
|
-
const serialNumber = 'BLIND-001'
|
|
11
|
+
constructor(api: API, log: Logger, opts?: Partial<import('./BaseMatterAccessory').BaseMatterAccessoryConfig & { deviceId?: string }>) {
|
|
12
|
+
const serialNumber = opts?.serialNumber ?? 'BLIND-001'
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
operationalStatus: {
|
|
29
|
-
global: 0,
|
|
30
|
-
lift: 0,
|
|
31
|
-
tilt: 0,
|
|
32
|
-
},
|
|
33
|
-
endProductType: 0,
|
|
34
|
-
configStatus: {
|
|
35
|
-
operational: true,
|
|
36
|
-
onlineReserved: true,
|
|
37
|
-
liftMovementReversed: false,
|
|
38
|
-
liftPositionAware: true,
|
|
39
|
-
tiltPositionAware: false,
|
|
40
|
-
liftEncoderControlled: true,
|
|
41
|
-
tiltEncoderControlled: false,
|
|
42
|
-
},
|
|
14
|
+
const clusters = opts?.clusters ?? {
|
|
15
|
+
windowCovering: {
|
|
16
|
+
targetPositionLiftPercent100ths: 5000,
|
|
17
|
+
currentPositionLiftPercent100ths: 5000,
|
|
18
|
+
operationalStatus: { global: 0, lift: 0, tilt: 0 },
|
|
19
|
+
endProductType: 0,
|
|
20
|
+
configStatus: {
|
|
21
|
+
operational: true,
|
|
22
|
+
onlineReserved: true,
|
|
23
|
+
liftMovementReversed: false,
|
|
24
|
+
liftPositionAware: true,
|
|
25
|
+
tiltPositionAware: false,
|
|
26
|
+
liftEncoderControlled: true,
|
|
27
|
+
tiltEncoderControlled: false,
|
|
43
28
|
},
|
|
44
29
|
},
|
|
30
|
+
}
|
|
45
31
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
stopMotion: async () => this.handleStop(),
|
|
53
|
-
},
|
|
32
|
+
const handlers = opts?.handlers ?? {
|
|
33
|
+
windowCovering: {
|
|
34
|
+
goToLiftPercentage: async (request: MatterRequests.GoToLiftPercentage) => this.handleGoToLift(request),
|
|
35
|
+
upOrOpen: async () => this.handleUpOrOpen(),
|
|
36
|
+
downOrClose: async () => this.handleDownOrClose(),
|
|
37
|
+
stopMotion: async () => this.handleStop(),
|
|
54
38
|
},
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
super(api, log, {
|
|
42
|
+
uuid: opts?.uuid ?? api.matter.uuid.generate(serialNumber),
|
|
43
|
+
displayName: opts?.displayName ?? 'Window Blind',
|
|
44
|
+
deviceType: api.matter.deviceTypes.WindowCovering,
|
|
45
|
+
serialNumber,
|
|
46
|
+
manufacturer: opts?.manufacturer ?? 'Homebridge Matter',
|
|
47
|
+
model: opts?.model ?? 'HB-MATTER-BLIND-WINDOW',
|
|
48
|
+
firmwareRevision: opts?.firmwareRevision ?? '2.0.0',
|
|
49
|
+
hardwareRevision: opts?.hardwareRevision ?? '1.0.0',
|
|
50
|
+
clusters,
|
|
51
|
+
handlers,
|
|
52
|
+
context: { deviceId: opts?.deviceId, ...(opts?.context ?? {}) },
|
|
55
53
|
})
|
|
56
54
|
|
|
57
55
|
this.logInfo('initialized.')
|
package/src/index.test.ts
CHANGED
|
@@ -3,7 +3,6 @@ import type { API } from 'homebridge'
|
|
|
3
3
|
import { describe, expect, it, vi } from 'vitest'
|
|
4
4
|
|
|
5
5
|
import registerPlatform from './index.js'
|
|
6
|
-
import { SwitchBotHAPPlatform } from './platform-hap.js'
|
|
7
6
|
import { PLATFORM_NAME, PLUGIN_NAME } from './settings.js'
|
|
8
7
|
|
|
9
8
|
describe('index.ts', () => {
|
|
@@ -14,6 +13,12 @@ describe('index.ts', () => {
|
|
|
14
13
|
|
|
15
14
|
registerPlatform(api)
|
|
16
15
|
|
|
17
|
-
|
|
16
|
+
// The platform registration now uses a runtime proxy/delegate constructor so
|
|
17
|
+
// assert the call happened and the third argument is a constructor function.
|
|
18
|
+
expect(api.registerPlatform).toHaveBeenCalled()
|
|
19
|
+
const callArgs = (api.registerPlatform as any).mock.calls[0]
|
|
20
|
+
expect(callArgs[0]).toBe(PLUGIN_NAME)
|
|
21
|
+
expect(callArgs[1]).toBe(PLATFORM_NAME)
|
|
22
|
+
expect(typeof callArgs[2]).toBe('function')
|
|
18
23
|
})
|
|
19
24
|
})
|
package/src/index.ts
CHANGED
|
@@ -10,9 +10,68 @@ import { PLATFORM_NAME, PLUGIN_NAME } from './settings.js'
|
|
|
10
10
|
|
|
11
11
|
// Register our platform with homebridge.
|
|
12
12
|
export default (api: API): void => {
|
|
13
|
-
//
|
|
14
|
-
//
|
|
15
|
-
|
|
16
|
-
//
|
|
17
|
-
|
|
13
|
+
// Register a single proxy platform class. Homebridge will instantiate this class
|
|
14
|
+
// with (log, config, api) and we delegate to the appropriate actual platform
|
|
15
|
+
// implementation (HAP or Matter) based on runtime detection. This avoids
|
|
16
|
+
// registering two platforms and possible conflicts.
|
|
17
|
+
// Compact helper to determine if Matter should be used.
|
|
18
|
+
const detectMatter = (apiObj: API): boolean => {
|
|
19
|
+
try {
|
|
20
|
+
const maybe = (apiObj as any).isMatterEnabled
|
|
21
|
+
if (typeof maybe === 'function') {
|
|
22
|
+
return Boolean(maybe.call(apiObj))
|
|
23
|
+
}
|
|
24
|
+
if (typeof maybe !== 'undefined') {
|
|
25
|
+
return Boolean(maybe)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const server = (apiObj as any).server ?? (apiObj as any).homebridgeServer ?? (apiObj as any).homebridge_server
|
|
29
|
+
const serverMaybe = server?.isMatterEnabled
|
|
30
|
+
if (typeof serverMaybe === 'function') {
|
|
31
|
+
return Boolean(serverMaybe.call(server))
|
|
32
|
+
}
|
|
33
|
+
if (typeof server?.isMatterEnabled !== 'undefined') {
|
|
34
|
+
return Boolean(server.isMatterEnabled)
|
|
35
|
+
}
|
|
36
|
+
} catch (e) {
|
|
37
|
+
// ignore
|
|
38
|
+
}
|
|
39
|
+
return false
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
class SwitchBotPlatformProxy {
|
|
43
|
+
private delegate: any
|
|
44
|
+
|
|
45
|
+
constructor(public readonly log: any, public readonly config: any, public readonly api: API) {
|
|
46
|
+
const isMatter = detectMatter(this.api)
|
|
47
|
+
this.log.info?.(`Homebridge SwitchBot Plugin initializing in ${isMatter ? 'Matter' : 'HAP'} mode.`)
|
|
48
|
+
const PlatformCtor = isMatter ? SwitchBotMatterPlatform : SwitchBotHAPPlatform
|
|
49
|
+
this.delegate = new PlatformCtor(this.log, this.config, this.api)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Homebridge lifecycle hooks we expect to be called — delegate if available.
|
|
53
|
+
configureAccessory(accessory: any): void {
|
|
54
|
+
if (typeof this.delegate.configureAccessory === 'function') {
|
|
55
|
+
return this.delegate.configureAccessory(accessory)
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
configureMatterAccessory?(accessory: any): void {
|
|
60
|
+
if (typeof this.delegate.configureMatterAccessory === 'function') {
|
|
61
|
+
return this.delegate.configureMatterAccessory(accessory)
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Expose common properties via getters for external access
|
|
66
|
+
get accessories(): any {
|
|
67
|
+
return this.delegate?.accessories
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
get matterAccessories(): any {
|
|
71
|
+
return this.delegate?.matterAccessories
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Register the proxy class with Homebridge. Homebridge will call `new SwitchBotPlatformProxy(log, config, api)`
|
|
76
|
+
api.registerPlatform(PLUGIN_NAME, PLATFORM_NAME, SwitchBotPlatformProxy as any)
|
|
18
77
|
}
|