matterbridge 1.6.3 → 1.6.5-dev.1

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.
@@ -64,6 +64,8 @@ export class MatterbridgeEndpoint extends Endpoint {
64
64
  softwareVersionString = undefined;
65
65
  hardwareVersion = undefined;
66
66
  hardwareVersionString = undefined;
67
+ name = undefined;
68
+ deviceType;
67
69
  uniqueStorageKey = undefined;
68
70
  tagList = undefined;
69
71
  // Maps matter deviceTypes and endpoints
@@ -78,12 +80,20 @@ export class MatterbridgeEndpoint extends Endpoint {
78
80
  * @param {MatterbridgeEndpointOptions} [options={}] - The options for the device.
79
81
  */
80
82
  constructor(definition, options = {}, debug = false) {
83
+ let deviceTypeList = [];
81
84
  // Get the first DeviceTypeDefinition
82
85
  let firstDefinition;
83
- if (Array.isArray(definition))
86
+ if (Array.isArray(definition)) {
84
87
  firstDefinition = definition[0];
85
- else
88
+ deviceTypeList = Array.from(definition.values()).map((dt) => ({
89
+ deviceType: dt.code,
90
+ revision: dt.revision,
91
+ }));
92
+ }
93
+ else {
86
94
  firstDefinition = definition;
95
+ deviceTypeList = [{ deviceType: firstDefinition.code, revision: firstDefinition.revision }];
96
+ }
87
97
  // Convert the first DeviceTypeDefinition to an EndpointType.Options
88
98
  const deviceTypeDefinitionV8 = {
89
99
  name: firstDefinition.name.replace('-', '_'),
@@ -104,31 +114,30 @@ export class MatterbridgeEndpoint extends Endpoint {
104
114
  };
105
115
  const endpointV8 = MutableEndpoint(deviceTypeDefinitionV8);
106
116
  // Convert the options to an Endpoint.Options
107
- // [{ mfgCode: null, namespaceId: 0x07, tag: 1, label: 'Switch1' }]
108
- // endpoint = endpoint.enable({features: { tagList: true }});
109
117
  const optionsV8 = {
110
118
  id: options.uniqueStorageKey?.replace(/[ .]/g, ''),
111
119
  number: options.endpointId,
112
- descriptor: options.tagList ? { tagList: options.tagList } : undefined,
113
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
120
+ descriptor: options.tagList ? { tagList: options.tagList, deviceTypeList } : { deviceTypeList },
114
121
  };
115
122
  super(endpointV8, optionsV8);
116
123
  this.uniqueStorageKey = options.uniqueStorageKey;
124
+ this.name = firstDefinition.name;
125
+ this.deviceType = firstDefinition.code;
117
126
  this.tagList = options.tagList;
127
+ if (Array.isArray(definition)) {
128
+ definition.forEach((deviceType) => {
129
+ this.deviceTypes.set(deviceType.code, deviceType);
130
+ });
131
+ }
132
+ else
133
+ this.deviceTypes.set(firstDefinition.code, firstDefinition);
118
134
  // console.log('MatterbridgeEndpoint.option', options);
119
135
  // console.log('MatterbridgeEndpoint.endpointV8', endpointV8);
120
136
  // console.log('MatterbridgeEndpoint.optionsV8', optionsV8);
121
- // Update the endpoint
137
+ // Create the logger
122
138
  this.log = new AnsiLogger({ logName: 'MatterbridgeEndpoint', logTimestampFormat: 4 /* TimestampFormat.TIME_MILLIS */, logLevel: debug === true ? "debug" /* LogLevel.DEBUG */ : MatterbridgeEndpoint.logLevel });
123
139
  this.log.debug(`${YELLOW}new${db} MatterbridgeEndpoint: ${zb}${'0x' + firstDefinition.code.toString(16).padStart(4, '0')}${db}-${zb}${firstDefinition.name}${db} ` +
124
140
  `id: ${CYAN}${options.uniqueStorageKey}${db} number: ${CYAN}${options.endpointId}${db} taglist: ${CYAN}${options.tagList ? debugStringify(options.tagList) : 'undefined'}${db}`);
125
- this.deviceTypes.set(firstDefinition.code, firstDefinition);
126
- // Add the other device types to the descriptor server
127
- if (Array.isArray(definition)) {
128
- definition.forEach((deviceType) => {
129
- this.addDeviceType(deviceType);
130
- });
131
- }
132
141
  // Add MatterbridgeBehavior with MatterbridgeBehaviorDevice
133
142
  this.behaviors.require(MatterbridgeBehavior, { deviceCommand: new MatterbridgeBehaviorDevice(this.log, this.commandHandler, undefined) });
134
143
  }
@@ -164,7 +173,7 @@ export class MatterbridgeEndpoint extends Endpoint {
164
173
  if (clusterId === Groups.Cluster.id)
165
174
  return GroupsServer;
166
175
  if (clusterId === OnOff.Cluster.id)
167
- return MatterbridgeOnOffServer;
176
+ return MatterbridgeOnOffServer.with('Lighting');
168
177
  if (clusterId === LevelControl.Cluster.id)
169
178
  return MatterbridgeLevelControlServer;
170
179
  if (clusterId === ColorControl.Cluster.id)
@@ -298,7 +307,12 @@ export class MatterbridgeEndpoint extends Endpoint {
298
307
  });
299
308
  });
300
309
  includeServerList.forEach((clusterId) => {
301
- this.log.debug(`- with cluster: ${hk}${'0x' + clusterId.toString(16).padStart(4, '0')}${db}-${hk}${getClusterNameById(clusterId)}${db}`);
310
+ if (!this.getClusterServerById(clusterId)) {
311
+ this.log.debug(`- with cluster: ${hk}${'0x' + clusterId.toString(16).padStart(4, '0')}${db}-${hk}${getClusterNameById(clusterId)}${db}`);
312
+ }
313
+ else {
314
+ includeServerList.splice(includeServerList.indexOf(clusterId), 1);
315
+ }
302
316
  });
303
317
  deviceTypes.forEach((deviceType) => {
304
318
  this.addDeviceType(deviceType);
@@ -349,6 +363,47 @@ export class MatterbridgeEndpoint extends Endpoint {
349
363
  this.addClusterServerFromList(endpoint, optionalServerList);
350
364
  return endpoint;
351
365
  }
366
+ /**
367
+ * Adds a child endpoint with the specified device types and options.
368
+ * If the child endpoint is not already present, it will be created and added.
369
+ * If the child endpoint is already present, the device types will be added to the existing child endpoint.
370
+ *
371
+ * @param {string} endpointName - The name of the new endpoint to add.
372
+ * @param {AtLeastOne<DeviceTypeDefinition>} deviceTypes - The device types to add.
373
+ * @param {MatterbridgeEndpointOptions} [options={}] - The options for the endpoint.
374
+ * @param {boolean} [debug=false] - Whether to enable debug logging.
375
+ * @returns {MatterbridgeEndpoint} - The child endpoint that was found or added.
376
+ */
377
+ addChildDeviceType(endpointName, deviceTypes, options = {}, debug = false) {
378
+ this.log.debug(`addChildDeviceType: ${CYAN}${endpointName}${db}`);
379
+ let child = this.getChildEndpointByName(endpointName);
380
+ if (!child) {
381
+ if ('tagList' in options) {
382
+ for (const tag of options.tagList) {
383
+ this.log.debug(`- with tagList: mfgCode ${CYAN}${tag.mfgCode}${db} namespaceId ${CYAN}${tag.namespaceId}${db} tag ${CYAN}${tag.tag}${db} label ${CYAN}${tag.label}${db}`);
384
+ }
385
+ child = new MatterbridgeEndpoint(deviceTypes[0], { uniqueStorageKey: endpointName, tagList: options.tagList }, debug);
386
+ }
387
+ else {
388
+ child = new MatterbridgeEndpoint(deviceTypes[0], { uniqueStorageKey: endpointName }, debug);
389
+ }
390
+ }
391
+ deviceTypes.forEach((deviceType) => {
392
+ this.log.debug(`- with deviceType: ${zb}${'0x' + deviceType.code.toString(16).padStart(4, '0')}${db}-${zb}${deviceType.name}${db}`);
393
+ });
394
+ deviceTypes.forEach((deviceType) => {
395
+ child.addDeviceType(deviceType);
396
+ });
397
+ if (this.lifecycle.isInstalled) {
398
+ this.log.debug(`- with lifecycle installed`);
399
+ this.add(child);
400
+ }
401
+ else {
402
+ this.log.debug(`- with lifecycle NOT installed`);
403
+ this.parts.add(child);
404
+ }
405
+ return child;
406
+ }
352
407
  /**
353
408
  * Adds a child endpoint with one or more device types with the required cluster servers and the specified cluster servers.
354
409
  * If the child endpoint is not already present in the childEndpoints, it will be added.
@@ -383,7 +438,15 @@ export class MatterbridgeEndpoint extends Endpoint {
383
438
  });
384
439
  });
385
440
  includeServerList.forEach((clusterId) => {
386
- this.log.debug(`- with cluster: ${hk}${'0x' + clusterId.toString(16).padStart(4, '0')}${db}-${hk}${getClusterNameById(clusterId)}${db}`);
441
+ if (!child.getClusterServerById(clusterId)) {
442
+ this.log.debug(`- with cluster: ${hk}${'0x' + clusterId.toString(16).padStart(4, '0')}${db}-${hk}${getClusterNameById(clusterId)}${db}`);
443
+ }
444
+ else {
445
+ includeServerList.splice(includeServerList.indexOf(clusterId), 1);
446
+ }
447
+ });
448
+ deviceTypes.forEach((deviceType) => {
449
+ child.addDeviceType(deviceType);
387
450
  });
388
451
  this.addClusterServerFromList(child, includeServerList);
389
452
  if (this.lifecycle.isInstalled) {
@@ -419,6 +482,10 @@ export class MatterbridgeEndpoint extends Endpoint {
419
482
  this.addDeviceType(deviceType);
420
483
  });
421
484
  }
485
+ async setBridgedDeviceReachability(reachable) {
486
+ // await this.setAttribute(BridgedDeviceBasicInformationCluster.id, 'reachable', reachable, this.log);
487
+ // await this.triggerEvent(BridgedDeviceBasicInformationCluster.id, 'reachableChanged', { reachableNewValue: reachable }, this.log);
488
+ }
422
489
  hasClusterServer(cluster) {
423
490
  return this.clusterServers.has(cluster.id);
424
491
  }
@@ -428,6 +495,9 @@ export class MatterbridgeEndpoint extends Endpoint {
428
495
  return clusterServer;
429
496
  }
430
497
  }
498
+ getAllClusterServers() {
499
+ return [...this.clusterServers.values()];
500
+ }
431
501
  getClusterServerById(clusterId) {
432
502
  return this.clusterServers.get(clusterId);
433
503
  }
@@ -1121,34 +1191,53 @@ export class MatterbridgeEndpoint extends Endpoint {
1121
1191
  /**
1122
1192
  * Get a default OnOff cluster server.
1123
1193
  *
1124
- * @param onOff - The initial state of the OnOff cluster (default: false).
1194
+ * @param {boolean} [onOff=false] - The initial state of the OnOff cluster.
1195
+ * @param {boolean} [globalSceneControl=false] - The global scene control state.
1196
+ * @param {number} [onTime=0] - The on time value.
1197
+ * @param {number} [offWaitTime=0] - The off wait time value.
1198
+ * @param {OnOff.StartUpOnOff | null} [startUpOnOff=null] - The start-up OnOff state. Null means previous state.
1199
+ * @returns {ClusterServer} - The configured OnOff cluster server.
1125
1200
  */
1126
- getDefaultOnOffClusterServer(onOff = false) {
1127
- return ClusterServer(OnOffCluster, {
1201
+ getDefaultOnOffClusterServer(onOff = false, globalSceneControl = false, onTime = 0, offWaitTime = 0, startUpOnOff = null) {
1202
+ return ClusterServer(OnOffCluster.with(OnOff.Feature.Lighting), {
1128
1203
  onOff,
1204
+ globalSceneControl,
1205
+ onTime,
1206
+ offWaitTime,
1207
+ startUpOnOff,
1129
1208
  }, {
1130
1209
  on: async (data) => {
1131
- this.log.debug('Matter command: on onOff:', data.attributes.onOff.getLocal());
1132
- await this.commandHandler.executeHandler('on', data);
1210
+ // Never called in edge
1133
1211
  },
1134
1212
  off: async (data) => {
1135
- this.log.debug('Matter command: off onOff:', data.attributes.onOff.getLocal());
1136
- await this.commandHandler.executeHandler('off', data);
1213
+ // Never called in edge
1137
1214
  },
1138
1215
  toggle: async (data) => {
1139
- this.log.debug('Matter command: toggle onOff:', data.attributes.onOff.getLocal());
1140
- await this.commandHandler.executeHandler('toggle', data);
1216
+ // Never called in edge
1217
+ },
1218
+ offWithEffect: async () => {
1219
+ // Never called in edge
1220
+ },
1221
+ onWithRecallGlobalScene: async () => {
1222
+ // Never called in edge
1223
+ },
1224
+ onWithTimedOff: async () => {
1225
+ // Never called in edge
1141
1226
  },
1142
1227
  }, {});
1143
1228
  }
1144
1229
  /**
1145
1230
  * Creates a default OnOff cluster server.
1146
1231
  *
1147
- * @param onOff - The initial state of the OnOff cluster (default: false).
1232
+ * @param {boolean} [onOff=false] - The initial state of the OnOff cluster.
1233
+ * @param {boolean} [globalSceneControl=false] - The global scene control state.
1234
+ * @param {number} [onTime=0] - The on time value.
1235
+ * @param {number} [offWaitTime=0] - The off wait time value.
1236
+ * @param {OnOff.StartUpOnOff | null} [startUpOnOff=null] - The start-up OnOff state. Null means previous state.
1148
1237
  * @returns {void}
1149
1238
  */
1150
- createDefaultOnOffClusterServer(onOff = false) {
1151
- this.addClusterServer(this.getDefaultOnOffClusterServer(onOff));
1239
+ createDefaultOnOffClusterServer(onOff = false, globalSceneControl = false, onTime = 0, offWaitTime = 0, startUpOnOff = null) {
1240
+ this.addClusterServer(this.getDefaultOnOffClusterServer(onOff, globalSceneControl, onTime, offWaitTime, startUpOnOff));
1152
1241
  }
1153
1242
  /**
1154
1243
  * Get a default level control cluster server.
@@ -1158,7 +1247,7 @@ export class MatterbridgeEndpoint extends Endpoint {
1158
1247
  * @param maxLevel - The maximum level (default: 254).
1159
1248
  * @param onLevel - The on level (default: null).
1160
1249
  */
1161
- getDefaultLevelControlClusterServer(currentLevel = 254, minLevel = 0, maxLevel = 254, onLevel = null) {
1250
+ getDefaultLevelControlClusterServer(currentLevel = 254, minLevel = 0, maxLevel = 254, onLevel = 254) {
1162
1251
  return ClusterServer(LevelControlCluster.with(LevelControl.Feature.OnOff), {
1163
1252
  currentLevel,
1164
1253
  minLevel,
@@ -1205,7 +1294,7 @@ export class MatterbridgeEndpoint extends Endpoint {
1205
1294
  * @param maxLevel - The maximum level (default: 254).
1206
1295
  * @param onLevel - The on level (default: null).
1207
1296
  */
1208
- createDefaultLevelControlClusterServer(currentLevel = 254, minLevel = 0, maxLevel = 254, onLevel = null) {
1297
+ createDefaultLevelControlClusterServer(currentLevel = 254, minLevel = 0, maxLevel = 254, onLevel = 254) {
1209
1298
  this.addClusterServer(this.getDefaultLevelControlClusterServer(currentLevel, minLevel, maxLevel, onLevel));
1210
1299
  }
1211
1300
  /**