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