matterbridge 1.2.13 → 1.2.14
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 +15 -1
- package/README.md +3 -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 +245 -73
- 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 +3 -3
- package/frontend/build/index.html +1 -1
- package/frontend/build/static/js/main.ed3f81ca.js +3 -0
- package/frontend/build/static/js/{main.742e4290.js.LICENSE.txt → main.ed3f81ca.js.LICENSE.txt} +2 -0
- package/frontend/build/static/js/main.ed3f81ca.js.map +1 -0
- package/package.json +13 -7
- 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
|
@@ -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;
|
|
@@ -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
|
}
|
|
@@ -1123,6 +1126,97 @@ export class Matterbridge extends EventEmitter {
|
|
|
1123
1126
|
}, 60 * 1000);
|
|
1124
1127
|
}, 1000);
|
|
1125
1128
|
}
|
|
1129
|
+
/**
|
|
1130
|
+
* Loads the schema for a plugin.
|
|
1131
|
+
* If the schema file exists, it reads the file and returns the parsed JSON data.
|
|
1132
|
+
* If the schema file does not exist, it creates a new file with default configuration and returns it.
|
|
1133
|
+
* If any error occurs during file access or creation, it logs an error and return an empty schema.
|
|
1134
|
+
*
|
|
1135
|
+
* @param plugin - The plugin for which to load the schema.
|
|
1136
|
+
* @returns A promise that resolves to the loaded or created schema.
|
|
1137
|
+
*/
|
|
1138
|
+
async loadPluginSchema(plugin) {
|
|
1139
|
+
const schemaFile = path.join(this.matterbridgeDirectory, `${plugin.name}.schema.json`);
|
|
1140
|
+
try {
|
|
1141
|
+
await fs.access(schemaFile);
|
|
1142
|
+
const data = await fs.readFile(schemaFile, 'utf8');
|
|
1143
|
+
const schema = JSON.parse(data);
|
|
1144
|
+
schema.title = plugin.description;
|
|
1145
|
+
schema.description = plugin.name + ' v. ' + plugin.version + ' by ' + plugin.author;
|
|
1146
|
+
this.log.debug(`Schema file found: ${schemaFile}.\nSchema:${rs}\n`, schema);
|
|
1147
|
+
return schema;
|
|
1148
|
+
}
|
|
1149
|
+
catch (err) {
|
|
1150
|
+
if (err instanceof Error) {
|
|
1151
|
+
const nodeErr = err;
|
|
1152
|
+
if (nodeErr.code === 'ENOENT') {
|
|
1153
|
+
let schema;
|
|
1154
|
+
if (plugin.name === 'matterbridge-zigbee2mqtt')
|
|
1155
|
+
schema = zigbee2mqtt_schema;
|
|
1156
|
+
else if (plugin.name === 'matterbridge-somfy-tahoma')
|
|
1157
|
+
schema = somfytahoma_schema;
|
|
1158
|
+
else
|
|
1159
|
+
schema = {
|
|
1160
|
+
title: plugin.description,
|
|
1161
|
+
description: plugin.name + ' v. ' + plugin.version + ' by ' + plugin.author,
|
|
1162
|
+
type: 'object',
|
|
1163
|
+
properties: {
|
|
1164
|
+
name: {
|
|
1165
|
+
description: 'Plugin name',
|
|
1166
|
+
type: 'string',
|
|
1167
|
+
readOnly: true,
|
|
1168
|
+
},
|
|
1169
|
+
type: {
|
|
1170
|
+
description: 'Plugin type',
|
|
1171
|
+
type: 'string',
|
|
1172
|
+
readOnly: true,
|
|
1173
|
+
},
|
|
1174
|
+
unregisterOnShutdown: {
|
|
1175
|
+
description: 'Unregister all devices on shutdown (development only)',
|
|
1176
|
+
type: 'boolean',
|
|
1177
|
+
},
|
|
1178
|
+
},
|
|
1179
|
+
};
|
|
1180
|
+
try {
|
|
1181
|
+
await this.writeFile(schemaFile, JSON.stringify(schema, null, 2));
|
|
1182
|
+
this.log.debug(`Created schema file: ${schemaFile}.\nSchema:${rs}\n`, schema);
|
|
1183
|
+
return schema;
|
|
1184
|
+
}
|
|
1185
|
+
catch (err) {
|
|
1186
|
+
this.log.error(`Error creating schema file ${schemaFile}: ${err}`);
|
|
1187
|
+
return schema;
|
|
1188
|
+
}
|
|
1189
|
+
}
|
|
1190
|
+
else {
|
|
1191
|
+
this.log.error(`Error accessing schema file ${schemaFile}: ${err}`);
|
|
1192
|
+
return {};
|
|
1193
|
+
}
|
|
1194
|
+
}
|
|
1195
|
+
this.log.error(`Error loading schema file ${schemaFile}: ${err}`);
|
|
1196
|
+
return {};
|
|
1197
|
+
}
|
|
1198
|
+
}
|
|
1199
|
+
/**
|
|
1200
|
+
* Saves the plugin configuration to a JSON file.
|
|
1201
|
+
* @param plugin - The registered plugin.
|
|
1202
|
+
* @param config - The platform configuration.
|
|
1203
|
+
* @returns A promise that resolves when the configuration is saved successfully, or rejects with an error.
|
|
1204
|
+
*/
|
|
1205
|
+
async savePluginConfigFromJson(plugin, config) {
|
|
1206
|
+
if (!config.name || !config.type || config.name !== plugin.name) {
|
|
1207
|
+
this.log.error(`Error saving plugin ${plg}${plugin.name}${er} config`);
|
|
1208
|
+
return;
|
|
1209
|
+
}
|
|
1210
|
+
const configFile = path.join(this.matterbridgeDirectory, `${plugin.name}.config.json`);
|
|
1211
|
+
try {
|
|
1212
|
+
await this.writeFile(configFile, JSON.stringify(config, null, 2));
|
|
1213
|
+
this.log.debug(`Saved config file: ${configFile}.\nConfig:${rs}\n`, config);
|
|
1214
|
+
}
|
|
1215
|
+
catch (err) {
|
|
1216
|
+
this.log.error(`Error saving plugin ${plg}${plugin.name}${er} config: ${err}`);
|
|
1217
|
+
return;
|
|
1218
|
+
}
|
|
1219
|
+
}
|
|
1126
1220
|
/**
|
|
1127
1221
|
* Loads the configuration for a plugin.
|
|
1128
1222
|
* If the configuration file exists, it reads the file and returns the parsed JSON data.
|
|
@@ -1148,22 +1242,30 @@ export class Matterbridge extends EventEmitter {
|
|
|
1148
1242
|
if (err instanceof Error) {
|
|
1149
1243
|
const nodeErr = err;
|
|
1150
1244
|
if (nodeErr.code === 'ENOENT') {
|
|
1245
|
+
let config;
|
|
1246
|
+
if (plugin.name === 'matterbridge-zigbee2mqtt')
|
|
1247
|
+
config = zigbee2mqtt_config;
|
|
1248
|
+
else if (plugin.name === 'matterbridge-somfy-tahoma')
|
|
1249
|
+
config = somfytahoma_config;
|
|
1250
|
+
else
|
|
1251
|
+
config = { name: plugin.name, type: plugin.type, unregisterOnShutdown: false };
|
|
1151
1252
|
try {
|
|
1152
|
-
await this.writeFile(configFile, JSON.stringify(
|
|
1153
|
-
this.log.debug(`Created config file: ${configFile}.\nConfig:${rs}\n`,
|
|
1154
|
-
return
|
|
1253
|
+
await this.writeFile(configFile, JSON.stringify(config, null, 2));
|
|
1254
|
+
this.log.debug(`Created config file: ${configFile}.\nConfig:${rs}\n`, config);
|
|
1255
|
+
return config;
|
|
1155
1256
|
}
|
|
1156
1257
|
catch (err) {
|
|
1157
1258
|
this.log.error(`Error creating config file ${configFile}: ${err}`);
|
|
1158
|
-
return
|
|
1259
|
+
return config;
|
|
1159
1260
|
}
|
|
1160
1261
|
}
|
|
1161
1262
|
else {
|
|
1162
1263
|
this.log.error(`Error accessing config file ${configFile}: ${err}`);
|
|
1163
|
-
return
|
|
1264
|
+
return {};
|
|
1164
1265
|
}
|
|
1165
1266
|
}
|
|
1166
|
-
|
|
1267
|
+
this.log.error(`Error loading config file ${configFile}: ${err}`);
|
|
1268
|
+
return {};
|
|
1167
1269
|
}
|
|
1168
1270
|
}
|
|
1169
1271
|
/**
|
|
@@ -1328,11 +1430,11 @@ export class Matterbridge extends EventEmitter {
|
|
|
1328
1430
|
plugin.loaded = true;
|
|
1329
1431
|
plugin.registeredDevices = 0;
|
|
1330
1432
|
plugin.addedDevices = 0;
|
|
1331
|
-
await this.nodeContext?.set('plugins', this.getBaseRegisteredPlugins());
|
|
1433
|
+
await this.nodeContext?.set('plugins', await this.getBaseRegisteredPlugins());
|
|
1332
1434
|
this.getLatestVersion(plugin.name)
|
|
1333
1435
|
.then(async (latestVersion) => {
|
|
1334
1436
|
plugin.latestVersion = latestVersion;
|
|
1335
|
-
await this.nodeContext?.set('plugins', this.getBaseRegisteredPlugins());
|
|
1437
|
+
await this.nodeContext?.set('plugins', await this.getBaseRegisteredPlugins());
|
|
1336
1438
|
if (plugin.version !== latestVersion)
|
|
1337
1439
|
this.log.warn(`The plugin ${plg}${plugin.name}${wr} is out of date. Current version: ${plugin.version}, Latest version: ${latestVersion}`);
|
|
1338
1440
|
else
|
|
@@ -1570,8 +1672,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1570
1672
|
this.log.error(`Node storage context undefined for ${plg}Matterbridge${er}`);
|
|
1571
1673
|
return;
|
|
1572
1674
|
}
|
|
1573
|
-
await this.matterbridgeContext.set('softwareVersion', 1);
|
|
1574
|
-
await this.matterbridgeContext.set('softwareVersionString', this.matterbridgeVersion);
|
|
1575
1675
|
this.log.debug(`Creating commissioning server for ${plg}Matterbridge${db}`);
|
|
1576
1676
|
this.commissioningServer = await this.createCommisioningServer(this.matterbridgeContext, 'Matterbridge');
|
|
1577
1677
|
this.log.debug(`Creating matter aggregator for ${plg}Matterbridge${db}`);
|
|
@@ -1622,8 +1722,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
1622
1722
|
continue;
|
|
1623
1723
|
if (!plugin.storageContext)
|
|
1624
1724
|
plugin.storageContext = await this.importCommissioningServerContext(plugin.name, registeredDevice.device);
|
|
1625
|
-
|
|
1626
|
-
await plugin.storageContext.set('softwareVersionString', this.matterbridgeVersion);
|
|
1725
|
+
this.log.debug(`Creating commissioning server for ${plg}${plugin.name}${db}`);
|
|
1627
1726
|
if (!plugin.commissioningServer)
|
|
1628
1727
|
plugin.commissioningServer = await this.createCommisioningServer(plugin.storageContext, plugin.name);
|
|
1629
1728
|
this.log.debug(`Adding device ${dev}${registeredDevice.device.name}${db} to commissioning server for plugin ${plg}${plugin.name}${db}`);
|
|
@@ -1635,10 +1734,9 @@ export class Matterbridge extends EventEmitter {
|
|
|
1635
1734
|
await this.matterServer?.addCommissioningServer(plugin.commissioningServer, { uniqueStorageKey: plugin.name });
|
|
1636
1735
|
}
|
|
1637
1736
|
if (plugin.type === 'DynamicPlatform') {
|
|
1638
|
-
|
|
1737
|
+
this.log.debug(`Creating commissioning server context for ${plg}${plugin.name}${db}`);
|
|
1639
1738
|
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);
|
|
1739
|
+
this.log.debug(`Creating commissioning server for ${plg}${plugin.name}${db}`);
|
|
1642
1740
|
plugin.commissioningServer = await this.createCommisioningServer(plugin.storageContext, plugin.name);
|
|
1643
1741
|
this.log.debug(`Creating aggregator for plugin ${plg}${plugin.name}${db}`);
|
|
1644
1742
|
plugin.aggregator = await this.createMatterAggregator(plugin.storageContext); // Generate serialNumber and uniqueId
|
|
@@ -1727,10 +1825,35 @@ export class Matterbridge extends EventEmitter {
|
|
|
1727
1825
|
this.log.debug(`Importing matter commissioning server storage context from device for ${plg}${pluginName}${db}`);
|
|
1728
1826
|
const basic = device.getClusterServer(BasicInformationCluster);
|
|
1729
1827
|
if (!basic) {
|
|
1730
|
-
|
|
1828
|
+
this.log.error('importCommissioningServerContext error: cannot find the BasicInformationCluster');
|
|
1829
|
+
process.exit(1);
|
|
1731
1830
|
}
|
|
1732
|
-
|
|
1733
|
-
|
|
1831
|
+
if (!this.storageManager) {
|
|
1832
|
+
this.log.error('importCommissioningServerContext error: no storage manager initialized');
|
|
1833
|
+
process.exit(1);
|
|
1834
|
+
}
|
|
1835
|
+
this.log.debug(`Importing commissioning server storage context for ${plg}${pluginName}${db}`);
|
|
1836
|
+
const storageContext = this.storageManager.createContext(pluginName);
|
|
1837
|
+
await storageContext.set('deviceName', basic.getNodeLabelAttribute());
|
|
1838
|
+
await storageContext.set('deviceType', DeviceTypeId(device.deviceType));
|
|
1839
|
+
await storageContext.set('vendorId', basic.getVendorIdAttribute());
|
|
1840
|
+
await storageContext.set('vendorName', basic.getVendorNameAttribute());
|
|
1841
|
+
await storageContext.set('productId', basic.getProductIdAttribute());
|
|
1842
|
+
await storageContext.set('productName', basic.getProductNameAttribute());
|
|
1843
|
+
await storageContext.set('nodeLabel', basic.getNodeLabelAttribute());
|
|
1844
|
+
await storageContext.set('productLabel', basic.getNodeLabelAttribute());
|
|
1845
|
+
await storageContext.set('serialNumber', basic.attributes.serialNumber?.getLocal());
|
|
1846
|
+
await storageContext.set('uniqueId', basic.attributes.uniqueId?.getLocal());
|
|
1847
|
+
await storageContext.set('softwareVersion', basic.getSoftwareVersionAttribute());
|
|
1848
|
+
await storageContext.set('softwareVersionString', basic.getSoftwareVersionStringAttribute());
|
|
1849
|
+
await storageContext.set('hardwareVersion', basic.getHardwareVersionAttribute());
|
|
1850
|
+
await storageContext.set('hardwareVersionString', basic.getHardwareVersionStringAttribute());
|
|
1851
|
+
this.log.debug(`Imported commissioning server storage context for ${plg}${pluginName}${db}`);
|
|
1852
|
+
this.log.debug(`- deviceName: ${await storageContext.get('deviceName')} deviceType: ${await storageContext.get('deviceType')}(0x${(await storageContext.get('deviceType'))?.toString(16).padStart(4, '0')})`);
|
|
1853
|
+
this.log.debug(`- serialNumber: ${await storageContext.get('serialNumber')} uniqueId: ${await storageContext.get('uniqueId')}`);
|
|
1854
|
+
this.log.debug(`- softwareVersion: ${await storageContext.get('softwareVersion')} softwareVersionString: ${await storageContext.get('softwareVersionString')}`);
|
|
1855
|
+
this.log.debug(`- hardwareVersion: ${await storageContext.get('hardwareVersion')} hardwareVersionString: ${await storageContext.get('hardwareVersionString')}`);
|
|
1856
|
+
return storageContext;
|
|
1734
1857
|
}
|
|
1735
1858
|
/**
|
|
1736
1859
|
* Creates a commissioning server storage context.
|
|
@@ -1750,7 +1873,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
1750
1873
|
* @param hardwareVersionString - The hardware version string of the device (optional).
|
|
1751
1874
|
* @returns The storage context for the commissioning server.
|
|
1752
1875
|
*/
|
|
1753
|
-
async createCommissioningServerContext(pluginName, deviceName, deviceType, vendorId, vendorName, productId, productName
|
|
1876
|
+
async createCommissioningServerContext(pluginName, deviceName, deviceType, vendorId, vendorName, productId, productName) {
|
|
1754
1877
|
if (!this.storageManager) {
|
|
1755
1878
|
this.log.error('No storage manager initialized');
|
|
1756
1879
|
process.exit(1);
|
|
@@ -1768,11 +1891,13 @@ export class Matterbridge extends EventEmitter {
|
|
|
1768
1891
|
await storageContext.set('productLabel', productName.slice(0, 32));
|
|
1769
1892
|
await storageContext.set('serialNumber', await storageContext.get('serialNumber', random));
|
|
1770
1893
|
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',
|
|
1894
|
+
await storageContext.set('softwareVersion', this.matterbridgeVersion && this.matterbridgeVersion.includes('.') ? parseInt(this.matterbridgeVersion.split('.')[0], 10) : 1);
|
|
1895
|
+
await storageContext.set('softwareVersionString', this.matterbridgeVersion ?? '1.0.0');
|
|
1896
|
+
await storageContext.set('hardwareVersion', this.systemInformation.osRelease && this.systemInformation.osRelease.includes('.') ? parseInt(this.systemInformation.osRelease.split('.')[0], 10) : 1);
|
|
1897
|
+
await storageContext.set('hardwareVersionString', this.systemInformation.osRelease ?? '1.0.0');
|
|
1775
1898
|
this.log.debug(`Created commissioning server storage context for ${plg}${pluginName}${db}`);
|
|
1899
|
+
this.log.debug(`- deviceName: ${await storageContext.get('deviceName')} deviceType: ${await storageContext.get('deviceType')}(0x${(await storageContext.get('deviceType'))?.toString(16).padStart(4, '0')})`);
|
|
1900
|
+
this.log.debug(`- serialNumber: ${await storageContext.get('serialNumber')} uniqueId: ${await storageContext.get('uniqueId')}`);
|
|
1776
1901
|
this.log.debug(`- softwareVersion: ${await storageContext.get('softwareVersion')} softwareVersionString: ${await storageContext.get('softwareVersionString')}`);
|
|
1777
1902
|
this.log.debug(`- hardwareVersion: ${await storageContext.get('hardwareVersion')} hardwareVersionString: ${await storageContext.get('hardwareVersionString')}`);
|
|
1778
1903
|
return storageContext;
|
|
@@ -1805,7 +1930,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
1805
1930
|
plugin.paired = false;
|
|
1806
1931
|
}
|
|
1807
1932
|
}
|
|
1808
|
-
await this.nodeContext?.set('plugins', this.getBaseRegisteredPlugins());
|
|
1933
|
+
await this.nodeContext?.set('plugins', await this.getBaseRegisteredPlugins());
|
|
1809
1934
|
}
|
|
1810
1935
|
else {
|
|
1811
1936
|
this.log.info(`*The commissioning server on port ${commissioningServer.getPort()} for ${plg}${pluginName}${nf} is already commissioned . Waiting for controllers to connect ...`);
|
|
@@ -1815,7 +1940,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
1815
1940
|
plugin.paired = true;
|
|
1816
1941
|
}
|
|
1817
1942
|
}
|
|
1818
|
-
await this.nodeContext?.set('plugins', this.getBaseRegisteredPlugins());
|
|
1943
|
+
await this.nodeContext?.set('plugins', await this.getBaseRegisteredPlugins());
|
|
1819
1944
|
}
|
|
1820
1945
|
}
|
|
1821
1946
|
/**
|
|
@@ -2044,18 +2169,43 @@ export class Matterbridge extends EventEmitter {
|
|
|
2044
2169
|
});
|
|
2045
2170
|
const gdcCluster = commissioningServer.getRootClusterServer(GeneralDiagnosticsCluster);
|
|
2046
2171
|
if (gdcCluster) {
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2172
|
+
// We have like "30:f6:ef:69:2b:c5" in this.systemInformation.macAddress
|
|
2173
|
+
const macArray = this.systemInformation.macAddress.split(':').map((hex) => parseInt(hex, 16));
|
|
2174
|
+
let hardwareAddress = new Uint8Array(macArray);
|
|
2175
|
+
if (hardwareAddress.length === 6)
|
|
2176
|
+
hardwareAddress = Uint8Array.from([0, 0, ...hardwareAddress]);
|
|
2177
|
+
// We have like "192.168.1.189" in this.systemInformation.ipv4Address
|
|
2178
|
+
const ipv4Array = this.systemInformation.ipv4Address.split('.').map((num) => parseInt(num));
|
|
2179
|
+
const iPv4Address = new Uint8Array(ipv4Array);
|
|
2180
|
+
// We have like "fd78:cbf8:4939:746:d555:85a9:74f6:9c6" in this.systemInformation.ipv6Address
|
|
2181
|
+
const ipv6Groups = this.systemInformation.ipv6Address.split(':');
|
|
2182
|
+
const ipv6Array = [];
|
|
2183
|
+
for (const group of ipv6Groups) {
|
|
2184
|
+
const decimal = parseInt(group, 16);
|
|
2185
|
+
ipv6Array.push(decimal >> 8); // High byte
|
|
2186
|
+
ipv6Array.push(decimal & 0xff); // Low byte
|
|
2187
|
+
}
|
|
2188
|
+
const iPv6Address = new Uint8Array(ipv6Array);
|
|
2189
|
+
this.log.debug(`GeneralDiagnosticsCluster for ${plg}${pluginName}${db} hardwareAddress ${this.systemInformation.macAddress} => ${debugStringify(hardwareAddress)}`);
|
|
2190
|
+
this.log.debug(`GeneralDiagnosticsCluster for ${plg}${pluginName}${db} iPv4Address ${this.systemInformation.ipv4Address} => ${debugStringify(iPv4Address)}`);
|
|
2191
|
+
this.log.debug(`GeneralDiagnosticsCluster for ${plg}${pluginName}${db} iPv6Address ${this.systemInformation.ipv6Address} => ${debugStringify(iPv6Address)}`);
|
|
2192
|
+
try {
|
|
2193
|
+
gdcCluster.setNetworkInterfacesAttribute([
|
|
2194
|
+
{
|
|
2195
|
+
name: 'eth0',
|
|
2196
|
+
isOperational: true,
|
|
2197
|
+
offPremiseServicesReachableIPv4: null,
|
|
2198
|
+
offPremiseServicesReachableIPv6: null,
|
|
2199
|
+
hardwareAddress,
|
|
2200
|
+
iPv4Addresses: [iPv4Address],
|
|
2201
|
+
iPv6Addresses: [iPv6Address],
|
|
2202
|
+
type: GeneralDiagnostics.InterfaceType.Ethernet,
|
|
2203
|
+
},
|
|
2204
|
+
]);
|
|
2205
|
+
}
|
|
2206
|
+
catch (error) {
|
|
2207
|
+
this.log.error(`GeneralDiagnosticsCluster.setNetworkInterfacesAttribute for ${plg}${pluginName}${er} error:`, error);
|
|
2208
|
+
}
|
|
2059
2209
|
}
|
|
2060
2210
|
else
|
|
2061
2211
|
this.log.warn(`*GeneralDiagnosticsCluster not found for ${plg}${pluginName}${wr}`);
|
|
@@ -2094,10 +2244,10 @@ export class Matterbridge extends EventEmitter {
|
|
|
2094
2244
|
nodeLabel: 'Matterbridge aggregator',
|
|
2095
2245
|
serialNumber: await context.get('aggregatorSerialNumber'),
|
|
2096
2246
|
uniqueId: await context.get('aggregatorUniqueId'),
|
|
2097
|
-
softwareVersion: 1,
|
|
2098
|
-
softwareVersionString: '
|
|
2099
|
-
hardwareVersion: 1,
|
|
2100
|
-
hardwareVersionString: '
|
|
2247
|
+
softwareVersion: await context.get('softwareVersion', 1),
|
|
2248
|
+
softwareVersionString: await context.get('softwareVersionString', '1.0.0'),
|
|
2249
|
+
hardwareVersion: await context.get('hardwareVersion', 1),
|
|
2250
|
+
hardwareVersionString: await context.get('hardwareVersionString', '1.0.0'),
|
|
2101
2251
|
reachable: true,
|
|
2102
2252
|
capabilityMinima: { caseSessionsPerFabric: 3, subscriptionsPerFabric: 3 },
|
|
2103
2253
|
}, {}, {
|
|
@@ -2172,9 +2322,11 @@ export class Matterbridge extends EventEmitter {
|
|
|
2172
2322
|
for (const detail of interfaceDetails) {
|
|
2173
2323
|
if (detail.family === 'IPv4' && !detail.internal && this.systemInformation.ipv4Address === 'Not found') {
|
|
2174
2324
|
this.systemInformation.ipv4Address = detail.address;
|
|
2325
|
+
this.systemInformation.macAddress = detail.mac;
|
|
2175
2326
|
}
|
|
2176
2327
|
else if (detail.family === 'IPv6' && !detail.internal && this.systemInformation.ipv6Address === 'Not found') {
|
|
2177
2328
|
this.systemInformation.ipv6Address = detail.address;
|
|
2329
|
+
this.systemInformation.macAddress = detail.mac;
|
|
2178
2330
|
}
|
|
2179
2331
|
}
|
|
2180
2332
|
// Break if both addresses are found to improve efficiency
|
|
@@ -2201,6 +2353,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
2201
2353
|
this.log.debug('Host System Information:');
|
|
2202
2354
|
this.log.debug(`- Hostname: ${this.systemInformation.hostname}`);
|
|
2203
2355
|
this.log.debug(`- User: ${this.systemInformation.user}`);
|
|
2356
|
+
this.log.debug(`- MAC Address: ${this.systemInformation.macAddress}`);
|
|
2204
2357
|
this.log.debug(`- IPv4 Address: ${this.systemInformation.ipv4Address}`);
|
|
2205
2358
|
this.log.debug(`- IPv6 Address: ${this.systemInformation.ipv6Address}`);
|
|
2206
2359
|
this.log.debug(`- Node.js: ${versionMajor}.${versionMinor}.${versionPatch}`);
|
|
@@ -2316,25 +2469,30 @@ export class Matterbridge extends EventEmitter {
|
|
|
2316
2469
|
*
|
|
2317
2470
|
* @returns {BaseRegisteredPlugin[]} An array of base registered plugins.
|
|
2318
2471
|
*/
|
|
2319
|
-
getBaseRegisteredPlugins() {
|
|
2320
|
-
const baseRegisteredPlugins =
|
|
2321
|
-
|
|
2322
|
-
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
|
|
2333
|
-
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
|
-
|
|
2337
|
-
|
|
2472
|
+
async getBaseRegisteredPlugins(includeConfigSchema = false) {
|
|
2473
|
+
const baseRegisteredPlugins = [];
|
|
2474
|
+
for (const plugin of this.registeredPlugins) {
|
|
2475
|
+
baseRegisteredPlugins.push({
|
|
2476
|
+
path: plugin.path,
|
|
2477
|
+
type: plugin.type,
|
|
2478
|
+
name: plugin.name,
|
|
2479
|
+
version: plugin.version,
|
|
2480
|
+
latestVersion: plugin.latestVersion,
|
|
2481
|
+
description: plugin.description,
|
|
2482
|
+
author: plugin.author,
|
|
2483
|
+
enabled: plugin.enabled,
|
|
2484
|
+
loaded: plugin.loaded,
|
|
2485
|
+
started: plugin.started,
|
|
2486
|
+
configured: plugin.configured,
|
|
2487
|
+
paired: plugin.paired,
|
|
2488
|
+
connected: plugin.connected,
|
|
2489
|
+
registeredDevices: plugin.registeredDevices,
|
|
2490
|
+
qrPairingCode: plugin.qrPairingCode,
|
|
2491
|
+
manualPairingCode: plugin.manualPairingCode,
|
|
2492
|
+
configJson: includeConfigSchema ? await this.loadPluginConfig(plugin) : {},
|
|
2493
|
+
schemaJson: includeConfigSchema ? await this.loadPluginSchema(plugin) : {},
|
|
2494
|
+
});
|
|
2495
|
+
}
|
|
2338
2496
|
return baseRegisteredPlugins;
|
|
2339
2497
|
}
|
|
2340
2498
|
/**
|
|
@@ -2555,9 +2713,9 @@ export class Matterbridge extends EventEmitter {
|
|
|
2555
2713
|
res.json(response);
|
|
2556
2714
|
});
|
|
2557
2715
|
// Endpoint to provide plugins
|
|
2558
|
-
this.expressApp.get('/api/plugins', (req, res) => {
|
|
2716
|
+
this.expressApp.get('/api/plugins', async (req, res) => {
|
|
2559
2717
|
this.log.debug('The frontend sent /api/plugins');
|
|
2560
|
-
res.json(this.getBaseRegisteredPlugins());
|
|
2718
|
+
res.json(await this.getBaseRegisteredPlugins(true));
|
|
2561
2719
|
});
|
|
2562
2720
|
// Endpoint to provide devices
|
|
2563
2721
|
this.expressApp.get('/api/devices', (req, res) => {
|
|
@@ -2662,7 +2820,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
2662
2820
|
res.json(data);
|
|
2663
2821
|
});
|
|
2664
2822
|
// Endpoint to receive commands
|
|
2665
|
-
this.expressApp.post('/api/command/:command/:param', async (req, res) => {
|
|
2823
|
+
this.expressApp.post('/api/command/:command/:param', express.json(), async (req, res) => {
|
|
2666
2824
|
const command = req.params.command;
|
|
2667
2825
|
let param = req.params.param;
|
|
2668
2826
|
this.log.debug(`The frontend sent /api/command/${command}/${param}`);
|
|
@@ -2679,6 +2837,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
2679
2837
|
}
|
|
2680
2838
|
// Handle the command debugLevel from Settings
|
|
2681
2839
|
if (command === 'setloglevel') {
|
|
2840
|
+
this.log.debug('setloglevel:', param);
|
|
2682
2841
|
if (param === 'Debug') {
|
|
2683
2842
|
this.log.setLogDebug(true);
|
|
2684
2843
|
this.debugEnabled = true;
|
|
@@ -2737,10 +2896,23 @@ export class Matterbridge extends EventEmitter {
|
|
|
2737
2896
|
}
|
|
2738
2897
|
this.updateProcess();
|
|
2739
2898
|
}
|
|
2899
|
+
// Handle the command saveschema from Home
|
|
2900
|
+
if (command === 'saveconfig') {
|
|
2901
|
+
param = param.replace(/\*/g, '\\');
|
|
2902
|
+
this.log.info(`Saving config for plugin ${plg}${param}${nf}...`);
|
|
2903
|
+
//console.log('Req.body:', JSON.stringify(req.body, null, 2));
|
|
2904
|
+
const plugins = await this.nodeContext?.get('plugins');
|
|
2905
|
+
if (!plugins)
|
|
2906
|
+
return;
|
|
2907
|
+
const plugin = plugins.find((plugin) => plugin.name === param);
|
|
2908
|
+
if (!plugin)
|
|
2909
|
+
return;
|
|
2910
|
+
this.savePluginConfigFromJson(plugin, req.body);
|
|
2911
|
+
}
|
|
2740
2912
|
// Handle the command installplugin from Home
|
|
2741
2913
|
if (command === 'installplugin') {
|
|
2742
2914
|
param = param.replace(/\*/g, '\\');
|
|
2743
|
-
this.log.info(`Installing plugin ${plg}${param}${
|
|
2915
|
+
this.log.info(`Installing plugin ${plg}${param}${nf}...`);
|
|
2744
2916
|
try {
|
|
2745
2917
|
await this.spawnCommand('npm', ['install', '-g', param, '--loglevel=verbose']);
|
|
2746
2918
|
this.log.info(`Plugin ${plg}${param}${nf} installed. Full restart required.`);
|
|
@@ -2772,7 +2944,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
2772
2944
|
const plugin = { path: packageJsonPath, type: '', name: packageJson.name, version: packageJson.version, description: packageJson.description, author: packageJson.author, enabled: true };
|
|
2773
2945
|
if (await this.loadPlugin(plugin)) {
|
|
2774
2946
|
this.registeredPlugins.push(plugin);
|
|
2775
|
-
await this.nodeContext?.set('plugins', this.getBaseRegisteredPlugins());
|
|
2947
|
+
await this.nodeContext?.set('plugins', await this.getBaseRegisteredPlugins());
|
|
2776
2948
|
this.log.info(`Plugin ${plg}${packageJsonPath}${nf} type ${plugin.type} added to matterbridge. Restart required.`);
|
|
2777
2949
|
}
|
|
2778
2950
|
else {
|
|
@@ -2794,7 +2966,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
2794
2966
|
// await this.savePluginConfig(this.registeredPlugins[index]);
|
|
2795
2967
|
}
|
|
2796
2968
|
this.registeredPlugins.splice(index, 1);
|
|
2797
|
-
await this.nodeContext?.set('plugins', this.getBaseRegisteredPlugins());
|
|
2969
|
+
await this.nodeContext?.set('plugins', await this.getBaseRegisteredPlugins());
|
|
2798
2970
|
this.log.info(`Plugin ${plg}${param}${nf} removed from matterbridge`);
|
|
2799
2971
|
}
|
|
2800
2972
|
else {
|