matterbridge 1.3.0 → 1.3.2

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.
Files changed (67) hide show
  1. package/CHANGELOG.md +50 -0
  2. package/CODEOWNERS +1 -0
  3. package/README.md +27 -11
  4. package/dist/cluster/CarbonDioxideConcentrationMeasurementCluster.d.ts +396 -0
  5. package/dist/cluster/CarbonDioxideConcentrationMeasurementCluster.d.ts.map +1 -0
  6. package/dist/cluster/CarbonDioxideConcentrationMeasurementCluster.js +30 -0
  7. package/dist/cluster/CarbonDioxideConcentrationMeasurementCluster.js.map +1 -0
  8. package/dist/cluster/FormaldehydeConcentrationMeasurementCluster.d.ts +396 -0
  9. package/dist/cluster/FormaldehydeConcentrationMeasurementCluster.d.ts.map +1 -0
  10. package/dist/cluster/FormaldehydeConcentrationMeasurementCluster.js +30 -0
  11. package/dist/cluster/FormaldehydeConcentrationMeasurementCluster.js.map +1 -0
  12. package/dist/cluster/NitrogenDioxideConcentrationMeasurementCluster.d.ts +396 -0
  13. package/dist/cluster/NitrogenDioxideConcentrationMeasurementCluster.d.ts.map +1 -0
  14. package/dist/cluster/NitrogenDioxideConcentrationMeasurementCluster.js +34 -0
  15. package/dist/cluster/NitrogenDioxideConcentrationMeasurementCluster.js.map +1 -0
  16. package/dist/cluster/OzoneConcentrationMeasurementCluster.d.ts +395 -0
  17. package/dist/cluster/OzoneConcentrationMeasurementCluster.d.ts.map +1 -0
  18. package/dist/cluster/OzoneConcentrationMeasurementCluster.js +29 -0
  19. package/dist/cluster/OzoneConcentrationMeasurementCluster.js.map +1 -0
  20. package/dist/cluster/Pm10ConcentrationMeasurementCluster.d.ts +395 -0
  21. package/dist/cluster/Pm10ConcentrationMeasurementCluster.d.ts.map +1 -0
  22. package/dist/cluster/Pm10ConcentrationMeasurementCluster.js +29 -0
  23. package/dist/cluster/Pm10ConcentrationMeasurementCluster.js.map +1 -0
  24. package/dist/cluster/Pm1ConcentrationMeasurementCluster.d.ts +395 -0
  25. package/dist/cluster/Pm1ConcentrationMeasurementCluster.d.ts.map +1 -0
  26. package/dist/cluster/Pm1ConcentrationMeasurementCluster.js +29 -0
  27. package/dist/cluster/Pm1ConcentrationMeasurementCluster.js.map +1 -0
  28. package/dist/cluster/Pm25ConcentrationMeasurementCluster.d.ts +395 -0
  29. package/dist/cluster/Pm25ConcentrationMeasurementCluster.d.ts.map +1 -0
  30. package/dist/cluster/Pm25ConcentrationMeasurementCluster.js +29 -0
  31. package/dist/cluster/Pm25ConcentrationMeasurementCluster.js.map +1 -0
  32. package/dist/cluster/PowerTopologyCluster.d.ts +0 -10
  33. package/dist/cluster/PowerTopologyCluster.d.ts.map +1 -1
  34. package/dist/cluster/RadonConcentrationMeasurementCluster.d.ts +395 -0
  35. package/dist/cluster/RadonConcentrationMeasurementCluster.d.ts.map +1 -0
  36. package/dist/cluster/RadonConcentrationMeasurementCluster.js +29 -0
  37. package/dist/cluster/RadonConcentrationMeasurementCluster.js.map +1 -0
  38. package/dist/cluster/SmokeCoAlarmCluster.d.ts +0 -24
  39. package/dist/cluster/SmokeCoAlarmCluster.d.ts.map +1 -1
  40. package/dist/defaultConfigSchema.d.ts +4 -7
  41. package/dist/defaultConfigSchema.d.ts.map +1 -1
  42. package/dist/defaultConfigSchema.js +13 -246
  43. package/dist/defaultConfigSchema.js.map +1 -1
  44. package/dist/index.d.ts +1 -0
  45. package/dist/index.d.ts.map +1 -1
  46. package/dist/index.js +1 -0
  47. package/dist/index.js.map +1 -1
  48. package/dist/matterbridge.d.ts +10 -30
  49. package/dist/matterbridge.d.ts.map +1 -1
  50. package/dist/matterbridge.js +74 -74
  51. package/dist/matterbridge.js.map +1 -1
  52. package/dist/matterbridgeAccessoryPlatform.d.ts +2 -2
  53. package/dist/matterbridgeAccessoryPlatform.d.ts.map +1 -1
  54. package/dist/matterbridgeAccessoryPlatform.js.map +1 -1
  55. package/dist/matterbridgeDevice.d.ts +455 -43
  56. package/dist/matterbridgeDevice.d.ts.map +1 -1
  57. package/dist/matterbridgeDevice.js +283 -29
  58. package/dist/matterbridgeDevice.js.map +1 -1
  59. package/dist/matterbridgeDynamicPlatform.d.ts +2 -2
  60. package/dist/matterbridgeDynamicPlatform.d.ts.map +1 -1
  61. package/dist/matterbridgeDynamicPlatform.js.map +1 -1
  62. package/dist/matterbridgePlatform.d.ts +11 -7
  63. package/dist/matterbridgePlatform.d.ts.map +1 -1
  64. package/dist/matterbridgePlatform.js.map +1 -1
  65. package/dist/matterbridgeV8.d.ts +0 -1
  66. package/dist/matterbridgeV8.d.ts.map +1 -1
  67. package/package.json +7 -7
@@ -4,7 +4,7 @@
4
4
  * @file matterbridge.ts
5
5
  * @author Luca Liguori
6
6
  * @date 2023-12-29
7
- * @version 1.2.0
7
+ * @version 1.3.2
8
8
  *
9
9
  * Copyright 2023, 2024 Luca Liguori.
10
10
  *
@@ -20,7 +20,6 @@
20
20
  * See the License for the specific language governing permissions and
21
21
  * limitations under the License. *
22
22
  */
23
- import { MatterbridgeDevice } from './matterbridgeDevice.js';
24
23
  import { NodeStorageManager } from 'node-persist-manager';
25
24
  import { AnsiLogger, BRIGHT, RESET, UNDERLINE, UNDERLINEOFF, YELLOW, db, debugStringify, stringify, er, nf, rs, wr, RED, GREEN, zb } from 'node-ansi-logger';
26
25
  import { fileURLToPath, pathToFileURL } from 'url';
@@ -32,7 +31,11 @@ import express from 'express';
32
31
  import os from 'os';
33
32
  import path from 'path';
34
33
  import WebSocket, { WebSocketServer } from 'ws';
34
+ // Matterbridge
35
+ import { MatterbridgeDevice } from './matterbridgeDevice.js';
36
+ import { shelly_config, somfytahoma_config, zigbee2mqtt_config } from './defaultConfigSchema.js';
35
37
  import { BridgedDeviceBasicInformation, BridgedDeviceBasicInformationCluster } from './cluster/BridgedDeviceBasicInformationCluster.js';
38
+ // @project-chip/matter-node.js
36
39
  import { CommissioningController, CommissioningServer, MatterServer } from '@project-chip/matter-node.js';
37
40
  import { BasicInformationCluster, ClusterServer, FixedLabelCluster, GeneralCommissioning, PowerSourceCluster, ThreadNetworkDiagnosticsCluster, getClusterNameById } from '@project-chip/matter-node.js/cluster';
38
41
  import { DeviceTypeId, VendorId } from '@project-chip/matter-node.js/datatype';
@@ -42,7 +45,7 @@ import { ManualPairingCodeCodec, QrCodeSchema } from '@project-chip/matter-node.
42
45
  import { StorageBackendDisk, StorageBackendJsonFile, StorageManager } from '@project-chip/matter-node.js/storage';
43
46
  import { requireMinNodeVersion, getParameter, getIntParameter, hasParameter } from '@project-chip/matter-node.js/util';
44
47
  import { CryptoNode } from '@project-chip/matter-node.js/crypto';
45
- import { shelly_config, shelly_schema, somfytahoma_config, somfytahoma_schema, zigbee2mqtt_config, zigbee2mqtt_schema } from './defaultConfigSchema.js';
48
+ // Default colors
46
49
  const plg = '\u001B[38;5;33m';
47
50
  const dev = '\u001B[38;5;79m';
48
51
  const typ = '\u001B[38;5;207m';
@@ -112,9 +115,9 @@ export class Matterbridge extends EventEmitter {
112
115
  commissioningServer;
113
116
  commissioningController;
114
117
  static instance;
118
+ // We load asyncronously so is private
115
119
  constructor() {
116
120
  super();
117
- // We load asyncronously
118
121
  }
119
122
  /**
120
123
  * Loads an instance of the Matterbridge class.
@@ -252,7 +255,7 @@ export class Matterbridge extends EventEmitter {
252
255
  - help: show the help
253
256
  - bridge: start Matterbridge in bridge mode
254
257
  - childbridge: start Matterbridge in childbridge mode
255
- - frontend [port]: start the frontend on the given port (default 3000)
258
+ - frontend [port]: start the frontend on the given port (default 8283)
256
259
  - debug: enable debug mode (default false)
257
260
  - reset: remove the commissioning for Matterbridge (bridge mode). Shutdown Matterbridge before using it!
258
261
  - factoryreset: remove all commissioning information and reset all internal storages. Shutdown Matterbridge before using it!
@@ -288,7 +291,7 @@ export class Matterbridge extends EventEmitter {
288
291
  this.nodeStorage = new NodeStorageManager({ dir: path.join(this.matterbridgeDirectory, 'storage'), logging: false });
289
292
  this.log.debug('Creating node storage context for matterbridge');
290
293
  this.nodeContext = await this.nodeStorage.createStorage('matterbridge');
291
- // Get the plugins from node storage
294
+ // Get the plugins from node storage and create the plugin node storage contexts
292
295
  this.registeredPlugins = await this.nodeContext.get('plugins', []);
293
296
  for (const plugin of this.registeredPlugins) {
294
297
  this.log.debug(`Creating node storage context for plugin ${plugin.name}`);
@@ -350,7 +353,7 @@ export class Matterbridge extends EventEmitter {
350
353
  */
351
354
  async parseCommandLine() {
352
355
  if (hasParameter('list')) {
353
- this.log.info('│ Registered plugins');
356
+ this.log.info(`│ Registered plugins (${this.registeredPlugins?.length})`);
354
357
  this.registeredPlugins.forEach((plugin, index) => {
355
358
  if (index !== this.registeredPlugins.length - 1) {
356
359
  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}`);
@@ -362,7 +365,7 @@ export class Matterbridge extends EventEmitter {
362
365
  }
363
366
  });
364
367
  const serializedRegisteredDevices = await this.nodeContext?.get('devices', []);
365
- this.log.info('│ Registered devices');
368
+ this.log.info(`│ Registered devices (${serializedRegisteredDevices?.length})`);
366
369
  serializedRegisteredDevices?.forEach((device, index) => {
367
370
  if (index !== serializedRegisteredDevices.length - 1) {
368
371
  this.log.info(`├─┬─ plugin ${plg}${device.pluginName}${nf} device: ${dev}${device.deviceName}${nf} uniqueId: ${YELLOW}${device.uniqueId}${nf}`);
@@ -496,6 +499,9 @@ export class Matterbridge extends EventEmitter {
496
499
  this.log.debug('Adding matterbridge commissioning server to matter server');
497
500
  await this.matterServer.addCommissioningServer(this.commissioningServer, { uniqueStorageKey: 'Matterbridge' });
498
501
  for (const plugin of this.registeredPlugins) {
502
+ plugin.configJson = await this.loadPluginConfig(plugin);
503
+ plugin.schemaJson = await this.loadPluginSchema(plugin);
504
+ this.getPluginLatestVersion(plugin);
499
505
  if (!plugin.enabled) {
500
506
  this.log.info(`Plugin ${plg}${plugin.name}${nf} not enabled`);
501
507
  continue;
@@ -523,6 +529,9 @@ export class Matterbridge extends EventEmitter {
523
529
  this.log.debug('Starting matterbridge in mode', this.bridgeMode);
524
530
  this.matterServer = this.createMatterServer(this.storageManager);
525
531
  for (const plugin of this.registeredPlugins) {
532
+ plugin.configJson = await this.loadPluginConfig(plugin);
533
+ plugin.schemaJson = await this.loadPluginSchema(plugin);
534
+ this.getPluginLatestVersion(plugin);
526
535
  if (!plugin.enabled) {
527
536
  this.log.info(`Plugin ${plg}${plugin.name}${nf} not enabled`);
528
537
  continue;
@@ -550,7 +559,7 @@ export class Matterbridge extends EventEmitter {
550
559
  pluginPath = path.join(pluginPath, 'package.json');
551
560
  // Resolve the package.json of the plugin
552
561
  let packageJsonPath = path.resolve(pluginPath);
553
- this.log.debug(`Loading plugin from ${plg}${packageJsonPath}${db}`);
562
+ this.log.debug(`Resolving plugin from ${plg}${packageJsonPath}${db}`);
554
563
  // Check if the package.json file exists
555
564
  let packageJsonExists = false;
556
565
  try {
@@ -851,7 +860,7 @@ export class Matterbridge extends EventEmitter {
851
860
  serializedRegisteredDevices.push(serializedMatterbridgeDevice);
852
861
  });
853
862
  await this.nodeContext.set('devices', serializedRegisteredDevices);
854
- this.log.info('Saved registered devices');
863
+ this.log.info(`Saved registered devices (${serializedRegisteredDevices?.length})`);
855
864
  // Clear nodeContext and nodeStorage (they just need 1000ms to write the data to disk)
856
865
  this.log.debug('Closing node storage context...');
857
866
  this.nodeContext.close();
@@ -899,7 +908,7 @@ export class Matterbridge extends EventEmitter {
899
908
  Matterbridge.instance = undefined;
900
909
  this.emit('shutdown');
901
910
  }
902
- }, 2 * 1000); // From 2 to 5 seconds
911
+ }, 2 * 1000);
903
912
  }, 3 * 1000);
904
913
  }
905
914
  }
@@ -907,7 +916,7 @@ export class Matterbridge extends EventEmitter {
907
916
  * Adds a bridged device to the Matterbridge.
908
917
  * @param pluginName - The name of the plugin.
909
918
  * @param device - The bridged device to add.
910
- * @returns {Promise<void>} - A promise that resolves when the storage process is started.
919
+ * @returns {Promise<void>} - A promise that resolves when the device is added.
911
920
  */
912
921
  async addBridgedDevice(pluginName, device) {
913
922
  if (this.bridgeMode === 'bridge' && !this.matterAggregator) {
@@ -1132,15 +1141,14 @@ export class Matterbridge extends EventEmitter {
1132
1141
  }
1133
1142
  /**
1134
1143
  * Loads the schema for a plugin.
1135
- * If the schema file exists, it reads the file and returns the parsed JSON data.
1136
- * If the schema file does not exist, it creates a new file with default configuration and returns it.
1137
- * If any error occurs during file access or creation, it logs an error and return an empty schema.
1144
+ * If the schema file exists in the plugin directory, it reads the file and returns the parsed JSON data and delete the schema form .matterbridge.
1145
+ * If the schema file does not exist, it creates a new schema with the default configuration and returns it.
1138
1146
  *
1139
1147
  * @param plugin - The plugin for which to load the schema.
1140
1148
  * @returns A promise that resolves to the loaded or created schema.
1141
1149
  */
1142
1150
  async loadPluginSchema(plugin) {
1143
- const schemaFile = path.join(this.matterbridgeDirectory, `${plugin.name}.schema.json`);
1151
+ let schemaFile = plugin.path.replace('package.json', `${plugin.name}.schema.json`);
1144
1152
  try {
1145
1153
  await fs.access(schemaFile);
1146
1154
  const data = await fs.readFile(schemaFile, 'utf8');
@@ -1149,76 +1157,64 @@ export class Matterbridge extends EventEmitter {
1149
1157
  schema.description = plugin.name + ' v. ' + plugin.version + ' by ' + plugin.author;
1150
1158
  this.log.debug(`Schema file found: ${schemaFile}.`);
1151
1159
  // this.log.debug(`Schema file found: ${schemaFile}.\nSchema:${rs}\n`, schema);
1160
+ schemaFile = path.join(this.matterbridgeDirectory, `${plugin.name}.schema.json`);
1161
+ try {
1162
+ await fs.unlink(schemaFile);
1163
+ this.log.debug(`Schema file ${schemaFile} deleted.`);
1164
+ }
1165
+ catch (err) {
1166
+ this.log.debug(`Schema file ${schemaFile} to delete not found.`);
1167
+ }
1152
1168
  return schema;
1153
1169
  }
1154
1170
  catch (err) {
1155
- if (err instanceof Error) {
1156
- const nodeErr = err;
1157
- if (nodeErr.code === 'ENOENT') {
1158
- let schema;
1159
- if (plugin.name === 'matterbridge-zigbee2mqtt')
1160
- schema = zigbee2mqtt_schema;
1161
- else if (plugin.name === 'matterbridge-somfy-tahoma')
1162
- schema = somfytahoma_schema;
1163
- else if (plugin.name === 'matterbridge-shelly')
1164
- schema = shelly_schema;
1165
- else
1166
- schema = {
1167
- title: plugin.description,
1168
- description: plugin.name + ' v. ' + plugin.version + ' by ' + plugin.author,
1169
- type: 'object',
1170
- properties: {
1171
- name: {
1172
- description: 'Plugin name',
1173
- type: 'string',
1174
- readOnly: true,
1175
- },
1176
- type: {
1177
- description: 'Plugin type',
1178
- type: 'string',
1179
- readOnly: true,
1180
- },
1181
- unregisterOnShutdown: {
1182
- description: 'Unregister all devices on shutdown (development only)',
1183
- type: 'boolean',
1184
- },
1185
- },
1186
- };
1187
- try {
1188
- await this.writeFile(schemaFile, JSON.stringify(schema, null, 2));
1189
- this.log.debug(`Created schema file: ${schemaFile}.`);
1190
- // this.log.debug(`Created schema file: ${schemaFile}.\nSchema:${rs}\n`, schema);
1191
- return schema;
1192
- }
1193
- catch (err) {
1194
- this.log.error(`Error creating schema file ${schemaFile}: ${err}`);
1195
- return schema;
1196
- }
1197
- }
1198
- else {
1199
- this.log.error(`Error accessing schema file ${schemaFile}: ${err}`);
1200
- return {};
1201
- }
1202
- }
1203
- this.log.error(`Error loading schema file ${schemaFile}: ${err}`);
1204
- return {};
1171
+ this.log.debug(`Schema file ${schemaFile} not found. Loading default schema.`);
1172
+ const schema = {
1173
+ title: plugin.description,
1174
+ description: plugin.name + ' v. ' + plugin.version + ' by ' + plugin.author,
1175
+ type: 'object',
1176
+ properties: {
1177
+ name: {
1178
+ description: 'Plugin name',
1179
+ type: 'string',
1180
+ readOnly: true,
1181
+ },
1182
+ type: {
1183
+ description: 'Plugin type',
1184
+ type: 'string',
1185
+ readOnly: true,
1186
+ },
1187
+ debug: {
1188
+ description: 'Enable the debug for the plugin (development only)',
1189
+ type: 'boolean',
1190
+ default: false,
1191
+ },
1192
+ unregisterOnShutdown: {
1193
+ description: 'Unregister all devices on shutdown (development only)',
1194
+ type: 'boolean',
1195
+ default: false,
1196
+ },
1197
+ },
1198
+ };
1199
+ return schema;
1205
1200
  }
1206
1201
  }
1207
1202
  /**
1208
1203
  * Saves the plugin configuration to a JSON file.
1209
1204
  * @param plugin - The registered plugin.
1210
1205
  * @param config - The platform configuration.
1211
- * @returns A promise that resolves when the configuration is saved successfully, or rejects with an error.
1206
+ * @returns A promise that resolves when the configuration is saved successfully, or with an error logged.
1212
1207
  */
1213
1208
  async savePluginConfigFromJson(plugin, config) {
1214
1209
  if (!config.name || !config.type || config.name !== plugin.name) {
1215
- this.log.error(`Error saving plugin ${plg}${plugin.name}${er} config`);
1210
+ this.log.error(`Error saving plugin ${plg}${plugin.name}${er} config. Wrong config data content.`);
1216
1211
  return;
1217
1212
  }
1218
1213
  const configFile = path.join(this.matterbridgeDirectory, `${plugin.name}.config.json`);
1219
1214
  try {
1220
1215
  await this.writeFile(configFile, JSON.stringify(config, null, 2));
1221
- this.log.debug(`Saved config file: ${configFile}.\nConfig:${rs}\n`, config);
1216
+ plugin.configJson = config;
1217
+ this.log.debug(`Saved config file ${configFile} for plugin ${plg}${plugin.name}${db}.\nConfig:${rs}\n`, config);
1222
1218
  }
1223
1219
  catch (err) {
1224
1220
  this.log.error(`Error saving plugin ${plg}${plugin.name}${er} config: ${err}`);
@@ -1229,7 +1225,7 @@ export class Matterbridge extends EventEmitter {
1229
1225
  * Loads the configuration for a plugin.
1230
1226
  * If the configuration file exists, it reads the file and returns the parsed JSON data.
1231
1227
  * If the configuration file does not exist, it creates a new file with default configuration and returns it.
1232
- * If any error occurs during file access or creation, it logs an error and rejects the promise with the error.
1228
+ * If any error occurs during file access or creation, it logs an error and return un empty config.
1233
1229
  *
1234
1230
  * @param plugin - The plugin for which to load the configuration.
1235
1231
  * @returns A promise that resolves to the loaded or created configuration.
@@ -1259,7 +1255,7 @@ export class Matterbridge extends EventEmitter {
1259
1255
  else if (plugin.name === 'matterbridge-shelly')
1260
1256
  config = shelly_config;
1261
1257
  else
1262
- config = { name: plugin.name, type: plugin.type, unregisterOnShutdown: false };
1258
+ config = { name: plugin.name, type: plugin.type, debug: false, unregisterOnShutdown: false };
1263
1259
  try {
1264
1260
  await this.writeFile(configFile, JSON.stringify(config, null, 2));
1265
1261
  this.log.debug(`Created config file: ${configFile}.`);
@@ -1440,9 +1436,11 @@ export class Matterbridge extends EventEmitter {
1440
1436
  plugin.loaded = true;
1441
1437
  plugin.registeredDevices = 0;
1442
1438
  plugin.addedDevices = 0;
1439
+ plugin.configJson = config;
1440
+ plugin.schemaJson = await this.loadPluginSchema(plugin);
1443
1441
  // Save the updated plugin data in the node storage
1444
1442
  await this.nodeContext?.set('plugins', await this.getBaseRegisteredPlugins());
1445
- this.getPluginLatestVersion(plugin);
1443
+ // this.getPluginLatestVersion(plugin); moved to parseCommandLine
1446
1444
  this.log.info(`Loaded plugin ${plg}${plugin.name}${nf} type ${typ}${platform.type} ${db}(entrypoint ${UNDERLINE}${pluginEntry}${UNDERLINEOFF})`);
1447
1445
  if (start)
1448
1446
  this.startPlugin(plugin, message); // No await do it asyncronously
@@ -2529,8 +2527,10 @@ export class Matterbridge extends EventEmitter {
2529
2527
  registeredDevices: plugin.registeredDevices,
2530
2528
  qrPairingCode: plugin.qrPairingCode,
2531
2529
  manualPairingCode: plugin.manualPairingCode,
2532
- configJson: includeConfigSchema ? await this.loadPluginConfig(plugin) : {},
2533
- schemaJson: includeConfigSchema ? await this.loadPluginSchema(plugin) : {},
2530
+ // configJson: includeConfigSchema ? await this.loadPluginConfig(plugin) : {},
2531
+ // schemaJson: includeConfigSchema ? await this.loadPluginSchema(plugin) : {},
2532
+ configJson: includeConfigSchema ? plugin.configJson : {},
2533
+ schemaJson: includeConfigSchema ? plugin.schemaJson : {},
2534
2534
  });
2535
2535
  }
2536
2536
  return baseRegisteredPlugins;