@matterbridge/core 3.7.2-dev-20260328-c77b608 → 3.7.2-dev-20260329-5cac980

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 (76) hide show
  1. package/dist/behaviors/activatedCarbonFilterMonitoringServer.d.ts +8 -0
  2. package/dist/behaviors/activatedCarbonFilterMonitoringServer.js +20 -0
  3. package/dist/{matterbridgeBehaviorsClient.js → behaviors/bindingServer.js} +1 -1
  4. package/dist/behaviors/booleanStateConfigurationServer.d.ts +7 -0
  5. package/dist/behaviors/booleanStateConfigurationServer.js +18 -0
  6. package/dist/behaviors/colorControlServer.d.ts +788 -0
  7. package/dist/behaviors/colorControlServer.js +105 -0
  8. package/dist/behaviors/deviceEnergyManagementModeServer.d.ts +5 -0
  9. package/dist/behaviors/deviceEnergyManagementModeServer.js +42 -0
  10. package/dist/behaviors/deviceEnergyManagementServer.d.ts +306 -0
  11. package/dist/behaviors/deviceEnergyManagementServer.js +31 -0
  12. package/dist/behaviors/doorLockServer.d.ts +20 -0
  13. package/dist/behaviors/doorLockServer.js +50 -0
  14. package/dist/behaviors/export.d.ts +24 -1
  15. package/dist/behaviors/export.js +24 -1
  16. package/dist/behaviors/fanControlServer.d.ts +7 -0
  17. package/dist/behaviors/fanControlServer.js +35 -0
  18. package/dist/behaviors/hepaFilterMonitoringServer.d.ts +8 -0
  19. package/dist/behaviors/hepaFilterMonitoringServer.js +20 -0
  20. package/dist/behaviors/identifyServer.d.ts +6 -0
  21. package/dist/behaviors/identifyServer.js +32 -0
  22. package/dist/behaviors/levelControlServer.d.ts +6 -0
  23. package/dist/behaviors/levelControlServer.js +32 -0
  24. package/dist/behaviors/matterbridgeServer.d.ts +14 -0
  25. package/dist/behaviors/matterbridgeServer.js +15 -0
  26. package/dist/behaviors/modeSelectServer.d.ts +5 -0
  27. package/dist/behaviors/modeSelectServer.js +18 -0
  28. package/dist/behaviors/onOffServer.d.ts +6 -0
  29. package/dist/behaviors/onOffServer.js +46 -0
  30. package/dist/behaviors/operationalStateServer.d.ts +10 -0
  31. package/dist/behaviors/operationalStateServer.js +84 -0
  32. package/dist/behaviors/pinDoorLockServer.d.ts +32 -0
  33. package/dist/behaviors/pinDoorLockServer.js +168 -0
  34. package/dist/behaviors/powerSourceServer.d.ts +4 -0
  35. package/dist/behaviors/powerSourceServer.js +21 -0
  36. package/dist/behaviors/serviceAreaServer.d.ts +5 -0
  37. package/dist/behaviors/serviceAreaServer.js +18 -0
  38. package/dist/behaviors/smokeCoAlarmServer.d.ts +83 -0
  39. package/dist/behaviors/smokeCoAlarmServer.js +18 -0
  40. package/dist/behaviors/switchServer.d.ts +4 -0
  41. package/dist/behaviors/switchServer.js +8 -0
  42. package/dist/behaviors/thermostatServer.d.ts +732 -0
  43. package/dist/behaviors/thermostatServer.js +50 -0
  44. package/dist/behaviors/userPinDoorLockServer.d.ts +57 -0
  45. package/dist/behaviors/userPinDoorLockServer.js +348 -0
  46. package/dist/behaviors/valveConfigurationAndControlServer.d.ts +8 -0
  47. package/dist/behaviors/valveConfigurationAndControlServer.js +44 -0
  48. package/dist/behaviors/windowCoveringServer.d.ts +583 -0
  49. package/dist/behaviors/windowCoveringServer.js +96 -0
  50. package/dist/devices/basicVideoPlayer.js +2 -1
  51. package/dist/devices/castingVideoPlayer.js +1 -1
  52. package/dist/devices/closure.js +1 -1
  53. package/dist/devices/closurePanel.js +1 -1
  54. package/dist/devices/dishwasher.js +2 -1
  55. package/dist/devices/evse.js +1 -1
  56. package/dist/devices/laundryWasher.js +2 -1
  57. package/dist/devices/microwaveOven.js +2 -1
  58. package/dist/devices/oven.js +1 -1
  59. package/dist/devices/refrigerator.js +1 -1
  60. package/dist/devices/roboticVacuumCleaner.js +2 -1
  61. package/dist/devices/temperatureControl.js +1 -1
  62. package/dist/devices/waterHeater.js +1 -1
  63. package/dist/export.d.ts +24 -1
  64. package/dist/export.js +24 -1
  65. package/dist/frontend.js +2 -0
  66. package/dist/matterbridge.d.ts +1 -0
  67. package/dist/matterbridge.js +11 -0
  68. package/dist/matterbridgeEndpoint.d.ts +12 -1
  69. package/dist/matterbridgeEndpoint.js +22 -1
  70. package/dist/matterbridgeEndpointCommandHandler.d.ts +5 -1
  71. package/dist/matterbridgeEndpointHelpers.d.ts +4 -1
  72. package/dist/matterbridgeEndpointHelpers.js +16 -1
  73. package/package.json +9 -5
  74. package/dist/matterbridgeBehaviorsServer.d.ts +0 -2687
  75. package/dist/matterbridgeBehaviorsServer.js +0 -1222
  76. /package/dist/{matterbridgeBehaviorsClient.d.ts → behaviors/bindingServer.d.ts} +0 -0
@@ -0,0 +1,50 @@
1
+ import { ThermostatServer } from '@matter/node/behaviors/thermostat';
2
+ import { Thermostat } from '@matter/types/clusters/thermostat';
3
+ import { MatterbridgeServer } from './matterbridgeServer.js';
4
+ export class MatterbridgeThermostatServer extends ThermostatServer.with(Thermostat.Feature.Cooling, Thermostat.Feature.Heating, Thermostat.Feature.AutoMode, Thermostat.Feature.Presets) {
5
+ async initialize() {
6
+ await super.initialize();
7
+ this.endpoint.construction.onSuccess(async () => {
8
+ const device = this.endpoint.stateOf(MatterbridgeServer);
9
+ device.log.debug(`Removing atomic commands (endpoint ${this.endpoint.maybeId}.${this.endpoint.maybeNumber})`);
10
+ await this.endpoint.setStateOf(ThermostatServer, {
11
+ acceptedCommandList: [0],
12
+ generatedCommandList: [],
13
+ });
14
+ });
15
+ }
16
+ async setpointRaiseLower(request) {
17
+ const device = this.endpoint.stateOf(MatterbridgeServer);
18
+ device.log.info(`Setting setpoint by ${request.amount} in mode ${request.mode} (endpoint ${this.endpoint.maybeId}.${this.endpoint.maybeNumber})`);
19
+ await device.commandHandler.executeHandler('Thermostat.setpointRaiseLower', {
20
+ command: 'setpointRaiseLower',
21
+ request,
22
+ cluster: ThermostatServer.id,
23
+ attributes: this.state,
24
+ endpoint: this.endpoint,
25
+ context: this.context,
26
+ });
27
+ const lookupSetpointAdjustMode = ['Heat', 'Cool', 'Both'];
28
+ device.log.debug(`MatterbridgeThermostatServer: setpointRaiseLower called with mode: ${lookupSetpointAdjustMode[request.mode]} amount: ${request.amount / 10}`);
29
+ await super.setpointRaiseLower(request);
30
+ }
31
+ async setActivePresetRequest(request) {
32
+ const device = this.endpoint.stateOf(MatterbridgeServer);
33
+ const presetHandle = request.presetHandle ? `0x${Buffer.from(request.presetHandle).toString('hex')}` : 'null';
34
+ device.log.info(`Setting preset to ${presetHandle} (endpoint ${this.endpoint.maybeId}.${this.endpoint.maybeNumber})`);
35
+ await device.commandHandler.executeHandler('Thermostat.setActivePresetRequest', {
36
+ command: 'setActivePresetRequest',
37
+ request,
38
+ cluster: ThermostatServer.id,
39
+ attributes: this.state,
40
+ endpoint: this.endpoint,
41
+ context: this.context,
42
+ });
43
+ device.log.debug(`MatterbridgeThermostatServer: setActivePresetRequest called with presetHandle: ${presetHandle}`);
44
+ await super.setActivePresetRequest(request);
45
+ const activePresetHandle = this.state.activePresetHandle ? `0x${Buffer.from(this.state.activePresetHandle).toString('hex')}` : 'null';
46
+ device.log.debug(`MatterbridgeThermostatServer: setActivePresetRequest completed with activePresetHandle: ${activePresetHandle} occupiedHeatingSetpoint: ${this.state.occupiedHeatingSetpoint} occupiedCoolingSetpoint: ${this.state.occupiedCoolingSetpoint}`);
47
+ }
48
+ }
49
+ export class MatterbridgePresetThermostatServer extends ThermostatServer.with(Thermostat.Feature.Presets, Thermostat.Feature.Cooling, Thermostat.Feature.Heating, Thermostat.Feature.AutoMode) {
50
+ }
@@ -0,0 +1,57 @@
1
+ import { DoorLockServer } from '@matter/node/behaviors/door-lock';
2
+ import { DoorLock } from '@matter/types/clusters/door-lock';
3
+ import { FabricIndex } from '@matter/types/datatype';
4
+ declare const MatterbridgeUserPinDoorLockServer_base: import("@matter/node").ClusterBehavior.Type<import("@matter/types").ClusterTypeModifier.WithAlterations<import("@matter/types").ClusterComposer.WithFeatures<DoorLock.Cluster, readonly [DoorLock.Feature.User, DoorLock.Feature.PinCredential, DoorLock.Feature.CredentialOverTheAirAccess]>, import("@matter/types").ClusterTypeModifier.ElementFlagAlterations<{
5
+ readonly events: {
6
+ readonly doorLockAlarm: true;
7
+ readonly lockOperation: true;
8
+ readonly lockOperationError: true;
9
+ };
10
+ readonly commands: {
11
+ readonly lockDoor: true;
12
+ readonly unlockDoor: true;
13
+ readonly unlockWithTimeout: true;
14
+ };
15
+ }>>, import("@matter/node").ClusterBehavior.Type<import("@matter/types").ClusterComposer.WithFeatures<DoorLock.Cluster, readonly [DoorLock.Feature.User, DoorLock.Feature.PinCredential, DoorLock.Feature.CredentialOverTheAirAccess]>, typeof DoorLockServer, import("@matter/node/behaviors/door-lock").DoorLockInterface>, import("@matter/node/behaviors/door-lock").DoorLockInterface>;
16
+ export declare class MatterbridgeUserPinDoorLockServer extends MatterbridgeUserPinDoorLockServer_base {
17
+ protected internal: MatterbridgeUserPinDoorLockServer.Internal;
18
+ private getAccessingFabricIndex;
19
+ private findStoredCredential;
20
+ private getStoredCredentialStateDebug;
21
+ private logStoredCredentialState;
22
+ private hasMatchingPinCredential;
23
+ private validateRemotePinCode;
24
+ private getNextOccupiedCredentialIndex;
25
+ private upsertStoredCredential;
26
+ private clearStoredCredential;
27
+ lockDoor(request: DoorLock.LockDoorRequest): Promise<void>;
28
+ unlockDoor(request: DoorLock.UnlockDoorRequest): Promise<void>;
29
+ unlockWithTimeout(request: DoorLock.UnlockWithTimeoutRequest): Promise<void>;
30
+ setUser(request: DoorLock.SetUserRequest): Promise<void>;
31
+ getUser(request: DoorLock.GetUserRequest): Promise<DoorLock.GetUserResponse>;
32
+ clearUser(request: DoorLock.ClearUserRequest): Promise<void>;
33
+ setCredential(request: DoorLock.SetCredentialRequest): Promise<DoorLock.SetCredentialResponse>;
34
+ getCredentialStatus(request: DoorLock.GetCredentialStatusRequest): Promise<DoorLock.GetCredentialStatusResponse>;
35
+ clearCredential(request: DoorLock.ClearCredentialRequest): Promise<void>;
36
+ }
37
+ export declare namespace MatterbridgeUserPinDoorLockServer {
38
+ type StoredCredential = DoorLock.Credential & {
39
+ credentialData: Uint8Array;
40
+ };
41
+ type StoredUser = {
42
+ userIndex: number;
43
+ userName: string | null;
44
+ userUniqueId: number | null;
45
+ userStatus: DoorLock.UserStatus | null;
46
+ userType: DoorLock.UserType | null;
47
+ credentialRule: DoorLock.CredentialRule | null;
48
+ credentials: StoredCredential[] | null;
49
+ creatorFabricIndex: FabricIndex | null;
50
+ lastModifiedFabricIndex: FabricIndex | null;
51
+ nextUserIndex: number | null;
52
+ };
53
+ class Internal {
54
+ users: StoredUser[];
55
+ }
56
+ }
57
+ export {};
@@ -0,0 +1,348 @@
1
+ import { DoorLockServer } from '@matter/node/behaviors/door-lock';
2
+ import { DoorLock } from '@matter/types/clusters/door-lock';
3
+ import { StatusResponse } from '@matter/types/common';
4
+ import { FabricIndex } from '@matter/types/datatype';
5
+ import { Status } from '@matter/types/globals';
6
+ import { getEnumDescription } from '@matterbridge/utils';
7
+ import { debugStringify } from 'node-ansi-logger';
8
+ import { MatterbridgeServer } from './matterbridgeServer.js';
9
+ export class MatterbridgeUserPinDoorLockServer extends DoorLockServer.with(DoorLock.Feature.User, DoorLock.Feature.PinCredential, DoorLock.Feature.CredentialOverTheAirAccess).enable({
10
+ events: { doorLockAlarm: true, lockOperation: true, lockOperationError: true },
11
+ commands: { lockDoor: true, unlockDoor: true, unlockWithTimeout: true },
12
+ }) {
13
+ getAccessingFabricIndex() {
14
+ let fabricIndex;
15
+ try {
16
+ fabricIndex = this.context.fabric;
17
+ }
18
+ catch {
19
+ return null;
20
+ }
21
+ if (fabricIndex === undefined || fabricIndex === FabricIndex.NO_FABRIC) {
22
+ return null;
23
+ }
24
+ return fabricIndex;
25
+ }
26
+ findStoredCredential(credential) {
27
+ for (const user of this.internal.users) {
28
+ for (const storedCredential of user.credentials ?? []) {
29
+ if (storedCredential.credentialType === credential.credentialType && storedCredential.credentialIndex === credential.credentialIndex) {
30
+ return { user, storedCredential };
31
+ }
32
+ }
33
+ }
34
+ return null;
35
+ }
36
+ getStoredCredentialStateDebug() {
37
+ if (this.internal.users.length === 0) {
38
+ return 'no users';
39
+ }
40
+ return this.internal.users
41
+ .map((user) => {
42
+ const credentials = user.credentials
43
+ ?.map((credential) => `${getEnumDescription(DoorLock.CredentialType, credential.credentialType)}:${credential.credentialIndex}=0x${Buffer.from(credential.credentialData).toString('hex')}`)
44
+ .join(', ') ?? 'none';
45
+ return `user ${user.userIndex} [${credentials}]`;
46
+ })
47
+ .join('; ');
48
+ }
49
+ logStoredCredentialState(reason) {
50
+ const device = this.endpoint.stateOf(MatterbridgeServer);
51
+ device.log.debug(`MatterbridgeDoorLockServer: ${reason}; stored credentials: ${this.getStoredCredentialStateDebug()}`);
52
+ }
53
+ hasMatchingPinCredential(pinCode) {
54
+ const device = this.endpoint.stateOf(MatterbridgeServer);
55
+ device.log.debug(`MatterbridgeDoorLockServer: checking remote PIN 0x${Buffer.from(pinCode).toString('hex')} against ${this.internal.users.length} user(s)`);
56
+ for (const user of this.internal.users) {
57
+ for (const storedCredential of user.credentials ?? []) {
58
+ if (storedCredential.credentialType !== DoorLock.CredentialType.Pin) {
59
+ continue;
60
+ }
61
+ if (Buffer.from(storedCredential.credentialData).equals(Buffer.from(pinCode))) {
62
+ device.log.debug(`MatterbridgeDoorLockServer: remote PIN matched userIndex ${user.userIndex} credentialIndex ${storedCredential.credentialIndex}`);
63
+ return true;
64
+ }
65
+ }
66
+ }
67
+ device.log.debug(`MatterbridgeDoorLockServer: remote PIN 0x${Buffer.from(pinCode).toString('hex')} did not match any stored PIN credential`);
68
+ return false;
69
+ }
70
+ validateRemotePinCode(pinCode) {
71
+ const device = this.endpoint.stateOf(MatterbridgeServer);
72
+ if (!this.state.requirePinForRemoteOperation) {
73
+ device.log.debug('MatterbridgeDoorLockServer: skipping remote PIN validation because requirePinForRemoteOperation is false');
74
+ return;
75
+ }
76
+ if (pinCode === undefined) {
77
+ device.log.debug('MatterbridgeDoorLockServer: rejecting remote operation because the request did not include a PIN');
78
+ this.logStoredCredentialState('remote PIN validation failed');
79
+ throw new StatusResponse.FailureError('Missing or invalid PIN code for remote operation');
80
+ }
81
+ device.log.debug(`MatterbridgeDoorLockServer: validating remote PIN 0x${Buffer.from(pinCode).toString('hex')}`);
82
+ if (!this.hasMatchingPinCredential(pinCode)) {
83
+ this.logStoredCredentialState('remote PIN validation failed');
84
+ throw new StatusResponse.FailureError('Missing or invalid PIN code for remote operation');
85
+ }
86
+ device.log.debug(`MatterbridgeDoorLockServer: accepted remote PIN 0x${Buffer.from(pinCode).toString('hex')}`);
87
+ }
88
+ getNextOccupiedCredentialIndex(credential) {
89
+ let nextCredentialIndex = null;
90
+ for (const user of this.internal.users) {
91
+ for (const storedCredential of user.credentials ?? []) {
92
+ if (storedCredential.credentialType !== credential.credentialType || storedCredential.credentialIndex <= credential.credentialIndex) {
93
+ continue;
94
+ }
95
+ if (nextCredentialIndex === null || storedCredential.credentialIndex < nextCredentialIndex) {
96
+ nextCredentialIndex = storedCredential.credentialIndex;
97
+ }
98
+ }
99
+ }
100
+ return nextCredentialIndex;
101
+ }
102
+ upsertStoredCredential(userIndex, credential, credentialData) {
103
+ const device = this.endpoint.stateOf(MatterbridgeServer);
104
+ if (userIndex === null) {
105
+ device.log.debug(`MatterbridgeDoorLockServer: not storing credentialType ${getEnumDescription(DoorLock.CredentialType, credential.credentialType)} credentialIndex ${credential.credentialIndex} because userIndex is null`);
106
+ return;
107
+ }
108
+ const user = this.internal.users.find((storedUser) => storedUser.userIndex === userIndex);
109
+ if (!user) {
110
+ device.log.debug(`MatterbridgeDoorLockServer: not storing credentialType ${getEnumDescription(DoorLock.CredentialType, credential.credentialType)} credentialIndex ${credential.credentialIndex} because userIndex ${userIndex} was not found`);
111
+ return;
112
+ }
113
+ let removedCredentials = 0;
114
+ for (const storedUser of this.internal.users) {
115
+ const nextCredentials = storedUser.credentials?.filter((storedCredential) => storedCredential.credentialType !== credential.credentialType || storedCredential.credentialIndex !== credential.credentialIndex) ?? null;
116
+ removedCredentials += (storedUser.credentials?.length ?? 0) - (nextCredentials?.length ?? 0);
117
+ storedUser.credentials = nextCredentials && nextCredentials.length > 0 ? nextCredentials : null;
118
+ }
119
+ if (!user.credentials) {
120
+ user.credentials = [];
121
+ }
122
+ user.credentials.push({
123
+ credentialType: credential.credentialType,
124
+ credentialIndex: credential.credentialIndex,
125
+ credentialData: Uint8Array.from(credentialData),
126
+ });
127
+ device.log.debug(`MatterbridgeDoorLockServer: stored credentialType ${getEnumDescription(DoorLock.CredentialType, credential.credentialType)} credentialIndex ${credential.credentialIndex} for userIndex ${userIndex} (removed ${removedCredentials} replaced credential(s))`);
128
+ this.logStoredCredentialState('credential stored');
129
+ }
130
+ clearStoredCredential(credential) {
131
+ const device = this.endpoint.stateOf(MatterbridgeServer);
132
+ for (const user of this.internal.users) {
133
+ if (credential === null) {
134
+ user.credentials = null;
135
+ continue;
136
+ }
137
+ const nextCredentials = user.credentials?.filter((storedCredential) => storedCredential.credentialType !== credential.credentialType || storedCredential.credentialIndex !== credential.credentialIndex) ?? null;
138
+ user.credentials = nextCredentials && nextCredentials.length > 0 ? nextCredentials : null;
139
+ }
140
+ device.log.debug(`MatterbridgeDoorLockServer: cleared ${credential ? `${getEnumDescription(DoorLock.CredentialType, credential.credentialType)} credentialIndex ${credential.credentialIndex}` : 'all credentials'} from internal state`);
141
+ this.logStoredCredentialState('credential cleared');
142
+ }
143
+ async lockDoor(request) {
144
+ const device = this.endpoint.stateOf(MatterbridgeServer);
145
+ device.log.info(`Locking door with pincode ${request.pinCode ? '0x' + Buffer.from(request.pinCode).toString('hex') : 'N/A'} (endpoint ${this.endpoint.maybeId}.${this.endpoint.maybeNumber})`);
146
+ this.validateRemotePinCode(request.pinCode);
147
+ device.log.debug(`MatterbridgeDoorLockServer: remote lockDoor PIN validation completed`);
148
+ await device.commandHandler.executeHandler('DoorLock.lockDoor', {
149
+ command: 'lockDoor',
150
+ request,
151
+ cluster: DoorLockServer.id,
152
+ attributes: this.state,
153
+ endpoint: this.endpoint,
154
+ context: this.context,
155
+ });
156
+ device.log.debug(`MatterbridgeDoorLockServer: lockDoor called`);
157
+ await super.lockDoor(request);
158
+ }
159
+ async unlockDoor(request) {
160
+ const device = this.endpoint.stateOf(MatterbridgeServer);
161
+ device.log.info(`Unlocking door with pincode ${request.pinCode ? '0x' + Buffer.from(request.pinCode).toString('hex') : 'N/A'} (endpoint ${this.endpoint.maybeId}.${this.endpoint.maybeNumber})`);
162
+ this.validateRemotePinCode(request.pinCode);
163
+ device.log.debug(`MatterbridgeDoorLockServer: remote unlockDoor PIN validation completed`);
164
+ await device.commandHandler.executeHandler('DoorLock.unlockDoor', {
165
+ command: 'unlockDoor',
166
+ request,
167
+ cluster: DoorLockServer.id,
168
+ attributes: this.state,
169
+ endpoint: this.endpoint,
170
+ context: this.context,
171
+ });
172
+ device.log.debug(`MatterbridgeDoorLockServer: unlockDoor called`);
173
+ await super.unlockDoor(request);
174
+ }
175
+ async unlockWithTimeout(request) {
176
+ const device = this.endpoint.stateOf(MatterbridgeServer);
177
+ device.log.info(`Unlocking door with pincode ${request.pinCode ? '0x' + Buffer.from(request.pinCode).toString('hex') : 'N/A'} timeout ${request.timeout} seconds (endpoint ${this.endpoint.maybeId}.${this.endpoint.maybeNumber})`);
178
+ this.validateRemotePinCode(request.pinCode);
179
+ device.log.debug(`MatterbridgeDoorLockServer: remote unlockWithTimeout PIN validation completed`);
180
+ await device.commandHandler.executeHandler('DoorLock.unlockWithTimeout', {
181
+ command: 'unlockWithTimeout',
182
+ request,
183
+ cluster: DoorLockServer.id,
184
+ attributes: this.state,
185
+ endpoint: this.endpoint,
186
+ context: this.context,
187
+ });
188
+ device.log.debug(`MatterbridgeDoorLockServer: unlockWithTimeout called`);
189
+ this.state.lockState = DoorLock.LockState.Unlocked;
190
+ }
191
+ async setUser(request) {
192
+ const device = this.endpoint.stateOf(MatterbridgeServer);
193
+ const accessingFabricIndex = this.getAccessingFabricIndex();
194
+ device.log.info(`Setting user operationType ${getEnumDescription(DoorLock.DataOperationType, request.operationType)} userIndex ${request.userIndex} userName ${request.userName ?? 'null'} userUniqueId ${request.userUniqueId ?? 'null'} userStatus ${getEnumDescription(DoorLock.UserStatus, request.userStatus, { fallback: 'null' })} userType ${getEnumDescription(DoorLock.UserType, request.userType, { fallback: 'null' })} credentialRule ${getEnumDescription(DoorLock.CredentialRule, request.credentialRule, { fallback: 'null' })} (endpoint ${this.endpoint.maybeId}.${this.endpoint.maybeNumber})`);
195
+ device.log.debug(`MatterbridgeDoorLockServer: setUser accessingFabricIndex ${accessingFabricIndex ?? 'null'}`);
196
+ await device.commandHandler.executeHandler('DoorLock.setUser', {
197
+ command: 'setUser',
198
+ request,
199
+ cluster: DoorLockServer.id,
200
+ attributes: this.state,
201
+ endpoint: this.endpoint,
202
+ context: this.context,
203
+ });
204
+ const user = this.internal.users.find((storedUser) => storedUser.userIndex === request.userIndex);
205
+ device.log.debug(`MatterbridgeDoorLockServer: setUser called for userIndex ${request.userIndex} (${user ? 'existing user ' + debugStringify(user) : 'new user'})`);
206
+ if (!user && request.operationType === DoorLock.DataOperationType.Add) {
207
+ this.internal.users.push({
208
+ userIndex: request.userIndex,
209
+ userName: request.userName,
210
+ userUniqueId: request.userUniqueId,
211
+ userStatus: request.userStatus,
212
+ userType: request.userType,
213
+ credentialRule: request.credentialRule,
214
+ credentials: null,
215
+ creatorFabricIndex: accessingFabricIndex,
216
+ lastModifiedFabricIndex: accessingFabricIndex,
217
+ nextUserIndex: null,
218
+ });
219
+ device.log.debug(`MatterbridgeDoorLockServer: added userIndex ${request.userIndex} (total users: ${this.internal.users.length}) to internal state: ${debugStringify(this.internal.users.find((storedUser) => storedUser.userIndex === request.userIndex))}`);
220
+ this.logStoredCredentialState('user added');
221
+ return;
222
+ }
223
+ this.logStoredCredentialState(`setUser completed for userIndex ${request.userIndex} without adding a new internal user`);
224
+ }
225
+ async getUser(request) {
226
+ const device = this.endpoint.stateOf(MatterbridgeServer);
227
+ device.log.info(`Getting userIndex ${request.userIndex} (endpoint ${this.endpoint.maybeId}.${this.endpoint.maybeNumber})`);
228
+ await device.commandHandler.executeHandler('DoorLock.getUser', {
229
+ command: 'getUser',
230
+ request,
231
+ cluster: DoorLockServer.id,
232
+ attributes: this.state,
233
+ endpoint: this.endpoint,
234
+ context: this.context,
235
+ });
236
+ const user = this.internal.users.find((storedUser) => storedUser.userIndex === request.userIndex);
237
+ device.log.debug(`MatterbridgeDoorLockServer: getUser called for userIndex ${request.userIndex} (total users: ${this.internal.users.length}) (${user ? 'existing user: ' + debugStringify(user) : 'new user'})`);
238
+ this.logStoredCredentialState(`getUser returning state for userIndex ${request.userIndex}`);
239
+ if (user) {
240
+ return {
241
+ ...user,
242
+ credentials: user.credentials?.map(({ credentialType, credentialIndex }) => ({ credentialType, credentialIndex })) ?? null,
243
+ };
244
+ }
245
+ return {
246
+ userIndex: request.userIndex,
247
+ userName: null,
248
+ userUniqueId: null,
249
+ userStatus: null,
250
+ userType: null,
251
+ credentialRule: null,
252
+ credentials: null,
253
+ creatorFabricIndex: null,
254
+ lastModifiedFabricIndex: null,
255
+ nextUserIndex: null,
256
+ };
257
+ }
258
+ async clearUser(request) {
259
+ const device = this.endpoint.stateOf(MatterbridgeServer);
260
+ device.log.info(`Clearing userIndex ${request.userIndex} ${request.userIndex === 0xfffe ? '(all users)' : ''} (endpoint ${this.endpoint.maybeId}.${this.endpoint.maybeNumber})`);
261
+ await device.commandHandler.executeHandler('DoorLock.clearUser', {
262
+ command: 'clearUser',
263
+ request,
264
+ cluster: DoorLockServer.id,
265
+ attributes: this.state,
266
+ endpoint: this.endpoint,
267
+ context: this.context,
268
+ });
269
+ device.log.debug(`MatterbridgeDoorLockServer: clearUser called for userIndex ${request.userIndex}`);
270
+ this.logStoredCredentialState(`clearUser completed for userIndex ${request.userIndex}`);
271
+ }
272
+ async setCredential(request) {
273
+ const device = this.endpoint.stateOf(MatterbridgeServer);
274
+ const accessingFabricIndex = this.getAccessingFabricIndex();
275
+ device.log.info(`Setting credential operationType ${getEnumDescription(DoorLock.DataOperationType, request.operationType)} credentialType ${getEnumDescription(DoorLock.CredentialType, request.credential.credentialType)} credentialIndex ${request.credential.credentialIndex} credentialData ${Buffer.from(request.credentialData).toString('hex') ? '0x' + Buffer.from(request.credentialData).toString('hex') : '0x'} userIndex ${request.userIndex ?? 'null'} userStatus ${getEnumDescription(DoorLock.UserStatus, request.userStatus, { fallback: 'null' })} userType ${getEnumDescription(DoorLock.UserType, request.userType, { fallback: 'null' })} (endpoint ${this.endpoint.maybeId}.${this.endpoint.maybeNumber})`);
276
+ await device.commandHandler.executeHandler('DoorLock.setCredential', {
277
+ command: 'setCredential',
278
+ request,
279
+ cluster: DoorLockServer.id,
280
+ attributes: this.state,
281
+ endpoint: this.endpoint,
282
+ context: this.context,
283
+ });
284
+ const user = this.internal.users.find((storedUser) => storedUser.userIndex === request.userIndex);
285
+ const existingCredential = this.findStoredCredential(request.credential);
286
+ device.log.debug(`MatterbridgeDoorLockServer: setCredential pre-update lookup for credentialIndex ${request.credential.credentialIndex} (${existingCredential ? 'existing credential found' : 'no existing credential'})`);
287
+ device.log.debug(`MatterbridgeDoorLockServer: setCredential called for credentialIndex ${request.credential.credentialIndex}`);
288
+ if (user && (request.operationType === DoorLock.DataOperationType.Add || request.operationType === DoorLock.DataOperationType.Modify)) {
289
+ this.upsertStoredCredential(request.userIndex, request.credential, request.credentialData);
290
+ user.lastModifiedFabricIndex = accessingFabricIndex;
291
+ device.log.debug(`MatterbridgeDoorLockServer: setCredential updated lastModifiedFabricIndex for userIndex ${user.userIndex} to ${accessingFabricIndex ?? 'null'}`);
292
+ }
293
+ else {
294
+ device.log.debug(`MatterbridgeDoorLockServer: setCredential did not update internal state for credentialIndex ${request.credential.credentialIndex} (user ${request.userIndex ?? 'null'} not found or operation not handled)`);
295
+ }
296
+ return {
297
+ status: Status.Success,
298
+ userIndex: request.userIndex,
299
+ };
300
+ }
301
+ async getCredentialStatus(request) {
302
+ const device = this.endpoint.stateOf(MatterbridgeServer);
303
+ device.log.info(`Getting credential status for credentialType ${getEnumDescription(DoorLock.CredentialType, request.credential.credentialType)} credentialIndex ${request.credential.credentialIndex} (endpoint ${this.endpoint.maybeId}.${this.endpoint.maybeNumber})`);
304
+ await device.commandHandler.executeHandler('DoorLock.getCredentialStatus', {
305
+ command: 'getCredentialStatus',
306
+ request,
307
+ cluster: DoorLockServer.id,
308
+ attributes: this.state,
309
+ endpoint: this.endpoint,
310
+ context: this.context,
311
+ });
312
+ const credentialRecord = this.findStoredCredential(request.credential);
313
+ const nextCredentialIndex = this.getNextOccupiedCredentialIndex(request.credential);
314
+ device.log.debug(`MatterbridgeDoorLockServer: getCredentialStatus called`);
315
+ device.log.debug(`MatterbridgeDoorLockServer: getCredentialStatus result for credentialIndex ${request.credential.credentialIndex} (${credentialRecord ? `userIndex ${credentialRecord.user.userIndex} credentialData 0x${Buffer.from(credentialRecord.storedCredential.credentialData).toString('hex')}` : 'credential missing'}, nextCredentialIndex ${nextCredentialIndex ?? 'null'})`);
316
+ return {
317
+ credentialExists: credentialRecord !== null,
318
+ userIndex: credentialRecord?.user.userIndex ?? null,
319
+ creatorFabricIndex: credentialRecord?.user.creatorFabricIndex ?? null,
320
+ lastModifiedFabricIndex: credentialRecord?.user.lastModifiedFabricIndex ?? null,
321
+ nextCredentialIndex,
322
+ credentialData: credentialRecord?.storedCredential.credentialData ?? null,
323
+ };
324
+ }
325
+ async clearCredential(request) {
326
+ const device = this.endpoint.stateOf(MatterbridgeServer);
327
+ device.log.info(`Clearing credentialType ${request.credential ? getEnumDescription(DoorLock.CredentialType, request.credential.credentialType) : 'null'} credentialIndex ${request.credential ? request.credential.credentialIndex : 'null'} ${request.credential === null ? '(all credentials)' : ''} (endpoint ${this.endpoint.maybeId}.${this.endpoint.maybeNumber})`);
328
+ await device.commandHandler.executeHandler('DoorLock.clearCredential', {
329
+ command: 'clearCredential',
330
+ request,
331
+ cluster: DoorLockServer.id,
332
+ attributes: this.state,
333
+ endpoint: this.endpoint,
334
+ context: this.context,
335
+ });
336
+ this.clearStoredCredential(request.credential);
337
+ device.log.debug('MatterbridgeDoorLockServer: clearCredential called');
338
+ this.logStoredCredentialState(`clearCredential completed for ${request.credential
339
+ ? `${getEnumDescription(DoorLock.CredentialType, request.credential.credentialType)} credentialIndex ${request.credential.credentialIndex}`
340
+ : 'all credentials'}`);
341
+ }
342
+ }
343
+ (function (MatterbridgeUserPinDoorLockServer) {
344
+ class Internal {
345
+ users = [];
346
+ }
347
+ MatterbridgeUserPinDoorLockServer.Internal = Internal;
348
+ })(MatterbridgeUserPinDoorLockServer || (MatterbridgeUserPinDoorLockServer = {}));
@@ -0,0 +1,8 @@
1
+ import { ValveConfigurationAndControlServer } from '@matter/node/behaviors/valve-configuration-and-control';
2
+ import { ValveConfigurationAndControl } from '@matter/types/clusters/valve-configuration-and-control';
3
+ declare const MatterbridgeValveConfigurationAndControlServer_base: import("@matter/node").ClusterBehavior.Type<import("@matter/types").ClusterComposer.WithFeatures<ValveConfigurationAndControl.Cluster, readonly [ValveConfigurationAndControl.Feature.Level]>, typeof ValveConfigurationAndControlServer, import("@matter/node/behaviors/valve-configuration-and-control").ValveConfigurationAndControlInterface>;
4
+ export declare class MatterbridgeValveConfigurationAndControlServer extends MatterbridgeValveConfigurationAndControlServer_base {
5
+ open(request: ValveConfigurationAndControl.OpenRequest): Promise<void>;
6
+ close(): Promise<void>;
7
+ }
8
+ export {};
@@ -0,0 +1,44 @@
1
+ import { ValveConfigurationAndControlServer } from '@matter/node/behaviors/valve-configuration-and-control';
2
+ import { ValveConfigurationAndControl } from '@matter/types/clusters/valve-configuration-and-control';
3
+ import { MatterbridgeServer } from './matterbridgeServer.js';
4
+ export class MatterbridgeValveConfigurationAndControlServer extends ValveConfigurationAndControlServer.with(ValveConfigurationAndControl.Feature.Level) {
5
+ async open(request) {
6
+ const device = this.endpoint.stateOf(MatterbridgeServer);
7
+ device.log.info(`Opening valve to ${request.targetLevel ? request.targetLevel + '%' : 'fully opened'} ${request.openDuration ? 'for ' + request.openDuration + 's' : 'until closed'} (endpoint ${this.endpoint.maybeId}.${this.endpoint.maybeNumber})`);
8
+ await device.commandHandler.executeHandler('ValveConfigurationAndControl.open', {
9
+ command: 'open',
10
+ request,
11
+ cluster: ValveConfigurationAndControlServer.id,
12
+ attributes: this.state,
13
+ endpoint: this.endpoint,
14
+ context: this.context,
15
+ });
16
+ device.log.debug(`MatterbridgeValveConfigurationAndControlServer: open called with openDuration: ${request.openDuration} targetLevel: ${request.targetLevel}`);
17
+ this.state.targetState = ValveConfigurationAndControl.ValveState.Open;
18
+ this.state.currentState = ValveConfigurationAndControl.ValveState.Open;
19
+ this.state.targetLevel = request.targetLevel ?? 100;
20
+ this.state.currentLevel = request.targetLevel ?? 100;
21
+ this.state.openDuration = request.openDuration ?? this.state.defaultOpenDuration;
22
+ if (this.state.openDuration === null)
23
+ this.state.remainingDuration = null;
24
+ }
25
+ async close() {
26
+ const device = this.endpoint.stateOf(MatterbridgeServer);
27
+ device.log.info(`Closing valve (endpoint ${this.endpoint.maybeId}.${this.endpoint.maybeNumber})`);
28
+ await device.commandHandler.executeHandler('ValveConfigurationAndControl.close', {
29
+ command: 'close',
30
+ request: {},
31
+ cluster: ValveConfigurationAndControlServer.id,
32
+ attributes: this.state,
33
+ endpoint: this.endpoint,
34
+ context: this.context,
35
+ });
36
+ device.log.debug(`MatterbridgeValveConfigurationAndControlServer: close called`);
37
+ this.state.targetState = ValveConfigurationAndControl.ValveState.Closed;
38
+ this.state.currentState = ValveConfigurationAndControl.ValveState.Closed;
39
+ this.state.targetLevel = 0;
40
+ this.state.currentLevel = 0;
41
+ this.state.openDuration = null;
42
+ this.state.remainingDuration = null;
43
+ }
44
+ }