matterbridge 1.3.1 → 1.3.3

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 (68) hide show
  1. package/CHANGELOG.md +49 -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 -254
  43. package/dist/defaultConfigSchema.js.map +1 -1
  44. package/dist/index.d.ts +19 -3
  45. package/dist/index.d.ts.map +1 -1
  46. package/dist/index.js +21 -4
  47. package/dist/index.js.map +1 -1
  48. package/dist/matterbridge.d.ts +3 -24
  49. package/dist/matterbridge.d.ts.map +1 -1
  50. package/dist/matterbridge.js +76 -98
  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 +449 -18
  56. package/dist/matterbridgeDevice.d.ts.map +1 -1
  57. package/dist/matterbridgeDevice.js +269 -8
  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/migrationV8.txt +61 -0
  68. package/package.json +5 -5
@@ -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!
@@ -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}`);
@@ -556,7 +559,7 @@ export class Matterbridge extends EventEmitter {
556
559
  pluginPath = path.join(pluginPath, 'package.json');
557
560
  // Resolve the package.json of the plugin
558
561
  let packageJsonPath = path.resolve(pluginPath);
559
- this.log.debug(`Loading plugin from ${plg}${packageJsonPath}${db}`);
562
+ this.log.debug(`Resolving plugin from ${plg}${packageJsonPath}${db}`);
560
563
  // Check if the package.json file exists
561
564
  let packageJsonExists = false;
562
565
  try {
@@ -857,7 +860,7 @@ export class Matterbridge extends EventEmitter {
857
860
  serializedRegisteredDevices.push(serializedMatterbridgeDevice);
858
861
  });
859
862
  await this.nodeContext.set('devices', serializedRegisteredDevices);
860
- this.log.info('Saved registered devices');
863
+ this.log.info(`Saved registered devices (${serializedRegisteredDevices?.length})`);
861
864
  // Clear nodeContext and nodeStorage (they just need 1000ms to write the data to disk)
862
865
  this.log.debug('Closing node storage context...');
863
866
  this.nodeContext.close();
@@ -905,7 +908,7 @@ export class Matterbridge extends EventEmitter {
905
908
  Matterbridge.instance = undefined;
906
909
  this.emit('shutdown');
907
910
  }
908
- }, 2 * 1000); // From 2 to 5 seconds
911
+ }, 2 * 1000);
909
912
  }, 3 * 1000);
910
913
  }
911
914
  }
@@ -1139,9 +1142,7 @@ export class Matterbridge extends EventEmitter {
1139
1142
  /**
1140
1143
  * Loads the schema for a plugin.
1141
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.
1142
- * If the schema file exists in matterbridgeDirectory, it reads the file and returns the parsed JSON data.
1143
1145
  * If the schema file does not exist, it creates a new schema with the default configuration and returns it.
1144
- * If any error occurs during file access or creation, it logs an error and return an empty schema.
1145
1146
  *
1146
1147
  * @param plugin - The plugin for which to load the schema.
1147
1148
  * @returns A promise that resolves to the loaded or created schema.
@@ -1167,77 +1168,36 @@ export class Matterbridge extends EventEmitter {
1167
1168
  return schema;
1168
1169
  }
1169
1170
  catch (err) {
1170
- this.log.debug(`Schema file ${schemaFile} not found.`);
1171
- }
1172
- schemaFile = path.join(this.matterbridgeDirectory, `${plugin.name}.schema.json`);
1173
- try {
1174
- await fs.access(schemaFile);
1175
- const data = await fs.readFile(schemaFile, 'utf8');
1176
- const schema = JSON.parse(data);
1177
- schema.title = plugin.description;
1178
- schema.description = plugin.name + ' v. ' + plugin.version + ' by ' + plugin.author;
1179
- this.log.debug(`Schema file found: ${schemaFile}.`);
1180
- // this.log.debug(`Schema file found: ${schemaFile}.\nSchema:${rs}\n`, schema);
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
+ };
1181
1199
  return schema;
1182
1200
  }
1183
- catch (err) {
1184
- if (err instanceof Error) {
1185
- const nodeErr = err;
1186
- if (nodeErr.code === 'ENOENT') {
1187
- let schema;
1188
- if (plugin.name === 'matterbridge-zigbee2mqtt')
1189
- schema = zigbee2mqtt_schema;
1190
- else if (plugin.name === 'matterbridge-somfy-tahoma')
1191
- schema = somfytahoma_schema;
1192
- else if (plugin.name === 'matterbridge-shelly')
1193
- schema = shelly_schema;
1194
- else
1195
- schema = {
1196
- title: plugin.description,
1197
- description: plugin.name + ' v. ' + plugin.version + ' by ' + plugin.author,
1198
- type: 'object',
1199
- properties: {
1200
- name: {
1201
- description: 'Plugin name',
1202
- type: 'string',
1203
- readOnly: true,
1204
- },
1205
- type: {
1206
- description: 'Plugin type',
1207
- type: 'string',
1208
- readOnly: true,
1209
- },
1210
- debug: {
1211
- description: 'Enable the debug for the plugin (development only)',
1212
- type: 'boolean',
1213
- },
1214
- unregisterOnShutdown: {
1215
- description: 'Unregister all devices on shutdown (development only)',
1216
- type: 'boolean',
1217
- },
1218
- },
1219
- };
1220
- return schema;
1221
- /*
1222
- try {
1223
- await this.writeFile(schemaFile, JSON.stringify(schema, null, 2));
1224
- this.log.debug(`Created schema file: ${schemaFile}.`);
1225
- // this.log.debug(`Created schema file: ${schemaFile}.\nSchema:${rs}\n`, schema);
1226
- return schema;
1227
- } catch (err) {
1228
- this.log.error(`Error creating schema file ${schemaFile}: ${err}`);
1229
- return schema;
1230
- }
1231
- */
1232
- }
1233
- else {
1234
- this.log.error(`Error accessing schema file ${schemaFile}: ${err}`);
1235
- return {};
1236
- }
1237
- }
1238
- this.log.error(`Error loading schema file ${schemaFile}: ${err}`);
1239
- return {};
1240
- }
1241
1201
  }
1242
1202
  /**
1243
1203
  * Saves the plugin configuration to a JSON file.
@@ -1295,7 +1255,7 @@ export class Matterbridge extends EventEmitter {
1295
1255
  else if (plugin.name === 'matterbridge-shelly')
1296
1256
  config = shelly_config;
1297
1257
  else
1298
- config = { name: plugin.name, type: plugin.type, unregisterOnShutdown: false };
1258
+ config = { name: plugin.name, type: plugin.type, debug: false, unregisterOnShutdown: false };
1299
1259
  try {
1300
1260
  await this.writeFile(configFile, JSON.stringify(config, null, 2));
1301
1261
  this.log.debug(`Created config file: ${configFile}.`);
@@ -1669,28 +1629,36 @@ export class Matterbridge extends EventEmitter {
1669
1629
  */
1670
1630
  async startMatterbridge() {
1671
1631
  if (this.bridgeMode === 'bridge') {
1672
- // Plugins are loaded and started by loadPlugin on startup and plugin.loaded is set to true
1673
- // Plugins configured by callback when Matterbridge is commissioned
1632
+ // Plugins are loaded and started by loadPlugin on startup and plugin.loaded and plugin.loaded are set to true
1633
+ // Plugins are configured by callback when Matterbridge is commissioned and plugin.configured is set to true
1674
1634
  this.log.debug('***Starting startMatterbridge interval for Matterbridge');
1675
1635
  let failCount = 0;
1676
- const startInterval = setInterval(async () => {
1636
+ const startMatterInterval = setInterval(async () => {
1677
1637
  for (const plugin of this.registeredPlugins) {
1678
- if (!plugin.enabled || plugin.error)
1638
+ // if (!plugin.enabled || plugin.error) continue;
1639
+ // new code to not start the bridge if one plugin is in error cause the controllers will delete the devices loosing all the configuration
1640
+ if (!plugin.enabled)
1679
1641
  continue;
1642
+ if (plugin.error) {
1643
+ clearInterval(startMatterInterval);
1644
+ this.log.debug('***Cleared startMatterInterval interval for Matterbridge for plugin in error state');
1645
+ this.log.error(`The plugin ${plg}${plugin.name}${er} is in error state.`);
1646
+ this.log.error('The bridge will not start until the problem is solved to prevent the controllers from deleting all registered devices.');
1647
+ this.log.error('If you want to start the bridge disable the plugin in error state and restart.');
1648
+ return;
1649
+ }
1680
1650
  if (!plugin.loaded || !plugin.started) {
1681
- this.log.debug(`***Waiting (failSafeCount=${failCount}/30) in startMatterbridge interval for plugin ${plg}${plugin.name}${db} loaded: ${plugin.loaded} started: ${plugin.started}...`);
1651
+ this.log.debug(`***Waiting (failSafeCount=${failCount}/30) in startMatterInterval interval for plugin ${plg}${plugin.name}${db} loaded: ${plugin.loaded} started: ${plugin.started}...`);
1682
1652
  failCount++;
1683
1653
  if (failCount > 30) {
1684
- this.log.error(`Error waiting for plugin ${plg}${plugin.name}${er} to load and start. Plugin is in error mode.`);
1654
+ this.log.error(`Error waiting for plugin ${plg}${plugin.name}${er} to load and start. Plugin is in error state.`);
1685
1655
  plugin.error = true;
1686
1656
  }
1687
- else {
1688
- return;
1689
- }
1657
+ return;
1690
1658
  }
1691
1659
  }
1692
- clearInterval(startInterval);
1693
- this.log.debug('***Cleared startMatterbridge interval for Matterbridge');
1660
+ clearInterval(startMatterInterval);
1661
+ this.log.debug('***Cleared startMatterInterval interval for Matterbridge');
1694
1662
  await this.startMatterServer();
1695
1663
  this.log.info('Matter server started');
1696
1664
  await this.showCommissioningQRCode(this.commissioningServer, this.matterbridgeContext, this.nodeContext, 'Matterbridge');
@@ -1760,9 +1728,20 @@ export class Matterbridge extends EventEmitter {
1760
1728
  let failCount = 0;
1761
1729
  const startMatterInterval = setInterval(async () => {
1762
1730
  let allStarted = true;
1763
- this.registeredPlugins.forEach((plugin) => {
1764
- if (!plugin.enabled || plugin.error)
1731
+ // this.registeredPlugins.forEach((plugin) => {
1732
+ for (const plugin of this.registeredPlugins) {
1733
+ // if (!plugin.enabled || plugin.error) return;
1734
+ // new code to not start the bridge if one plugin is in error cause the controllers will delete the devices loosing all the configuration
1735
+ if (!plugin.enabled)
1736
+ continue;
1737
+ if (plugin.error) {
1738
+ clearInterval(startMatterInterval);
1739
+ this.log.debug('***Cleared startMatterInterval interval for Matterbridge for plugin in error state');
1740
+ this.log.error(`The plugin ${plg}${plugin.name}${er} is in error state.`);
1741
+ this.log.error('The bridge will not start until the problem is solved to prevent the controllers from deleting all registered devices.');
1742
+ this.log.error('If you want to start the bridge disable the plugin in error state and restart.');
1765
1743
  return;
1744
+ }
1766
1745
  this.log.debug(`***Checking plugin ${plg}${plugin.name}${db} to start matter in childbridge mode...`);
1767
1746
  if (!plugin.loaded || !plugin.started) {
1768
1747
  allStarted = false;
@@ -1771,14 +1750,13 @@ export class Matterbridge extends EventEmitter {
1771
1750
  if (failCount > 30) {
1772
1751
  this.log.error(`Error waiting for plugin ${plg}${plugin.name}${er} to load and start. Plugin is in error mode.`);
1773
1752
  plugin.error = true;
1774
- return;
1775
1753
  }
1776
1754
  }
1777
- });
1755
+ }
1778
1756
  if (!allStarted)
1779
1757
  return;
1780
1758
  clearInterval(startMatterInterval);
1781
- this.log.debug('***Cleared startMatterbridge interval in childbridge mode');
1759
+ this.log.debug('***Cleared startMatterInterval interval in childbridge mode');
1782
1760
  await this.startMatterServer();
1783
1761
  this.log.info('Matter server started');
1784
1762
  for (const plugin of this.registeredPlugins) {