@switchbot/homebridge-switchbot 5.0.0-beta.0 → 5.0.0-beta.2

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.
Files changed (110) hide show
  1. package/dist/devices-matter/ColorLightAccessory.d.ts +3 -1
  2. package/dist/devices-matter/ColorLightAccessory.d.ts.map +1 -1
  3. package/dist/devices-matter/ColorLightAccessory.js +35 -36
  4. package/dist/devices-matter/ColorLightAccessory.js.map +1 -1
  5. package/dist/devices-matter/ColorTemperatureLightAccessory.d.ts +3 -1
  6. package/dist/devices-matter/ColorTemperatureLightAccessory.d.ts.map +1 -1
  7. package/dist/devices-matter/ColorTemperatureLightAccessory.js +32 -37
  8. package/dist/devices-matter/ColorTemperatureLightAccessory.js.map +1 -1
  9. package/dist/devices-matter/ContactSensorAccessory.d.ts +3 -1
  10. package/dist/devices-matter/ContactSensorAccessory.d.ts.map +1 -1
  11. package/dist/devices-matter/ContactSensorAccessory.js +16 -13
  12. package/dist/devices-matter/ContactSensorAccessory.js.map +1 -1
  13. package/dist/devices-matter/DimmableLightAccessory.d.ts +3 -1
  14. package/dist/devices-matter/DimmableLightAccessory.d.ts.map +1 -1
  15. package/dist/devices-matter/DimmableLightAccessory.js +24 -27
  16. package/dist/devices-matter/DimmableLightAccessory.js.map +1 -1
  17. package/dist/devices-matter/DoorLockAccessory.d.ts +3 -1
  18. package/dist/devices-matter/DoorLockAccessory.d.ts.map +1 -1
  19. package/dist/devices-matter/DoorLockAccessory.js +25 -22
  20. package/dist/devices-matter/DoorLockAccessory.js.map +1 -1
  21. package/dist/devices-matter/ExtendedColorLightAccessory.d.ts +3 -1
  22. package/dist/devices-matter/ExtendedColorLightAccessory.d.ts.map +1 -1
  23. package/dist/devices-matter/ExtendedColorLightAccessory.js +40 -41
  24. package/dist/devices-matter/ExtendedColorLightAccessory.js.map +1 -1
  25. package/dist/devices-matter/FanAccessory.d.ts +3 -1
  26. package/dist/devices-matter/FanAccessory.d.ts.map +1 -1
  27. package/dist/devices-matter/FanAccessory.js +31 -23
  28. package/dist/devices-matter/FanAccessory.js.map +1 -1
  29. package/dist/devices-matter/HumiditySensorAccessory.d.ts +3 -1
  30. package/dist/devices-matter/HumiditySensorAccessory.d.ts.map +1 -1
  31. package/dist/devices-matter/HumiditySensorAccessory.js +16 -15
  32. package/dist/devices-matter/HumiditySensorAccessory.js.map +1 -1
  33. package/dist/devices-matter/LeakSensorAccessory.d.ts +3 -1
  34. package/dist/devices-matter/LeakSensorAccessory.d.ts.map +1 -1
  35. package/dist/devices-matter/LeakSensorAccessory.js +16 -13
  36. package/dist/devices-matter/LeakSensorAccessory.js.map +1 -1
  37. package/dist/devices-matter/LightSensorAccessory.d.ts +3 -1
  38. package/dist/devices-matter/LightSensorAccessory.d.ts.map +1 -1
  39. package/dist/devices-matter/LightSensorAccessory.js +16 -15
  40. package/dist/devices-matter/LightSensorAccessory.js.map +1 -1
  41. package/dist/devices-matter/OccupancySensorAccessory.d.ts +3 -1
  42. package/dist/devices-matter/OccupancySensorAccessory.d.ts.map +1 -1
  43. package/dist/devices-matter/OccupancySensorAccessory.js +17 -21
  44. package/dist/devices-matter/OccupancySensorAccessory.js.map +1 -1
  45. package/dist/devices-matter/OnOffLightAccessory.d.ts +3 -1
  46. package/dist/devices-matter/OnOffLightAccessory.d.ts.map +1 -1
  47. package/dist/devices-matter/OnOffLightAccessory.js +22 -21
  48. package/dist/devices-matter/OnOffLightAccessory.js.map +1 -1
  49. package/dist/devices-matter/OnOffOutletAccessory.d.ts +3 -3
  50. package/dist/devices-matter/OnOffOutletAccessory.d.ts.map +1 -1
  51. package/dist/devices-matter/OnOffOutletAccessory.js +24 -27
  52. package/dist/devices-matter/OnOffOutletAccessory.js.map +1 -1
  53. package/dist/devices-matter/OnOffSwitchAccessory.d.ts +3 -1
  54. package/dist/devices-matter/OnOffSwitchAccessory.d.ts.map +1 -1
  55. package/dist/devices-matter/OnOffSwitchAccessory.js +18 -19
  56. package/dist/devices-matter/OnOffSwitchAccessory.js.map +1 -1
  57. package/dist/devices-matter/RoboticVacuumAccessory.d.ts +3 -1
  58. package/dist/devices-matter/RoboticVacuumAccessory.d.ts.map +1 -1
  59. package/dist/devices-matter/RoboticVacuumAccessory.js +77 -145
  60. package/dist/devices-matter/RoboticVacuumAccessory.js.map +1 -1
  61. package/dist/devices-matter/TemperatureSensorAccessory.d.ts +3 -1
  62. package/dist/devices-matter/TemperatureSensorAccessory.d.ts.map +1 -1
  63. package/dist/devices-matter/TemperatureSensorAccessory.js +18 -15
  64. package/dist/devices-matter/TemperatureSensorAccessory.js.map +1 -1
  65. package/dist/devices-matter/ThermostatAccessory.d.ts +3 -1
  66. package/dist/devices-matter/ThermostatAccessory.d.ts.map +1 -1
  67. package/dist/devices-matter/ThermostatAccessory.js +37 -29
  68. package/dist/devices-matter/ThermostatAccessory.js.map +1 -1
  69. package/dist/devices-matter/VenetianBlindAccessory.d.ts +3 -1
  70. package/dist/devices-matter/VenetianBlindAccessory.d.ts.map +1 -1
  71. package/dist/devices-matter/VenetianBlindAccessory.js +39 -40
  72. package/dist/devices-matter/VenetianBlindAccessory.js.map +1 -1
  73. package/dist/devices-matter/WindowBlindAccessory.d.ts +3 -1
  74. package/dist/devices-matter/WindowBlindAccessory.d.ts.map +1 -1
  75. package/dist/devices-matter/WindowBlindAccessory.js +36 -37
  76. package/dist/devices-matter/WindowBlindAccessory.js.map +1 -1
  77. package/dist/platform-hap.d.ts.map +1 -1
  78. package/dist/platform-hap.js +18 -1
  79. package/dist/platform-hap.js.map +1 -1
  80. package/dist/platform-matter.d.ts.map +1 -1
  81. package/dist/platform-matter.js +17 -0
  82. package/dist/platform-matter.js.map +1 -1
  83. package/dist/utils.d.ts +5 -0
  84. package/dist/utils.d.ts.map +1 -1
  85. package/dist/utils.js +43 -0
  86. package/dist/utils.js.map +1 -1
  87. package/docs/variables/default.html +1 -1
  88. package/package.json +1 -1
  89. package/src/devices-matter/ColorLightAccessory.ts +37 -40
  90. package/src/devices-matter/ColorTemperatureLightAccessory.ts +35 -41
  91. package/src/devices-matter/ContactSensorAccessory.ts +18 -14
  92. package/src/devices-matter/DimmableLightAccessory.ts +26 -30
  93. package/src/devices-matter/DoorLockAccessory.ts +28 -24
  94. package/src/devices-matter/ExtendedColorLightAccessory.ts +43 -47
  95. package/src/devices-matter/FanAccessory.ts +33 -25
  96. package/src/devices-matter/HumiditySensorAccessory.ts +18 -16
  97. package/src/devices-matter/LeakSensorAccessory.ts +18 -14
  98. package/src/devices-matter/LightSensorAccessory.ts +18 -16
  99. package/src/devices-matter/OccupancySensorAccessory.ts +18 -22
  100. package/src/devices-matter/OnOffLightAccessory.ts +23 -23
  101. package/src/devices-matter/OnOffOutletAccessory.ts +25 -31
  102. package/src/devices-matter/OnOffSwitchAccessory.ts +20 -21
  103. package/src/devices-matter/RoboticVacuumAccessory.ts +79 -156
  104. package/src/devices-matter/TemperatureSensorAccessory.ts +20 -16
  105. package/src/devices-matter/ThermostatAccessory.ts +39 -34
  106. package/src/devices-matter/VenetianBlindAccessory.ts +41 -44
  107. package/src/devices-matter/WindowBlindAccessory.ts +37 -39
  108. package/src/platform-hap.ts +16 -1
  109. package/src/platform-matter.ts +15 -0
  110. package/src/utils.ts +49 -0
@@ -7,30 +7,29 @@ import type { API, Logger } from 'homebridge'
7
7
  import { BaseMatterAccessory } from './BaseMatterAccessory.js'
8
8
 
9
9
  export class OnOffSwitchAccessory extends BaseMatterAccessory {
10
- constructor(api: API, log: Logger) {
11
- const serialNumber = 'SWITCH-001'
10
+ constructor(api: API, log: Logger, opts?: Partial<import('./BaseMatterAccessory').BaseMatterAccessoryConfig & { deviceId?: string }>) {
11
+ const serialNumber = opts?.serialNumber ?? 'SWITCH-001'
12
+ const displayName = opts?.displayName ?? 'On/Off Switch'
13
+ const manufacturer = opts?.manufacturer ?? 'Homebridge Matter'
14
+ const model = opts?.model ?? 'HB-MATTER-SWITCH-ON-OFF'
15
+ const firmwareRevision = opts?.firmwareRevision ?? '2.0.0'
16
+ const hardwareRevision = opts?.hardwareRevision ?? '1.0.0'
17
+
18
+ const clusters = opts?.clusters ?? { onOff: { onOff: false } }
19
+ const handlers = opts?.handlers ?? { onOff: { on: async () => this.handleOn(), off: async () => this.handleOff() } }
20
+
12
21
  super(api, log, {
13
- uuid: api.matter.uuid.generate(serialNumber),
14
- displayName: 'On/Off Switch',
22
+ uuid: opts?.uuid ?? api.matter.uuid.generate(serialNumber),
23
+ displayName,
15
24
  deviceType: api.matter.deviceTypes.OnOffSwitch,
16
25
  serialNumber,
17
- manufacturer: 'Homebridge Matter',
18
- model: 'HB-MATTER-SWITCH-ON-OFF',
19
- firmwareRevision: '2.0.0',
20
- hardwareRevision: '1.0.0',
21
-
22
- clusters: {
23
- onOff: {
24
- onOff: false,
25
- },
26
- },
27
-
28
- handlers: {
29
- onOff: {
30
- on: async () => this.handleOn(),
31
- off: async () => this.handleOff(),
32
- },
33
- },
26
+ manufacturer,
27
+ model,
28
+ firmwareRevision,
29
+ hardwareRevision,
30
+ clusters,
31
+ handlers,
32
+ context: { deviceId: opts?.deviceId, ...(opts?.context ?? {}) },
34
33
  })
35
34
 
36
35
  this.logInfo('initialized.')
@@ -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
- clusters: {
46
- // Power Source: Battery status and percentage
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: 'Temperature Sensor',
23
+ uuid: opts?.uuid ?? api.matter.uuid.generate(serialNumber),
24
+ displayName,
15
25
  deviceType: api.matter.deviceTypes.TemperatureSensor,
16
26
  serialNumber,
17
- manufacturer: 'Homebridge Matter',
18
- model: 'HB-MATTER-SENSOR-TEMPERATURE',
19
- firmwareRevision: '2.0.0',
20
- hardwareRevision: '1.0.0',
21
-
22
- clusters: {
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
- super(api, log, {
13
- uuid: api.matter.uuid.generate(serialNumber),
14
- displayName: 'Thermostat',
15
- deviceType: api.matter.deviceTypes.Thermostat,
16
- serialNumber,
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
- clusters: {
23
- thermostat: {
24
- localTemperature: 2100, // 21.00°C
25
- occupiedHeatingSetpoint: 2000,
26
- occupiedCoolingSetpoint: 2400,
27
- minHeatSetpointLimit: 700,
28
- maxHeatSetpointLimit: 3000,
29
- minCoolSetpointLimit: 1600,
30
- maxCoolSetpointLimit: 3200,
31
- controlSequenceOfOperation: 4, // cooling and heating
32
- systemMode: 0, // off
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
- handlers: {
37
- thermostat: {
38
- setpointRaiseLower: async (request: MatterRequests.SetpointRaiseLower) =>
39
- this.handleSetpointRaiseLower(request),
40
- systemModeChange: async (request: { systemMode: number, oldSystemMode: number }) =>
41
- this.handleSystemModeChange(request),
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
- clusters: {
25
- windowCovering: {
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
- super(api, log, {
15
- uuid: api.matter.uuid.generate(serialNumber),
16
- displayName: 'Window Blind',
17
- deviceType: api.matter.deviceTypes.WindowCovering,
18
- serialNumber,
19
- manufacturer: 'Homebridge Matter',
20
- model: 'HB-MATTER-BLIND-WINDOW',
21
- firmwareRevision: '2.0.0',
22
- hardwareRevision: '1.0.0',
23
-
24
- clusters: {
25
- windowCovering: {
26
- targetPositionLiftPercent100ths: 5000,
27
- currentPositionLiftPercent100ths: 5000,
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
- handlers: {
47
- windowCovering: {
48
- goToLiftPercentage: async (request: MatterRequests.GoToLiftPercentage) =>
49
- this.handleGoToLift(request),
50
- upOrOpen: async () => this.handleUpOrOpen(),
51
- downOrClose: async () => this.handleDownOrClose(),
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.')
@@ -55,7 +55,7 @@ import { TV } from './irdevice/tv.js'
55
55
  import { VacuumCleaner } from './irdevice/vacuumcleaner.js'
56
56
  import { WaterHeater } from './irdevice/waterheater.js'
57
57
  import { PLATFORM_NAME, PLUGIN_NAME } from './settings.js'
58
- import { formatDeviceIdAsMac, isBlindTiltDevice, isCurtainDevice, safeStringify, sleep } from './utils.js'
58
+ import { cleanDeviceConfig, formatDeviceIdAsMac, isBlindTiltDevice, isCurtainDevice, safeStringify, sleep } from './utils.js'
59
59
 
60
60
  /**
61
61
  * HomebridgePlatform
@@ -119,6 +119,21 @@ export class SwitchBotHAPPlatform implements DynamicPlatformPlugin {
119
119
  devices: config.devices as { deviceId: string }[],
120
120
  }
121
121
 
122
+ // Normalize deviceConfig to remove UI-inserted defaults (lots of false/empty values)
123
+ try {
124
+ if ((this.config as any).options) {
125
+ const cleaned = cleanDeviceConfig((this.config as any).options.deviceConfig)
126
+ if (cleaned) {
127
+ ;(this.config as any).options.deviceConfig = cleaned
128
+ } else {
129
+ // remove the empty deviceConfig so downstream checks treat it as absent
130
+ delete (this.config as any).options.deviceConfig
131
+ }
132
+ }
133
+ } catch (e) {
134
+ this.debugErrorLog(`Failed to clean deviceConfig: ${e}`)
135
+ }
136
+
122
137
  // Plugin Configuration
123
138
  this.getPlatformLogSettings()
124
139
  this.getPlatformRateSettings()