matterbridge 1.2.5 → 1.2.6
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 +19 -0
- package/README.md +29 -4
- package/dist/AirQualityCluster.d.ts +2 -13
- package/dist/AirQualityCluster.d.ts.map +1 -1
- package/dist/AirQualityCluster.js +6 -6
- package/dist/AirQualityCluster.js.map +1 -1
- package/dist/EveHistoryCluster.d.ts +6 -26
- package/dist/EveHistoryCluster.d.ts.map +1 -1
- package/dist/EveHistoryCluster.js +6 -14
- package/dist/EveHistoryCluster.js.map +1 -1
- package/dist/TvocCluster.d.ts +2 -15
- package/dist/TvocCluster.d.ts.map +1 -1
- package/dist/TvocCluster.js +6 -6
- package/dist/TvocCluster.js.map +1 -1
- package/dist/matterbridge.d.ts +7 -0
- package/dist/matterbridge.d.ts.map +1 -1
- package/dist/matterbridge.js +273 -162
- package/dist/matterbridge.js.map +1 -1
- package/dist/matterbridgeDevice.js +1 -1
- package/dist/matterbridgeDevice.js.map +1 -1
- package/matterbridge.service +5 -6
- package/package.json +1 -1
- package/dist/matterbridgeComposed.d.ts +0 -43
- package/dist/matterbridgeComposed.d.ts.map +0 -1
- package/dist/matterbridgeComposed.js +0 -58
- package/dist/matterbridgeComposed.js.map +0 -1
package/dist/matterbridge.js
CHANGED
|
@@ -31,7 +31,9 @@ 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, BooleanStateCluster,
|
|
34
|
+
import { BasicInformationCluster, BooleanStateCluster,
|
|
35
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
36
|
+
BridgedDeviceBasicInformation, BridgedDeviceBasicInformationCluster, ClusterServer, GeneralCommissioning, PowerSourceCluster, ThreadNetworkDiagnosticsCluster, getClusterNameById, } from '@project-chip/matter-node.js/cluster';
|
|
35
37
|
import { DeviceTypeId, VendorId } from '@project-chip/matter-node.js/datatype';
|
|
36
38
|
import { Aggregator, DeviceTypes, NodeStateInformation } from '@project-chip/matter-node.js/device';
|
|
37
39
|
import { Format, Level, Logger } from '@project-chip/matter-node.js/log';
|
|
@@ -79,6 +81,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
79
81
|
matterbridgeLatestVersion = '';
|
|
80
82
|
bridgeMode = '';
|
|
81
83
|
debugEnabled = false;
|
|
84
|
+
port = 5540;
|
|
82
85
|
log;
|
|
83
86
|
hasCleanupStarted = false;
|
|
84
87
|
registeredPlugins = [];
|
|
@@ -137,6 +140,8 @@ export class Matterbridge extends EventEmitter {
|
|
|
137
140
|
- childbridge: start Matterbridge in childbridge mode
|
|
138
141
|
- frontend [port]: start the frontend on the given port (default 3000)
|
|
139
142
|
- debug: enable debug mode (default false)
|
|
143
|
+
- reset: remove the commissioning for Matterbridge (bridge mode). Shutdown Matterbridge before using it!
|
|
144
|
+
- factoryreset: remove all commissioning information and reset all internal storages. Shutdown Matterbridge before using it!
|
|
140
145
|
- list: list the registered plugins
|
|
141
146
|
- add [plugin path]: register the plugin from the given absolute or relative path
|
|
142
147
|
- add [plugin name]: register the globally installed plugin with the given name
|
|
@@ -145,9 +150,13 @@ export class Matterbridge extends EventEmitter {
|
|
|
145
150
|
- enable [plugin path]: enable the plugin from the given absolute or relative path
|
|
146
151
|
- enable [plugin name]: enable the globally installed plugin with the given name
|
|
147
152
|
- disable [plugin path]: disable the plugin from the given absolute or relative path
|
|
148
|
-
- disable [plugin name]: disable the globally installed plugin with the given name
|
|
153
|
+
- disable [plugin name]: disable the globally installed plugin with the given name
|
|
154
|
+
- reset [plugin path]: remove the commissioning for the plugin from the given absolute or relative path (childbridge mode). Shutdown Matterbridge before using it!
|
|
155
|
+
- reset [plugin name]: remove the commissioning for the globally installed plugin (childbridge mode). Shutdown Matterbridge before using it!\n`);
|
|
149
156
|
process.exit(0);
|
|
150
157
|
}
|
|
158
|
+
// Set the first port to use
|
|
159
|
+
this.port = getIntParameter('port') ?? 5540;
|
|
151
160
|
// Set Matterbridge logger
|
|
152
161
|
if (hasParameter('debug'))
|
|
153
162
|
this.debugEnabled = true;
|
|
@@ -225,6 +234,10 @@ export class Matterbridge extends EventEmitter {
|
|
|
225
234
|
if (process.platform === 'win32' && command === 'npm') {
|
|
226
235
|
command = command + '.cmd';
|
|
227
236
|
}
|
|
237
|
+
if (process.platform === 'linux' && command === 'npm') {
|
|
238
|
+
args.unshift(command);
|
|
239
|
+
command = 'sudo';
|
|
240
|
+
}
|
|
228
241
|
return new Promise((resolve, reject) => {
|
|
229
242
|
const childProcess = spawn(command, args, {
|
|
230
243
|
stdio: 'inherit',
|
|
@@ -297,10 +310,33 @@ export class Matterbridge extends EventEmitter {
|
|
|
297
310
|
this.emit('shutdown');
|
|
298
311
|
process.exit(0);
|
|
299
312
|
}
|
|
300
|
-
|
|
313
|
+
if (hasParameter('factoryreset')) {
|
|
314
|
+
// Delete matter storage file
|
|
315
|
+
await fs.unlink(path.join(this.matterbridgeDirectory, 'matterbridge.json'));
|
|
316
|
+
// Delete node storage directory with its subdirectories
|
|
317
|
+
await fs.rmdir(path.join(this.matterbridgeDirectory, 'storage'), { recursive: true });
|
|
318
|
+
this.log.info('Factory reset done! Remove all paired devices from the controllers.');
|
|
319
|
+
this.emit('shutdown');
|
|
320
|
+
process.exit(0);
|
|
321
|
+
}
|
|
322
|
+
// Start the storage and create matterbridgeContext (we need it now for frontend and later for matterbridge)
|
|
301
323
|
await this.startStorage('json', path.join(this.matterbridgeDirectory, 'matterbridge.json'));
|
|
302
|
-
this.log.debug(`Creating commissioning server context for ${plg}Matterbridge${db}`);
|
|
303
324
|
this.matterbridgeContext = await this.createCommissioningServerContext('Matterbridge', 'Matterbridge', DeviceTypes.AGGREGATOR.code, 0xfff1, 'Matterbridge', 0x8000, 'Matterbridge aggregator');
|
|
325
|
+
if (hasParameter('reset') && getParameter('reset') === undefined) {
|
|
326
|
+
this.log.info('Resetting Matterbridge commissioning information...');
|
|
327
|
+
await this.matterbridgeContext?.clearAll();
|
|
328
|
+
await this.stopStorage();
|
|
329
|
+
this.log.info('Reset done! Remove the device from the controller.');
|
|
330
|
+
this.emit('shutdown');
|
|
331
|
+
process.exit(0);
|
|
332
|
+
}
|
|
333
|
+
if (getParameter('reset') && getParameter('reset') !== undefined) {
|
|
334
|
+
this.log.debug(`Reset plugin ${getParameter('reset')}`);
|
|
335
|
+
await this.executeCommandLine(getParameter('reset'), 'reset');
|
|
336
|
+
await this.stopStorage();
|
|
337
|
+
this.emit('shutdown');
|
|
338
|
+
process.exit(0);
|
|
339
|
+
}
|
|
304
340
|
// Initialize frontend
|
|
305
341
|
await this.initializeFrontend(getIntParameter('frontend'));
|
|
306
342
|
if (hasParameter('test')) {
|
|
@@ -311,9 +347,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
311
347
|
}
|
|
312
348
|
if (hasParameter('controller')) {
|
|
313
349
|
this.bridgeMode = 'controller';
|
|
314
|
-
this.
|
|
315
|
-
this.mattercontrollerContext = this.storageManager?.createContext('mattercontrollerContext');
|
|
316
|
-
await this.startMatterbridge();
|
|
350
|
+
await this.startMattercontroller();
|
|
317
351
|
return;
|
|
318
352
|
}
|
|
319
353
|
if (hasParameter('bridge')) {
|
|
@@ -464,6 +498,28 @@ export class Matterbridge extends EventEmitter {
|
|
|
464
498
|
this.log.warn(`Plugin ${plg}${packageJsonPath}${wr} not registerd in matterbridge`);
|
|
465
499
|
}
|
|
466
500
|
}
|
|
501
|
+
else if (mode === 'reset') {
|
|
502
|
+
const plugin = this.registeredPlugins.find((registeredPlugin) => registeredPlugin.name === packageJson.name);
|
|
503
|
+
if (plugin) {
|
|
504
|
+
plugin.loaded = undefined;
|
|
505
|
+
plugin.started = undefined;
|
|
506
|
+
plugin.configured = undefined;
|
|
507
|
+
plugin.connected = undefined;
|
|
508
|
+
plugin.paired = undefined;
|
|
509
|
+
plugin.connected = undefined;
|
|
510
|
+
if (!this.storageManager)
|
|
511
|
+
this.log.error(`Plugin ${plg}${plugin.name}${er} storageManager not found`);
|
|
512
|
+
const context = this.storageManager?.createContext(plugin.name);
|
|
513
|
+
if (!context)
|
|
514
|
+
this.log.error(`Plugin ${plg}${plugin.name}${er} context not found`);
|
|
515
|
+
await context?.clearAll();
|
|
516
|
+
await this.nodeContext?.set('plugins', this.getBaseRegisteredPlugins());
|
|
517
|
+
this.log.info(`Reset commissionig for plugin ${plg}${plugin.name}${nf} done! Remove the device from the controller.`);
|
|
518
|
+
}
|
|
519
|
+
else {
|
|
520
|
+
this.log.warn(`Plugin ${plg}${packageJsonPath}${wr} not registerd in matterbridge`);
|
|
521
|
+
}
|
|
522
|
+
}
|
|
467
523
|
}
|
|
468
524
|
catch (err) {
|
|
469
525
|
this.log.error(`Failed to load plugin from ${plg}${packageJsonPath}${er}: ${err}`);
|
|
@@ -524,7 +580,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
524
580
|
await this.savePluginConfig(plugin);
|
|
525
581
|
}
|
|
526
582
|
else {
|
|
527
|
-
this.log.warn(`Plugin ${plg}${plugin.name}${
|
|
583
|
+
this.log.warn(`Plugin ${plg}${plugin.name}${wr} platform not found`);
|
|
528
584
|
}
|
|
529
585
|
}
|
|
530
586
|
// Set reachability to false
|
|
@@ -536,12 +592,34 @@ export class Matterbridge extends EventEmitter {
|
|
|
536
592
|
this.log.error(`Plugin ${plg}${registeredDevice.plugin}${er} not found`);
|
|
537
593
|
return;
|
|
538
594
|
}
|
|
539
|
-
this.
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
595
|
+
if (this.bridgeMode === 'bridge' && registeredDevice.device.number) {
|
|
596
|
+
this.log.debug(`*-- device: ${dev}${registeredDevice.device.name}${db} plugin ${plg}${registeredDevice.plugin}${db} type ${plugin.type}${db}`);
|
|
597
|
+
registeredDevice.device.setBridgedDeviceReachability(false);
|
|
598
|
+
registeredDevice.device.getClusterServerById(BridgedDeviceBasicInformation.Cluster.id)?.triggerReachableChangedEvent({ reachableNewValue: false });
|
|
599
|
+
}
|
|
600
|
+
if (this.bridgeMode === 'childbridge') {
|
|
601
|
+
if (plugin.type === 'DynamicPlatform' && registeredDevice.device.number) {
|
|
602
|
+
this.log.debug(`*-- device: ${dev}${registeredDevice.device.name}${db} plugin ${plg}${registeredDevice.plugin}${db} type ${plugin.type}${db}`);
|
|
603
|
+
registeredDevice.device.setBridgedDeviceReachability(false);
|
|
604
|
+
registeredDevice.device.getClusterServerById(BridgedDeviceBasicInformation.Cluster.id)?.triggerReachableChangedEvent({ reachableNewValue: false });
|
|
605
|
+
}
|
|
606
|
+
}
|
|
544
607
|
});
|
|
608
|
+
if (this.bridgeMode === 'bridge') {
|
|
609
|
+
this.log.debug('*Changing reachability to false for Matterbridge');
|
|
610
|
+
this.matterAggregator?.getClusterServerById(BasicInformation.Cluster.id)?.setReachableAttribute(false);
|
|
611
|
+
this.matterAggregator?.getClusterServerById(BasicInformation.Cluster.id)?.triggerReachableChangedEvent({ reachableNewValue: false });
|
|
612
|
+
this.commissioningServer?.setReachability(false);
|
|
613
|
+
}
|
|
614
|
+
if (this.bridgeMode === 'childbridge') {
|
|
615
|
+
for (const plugin of this.registeredPlugins) {
|
|
616
|
+
if (!plugin.enabled || plugin.error) continue;
|
|
617
|
+
this.log.debug(`*Changing reachability to false for plugin ${plg}${plugin.name}${db} type ${plugin.type}`);
|
|
618
|
+
plugin.aggregator?.getClusterServerById(BasicInformation.Cluster.id)?.setReachableAttribute(false);
|
|
619
|
+
plugin.aggregator?.getClusterServerById(BasicInformation.Cluster.id)?.triggerReachableChangedEvent({ reachableNewValue: false });
|
|
620
|
+
plugin.commissioningServer?.setReachability(false);
|
|
621
|
+
}
|
|
622
|
+
}
|
|
545
623
|
*/
|
|
546
624
|
// Close the express server
|
|
547
625
|
if (this.expressServer) {
|
|
@@ -717,8 +795,10 @@ export class Matterbridge extends EventEmitter {
|
|
|
717
795
|
this.log.warn(`Removing bridged device ${dev}${device.deviceName}${wr} (${dev}${device.name}${wr}) plugin ${plg}${pluginName}${wr} not connected`);
|
|
718
796
|
return;
|
|
719
797
|
}
|
|
720
|
-
//
|
|
798
|
+
// Remove the device from matterbridge aggregator in bridge mode
|
|
721
799
|
if (this.bridgeMode === 'bridge') {
|
|
800
|
+
device.setBridgedDeviceReachability(false);
|
|
801
|
+
device.getClusterServerById(BridgedDeviceBasicInformation.Cluster.id)?.triggerReachableChangedEvent({ reachableNewValue: false });
|
|
722
802
|
this.matterAggregator.removeBridgedDevice(device);
|
|
723
803
|
this.registeredDevices.forEach((registeredDevice, index) => {
|
|
724
804
|
if (registeredDevice.device === device) {
|
|
@@ -735,7 +815,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
735
815
|
// Only register the device in childbridge mode
|
|
736
816
|
if (this.bridgeMode === 'childbridge') {
|
|
737
817
|
if (plugin.type === 'AccessoryPlatform') {
|
|
738
|
-
this.log.warn(`Removing bridged device ${dev}${device.deviceName}${wr} (${dev}${device.name}${wr}) for plugin ${plg}${pluginName}${wr}
|
|
818
|
+
this.log.warn(`Removing bridged device ${dev}${device.deviceName}${wr} (${dev}${device.name}${wr}) for plugin ${plg}${pluginName}${wr}: AccessoryPlatform not supported in childbridge mode`);
|
|
739
819
|
}
|
|
740
820
|
else if (plugin.type === 'DynamicPlatform') {
|
|
741
821
|
this.registeredDevices.forEach((registeredDevice, index) => {
|
|
@@ -744,6 +824,8 @@ export class Matterbridge extends EventEmitter {
|
|
|
744
824
|
return;
|
|
745
825
|
}
|
|
746
826
|
});
|
|
827
|
+
device.setBridgedDeviceReachability(false);
|
|
828
|
+
device.getClusterServerById(BridgedDeviceBasicInformation.Cluster.id)?.triggerReachableChangedEvent({ reachableNewValue: false });
|
|
747
829
|
plugin.aggregator.removeBridgedDevice(device);
|
|
748
830
|
}
|
|
749
831
|
this.log.info(`Removed bridged device(${plugin.registeredDevices}/${plugin.addedDevices}) ${dev}${device.deviceName}${nf} (${dev}${device.name}${nf}) for plugin ${plg}${pluginName}${nf}`);
|
|
@@ -1158,20 +1240,23 @@ export class Matterbridge extends EventEmitter {
|
|
|
1158
1240
|
}
|
|
1159
1241
|
}
|
|
1160
1242
|
/**
|
|
1161
|
-
* Starts the Matterbridge
|
|
1162
|
-
* If the bridge mode is 'bridge', it creates a commissioning server, matter aggregator,
|
|
1163
|
-
* and starts the matter server.
|
|
1164
|
-
* If the bridge mode is 'childbridge', it starts the plugins, creates commissioning servers,
|
|
1165
|
-
* and starts the matter server when all plugins are loaded and started.
|
|
1243
|
+
* Starts the Matterbridge controller.
|
|
1166
1244
|
* @private
|
|
1167
1245
|
* @returns {Promise<void>} A promise that resolves when the Matterbridge is started.
|
|
1168
1246
|
*/
|
|
1169
|
-
async
|
|
1247
|
+
async startMattercontroller() {
|
|
1170
1248
|
if (!this.storageManager) {
|
|
1171
1249
|
this.log.error('No storage manager initialized');
|
|
1172
1250
|
await this.cleanup('No storage manager initialized');
|
|
1173
1251
|
return;
|
|
1174
1252
|
}
|
|
1253
|
+
this.log.info('Creating context: mattercontrollerContext');
|
|
1254
|
+
this.mattercontrollerContext = this.storageManager.createContext('mattercontrollerContext');
|
|
1255
|
+
if (!this.mattercontrollerContext) {
|
|
1256
|
+
this.log.error('No storage context mattercontrollerContext initialized');
|
|
1257
|
+
await this.cleanup('No storage context mattercontrollerContext initialized');
|
|
1258
|
+
return;
|
|
1259
|
+
}
|
|
1175
1260
|
this.log.debug('Starting matterbridge in mode', this.bridgeMode);
|
|
1176
1261
|
this.createMatterServer(this.storageManager);
|
|
1177
1262
|
if (!this.matterServer) {
|
|
@@ -1179,139 +1264,167 @@ export class Matterbridge extends EventEmitter {
|
|
|
1179
1264
|
await this.cleanup('No matter server initialized');
|
|
1180
1265
|
return;
|
|
1181
1266
|
}
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1267
|
+
this.log.info('Creating matter commissioning controller');
|
|
1268
|
+
this.commissioningController = new CommissioningController({
|
|
1269
|
+
autoConnect: false,
|
|
1270
|
+
});
|
|
1271
|
+
this.log.info('Adding matter commissioning controller to matter server');
|
|
1272
|
+
await this.matterServer.addCommissioningController(this.commissioningController);
|
|
1273
|
+
this.log.info('Starting matter server');
|
|
1274
|
+
await this.matterServer.start();
|
|
1275
|
+
this.log.info('Matter server started');
|
|
1276
|
+
if (hasParameter('pairingcode')) {
|
|
1277
|
+
this.log.info('Pairing device with pairingcode:', getParameter('pairingcode'));
|
|
1278
|
+
const pairingCode = getParameter('pairingcode');
|
|
1279
|
+
const ip = this.mattercontrollerContext.has('ip') ? this.mattercontrollerContext.get('ip') : undefined;
|
|
1280
|
+
const port = this.mattercontrollerContext.has('port') ? this.mattercontrollerContext.get('port') : undefined;
|
|
1281
|
+
let longDiscriminator, setupPin, shortDiscriminator;
|
|
1282
|
+
if (pairingCode !== undefined) {
|
|
1283
|
+
const pairingCodeCodec = ManualPairingCodeCodec.decode(pairingCode);
|
|
1284
|
+
shortDiscriminator = pairingCodeCodec.shortDiscriminator;
|
|
1285
|
+
longDiscriminator = undefined;
|
|
1286
|
+
setupPin = pairingCodeCodec.passcode;
|
|
1287
|
+
this.log.info(`Data extracted from pairing code: ${Logger.toJSON(pairingCodeCodec)}`);
|
|
1187
1288
|
}
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
this.log.info('Matter server started');
|
|
1197
|
-
if (hasParameter('pairingcode')) {
|
|
1198
|
-
const pairingCode = getParameter('pairingcode');
|
|
1199
|
-
const ip = this.mattercontrollerContext.has('ip') ? this.mattercontrollerContext.get('ip') : undefined;
|
|
1200
|
-
const port = this.mattercontrollerContext.has('port') ? this.mattercontrollerContext.get('port') : undefined;
|
|
1201
|
-
let longDiscriminator, setupPin, shortDiscriminator;
|
|
1202
|
-
if (pairingCode !== undefined) {
|
|
1203
|
-
const pairingCodeCodec = ManualPairingCodeCodec.decode(pairingCode);
|
|
1204
|
-
shortDiscriminator = pairingCodeCodec.shortDiscriminator;
|
|
1205
|
-
longDiscriminator = undefined;
|
|
1206
|
-
setupPin = pairingCodeCodec.passcode;
|
|
1207
|
-
this.log.info(`Data extracted from pairing code: ${Logger.toJSON(pairingCodeCodec)}`);
|
|
1208
|
-
}
|
|
1209
|
-
else {
|
|
1210
|
-
longDiscriminator = await this.mattercontrollerContext.get('longDiscriminator', 3840);
|
|
1211
|
-
if (longDiscriminator > 4095)
|
|
1212
|
-
throw new Error('Discriminator value must be less than 4096');
|
|
1213
|
-
setupPin = this.mattercontrollerContext.get('pin', 20202021);
|
|
1214
|
-
}
|
|
1215
|
-
if ((shortDiscriminator === undefined && longDiscriminator === undefined) || setupPin === undefined) {
|
|
1216
|
-
throw new Error('Please specify the longDiscriminator of the device to commission with -longDiscriminator or provide a valid passcode with -passcode');
|
|
1217
|
-
}
|
|
1218
|
-
const commissioningOptions = {
|
|
1219
|
-
regulatoryLocation: GeneralCommissioning.RegulatoryLocationType.IndoorOutdoor,
|
|
1220
|
-
regulatoryCountryCode: 'XX',
|
|
1221
|
-
};
|
|
1222
|
-
const options = {
|
|
1223
|
-
commissioning: commissioningOptions,
|
|
1224
|
-
discovery: {
|
|
1225
|
-
knownAddress: ip !== undefined && port !== undefined ? { ip, port, type: 'udp' } : undefined,
|
|
1226
|
-
identifierData: longDiscriminator !== undefined ? { longDiscriminator } : shortDiscriminator !== undefined ? { shortDiscriminator } : {},
|
|
1227
|
-
},
|
|
1228
|
-
passcode: setupPin,
|
|
1229
|
-
};
|
|
1230
|
-
//this.log.info(`Commissioning ... ${JSON.stringify(options)}`);
|
|
1231
|
-
this.log.info('Commissioning ...', options);
|
|
1232
|
-
const nodeId = await this.commissioningController.commissionNode(options);
|
|
1233
|
-
this.mattercontrollerContext.set('nodeId', nodeId.nodeId);
|
|
1234
|
-
this.log.info(`Commissioning successfully done with nodeId: ${nodeId.nodeId}`);
|
|
1235
|
-
// eslint-disable-next-line no-console
|
|
1236
|
-
this.log.info('ActiveSessionInformation:', this.commissioningController.getActiveSessionInformation());
|
|
1237
|
-
} // (hasParameter('pairingcode'))
|
|
1238
|
-
if (hasParameter('discover')) {
|
|
1239
|
-
//const discover = await this.commissioningController.discoverCommissionableDevices({ productId: 0x8000, deviceType: 0xfff1 });
|
|
1240
|
-
//console.log(discover);
|
|
1289
|
+
else {
|
|
1290
|
+
longDiscriminator = await this.mattercontrollerContext.get('longDiscriminator', 3840);
|
|
1291
|
+
if (longDiscriminator > 4095)
|
|
1292
|
+
throw new Error('Discriminator value must be less than 4096');
|
|
1293
|
+
setupPin = this.mattercontrollerContext.get('pin', 20202021);
|
|
1294
|
+
}
|
|
1295
|
+
if ((shortDiscriminator === undefined && longDiscriminator === undefined) || setupPin === undefined) {
|
|
1296
|
+
throw new Error('Please specify the longDiscriminator of the device to commission with -longDiscriminator or provide a valid passcode with -passcode');
|
|
1241
1297
|
}
|
|
1298
|
+
const commissioningOptions = {
|
|
1299
|
+
regulatoryLocation: GeneralCommissioning.RegulatoryLocationType.IndoorOutdoor,
|
|
1300
|
+
regulatoryCountryCode: 'XX',
|
|
1301
|
+
};
|
|
1302
|
+
const options = {
|
|
1303
|
+
commissioning: commissioningOptions,
|
|
1304
|
+
discovery: {
|
|
1305
|
+
knownAddress: ip !== undefined && port !== undefined ? { ip, port, type: 'udp' } : undefined,
|
|
1306
|
+
identifierData: longDiscriminator !== undefined ? { longDiscriminator } : shortDiscriminator !== undefined ? { shortDiscriminator } : {},
|
|
1307
|
+
},
|
|
1308
|
+
passcode: setupPin,
|
|
1309
|
+
};
|
|
1310
|
+
this.log.info('Commissioning with options:', options);
|
|
1311
|
+
const nodeId = await this.commissioningController.commissionNode(options);
|
|
1312
|
+
this.log.info(`Commissioning successfully done with nodeId: ${nodeId.nodeId}`);
|
|
1313
|
+
this.log.info('ActiveSessionInformation:', this.commissioningController.getActiveSessionInformation());
|
|
1314
|
+
} // (hasParameter('pairingcode'))
|
|
1315
|
+
if (hasParameter('unpairall')) {
|
|
1316
|
+
this.log.info('***Commissioning controller unpairing all nodes...');
|
|
1242
1317
|
const nodeIds = this.commissioningController.getCommissionedNodes();
|
|
1243
|
-
this.log.info(`***Commissioning controller has ${nodeIds.length} nodes commisioned: ${this.commissioningController.isCommissioned()}`);
|
|
1244
1318
|
for (const nodeId of nodeIds) {
|
|
1245
|
-
this.log.info(
|
|
1246
|
-
|
|
1247
|
-
const node = await this.commissioningController.connectNode(nodeId, {
|
|
1248
|
-
attributeChangedCallback: (peerNodeId, { path: { nodeId, clusterId, endpointId, attributeName }, value }) => this.log.info(`***mattributeChangedCallback ${peerNodeId}: Attribute ${nodeId}/${endpointId}/${clusterId}/${attributeName} changed to ${Logger.toJSON(value)}`),
|
|
1249
|
-
eventTriggeredCallback: (peerNodeId, { path: { nodeId, clusterId, endpointId, eventName }, events }) => this.log.info(`***eventTriggeredCallback ${peerNodeId}: Event ${nodeId}/${endpointId}/${clusterId}/${eventName} triggered with ${Logger.toJSON(events)}`),
|
|
1250
|
-
stateInformationCallback: (peerNodeId, info) => {
|
|
1251
|
-
switch (info) {
|
|
1252
|
-
case NodeStateInformation.Connected:
|
|
1253
|
-
this.log.info(`***stateInformationCallback ${peerNodeId}: Node ${nodeId} connected`);
|
|
1254
|
-
break;
|
|
1255
|
-
case NodeStateInformation.Disconnected:
|
|
1256
|
-
this.log.info(`***stateInformationCallback ${peerNodeId}: Node ${nodeId} disconnected`);
|
|
1257
|
-
break;
|
|
1258
|
-
case NodeStateInformation.Reconnecting:
|
|
1259
|
-
this.log.info(`***stateInformationCallback ${peerNodeId}: Node ${nodeId} reconnecting`);
|
|
1260
|
-
break;
|
|
1261
|
-
case NodeStateInformation.WaitingForDeviceDiscovery:
|
|
1262
|
-
this.log.info(`***stateInformationCallback ${peerNodeId}: Node ${nodeId} waiting for device discovery`);
|
|
1263
|
-
break;
|
|
1264
|
-
case NodeStateInformation.StructureChanged:
|
|
1265
|
-
this.log.info(`***stateInformationCallback ${peerNodeId}: Node ${nodeId} structure changed`);
|
|
1266
|
-
break;
|
|
1267
|
-
case NodeStateInformation.Decommissioned:
|
|
1268
|
-
this.log.info(`***stateInformationCallback ${peerNodeId}: Node ${nodeId} decommissioned`);
|
|
1269
|
-
break;
|
|
1270
|
-
default:
|
|
1271
|
-
this.log.info(`***stateInformationCallback ${peerNodeId}: Node ${nodeId} NodeStateInformation.${info}`);
|
|
1272
|
-
break;
|
|
1273
|
-
}
|
|
1274
|
-
},
|
|
1275
|
-
});
|
|
1276
|
-
node.logStructure();
|
|
1277
|
-
// Get the interaction client
|
|
1278
|
-
const interactionClient = await node.getInteractionClient();
|
|
1279
|
-
let cluster;
|
|
1280
|
-
let attributes;
|
|
1281
|
-
// Log BasicInformationCluster
|
|
1282
|
-
cluster = BasicInformationCluster;
|
|
1283
|
-
attributes = await interactionClient.getMultipleAttributes({
|
|
1284
|
-
attributes: [{ clusterId: cluster.id }],
|
|
1285
|
-
});
|
|
1286
|
-
this.log.warn(`Cluster: ${cluster.name} attributes:`);
|
|
1287
|
-
attributes.forEach((attribute) => {
|
|
1288
|
-
this.log.info(
|
|
1289
|
-
// eslint-disable-next-line max-len
|
|
1290
|
-
`- endpoint: ${attribute.path.endpointId} clusterId: ${attribute.path.clusterId} id: ${attribute.path.attributeId} name: ${attribute.path.attributeName}: ${typeof attribute.value === 'object' ? stringify(attribute.value) : attribute.value}`);
|
|
1291
|
-
});
|
|
1292
|
-
// Log PowerSourceCluster
|
|
1293
|
-
cluster = PowerSourceCluster;
|
|
1294
|
-
attributes = await interactionClient.getMultipleAttributes({
|
|
1295
|
-
attributes: [{ clusterId: cluster.id }],
|
|
1296
|
-
});
|
|
1297
|
-
this.log.warn(`Cluster: ${cluster.name} attributes:`);
|
|
1298
|
-
attributes.forEach((attribute) => {
|
|
1299
|
-
this.log.info(
|
|
1300
|
-
// eslint-disable-next-line max-len
|
|
1301
|
-
`- endpoint: ${attribute.path.endpointId} clusterId: ${attribute.path.clusterId} id: ${attribute.path.attributeId} name: ${attribute.path.attributeName}: ${typeof attribute.value === 'object' ? stringify(attribute.value) : attribute.value}`);
|
|
1302
|
-
});
|
|
1303
|
-
// Log ThreadNetworkDiagnostics
|
|
1304
|
-
cluster = ThreadNetworkDiagnosticsCluster;
|
|
1305
|
-
attributes = await interactionClient.getMultipleAttributes({
|
|
1306
|
-
attributes: [{ clusterId: cluster.id }],
|
|
1307
|
-
});
|
|
1308
|
-
this.log.warn(`Cluster: ${cluster.name} attributes:`);
|
|
1309
|
-
attributes.forEach((attribute) => {
|
|
1310
|
-
this.log.info(
|
|
1311
|
-
// eslint-disable-next-line max-len
|
|
1312
|
-
`- endpoint: ${attribute.path.endpointId} clusterId: ${attribute.path.clusterId} id: ${attribute.path.attributeId} name: ${attribute.path.attributeName}: ${typeof attribute.value === 'object' ? stringify(attribute.value) : attribute.value}`);
|
|
1313
|
-
});
|
|
1319
|
+
this.log.info('***Commissioning controller unpairing node:', nodeId);
|
|
1320
|
+
await this.commissioningController.removeNode(nodeId);
|
|
1314
1321
|
}
|
|
1322
|
+
return;
|
|
1323
|
+
}
|
|
1324
|
+
if (hasParameter('discover')) {
|
|
1325
|
+
//const discover = await this.commissioningController.discoverCommissionableDevices({ productId: 0x8000, deviceType: 0xfff1 });
|
|
1326
|
+
//console.log(discover);
|
|
1327
|
+
}
|
|
1328
|
+
if (!this.commissioningController.isCommissioned()) {
|
|
1329
|
+
this.log.info('***Commissioning controller is not commissioned: use matterbridge -controller -pairingcode [pairingcode] to commission a device');
|
|
1330
|
+
return;
|
|
1331
|
+
}
|
|
1332
|
+
const nodeIds = this.commissioningController.getCommissionedNodes();
|
|
1333
|
+
this.log.info(`***Commissioning controller is commissioned ${this.commissioningController.isCommissioned()} and has ${nodeIds.length} nodes commisioned: `);
|
|
1334
|
+
for (const nodeId of nodeIds) {
|
|
1335
|
+
this.log.info(`***Connecting to commissioned node: ${nodeId}`);
|
|
1336
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
1337
|
+
const node = await this.commissioningController.connectNode(nodeId, {
|
|
1338
|
+
attributeChangedCallback: (peerNodeId, { path: { nodeId, clusterId, endpointId, attributeName }, value }) => this.log.info(`***Commissioning controller attributeChangedCallback ${peerNodeId}: attribute ${nodeId}/${endpointId}/${clusterId}/${attributeName} changed to ${Logger.toJSON(value)}`),
|
|
1339
|
+
eventTriggeredCallback: (peerNodeId, { path: { nodeId, clusterId, endpointId, eventName }, events }) => this.log.info(`***Commissioning controller eventTriggeredCallback ${peerNodeId}: Event ${nodeId}/${endpointId}/${clusterId}/${eventName} triggered with ${Logger.toJSON(events)}`),
|
|
1340
|
+
stateInformationCallback: (peerNodeId, info) => {
|
|
1341
|
+
switch (info) {
|
|
1342
|
+
case NodeStateInformation.Connected:
|
|
1343
|
+
this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} connected`);
|
|
1344
|
+
break;
|
|
1345
|
+
case NodeStateInformation.Disconnected:
|
|
1346
|
+
this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} disconnected`);
|
|
1347
|
+
break;
|
|
1348
|
+
case NodeStateInformation.Reconnecting:
|
|
1349
|
+
this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} reconnecting`);
|
|
1350
|
+
break;
|
|
1351
|
+
case NodeStateInformation.WaitingForDeviceDiscovery:
|
|
1352
|
+
this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} waiting for device discovery`);
|
|
1353
|
+
break;
|
|
1354
|
+
case NodeStateInformation.StructureChanged:
|
|
1355
|
+
this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} structure changed`);
|
|
1356
|
+
break;
|
|
1357
|
+
case NodeStateInformation.Decommissioned:
|
|
1358
|
+
this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} decommissioned`);
|
|
1359
|
+
break;
|
|
1360
|
+
default:
|
|
1361
|
+
this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} NodeStateInformation.${info}`);
|
|
1362
|
+
break;
|
|
1363
|
+
}
|
|
1364
|
+
},
|
|
1365
|
+
});
|
|
1366
|
+
//node.logStructure();
|
|
1367
|
+
// Get the interaction client
|
|
1368
|
+
this.log.info('Getting the interaction client');
|
|
1369
|
+
const interactionClient = await node.getInteractionClient();
|
|
1370
|
+
let cluster;
|
|
1371
|
+
let attributes;
|
|
1372
|
+
// Log BasicInformationCluster
|
|
1373
|
+
cluster = BasicInformationCluster;
|
|
1374
|
+
attributes = await interactionClient.getMultipleAttributes({
|
|
1375
|
+
attributes: [{ clusterId: cluster.id }],
|
|
1376
|
+
});
|
|
1377
|
+
this.log.warn(`Cluster: ${cluster.name} attributes:`);
|
|
1378
|
+
attributes.forEach((attribute) => {
|
|
1379
|
+
this.log.info(
|
|
1380
|
+
// eslint-disable-next-line max-len
|
|
1381
|
+
`- endpoint ${attribute.path.endpointId} cluster ${getClusterNameById(attribute.path.clusterId)} (${attribute.path.clusterId}) attribute ${attribute.path.attributeName} (${attribute.path.attributeId}): ${typeof attribute.value === 'object' ? stringify(attribute.value) : attribute.value}`);
|
|
1382
|
+
});
|
|
1383
|
+
// Log PowerSourceCluster
|
|
1384
|
+
cluster = PowerSourceCluster;
|
|
1385
|
+
attributes = await interactionClient.getMultipleAttributes({
|
|
1386
|
+
attributes: [{ clusterId: cluster.id }],
|
|
1387
|
+
});
|
|
1388
|
+
this.log.warn(`Cluster: ${cluster.name} attributes:`);
|
|
1389
|
+
attributes.forEach((attribute) => {
|
|
1390
|
+
this.log.info(
|
|
1391
|
+
// eslint-disable-next-line max-len
|
|
1392
|
+
`- endpoint ${attribute.path.endpointId} cluster ${getClusterNameById(attribute.path.clusterId)} (${attribute.path.clusterId}) attribute ${attribute.path.attributeName} (${attribute.path.attributeId}): ${typeof attribute.value === 'object' ? stringify(attribute.value) : attribute.value}`);
|
|
1393
|
+
});
|
|
1394
|
+
// Log ThreadNetworkDiagnostics
|
|
1395
|
+
cluster = ThreadNetworkDiagnosticsCluster;
|
|
1396
|
+
attributes = await interactionClient.getMultipleAttributes({
|
|
1397
|
+
attributes: [{ clusterId: cluster.id }],
|
|
1398
|
+
});
|
|
1399
|
+
this.log.warn(`Cluster: ${cluster.name} attributes:`);
|
|
1400
|
+
attributes.forEach((attribute) => {
|
|
1401
|
+
this.log.info(
|
|
1402
|
+
// eslint-disable-next-line max-len
|
|
1403
|
+
`- endpoint ${attribute.path.endpointId} cluster ${getClusterNameById(attribute.path.clusterId)} (${attribute.path.clusterId}) attribute ${attribute.path.attributeName} (${attribute.path.attributeId}): ${typeof attribute.value === 'object' ? stringify(attribute.value) : attribute.value}`);
|
|
1404
|
+
});
|
|
1405
|
+
}
|
|
1406
|
+
}
|
|
1407
|
+
/**
|
|
1408
|
+
* Starts the Matterbridge based on the bridge mode.
|
|
1409
|
+
* If the bridge mode is 'bridge', it creates a commissioning server, matter aggregator,
|
|
1410
|
+
* and starts the matter server.
|
|
1411
|
+
* If the bridge mode is 'childbridge', it starts the plugins, creates commissioning servers,
|
|
1412
|
+
* and starts the matter server when all plugins are loaded and started.
|
|
1413
|
+
* @private
|
|
1414
|
+
* @returns {Promise<void>} A promise that resolves when the Matterbridge is started.
|
|
1415
|
+
*/
|
|
1416
|
+
async startMatterbridge() {
|
|
1417
|
+
if (!this.storageManager) {
|
|
1418
|
+
this.log.error('No storage manager initialized');
|
|
1419
|
+
await this.cleanup('No storage manager initialized');
|
|
1420
|
+
return;
|
|
1421
|
+
}
|
|
1422
|
+
this.log.debug('Starting matterbridge in mode', this.bridgeMode);
|
|
1423
|
+
this.createMatterServer(this.storageManager);
|
|
1424
|
+
if (!this.matterServer) {
|
|
1425
|
+
this.log.error('No matter server initialized');
|
|
1426
|
+
await this.cleanup('No matter server initialized');
|
|
1427
|
+
return;
|
|
1315
1428
|
}
|
|
1316
1429
|
if (this.bridgeMode === 'bridge') {
|
|
1317
1430
|
// Plugins are loaded by loadPlugin on startup and plugin.loaded is set to true
|
|
@@ -1362,6 +1475,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
1362
1475
|
await this.startMatterServer();
|
|
1363
1476
|
this.log.info('Matter server started');
|
|
1364
1477
|
await this.showCommissioningQRCode(this.commissioningServer, this.matterbridgeContext, this.nodeContext, 'Matterbridge');
|
|
1478
|
+
//if (hasParameter('advertise')) await this.commissioningServer.advertise();
|
|
1365
1479
|
/*
|
|
1366
1480
|
setInterval(() => {
|
|
1367
1481
|
this.matterAggregator?.getBridgedDevices().forEach((device) => {
|
|
@@ -1445,6 +1559,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
1445
1559
|
});
|
|
1446
1560
|
if (!allStarted)
|
|
1447
1561
|
return;
|
|
1562
|
+
clearInterval(startMatterInterval);
|
|
1448
1563
|
this.log.info('Starting matter server...');
|
|
1449
1564
|
// Setting reachability to true
|
|
1450
1565
|
this.registeredPlugins.forEach((plugin) => {
|
|
@@ -1481,7 +1596,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
1481
1596
|
await this.showCommissioningQRCode(plugin.commissioningServer, plugin.storageContext, plugin.nodeContext, plugin.name);
|
|
1482
1597
|
}
|
|
1483
1598
|
Logger.defaultLogLevel = this.debugEnabled ? Level.DEBUG : Level.INFO;
|
|
1484
|
-
clearInterval(startMatterInterval);
|
|
1599
|
+
//clearInterval(startMatterInterval);
|
|
1485
1600
|
}, 1000);
|
|
1486
1601
|
}
|
|
1487
1602
|
}
|
|
@@ -1555,9 +1670,9 @@ export class Matterbridge extends EventEmitter {
|
|
|
1555
1670
|
await storageContext.set('softwareVersionString', softwareVersionString ?? '1.0.0');
|
|
1556
1671
|
await storageContext.set('hardwareVersion', hardwareVersion ?? 1);
|
|
1557
1672
|
await storageContext.set('hardwareVersionString', hardwareVersionString ?? '1.0.0');
|
|
1558
|
-
this.log.debug(
|
|
1559
|
-
this.log.debug(
|
|
1560
|
-
this.log.debug(
|
|
1673
|
+
this.log.debug(`Created commissioning server storage context for ${plg}${pluginName}${db}`);
|
|
1674
|
+
this.log.debug(`- softwareVersion: ${await storageContext.get('softwareVersion')} softwareVersionString: ${await storageContext.get('softwareVersionString')}`);
|
|
1675
|
+
this.log.debug(`- hardwareVersion: ${await storageContext.get('hardwareVersion')} hardwareVersionString: ${await storageContext.get('hardwareVersionString')}`);
|
|
1561
1676
|
return storageContext;
|
|
1562
1677
|
}
|
|
1563
1678
|
/**
|
|
@@ -1572,7 +1687,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
1572
1687
|
if (!commissioningServer || !storageContext || !pluginName)
|
|
1573
1688
|
return;
|
|
1574
1689
|
if (!commissioningServer.isCommissioned()) {
|
|
1575
|
-
this.log.info(`***The commissioning server for ${plg}${pluginName}${nf} is not commissioned. Pair it scanning the QR code ...`);
|
|
1690
|
+
this.log.info(`***The commissioning server on port ${commissioningServer.getPort()} for ${plg}${pluginName}${nf} is not commissioned. Pair it scanning the QR code ...`);
|
|
1576
1691
|
const { qrPairingCode, manualPairingCode } = commissioningServer.getPairingCode();
|
|
1577
1692
|
storageContext.set('qrPairingCode', qrPairingCode);
|
|
1578
1693
|
storageContext.set('manualPairingCode', manualPairingCode);
|
|
@@ -1591,7 +1706,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
1591
1706
|
await this.nodeContext?.set('plugins', this.getBaseRegisteredPlugins());
|
|
1592
1707
|
}
|
|
1593
1708
|
else {
|
|
1594
|
-
this.log.info(`***The commissioning server for ${plg}${pluginName}${nf} is already commissioned. Waiting for controllers to connect ...`);
|
|
1709
|
+
this.log.info(`***The commissioning server on port ${commissioningServer.getPort()} for ${plg}${pluginName}${nf} is already commissioned . Waiting for controllers to connect ...`);
|
|
1595
1710
|
if (pluginName !== 'Matterbridge') {
|
|
1596
1711
|
const plugin = this.findPlugin(pluginName);
|
|
1597
1712
|
if (plugin) {
|
|
@@ -1641,7 +1756,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
1641
1756
|
this.log.debug(`Creating matter commissioning server for plugin ${plg}${pluginName}${db} with softwareVersion ${softwareVersion} softwareVersionString ${softwareVersionString}`);
|
|
1642
1757
|
this.log.debug(`Creating matter commissioning server for plugin ${plg}${pluginName}${db} with hardwareVersion ${hardwareVersion} hardwareVersionString ${hardwareVersionString}`);
|
|
1643
1758
|
const commissioningServer = new CommissioningServer({
|
|
1644
|
-
port:
|
|
1759
|
+
port: this.port++,
|
|
1645
1760
|
passcode: undefined,
|
|
1646
1761
|
discriminator: undefined,
|
|
1647
1762
|
deviceName,
|
|
@@ -1728,9 +1843,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
1728
1843
|
this.log.warn(`***Commissioning removed from fabric ${fabricIndex} for ${plg}${pluginName}${nf}. Resetting the commissioning server ...`);
|
|
1729
1844
|
await commissioningServer.factoryReset();
|
|
1730
1845
|
if (pluginName === 'Matterbridge') {
|
|
1731
|
-
this.matterbridgeContext?.
|
|
1732
|
-
this.matterbridgeContext?.delete(`${pluginName}.EventHandler`);
|
|
1733
|
-
this.matterbridgeContext?.delete(`${pluginName}.SessionManager`);
|
|
1846
|
+
await this.matterbridgeContext?.clearAll();
|
|
1734
1847
|
}
|
|
1735
1848
|
else {
|
|
1736
1849
|
for (const plugin of this.registeredPlugins) {
|
|
@@ -1738,9 +1851,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
1738
1851
|
await plugin.platform?.onShutdown('Commissioning removed by the controller');
|
|
1739
1852
|
plugin.paired = false;
|
|
1740
1853
|
plugin.connected = false;
|
|
1741
|
-
plugin.storageContext?.
|
|
1742
|
-
plugin.storageContext?.delete(`${pluginName}.EventHandler`);
|
|
1743
|
-
plugin.storageContext?.delete(`${pluginName}.SessionManager`);
|
|
1854
|
+
await plugin.storageContext?.clearAll();
|
|
1744
1855
|
}
|
|
1745
1856
|
}
|
|
1746
1857
|
}
|