matterbridge 1.2.13 → 1.2.15
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 +31 -1
- package/README.md +6 -3
- package/dist/defaultConfigSchema.d.ts +28 -0
- package/dist/defaultConfigSchema.d.ts.map +1 -0
- package/dist/defaultConfigSchema.js +217 -0
- package/dist/defaultConfigSchema.js.map +1 -0
- package/dist/matterbridge.d.ts +22 -0
- package/dist/matterbridge.d.ts.map +1 -1
- package/dist/matterbridge.js +321 -154
- package/dist/matterbridge.js.map +1 -1
- package/dist/matterbridgeDevice.d.ts +22 -14
- package/dist/matterbridgeDevice.d.ts.map +1 -1
- package/dist/matterbridgeDevice.js +2 -2
- package/dist/matterbridgeDevice.js.map +1 -1
- package/frontend/build/asset-manifest.json +6 -6
- package/frontend/build/index.html +1 -1
- package/frontend/build/static/css/main.57bd18a9.css +2 -0
- package/frontend/build/static/css/main.57bd18a9.css.map +1 -0
- package/frontend/build/static/js/main.5886e61b.js +3 -0
- package/frontend/build/static/js/{main.742e4290.js.LICENSE.txt → main.5886e61b.js.LICENSE.txt} +2 -0
- package/frontend/build/static/js/main.5886e61b.js.map +1 -0
- package/package.json +14 -7
- package/frontend/build/static/css/main.979e07d2.css +0 -2
- package/frontend/build/static/css/main.979e07d2.css.map +0 -1
- package/frontend/build/static/js/main.742e4290.js +0 -3
- package/frontend/build/static/js/main.742e4290.js.map +0 -1
package/dist/matterbridge.js
CHANGED
|
@@ -34,7 +34,7 @@ import path from 'path';
|
|
|
34
34
|
import WebSocket, { WebSocketServer } from 'ws';
|
|
35
35
|
import { BridgedDeviceBasicInformation, BridgedDeviceBasicInformationCluster } from './BridgedDeviceBasicInformationCluster.js';
|
|
36
36
|
import { CommissioningController, CommissioningServer, MatterServer } from '@project-chip/matter-node.js';
|
|
37
|
-
import { BasicInformationCluster,
|
|
37
|
+
import { BasicInformationCluster, ClusterServer, FixedLabelCluster, GeneralCommissioning, GeneralDiagnostics, GeneralDiagnosticsCluster, PowerSourceCluster, ThreadNetworkDiagnosticsCluster, getClusterNameById, } from '@project-chip/matter-node.js/cluster';
|
|
38
38
|
import { DeviceTypeId, VendorId } from '@project-chip/matter-node.js/datatype';
|
|
39
39
|
import { Aggregator, DeviceTypes, NodeStateInformation } from '@project-chip/matter-node.js/device';
|
|
40
40
|
import { Format, Level, Logger } from '@project-chip/matter-node.js/log';
|
|
@@ -42,6 +42,7 @@ import { ManualPairingCodeCodec, QrCodeSchema } from '@project-chip/matter-node.
|
|
|
42
42
|
import { StorageBackendDisk, StorageBackendJsonFile, StorageManager } from '@project-chip/matter-node.js/storage';
|
|
43
43
|
import { requireMinNodeVersion, getParameter, getIntParameter, hasParameter } from '@project-chip/matter-node.js/util';
|
|
44
44
|
import { CryptoNode } from '@project-chip/matter-node.js/crypto';
|
|
45
|
+
import { somfytahoma_config, somfytahoma_schema, zigbee2mqtt_config, zigbee2mqtt_schema } from './defaultConfigSchema.js';
|
|
45
46
|
const plg = '\u001B[38;5;33m';
|
|
46
47
|
const dev = '\u001B[38;5;79m';
|
|
47
48
|
const typ = '\u001B[38;5;207m';
|
|
@@ -50,6 +51,7 @@ const typ = '\u001B[38;5;207m';
|
|
|
50
51
|
*/
|
|
51
52
|
export class Matterbridge extends EventEmitter {
|
|
52
53
|
systemInformation = {
|
|
54
|
+
macAddress: '',
|
|
53
55
|
ipv4Address: '',
|
|
54
56
|
ipv6Address: '',
|
|
55
57
|
nodeVersion: '',
|
|
@@ -157,7 +159,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
157
159
|
addedDevices: 0,
|
|
158
160
|
};
|
|
159
161
|
this.registeredPlugins.push(plugin);
|
|
160
|
-
await this.nodeContext?.set('plugins', this.getBaseRegisteredPlugins());
|
|
162
|
+
await this.nodeContext?.set('plugins', await this.getBaseRegisteredPlugins());
|
|
161
163
|
// Log system info and create .matterbridge directory
|
|
162
164
|
await this.logNodeAndSystemInfo();
|
|
163
165
|
this.matterbridgeDirectory = dataPath;
|
|
@@ -179,7 +181,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
179
181
|
this.log.debug(`Creating commissioning server for ${plg}Matterbridge${db}`);
|
|
180
182
|
this.commissioningServer = await this.createCommisioningServer(this.matterbridgeContext, 'Matterbridge');
|
|
181
183
|
this.log.debug(`Creating matter aggregator for ${plg}Matterbridge${db}`);
|
|
182
|
-
this.matterAggregator = await this.createMatterAggregator(this.matterbridgeContext);
|
|
184
|
+
this.matterAggregator = await this.createMatterAggregator(this.matterbridgeContext, 'Matterbridge');
|
|
183
185
|
this.log.debug('Adding matterbridge aggregator to commissioning server');
|
|
184
186
|
this.commissioningServer.addDevice(this.matterAggregator);
|
|
185
187
|
this.log.debug('Adding matterbridge commissioning server to matter server');
|
|
@@ -499,7 +501,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
499
501
|
const plugin = { path: packageJsonPath, type: '', name: packageJson.name, version: packageJson.version, description: packageJson.description, author: packageJson.author, enabled: true };
|
|
500
502
|
if (await this.loadPlugin(plugin)) {
|
|
501
503
|
this.registeredPlugins.push(plugin);
|
|
502
|
-
await this.nodeContext?.set('plugins', this.getBaseRegisteredPlugins());
|
|
504
|
+
await this.nodeContext?.set('plugins', await this.getBaseRegisteredPlugins());
|
|
503
505
|
this.log.info(`Plugin ${plg}${packageJsonPath}${nf} type ${plugin.type} added to matterbridge`);
|
|
504
506
|
}
|
|
505
507
|
else {
|
|
@@ -513,7 +515,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
513
515
|
else if (mode === 'remove') {
|
|
514
516
|
if (this.registeredPlugins.find((registeredPlugin) => registeredPlugin.name === packageJson.name)) {
|
|
515
517
|
this.registeredPlugins.splice(this.registeredPlugins.findIndex((registeredPlugin) => registeredPlugin.name === packageJson.name), 1);
|
|
516
|
-
await this.nodeContext?.set('plugins', this.getBaseRegisteredPlugins());
|
|
518
|
+
await this.nodeContext?.set('plugins', await this.getBaseRegisteredPlugins());
|
|
517
519
|
this.log.info(`Plugin ${plg}${packageJsonPath}${nf} removed from matterbridge`);
|
|
518
520
|
}
|
|
519
521
|
else {
|
|
@@ -528,7 +530,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
528
530
|
plugin.started = undefined;
|
|
529
531
|
plugin.configured = undefined;
|
|
530
532
|
plugin.connected = undefined;
|
|
531
|
-
await this.nodeContext?.set('plugins', this.getBaseRegisteredPlugins());
|
|
533
|
+
await this.nodeContext?.set('plugins', await this.getBaseRegisteredPlugins());
|
|
532
534
|
this.log.info(`Plugin ${plg}${packageJsonPath}${nf} enabled`);
|
|
533
535
|
}
|
|
534
536
|
else {
|
|
@@ -543,7 +545,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
543
545
|
plugin.started = undefined;
|
|
544
546
|
plugin.configured = undefined;
|
|
545
547
|
plugin.connected = undefined;
|
|
546
|
-
await this.nodeContext?.set('plugins', this.getBaseRegisteredPlugins());
|
|
548
|
+
await this.nodeContext?.set('plugins', await this.getBaseRegisteredPlugins());
|
|
547
549
|
this.log.info(`Plugin ${plg}${packageJsonPath}${nf} disabled`);
|
|
548
550
|
}
|
|
549
551
|
else {
|
|
@@ -565,7 +567,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
565
567
|
if (!context)
|
|
566
568
|
this.log.error(`Plugin ${plg}${plugin.name}${er} context not found`);
|
|
567
569
|
await context?.clearAll();
|
|
568
|
-
await this.nodeContext?.set('plugins', this.getBaseRegisteredPlugins());
|
|
570
|
+
await this.nodeContext?.set('plugins', await this.getBaseRegisteredPlugins());
|
|
569
571
|
this.log.info(`Reset commissionig for plugin ${plg}${plugin.name}${nf} done! Remove the device from the controller.`);
|
|
570
572
|
}
|
|
571
573
|
else {
|
|
@@ -992,7 +994,8 @@ export class Matterbridge extends EventEmitter {
|
|
|
992
994
|
}
|
|
993
995
|
}
|
|
994
996
|
catch (error) {
|
|
995
|
-
this.log.error('Storage initialize() error!');
|
|
997
|
+
this.log.error('Storage initialize() error! The file .matterbridge/matterbridge.json may be corrupted.');
|
|
998
|
+
this.log.error('Please delete it and rename matterbridge.backup.json to matterbridge.json and try to restart Matterbridge.');
|
|
996
999
|
await this.cleanup('Storage initialize() error!');
|
|
997
1000
|
}
|
|
998
1001
|
}
|
|
@@ -1035,93 +1038,176 @@ export class Matterbridge extends EventEmitter {
|
|
|
1035
1038
|
this.mattercontrollerContext = undefined;
|
|
1036
1039
|
}
|
|
1037
1040
|
async testStartMatterBridge() {
|
|
1038
|
-
|
|
1039
|
-
// Plugins are started and configured by callback when Matterbridge is commissioned
|
|
1041
|
+
/*
|
|
1040
1042
|
if (!this.storageManager) {
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1043
|
+
this.log.error('No storage manager initialized');
|
|
1044
|
+
await this.cleanup('No storage manager initialized');
|
|
1045
|
+
return;
|
|
1044
1046
|
}
|
|
1045
1047
|
this.log.debug('Starting matterbridge in mode', this.bridgeMode);
|
|
1046
1048
|
this.matterServer = this.createMatterServer(this.storageManager);
|
|
1049
|
+
|
|
1047
1050
|
this.log.debug('***Starting startMatterbridge interval for Matterbridge');
|
|
1048
1051
|
let failCount = 0;
|
|
1049
1052
|
const startInterval = setInterval(async () => {
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1053
|
+
for (const plugin of this.registeredPlugins) {
|
|
1054
|
+
if (!plugin.enabled) continue;
|
|
1055
|
+
if (!plugin.loaded) {
|
|
1056
|
+
this.log.info(`***Waiting (failSafeCount=${failCount}/30) in startMatterbridge interval for plugin ${plg}${plugin.name}${db} loaded: ${plugin.loaded}...`);
|
|
1057
|
+
failCount++;
|
|
1058
|
+
if (failCount > 30) {
|
|
1059
|
+
this.log.error(`***Failed to load plugin ${plg}${plugin.name}${er}`);
|
|
1060
|
+
plugin.error = true;
|
|
1061
|
+
} else {
|
|
1062
|
+
return;
|
|
1063
|
+
}
|
|
1064
|
+
}
|
|
1065
|
+
}
|
|
1066
|
+
clearInterval(startInterval);
|
|
1067
|
+
this.log.debug('***Cleared startMatterbridge interval for Matterbridge');
|
|
1068
|
+
|
|
1069
|
+
this.log.debug(`Creating commissioning server context for ${plg}Matterbridge${db}`);
|
|
1070
|
+
this.matterbridgeContext = await this.createCommissioningServerContext('Matterbridge', 'Matterbridge', DeviceTypes.AGGREGATOR.code, 0xfff1, 'Matterbridge', 0x8000, 'Matterbridge Aggregator');
|
|
1071
|
+
if (!this.matterbridgeContext) {
|
|
1072
|
+
this.log.error(`Error creating storage context for ${plg}Matterbridge${er}`);
|
|
1073
|
+
return;
|
|
1074
|
+
}
|
|
1075
|
+
if (!this.nodeContext) {
|
|
1076
|
+
this.log.error(`Node storage context undefined for ${plg}Matterbridge${er}`);
|
|
1077
|
+
return;
|
|
1078
|
+
}
|
|
1079
|
+
await this.matterbridgeContext.set('softwareVersion', 1);
|
|
1080
|
+
await this.matterbridgeContext.set('softwareVersionString', this.matterbridgeVersion);
|
|
1081
|
+
this.log.debug(`Creating commissioning server for ${plg}Matterbridge${db}`);
|
|
1082
|
+
this.commissioningServer = await this.createCommisioningServer(this.matterbridgeContext, 'Matterbridge');
|
|
1083
|
+
|
|
1084
|
+
this.log.debug(`Creating matter aggregator for ${plg}Matterbridge${db}`);
|
|
1085
|
+
this.matterAggregator = await this.createMatterAggregator(this.matterbridgeContext, 'Matterbridge');
|
|
1086
|
+
this.log.debug('Adding matterbridge aggregator to commissioning server');
|
|
1087
|
+
this.commissioningServer.addDevice(this.matterAggregator);
|
|
1088
|
+
|
|
1089
|
+
const device = new MatterbridgeDevice(DeviceTypes.CONTACT_SENSOR);
|
|
1090
|
+
device.createDefaultIdentifyClusterServer();
|
|
1091
|
+
device.createDefaultBridgedDeviceBasicInformationClusterServer('Boolean test', '0x89930475', 0x8000, 'Matterbridge', 'Boolean');
|
|
1092
|
+
device.createDefaultBooleanStateClusterServer(true);
|
|
1093
|
+
device.createDefaultPowerSourceReplaceableBatteryClusterServer(75);
|
|
1094
|
+
device.createDefaultPowerSourceConfigurationClusterServer(1);
|
|
1095
|
+
|
|
1096
|
+
this.log.debug('Adding matterbridge commissioning server to matter server');
|
|
1097
|
+
await this.matterServer?.addCommissioningServer(this.commissioningServer, { uniqueStorageKey: 'Matterbridge' });
|
|
1098
|
+
this.log.debug(`Setting reachability to true for ${plg}Matterbridge${db}`);
|
|
1099
|
+
this.commissioningServer.setReachability(true);
|
|
1100
|
+
this.log.debug('Starting matter server...');
|
|
1101
|
+
await this.startMatterServer();
|
|
1102
|
+
this.log.info('Matter server started');
|
|
1103
|
+
await this.showCommissioningQRCode(this.commissioningServer, this.matterbridgeContext, this.nodeContext, 'Matterbridge');
|
|
1104
|
+
|
|
1105
|
+
setTimeout(() => {
|
|
1106
|
+
this.matterAggregator?.addBridgedDevice(device);
|
|
1107
|
+
this.log.info('Added device to aggregator');
|
|
1108
|
+
}, 30 * 1000);
|
|
1109
|
+
|
|
1110
|
+
setInterval(() => {
|
|
1111
|
+
const cluster = device.getClusterServer(BooleanStateCluster);
|
|
1112
|
+
if (!cluster) return;
|
|
1113
|
+
const contact = cluster.getStateValueAttribute();
|
|
1114
|
+
cluster.setStateValueAttribute(!contact);
|
|
1115
|
+
if (cluster.isEventSupportedByName('stateChange')) cluster.triggerStateChangeEvent!({ stateValue: !contact });
|
|
1116
|
+
this.log.info('Set attribute and event for BooleanStateCluster to', !contact);
|
|
1117
|
+
}, 60 * 1000);
|
|
1118
|
+
}, 1000);
|
|
1119
|
+
*/
|
|
1120
|
+
}
|
|
1121
|
+
/**
|
|
1122
|
+
* Loads the schema for a plugin.
|
|
1123
|
+
* If the schema file exists, it reads the file and returns the parsed JSON data.
|
|
1124
|
+
* If the schema file does not exist, it creates a new file with default configuration and returns it.
|
|
1125
|
+
* If any error occurs during file access or creation, it logs an error and return an empty schema.
|
|
1126
|
+
*
|
|
1127
|
+
* @param plugin - The plugin for which to load the schema.
|
|
1128
|
+
* @returns A promise that resolves to the loaded or created schema.
|
|
1129
|
+
*/
|
|
1130
|
+
async loadPluginSchema(plugin) {
|
|
1131
|
+
const schemaFile = path.join(this.matterbridgeDirectory, `${plugin.name}.schema.json`);
|
|
1132
|
+
try {
|
|
1133
|
+
await fs.access(schemaFile);
|
|
1134
|
+
const data = await fs.readFile(schemaFile, 'utf8');
|
|
1135
|
+
const schema = JSON.parse(data);
|
|
1136
|
+
schema.title = plugin.description;
|
|
1137
|
+
schema.description = plugin.name + ' v. ' + plugin.version + ' by ' + plugin.author;
|
|
1138
|
+
this.log.debug(`Schema file found: ${schemaFile}.\nSchema:${rs}\n`, schema);
|
|
1139
|
+
return schema;
|
|
1140
|
+
}
|
|
1141
|
+
catch (err) {
|
|
1142
|
+
if (err instanceof Error) {
|
|
1143
|
+
const nodeErr = err;
|
|
1144
|
+
if (nodeErr.code === 'ENOENT') {
|
|
1145
|
+
let schema;
|
|
1146
|
+
if (plugin.name === 'matterbridge-zigbee2mqtt')
|
|
1147
|
+
schema = zigbee2mqtt_schema;
|
|
1148
|
+
else if (plugin.name === 'matterbridge-somfy-tahoma')
|
|
1149
|
+
schema = somfytahoma_schema;
|
|
1150
|
+
else
|
|
1151
|
+
schema = {
|
|
1152
|
+
title: plugin.description,
|
|
1153
|
+
description: plugin.name + ' v. ' + plugin.version + ' by ' + plugin.author,
|
|
1154
|
+
type: 'object',
|
|
1155
|
+
properties: {
|
|
1156
|
+
name: {
|
|
1157
|
+
description: 'Plugin name',
|
|
1158
|
+
type: 'string',
|
|
1159
|
+
readOnly: true,
|
|
1160
|
+
},
|
|
1161
|
+
type: {
|
|
1162
|
+
description: 'Plugin type',
|
|
1163
|
+
type: 'string',
|
|
1164
|
+
readOnly: true,
|
|
1165
|
+
},
|
|
1166
|
+
unregisterOnShutdown: {
|
|
1167
|
+
description: 'Unregister all devices on shutdown (development only)',
|
|
1168
|
+
type: 'boolean',
|
|
1169
|
+
},
|
|
1170
|
+
},
|
|
1171
|
+
};
|
|
1172
|
+
try {
|
|
1173
|
+
await this.writeFile(schemaFile, JSON.stringify(schema, null, 2));
|
|
1174
|
+
this.log.debug(`Created schema file: ${schemaFile}.\nSchema:${rs}\n`, schema);
|
|
1175
|
+
return schema;
|
|
1059
1176
|
}
|
|
1060
|
-
|
|
1061
|
-
|
|
1177
|
+
catch (err) {
|
|
1178
|
+
this.log.error(`Error creating schema file ${schemaFile}: ${err}`);
|
|
1179
|
+
return schema;
|
|
1062
1180
|
}
|
|
1063
1181
|
}
|
|
1182
|
+
else {
|
|
1183
|
+
this.log.error(`Error accessing schema file ${schemaFile}: ${err}`);
|
|
1184
|
+
return {};
|
|
1185
|
+
}
|
|
1064
1186
|
}
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
this.log.debug(
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
device.createDefaultBooleanStateClusterServer(true);
|
|
1090
|
-
device.createDefaultPowerSourceReplaceableBatteryClusterServer(75);
|
|
1091
|
-
device.createDefaultPowerSourceConfigurationClusterServer(1);
|
|
1092
|
-
//this.commissioningServer.addDevice(device);
|
|
1093
|
-
//this.matterAggregator.addBridgedDevice(device);
|
|
1094
|
-
this.log.debug('Adding matterbridge commissioning server to matter server');
|
|
1095
|
-
await this.matterServer?.addCommissioningServer(this.commissioningServer, { uniqueStorageKey: 'Matterbridge' });
|
|
1096
|
-
this.log.debug(`Setting reachability to true for ${plg}Matterbridge${db}`);
|
|
1097
|
-
this.commissioningServer.setReachability(true);
|
|
1098
|
-
this.log.debug('Starting matter server...');
|
|
1099
|
-
await this.startMatterServer();
|
|
1100
|
-
this.log.info('Matter server started');
|
|
1101
|
-
await this.showCommissioningQRCode(this.commissioningServer, this.matterbridgeContext, this.nodeContext, 'Matterbridge');
|
|
1102
|
-
//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)
|
|
1103
|
-
//2024-03-31 21:54:12.066 DEBUG InteractionServer Subscribe to attributes:*/*/*, events:!*/*/*
|
|
1104
|
-
/*
|
|
1105
|
-
2024-03-31 21:52:22.669 DEBUG SubscriptionHandler Sending subscription update message for ID 2529153003 with 1 attributes and 1 events
|
|
1106
|
-
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
|
|
1107
|
-
2024-03-31 21:52:22.669 DEBUG SubscriptionHandler Sending subscription changes for ID 2529153003: MA-contactsensor(0x3b)/BooleanState(0x45)/stateValue(0x0)=true (1805472651)
|
|
1108
|
-
2024-03-31 21:52:22.670 DEBUG InteractionMessenger Sending DataReport chunk with 1 attributes and 1 events: 85 bytes
|
|
1109
|
-
*/
|
|
1110
|
-
setTimeout(() => {
|
|
1111
|
-
this.matterAggregator?.addBridgedDevice(device);
|
|
1112
|
-
this.log.info('Added device to aggregator');
|
|
1113
|
-
}, 30 * 1000);
|
|
1114
|
-
setInterval(() => {
|
|
1115
|
-
const cluster = device.getClusterServer(BooleanStateCluster);
|
|
1116
|
-
if (!cluster)
|
|
1117
|
-
return;
|
|
1118
|
-
const contact = cluster.getStateValueAttribute();
|
|
1119
|
-
cluster.setStateValueAttribute(!contact);
|
|
1120
|
-
if (cluster.isEventSupportedByName('stateChange'))
|
|
1121
|
-
cluster.triggerStateChangeEvent({ stateValue: !contact });
|
|
1122
|
-
this.log.info('Set attribute and event for BooleanStateCluster to', !contact);
|
|
1123
|
-
}, 60 * 1000);
|
|
1124
|
-
}, 1000);
|
|
1187
|
+
this.log.error(`Error loading schema file ${schemaFile}: ${err}`);
|
|
1188
|
+
return {};
|
|
1189
|
+
}
|
|
1190
|
+
}
|
|
1191
|
+
/**
|
|
1192
|
+
* Saves the plugin configuration to a JSON file.
|
|
1193
|
+
* @param plugin - The registered plugin.
|
|
1194
|
+
* @param config - The platform configuration.
|
|
1195
|
+
* @returns A promise that resolves when the configuration is saved successfully, or rejects with an error.
|
|
1196
|
+
*/
|
|
1197
|
+
async savePluginConfigFromJson(plugin, config) {
|
|
1198
|
+
if (!config.name || !config.type || config.name !== plugin.name) {
|
|
1199
|
+
this.log.error(`Error saving plugin ${plg}${plugin.name}${er} config`);
|
|
1200
|
+
return;
|
|
1201
|
+
}
|
|
1202
|
+
const configFile = path.join(this.matterbridgeDirectory, `${plugin.name}.config.json`);
|
|
1203
|
+
try {
|
|
1204
|
+
await this.writeFile(configFile, JSON.stringify(config, null, 2));
|
|
1205
|
+
this.log.debug(`Saved config file: ${configFile}.\nConfig:${rs}\n`, config);
|
|
1206
|
+
}
|
|
1207
|
+
catch (err) {
|
|
1208
|
+
this.log.error(`Error saving plugin ${plg}${plugin.name}${er} config: ${err}`);
|
|
1209
|
+
return;
|
|
1210
|
+
}
|
|
1125
1211
|
}
|
|
1126
1212
|
/**
|
|
1127
1213
|
* Loads the configuration for a plugin.
|
|
@@ -1148,22 +1234,30 @@ export class Matterbridge extends EventEmitter {
|
|
|
1148
1234
|
if (err instanceof Error) {
|
|
1149
1235
|
const nodeErr = err;
|
|
1150
1236
|
if (nodeErr.code === 'ENOENT') {
|
|
1237
|
+
let config;
|
|
1238
|
+
if (plugin.name === 'matterbridge-zigbee2mqtt')
|
|
1239
|
+
config = zigbee2mqtt_config;
|
|
1240
|
+
else if (plugin.name === 'matterbridge-somfy-tahoma')
|
|
1241
|
+
config = somfytahoma_config;
|
|
1242
|
+
else
|
|
1243
|
+
config = { name: plugin.name, type: plugin.type, unregisterOnShutdown: false };
|
|
1151
1244
|
try {
|
|
1152
|
-
await this.writeFile(configFile, JSON.stringify(
|
|
1153
|
-
this.log.debug(`Created config file: ${configFile}.\nConfig:${rs}\n`,
|
|
1154
|
-
return
|
|
1245
|
+
await this.writeFile(configFile, JSON.stringify(config, null, 2));
|
|
1246
|
+
this.log.debug(`Created config file: ${configFile}.\nConfig:${rs}\n`, config);
|
|
1247
|
+
return config;
|
|
1155
1248
|
}
|
|
1156
1249
|
catch (err) {
|
|
1157
1250
|
this.log.error(`Error creating config file ${configFile}: ${err}`);
|
|
1158
|
-
return
|
|
1251
|
+
return config;
|
|
1159
1252
|
}
|
|
1160
1253
|
}
|
|
1161
1254
|
else {
|
|
1162
1255
|
this.log.error(`Error accessing config file ${configFile}: ${err}`);
|
|
1163
|
-
return
|
|
1256
|
+
return {};
|
|
1164
1257
|
}
|
|
1165
1258
|
}
|
|
1166
|
-
|
|
1259
|
+
this.log.error(`Error loading config file ${configFile}: ${err}`);
|
|
1260
|
+
return {};
|
|
1167
1261
|
}
|
|
1168
1262
|
}
|
|
1169
1263
|
/**
|
|
@@ -1328,11 +1422,11 @@ export class Matterbridge extends EventEmitter {
|
|
|
1328
1422
|
plugin.loaded = true;
|
|
1329
1423
|
plugin.registeredDevices = 0;
|
|
1330
1424
|
plugin.addedDevices = 0;
|
|
1331
|
-
await this.nodeContext?.set('plugins', this.getBaseRegisteredPlugins());
|
|
1425
|
+
await this.nodeContext?.set('plugins', await this.getBaseRegisteredPlugins());
|
|
1332
1426
|
this.getLatestVersion(plugin.name)
|
|
1333
1427
|
.then(async (latestVersion) => {
|
|
1334
1428
|
plugin.latestVersion = latestVersion;
|
|
1335
|
-
await this.nodeContext?.set('plugins', this.getBaseRegisteredPlugins());
|
|
1429
|
+
await this.nodeContext?.set('plugins', await this.getBaseRegisteredPlugins());
|
|
1336
1430
|
if (plugin.version !== latestVersion)
|
|
1337
1431
|
this.log.warn(`The plugin ${plg}${plugin.name}${wr} is out of date. Current version: ${plugin.version}, Latest version: ${latestVersion}`);
|
|
1338
1432
|
else
|
|
@@ -1570,12 +1664,10 @@ export class Matterbridge extends EventEmitter {
|
|
|
1570
1664
|
this.log.error(`Node storage context undefined for ${plg}Matterbridge${er}`);
|
|
1571
1665
|
return;
|
|
1572
1666
|
}
|
|
1573
|
-
await this.matterbridgeContext.set('softwareVersion', 1);
|
|
1574
|
-
await this.matterbridgeContext.set('softwareVersionString', this.matterbridgeVersion);
|
|
1575
1667
|
this.log.debug(`Creating commissioning server for ${plg}Matterbridge${db}`);
|
|
1576
1668
|
this.commissioningServer = await this.createCommisioningServer(this.matterbridgeContext, 'Matterbridge');
|
|
1577
1669
|
this.log.debug(`Creating matter aggregator for ${plg}Matterbridge${db}`);
|
|
1578
|
-
this.matterAggregator = await this.createMatterAggregator(this.matterbridgeContext);
|
|
1670
|
+
this.matterAggregator = await this.createMatterAggregator(this.matterbridgeContext, 'Matterbridge');
|
|
1579
1671
|
this.log.debug('Adding matterbridge aggregator to commissioning server');
|
|
1580
1672
|
this.commissioningServer.addDevice(this.matterAggregator);
|
|
1581
1673
|
this.log.debug('Adding matterbridge commissioning server to matter server');
|
|
@@ -1622,8 +1714,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
1622
1714
|
continue;
|
|
1623
1715
|
if (!plugin.storageContext)
|
|
1624
1716
|
plugin.storageContext = await this.importCommissioningServerContext(plugin.name, registeredDevice.device);
|
|
1625
|
-
|
|
1626
|
-
await plugin.storageContext.set('softwareVersionString', this.matterbridgeVersion);
|
|
1717
|
+
this.log.debug(`Creating commissioning server for ${plg}${plugin.name}${db}`);
|
|
1627
1718
|
if (!plugin.commissioningServer)
|
|
1628
1719
|
plugin.commissioningServer = await this.createCommisioningServer(plugin.storageContext, plugin.name);
|
|
1629
1720
|
this.log.debug(`Adding device ${dev}${registeredDevice.device.name}${db} to commissioning server for plugin ${plg}${plugin.name}${db}`);
|
|
@@ -1635,13 +1726,12 @@ export class Matterbridge extends EventEmitter {
|
|
|
1635
1726
|
await this.matterServer?.addCommissioningServer(plugin.commissioningServer, { uniqueStorageKey: plugin.name });
|
|
1636
1727
|
}
|
|
1637
1728
|
if (plugin.type === 'DynamicPlatform') {
|
|
1638
|
-
|
|
1729
|
+
this.log.debug(`Creating commissioning server context for ${plg}${plugin.name}${db}`);
|
|
1639
1730
|
plugin.storageContext = await this.createCommissioningServerContext(plugin.name, 'Matterbridge', DeviceTypes.AGGREGATOR.code, 0xfff1, 'Matterbridge', 0x8000, 'Matterbridge Dynamic Platform');
|
|
1640
|
-
|
|
1641
|
-
await plugin.storageContext.set('softwareVersionString', this.matterbridgeVersion);
|
|
1731
|
+
this.log.debug(`Creating commissioning server for ${plg}${plugin.name}${db}`);
|
|
1642
1732
|
plugin.commissioningServer = await this.createCommisioningServer(plugin.storageContext, plugin.name);
|
|
1643
1733
|
this.log.debug(`Creating aggregator for plugin ${plg}${plugin.name}${db}`);
|
|
1644
|
-
plugin.aggregator = await this.createMatterAggregator(plugin.storageContext); // Generate serialNumber and uniqueId
|
|
1734
|
+
plugin.aggregator = await this.createMatterAggregator(plugin.storageContext, plugin.name); // Generate serialNumber and uniqueId
|
|
1645
1735
|
this.log.debug(`Adding matter aggregator to commissioning server for plugin ${plg}${plugin.name}${db}`);
|
|
1646
1736
|
plugin.commissioningServer.addDevice(plugin.aggregator);
|
|
1647
1737
|
this.log.debug(`Adding commissioning server to matter server for plugin ${plg}${plugin.name}${db}`);
|
|
@@ -1727,10 +1817,35 @@ export class Matterbridge extends EventEmitter {
|
|
|
1727
1817
|
this.log.debug(`Importing matter commissioning server storage context from device for ${plg}${pluginName}${db}`);
|
|
1728
1818
|
const basic = device.getClusterServer(BasicInformationCluster);
|
|
1729
1819
|
if (!basic) {
|
|
1730
|
-
|
|
1820
|
+
this.log.error('importCommissioningServerContext error: cannot find the BasicInformationCluster');
|
|
1821
|
+
process.exit(1);
|
|
1822
|
+
}
|
|
1823
|
+
if (!this.storageManager) {
|
|
1824
|
+
this.log.error('importCommissioningServerContext error: no storage manager initialized');
|
|
1825
|
+
process.exit(1);
|
|
1731
1826
|
}
|
|
1732
|
-
|
|
1733
|
-
|
|
1827
|
+
this.log.debug(`Importing commissioning server storage context for ${plg}${pluginName}${db}`);
|
|
1828
|
+
const storageContext = this.storageManager.createContext(pluginName);
|
|
1829
|
+
await storageContext.set('deviceName', basic.getNodeLabelAttribute());
|
|
1830
|
+
await storageContext.set('deviceType', DeviceTypeId(device.deviceType));
|
|
1831
|
+
await storageContext.set('vendorId', basic.getVendorIdAttribute());
|
|
1832
|
+
await storageContext.set('vendorName', basic.getVendorNameAttribute());
|
|
1833
|
+
await storageContext.set('productId', basic.getProductIdAttribute());
|
|
1834
|
+
await storageContext.set('productName', basic.getProductNameAttribute());
|
|
1835
|
+
await storageContext.set('nodeLabel', basic.getNodeLabelAttribute());
|
|
1836
|
+
await storageContext.set('productLabel', basic.getNodeLabelAttribute());
|
|
1837
|
+
await storageContext.set('serialNumber', basic.attributes.serialNumber?.getLocal());
|
|
1838
|
+
await storageContext.set('uniqueId', basic.attributes.uniqueId?.getLocal());
|
|
1839
|
+
await storageContext.set('softwareVersion', basic.getSoftwareVersionAttribute());
|
|
1840
|
+
await storageContext.set('softwareVersionString', basic.getSoftwareVersionStringAttribute());
|
|
1841
|
+
await storageContext.set('hardwareVersion', basic.getHardwareVersionAttribute());
|
|
1842
|
+
await storageContext.set('hardwareVersionString', basic.getHardwareVersionStringAttribute());
|
|
1843
|
+
this.log.debug(`Imported commissioning server storage context for ${plg}${pluginName}${db}`);
|
|
1844
|
+
this.log.debug(`- deviceName: ${await storageContext.get('deviceName')} deviceType: ${await storageContext.get('deviceType')}(0x${(await storageContext.get('deviceType'))?.toString(16).padStart(4, '0')})`);
|
|
1845
|
+
this.log.debug(`- serialNumber: ${await storageContext.get('serialNumber')} uniqueId: ${await storageContext.get('uniqueId')}`);
|
|
1846
|
+
this.log.debug(`- softwareVersion: ${await storageContext.get('softwareVersion')} softwareVersionString: ${await storageContext.get('softwareVersionString')}`);
|
|
1847
|
+
this.log.debug(`- hardwareVersion: ${await storageContext.get('hardwareVersion')} hardwareVersionString: ${await storageContext.get('hardwareVersionString')}`);
|
|
1848
|
+
return storageContext;
|
|
1734
1849
|
}
|
|
1735
1850
|
/**
|
|
1736
1851
|
* Creates a commissioning server storage context.
|
|
@@ -1750,7 +1865,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
1750
1865
|
* @param hardwareVersionString - The hardware version string of the device (optional).
|
|
1751
1866
|
* @returns The storage context for the commissioning server.
|
|
1752
1867
|
*/
|
|
1753
|
-
async createCommissioningServerContext(pluginName, deviceName, deviceType, vendorId, vendorName, productId, productName
|
|
1868
|
+
async createCommissioningServerContext(pluginName, deviceName, deviceType, vendorId, vendorName, productId, productName) {
|
|
1754
1869
|
if (!this.storageManager) {
|
|
1755
1870
|
this.log.error('No storage manager initialized');
|
|
1756
1871
|
process.exit(1);
|
|
@@ -1768,11 +1883,13 @@ export class Matterbridge extends EventEmitter {
|
|
|
1768
1883
|
await storageContext.set('productLabel', productName.slice(0, 32));
|
|
1769
1884
|
await storageContext.set('serialNumber', await storageContext.get('serialNumber', random));
|
|
1770
1885
|
await storageContext.set('uniqueId', await storageContext.get('uniqueId', random));
|
|
1771
|
-
await storageContext.set('softwareVersion',
|
|
1772
|
-
await storageContext.set('softwareVersionString',
|
|
1773
|
-
await storageContext.set('hardwareVersion',
|
|
1774
|
-
await storageContext.set('hardwareVersionString',
|
|
1886
|
+
await storageContext.set('softwareVersion', this.matterbridgeVersion && this.matterbridgeVersion.includes('.') ? parseInt(this.matterbridgeVersion.split('.')[0], 10) : 1);
|
|
1887
|
+
await storageContext.set('softwareVersionString', this.matterbridgeVersion ?? '1.0.0');
|
|
1888
|
+
await storageContext.set('hardwareVersion', this.systemInformation.osRelease && this.systemInformation.osRelease.includes('.') ? parseInt(this.systemInformation.osRelease.split('.')[0], 10) : 1);
|
|
1889
|
+
await storageContext.set('hardwareVersionString', this.systemInformation.osRelease ?? '1.0.0');
|
|
1775
1890
|
this.log.debug(`Created commissioning server storage context for ${plg}${pluginName}${db}`);
|
|
1891
|
+
this.log.debug(`- deviceName: ${await storageContext.get('deviceName')} deviceType: ${await storageContext.get('deviceType')}(0x${(await storageContext.get('deviceType'))?.toString(16).padStart(4, '0')})`);
|
|
1892
|
+
this.log.debug(`- serialNumber: ${await storageContext.get('serialNumber')} uniqueId: ${await storageContext.get('uniqueId')}`);
|
|
1776
1893
|
this.log.debug(`- softwareVersion: ${await storageContext.get('softwareVersion')} softwareVersionString: ${await storageContext.get('softwareVersionString')}`);
|
|
1777
1894
|
this.log.debug(`- hardwareVersion: ${await storageContext.get('hardwareVersion')} hardwareVersionString: ${await storageContext.get('hardwareVersionString')}`);
|
|
1778
1895
|
return storageContext;
|
|
@@ -1805,7 +1922,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
1805
1922
|
plugin.paired = false;
|
|
1806
1923
|
}
|
|
1807
1924
|
}
|
|
1808
|
-
await this.nodeContext?.set('plugins', this.getBaseRegisteredPlugins());
|
|
1925
|
+
await this.nodeContext?.set('plugins', await this.getBaseRegisteredPlugins());
|
|
1809
1926
|
}
|
|
1810
1927
|
else {
|
|
1811
1928
|
this.log.info(`*The commissioning server on port ${commissioningServer.getPort()} for ${plg}${pluginName}${nf} is already commissioned . Waiting for controllers to connect ...`);
|
|
@@ -1815,7 +1932,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
1815
1932
|
plugin.paired = true;
|
|
1816
1933
|
}
|
|
1817
1934
|
}
|
|
1818
|
-
await this.nodeContext?.set('plugins', this.getBaseRegisteredPlugins());
|
|
1935
|
+
await this.nodeContext?.set('plugins', await this.getBaseRegisteredPlugins());
|
|
1819
1936
|
}
|
|
1820
1937
|
}
|
|
1821
1938
|
/**
|
|
@@ -2044,18 +2161,43 @@ export class Matterbridge extends EventEmitter {
|
|
|
2044
2161
|
});
|
|
2045
2162
|
const gdcCluster = commissioningServer.getRootClusterServer(GeneralDiagnosticsCluster);
|
|
2046
2163
|
if (gdcCluster) {
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2164
|
+
// We have like "30:f6:ef:69:2b:c5" in this.systemInformation.macAddress
|
|
2165
|
+
const macArray = this.systemInformation.macAddress.split(':').map((hex) => parseInt(hex, 16));
|
|
2166
|
+
let hardwareAddress = new Uint8Array(macArray);
|
|
2167
|
+
if (hardwareAddress.length === 6)
|
|
2168
|
+
hardwareAddress = Uint8Array.from([0, 0, ...hardwareAddress]);
|
|
2169
|
+
// We have like "192.168.1.189" in this.systemInformation.ipv4Address
|
|
2170
|
+
const ipv4Array = this.systemInformation.ipv4Address.split('.').map((num) => parseInt(num));
|
|
2171
|
+
const iPv4Address = new Uint8Array(ipv4Array);
|
|
2172
|
+
// We have like "fd78:cbf8:4939:746:d555:85a9:74f6:9c6" in this.systemInformation.ipv6Address
|
|
2173
|
+
const ipv6Groups = this.systemInformation.ipv6Address.split(':');
|
|
2174
|
+
const ipv6Array = [];
|
|
2175
|
+
for (const group of ipv6Groups) {
|
|
2176
|
+
const decimal = parseInt(group, 16);
|
|
2177
|
+
ipv6Array.push(decimal >> 8); // High byte
|
|
2178
|
+
ipv6Array.push(decimal & 0xff); // Low byte
|
|
2179
|
+
}
|
|
2180
|
+
const iPv6Address = new Uint8Array(ipv6Array);
|
|
2181
|
+
this.log.debug(`GeneralDiagnosticsCluster for ${plg}${pluginName}${db} hardwareAddress ${this.systemInformation.macAddress} => ${debugStringify(hardwareAddress)}`);
|
|
2182
|
+
this.log.debug(`GeneralDiagnosticsCluster for ${plg}${pluginName}${db} iPv4Address ${this.systemInformation.ipv4Address} => ${debugStringify(iPv4Address)}`);
|
|
2183
|
+
this.log.debug(`GeneralDiagnosticsCluster for ${plg}${pluginName}${db} iPv6Address ${this.systemInformation.ipv6Address} => ${debugStringify(iPv6Address)}`);
|
|
2184
|
+
try {
|
|
2185
|
+
gdcCluster.setNetworkInterfacesAttribute([
|
|
2186
|
+
{
|
|
2187
|
+
name: 'eth0',
|
|
2188
|
+
isOperational: true,
|
|
2189
|
+
offPremiseServicesReachableIPv4: null,
|
|
2190
|
+
offPremiseServicesReachableIPv6: null,
|
|
2191
|
+
hardwareAddress,
|
|
2192
|
+
iPv4Addresses: [iPv4Address],
|
|
2193
|
+
iPv6Addresses: [iPv6Address],
|
|
2194
|
+
type: GeneralDiagnostics.InterfaceType.Ethernet,
|
|
2195
|
+
},
|
|
2196
|
+
]);
|
|
2197
|
+
}
|
|
2198
|
+
catch (error) {
|
|
2199
|
+
this.log.error(`GeneralDiagnosticsCluster.setNetworkInterfacesAttribute for ${plg}${pluginName}${er} error:`, error);
|
|
2200
|
+
}
|
|
2059
2201
|
}
|
|
2060
2202
|
else
|
|
2061
2203
|
this.log.warn(`*GeneralDiagnosticsCluster not found for ${plg}${pluginName}${wr}`);
|
|
@@ -2078,10 +2220,13 @@ export class Matterbridge extends EventEmitter {
|
|
|
2078
2220
|
* @param {StorageContext} context - The storage context.
|
|
2079
2221
|
* @returns {Aggregator} - The created Matter Aggregator.
|
|
2080
2222
|
*/
|
|
2081
|
-
async createMatterAggregator(context) {
|
|
2223
|
+
async createMatterAggregator(context, pluginName) {
|
|
2082
2224
|
const random = 'AG' + CryptoNode.getRandomData(8).toHex();
|
|
2083
2225
|
await context.set('aggregatorSerialNumber', await context.get('aggregatorSerialNumber', random));
|
|
2084
2226
|
await context.set('aggregatorUniqueId', await context.get('aggregatorUniqueId', random));
|
|
2227
|
+
this.log.debug(`Creating matter aggregator for plugin ${plg}${pluginName}${db} with uniqueId ${await context.get('aggregatorUniqueId')} serialNumber ${await context.get('aggregatorSerialNumber')}`);
|
|
2228
|
+
this.log.debug(`Creating matter aggregator for plugin ${plg}${pluginName}${db} with softwareVersion ${await context.get('softwareVersion', 1)} softwareVersionString ${await context.get('softwareVersionString', '1.0.0')}`);
|
|
2229
|
+
this.log.debug(`Creating matter aggregator for plugin ${plg}${pluginName}${db} with hardwareVersion ${await context.get('hardwareVersion', 1)} hardwareVersionString ${await context.get('hardwareVersionString', '1.0.0')}`);
|
|
2085
2230
|
const matterAggregator = new Aggregator();
|
|
2086
2231
|
matterAggregator.addClusterServer(ClusterServer(BasicInformationCluster, {
|
|
2087
2232
|
dataModelRevision: 1,
|
|
@@ -2094,10 +2239,10 @@ export class Matterbridge extends EventEmitter {
|
|
|
2094
2239
|
nodeLabel: 'Matterbridge aggregator',
|
|
2095
2240
|
serialNumber: await context.get('aggregatorSerialNumber'),
|
|
2096
2241
|
uniqueId: await context.get('aggregatorUniqueId'),
|
|
2097
|
-
softwareVersion: 1,
|
|
2098
|
-
softwareVersionString: '
|
|
2099
|
-
hardwareVersion: 1,
|
|
2100
|
-
hardwareVersionString: '
|
|
2242
|
+
softwareVersion: await context.get('softwareVersion', 1),
|
|
2243
|
+
softwareVersionString: await context.get('softwareVersionString', '1.0.0'),
|
|
2244
|
+
hardwareVersion: await context.get('hardwareVersion', 1),
|
|
2245
|
+
hardwareVersionString: await context.get('hardwareVersionString', '1.0.0'),
|
|
2101
2246
|
reachable: true,
|
|
2102
2247
|
capabilityMinima: { caseSessionsPerFabric: 3, subscriptionsPerFabric: 3 },
|
|
2103
2248
|
}, {}, {
|
|
@@ -2172,9 +2317,11 @@ export class Matterbridge extends EventEmitter {
|
|
|
2172
2317
|
for (const detail of interfaceDetails) {
|
|
2173
2318
|
if (detail.family === 'IPv4' && !detail.internal && this.systemInformation.ipv4Address === 'Not found') {
|
|
2174
2319
|
this.systemInformation.ipv4Address = detail.address;
|
|
2320
|
+
this.systemInformation.macAddress = detail.mac;
|
|
2175
2321
|
}
|
|
2176
2322
|
else if (detail.family === 'IPv6' && !detail.internal && this.systemInformation.ipv6Address === 'Not found') {
|
|
2177
2323
|
this.systemInformation.ipv6Address = detail.address;
|
|
2324
|
+
this.systemInformation.macAddress = detail.mac;
|
|
2178
2325
|
}
|
|
2179
2326
|
}
|
|
2180
2327
|
// Break if both addresses are found to improve efficiency
|
|
@@ -2201,6 +2348,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
2201
2348
|
this.log.debug('Host System Information:');
|
|
2202
2349
|
this.log.debug(`- Hostname: ${this.systemInformation.hostname}`);
|
|
2203
2350
|
this.log.debug(`- User: ${this.systemInformation.user}`);
|
|
2351
|
+
this.log.debug(`- MAC Address: ${this.systemInformation.macAddress}`);
|
|
2204
2352
|
this.log.debug(`- IPv4 Address: ${this.systemInformation.ipv4Address}`);
|
|
2205
2353
|
this.log.debug(`- IPv6 Address: ${this.systemInformation.ipv6Address}`);
|
|
2206
2354
|
this.log.debug(`- Node.js: ${versionMajor}.${versionMinor}.${versionPatch}`);
|
|
@@ -2316,25 +2464,30 @@ export class Matterbridge extends EventEmitter {
|
|
|
2316
2464
|
*
|
|
2317
2465
|
* @returns {BaseRegisteredPlugin[]} An array of base registered plugins.
|
|
2318
2466
|
*/
|
|
2319
|
-
getBaseRegisteredPlugins() {
|
|
2320
|
-
const baseRegisteredPlugins =
|
|
2321
|
-
|
|
2322
|
-
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
|
|
2333
|
-
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
|
-
|
|
2337
|
-
|
|
2467
|
+
async getBaseRegisteredPlugins(includeConfigSchema = false) {
|
|
2468
|
+
const baseRegisteredPlugins = [];
|
|
2469
|
+
for (const plugin of this.registeredPlugins) {
|
|
2470
|
+
baseRegisteredPlugins.push({
|
|
2471
|
+
path: plugin.path,
|
|
2472
|
+
type: plugin.type,
|
|
2473
|
+
name: plugin.name,
|
|
2474
|
+
version: plugin.version,
|
|
2475
|
+
latestVersion: plugin.latestVersion,
|
|
2476
|
+
description: plugin.description,
|
|
2477
|
+
author: plugin.author,
|
|
2478
|
+
enabled: plugin.enabled,
|
|
2479
|
+
loaded: plugin.loaded,
|
|
2480
|
+
started: plugin.started,
|
|
2481
|
+
configured: plugin.configured,
|
|
2482
|
+
paired: plugin.paired,
|
|
2483
|
+
connected: plugin.connected,
|
|
2484
|
+
registeredDevices: plugin.registeredDevices,
|
|
2485
|
+
qrPairingCode: plugin.qrPairingCode,
|
|
2486
|
+
manualPairingCode: plugin.manualPairingCode,
|
|
2487
|
+
configJson: includeConfigSchema ? await this.loadPluginConfig(plugin) : {},
|
|
2488
|
+
schemaJson: includeConfigSchema ? await this.loadPluginSchema(plugin) : {},
|
|
2489
|
+
});
|
|
2490
|
+
}
|
|
2338
2491
|
return baseRegisteredPlugins;
|
|
2339
2492
|
}
|
|
2340
2493
|
/**
|
|
@@ -2555,9 +2708,9 @@ export class Matterbridge extends EventEmitter {
|
|
|
2555
2708
|
res.json(response);
|
|
2556
2709
|
});
|
|
2557
2710
|
// Endpoint to provide plugins
|
|
2558
|
-
this.expressApp.get('/api/plugins', (req, res) => {
|
|
2711
|
+
this.expressApp.get('/api/plugins', async (req, res) => {
|
|
2559
2712
|
this.log.debug('The frontend sent /api/plugins');
|
|
2560
|
-
res.json(this.getBaseRegisteredPlugins());
|
|
2713
|
+
res.json(await this.getBaseRegisteredPlugins(true));
|
|
2561
2714
|
});
|
|
2562
2715
|
// Endpoint to provide devices
|
|
2563
2716
|
this.expressApp.get('/api/devices', (req, res) => {
|
|
@@ -2662,7 +2815,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
2662
2815
|
res.json(data);
|
|
2663
2816
|
});
|
|
2664
2817
|
// Endpoint to receive commands
|
|
2665
|
-
this.expressApp.post('/api/command/:command/:param', async (req, res) => {
|
|
2818
|
+
this.expressApp.post('/api/command/:command/:param', express.json(), async (req, res) => {
|
|
2666
2819
|
const command = req.params.command;
|
|
2667
2820
|
let param = req.params.param;
|
|
2668
2821
|
this.log.debug(`The frontend sent /api/command/${command}/${param}`);
|
|
@@ -2679,6 +2832,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
2679
2832
|
}
|
|
2680
2833
|
// Handle the command debugLevel from Settings
|
|
2681
2834
|
if (command === 'setloglevel') {
|
|
2835
|
+
this.log.debug('setloglevel:', param);
|
|
2682
2836
|
if (param === 'Debug') {
|
|
2683
2837
|
this.log.setLogDebug(true);
|
|
2684
2838
|
this.debugEnabled = true;
|
|
@@ -2737,10 +2891,23 @@ export class Matterbridge extends EventEmitter {
|
|
|
2737
2891
|
}
|
|
2738
2892
|
this.updateProcess();
|
|
2739
2893
|
}
|
|
2894
|
+
// Handle the command saveschema from Home
|
|
2895
|
+
if (command === 'saveconfig') {
|
|
2896
|
+
param = param.replace(/\*/g, '\\');
|
|
2897
|
+
this.log.info(`Saving config for plugin ${plg}${param}${nf}...`);
|
|
2898
|
+
//console.log('Req.body:', JSON.stringify(req.body, null, 2));
|
|
2899
|
+
const plugins = await this.nodeContext?.get('plugins');
|
|
2900
|
+
if (!plugins)
|
|
2901
|
+
return;
|
|
2902
|
+
const plugin = plugins.find((plugin) => plugin.name === param);
|
|
2903
|
+
if (!plugin)
|
|
2904
|
+
return;
|
|
2905
|
+
this.savePluginConfigFromJson(plugin, req.body);
|
|
2906
|
+
}
|
|
2740
2907
|
// Handle the command installplugin from Home
|
|
2741
2908
|
if (command === 'installplugin') {
|
|
2742
2909
|
param = param.replace(/\*/g, '\\');
|
|
2743
|
-
this.log.info(`Installing plugin ${plg}${param}${
|
|
2910
|
+
this.log.info(`Installing plugin ${plg}${param}${nf}...`);
|
|
2744
2911
|
try {
|
|
2745
2912
|
await this.spawnCommand('npm', ['install', '-g', param, '--loglevel=verbose']);
|
|
2746
2913
|
this.log.info(`Plugin ${plg}${param}${nf} installed. Full restart required.`);
|
|
@@ -2772,7 +2939,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
2772
2939
|
const plugin = { path: packageJsonPath, type: '', name: packageJson.name, version: packageJson.version, description: packageJson.description, author: packageJson.author, enabled: true };
|
|
2773
2940
|
if (await this.loadPlugin(plugin)) {
|
|
2774
2941
|
this.registeredPlugins.push(plugin);
|
|
2775
|
-
await this.nodeContext?.set('plugins', this.getBaseRegisteredPlugins());
|
|
2942
|
+
await this.nodeContext?.set('plugins', await this.getBaseRegisteredPlugins());
|
|
2776
2943
|
this.log.info(`Plugin ${plg}${packageJsonPath}${nf} type ${plugin.type} added to matterbridge. Restart required.`);
|
|
2777
2944
|
}
|
|
2778
2945
|
else {
|
|
@@ -2794,7 +2961,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
2794
2961
|
// await this.savePluginConfig(this.registeredPlugins[index]);
|
|
2795
2962
|
}
|
|
2796
2963
|
this.registeredPlugins.splice(index, 1);
|
|
2797
|
-
await this.nodeContext?.set('plugins', this.getBaseRegisteredPlugins());
|
|
2964
|
+
await this.nodeContext?.set('plugins', await this.getBaseRegisteredPlugins());
|
|
2798
2965
|
this.log.info(`Plugin ${plg}${param}${nf} removed from matterbridge`);
|
|
2799
2966
|
}
|
|
2800
2967
|
else {
|