matterbridge 1.2.5 → 1.2.7
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 +32 -0
- package/README.md +30 -5
- package/TODO.md +0 -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/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 +7 -0
- package/dist/matterbridge.d.ts.map +1 -1
- package/dist/matterbridge.js +334 -198
- package/dist/matterbridge.js.map +1 -1
- package/dist/matterbridgeDevice.d.ts +37 -12
- package/dist/matterbridgeDevice.d.ts.map +1 -1
- package/dist/matterbridgeDevice.js +100 -18
- package/dist/matterbridgeDevice.js.map +1 -1
- package/dist/utils.d.ts +52 -1
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +121 -1
- package/dist/utils.js.map +1 -1
- package/frontend/build/asset-manifest.json +3 -3
- package/frontend/build/index.html +1 -1
- package/frontend/build/static/js/{main.6b861489.js → main.491fc08f.js} +3 -3
- package/frontend/build/static/js/main.491fc08f.js.map +1 -0
- package/matterbridge.service +5 -6
- package/package.json +2 -2
- 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/frontend/build/static/js/main.6b861489.js.map +0 -1
- /package/frontend/build/static/js/{main.6b861489.js.LICENSE.txt → main.491fc08f.js.LICENSE.txt} +0 -0
package/dist/matterbridge.js
CHANGED
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
*/
|
|
23
23
|
import { MatterbridgeDevice } from './matterbridgeDevice.js';
|
|
24
24
|
import { NodeStorageManager } from 'node-persist-manager';
|
|
25
|
-
import { AnsiLogger, BRIGHT, RESET, UNDERLINE, UNDERLINEOFF, YELLOW, db, debugStringify, stringify, er, nf, rs, wr } from 'node-ansi-logger';
|
|
25
|
+
import { AnsiLogger, BRIGHT, RESET, UNDERLINE, UNDERLINEOFF, YELLOW, db, debugStringify, stringify, er, nf, rs, wr, RED, GREEN } from 'node-ansi-logger';
|
|
26
26
|
import { fileURLToPath, pathToFileURL } from 'url';
|
|
27
27
|
import { promises as fs } from 'fs';
|
|
28
28
|
import { exec, spawn } from 'child_process';
|
|
@@ -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;
|
|
@@ -190,25 +199,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
190
199
|
// Set matter.js logger level and format
|
|
191
200
|
Logger.defaultLogLevel = this.debugEnabled ? Level.DEBUG : Level.INFO;
|
|
192
201
|
Logger.format = Format.ANSI;
|
|
193
|
-
// Initialize NodeStorage
|
|
194
|
-
/*
|
|
195
|
-
this.log.debug('Creating node storage manager');
|
|
196
|
-
this.nodeStorage = new NodeStorageManager({ dir: path.join(this.matterbridgeDirectory, 'storage'), logging: false });
|
|
197
|
-
this.log.debug('Creating node storage context for matterbridge');
|
|
198
|
-
this.nodeContext = await this.nodeStorage.createStorage('matterbridge');
|
|
199
|
-
// Get the plugins from node storage
|
|
200
|
-
this.registeredPlugins = await this.nodeContext.get<RegisteredPlugin[]>('plugins', []);
|
|
201
|
-
for (const plugin of this.registeredPlugins) {
|
|
202
|
-
this.log.debug(`Creating node storage context for plugin ${plugin.name}`);
|
|
203
|
-
plugin.nodeContext = await this.nodeStorage.createStorage(plugin.name);
|
|
204
|
-
await plugin.nodeContext.set<string>('name', plugin.name);
|
|
205
|
-
await plugin.nodeContext.set<string>('type', plugin.type);
|
|
206
|
-
await plugin.nodeContext.set<string>('path', plugin.path);
|
|
207
|
-
await plugin.nodeContext.set<string>('version', plugin.version);
|
|
208
|
-
await plugin.nodeContext.set<string>('description', plugin.description);
|
|
209
|
-
await plugin.nodeContext.set<string>('author', plugin.author);
|
|
210
|
-
}
|
|
211
|
-
*/
|
|
212
202
|
// Parse command line
|
|
213
203
|
this.parseCommandLine();
|
|
214
204
|
}
|
|
@@ -225,9 +215,13 @@ export class Matterbridge extends EventEmitter {
|
|
|
225
215
|
if (process.platform === 'win32' && command === 'npm') {
|
|
226
216
|
command = command + '.cmd';
|
|
227
217
|
}
|
|
218
|
+
if (process.platform === 'linux' && command === 'npm' && !hasParameter('docker')) {
|
|
219
|
+
args.unshift(command);
|
|
220
|
+
command = 'sudo';
|
|
221
|
+
}
|
|
228
222
|
return new Promise((resolve, reject) => {
|
|
229
223
|
const childProcess = spawn(command, args, {
|
|
230
|
-
stdio: 'inherit',
|
|
224
|
+
stdio: ['inherit', 'pipe', 'pipe'],
|
|
231
225
|
});
|
|
232
226
|
childProcess.on('error', (err) => {
|
|
233
227
|
this.log.error(`Failed to start child process: ${err.message}`);
|
|
@@ -250,6 +244,22 @@ export class Matterbridge extends EventEmitter {
|
|
|
250
244
|
childProcess.on('disconnect', () => {
|
|
251
245
|
this.log.info('Child process has been disconnected from the parent');
|
|
252
246
|
});
|
|
247
|
+
if (childProcess.stdout) {
|
|
248
|
+
childProcess.stdout.on('data', (data) => {
|
|
249
|
+
// Convert the Buffer data to a string.
|
|
250
|
+
const message = data.toString();
|
|
251
|
+
this.log.info(message);
|
|
252
|
+
// TODO: Send this message to the frontend.
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
if (childProcess.stderr) {
|
|
256
|
+
childProcess.stderr.on('data', (data) => {
|
|
257
|
+
// Convert the Buffer data to a string.
|
|
258
|
+
const message = data.toString();
|
|
259
|
+
this.log.error(message);
|
|
260
|
+
// TODO: Handle the error message.
|
|
261
|
+
});
|
|
262
|
+
}
|
|
253
263
|
});
|
|
254
264
|
}
|
|
255
265
|
/**
|
|
@@ -259,15 +269,27 @@ export class Matterbridge extends EventEmitter {
|
|
|
259
269
|
*/
|
|
260
270
|
async parseCommandLine() {
|
|
261
271
|
if (hasParameter('list')) {
|
|
262
|
-
this.log.info('Registered plugins
|
|
272
|
+
this.log.info('│ Registered plugins');
|
|
263
273
|
this.registeredPlugins.forEach((plugin, index) => {
|
|
264
|
-
if (index
|
|
265
|
-
this.log.info(
|
|
266
|
-
this.log.info(
|
|
274
|
+
if (index !== this.registeredPlugins.length - 1) {
|
|
275
|
+
this.log.info(`├─┬─ plugin ${plg}${plugin.name}${nf}: "${plg}${BRIGHT}${plugin.description}${RESET}${nf}" type: ${typ}${plugin.type}${nf} ${plugin.enabled ? GREEN : RED}enabled ${plugin.paired ? GREEN : RED}paired${nf}`);
|
|
276
|
+
this.log.info(`│ └─ entry ${UNDERLINE}${db}${plugin.path}${UNDERLINEOFF}${db}`);
|
|
267
277
|
}
|
|
268
278
|
else {
|
|
269
|
-
this.log.info(
|
|
270
|
-
this.log.info(
|
|
279
|
+
this.log.info(`└─┬─ plugin ${plg}${plugin.name}${nf}: "${plg}${BRIGHT}${plugin.description}${RESET}${nf}" type: ${typ}${plugin.type}${nf} ${plugin.enabled ? GREEN : RED}enabled ${plugin.paired ? GREEN : RED}paired${nf}`);
|
|
280
|
+
this.log.info(` └─ entry ${UNDERLINE}${db}${plugin.path}${UNDERLINEOFF}${db}`);
|
|
281
|
+
}
|
|
282
|
+
});
|
|
283
|
+
const serializedRegisteredDevices = await this.nodeContext?.get('devices', []);
|
|
284
|
+
this.log.info('│ Registered devices');
|
|
285
|
+
serializedRegisteredDevices?.forEach((device, index) => {
|
|
286
|
+
if (index !== serializedRegisteredDevices.length - 1) {
|
|
287
|
+
this.log.info(`├─┬─ plugin ${plg}${device.pluginName}${nf} device: ${dev}${device.deviceName}${nf} uniqueId: ${YELLOW}${device.uniqueId}${nf}`);
|
|
288
|
+
this.log.info(`│ └─ endpoint ${RED}${device.endpoint}${nf} ${typ}${device.endpointName}${nf} ${debugStringify(device.clusterServersId)}`);
|
|
289
|
+
}
|
|
290
|
+
else {
|
|
291
|
+
this.log.info(`└─┬─ plugin ${plg}${device.pluginName}${nf} device: ${dev}${device.deviceName}${nf} uniqueId: ${YELLOW}${device.uniqueId}${nf}`);
|
|
292
|
+
this.log.info(` └─ endpoint ${RED}${device.endpoint}${nf} ${typ}${device.endpointName}${nf} ${debugStringify(device.clusterServersId)}`);
|
|
271
293
|
}
|
|
272
294
|
});
|
|
273
295
|
this.emit('shutdown');
|
|
@@ -297,10 +319,33 @@ export class Matterbridge extends EventEmitter {
|
|
|
297
319
|
this.emit('shutdown');
|
|
298
320
|
process.exit(0);
|
|
299
321
|
}
|
|
300
|
-
|
|
322
|
+
if (hasParameter('factoryreset')) {
|
|
323
|
+
// Delete matter storage file
|
|
324
|
+
await fs.unlink(path.join(this.matterbridgeDirectory, 'matterbridge.json'));
|
|
325
|
+
// Delete node storage directory with its subdirectories
|
|
326
|
+
await fs.rmdir(path.join(this.matterbridgeDirectory, 'storage'), { recursive: true });
|
|
327
|
+
this.log.info('Factory reset done! Remove all paired devices from the controllers.');
|
|
328
|
+
this.emit('shutdown');
|
|
329
|
+
process.exit(0);
|
|
330
|
+
}
|
|
331
|
+
// Start the storage and create matterbridgeContext (we need it now for frontend and later for matterbridge)
|
|
301
332
|
await this.startStorage('json', path.join(this.matterbridgeDirectory, 'matterbridge.json'));
|
|
302
|
-
this.log.debug(`Creating commissioning server context for ${plg}Matterbridge${db}`);
|
|
303
333
|
this.matterbridgeContext = await this.createCommissioningServerContext('Matterbridge', 'Matterbridge', DeviceTypes.AGGREGATOR.code, 0xfff1, 'Matterbridge', 0x8000, 'Matterbridge aggregator');
|
|
334
|
+
if (hasParameter('reset') && getParameter('reset') === undefined) {
|
|
335
|
+
this.log.info('Resetting Matterbridge commissioning information...');
|
|
336
|
+
await this.matterbridgeContext?.clearAll();
|
|
337
|
+
await this.stopStorage();
|
|
338
|
+
this.log.info('Reset done! Remove the device from the controller.');
|
|
339
|
+
this.emit('shutdown');
|
|
340
|
+
process.exit(0);
|
|
341
|
+
}
|
|
342
|
+
if (getParameter('reset') && getParameter('reset') !== undefined) {
|
|
343
|
+
this.log.debug(`Reset plugin ${getParameter('reset')}`);
|
|
344
|
+
await this.executeCommandLine(getParameter('reset'), 'reset');
|
|
345
|
+
await this.stopStorage();
|
|
346
|
+
this.emit('shutdown');
|
|
347
|
+
process.exit(0);
|
|
348
|
+
}
|
|
304
349
|
// Initialize frontend
|
|
305
350
|
await this.initializeFrontend(getIntParameter('frontend'));
|
|
306
351
|
if (hasParameter('test')) {
|
|
@@ -311,9 +356,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
311
356
|
}
|
|
312
357
|
if (hasParameter('controller')) {
|
|
313
358
|
this.bridgeMode = 'controller';
|
|
314
|
-
this.
|
|
315
|
-
this.mattercontrollerContext = this.storageManager?.createContext('mattercontrollerContext');
|
|
316
|
-
await this.startMatterbridge();
|
|
359
|
+
await this.startMattercontroller();
|
|
317
360
|
return;
|
|
318
361
|
}
|
|
319
362
|
if (hasParameter('bridge')) {
|
|
@@ -464,6 +507,28 @@ export class Matterbridge extends EventEmitter {
|
|
|
464
507
|
this.log.warn(`Plugin ${plg}${packageJsonPath}${wr} not registerd in matterbridge`);
|
|
465
508
|
}
|
|
466
509
|
}
|
|
510
|
+
else if (mode === 'reset') {
|
|
511
|
+
const plugin = this.registeredPlugins.find((registeredPlugin) => registeredPlugin.name === packageJson.name);
|
|
512
|
+
if (plugin) {
|
|
513
|
+
plugin.loaded = undefined;
|
|
514
|
+
plugin.started = undefined;
|
|
515
|
+
plugin.configured = undefined;
|
|
516
|
+
plugin.connected = undefined;
|
|
517
|
+
plugin.paired = undefined;
|
|
518
|
+
plugin.connected = undefined;
|
|
519
|
+
if (!this.storageManager)
|
|
520
|
+
this.log.error(`Plugin ${plg}${plugin.name}${er} storageManager not found`);
|
|
521
|
+
const context = this.storageManager?.createContext(plugin.name);
|
|
522
|
+
if (!context)
|
|
523
|
+
this.log.error(`Plugin ${plg}${plugin.name}${er} context not found`);
|
|
524
|
+
await context?.clearAll();
|
|
525
|
+
await this.nodeContext?.set('plugins', this.getBaseRegisteredPlugins());
|
|
526
|
+
this.log.info(`Reset commissionig for plugin ${plg}${plugin.name}${nf} done! Remove the device from the controller.`);
|
|
527
|
+
}
|
|
528
|
+
else {
|
|
529
|
+
this.log.warn(`Plugin ${plg}${packageJsonPath}${wr} not registerd in matterbridge`);
|
|
530
|
+
}
|
|
531
|
+
}
|
|
467
532
|
}
|
|
468
533
|
catch (err) {
|
|
469
534
|
this.log.error(`Failed to load plugin from ${plg}${packageJsonPath}${er}: ${err}`);
|
|
@@ -524,7 +589,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
524
589
|
await this.savePluginConfig(plugin);
|
|
525
590
|
}
|
|
526
591
|
else {
|
|
527
|
-
this.log.warn(`Plugin ${plg}${plugin.name}${
|
|
592
|
+
this.log.warn(`Plugin ${plg}${plugin.name}${wr} platform not found`);
|
|
528
593
|
}
|
|
529
594
|
}
|
|
530
595
|
// Set reachability to false
|
|
@@ -536,12 +601,34 @@ export class Matterbridge extends EventEmitter {
|
|
|
536
601
|
this.log.error(`Plugin ${plg}${registeredDevice.plugin}${er} not found`);
|
|
537
602
|
return;
|
|
538
603
|
}
|
|
539
|
-
this.
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
604
|
+
if (this.bridgeMode === 'bridge' && registeredDevice.device.number) {
|
|
605
|
+
this.log.debug(`*-- device: ${dev}${registeredDevice.device.name}${db} plugin ${plg}${registeredDevice.plugin}${db} type ${plugin.type}${db}`);
|
|
606
|
+
registeredDevice.device.setBridgedDeviceReachability(false);
|
|
607
|
+
registeredDevice.device.getClusterServerById(BridgedDeviceBasicInformation.Cluster.id)?.triggerReachableChangedEvent({ reachableNewValue: false });
|
|
608
|
+
}
|
|
609
|
+
if (this.bridgeMode === 'childbridge') {
|
|
610
|
+
if (plugin.type === 'DynamicPlatform' && registeredDevice.device.number) {
|
|
611
|
+
this.log.debug(`*-- device: ${dev}${registeredDevice.device.name}${db} plugin ${plg}${registeredDevice.plugin}${db} type ${plugin.type}${db}`);
|
|
612
|
+
registeredDevice.device.setBridgedDeviceReachability(false);
|
|
613
|
+
registeredDevice.device.getClusterServerById(BridgedDeviceBasicInformation.Cluster.id)?.triggerReachableChangedEvent({ reachableNewValue: false });
|
|
614
|
+
}
|
|
615
|
+
}
|
|
544
616
|
});
|
|
617
|
+
if (this.bridgeMode === 'bridge') {
|
|
618
|
+
this.log.debug('*Changing reachability to false for Matterbridge');
|
|
619
|
+
this.matterAggregator?.getClusterServerById(BasicInformation.Cluster.id)?.setReachableAttribute(false);
|
|
620
|
+
this.matterAggregator?.getClusterServerById(BasicInformation.Cluster.id)?.triggerReachableChangedEvent({ reachableNewValue: false });
|
|
621
|
+
this.commissioningServer?.setReachability(false);
|
|
622
|
+
}
|
|
623
|
+
if (this.bridgeMode === 'childbridge') {
|
|
624
|
+
for (const plugin of this.registeredPlugins) {
|
|
625
|
+
if (!plugin.enabled || plugin.error) continue;
|
|
626
|
+
this.log.debug(`*Changing reachability to false for plugin ${plg}${plugin.name}${db} type ${plugin.type}`);
|
|
627
|
+
plugin.aggregator?.getClusterServerById(BasicInformation.Cluster.id)?.setReachableAttribute(false);
|
|
628
|
+
plugin.aggregator?.getClusterServerById(BasicInformation.Cluster.id)?.triggerReachableChangedEvent({ reachableNewValue: false });
|
|
629
|
+
plugin.commissioningServer?.setReachability(false);
|
|
630
|
+
}
|
|
631
|
+
}
|
|
545
632
|
*/
|
|
546
633
|
// Close the express server
|
|
547
634
|
if (this.expressServer) {
|
|
@@ -563,7 +650,9 @@ export class Matterbridge extends EventEmitter {
|
|
|
563
650
|
this.log.info('Saving registered devices...');
|
|
564
651
|
const serializedRegisteredDevices = [];
|
|
565
652
|
this.registeredDevices.forEach((registeredDevice) => {
|
|
566
|
-
|
|
653
|
+
const serializedMatterbridgeDevice = registeredDevice.device.serialize(registeredDevice.plugin);
|
|
654
|
+
//this.log.info(`- ${serializedMatterbridgeDevice.deviceName}${rs}\n`, serializedMatterbridgeDevice);
|
|
655
|
+
serializedRegisteredDevices.push(serializedMatterbridgeDevice);
|
|
567
656
|
});
|
|
568
657
|
await this.nodeContext.set('devices', serializedRegisteredDevices);
|
|
569
658
|
this.log.info('Saved registered devices');
|
|
@@ -717,8 +806,10 @@ export class Matterbridge extends EventEmitter {
|
|
|
717
806
|
this.log.warn(`Removing bridged device ${dev}${device.deviceName}${wr} (${dev}${device.name}${wr}) plugin ${plg}${pluginName}${wr} not connected`);
|
|
718
807
|
return;
|
|
719
808
|
}
|
|
720
|
-
//
|
|
809
|
+
// Remove the device from matterbridge aggregator in bridge mode
|
|
721
810
|
if (this.bridgeMode === 'bridge') {
|
|
811
|
+
device.setBridgedDeviceReachability(false);
|
|
812
|
+
device.getClusterServerById(BridgedDeviceBasicInformation.Cluster.id)?.triggerReachableChangedEvent({ reachableNewValue: false });
|
|
722
813
|
this.matterAggregator.removeBridgedDevice(device);
|
|
723
814
|
this.registeredDevices.forEach((registeredDevice, index) => {
|
|
724
815
|
if (registeredDevice.device === device) {
|
|
@@ -732,10 +823,10 @@ export class Matterbridge extends EventEmitter {
|
|
|
732
823
|
if (plugin.addedDevices !== undefined)
|
|
733
824
|
plugin.addedDevices--;
|
|
734
825
|
}
|
|
735
|
-
//
|
|
826
|
+
// Remove the device in childbridge mode
|
|
736
827
|
if (this.bridgeMode === 'childbridge') {
|
|
737
828
|
if (plugin.type === 'AccessoryPlatform') {
|
|
738
|
-
this.log.
|
|
829
|
+
this.log.info(`Removing bridged device ${dev}${device.deviceName}${nf} (${dev}${device.name}${nf}) for plugin ${plg}${pluginName}${nf}: AccessoryPlatform not supported in childbridge mode`);
|
|
739
830
|
}
|
|
740
831
|
else if (plugin.type === 'DynamicPlatform') {
|
|
741
832
|
this.registeredDevices.forEach((registeredDevice, index) => {
|
|
@@ -744,6 +835,8 @@ export class Matterbridge extends EventEmitter {
|
|
|
744
835
|
return;
|
|
745
836
|
}
|
|
746
837
|
});
|
|
838
|
+
device.setBridgedDeviceReachability(false);
|
|
839
|
+
device.getClusterServerById(BridgedDeviceBasicInformation.Cluster.id)?.triggerReachableChangedEvent({ reachableNewValue: false });
|
|
747
840
|
plugin.aggregator.removeBridgedDevice(device);
|
|
748
841
|
}
|
|
749
842
|
this.log.info(`Removed bridged device(${plugin.registeredDevices}/${plugin.addedDevices}) ${dev}${device.deviceName}${nf} (${dev}${device.name}${nf}) for plugin ${plg}${pluginName}${nf}`);
|
|
@@ -762,7 +855,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
762
855
|
async removeAllBridgedDevices(pluginName) {
|
|
763
856
|
const plugin = this.findPlugin(pluginName);
|
|
764
857
|
if (this.bridgeMode === 'childbridge' && plugin?.type === 'AccessoryPlatform') {
|
|
765
|
-
this.log.
|
|
858
|
+
this.log.info(`Removing devices for plugin ${plg}${pluginName}${nf}: AccessoryPlatform not supported in childbridge mode`);
|
|
766
859
|
return;
|
|
767
860
|
}
|
|
768
861
|
const devicesToRemove = [];
|
|
@@ -1158,20 +1251,23 @@ export class Matterbridge extends EventEmitter {
|
|
|
1158
1251
|
}
|
|
1159
1252
|
}
|
|
1160
1253
|
/**
|
|
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.
|
|
1254
|
+
* Starts the Matterbridge controller.
|
|
1166
1255
|
* @private
|
|
1167
1256
|
* @returns {Promise<void>} A promise that resolves when the Matterbridge is started.
|
|
1168
1257
|
*/
|
|
1169
|
-
async
|
|
1258
|
+
async startMattercontroller() {
|
|
1170
1259
|
if (!this.storageManager) {
|
|
1171
1260
|
this.log.error('No storage manager initialized');
|
|
1172
1261
|
await this.cleanup('No storage manager initialized');
|
|
1173
1262
|
return;
|
|
1174
1263
|
}
|
|
1264
|
+
this.log.info('Creating context: mattercontrollerContext');
|
|
1265
|
+
this.mattercontrollerContext = this.storageManager.createContext('mattercontrollerContext');
|
|
1266
|
+
if (!this.mattercontrollerContext) {
|
|
1267
|
+
this.log.error('No storage context mattercontrollerContext initialized');
|
|
1268
|
+
await this.cleanup('No storage context mattercontrollerContext initialized');
|
|
1269
|
+
return;
|
|
1270
|
+
}
|
|
1175
1271
|
this.log.debug('Starting matterbridge in mode', this.bridgeMode);
|
|
1176
1272
|
this.createMatterServer(this.storageManager);
|
|
1177
1273
|
if (!this.matterServer) {
|
|
@@ -1179,139 +1275,167 @@ export class Matterbridge extends EventEmitter {
|
|
|
1179
1275
|
await this.cleanup('No matter server initialized');
|
|
1180
1276
|
return;
|
|
1181
1277
|
}
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1278
|
+
this.log.info('Creating matter commissioning controller');
|
|
1279
|
+
this.commissioningController = new CommissioningController({
|
|
1280
|
+
autoConnect: false,
|
|
1281
|
+
});
|
|
1282
|
+
this.log.info('Adding matter commissioning controller to matter server');
|
|
1283
|
+
await this.matterServer.addCommissioningController(this.commissioningController);
|
|
1284
|
+
this.log.info('Starting matter server');
|
|
1285
|
+
await this.matterServer.start();
|
|
1286
|
+
this.log.info('Matter server started');
|
|
1287
|
+
if (hasParameter('pairingcode')) {
|
|
1288
|
+
this.log.info('Pairing device with pairingcode:', getParameter('pairingcode'));
|
|
1289
|
+
const pairingCode = getParameter('pairingcode');
|
|
1290
|
+
const ip = this.mattercontrollerContext.has('ip') ? this.mattercontrollerContext.get('ip') : undefined;
|
|
1291
|
+
const port = this.mattercontrollerContext.has('port') ? this.mattercontrollerContext.get('port') : undefined;
|
|
1292
|
+
let longDiscriminator, setupPin, shortDiscriminator;
|
|
1293
|
+
if (pairingCode !== undefined) {
|
|
1294
|
+
const pairingCodeCodec = ManualPairingCodeCodec.decode(pairingCode);
|
|
1295
|
+
shortDiscriminator = pairingCodeCodec.shortDiscriminator;
|
|
1296
|
+
longDiscriminator = undefined;
|
|
1297
|
+
setupPin = pairingCodeCodec.passcode;
|
|
1298
|
+
this.log.info(`Data extracted from pairing code: ${Logger.toJSON(pairingCodeCodec)}`);
|
|
1187
1299
|
}
|
|
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);
|
|
1300
|
+
else {
|
|
1301
|
+
longDiscriminator = await this.mattercontrollerContext.get('longDiscriminator', 3840);
|
|
1302
|
+
if (longDiscriminator > 4095)
|
|
1303
|
+
throw new Error('Discriminator value must be less than 4096');
|
|
1304
|
+
setupPin = this.mattercontrollerContext.get('pin', 20202021);
|
|
1305
|
+
}
|
|
1306
|
+
if ((shortDiscriminator === undefined && longDiscriminator === undefined) || setupPin === undefined) {
|
|
1307
|
+
throw new Error('Please specify the longDiscriminator of the device to commission with -longDiscriminator or provide a valid passcode with -passcode');
|
|
1241
1308
|
}
|
|
1309
|
+
const commissioningOptions = {
|
|
1310
|
+
regulatoryLocation: GeneralCommissioning.RegulatoryLocationType.IndoorOutdoor,
|
|
1311
|
+
regulatoryCountryCode: 'XX',
|
|
1312
|
+
};
|
|
1313
|
+
const options = {
|
|
1314
|
+
commissioning: commissioningOptions,
|
|
1315
|
+
discovery: {
|
|
1316
|
+
knownAddress: ip !== undefined && port !== undefined ? { ip, port, type: 'udp' } : undefined,
|
|
1317
|
+
identifierData: longDiscriminator !== undefined ? { longDiscriminator } : shortDiscriminator !== undefined ? { shortDiscriminator } : {},
|
|
1318
|
+
},
|
|
1319
|
+
passcode: setupPin,
|
|
1320
|
+
};
|
|
1321
|
+
this.log.info('Commissioning with options:', options);
|
|
1322
|
+
const nodeId = await this.commissioningController.commissionNode(options);
|
|
1323
|
+
this.log.info(`Commissioning successfully done with nodeId: ${nodeId.nodeId}`);
|
|
1324
|
+
this.log.info('ActiveSessionInformation:', this.commissioningController.getActiveSessionInformation());
|
|
1325
|
+
} // (hasParameter('pairingcode'))
|
|
1326
|
+
if (hasParameter('unpairall')) {
|
|
1327
|
+
this.log.info('***Commissioning controller unpairing all nodes...');
|
|
1242
1328
|
const nodeIds = this.commissioningController.getCommissionedNodes();
|
|
1243
|
-
this.log.info(`***Commissioning controller has ${nodeIds.length} nodes commisioned: ${this.commissioningController.isCommissioned()}`);
|
|
1244
1329
|
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
|
-
});
|
|
1330
|
+
this.log.info('***Commissioning controller unpairing node:', nodeId);
|
|
1331
|
+
await this.commissioningController.removeNode(nodeId);
|
|
1314
1332
|
}
|
|
1333
|
+
return;
|
|
1334
|
+
}
|
|
1335
|
+
if (hasParameter('discover')) {
|
|
1336
|
+
//const discover = await this.commissioningController.discoverCommissionableDevices({ productId: 0x8000, deviceType: 0xfff1 });
|
|
1337
|
+
//console.log(discover);
|
|
1338
|
+
}
|
|
1339
|
+
if (!this.commissioningController.isCommissioned()) {
|
|
1340
|
+
this.log.info('***Commissioning controller is not commissioned: use matterbridge -controller -pairingcode [pairingcode] to commission a device');
|
|
1341
|
+
return;
|
|
1342
|
+
}
|
|
1343
|
+
const nodeIds = this.commissioningController.getCommissionedNodes();
|
|
1344
|
+
this.log.info(`***Commissioning controller is commissioned ${this.commissioningController.isCommissioned()} and has ${nodeIds.length} nodes commisioned: `);
|
|
1345
|
+
for (const nodeId of nodeIds) {
|
|
1346
|
+
this.log.info(`***Connecting to commissioned node: ${nodeId}`);
|
|
1347
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
1348
|
+
const node = await this.commissioningController.connectNode(nodeId, {
|
|
1349
|
+
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)}`),
|
|
1350
|
+
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)}`),
|
|
1351
|
+
stateInformationCallback: (peerNodeId, info) => {
|
|
1352
|
+
switch (info) {
|
|
1353
|
+
case NodeStateInformation.Connected:
|
|
1354
|
+
this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} connected`);
|
|
1355
|
+
break;
|
|
1356
|
+
case NodeStateInformation.Disconnected:
|
|
1357
|
+
this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} disconnected`);
|
|
1358
|
+
break;
|
|
1359
|
+
case NodeStateInformation.Reconnecting:
|
|
1360
|
+
this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} reconnecting`);
|
|
1361
|
+
break;
|
|
1362
|
+
case NodeStateInformation.WaitingForDeviceDiscovery:
|
|
1363
|
+
this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} waiting for device discovery`);
|
|
1364
|
+
break;
|
|
1365
|
+
case NodeStateInformation.StructureChanged:
|
|
1366
|
+
this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} structure changed`);
|
|
1367
|
+
break;
|
|
1368
|
+
case NodeStateInformation.Decommissioned:
|
|
1369
|
+
this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} decommissioned`);
|
|
1370
|
+
break;
|
|
1371
|
+
default:
|
|
1372
|
+
this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} NodeStateInformation.${info}`);
|
|
1373
|
+
break;
|
|
1374
|
+
}
|
|
1375
|
+
},
|
|
1376
|
+
});
|
|
1377
|
+
//node.logStructure();
|
|
1378
|
+
// Get the interaction client
|
|
1379
|
+
this.log.info('Getting the interaction client');
|
|
1380
|
+
const interactionClient = await node.getInteractionClient();
|
|
1381
|
+
let cluster;
|
|
1382
|
+
let attributes;
|
|
1383
|
+
// Log BasicInformationCluster
|
|
1384
|
+
cluster = BasicInformationCluster;
|
|
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 PowerSourceCluster
|
|
1395
|
+
cluster = PowerSourceCluster;
|
|
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
|
+
// Log ThreadNetworkDiagnostics
|
|
1406
|
+
cluster = ThreadNetworkDiagnosticsCluster;
|
|
1407
|
+
attributes = await interactionClient.getMultipleAttributes({
|
|
1408
|
+
attributes: [{ clusterId: cluster.id }],
|
|
1409
|
+
});
|
|
1410
|
+
this.log.warn(`Cluster: ${cluster.name} attributes:`);
|
|
1411
|
+
attributes.forEach((attribute) => {
|
|
1412
|
+
this.log.info(
|
|
1413
|
+
// eslint-disable-next-line max-len
|
|
1414
|
+
`- 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}`);
|
|
1415
|
+
});
|
|
1416
|
+
}
|
|
1417
|
+
}
|
|
1418
|
+
/**
|
|
1419
|
+
* Starts the Matterbridge based on the bridge mode.
|
|
1420
|
+
* If the bridge mode is 'bridge', it creates a commissioning server, matter aggregator,
|
|
1421
|
+
* and starts the matter server.
|
|
1422
|
+
* If the bridge mode is 'childbridge', it starts the plugins, creates commissioning servers,
|
|
1423
|
+
* and starts the matter server when all plugins are loaded and started.
|
|
1424
|
+
* @private
|
|
1425
|
+
* @returns {Promise<void>} A promise that resolves when the Matterbridge is started.
|
|
1426
|
+
*/
|
|
1427
|
+
async startMatterbridge() {
|
|
1428
|
+
if (!this.storageManager) {
|
|
1429
|
+
this.log.error('No storage manager initialized');
|
|
1430
|
+
await this.cleanup('No storage manager initialized');
|
|
1431
|
+
return;
|
|
1432
|
+
}
|
|
1433
|
+
this.log.debug('Starting matterbridge in mode', this.bridgeMode);
|
|
1434
|
+
this.createMatterServer(this.storageManager);
|
|
1435
|
+
if (!this.matterServer) {
|
|
1436
|
+
this.log.error('No matter server initialized');
|
|
1437
|
+
await this.cleanup('No matter server initialized');
|
|
1438
|
+
return;
|
|
1315
1439
|
}
|
|
1316
1440
|
if (this.bridgeMode === 'bridge') {
|
|
1317
1441
|
// Plugins are loaded by loadPlugin on startup and plugin.loaded is set to true
|
|
@@ -1362,6 +1486,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
1362
1486
|
await this.startMatterServer();
|
|
1363
1487
|
this.log.info('Matter server started');
|
|
1364
1488
|
await this.showCommissioningQRCode(this.commissioningServer, this.matterbridgeContext, this.nodeContext, 'Matterbridge');
|
|
1489
|
+
//if (hasParameter('advertise')) await this.commissioningServer.advertise();
|
|
1365
1490
|
/*
|
|
1366
1491
|
setInterval(() => {
|
|
1367
1492
|
this.matterAggregator?.getBridgedDevices().forEach((device) => {
|
|
@@ -1445,6 +1570,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
1445
1570
|
});
|
|
1446
1571
|
if (!allStarted)
|
|
1447
1572
|
return;
|
|
1573
|
+
clearInterval(startMatterInterval);
|
|
1448
1574
|
this.log.info('Starting matter server...');
|
|
1449
1575
|
// Setting reachability to true
|
|
1450
1576
|
this.registeredPlugins.forEach((plugin) => {
|
|
@@ -1481,7 +1607,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
1481
1607
|
await this.showCommissioningQRCode(plugin.commissioningServer, plugin.storageContext, plugin.nodeContext, plugin.name);
|
|
1482
1608
|
}
|
|
1483
1609
|
Logger.defaultLogLevel = this.debugEnabled ? Level.DEBUG : Level.INFO;
|
|
1484
|
-
clearInterval(startMatterInterval);
|
|
1610
|
+
//clearInterval(startMatterInterval);
|
|
1485
1611
|
}, 1000);
|
|
1486
1612
|
}
|
|
1487
1613
|
}
|
|
@@ -1555,9 +1681,9 @@ export class Matterbridge extends EventEmitter {
|
|
|
1555
1681
|
await storageContext.set('softwareVersionString', softwareVersionString ?? '1.0.0');
|
|
1556
1682
|
await storageContext.set('hardwareVersion', hardwareVersion ?? 1);
|
|
1557
1683
|
await storageContext.set('hardwareVersionString', hardwareVersionString ?? '1.0.0');
|
|
1558
|
-
this.log.debug(
|
|
1559
|
-
this.log.debug(
|
|
1560
|
-
this.log.debug(
|
|
1684
|
+
this.log.debug(`Created commissioning server storage context for ${plg}${pluginName}${db}`);
|
|
1685
|
+
this.log.debug(`- softwareVersion: ${await storageContext.get('softwareVersion')} softwareVersionString: ${await storageContext.get('softwareVersionString')}`);
|
|
1686
|
+
this.log.debug(`- hardwareVersion: ${await storageContext.get('hardwareVersion')} hardwareVersionString: ${await storageContext.get('hardwareVersionString')}`);
|
|
1561
1687
|
return storageContext;
|
|
1562
1688
|
}
|
|
1563
1689
|
/**
|
|
@@ -1572,7 +1698,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
1572
1698
|
if (!commissioningServer || !storageContext || !pluginName)
|
|
1573
1699
|
return;
|
|
1574
1700
|
if (!commissioningServer.isCommissioned()) {
|
|
1575
|
-
this.log.info(`***The commissioning server for ${plg}${pluginName}${nf} is not commissioned. Pair it scanning the QR code ...`);
|
|
1701
|
+
this.log.info(`***The commissioning server on port ${commissioningServer.getPort()} for ${plg}${pluginName}${nf} is not commissioned. Pair it scanning the QR code ...`);
|
|
1576
1702
|
const { qrPairingCode, manualPairingCode } = commissioningServer.getPairingCode();
|
|
1577
1703
|
storageContext.set('qrPairingCode', qrPairingCode);
|
|
1578
1704
|
storageContext.set('manualPairingCode', manualPairingCode);
|
|
@@ -1591,7 +1717,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
1591
1717
|
await this.nodeContext?.set('plugins', this.getBaseRegisteredPlugins());
|
|
1592
1718
|
}
|
|
1593
1719
|
else {
|
|
1594
|
-
this.log.info(`***The commissioning server for ${plg}${pluginName}${nf} is already commissioned. Waiting for controllers to connect ...`);
|
|
1720
|
+
this.log.info(`***The commissioning server on port ${commissioningServer.getPort()} for ${plg}${pluginName}${nf} is already commissioned . Waiting for controllers to connect ...`);
|
|
1595
1721
|
if (pluginName !== 'Matterbridge') {
|
|
1596
1722
|
const plugin = this.findPlugin(pluginName);
|
|
1597
1723
|
if (plugin) {
|
|
@@ -1641,7 +1767,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
1641
1767
|
this.log.debug(`Creating matter commissioning server for plugin ${plg}${pluginName}${db} with softwareVersion ${softwareVersion} softwareVersionString ${softwareVersionString}`);
|
|
1642
1768
|
this.log.debug(`Creating matter commissioning server for plugin ${plg}${pluginName}${db} with hardwareVersion ${hardwareVersion} hardwareVersionString ${hardwareVersionString}`);
|
|
1643
1769
|
const commissioningServer = new CommissioningServer({
|
|
1644
|
-
port:
|
|
1770
|
+
port: this.port++,
|
|
1645
1771
|
passcode: undefined,
|
|
1646
1772
|
discriminator: undefined,
|
|
1647
1773
|
deviceName,
|
|
@@ -1728,9 +1854,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
1728
1854
|
this.log.warn(`***Commissioning removed from fabric ${fabricIndex} for ${plg}${pluginName}${nf}. Resetting the commissioning server ...`);
|
|
1729
1855
|
await commissioningServer.factoryReset();
|
|
1730
1856
|
if (pluginName === 'Matterbridge') {
|
|
1731
|
-
this.matterbridgeContext?.
|
|
1732
|
-
this.matterbridgeContext?.delete(`${pluginName}.EventHandler`);
|
|
1733
|
-
this.matterbridgeContext?.delete(`${pluginName}.SessionManager`);
|
|
1857
|
+
await this.matterbridgeContext?.clearAll();
|
|
1734
1858
|
}
|
|
1735
1859
|
else {
|
|
1736
1860
|
for (const plugin of this.registeredPlugins) {
|
|
@@ -1738,9 +1862,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
1738
1862
|
await plugin.platform?.onShutdown('Commissioning removed by the controller');
|
|
1739
1863
|
plugin.paired = false;
|
|
1740
1864
|
plugin.connected = false;
|
|
1741
|
-
plugin.storageContext?.
|
|
1742
|
-
plugin.storageContext?.delete(`${pluginName}.EventHandler`);
|
|
1743
|
-
plugin.storageContext?.delete(`${pluginName}.SessionManager`);
|
|
1865
|
+
await plugin.storageContext?.clearAll();
|
|
1744
1866
|
}
|
|
1745
1867
|
}
|
|
1746
1868
|
}
|
|
@@ -2057,7 +2179,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
2057
2179
|
*
|
|
2058
2180
|
* @param port The port number to run the frontend server on. Default is 3000.
|
|
2059
2181
|
*/
|
|
2060
|
-
async initializeFrontend(port =
|
|
2182
|
+
async initializeFrontend(port = 8283) {
|
|
2061
2183
|
this.log.debug(`Initializing the frontend on port ${YELLOW}${port}${db} static ${UNDERLINE}${path.join(this.rootDirectory, 'frontend/build')}${UNDERLINEOFF}${rs}`);
|
|
2062
2184
|
this.expressApp = express();
|
|
2063
2185
|
// Serve React build directory
|
|
@@ -2082,6 +2204,24 @@ export class Matterbridge extends EventEmitter {
|
|
|
2082
2204
|
res.json({ valid: false });
|
|
2083
2205
|
}
|
|
2084
2206
|
});
|
|
2207
|
+
// Endpoint to provide manual pairing code
|
|
2208
|
+
this.expressApp.get('/api/pairing-code', (req, res) => {
|
|
2209
|
+
this.log.debug('The frontend sent /api/pairing-code');
|
|
2210
|
+
if (!this.matterbridgeContext) {
|
|
2211
|
+
this.log.error('/api/pairing-code matterbridgeContext not found');
|
|
2212
|
+
res.json([]);
|
|
2213
|
+
return;
|
|
2214
|
+
}
|
|
2215
|
+
try {
|
|
2216
|
+
const qrData = { qrPairingCode: this.matterbridgeContext.get('qrPairingCode'), manualPairingCode: this.matterbridgeContext.get('manualPairingCode') };
|
|
2217
|
+
res.json(qrData);
|
|
2218
|
+
}
|
|
2219
|
+
catch (error) {
|
|
2220
|
+
if (this.bridgeMode === 'bridge')
|
|
2221
|
+
this.log.error('qrPairingCode for /api/qr-code not found');
|
|
2222
|
+
res.json({});
|
|
2223
|
+
}
|
|
2224
|
+
});
|
|
2085
2225
|
// Endpoint to provide QR pairing code
|
|
2086
2226
|
this.expressApp.get('/api/qr-code', (req, res) => {
|
|
2087
2227
|
this.log.debug('The frontend sent /api/qr-code');
|
|
@@ -2196,7 +2336,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
2196
2336
|
res.status(400).json({ error: 'No command provided' });
|
|
2197
2337
|
return;
|
|
2198
2338
|
}
|
|
2199
|
-
this.log.
|
|
2339
|
+
this.log.info(`Received frontend command: ${command}:${param}`);
|
|
2200
2340
|
// Handle the command setpassword from Settings
|
|
2201
2341
|
if (command === 'setpassword') {
|
|
2202
2342
|
const password = param.slice(1, -1); // Remove the first and last characters
|
|
@@ -2246,10 +2386,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2246
2386
|
}
|
|
2247
2387
|
this.updateProcess();
|
|
2248
2388
|
}
|
|
2249
|
-
// Handle the command update from Header
|
|
2250
|
-
if (command === 'update') {
|
|
2251
|
-
this.log.warn(`The /api/command/${command} is not yet implemented`);
|
|
2252
|
-
}
|
|
2253
2389
|
// Handle the command installplugin from Home
|
|
2254
2390
|
if (command === 'installplugin') {
|
|
2255
2391
|
param = param.replace(/\*/g, '\\');
|