homebridge 2.0.0-alpha.8 → 2.0.0-alpha.80

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 (112) hide show
  1. package/README.md +1 -1
  2. package/bin/homebridge.js +22 -0
  3. package/dist/api.d.ts +250 -3
  4. package/dist/api.d.ts.map +1 -1
  5. package/dist/api.js +92 -0
  6. package/dist/api.js.map +1 -1
  7. package/dist/bridgeService.d.ts +11 -3
  8. package/dist/bridgeService.d.ts.map +1 -1
  9. package/dist/bridgeService.js +9 -5
  10. package/dist/bridgeService.js.map +1 -1
  11. package/dist/childBridgeFork.d.ts +30 -3
  12. package/dist/childBridgeFork.d.ts.map +1 -1
  13. package/dist/childBridgeFork.js +295 -5
  14. package/dist/childBridgeFork.js.map +1 -1
  15. package/dist/childBridgeService.d.ts +22 -0
  16. package/dist/childBridgeService.d.ts.map +1 -1
  17. package/dist/childBridgeService.js +85 -26
  18. package/dist/childBridgeService.js.map +1 -1
  19. package/dist/cli.d.ts.map +1 -1
  20. package/dist/cli.js +2 -1
  21. package/dist/cli.js.map +1 -1
  22. package/dist/externalPortService.d.ts +27 -6
  23. package/dist/externalPortService.d.ts.map +1 -1
  24. package/dist/externalPortService.js +73 -7
  25. package/dist/externalPortService.js.map +1 -1
  26. package/dist/index.d.ts +49 -3
  27. package/dist/index.d.ts.map +1 -1
  28. package/dist/index.js +15 -0
  29. package/dist/index.js.map +1 -1
  30. package/dist/ipcService.d.ts +20 -0
  31. package/dist/ipcService.d.ts.map +1 -1
  32. package/dist/ipcService.js.map +1 -1
  33. package/dist/logger.d.ts +6 -0
  34. package/dist/logger.d.ts.map +1 -1
  35. package/dist/logger.js +8 -0
  36. package/dist/logger.js.map +1 -1
  37. package/dist/matter/index.d.ts +123 -0
  38. package/dist/matter/index.d.ts.map +1 -0
  39. package/dist/matter/index.js +19 -0
  40. package/dist/matter/index.js.map +1 -0
  41. package/dist/matter/matterAccessoryCache.d.ts +79 -0
  42. package/dist/matter/matterAccessoryCache.d.ts.map +1 -0
  43. package/dist/matter/matterAccessoryCache.js +175 -0
  44. package/dist/matter/matterAccessoryCache.js.map +1 -0
  45. package/dist/matter/matterBehaviors.d.ts +178 -0
  46. package/dist/matter/matterBehaviors.d.ts.map +1 -0
  47. package/dist/matter/matterBehaviors.js +655 -0
  48. package/dist/matter/matterBehaviors.js.map +1 -0
  49. package/dist/matter/matterConfigValidator.d.ts +81 -0
  50. package/dist/matter/matterConfigValidator.d.ts.map +1 -0
  51. package/dist/matter/matterConfigValidator.js +240 -0
  52. package/dist/matter/matterConfigValidator.js.map +1 -0
  53. package/dist/matter/matterErrorHandler.d.ts +106 -0
  54. package/dist/matter/matterErrorHandler.d.ts.map +1 -0
  55. package/dist/matter/matterErrorHandler.js +495 -0
  56. package/dist/matter/matterErrorHandler.js.map +1 -0
  57. package/dist/matter/matterLogFormatter.d.ts +19 -0
  58. package/dist/matter/matterLogFormatter.d.ts.map +1 -0
  59. package/dist/matter/matterLogFormatter.js +136 -0
  60. package/dist/matter/matterLogFormatter.js.map +1 -0
  61. package/dist/matter/matterNetworkMonitor.d.ts +68 -0
  62. package/dist/matter/matterNetworkMonitor.d.ts.map +1 -0
  63. package/dist/matter/matterNetworkMonitor.js +249 -0
  64. package/dist/matter/matterNetworkMonitor.js.map +1 -0
  65. package/dist/matter/matterServer.d.ts +649 -0
  66. package/dist/matter/matterServer.d.ts.map +1 -0
  67. package/dist/matter/matterServer.js +1548 -0
  68. package/dist/matter/matterServer.js.map +1 -0
  69. package/dist/matter/matterServerHelpers.d.ts +81 -0
  70. package/dist/matter/matterServerHelpers.d.ts.map +1 -0
  71. package/dist/matter/matterServerHelpers.js +305 -0
  72. package/dist/matter/matterServerHelpers.js.map +1 -0
  73. package/dist/matter/matterSharedTypes.d.ts +170 -0
  74. package/dist/matter/matterSharedTypes.d.ts.map +1 -0
  75. package/dist/matter/matterSharedTypes.js +52 -0
  76. package/dist/matter/matterSharedTypes.js.map +1 -0
  77. package/dist/matter/matterStorage.d.ts +128 -0
  78. package/dist/matter/matterStorage.d.ts.map +1 -0
  79. package/dist/matter/matterStorage.js +415 -0
  80. package/dist/matter/matterStorage.js.map +1 -0
  81. package/dist/matter/matterTypes.d.ts +658 -0
  82. package/dist/matter/matterTypes.d.ts.map +1 -0
  83. package/dist/matter/matterTypes.js +174 -0
  84. package/dist/matter/matterTypes.js.map +1 -0
  85. package/dist/platformAccessory.d.ts +1 -0
  86. package/dist/platformAccessory.d.ts.map +1 -1
  87. package/dist/platformAccessory.js +8 -1
  88. package/dist/platformAccessory.js.map +1 -1
  89. package/dist/plugin.d.ts +0 -1
  90. package/dist/plugin.d.ts.map +1 -1
  91. package/dist/plugin.js +5 -8
  92. package/dist/plugin.js.map +1 -1
  93. package/dist/pluginManager.d.ts.map +1 -1
  94. package/dist/pluginManager.js +22 -21
  95. package/dist/pluginManager.js.map +1 -1
  96. package/dist/server.d.ts +23 -1
  97. package/dist/server.d.ts.map +1 -1
  98. package/dist/server.js +391 -10
  99. package/dist/server.js.map +1 -1
  100. package/dist/storageService.js +8 -8
  101. package/dist/storageService.js.map +1 -1
  102. package/dist/user.d.ts +1 -0
  103. package/dist/user.d.ts.map +1 -1
  104. package/dist/user.js +10 -7
  105. package/dist/user.js.map +1 -1
  106. package/dist/util/mac.d.ts.map +1 -1
  107. package/dist/util/mac.js +2 -2
  108. package/dist/util/mac.js.map +1 -1
  109. package/dist/version.js +2 -2
  110. package/dist/version.js.map +1 -1
  111. package/package.json +21 -20
  112. package/bin/homebridge +0 -19
@@ -0,0 +1,655 @@
1
+ /**
2
+ * Custom Matter Behavior Classes for Homebridge
3
+ *
4
+ * These custom behaviors extend the base Matter.js behaviors and override
5
+ * command methods to allow plugins to inject custom handlers.
6
+ *
7
+ * Note: Only clusters with user-triggered commands need custom behaviors.
8
+ * Read-only clusters (like sensors) don't need custom behaviors since
9
+ * they only report state, they don't receive commands.
10
+ */
11
+ import { ColorControlServer, DoorLockServer, FanControlServer, IdentifyServer, LevelControlServer, OnOffServer, RvcCleanModeServer, RvcOperationalStateServer, RvcRunModeServer, ServiceAreaServer, ThermostatServer, WindowCoveringBaseServer, } from '@matter/main/behaviors';
12
+ import { RvcOperationalState } from '@matter/main/clusters';
13
+ import { Logger } from '../logger.js';
14
+ import { clusterNames } from './matterTypes.js';
15
+ const log = Logger.withPrefix('Matter/Behaviours');
16
+ /**
17
+ * Command names for each cluster
18
+ * Provides type safety and autocomplete for command names
19
+ */
20
+ const commandNames = {
21
+ OnOff: {
22
+ on: 'on',
23
+ off: 'off',
24
+ toggle: 'toggle',
25
+ },
26
+ LevelControl: {
27
+ moveToLevel: 'moveToLevel',
28
+ moveToLevelWithOnOff: 'moveToLevelWithOnOff',
29
+ move: 'move',
30
+ step: 'step',
31
+ stop: 'stop',
32
+ },
33
+ WindowCovering: {
34
+ upOrOpen: 'upOrOpen',
35
+ downOrClose: 'downOrClose',
36
+ stopMotion: 'stopMotion',
37
+ goToLiftPercentage: 'goToLiftPercentage',
38
+ goToTiltPercentage: 'goToTiltPercentage',
39
+ },
40
+ FanControl: {
41
+ step: 'step',
42
+ fanModeChange: 'fanModeChange',
43
+ percentSettingChange: 'percentSettingChange',
44
+ },
45
+ DoorLock: {
46
+ lockDoor: 'lockDoor',
47
+ unlockDoor: 'unlockDoor',
48
+ },
49
+ Thermostat: {
50
+ setpointRaiseLower: 'setpointRaiseLower',
51
+ systemModeChange: 'systemModeChange',
52
+ occupiedHeatingSetpointChange: 'occupiedHeatingSetpointChange',
53
+ occupiedCoolingSetpointChange: 'occupiedCoolingSetpointChange',
54
+ },
55
+ Identify: {
56
+ identify: 'identify',
57
+ },
58
+ ColorControl: {
59
+ moveToColorTemperatureLogic: 'moveToColorTemperatureLogic',
60
+ moveToHueAndSaturationLogic: 'moveToHueAndSaturationLogic',
61
+ moveToColorLogic: 'moveToColorLogic',
62
+ moveToHueLogic: 'moveToHueLogic',
63
+ moveToSaturationLogic: 'moveToSaturationLogic',
64
+ stopAllColorMovement: 'stopAllColorMovement',
65
+ },
66
+ RvcOperationalState: {
67
+ pause: 'pause',
68
+ resume: 'resume',
69
+ goHome: 'goHome',
70
+ },
71
+ RvcRunMode: {
72
+ changeToMode: 'changeToMode',
73
+ },
74
+ RvcCleanMode: {
75
+ changeToMode: 'changeToMode',
76
+ },
77
+ ServiceArea: {
78
+ selectAreas: 'selectAreas',
79
+ skipArea: 'skipArea',
80
+ },
81
+ };
82
+ /**
83
+ * Store for custom handlers
84
+ * Maps endpoint ID -> cluster name -> command name -> handler
85
+ */
86
+ const handlerStore = new Map();
87
+ /**
88
+ * Store for accessory references
89
+ * This allows handlers to update cached clusters after state changes
90
+ */
91
+ let accessoriesMap = null;
92
+ /**
93
+ * Set the accessories map reference for cache syncing
94
+ */
95
+ export function setAccessoriesMap(map) {
96
+ accessoriesMap = map;
97
+ }
98
+ /**
99
+ * Sync endpoint state back to cached clusters
100
+ * Ensures state changes from handlers persist across restarts
101
+ *
102
+ * @param endpointId - Unique endpoint identifier (accessory UUID)
103
+ * @param clusterName - Name of the Matter cluster
104
+ * @param attributes - Cluster attributes to sync
105
+ */
106
+ function syncEndpointStateToCache(endpointId, clusterName, attributes) {
107
+ if (!accessoriesMap) {
108
+ return;
109
+ }
110
+ const accessory = accessoriesMap.get(endpointId);
111
+ if (!accessory) {
112
+ return;
113
+ }
114
+ // Update the cached clusters with the new state
115
+ if (!accessory.clusters[clusterName]) {
116
+ accessory.clusters[clusterName] = {};
117
+ }
118
+ accessory.clusters[clusterName] = {
119
+ ...accessory.clusters[clusterName],
120
+ ...attributes,
121
+ };
122
+ log.debug(`Synced ${clusterName} state to cache for ${endpointId}:`, attributes);
123
+ }
124
+ /**
125
+ * Register a handler for a specific endpoint/cluster/command
126
+ *
127
+ * @param endpointId - Unique endpoint identifier (typically the accessory UUID)
128
+ * @param clusterName - Name of the Matter cluster (e.g., 'onOff', 'levelControl')
129
+ * @param commandName - Name of the command method (e.g., 'on', 'off', 'moveToLevel')
130
+ * @param handler - Callback function to execute when the command is received
131
+ *
132
+ * @example
133
+ * ```typescript
134
+ * registerHandler('my-light-uuid', 'onOff', 'on', async () => {
135
+ * console.log('Light turned on!')
136
+ * })
137
+ * ```
138
+ */
139
+ export function registerHandler(endpointId, clusterName, commandName, handler) {
140
+ if (!handlerStore.has(endpointId)) {
141
+ handlerStore.set(endpointId, new Map());
142
+ }
143
+ const endpointHandlers = handlerStore.get(endpointId);
144
+ if (!endpointHandlers.has(clusterName)) {
145
+ endpointHandlers.set(clusterName, new Map());
146
+ }
147
+ const clusterHandlers = endpointHandlers.get(clusterName);
148
+ clusterHandlers.set(commandName, handler);
149
+ log.debug(`Registered handler for ${endpointId}.${clusterName}.${commandName}`);
150
+ }
151
+ /**
152
+ * Get a handler for a specific endpoint/cluster/command
153
+ */
154
+ function getHandler(endpointId, clusterName, commandName) {
155
+ return handlerStore.get(endpointId)?.get(clusterName)?.get(commandName);
156
+ }
157
+ /**
158
+ * Optional methods that are called internally by Matter.js
159
+ * These are typically handled by their combined counterparts (e.g., moveToHueAndSaturationLogic)
160
+ */
161
+ const OPTIONAL_METHODS = new Set([
162
+ commandNames.ColorControl.moveToHueLogic,
163
+ commandNames.ColorControl.moveToSaturationLogic,
164
+ commandNames.ColorControl.stopAllColorMovement,
165
+ ]);
166
+ /**
167
+ * Execute a handler with consistent error handling and logging
168
+ *
169
+ * @param endpointId - Unique endpoint identifier (accessory UUID)
170
+ * @param clusterName - Name of the Matter cluster
171
+ * @param commandName - Name of the command method
172
+ * @param request - Optional request data passed to the handler
173
+ */
174
+ function executeHandler(endpointId, clusterName, commandName, request) {
175
+ const handler = getHandler(endpointId, clusterName, commandName);
176
+ log.debug(`${clusterName}.${commandName} called for endpoint ${endpointId}`);
177
+ if (handler) {
178
+ handler(request);
179
+ log.debug(` ✓ Plugin handler for ${endpointId}.${clusterName}.${commandName} executed successfully`);
180
+ }
181
+ else if (!OPTIONAL_METHODS.has(commandName)) {
182
+ // warn about missing handlers, except for optional methods
183
+ log.warn(` ⚠ No handler registered for ${endpointId}.${clusterName}.${commandName}`);
184
+ }
185
+ }
186
+ /**
187
+ * Custom OnOff Server that calls plugin handlers
188
+ */
189
+ export class HomebridgeOnOffServer extends OnOffServer {
190
+ on() {
191
+ const endpointId = this.endpoint.id;
192
+ executeHandler(endpointId, clusterNames.OnOff, commandNames.OnOff.on);
193
+ const result = super.on();
194
+ syncEndpointStateToCache(endpointId, clusterNames.OnOff, { onOff: true });
195
+ return result;
196
+ }
197
+ off() {
198
+ const endpointId = this.endpoint.id;
199
+ executeHandler(endpointId, clusterNames.OnOff, commandNames.OnOff.off);
200
+ const result = super.off();
201
+ syncEndpointStateToCache(endpointId, clusterNames.OnOff, { onOff: false });
202
+ return result;
203
+ }
204
+ toggle() {
205
+ const endpointId = this.endpoint.id;
206
+ executeHandler(endpointId, clusterNames.OnOff, commandNames.OnOff.toggle);
207
+ const result = super.toggle();
208
+ // Read the new state from the endpoint and sync to cache
209
+ const newState = this.state.onOff;
210
+ syncEndpointStateToCache(endpointId, clusterNames.OnOff, { onOff: newState });
211
+ return result;
212
+ }
213
+ }
214
+ /**
215
+ * Custom LevelControl Server that calls plugin handlers
216
+ */
217
+ export class HomebridgeLevelControlServer extends LevelControlServer {
218
+ moveToLevel(request) {
219
+ const endpointId = this.endpoint.id;
220
+ executeHandler(endpointId, clusterNames.LevelControl, commandNames.LevelControl.moveToLevel, request);
221
+ const result = super.moveToLevel(request);
222
+ syncEndpointStateToCache(endpointId, clusterNames.LevelControl, { currentLevel: request.level });
223
+ return result;
224
+ }
225
+ moveToLevelWithOnOff(request) {
226
+ const endpointId = this.endpoint.id;
227
+ // Try specific handler first, fall back to moveToLevel handler
228
+ const handler = getHandler(endpointId, clusterNames.LevelControl, commandNames.LevelControl.moveToLevelWithOnOff)
229
+ || getHandler(endpointId, clusterNames.LevelControl, commandNames.LevelControl.moveToLevel);
230
+ log.debug(`LevelControl.moveToLevelWithOnOff called for endpoint ${endpointId} with level ${request.level}`);
231
+ if (handler) {
232
+ handler(request);
233
+ log.debug(` ✓ Plugin handler for ${endpointId}.LevelControl.moveToLevelWithOnOff executed successfully`);
234
+ }
235
+ else {
236
+ log.warn(` ⚠ No handler registered for ${endpointId}.levelControl.moveToLevelWithOnOff or moveToLevel`);
237
+ }
238
+ const result = super.moveToLevelWithOnOff(request);
239
+ syncEndpointStateToCache(endpointId, clusterNames.LevelControl, { currentLevel: request.level });
240
+ return result;
241
+ }
242
+ move(request) {
243
+ const endpointId = this.endpoint.id;
244
+ executeHandler(endpointId, clusterNames.LevelControl, commandNames.LevelControl.move, request);
245
+ return super.move(request);
246
+ }
247
+ step(request) {
248
+ const endpointId = this.endpoint.id;
249
+ executeHandler(endpointId, clusterNames.LevelControl, commandNames.LevelControl.step, request);
250
+ return super.step(request);
251
+ }
252
+ stop(request) {
253
+ const endpointId = this.endpoint.id;
254
+ executeHandler(endpointId, clusterNames.LevelControl, commandNames.LevelControl.stop, request);
255
+ return super.stop(request);
256
+ }
257
+ }
258
+ /**
259
+ * Custom WindowCovering Server that calls plugin handlers
260
+ */
261
+ export class HomebridgeWindowCoveringServer extends WindowCoveringBaseServer {
262
+ upOrOpen() {
263
+ const endpointId = this.endpoint.id;
264
+ executeHandler(endpointId, clusterNames.WindowCovering, commandNames.WindowCovering.upOrOpen);
265
+ const result = super.upOrOpen();
266
+ // Sync state to cache - window covering opening
267
+ const currentState = this.state;
268
+ const stateUpdate = {};
269
+ if (currentState.targetPositionLiftPercent100ths !== undefined) {
270
+ stateUpdate.targetPositionLiftPercent100ths = currentState.targetPositionLiftPercent100ths;
271
+ }
272
+ if (currentState.currentPositionLiftPercent100ths !== undefined) {
273
+ stateUpdate.currentPositionLiftPercent100ths = currentState.currentPositionLiftPercent100ths;
274
+ }
275
+ syncEndpointStateToCache(endpointId, clusterNames.WindowCovering, stateUpdate);
276
+ return result;
277
+ }
278
+ downOrClose() {
279
+ const endpointId = this.endpoint.id;
280
+ executeHandler(endpointId, clusterNames.WindowCovering, commandNames.WindowCovering.downOrClose);
281
+ const result = super.downOrClose();
282
+ // Sync state to cache - window covering closing
283
+ const currentState = this.state;
284
+ const stateUpdate = {};
285
+ if (currentState.targetPositionLiftPercent100ths !== undefined) {
286
+ stateUpdate.targetPositionLiftPercent100ths = currentState.targetPositionLiftPercent100ths;
287
+ }
288
+ if (currentState.currentPositionLiftPercent100ths !== undefined) {
289
+ stateUpdate.currentPositionLiftPercent100ths = currentState.currentPositionLiftPercent100ths;
290
+ }
291
+ syncEndpointStateToCache(endpointId, clusterNames.WindowCovering, stateUpdate);
292
+ return result;
293
+ }
294
+ stopMotion() {
295
+ const endpointId = this.endpoint.id;
296
+ executeHandler(endpointId, clusterNames.WindowCovering, commandNames.WindowCovering.stopMotion);
297
+ const result = super.stopMotion();
298
+ // Sync state to cache - window covering stopped
299
+ const currentState = this.state;
300
+ const stateUpdate = {};
301
+ if (currentState.targetPositionLiftPercent100ths !== undefined) {
302
+ stateUpdate.targetPositionLiftPercent100ths = currentState.targetPositionLiftPercent100ths;
303
+ }
304
+ if (currentState.currentPositionLiftPercent100ths !== undefined) {
305
+ stateUpdate.currentPositionLiftPercent100ths = currentState.currentPositionLiftPercent100ths;
306
+ }
307
+ syncEndpointStateToCache(endpointId, clusterNames.WindowCovering, stateUpdate);
308
+ return result;
309
+ }
310
+ goToLiftPercentage(request) {
311
+ const endpointId = this.endpoint.id;
312
+ executeHandler(endpointId, clusterNames.WindowCovering, commandNames.WindowCovering.goToLiftPercentage, request);
313
+ const result = super.goToLiftPercentage(request);
314
+ // Sync state to cache - window covering moving to target position
315
+ const currentState = this.state;
316
+ const stateUpdate = {};
317
+ if (currentState.targetPositionLiftPercent100ths !== undefined) {
318
+ stateUpdate.targetPositionLiftPercent100ths = currentState.targetPositionLiftPercent100ths;
319
+ }
320
+ if (currentState.currentPositionLiftPercent100ths !== undefined) {
321
+ stateUpdate.currentPositionLiftPercent100ths = currentState.currentPositionLiftPercent100ths;
322
+ }
323
+ syncEndpointStateToCache(endpointId, clusterNames.WindowCovering, stateUpdate);
324
+ return result;
325
+ }
326
+ goToTiltPercentage(request) {
327
+ const endpointId = this.endpoint.id;
328
+ executeHandler(endpointId, clusterNames.WindowCovering, commandNames.WindowCovering.goToTiltPercentage, request);
329
+ const result = super.goToTiltPercentage(request);
330
+ // Sync state to cache - window covering tilting to target angle
331
+ const currentState = this.state;
332
+ const stateUpdate = {};
333
+ if (currentState.targetPositionTiltPercent100ths !== undefined) {
334
+ stateUpdate.targetPositionTiltPercent100ths = currentState.targetPositionTiltPercent100ths;
335
+ }
336
+ if (currentState.currentPositionTiltPercent100ths !== undefined) {
337
+ stateUpdate.currentPositionTiltPercent100ths = currentState.currentPositionTiltPercent100ths;
338
+ }
339
+ syncEndpointStateToCache(endpointId, clusterNames.WindowCovering, stateUpdate);
340
+ return result;
341
+ }
342
+ }
343
+ /**
344
+ * Custom FanControl Server that calls plugin handlers
345
+ */
346
+ export class HomebridgeFanControlServer extends FanControlServer {
347
+ initialize() {
348
+ super.initialize();
349
+ // React to fanMode attribute changes (on/off)
350
+ this.reactTo(this.events.fanMode$Changed, this.#handleFanModeChange);
351
+ // React to percentSetting attribute changes (speed)
352
+ this.reactTo(this.events.percentSetting$Changed, this.#handlePercentSettingChange);
353
+ }
354
+ #handleFanModeChange(value, oldValue) {
355
+ const endpointId = this.endpoint.id;
356
+ executeHandler(endpointId, clusterNames.FanControl, commandNames.FanControl.fanModeChange, { fanMode: value, oldFanMode: oldValue });
357
+ syncEndpointStateToCache(endpointId, clusterNames.FanControl, { fanMode: value });
358
+ }
359
+ #handlePercentSettingChange(value, oldValue) {
360
+ const endpointId = this.endpoint.id;
361
+ executeHandler(endpointId, clusterNames.FanControl, commandNames.FanControl.percentSettingChange, {
362
+ percentSetting: value,
363
+ oldPercentSetting: oldValue,
364
+ });
365
+ syncEndpointStateToCache(endpointId, clusterNames.FanControl, {
366
+ percentSetting: value,
367
+ percentCurrent: value,
368
+ });
369
+ }
370
+ }
371
+ /**
372
+ * Custom DoorLock Server that calls plugin handlers
373
+ */
374
+ export class HomebridgeDoorLockServer extends DoorLockServer {
375
+ lockDoor() {
376
+ const endpointId = this.endpoint.id;
377
+ executeHandler(endpointId, clusterNames.DoorLock, commandNames.DoorLock.lockDoor);
378
+ const result = super.lockDoor();
379
+ // Sync lock state to cache
380
+ const currentState = this.state;
381
+ if (currentState.lockState !== undefined) {
382
+ syncEndpointStateToCache(endpointId, clusterNames.DoorLock, { lockState: currentState.lockState });
383
+ }
384
+ return result;
385
+ }
386
+ unlockDoor() {
387
+ const endpointId = this.endpoint.id;
388
+ executeHandler(endpointId, clusterNames.DoorLock, commandNames.DoorLock.unlockDoor);
389
+ const result = super.unlockDoor();
390
+ // Sync lock state to cache
391
+ const currentState = this.state;
392
+ if (currentState.lockState !== undefined) {
393
+ syncEndpointStateToCache(endpointId, clusterNames.DoorLock, { lockState: currentState.lockState });
394
+ }
395
+ return result;
396
+ }
397
+ }
398
+ /**
399
+ * Custom Thermostat Server that calls plugin handlers
400
+ */
401
+ export class HomebridgeThermostatServer extends ThermostatServer {
402
+ initialize() {
403
+ super.initialize();
404
+ // React to systemMode attribute changes (off, heat, cool, auto, etc.)
405
+ this.reactTo(this.events.systemMode$Changed, this.#handleSystemModeChange);
406
+ // React to occupiedHeatingSetpoint attribute changes (target heating temperature)
407
+ const events = this.events;
408
+ if (events.occupiedHeatingSetpoint$Changing) {
409
+ this.reactTo(events.occupiedHeatingSetpoint$Changing, this.#handleOccupiedHeatingSetpointChanging);
410
+ }
411
+ // React to occupiedCoolingSetpoint attribute changes (target cooling temperature)
412
+ if (events.occupiedCoolingSetpoint$Changing) {
413
+ this.reactTo(events.occupiedCoolingSetpoint$Changing, this.#handleOccupiedCoolingSetpointChanging);
414
+ }
415
+ }
416
+ #handleSystemModeChange(value, oldValue) {
417
+ const endpointId = this.endpoint.id;
418
+ executeHandler(endpointId, clusterNames.Thermostat, commandNames.Thermostat.systemModeChange, {
419
+ systemMode: value,
420
+ oldSystemMode: oldValue,
421
+ });
422
+ syncEndpointStateToCache(endpointId, clusterNames.Thermostat, { systemMode: value });
423
+ }
424
+ #handleOccupiedHeatingSetpointChanging(value) {
425
+ const endpointId = this.endpoint.id;
426
+ const oldValue = this.state.occupiedHeatingSetpoint;
427
+ executeHandler(endpointId, clusterNames.Thermostat, commandNames.Thermostat.occupiedHeatingSetpointChange, {
428
+ occupiedHeatingSetpoint: value,
429
+ oldOccupiedHeatingSetpoint: oldValue,
430
+ });
431
+ syncEndpointStateToCache(endpointId, clusterNames.Thermostat, { occupiedHeatingSetpoint: value });
432
+ }
433
+ #handleOccupiedCoolingSetpointChanging(value) {
434
+ const endpointId = this.endpoint.id;
435
+ const oldValue = this.state.occupiedCoolingSetpoint;
436
+ executeHandler(endpointId, clusterNames.Thermostat, commandNames.Thermostat.occupiedCoolingSetpointChange, {
437
+ occupiedCoolingSetpoint: value,
438
+ oldOccupiedCoolingSetpoint: oldValue,
439
+ });
440
+ syncEndpointStateToCache(endpointId, clusterNames.Thermostat, { occupiedCoolingSetpoint: value });
441
+ }
442
+ setpointRaiseLower(request) {
443
+ const endpointId = this.endpoint.id;
444
+ executeHandler(endpointId, clusterNames.Thermostat, commandNames.Thermostat.setpointRaiseLower, request);
445
+ const result = super.setpointRaiseLower(request);
446
+ // Sync thermostat setpoints to cache
447
+ const currentState = this.state;
448
+ const stateUpdate = {};
449
+ if (currentState.occupiedCoolingSetpoint !== undefined) {
450
+ stateUpdate.occupiedCoolingSetpoint = currentState.occupiedCoolingSetpoint;
451
+ }
452
+ if (currentState.occupiedHeatingSetpoint !== undefined) {
453
+ stateUpdate.occupiedHeatingSetpoint = currentState.occupiedHeatingSetpoint;
454
+ }
455
+ syncEndpointStateToCache(endpointId, clusterNames.Thermostat, stateUpdate);
456
+ return result;
457
+ }
458
+ }
459
+ /**
460
+ * Custom Identify Server that calls plugin handlers
461
+ */
462
+ export class HomebridgeIdentifyServer extends IdentifyServer {
463
+ identify(request) {
464
+ const endpointId = this.endpoint.id;
465
+ executeHandler(endpointId, clusterNames.Identify, commandNames.Identify.identify, request);
466
+ return super.identify(request);
467
+ }
468
+ }
469
+ /**
470
+ * Custom ColorControl Server that calls plugin handlers
471
+ *
472
+ * ColorControl handles color changes for lights (hue, saturation, XY color, color temperature).
473
+ * Plugin developers can override these *Logic methods to handle color changes in their hardware.
474
+ *
475
+ * Features (Xy, ColorTemperature, HueSaturation) are added by the device type, not this behavior.
476
+ * This ensures each device only gets the features it needs.
477
+ */
478
+ export class HomebridgeColorControlServer extends ColorControlServer {
479
+ /**
480
+ * Called when color temperature is changed
481
+ * @param colorTemperatureMireds - Target color temperature in mireds (micro reciprocal degrees)
482
+ * @param transitionTime - Transition time in seconds (0 = as fast as possible)
483
+ */
484
+ moveToColorTemperatureLogic(colorTemperatureMireds, transitionTime) {
485
+ const endpointId = this.endpoint.id;
486
+ executeHandler(endpointId, clusterNames.ColorControl, commandNames.ColorControl.moveToColorTemperatureLogic, { colorTemperatureMireds, transitionTime });
487
+ const result = super.moveToColorTemperatureLogic(colorTemperatureMireds, transitionTime);
488
+ // Sync color temperature to cache
489
+ const currentState = this.state;
490
+ if (currentState.colorTemperatureMireds !== undefined) {
491
+ syncEndpointStateToCache(endpointId, clusterNames.ColorControl, {
492
+ colorTemperatureMireds: currentState.colorTemperatureMireds,
493
+ });
494
+ }
495
+ return result;
496
+ }
497
+ /**
498
+ * Called when hue and saturation are changed together
499
+ * @param hue - Target hue value (0-254 for normal hue, 0-65535 for enhanced hue)
500
+ * @param saturation - Target saturation value (0-254)
501
+ * @param transitionTime - Transition time in seconds (0 = as fast as possible)
502
+ */
503
+ moveToHueAndSaturationLogic(hue, saturation, transitionTime) {
504
+ const endpointId = this.endpoint.id;
505
+ executeHandler(endpointId, clusterNames.ColorControl, commandNames.ColorControl.moveToHueAndSaturationLogic, { hue, saturation, transitionTime });
506
+ const result = super.moveToHueAndSaturationLogic(hue, saturation, transitionTime);
507
+ // Sync hue and saturation to cache
508
+ const currentState = this.state;
509
+ const stateUpdate = {};
510
+ if (currentState.currentHue !== undefined) {
511
+ stateUpdate.currentHue = currentState.currentHue;
512
+ }
513
+ if (currentState.currentSaturation !== undefined) {
514
+ stateUpdate.currentSaturation = currentState.currentSaturation;
515
+ }
516
+ syncEndpointStateToCache(endpointId, clusterNames.ColorControl, stateUpdate);
517
+ return result;
518
+ }
519
+ /**
520
+ * Called when XY color coordinates are changed
521
+ * @param targetX - Target X value (0-65535 representing 0.0-1.0 in CIE color space)
522
+ * @param targetY - Target Y value (0-65535 representing 0.0-1.0 in CIE color space)
523
+ * @param transitionTime - Transition time in seconds (0 = as fast as possible)
524
+ */
525
+ moveToColorLogic(targetX, targetY, transitionTime) {
526
+ const endpointId = this.endpoint.id;
527
+ executeHandler(endpointId, clusterNames.ColorControl, commandNames.ColorControl.moveToColorLogic, { targetX, targetY, transitionTime });
528
+ const result = super.moveToColorLogic(targetX, targetY, transitionTime);
529
+ // Sync XY color to cache
530
+ const currentState = this.state;
531
+ const stateUpdate = {};
532
+ if (currentState.currentX !== undefined) {
533
+ stateUpdate.currentX = currentState.currentX;
534
+ }
535
+ if (currentState.currentY !== undefined) {
536
+ stateUpdate.currentY = currentState.currentY;
537
+ }
538
+ syncEndpointStateToCache(endpointId, clusterNames.ColorControl, stateUpdate);
539
+ return result;
540
+ }
541
+ /**
542
+ * Called when hue is changed individually
543
+ * @param targetHue - Target hue value
544
+ * @param direction - Direction to move (shortest, longest, up, down)
545
+ * @param transitionTime - Transition time in seconds
546
+ * @param isEnhancedHue - Whether this is enhanced hue (16-bit) or normal hue (8-bit)
547
+ */
548
+ moveToHueLogic(targetHue, direction, transitionTime, isEnhancedHue = false) {
549
+ const endpointId = this.endpoint.id;
550
+ executeHandler(endpointId, clusterNames.ColorControl, commandNames.ColorControl.moveToHueLogic, { targetHue, direction, transitionTime, isEnhancedHue });
551
+ const result = super.moveToHueLogic(targetHue, direction, transitionTime, isEnhancedHue);
552
+ // Sync hue to cache
553
+ const currentState = this.state;
554
+ const stateUpdate = {};
555
+ if (isEnhancedHue && currentState.enhancedCurrentHue !== undefined) {
556
+ stateUpdate.enhancedCurrentHue = currentState.enhancedCurrentHue;
557
+ }
558
+ else if (currentState.currentHue !== undefined) {
559
+ stateUpdate.currentHue = currentState.currentHue;
560
+ }
561
+ syncEndpointStateToCache(endpointId, clusterNames.ColorControl, stateUpdate);
562
+ return result;
563
+ }
564
+ /**
565
+ * Called when saturation is changed individually
566
+ * @param targetSaturation - Target saturation value (0-254)
567
+ * @param transitionTime - Transition time in seconds
568
+ */
569
+ moveToSaturationLogic(targetSaturation, transitionTime) {
570
+ const endpointId = this.endpoint.id;
571
+ executeHandler(endpointId, clusterNames.ColorControl, commandNames.ColorControl.moveToSaturationLogic, { targetSaturation, transitionTime });
572
+ const result = super.moveToSaturationLogic(targetSaturation, transitionTime);
573
+ // Sync saturation to cache
574
+ const currentState = this.state;
575
+ if (currentState.currentSaturation !== undefined) {
576
+ syncEndpointStateToCache(endpointId, clusterNames.ColorControl, {
577
+ currentSaturation: currentState.currentSaturation,
578
+ });
579
+ }
580
+ return result;
581
+ }
582
+ /**
583
+ * Called when all color movement should be stopped
584
+ */
585
+ stopAllColorMovement() {
586
+ const endpointId = this.endpoint.id;
587
+ executeHandler(endpointId, clusterNames.ColorControl, commandNames.ColorControl.stopAllColorMovement);
588
+ return super.stopAllColorMovement();
589
+ }
590
+ }
591
+ /**
592
+ * Custom RvcOperationalState Server that calls plugin handlers
593
+ * Handles robotic vacuum cleaner operational state commands
594
+ */
595
+ export class HomebridgeRvcOperationalStateServer extends RvcOperationalStateServer {
596
+ pause() {
597
+ const endpointId = this.endpoint.id;
598
+ executeHandler(endpointId, clusterNames.RvcOperationalState, commandNames.RvcOperationalState.pause);
599
+ return super.pause();
600
+ }
601
+ resume() {
602
+ const endpointId = this.endpoint.id;
603
+ executeHandler(endpointId, clusterNames.RvcOperationalState, commandNames.RvcOperationalState.resume);
604
+ return super.resume();
605
+ }
606
+ goHome() {
607
+ const endpointId = this.endpoint.id;
608
+ executeHandler(endpointId, clusterNames.RvcOperationalState, commandNames.RvcOperationalState.goHome);
609
+ // Return success response instead of calling unimplemented base method
610
+ return {
611
+ commandResponseState: {
612
+ errorStateId: RvcOperationalState.ErrorState.NoError,
613
+ },
614
+ };
615
+ }
616
+ }
617
+ /**
618
+ * Custom RvcRunMode Server that calls plugin handlers
619
+ * Handles robotic vacuum cleaner run mode changes
620
+ */
621
+ export class HomebridgeRvcRunModeServer extends RvcRunModeServer {
622
+ changeToMode(request) {
623
+ const endpointId = this.endpoint.id;
624
+ executeHandler(endpointId, clusterNames.RvcRunMode, commandNames.RvcRunMode.changeToMode, request);
625
+ return super.changeToMode(request);
626
+ }
627
+ }
628
+ /**
629
+ * Custom RvcCleanMode Server that calls plugin handlers
630
+ * Handles robotic vacuum cleaner cleaning mode changes
631
+ */
632
+ export class HomebridgeRvcCleanModeServer extends RvcCleanModeServer {
633
+ changeToMode(request) {
634
+ const endpointId = this.endpoint.id;
635
+ executeHandler(endpointId, clusterNames.RvcCleanMode, commandNames.RvcCleanMode.changeToMode, request);
636
+ return super.changeToMode(request);
637
+ }
638
+ }
639
+ /**
640
+ * Custom ServiceArea Server that calls plugin handlers
641
+ * Handles service area selection for robotic vacuum cleaners
642
+ */
643
+ export class HomebridgeServiceAreaServer extends ServiceAreaServer {
644
+ selectAreas(request) {
645
+ const endpointId = this.endpoint.id;
646
+ executeHandler(endpointId, clusterNames.ServiceArea, commandNames.ServiceArea.selectAreas, request);
647
+ return super.selectAreas(request);
648
+ }
649
+ skipArea(request) {
650
+ const endpointId = this.endpoint.id;
651
+ executeHandler(endpointId, clusterNames.ServiceArea, commandNames.ServiceArea.skipArea, request);
652
+ return super.skipArea(request);
653
+ }
654
+ }
655
+ //# sourceMappingURL=matterBehaviors.js.map