matterbridge 1.2.3 → 1.2.4
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/CHANGELOG.md +179 -171
- package/README.md +306 -297
- package/dist/AirQualityCluster.d.ts +134 -104
- package/dist/AirQualityCluster.d.ts.map +1 -1
- package/dist/AirQualityCluster.js +43 -26
- package/dist/AirQualityCluster.js.map +1 -1
- package/dist/EveHistoryCluster.d.ts +446 -0
- package/dist/EveHistoryCluster.d.ts.map +1 -0
- package/dist/EveHistoryCluster.js +170 -0
- package/dist/EveHistoryCluster.js.map +1 -0
- package/dist/TvocCluster.d.ts +364 -148
- package/dist/TvocCluster.d.ts.map +1 -1
- package/dist/TvocCluster.js +115 -32
- package/dist/TvocCluster.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/matterbridge.d.ts +1 -1
- package/dist/matterbridge.d.ts.map +1 -1
- package/dist/matterbridge.js +244 -138
- package/dist/matterbridge.js.map +1 -1
- package/dist/matterbridgeDevice.d.ts +16 -3
- package/dist/matterbridgeDevice.d.ts.map +1 -1
- package/dist/matterbridgeDevice.js +68 -10
- package/dist/matterbridgeDevice.js.map +1 -1
- package/frontend/build/asset-manifest.json +14 -14
- package/frontend/build/static/css/main.61f6cf42.css +1 -1
- package/frontend/build/static/js/main.e3553a4d.js +2 -2
- package/frontend/build/static/js/main.e3553a4d.js.LICENSE.txt +99 -99
- package/package.json +87 -87
package/dist/matterbridge.js
CHANGED
|
@@ -31,7 +31,7 @@ import express from 'express';
|
|
|
31
31
|
import os from 'os';
|
|
32
32
|
import path from 'path';
|
|
33
33
|
import { CommissioningController, CommissioningServer, MatterServer } from '@project-chip/matter-node.js';
|
|
34
|
-
import { BasicInformationCluster, BridgedDeviceBasicInformationCluster, ClusterServer, GeneralCommissioning, PowerSourceCluster, ThreadNetworkDiagnosticsCluster
|
|
34
|
+
import { BasicInformationCluster, BooleanStateCluster, BridgedDeviceBasicInformationCluster, ClusterServer, GeneralCommissioning, PowerSourceCluster, ThreadNetworkDiagnosticsCluster } from '@project-chip/matter-node.js/cluster';
|
|
35
35
|
import { DeviceTypeId, VendorId } from '@project-chip/matter-node.js/datatype';
|
|
36
36
|
import { Aggregator, DeviceTypes, NodeStateInformation } from '@project-chip/matter-node.js/device';
|
|
37
37
|
import { Format, Level, Logger } from '@project-chip/matter-node.js/log';
|
|
@@ -297,7 +297,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
297
297
|
// Start the storage (we need it now for frontend and later for matterbridge)
|
|
298
298
|
await this.startStorage('json', path.join(this.matterbridgeDirectory, 'matterbridge.json'));
|
|
299
299
|
this.log.debug(`Creating commissioning server context for ${plg}Matterbridge${db}`);
|
|
300
|
-
this.matterbridgeContext = this.createCommissioningServerContext('Matterbridge', 'Matterbridge', DeviceTypes.AGGREGATOR.code, 0xfff1, 'Matterbridge', 0x8000, 'Matterbridge aggregator');
|
|
300
|
+
this.matterbridgeContext = await this.createCommissioningServerContext('Matterbridge', 'Matterbridge', DeviceTypes.AGGREGATOR.code, 0xfff1, 'Matterbridge', 0x8000, 'Matterbridge aggregator');
|
|
301
301
|
// Initialize frontend
|
|
302
302
|
await this.initializeFrontend(getIntParameter('frontend'));
|
|
303
303
|
if (hasParameter('test')) {
|
|
@@ -310,7 +310,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
310
310
|
this.bridgeMode = 'controller';
|
|
311
311
|
this.log.info('Creating mattercontrollerContext: mattercontrollerContext');
|
|
312
312
|
this.mattercontrollerContext = this.storageManager?.createContext('mattercontrollerContext');
|
|
313
|
-
await this.
|
|
313
|
+
await this.startMatterbridge();
|
|
314
314
|
return;
|
|
315
315
|
}
|
|
316
316
|
if (hasParameter('bridge')) {
|
|
@@ -329,7 +329,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
329
329
|
plugin.manualPairingCode = undefined;
|
|
330
330
|
this.loadPlugin(plugin); // No await do it asyncronously
|
|
331
331
|
}
|
|
332
|
-
await this.
|
|
332
|
+
await this.startMatterbridge();
|
|
333
333
|
return;
|
|
334
334
|
}
|
|
335
335
|
if (hasParameter('childbridge')) {
|
|
@@ -348,7 +348,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
348
348
|
plugin.manualPairingCode = (await plugin.nodeContext?.get('manualPairingCode', undefined)) ?? undefined;
|
|
349
349
|
this.loadPlugin(plugin, true, 'Matterbridge is starting'); // No await do it asyncronously
|
|
350
350
|
}
|
|
351
|
-
await this.
|
|
351
|
+
await this.startMatterbridge();
|
|
352
352
|
return;
|
|
353
353
|
}
|
|
354
354
|
}
|
|
@@ -825,50 +825,98 @@ export class Matterbridge extends EventEmitter {
|
|
|
825
825
|
this.mattercontrollerContext = undefined;
|
|
826
826
|
}
|
|
827
827
|
async testStartMatterBridge() {
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
this.
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
plugin.started = false;
|
|
835
|
-
plugin.configured = false;
|
|
836
|
-
plugin.connected = undefined;
|
|
837
|
-
this.log.error(`****Starting registeredPlugin ${plugin.name}`);
|
|
838
|
-
this.loadPlugin(plugin, true, 'Matterbridge is starting');
|
|
839
|
-
});
|
|
840
|
-
this.log.error('****Stop forEach registeredPlugin');
|
|
841
|
-
*/
|
|
842
|
-
/*
|
|
843
|
-
for (const plugin of this.registeredPlugins) {
|
|
844
|
-
if (!plugin.enabled) continue;
|
|
845
|
-
// No await do it asyncronously
|
|
846
|
-
this.loadPlugin(plugin)
|
|
847
|
-
.then(() => {
|
|
848
|
-
// No await do it asyncronously
|
|
849
|
-
this.startPlugin(plugin)
|
|
850
|
-
.then(() => {})
|
|
851
|
-
.catch((err) => {
|
|
852
|
-
this.log.error(`Failed to start plugin ${plg}${plugin.name}${er}: ${err}`);
|
|
853
|
-
});
|
|
854
|
-
})
|
|
855
|
-
.catch((err) => {
|
|
856
|
-
this.log.error(`Failed to load plugin ${plg}${plugin.name}${er}: ${err}`);
|
|
857
|
-
});
|
|
828
|
+
// Plugins are loaded by loadPlugin on startup and plugin.loaded is set to true
|
|
829
|
+
// Plugins are started and configured by callback when Matterbridge is commissioned
|
|
830
|
+
if (!this.storageManager) {
|
|
831
|
+
this.log.error('No storage manager initialized');
|
|
832
|
+
await this.cleanup('No storage manager initialized');
|
|
833
|
+
return;
|
|
858
834
|
}
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
this.log.info(`Waiting ${times} secs for plugin ${plg}${plugin.name}${db} to load (${plugin.loaded}) and start (${plugin.started}) and send devices ...`);
|
|
866
|
-
if (!plugin.loaded || !plugin.started) return;
|
|
867
|
-
this.log.info(`Plugin ${plg}${plugin.name}${db} sent ${plugin.registeredDevices} devices`);
|
|
868
|
-
clearInterval(interval);
|
|
869
|
-
}, 1000);
|
|
835
|
+
this.log.debug('Starting matterbridge in mode', this.bridgeMode);
|
|
836
|
+
this.createMatterServer(this.storageManager);
|
|
837
|
+
if (!this.matterServer) {
|
|
838
|
+
this.log.error('No matter server initialized');
|
|
839
|
+
await this.cleanup('No matter server initialized');
|
|
840
|
+
return;
|
|
870
841
|
}
|
|
871
|
-
|
|
842
|
+
this.log.debug('***Starting startMatterbridge interval for Matterbridge');
|
|
843
|
+
let failCount = 0;
|
|
844
|
+
const startInterval = setInterval(async () => {
|
|
845
|
+
for (const plugin of this.registeredPlugins) {
|
|
846
|
+
if (!plugin.enabled)
|
|
847
|
+
continue;
|
|
848
|
+
if (!plugin.loaded) {
|
|
849
|
+
this.log.info(`***Waiting (failSafeCount=${failCount}/30) in startMatterbridge interval for plugin ${plg}${plugin.name}${db} loaded: ${plugin.loaded}...`);
|
|
850
|
+
failCount++;
|
|
851
|
+
if (failCount > 30) {
|
|
852
|
+
this.log.error(`***Failed to load plugin ${plg}${plugin.name}${er}`);
|
|
853
|
+
plugin.error = true;
|
|
854
|
+
}
|
|
855
|
+
else {
|
|
856
|
+
return;
|
|
857
|
+
}
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
clearInterval(startInterval);
|
|
861
|
+
this.log.debug('***Cleared startMatterbridge interval for Matterbridge');
|
|
862
|
+
this.log.debug(`Creating commissioning server context for ${plg}Matterbridge${db}`);
|
|
863
|
+
this.matterbridgeContext = await this.createCommissioningServerContext('Matterbridge', 'Matterbridge', DeviceTypes.AGGREGATOR.code, 0xfff1, 'Matterbridge', 0x8000, 'Matterbridge Aggregator');
|
|
864
|
+
if (!this.matterbridgeContext) {
|
|
865
|
+
this.log.error(`Error creating storage context for ${plg}Matterbridge${er}`);
|
|
866
|
+
return;
|
|
867
|
+
}
|
|
868
|
+
if (!this.nodeContext) {
|
|
869
|
+
this.log.error(`Node storage context undefined for ${plg}Matterbridge${er}`);
|
|
870
|
+
return;
|
|
871
|
+
}
|
|
872
|
+
await this.matterbridgeContext.set('softwareVersion', 1);
|
|
873
|
+
await this.matterbridgeContext.set('softwareVersionString', this.matterbridgeVersion);
|
|
874
|
+
this.log.debug(`Creating commissioning server for ${plg}Matterbridge${db}`);
|
|
875
|
+
this.commissioningServer = await this.createCommisioningServer(this.matterbridgeContext, 'Matterbridge');
|
|
876
|
+
this.log.debug(`Creating matter aggregator for ${plg}Matterbridge${db}`);
|
|
877
|
+
this.matterAggregator = await this.createMatterAggregator(this.matterbridgeContext);
|
|
878
|
+
this.log.debug('Adding matterbridge aggregator to commissioning server');
|
|
879
|
+
this.commissioningServer.addDevice(this.matterAggregator);
|
|
880
|
+
const device = new MatterbridgeDevice(DeviceTypes.CONTACT_SENSOR);
|
|
881
|
+
device.createDefaultIdentifyClusterServer();
|
|
882
|
+
//device.createDefaultBasicInformationClusterServer('Boolean test', '0x89930475', 0x8000, 'Matterbridge', 77, 'Boolean');
|
|
883
|
+
device.createDefaultBridgedDeviceBasicInformationClusterServer('Boolean test', '0x89930475', 0x8000, 'Matterbridge', 'Boolean');
|
|
884
|
+
device.createDefaultBooleanStateClusterServer(true);
|
|
885
|
+
device.createDefaultPowerSourceReplaceableBatteryClusterServer(75);
|
|
886
|
+
device.createDefaultPowerSourceConfigurationClusterServer(1);
|
|
887
|
+
//this.commissioningServer.addDevice(device);
|
|
888
|
+
//this.matterAggregator.addBridgedDevice(device);
|
|
889
|
+
this.log.debug('Adding matterbridge commissioning server to matter server');
|
|
890
|
+
await this.matterServer?.addCommissioningServer(this.commissioningServer, { uniqueStorageKey: 'Matterbridge' });
|
|
891
|
+
this.log.debug(`Setting reachability to true for ${plg}Matterbridge${db}`);
|
|
892
|
+
this.commissioningServer.setReachability(true);
|
|
893
|
+
this.log.debug('Starting matter server...');
|
|
894
|
+
await this.startMatterServer();
|
|
895
|
+
this.log.info('Matter server started');
|
|
896
|
+
await this.showCommissioningQRCode(this.commissioningServer, this.matterbridgeContext, this.nodeContext, 'Matterbridge');
|
|
897
|
+
//2024-03-31 21:54:12.065 DEBUG InteractionServer Received subscribe request from udp://fe80::14b6:bb16:5d3a:1293%9:51251 on session secure/48170 (keepSubscriptions=true, isFabricFiltered=false)
|
|
898
|
+
//2024-03-31 21:54:12.066 DEBUG InteractionServer Subscribe to attributes:*/*/*, events:!*/*/*
|
|
899
|
+
/*
|
|
900
|
+
2024-03-31 21:52:22.669 DEBUG SubscriptionHandler Sending subscription update message for ID 2529153003 with 1 attributes and 1 events
|
|
901
|
+
2024-03-31 21:52:22.669 DEBUG MessageExchange New exchange protocol: 1 id: 30278 session: secure/24489 peerSessionId: 2131 active threshold ms: 4000 active interval ms: 300 idle interval ms: 300 retries: 5
|
|
902
|
+
2024-03-31 21:52:22.669 DEBUG SubscriptionHandler Sending subscription changes for ID 2529153003: MA-contactsensor(0x3b)/BooleanState(0x45)/stateValue(0x0)=true (1805472651)
|
|
903
|
+
2024-03-31 21:52:22.670 DEBUG InteractionMessenger Sending DataReport chunk with 1 attributes and 1 events: 85 bytes
|
|
904
|
+
*/
|
|
905
|
+
setTimeout(() => {
|
|
906
|
+
this.matterAggregator?.addBridgedDevice(device);
|
|
907
|
+
this.log.info('Added device to aggregator');
|
|
908
|
+
}, 30 * 1000);
|
|
909
|
+
setInterval(() => {
|
|
910
|
+
const cluster = device.getClusterServer(BooleanStateCluster);
|
|
911
|
+
if (!cluster)
|
|
912
|
+
return;
|
|
913
|
+
const contact = cluster.getStateValueAttribute();
|
|
914
|
+
cluster.setStateValueAttribute(!contact);
|
|
915
|
+
if (cluster.isEventSupportedByName('stateChange'))
|
|
916
|
+
cluster.triggerStateChangeEvent({ stateValue: !contact });
|
|
917
|
+
this.log.info('Set attribute and event for BooleanStateCluster to', !contact);
|
|
918
|
+
}, 60 * 1000);
|
|
919
|
+
}, 1000);
|
|
872
920
|
}
|
|
873
921
|
/**
|
|
874
922
|
* Loads the configuration for a plugin.
|
|
@@ -884,8 +932,12 @@ export class Matterbridge extends EventEmitter {
|
|
|
884
932
|
try {
|
|
885
933
|
await fs.access(configFile);
|
|
886
934
|
const data = await fs.readFile(configFile, 'utf8');
|
|
887
|
-
|
|
888
|
-
|
|
935
|
+
const config = JSON.parse(data);
|
|
936
|
+
this.log.debug(`Config file found: ${configFile}.\nConfig:${rs}\n`, config);
|
|
937
|
+
/* The first time a plugin is added to the system, the config file is created with the plugin name and type "".*/
|
|
938
|
+
config.name = plugin.name;
|
|
939
|
+
config.type = plugin.type;
|
|
940
|
+
return config;
|
|
889
941
|
}
|
|
890
942
|
catch (err) {
|
|
891
943
|
if (err instanceof Error) {
|
|
@@ -1071,12 +1123,14 @@ export class Matterbridge extends EventEmitter {
|
|
|
1071
1123
|
}
|
|
1072
1124
|
else {
|
|
1073
1125
|
this.log.error(`Plugin ${plg}${plugin.name}${er} does not provide a default export`);
|
|
1074
|
-
return
|
|
1126
|
+
return;
|
|
1127
|
+
//return Promise.reject(new Error(`Plugin ${plg}${plugin.name}${er} does not provide a default export`));
|
|
1075
1128
|
}
|
|
1076
1129
|
}
|
|
1077
1130
|
catch (err) {
|
|
1078
1131
|
this.log.error(`Failed to load plugin ${plg}${plugin.name}${er}: ${err}`);
|
|
1079
|
-
return
|
|
1132
|
+
return;
|
|
1133
|
+
//return Promise.reject(new Error(`Failed to load plugin ${plg}${plugin.name}${er}: ${err}`));
|
|
1080
1134
|
}
|
|
1081
1135
|
}
|
|
1082
1136
|
/**
|
|
@@ -1088,7 +1142,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
1088
1142
|
* @private
|
|
1089
1143
|
* @returns {Promise<void>} A promise that resolves when the Matterbridge is started.
|
|
1090
1144
|
*/
|
|
1091
|
-
async
|
|
1145
|
+
async startMatterbridge() {
|
|
1092
1146
|
if (!this.storageManager) {
|
|
1093
1147
|
this.log.error('No storage manager initialized');
|
|
1094
1148
|
await this.cleanup('No storage manager initialized');
|
|
@@ -1129,7 +1183,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
1129
1183
|
this.log.info(`Data extracted from pairing code: ${Logger.toJSON(pairingCodeCodec)}`);
|
|
1130
1184
|
}
|
|
1131
1185
|
else {
|
|
1132
|
-
longDiscriminator = this.mattercontrollerContext.get('longDiscriminator', 3840);
|
|
1186
|
+
longDiscriminator = await this.mattercontrollerContext.get('longDiscriminator', 3840);
|
|
1133
1187
|
if (longDiscriminator > 4095)
|
|
1134
1188
|
throw new Error('Discriminator value must be less than 4096');
|
|
1135
1189
|
setupPin = this.mattercontrollerContext.get('pin', 20202021);
|
|
@@ -1238,30 +1292,62 @@ export class Matterbridge extends EventEmitter {
|
|
|
1238
1292
|
if (this.bridgeMode === 'bridge') {
|
|
1239
1293
|
// Plugins are loaded by loadPlugin on startup and plugin.loaded is set to true
|
|
1240
1294
|
// Plugins are started and configured by callback when Matterbridge is commissioned
|
|
1241
|
-
this.log.debug(
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1295
|
+
this.log.debug('***Starting startMatterbridge interval for Matterbridge');
|
|
1296
|
+
let failCount = 0;
|
|
1297
|
+
const startInterval = setInterval(async () => {
|
|
1298
|
+
for (const plugin of this.registeredPlugins) {
|
|
1299
|
+
if (!plugin.enabled)
|
|
1300
|
+
continue;
|
|
1301
|
+
if (!plugin.loaded) {
|
|
1302
|
+
this.log.info(`***Waiting (failSafeCount=${failCount}/30) in startMatterbridge interval for plugin ${plg}${plugin.name}${db} loaded: ${plugin.loaded}...`);
|
|
1303
|
+
failCount++;
|
|
1304
|
+
if (failCount > 30) {
|
|
1305
|
+
this.log.error(`***Failed to load plugin ${plg}${plugin.name}${er}`);
|
|
1306
|
+
plugin.error = true;
|
|
1307
|
+
}
|
|
1308
|
+
else {
|
|
1309
|
+
return;
|
|
1310
|
+
}
|
|
1311
|
+
}
|
|
1312
|
+
}
|
|
1313
|
+
clearInterval(startInterval);
|
|
1314
|
+
this.log.debug('***Cleared startMatterbridge interval for Matterbridge');
|
|
1315
|
+
this.log.debug(`Creating commissioning server context for ${plg}Matterbridge${db}`);
|
|
1316
|
+
this.matterbridgeContext = await this.createCommissioningServerContext('Matterbridge', 'Matterbridge', DeviceTypes.AGGREGATOR.code, 0xfff1, 'Matterbridge', 0x8000, 'Matterbridge Aggregator');
|
|
1317
|
+
if (!this.matterbridgeContext) {
|
|
1318
|
+
this.log.error(`Error creating storage context for ${plg}Matterbridge${er}`);
|
|
1319
|
+
return;
|
|
1320
|
+
}
|
|
1321
|
+
if (!this.nodeContext) {
|
|
1322
|
+
this.log.error(`Node storage context undefined for ${plg}Matterbridge${er}`);
|
|
1323
|
+
return;
|
|
1324
|
+
}
|
|
1325
|
+
await this.matterbridgeContext.set('softwareVersion', 1);
|
|
1326
|
+
await this.matterbridgeContext.set('softwareVersionString', this.matterbridgeVersion);
|
|
1327
|
+
this.log.debug(`Creating commissioning server for ${plg}Matterbridge${db}`);
|
|
1328
|
+
this.commissioningServer = await this.createCommisioningServer(this.matterbridgeContext, 'Matterbridge');
|
|
1329
|
+
this.log.debug(`Creating matter aggregator for ${plg}Matterbridge${db}`);
|
|
1330
|
+
this.matterAggregator = await this.createMatterAggregator(this.matterbridgeContext);
|
|
1331
|
+
this.log.debug('Adding matterbridge aggregator to commissioning server');
|
|
1332
|
+
this.commissioningServer.addDevice(this.matterAggregator);
|
|
1333
|
+
this.log.debug('Adding matterbridge commissioning server to matter server');
|
|
1334
|
+
await this.matterServer?.addCommissioningServer(this.commissioningServer, { uniqueStorageKey: 'Matterbridge' });
|
|
1335
|
+
this.log.debug(`Setting reachability to true for ${plg}Matterbridge${db}`);
|
|
1336
|
+
this.commissioningServer.setReachability(true);
|
|
1337
|
+
this.log.debug('Starting matter server...');
|
|
1338
|
+
await this.startMatterServer();
|
|
1339
|
+
this.log.info('Matter server started');
|
|
1340
|
+
await this.showCommissioningQRCode(this.commissioningServer, this.matterbridgeContext, this.nodeContext, 'Matterbridge');
|
|
1341
|
+
/*
|
|
1342
|
+
setInterval(() => {
|
|
1343
|
+
this.matterAggregator?.getBridgedDevices().forEach((device) => {
|
|
1344
|
+
this.log.info(`Bridged device: ${dev}${device.name}${nf}`);
|
|
1345
|
+
device.getClusterServer(BridgedDeviceBasicInformationCluster)?.setReachableAttribute(true);
|
|
1346
|
+
device.getClusterServer(BridgedDeviceBasicInformationCluster)?.triggerReachableChangedEvent({ reachableNewValue: true });
|
|
1347
|
+
});
|
|
1348
|
+
}, 30 * 1000);
|
|
1349
|
+
*/
|
|
1350
|
+
}, 1000);
|
|
1265
1351
|
}
|
|
1266
1352
|
if (this.bridgeMode === 'childbridge') {
|
|
1267
1353
|
// Plugins are loaded and started by loadPlugin on startup
|
|
@@ -1272,45 +1358,50 @@ export class Matterbridge extends EventEmitter {
|
|
|
1272
1358
|
if (!plugin.enabled)
|
|
1273
1359
|
return;
|
|
1274
1360
|
// Start the interval to check if the plugins is started
|
|
1275
|
-
|
|
1276
|
-
|
|
1361
|
+
this.log.debug(`*Starting startMatterbridge interval for plugin ${plg}${plugin.name}${db} loaded: ${plugin.loaded} started: ${plugin.started}...`);
|
|
1362
|
+
let failCount = 0;
|
|
1277
1363
|
const startInterval = setInterval(async () => {
|
|
1278
|
-
if (!
|
|
1279
|
-
this.log.
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1364
|
+
if (!plugin.loaded || !plugin.started /* || !plugin.configured*/) {
|
|
1365
|
+
this.log.info(`***Waiting (failSafeCount=${failCount}/30) in startMatterbridge interval for plugin ${plg}${plugin.name}${db} loaded: ${plugin.loaded} started: ${plugin.started}...`);
|
|
1366
|
+
failCount++;
|
|
1367
|
+
if (failCount > 30) {
|
|
1368
|
+
this.log.error(`***Failed to load plugin ${plg}${plugin.name}${er}`);
|
|
1369
|
+
plugin.error = true;
|
|
1370
|
+
clearInterval(startInterval);
|
|
1371
|
+
}
|
|
1285
1372
|
return;
|
|
1286
1373
|
}
|
|
1287
1374
|
if (plugin.type === 'AccessoryPlatform') {
|
|
1288
|
-
this.registeredDevices
|
|
1289
|
-
|
|
1290
|
-
|
|
1375
|
+
for (const registeredDevice of this.registeredDevices) {
|
|
1376
|
+
if (registeredDevice.plugin !== plugin.name)
|
|
1377
|
+
continue;
|
|
1291
1378
|
if (!plugin.storageContext)
|
|
1292
|
-
plugin.storageContext = this.importCommissioningServerContext(plugin.name, registeredDevice.device);
|
|
1379
|
+
plugin.storageContext = await this.importCommissioningServerContext(plugin.name, registeredDevice.device);
|
|
1380
|
+
await plugin.storageContext.set('softwareVersion', 1);
|
|
1381
|
+
await plugin.storageContext.set('softwareVersionString', this.matterbridgeVersion);
|
|
1293
1382
|
if (!plugin.commissioningServer)
|
|
1294
|
-
plugin.commissioningServer = this.createCommisioningServer(plugin.storageContext, plugin.name);
|
|
1383
|
+
plugin.commissioningServer = await this.createCommisioningServer(plugin.storageContext, plugin.name);
|
|
1295
1384
|
this.log.debug(`Adding device ${dev}${registeredDevice.device.name}${db} to commissioning server for plugin ${plg}${plugin.name}${db}`);
|
|
1296
1385
|
plugin.commissioningServer.addDevice(registeredDevice.device);
|
|
1297
|
-
}
|
|
1386
|
+
}
|
|
1298
1387
|
this.log.debug(`Adding commissioning server to matter server for plugin ${plg}${plugin.name}${db}`);
|
|
1299
|
-
await this.matterServer
|
|
1388
|
+
await this.matterServer?.addCommissioningServer(plugin.commissioningServer, { uniqueStorageKey: plugin.name });
|
|
1300
1389
|
}
|
|
1301
1390
|
if (plugin.type === 'DynamicPlatform') {
|
|
1302
|
-
|
|
1303
|
-
plugin.storageContext.
|
|
1304
|
-
plugin.storageContext.set('
|
|
1305
|
-
plugin.
|
|
1391
|
+
// eslint-disable-next-line max-len
|
|
1392
|
+
plugin.storageContext = await this.createCommissioningServerContext(plugin.name, 'Matterbridge', DeviceTypes.AGGREGATOR.code, 0xfff1, 'Matterbridge', 0x8000, 'Matterbridge Dynamic Platform');
|
|
1393
|
+
await plugin.storageContext.set('softwareVersion', 1);
|
|
1394
|
+
await plugin.storageContext.set('softwareVersionString', this.matterbridgeVersion);
|
|
1395
|
+
plugin.commissioningServer = await this.createCommisioningServer(plugin.storageContext, plugin.name);
|
|
1306
1396
|
this.log.debug(`Creating aggregator for plugin ${plg}${plugin.name}${db}`);
|
|
1307
|
-
plugin.aggregator = this.createMatterAggregator(plugin.storageContext); // Generate serialNumber and uniqueId
|
|
1397
|
+
plugin.aggregator = await this.createMatterAggregator(plugin.storageContext); // Generate serialNumber and uniqueId
|
|
1308
1398
|
this.log.debug(`Adding matter aggregator to commissioning server for plugin ${plg}${plugin.name}${db}`);
|
|
1309
1399
|
plugin.commissioningServer.addDevice(plugin.aggregator);
|
|
1310
1400
|
this.log.debug(`Adding commissioning server to matter server for plugin ${plg}${plugin.name}${db}`);
|
|
1311
|
-
await this.matterServer
|
|
1401
|
+
await this.matterServer?.addCommissioningServer(plugin.commissioningServer, { uniqueStorageKey: plugin.name });
|
|
1312
1402
|
}
|
|
1313
1403
|
clearInterval(startInterval);
|
|
1404
|
+
this.log.debug(`*Cleared startMatterbridge interval for plugin ${plg}${plugin.name}${db} loaded: ${plugin.loaded} started: ${plugin.started}...`);
|
|
1314
1405
|
}, 1000);
|
|
1315
1406
|
});
|
|
1316
1407
|
// Start the interval to check if all plugins are loaded and started and so start the matter server
|
|
@@ -1321,10 +1412,12 @@ export class Matterbridge extends EventEmitter {
|
|
|
1321
1412
|
this.registeredPlugins.forEach((plugin) => {
|
|
1322
1413
|
if (!plugin.enabled)
|
|
1323
1414
|
return;
|
|
1415
|
+
if (plugin.error)
|
|
1416
|
+
return;
|
|
1324
1417
|
if (plugin.enabled && (!plugin.loaded || !plugin.started))
|
|
1325
1418
|
allStarted = false;
|
|
1326
1419
|
if (!allStarted)
|
|
1327
|
-
this.log.info(
|
|
1420
|
+
this.log.info(`***Waiting in start matter server interval for plugin ${plg}${plugin.name}${db} to load (${plugin.loaded}) and start (${plugin.started}) ...`);
|
|
1328
1421
|
});
|
|
1329
1422
|
if (!allStarted)
|
|
1330
1423
|
return;
|
|
@@ -1389,14 +1482,14 @@ export class Matterbridge extends EventEmitter {
|
|
|
1389
1482
|
* @returns The commissioning server context.
|
|
1390
1483
|
* @throws Error if the BasicInformationCluster is not found.
|
|
1391
1484
|
*/
|
|
1392
|
-
importCommissioningServerContext(pluginName, device) {
|
|
1485
|
+
async importCommissioningServerContext(pluginName, device) {
|
|
1393
1486
|
this.log.debug(`Importing matter commissioning server storage context from device for ${plg}${pluginName}${db}`);
|
|
1394
1487
|
const basic = device.getClusterServer(BasicInformationCluster);
|
|
1395
1488
|
if (!basic) {
|
|
1396
1489
|
throw new Error('importCommissioningServerContext error: cannot find the BasicInformationCluster');
|
|
1397
1490
|
}
|
|
1398
1491
|
//const random = 'CS' + CryptoNode.getRandomData(8).toHex();
|
|
1399
|
-
return this.createCommissioningServerContext(pluginName, basic.getNodeLabelAttribute(), DeviceTypeId(device.deviceType), basic.getVendorIdAttribute(), basic.getVendorNameAttribute(), basic.getProductIdAttribute(), basic.getProductNameAttribute(), basic.attributes.serialNumber?.getLocal(), basic.attributes.uniqueId?.getLocal(), basic.
|
|
1492
|
+
return await this.createCommissioningServerContext(pluginName, basic.getNodeLabelAttribute(), DeviceTypeId(device.deviceType), basic.getVendorIdAttribute(), basic.getVendorNameAttribute(), basic.getProductIdAttribute(), basic.getProductNameAttribute(), basic.attributes.serialNumber?.getLocal(), basic.attributes.uniqueId?.getLocal(), basic.getSoftwareVersionAttribute(), basic.getSoftwareVersionStringAttribute(), basic.getHardwareVersionAttribute(), basic.getHardwareVersionStringAttribute());
|
|
1400
1493
|
}
|
|
1401
1494
|
/**
|
|
1402
1495
|
* Creates a commissioning server storage context.
|
|
@@ -1416,7 +1509,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
1416
1509
|
* @param hardwareVersionString - The hardware version string of the device (optional).
|
|
1417
1510
|
* @returns The storage context for the commissioning server.
|
|
1418
1511
|
*/
|
|
1419
|
-
createCommissioningServerContext(pluginName, deviceName, deviceType, vendorId, vendorName, productId, productName, serialNumber, uniqueId, softwareVersion, softwareVersionString, hardwareVersion, hardwareVersionString) {
|
|
1512
|
+
async createCommissioningServerContext(pluginName, deviceName, deviceType, vendorId, vendorName, productId, productName, serialNumber, uniqueId, softwareVersion, softwareVersionString, hardwareVersion, hardwareVersionString) {
|
|
1420
1513
|
if (!this.storageManager) {
|
|
1421
1514
|
this.log.error('No storage manager initialized');
|
|
1422
1515
|
process.exit(1);
|
|
@@ -1424,20 +1517,23 @@ export class Matterbridge extends EventEmitter {
|
|
|
1424
1517
|
this.log.debug(`Creating commissioning server storage context for ${plg}${pluginName}${db}`);
|
|
1425
1518
|
const random = 'CS' + CryptoNode.getRandomData(8).toHex();
|
|
1426
1519
|
const storageContext = this.storageManager.createContext(pluginName);
|
|
1427
|
-
storageContext.set('deviceName', deviceName);
|
|
1428
|
-
storageContext.set('deviceType', deviceType);
|
|
1429
|
-
storageContext.set('vendorId', vendorId);
|
|
1430
|
-
storageContext.set('vendorName', vendorName.slice(0, 32));
|
|
1431
|
-
storageContext.set('productId', productId);
|
|
1432
|
-
storageContext.set('productName', productName.slice(0, 32));
|
|
1433
|
-
storageContext.set('nodeLabel', productName.slice(0, 32));
|
|
1434
|
-
storageContext.set('productLabel', productName.slice(0, 32));
|
|
1435
|
-
storageContext.set('serialNumber', storageContext.get('serialNumber', random));
|
|
1436
|
-
storageContext.set('uniqueId', storageContext.get('uniqueId', random));
|
|
1437
|
-
storageContext.set('softwareVersion', softwareVersion ?? 1);
|
|
1438
|
-
storageContext.set('softwareVersionString', softwareVersionString ?? '1.0');
|
|
1439
|
-
storageContext.set('hardwareVersion', hardwareVersion ?? 1);
|
|
1440
|
-
storageContext.set('hardwareVersionString', hardwareVersionString ?? '1.0');
|
|
1520
|
+
await storageContext.set('deviceName', deviceName);
|
|
1521
|
+
await storageContext.set('deviceType', deviceType);
|
|
1522
|
+
await storageContext.set('vendorId', vendorId);
|
|
1523
|
+
await storageContext.set('vendorName', vendorName.slice(0, 32));
|
|
1524
|
+
await storageContext.set('productId', productId);
|
|
1525
|
+
await storageContext.set('productName', productName.slice(0, 32));
|
|
1526
|
+
await storageContext.set('nodeLabel', productName.slice(0, 32));
|
|
1527
|
+
await storageContext.set('productLabel', productName.slice(0, 32));
|
|
1528
|
+
await storageContext.set('serialNumber', await storageContext.get('serialNumber', random));
|
|
1529
|
+
await storageContext.set('uniqueId', await storageContext.get('uniqueId', random));
|
|
1530
|
+
await storageContext.set('softwareVersion', softwareVersion ?? 1);
|
|
1531
|
+
await storageContext.set('softwareVersionString', softwareVersionString ?? '1.0.0');
|
|
1532
|
+
await storageContext.set('hardwareVersion', hardwareVersion ?? 1);
|
|
1533
|
+
await storageContext.set('hardwareVersionString', hardwareVersionString ?? '1.0.0');
|
|
1534
|
+
this.log.debug(`**Created commissioning server storage context for ${plg}${pluginName}${db}`);
|
|
1535
|
+
this.log.debug(`**- softwareVersion: ${await storageContext.get('softwareVersion')} softwareVersionString: ${await storageContext.get('softwareVersionString')}`);
|
|
1536
|
+
this.log.debug(`**- hardwareVersion: ${await storageContext.get('hardwareVersion')} hardwareVersionString: ${await storageContext.get('hardwareVersionString')}`);
|
|
1441
1537
|
return storageContext;
|
|
1442
1538
|
}
|
|
1443
1539
|
/**
|
|
@@ -1502,20 +1598,20 @@ export class Matterbridge extends EventEmitter {
|
|
|
1502
1598
|
* @param {string} pluginName - The name of the commissioning server.
|
|
1503
1599
|
* @returns {CommissioningServer} The created commissioning server.
|
|
1504
1600
|
*/
|
|
1505
|
-
createCommisioningServer(context, pluginName) {
|
|
1601
|
+
async createCommisioningServer(context, pluginName) {
|
|
1506
1602
|
this.log.debug(`Creating matter commissioning server for plugin ${plg}${pluginName}${db}`);
|
|
1507
|
-
const deviceName = context.get('deviceName');
|
|
1508
|
-
const deviceType = context.get('deviceType');
|
|
1509
|
-
const vendorId = context.get('vendorId');
|
|
1510
|
-
const vendorName = context.get('vendorName'); // Home app = Manufacturer
|
|
1511
|
-
const productId = context.get('productId');
|
|
1512
|
-
const productName = context.get('productName'); // Home app = Model
|
|
1513
|
-
const serialNumber = context.get('serialNumber');
|
|
1514
|
-
const uniqueId = context.get('uniqueId');
|
|
1515
|
-
const softwareVersion = context.get('softwareVersion', 1);
|
|
1516
|
-
const softwareVersionString = context.get('softwareVersionString', '1.0.0'); // Home app = Firmware Revision
|
|
1517
|
-
const hardwareVersion = context.get('hardwareVersion', 1);
|
|
1518
|
-
const hardwareVersionString = context.get('hardwareVersionString', '1.0.0');
|
|
1603
|
+
const deviceName = await context.get('deviceName');
|
|
1604
|
+
const deviceType = await context.get('deviceType');
|
|
1605
|
+
const vendorId = await context.get('vendorId');
|
|
1606
|
+
const vendorName = await context.get('vendorName'); // Home app = Manufacturer
|
|
1607
|
+
const productId = await context.get('productId');
|
|
1608
|
+
const productName = await context.get('productName'); // Home app = Model
|
|
1609
|
+
const serialNumber = await context.get('serialNumber');
|
|
1610
|
+
const uniqueId = await context.get('uniqueId');
|
|
1611
|
+
const softwareVersion = await context.get('softwareVersion', 1);
|
|
1612
|
+
const softwareVersionString = await context.get('softwareVersionString', '1.0.0'); // Home app = Firmware Revision
|
|
1613
|
+
const hardwareVersion = await context.get('hardwareVersion', 1);
|
|
1614
|
+
const hardwareVersionString = await context.get('hardwareVersionString', '1.0.0');
|
|
1519
1615
|
this.log.debug(`Creating matter commissioning server for plugin ${plg}${pluginName}${db} with deviceName ${deviceName} deviceType ${deviceType}(0x${deviceType.toString(16).padStart(4, '0')})`);
|
|
1520
1616
|
this.log.debug(`Creating matter commissioning server for plugin ${plg}${pluginName}${db} with uniqueId ${uniqueId} serialNumber ${serialNumber}`);
|
|
1521
1617
|
this.log.debug(`Creating matter commissioning server for plugin ${plg}${pluginName}${db} with softwareVersion ${softwareVersion} softwareVersionString ${softwareVersionString}`);
|
|
@@ -1645,10 +1741,10 @@ export class Matterbridge extends EventEmitter {
|
|
|
1645
1741
|
* @param {StorageContext} context - The storage context.
|
|
1646
1742
|
* @returns {Aggregator} - The created Matter Aggregator.
|
|
1647
1743
|
*/
|
|
1648
|
-
createMatterAggregator(context) {
|
|
1744
|
+
async createMatterAggregator(context) {
|
|
1649
1745
|
const random = 'AG' + CryptoNode.getRandomData(8).toHex();
|
|
1650
|
-
context.set('aggregatorSerialNumber', context.get('aggregatorSerialNumber', random));
|
|
1651
|
-
context.set('aggregatorUniqueId', context.get('aggregatorUniqueId', random));
|
|
1746
|
+
await context.set('aggregatorSerialNumber', await context.get('aggregatorSerialNumber', random));
|
|
1747
|
+
await context.set('aggregatorUniqueId', await context.get('aggregatorUniqueId', random));
|
|
1652
1748
|
const matterAggregator = new Aggregator();
|
|
1653
1749
|
matterAggregator.addClusterServer(ClusterServer(BasicInformationCluster, {
|
|
1654
1750
|
dataModelRevision: 1,
|
|
@@ -1659,8 +1755,8 @@ export class Matterbridge extends EventEmitter {
|
|
|
1659
1755
|
productName: 'Matterbridge aggregator',
|
|
1660
1756
|
productLabel: 'Matterbridge aggregator',
|
|
1661
1757
|
nodeLabel: 'Matterbridge aggregator',
|
|
1662
|
-
serialNumber: context.get('aggregatorSerialNumber'),
|
|
1663
|
-
uniqueId: context.get('aggregatorUniqueId'),
|
|
1758
|
+
serialNumber: await context.get('aggregatorSerialNumber'),
|
|
1759
|
+
uniqueId: await context.get('aggregatorUniqueId'),
|
|
1664
1760
|
softwareVersion: 1,
|
|
1665
1761
|
softwareVersionString: 'v.1.0',
|
|
1666
1762
|
hardwareVersion: 1,
|
|
@@ -1995,7 +2091,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
1995
2091
|
data.push({
|
|
1996
2092
|
pluginName: registeredDevice.plugin,
|
|
1997
2093
|
type: registeredDevice.device.name + ' (0x' + registeredDevice.device.deviceType.toString(16).padStart(4, '0') + ')',
|
|
1998
|
-
endpoint: registeredDevice.device.
|
|
2094
|
+
endpoint: registeredDevice.device.number,
|
|
1999
2095
|
name,
|
|
2000
2096
|
serial,
|
|
2001
2097
|
uniqueId,
|
|
@@ -2015,7 +2111,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
2015
2111
|
}
|
|
2016
2112
|
const data = [];
|
|
2017
2113
|
this.registeredDevices.forEach((registeredDevice) => {
|
|
2018
|
-
if (registeredDevice.plugin === selectedPluginName && registeredDevice.device.
|
|
2114
|
+
if (registeredDevice.plugin === selectedPluginName && registeredDevice.device.number === selectedDeviceEndpoint) {
|
|
2019
2115
|
const clusterServers = registeredDevice.device.getAllClusterServers();
|
|
2020
2116
|
clusterServers.forEach((clusterServer) => {
|
|
2021
2117
|
Object.entries(clusterServer.attributes).forEach(([key, value]) => {
|
|
@@ -2156,7 +2252,15 @@ export class Matterbridge extends EventEmitter {
|
|
|
2156
2252
|
if (pluginToEnable) {
|
|
2157
2253
|
pluginToEnable.enabled = true;
|
|
2158
2254
|
pluginToEnable.platform = await this.loadPlugin(pluginToEnable);
|
|
2159
|
-
|
|
2255
|
+
if (pluginToEnable.platform) {
|
|
2256
|
+
await this.startPlugin(pluginToEnable, 'The plugin has been enabled', true);
|
|
2257
|
+
pluginToEnable.enabled = false;
|
|
2258
|
+
}
|
|
2259
|
+
else {
|
|
2260
|
+
pluginToEnable.enabled = false;
|
|
2261
|
+
pluginToEnable.error = true;
|
|
2262
|
+
this.log.error(`Error enabling plugin ${plg}${param}${er}`);
|
|
2263
|
+
}
|
|
2160
2264
|
}
|
|
2161
2265
|
}
|
|
2162
2266
|
}
|
|
@@ -2220,6 +2324,8 @@ export class Matterbridge extends EventEmitter {
|
|
|
2220
2324
|
attributes += `Cover position: ${clusterServer.attributes.currentPositionLiftPercent100ths.getLocal() / 100}% `;
|
|
2221
2325
|
if (clusterServer.name === 'DoorLock')
|
|
2222
2326
|
attributes += `State: ${clusterServer.attributes.lockState.getLocal() === 1 ? 'Locked' : 'Not locked'} `;
|
|
2327
|
+
if (clusterServer.name === 'Thermostat')
|
|
2328
|
+
attributes += `Temperature: ${clusterServer.attributes.localTemperature.getLocal() / 100}°C `;
|
|
2223
2329
|
if (clusterServer.name === 'LevelControl')
|
|
2224
2330
|
attributes += `Level: ${clusterServer.getCurrentLevelAttribute()}% `;
|
|
2225
2331
|
if (clusterServer.name === 'ColorControl')
|