matterbridge 2.1.6-dev.5 → 2.1.6-dev.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,4 +1,4 @@
1
- # <img src="https://github.com/Luligu/matterbridge/blob/main/frontend/public/matterbridge%2064x64.png" alt="Matterbridge Logo" width="64px" height="64px">&nbsp;&nbsp;&nbsp;Matterbridge changelog
1
+ # <img src="frontend/public/matterbridge.svg" alt="Matterbridge Logo" width="64px" height="64px">&nbsp;&nbsp;&nbsp;Matterbridge changelog
2
2
 
3
3
  All notable changes to this project will be documented in this file.
4
4
 
@@ -33,16 +33,19 @@ matterbridge-zigbee2mqtt v. 2.4.4
33
33
  matterbridge-somfy-tahoma v. 1.2.3
34
34
  matterbridge-hass v. 0.0.8
35
35
 
36
- ## [2.1.6] - 2025-02-15
36
+ ## [2.1.6] - 2025-02-20
37
37
 
38
38
  ### Added
39
39
 
40
- - [docker]: Added health check directly in the docker image. No need to change configuration.
40
+ - [docker]: Added health check directly in the docker image. No need to change configuration of docker compose.
41
41
  - [platform]: Saving in the storage the selects for faster loading of plugins.
42
42
  - [icon]: Added matterbridge svg icon (thanks: https://github.com/robvanoostenrijk https://github.com/stuntguy3000).
43
- - [frontend]: Added processUptime.
43
+ - [pluginManager]: Refactor PluginManager to optimize memory and load time.
44
44
  - [frontend]: Frontend v.2.4.2.
45
- - [PluginManager]: Refactor PluginManager to optimize memory and load time.
45
+ - [frontend]: Added processUptime.
46
+ - [frontend]: Added Share fabrics and Stop sharing to the menu. This allows to pair other controllers without the need to share from the first controller.
47
+ - [frontend]: Added subscriptions to QRDiv.
48
+ - [utils]: Optimized memory and loading time.
46
49
 
47
50
  ### Changed
48
51
 
@@ -51,6 +54,10 @@ matterbridge-hass v. 0.0.8
51
54
  - [package]: Update matter.js to 0.12.4-alpha.0-20250215-5af08a8d6
52
55
  - [package]: Update matter.js to 0.12.4-alpha.0-20250217-b0bba5179
53
56
 
57
+ ### Fixed
58
+
59
+ - [matterbridge]: Check endpoint state in /api/devices.
60
+
54
61
  <a href="https://www.buymeacoffee.com/luligugithub">
55
62
  <img src="./yellow-button.png" alt="Buy me a coffee" width="120">
56
63
  </a>
@@ -260,7 +267,7 @@ matterbridge-hass v. 0.0.8
260
267
 
261
268
  ### Added
262
269
 
263
- - [edge]: Added guide https://github.com/Luligu/matterbridge/blob/dev/README-EDGE.md.
270
+ - [edge]: Added guide [README-EDGE.md](README-EDGE.md).
264
271
  - [storage]: Added conversion from old matter storage to the new api format with fabrics, resumptionRecords, network, commissioning, operationalCredentials, acl and parts number. The conversion is triggered every time you shutdown or restart matterbridge till the new storage has been used with matterbridge edge.
265
272
  - [storage]: Added conversion for child endpoint numbers.
266
273
  - [storage]: Added conversion for childbridge mode.
@@ -397,7 +404,7 @@ It is possible that some controllers see them as new devices or need time to rea
397
404
  ### Added
398
405
 
399
406
  - [matter.js]: Almost completed the phase 2 of migration to edge (matter.js new API).
400
- - [nginx]: Added the route /matterbridge/ to be used with nginx proxy server https://github.com/Luligu/matterbridge/blob/dev/README-NGINX.md.
407
+ - [nginx]: Added the route /matterbridge/ to be used with nginx proxy server [README-NGINX.md](README-NGINX.md).
401
408
  - [config]: Config and schema are loaded before loading the plugin to allow to configure the plugin even when it throws error on load.
402
409
  - [config]: Added version to the config.
403
410
  - [frontend]: Added badge "edge" when running in edge mode.
@@ -601,7 +608,7 @@ It is possible that some controllers see them as new devices or need time to rea
601
608
 
602
609
  ### Breaking Changes for developers
603
610
 
604
- - please read this [Development guide lines](https://github.com/Luligu/matterbridge/blob/main/README-DEV.md)
611
+ - please read this [Development guide lines](README-DEV.md)
605
612
 
606
613
  ### Added
607
614
 
package/README-DEV.md CHANGED
@@ -1,4 +1,4 @@
1
- # <img src="https://github.com/Luligu/matterbridge/blob/main/frontend/public/matterbridge%2064x64.png" alt="Matterbridge Logo" width="64px" height="64px">&nbsp;&nbsp;&nbsp;Matterbridge
1
+ # <img src="frontend/public/matterbridge.svg" alt="Matterbridge Logo" width="64px" height="64px">&nbsp;&nbsp;&nbsp;Matterbridge
2
2
 
3
3
  [![npm version](https://img.shields.io/npm/v/matterbridge.svg)](https://www.npmjs.com/package/matterbridge)
4
4
  [![npm downloads](https://img.shields.io/npm/dt/matterbridge.svg)](https://www.npmjs.com/package/matterbridge)
package/README-DOCKER.md CHANGED
@@ -1,4 +1,4 @@
1
- # <img src="https://github.com/Luligu/matterbridge/blob/main/frontend/public/matterbridge%2064x64.png" alt="Matterbridge Logo" width="64px" height="64px">&nbsp;&nbsp;&nbsp;Matterbridge
1
+ # <img src="frontend/public/matterbridge.svg" alt="Matterbridge Logo" width="64px" height="64px">&nbsp;&nbsp;&nbsp;Matterbridge
2
2
 
3
3
  [![npm version](https://img.shields.io/npm/v/matterbridge.svg)](https://www.npmjs.com/package/matterbridge)
4
4
  [![npm downloads](https://img.shields.io/npm/dt/matterbridge.svg)](https://www.npmjs.com/package/matterbridge)
package/README-EDGE.md CHANGED
@@ -1,4 +1,4 @@
1
- # <img src="https://github.com/Luligu/matterbridge/blob/main/frontend/public/matterbridge%2064x64.png" alt="Matterbridge Logo" width="64px" height="64px">&nbsp;&nbsp;&nbsp;Matterbridge edge
1
+ # <img src="frontend/public/matterbridge.svg" alt="Matterbridge Logo" width="64px" height="64px">&nbsp;&nbsp;&nbsp;Matterbridge edge
2
2
 
3
3
  Matterbridge Edge is the version of Matterbridge running with the new Matter.js API.
4
4
 
package/README-NGINX.md CHANGED
@@ -1,4 +1,4 @@
1
- # <img src="https://github.com/Luligu/matterbridge/blob/main/frontend/public/matterbridge%2064x64.png" alt="Matterbridge Logo" width="64px" height="64px">&nbsp;&nbsp;&nbsp;Matterbridge
1
+ # <img src="frontend/public/matterbridge.svg" alt="Matterbridge Logo" width="64px" height="64px">&nbsp;&nbsp;&nbsp;Matterbridge
2
2
 
3
3
  [![npm version](https://img.shields.io/npm/v/matterbridge.svg)](https://www.npmjs.com/package/matterbridge)
4
4
  [![npm downloads](https://img.shields.io/npm/dt/matterbridge.svg)](https://www.npmjs.com/package/matterbridge)
package/README-PODMAN.md CHANGED
@@ -1,4 +1,4 @@
1
- # <img src="https://github.com/Luligu/matterbridge/blob/main/frontend/public/matterbridge%2064x64.png" alt="Matterbridge Logo" width="64px" height="64px">&nbsp;&nbsp;&nbsp;Matterbridge
1
+ # <img src="frontend/public/matterbridge.svg" alt="Matterbridge Logo" width="64px" height="64px">&nbsp;&nbsp;&nbsp;Matterbridge
2
2
 
3
3
  [![npm version](https://img.shields.io/npm/v/matterbridge.svg)](https://www.npmjs.com/package/matterbridge)
4
4
  [![npm downloads](https://img.shields.io/npm/dt/matterbridge.svg)](https://www.npmjs.com/package/matterbridge)
package/README-SERVICE.md CHANGED
@@ -1,4 +1,4 @@
1
- # <img src="https://github.com/Luligu/matterbridge/blob/main/frontend/public/matterbridge%2064x64.png" alt="Matterbridge Logo" width="64px" height="64px">&nbsp;&nbsp;&nbsp;Matterbridge
1
+ # <img src="frontend/public/matterbridge.svg" alt="Matterbridge Logo" width="64px" height="64px">&nbsp;&nbsp;&nbsp;Matterbridge
2
2
 
3
3
  [![npm version](https://img.shields.io/npm/v/matterbridge.svg)](https://www.npmjs.com/package/matterbridge)
4
4
  [![npm downloads](https://img.shields.io/npm/dt/matterbridge.svg)](https://www.npmjs.com/package/matterbridge)
package/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # <img src="https://github.com/Luligu/matterbridge/blob/main/frontend/public/matterbridge%2064x64.png" alt="Matterbridge Logo" width="64px" height="64px">&nbsp;&nbsp;&nbsp;Matterbridge
1
+ # <img src="frontend/public/matterbridge.svg" alt="Matterbridge Logo" width="64px" height="64px">&nbsp;&nbsp;&nbsp;Matterbridge
2
2
 
3
3
  [![npm version](https://img.shields.io/npm/v/matterbridge.svg)](https://www.npmjs.com/package/matterbridge)
4
4
  [![npm downloads](https://img.shields.io/npm/dt/matterbridge.svg)](https://www.npmjs.com/package/matterbridge)
@@ -48,6 +48,7 @@ To run Matterbridge, you need either a [Node.js](https://nodejs.org/en) environm
48
48
 
49
49
  If you don't have Node.js already install, please use this method to install it on a debian device: https://github.com/nodesource/distributions.
50
50
  The supported versions of node are 18, 20 and 22. Please install node 22 LTS.
51
+ Node 23 is not supported.
51
52
  Nvm is not a good choice and should not be used for production.
52
53
 
53
54
  If you don't have Docker already install, please use this method to install it on a debian device: https://docs.docker.com/desktop/setup/install/linux/debian/.
@@ -121,34 +122,34 @@ To use the frontend with ssl place the certificates in the .matterbridge/certs d
121
122
  From the frontend you can do all operations in an easy way.
122
123
 
123
124
  Home page:
124
- ![See the screenshot here](https://github.com/Luligu/matterbridge/blob/main/screenshot/Screenshot%20home.jpg)
125
+ ![See the screenshot here](screenshot/Screenshot%20home.jpg)
125
126
 
126
127
  Devices page:
127
- [See the screenshot here](https://github.com/Luligu/matterbridge/blob/main/screenshot/Screenshot%20devices.jpg)
128
+ [See the screenshot here](screenshot/Screenshot%20devices.jpg)
128
129
 
129
130
  Logs page:
130
- [See the screenshot here](https://github.com/Luligu/matterbridge/blob/main/screenshot/Screenshot%20logs.jpg)
131
+ [See the screenshot here](screenshot/Screenshot%20logs.jpg)
131
132
 
132
133
  Config editor:
133
- [See the screenshot here](https://github.com/Luligu/matterbridge/blob/main/screenshot/Screenshot%20config%20editor.jpg)
134
+ [See the screenshot here](screenshot/Screenshot%20config%20editor.jpg)
134
135
 
135
136
  ## Advanced configurations
136
137
 
137
138
  ### Run matterbridge as a daemon with systemctl (Linux only)
138
139
 
139
- [Service configurations](https://github.com/Luligu/matterbridge/blob/main/README-SERVICE.md)
140
+ [Service configurations](README-SERVICE.md)
140
141
 
141
142
  ### Run matterbridge with docker and docker compose
142
143
 
143
- [Docker configurations](https://github.com/Luligu/matterbridge/blob/main/README-DOCKER.md)
144
+ [Docker configurations](README-DOCKER.md)
144
145
 
145
146
  ### Run matterbridge with podman
146
147
 
147
- [Podman configurations](https://github.com/Luligu/matterbridge/blob/main/README-PODMAN.md)
148
+ [Podman configurations](README-PODMAN.md)
148
149
 
149
150
  ### Run matterbridge with nginx
150
151
 
151
- [Nginx configurations](https://github.com/Luligu/matterbridge/blob/main/README-NGINX.md)
152
+ [Nginx configurations](README-NGINX.md)
152
153
 
153
154
  ### Run matterbridge as an home assistant add-on with the official add-on
154
155
 
@@ -160,14 +161,14 @@ The other Home Assistant Community Add-ons and plugins are not verified to work
160
161
 
161
162
  ## Development
162
163
 
163
- [Development](https://github.com/Luligu/matterbridge/blob/main/README-DEV.md)
164
+ [Development](README-DEV.md)
164
165
 
165
166
  ## Plugins
166
167
 
167
168
  ### Shelly
168
169
 
169
170
  <a href="https://github.com/Luligu/matterbridge-shelly">
170
- <img src="https://github.com/Luligu/matterbridge/blob/dev/screenshot/Shelly.png" alt="Shelly plugin logo" width="100" />
171
+ <img src="screenshot/Shelly.svg" alt="Shelly plugin logo" width="100" />
171
172
  </a>
172
173
 
173
174
  Matterbridge shelly plugin allows you to expose all Shelly Gen 1, Gen 2, Gen 3 and BLU devices to Matter.
@@ -196,7 +197,7 @@ Features:
196
197
  ### Zigbee2MQTT
197
198
 
198
199
  <a href="https://github.com/Luligu/matterbridge-zigbee2mqtt">
199
- <img src="https://github.com/Luligu/matterbridge/blob/dev/screenshot/Zigbee2MQTT.png" alt="Zigbee2MQTT plugin logo" width="100" />
200
+ <img src="screenshot/Zigbee2MQTT.svg" alt="Zigbee2MQTT plugin logo" width="100" />
200
201
  </a>
201
202
 
202
203
  Matterbridge zigbee2mqtt is a matterbridge production-level plugin that expose all zigbee2mqtt devices and groups to Matter.
@@ -206,7 +207,7 @@ No hub or dedicated hardware needed.
206
207
  ### Somfy tahoma
207
208
 
208
209
  <a href="https://github.com/Luligu/matterbridge-somfy-tahoma">
209
- <img src="https://github.com/Luligu/matterbridge/blob/dev/screenshot/Somfy.png" alt="Somfy plugin logo" width="100" />
210
+ <img src="screenshot/Somfy.svg" alt="Somfy plugin logo" width="100" />
210
211
  </a>
211
212
 
212
213
  Matterbridge Somfy Tahoma is a matterbridge production-level plugin that expose the Somfy Tahoma screen devices to Matter.
@@ -351,7 +352,7 @@ Place your own certificates in the `.matterbridge/cert` directory:
351
352
  - `key.pem`
352
353
  - `ca.pem` (optional)
353
354
 
354
- ![image](https://github.com/user-attachments/assets/846785ca-6f5c-458b-b786-a6417a4da319)
355
+ ![image](screenshot/Screenshot%20Certificates.png)
355
356
 
356
357
  ### Change the command line
357
358
 
@@ -365,7 +366,7 @@ matterbridge -ssl -frontend 443
365
366
 
366
367
  If the certificate are correctly configured, you will be able to connect with https to the frontend.
367
368
 
368
- ![image](https://github.com/user-attachments/assets/9c38776d-064f-4d91-9359-a2cd3319b1ff)
369
+ ![image](screenshot/Screenshot%20Browser%20Secured.png)
369
370
 
370
371
  ## How to send the debug log files
371
372
 
@@ -373,9 +374,9 @@ If the certificate are correctly configured, you will be able to connect with ht
373
374
 
374
375
  In the frontend, go to settings and enable debug mode as shown below:
375
376
 
376
- ![Debug Matterbridge Settings](https://github.com/user-attachments/assets/83181dc2-969a-4b71-aff4-f1498fa1d665)
377
+ ![Debug Matterbridge Settings](screenshot/Screenshot%20Matterbridge%20Logger%20Debug.png)
377
378
 
378
- ![Debug Matter Settings](https://github.com/user-attachments/assets/617961a9-7cb0-46cf-9878-981f61738f8c)
379
+ ![Debug Matter Settings](screenshot/Screenshot%20Matter%20Logger%20Debug.png)
379
380
 
380
381
  ### Restart
381
382
 
@@ -383,7 +384,7 @@ Wait a few minutes to allow the logs to to accumulate.
383
384
 
384
385
  Then, from the dots menu in the frontend, download the `matterbridge.log` and `matter.log` files.
385
386
 
386
- ![image](https://github.com/user-attachments/assets/04ba65f6-594a-4ff8-9732-3df049f5a33e)
387
+ ![image](screenshot/Screenshot%20Debug%20Download%20Logs.png)
387
388
 
388
389
  # Known general issues
389
390
 
package/dist/cli.js CHANGED
@@ -177,7 +177,7 @@ async function main() {
177
177
  log.debug(`***Matterbridge.loadInstance(true) called`);
178
178
  instance = await Matterbridge.loadInstance(true);
179
179
  log.debug(`***Matterbridge.loadInstance(true) exited`);
180
- if (instance.shutdown) {
180
+ if (!instance || instance.shutdown) {
181
181
  shutdown();
182
182
  }
183
183
  else {
package/dist/frontend.js CHANGED
@@ -269,7 +269,7 @@ export class Frontend {
269
269
  this.log.debug('The frontend sent /api/devices');
270
270
  const devices = [];
271
271
  this.matterbridge.devices.forEach(async (device) => {
272
- if (!device.plugin || !device.name || !device.deviceName || !device.serialNumber || !device.uniqueId)
272
+ if (!device.plugin || !device.name || !device.deviceName || !device.serialNumber || !device.uniqueId || !device.lifecycle.isReady)
273
273
  return;
274
274
  const cluster = this.getClusterTextFromDevice(device);
275
275
  devices.push({
@@ -1087,9 +1087,22 @@ export class Frontend {
1087
1087
  }
1088
1088
  else if (data.method === '/api/advertise') {
1089
1089
  const pairingCodes = await this.matterbridge.advertiseServerNode(this.matterbridge.serverNode);
1090
+ this.matterbridge.matterbridgeInformation.matterbridgeAdvertise = true;
1091
+ this.matterbridge.matterbridgeQrPairingCode = pairingCodes?.qrPairingCode;
1092
+ this.matterbridge.matterbridgeManualPairingCode = pairingCodes?.manualPairingCode;
1093
+ this.wssSendRefreshRequired();
1094
+ this.wssSendSnackbarMessage(`Started fabrics share`, 0);
1090
1095
  client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, response: pairingCodes }));
1091
1096
  return;
1092
1097
  }
1098
+ else if (data.method === '/api/stopadvertise') {
1099
+ await this.matterbridge.stopAdvertiseServerNode(this.matterbridge.serverNode);
1100
+ this.matterbridge.matterbridgeInformation.matterbridgeAdvertise = false;
1101
+ this.wssSendRefreshRequired();
1102
+ this.wssSendSnackbarMessage(`Stopped fabrics share`, 0);
1103
+ client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src }));
1104
+ return;
1105
+ }
1093
1106
  else if (data.method === '/api/settings') {
1094
1107
  client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, response: await this.getApiSettings() }));
1095
1108
  return;
@@ -1104,7 +1117,7 @@ export class Frontend {
1104
1117
  this.matterbridge.devices.forEach(async (device) => {
1105
1118
  if (data.params.pluginName && data.params.pluginName !== device.plugin)
1106
1119
  return;
1107
- if (!device.plugin || !device.name || !device.deviceName || !device.serialNumber || !device.uniqueId)
1120
+ if (!device.plugin || !device.name || !device.deviceName || !device.serialNumber || !device.uniqueId || !device.lifecycle.isReady)
1108
1121
  return;
1109
1122
  const cluster = this.getClusterTextFromDevice(device);
1110
1123
  devices.push({
@@ -4,8 +4,8 @@ import { promises as fs } from 'node:fs';
4
4
  import EventEmitter from 'node:events';
5
5
  import { AnsiLogger, UNDERLINE, UNDERLINEOFF, YELLOW, db, debugStringify, BRIGHT, RESET, er, nf, rs, wr, RED, GREEN, zb, CYAN, nt } from './logger/export.js';
6
6
  import { NodeStorageManager } from './storage/export.js';
7
- import { getParameter, getIntParameter, hasParameter } from './utils/export.js';
8
- import { logInterfaces, copyDirectory, getNpmPackageVersion, getGlobalNodeModules } from './utils/utils.js';
7
+ import { getParameter, getIntParameter, hasParameter, copyDirectory, withTimeout } from './utils/export.js';
8
+ import { logInterfaces, getNpmPackageVersion, getGlobalNodeModules } from './utils/network.js';
9
9
  import { PluginManager } from './pluginManager.js';
10
10
  import { DeviceManager } from './deviceManager.js';
11
11
  import { MatterbridgeEndpoint } from './matterbridgeEndpoint.js';
@@ -52,6 +52,7 @@ export class Matterbridge extends EventEmitter {
52
52
  matterbridgeFabricInformations: [],
53
53
  matterbridgeSessionInformations: [],
54
54
  matterbridgePaired: false,
55
+ matterbridgeAdvertise: false,
55
56
  bridgeMode: '',
56
57
  restartMode: '',
57
58
  readOnly: hasParameter('readonly'),
@@ -251,7 +252,7 @@ export class Matterbridge extends EventEmitter {
251
252
  this.port = getIntParameter('port') ?? (await this.nodeContext.get('matterport', 5540)) ?? 5540;
252
253
  this.passcode = getIntParameter('passcode') ?? (await this.nodeContext.get('matterpasscode')) ?? PaseClient.generateRandomPasscode();
253
254
  this.discriminator = getIntParameter('discriminator') ?? (await this.nodeContext.get('matterdiscriminator')) ?? PaseClient.generateRandomDiscriminator();
254
- this.log.debug(`Initializing commissioning server for Matterbridge... on port ${this.port} with passcode ${this.passcode} and discriminator ${this.discriminator}`);
255
+ this.log.debug(`Initializing server node for Matterbridge... on port ${this.port} with passcode ${this.passcode} and discriminator ${this.discriminator}`);
255
256
  if (hasParameter('logger')) {
256
257
  const level = getParameter('logger');
257
258
  if (level === 'debug') {
@@ -289,6 +290,8 @@ export class Matterbridge extends EventEmitter {
289
290
  }
290
291
  this.log.notice('Matterbridge is starting...');
291
292
  this.log.debug(`Matterbridge logLevel: ${this.log.logLevel} fileLoger: ${this.matterbridgeInformation.fileLogger}.`);
293
+ if (this.profile !== undefined)
294
+ this.log.debug(`Matterbridge profile: ${this.profile}.`);
292
295
  if (hasParameter('matterlogger')) {
293
296
  const level = getParameter('matterlogger');
294
297
  if (level === 'debug') {
@@ -517,6 +520,7 @@ export class Matterbridge extends EventEmitter {
517
520
  return;
518
521
  }
519
522
  if (hasParameter('factoryreset')) {
523
+ this.initialized = true;
520
524
  await this.shutdownProcessAndFactoryReset();
521
525
  this.shutdown = true;
522
526
  return;
@@ -529,6 +533,7 @@ export class Matterbridge extends EventEmitter {
529
533
  throw new Error(`Fatal error creating matter storage: ${error instanceof Error ? error.message : error}`);
530
534
  }
531
535
  if (hasParameter('reset') && getParameter('reset') === undefined) {
536
+ this.initialized = true;
532
537
  await this.shutdownProcessAndReset();
533
538
  this.shutdown = true;
534
539
  return;
@@ -1526,20 +1531,6 @@ export class Matterbridge extends EventEmitter {
1526
1531
  if (!matterServerNode)
1527
1532
  return;
1528
1533
  this.log.notice(`Closing ${matterServerNode.id} server node`);
1529
- const withTimeout = (promise, ms) => {
1530
- return new Promise((resolve, reject) => {
1531
- const timer = setTimeout(() => reject(new Error('Operation timed out')), ms);
1532
- promise
1533
- .then((result) => {
1534
- clearTimeout(timer);
1535
- resolve(result);
1536
- })
1537
- .catch((error) => {
1538
- clearTimeout(timer);
1539
- reject(error);
1540
- });
1541
- });
1542
- };
1543
1534
  try {
1544
1535
  await withTimeout(matterServerNode.close(), 30000);
1545
1536
  this.log.info(`Closed ${matterServerNode.id} server node`);
@@ -1552,7 +1543,7 @@ export class Matterbridge extends EventEmitter {
1552
1543
  if (matterServerNode) {
1553
1544
  await matterServerNode.env.get(DeviceCommissioner)?.allowBasicCommissioning();
1554
1545
  const { qrPairingCode, manualPairingCode } = matterServerNode.state.commissioning.pairingCodes;
1555
- this.log.notice(`Advertising for ${matterServerNode.id} is now started with the following pairing codes: qrPairingCode ${qrPairingCode}, manualPairingCode ${manualPairingCode}`);
1546
+ this.log.notice(`Started advertising for ${matterServerNode.id} with the following pairing codes: qrPairingCode ${qrPairingCode}, manualPairingCode ${manualPairingCode}`);
1556
1547
  return { qrPairingCode, manualPairingCode };
1557
1548
  }
1558
1549
  }
@@ -25,7 +25,7 @@ export class MatterbridgePlatform {
25
25
  this.log = log;
26
26
  this.config = config;
27
27
  if (!isValidString(this.config.name) || this.config.name === '')
28
- return;
28
+ throw new Error('Platform: the plugin name is missing or invalid.');
29
29
  this.log.debug(`Creating storage for plugin ${this.config.name} in ${path.join(this.matterbridge.matterbridgeDirectory, this.config.name)}`);
30
30
  this.storage = new NodeStorageManager({
31
31
  dir: path.join(this.matterbridge.matterbridgeDirectory, this.config.name),
@@ -37,8 +37,8 @@ export class MatterbridgePlatform {
37
37
  this.log.debug(`Creating context for plugin ${this.config.name}`);
38
38
  this.contextReady = this.storage.createStorage('context').then((context) => {
39
39
  this.context = context;
40
+ this.context.remove('endpointMap');
40
41
  this.log.debug(`Created context for plugin ${this.config.name}`);
41
- return context;
42
42
  });
43
43
  this.log.debug(`Loading selectDevice for plugin ${this.config.name}`);
44
44
  this.selectDeviceContextReady = this.storage.createStorage('selectDevice').then(async (context) => {
@@ -83,13 +83,9 @@ export class MatterbridgePlatform {
83
83
  this.selectEntity.clear();
84
84
  this.registeredEndpoints.clear();
85
85
  this.registeredEndpointsByName.clear();
86
- this.contextReady = undefined;
87
- this.selectDeviceContextReady = undefined;
88
- this.selectEntityContextReady = undefined;
89
86
  await this.context?.close();
90
87
  this.context = undefined;
91
88
  await this.storage?.close();
92
- this.storage = undefined;
93
89
  }
94
90
  async onChangeLoggerLevel(logLevel) {
95
91
  this.log.debug(`The plugin doesn't override onChangeLoggerLevel. Logger level set to: ${logLevel}`);
@@ -454,8 +454,6 @@ export class PluginManager {
454
454
  plugin.loaded = true;
455
455
  plugin.registeredDevices = 0;
456
456
  plugin.addedDevices = 0;
457
- plugin.configJson = config;
458
- plugin.schemaJson = await this.loadSchema(plugin);
459
457
  await this.saveToStorage();
460
458
  this.log.notice(`Loaded plugin ${plg}${plugin.name}${nt} type ${typ}${platform.type}${nt} (entrypoint ${UNDERLINE}${pluginEntry}${UNDERLINEOFF})`);
461
459
  if (start)
@@ -0,0 +1,26 @@
1
+ import { AnsiLogger } from '../logger/export.js';
2
+ export async function copyDirectory(srcDir, destDir) {
3
+ const log = new AnsiLogger({ logName: 'Archive', logTimestampFormat: 4, logLevel: "info" });
4
+ const fs = await import('node:fs').then((mod) => mod.promises);
5
+ const path = await import('node:path');
6
+ log.debug(`copyDirectory: copying directory from ${srcDir} to ${destDir}`);
7
+ try {
8
+ await fs.mkdir(destDir, { recursive: true });
9
+ const entries = await fs.readdir(srcDir, { withFileTypes: true });
10
+ for (const entry of entries) {
11
+ const srcPath = path.join(srcDir, entry.name);
12
+ const destPath = path.join(destDir, entry.name);
13
+ if (entry.isDirectory()) {
14
+ await copyDirectory(srcPath, destPath);
15
+ }
16
+ else if (entry.isFile()) {
17
+ await fs.copyFile(srcPath, destPath);
18
+ }
19
+ }
20
+ return true;
21
+ }
22
+ catch (error) {
23
+ log.error(`copyDirectory error copying from ${srcDir} to ${destDir}: ${error instanceof Error ? error.message : error}`);
24
+ return false;
25
+ }
26
+ }
@@ -0,0 +1,69 @@
1
+ import { AnsiLogger } from '../logger/export.js';
2
+ export async function createZip(outputPath, ...sourcePaths) {
3
+ const log = new AnsiLogger({ logName: 'Archive', logTimestampFormat: 4, logLevel: "info" });
4
+ const { default: archiver } = await import('archiver');
5
+ const { glob } = await import('glob');
6
+ const { createWriteStream, statSync } = await import('node:fs');
7
+ const path = await import('node:path');
8
+ log.debug(`creating archive ${outputPath} from ${sourcePaths.join(', ')} ...`);
9
+ return new Promise((resolve, reject) => {
10
+ const output = createWriteStream(outputPath);
11
+ const archive = archiver('zip', {
12
+ zlib: { level: 9 },
13
+ });
14
+ output.on('close', () => {
15
+ log.debug(`archive ${outputPath} closed with ${archive.pointer()} total bytes`);
16
+ resolve(archive.pointer());
17
+ });
18
+ output.on('end', () => {
19
+ log.debug(`archive ${outputPath} data has been drained ${archive.pointer()} total bytes`);
20
+ });
21
+ archive.on('error', (error) => {
22
+ log.error(`archive error: ${error.message}`);
23
+ reject(error);
24
+ });
25
+ archive.on('warning', (error) => {
26
+ if (error.code === 'ENOENT') {
27
+ log.warn(`archive warning: ${error.message}`);
28
+ }
29
+ else {
30
+ log.error(`archive warning: ${error.message}`);
31
+ reject(error);
32
+ }
33
+ });
34
+ archive.on('entry', (entry) => {
35
+ log.debug(`- archive entry: ${entry.name}`);
36
+ });
37
+ archive.pipe(output);
38
+ for (const sourcePath of sourcePaths) {
39
+ let stats;
40
+ try {
41
+ stats = statSync(sourcePath);
42
+ }
43
+ catch (error) {
44
+ if (sourcePath.includes('*')) {
45
+ const files = glob.sync(sourcePath.replace(/\\/g, '/'));
46
+ log.debug(`adding files matching glob pattern: ${sourcePath}`);
47
+ for (const file of files) {
48
+ log.debug(`- glob file: ${file}`);
49
+ archive.file(file, { name: file });
50
+ }
51
+ }
52
+ else {
53
+ log.error(`no files or directory found for pattern ${sourcePath}: ${error}`);
54
+ }
55
+ continue;
56
+ }
57
+ if (stats.isFile()) {
58
+ log.debug(`adding file: ${sourcePath}`);
59
+ archive.file(sourcePath, { name: path.basename(sourcePath) });
60
+ }
61
+ else if (stats.isDirectory()) {
62
+ log.debug(`adding directory: ${sourcePath}`);
63
+ archive.directory(sourcePath, path.basename(sourcePath));
64
+ }
65
+ }
66
+ log.debug(`finalizing archive ${outputPath}...`);
67
+ archive.finalize().catch(reject);
68
+ });
69
+ }
@@ -0,0 +1,35 @@
1
+ export function deepCopy(value) {
2
+ if (typeof value !== 'object' || value === null) {
3
+ return value;
4
+ }
5
+ else if (Array.isArray(value)) {
6
+ return value.map((item) => deepCopy(item));
7
+ }
8
+ else if (value instanceof Date) {
9
+ return new Date(value.getTime());
10
+ }
11
+ else if (value instanceof Map) {
12
+ const mapCopy = new Map();
13
+ value.forEach((val, key) => {
14
+ mapCopy.set(key, deepCopy(val));
15
+ });
16
+ return mapCopy;
17
+ }
18
+ else if (value instanceof Set) {
19
+ const setCopy = new Set();
20
+ value.forEach((item) => {
21
+ setCopy.add(deepCopy(item));
22
+ });
23
+ return setCopy;
24
+ }
25
+ else {
26
+ const proto = Object.getPrototypeOf(value);
27
+ const copy = Object.create(proto);
28
+ for (const key in value) {
29
+ if (Object.prototype.hasOwnProperty.call(value, key)) {
30
+ copy[key] = deepCopy(value[key]);
31
+ }
32
+ }
33
+ return copy;
34
+ }
35
+ }
@@ -0,0 +1,56 @@
1
+ export function deepEqual(a, b, excludeProperties = []) {
2
+ const debug = false;
3
+ const debugLog = (...messages) => {
4
+ if (debug) {
5
+ console.log(...messages);
6
+ }
7
+ };
8
+ if (a === b) {
9
+ return true;
10
+ }
11
+ if (typeof a !== typeof b) {
12
+ debugLog(`deepEqual false for typeof a: ${typeof a} typeof b: ${typeof b}`);
13
+ return false;
14
+ }
15
+ if (a == null || b == null) {
16
+ debugLog('deepEqual false for == null');
17
+ return false;
18
+ }
19
+ if (Array.isArray(a) && Array.isArray(b)) {
20
+ if (a.length !== b.length) {
21
+ debugLog(`deepEqual false for array a.length(${a.length}) !== b.length(${b.length})`);
22
+ return false;
23
+ }
24
+ for (let i = 0; i < a.length; i++) {
25
+ if (!deepEqual(a[i], b[i], excludeProperties)) {
26
+ debugLog('deepEqual false for array !deepEqual(a[i], b[i])');
27
+ debugLog(`- aProps.length(${a[i]}):`, a[i]);
28
+ debugLog(`- bProps.length(${b[i]}):`, b[i]);
29
+ return false;
30
+ }
31
+ }
32
+ return true;
33
+ }
34
+ if (typeof a === 'object' && typeof b === 'object') {
35
+ const aProps = Object.getOwnPropertyNames(a).filter((prop) => !excludeProperties.includes(prop));
36
+ const bProps = Object.getOwnPropertyNames(b).filter((prop) => !excludeProperties.includes(prop));
37
+ if (aProps.length !== bProps.length) {
38
+ debugLog(`deepEqual false for aProps.length(${aProps.length}) !== bProps.length(${bProps.length})`);
39
+ debugLog(`- aProps.length(${aProps.length}):`, aProps);
40
+ debugLog(`- bProps.length(${bProps.length}):`, bProps);
41
+ return false;
42
+ }
43
+ for (const prop of aProps) {
44
+ if (!Object.prototype.hasOwnProperty.call(b, prop)) {
45
+ debugLog(`deepEqual false for !b.hasOwnProperty(${prop})`);
46
+ return false;
47
+ }
48
+ if (!deepEqual(a[prop], b[prop], excludeProperties)) {
49
+ debugLog(`deepEqual false for !deepEqual(a[${prop}], b[${prop}])`);
50
+ return false;
51
+ }
52
+ }
53
+ return true;
54
+ }
55
+ return false;
56
+ }
@@ -1,4 +1,9 @@
1
- export * from './utils.js';
1
+ export * from './network.js';
2
2
  export * from './parameter.js';
3
3
  export * from './isvalid.js';
4
4
  export * from './colorUtils.js';
5
+ export * from './deepCopy.js';
6
+ export * from './deepEqual.js';
7
+ export * from './copyDirectory.js';
8
+ export * from './createZip.js';
9
+ export * from './wait.js';