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 +15 -8
- package/README-DEV.md +1 -1
- package/README-DOCKER.md +1 -1
- package/README-EDGE.md +1 -1
- package/README-NGINX.md +1 -1
- package/README-PODMAN.md +1 -1
- package/README-SERVICE.md +1 -1
- package/README.md +19 -18
- package/dist/cli.js +1 -1
- package/dist/frontend.js +15 -2
- package/dist/matterbridge.js +9 -18
- package/dist/matterbridgePlatform.js +2 -6
- package/dist/pluginManager.js +0 -2
- package/dist/utils/copyDirectory.js +26 -0
- package/dist/utils/createZip.js +69 -0
- package/dist/utils/deepCopy.js +35 -0
- package/dist/utils/deepEqual.js +56 -0
- package/dist/utils/export.js +6 -1
- package/dist/utils/network.js +143 -0
- package/dist/utils/wait.js +57 -0
- package/frontend/build/asset-manifest.json +3 -3
- package/frontend/build/index.html +1 -1
- package/frontend/build/static/js/{main.be75f5a3.js → main.82e3eb54.js} +9 -9
- package/frontend/build/static/js/main.82e3eb54.js.map +1 -0
- package/npm-shrinkwrap.json +14 -14
- package/package.json +3 -3
- package/dist/utils/utils.js +0 -367
- package/frontend/build/static/js/main.be75f5a3.js.map +0 -1
- /package/frontend/build/static/js/{main.be75f5a3.js.LICENSE.txt → main.82e3eb54.js.LICENSE.txt} +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# <img src="
|
|
1
|
+
# <img src="frontend/public/matterbridge.svg" alt="Matterbridge Logo" width="64px" height="64px"> 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-
|
|
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
|
-
- [
|
|
43
|
+
- [pluginManager]: Refactor PluginManager to optimize memory and load time.
|
|
44
44
|
- [frontend]: Frontend v.2.4.2.
|
|
45
|
-
- [
|
|
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
|
|
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
|
|
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](
|
|
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="
|
|
1
|
+
# <img src="frontend/public/matterbridge.svg" alt="Matterbridge Logo" width="64px" height="64px"> Matterbridge
|
|
2
2
|
|
|
3
3
|
[](https://www.npmjs.com/package/matterbridge)
|
|
4
4
|
[](https://www.npmjs.com/package/matterbridge)
|
package/README-DOCKER.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# <img src="
|
|
1
|
+
# <img src="frontend/public/matterbridge.svg" alt="Matterbridge Logo" width="64px" height="64px"> Matterbridge
|
|
2
2
|
|
|
3
3
|
[](https://www.npmjs.com/package/matterbridge)
|
|
4
4
|
[](https://www.npmjs.com/package/matterbridge)
|
package/README-EDGE.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# <img src="
|
|
1
|
+
# <img src="frontend/public/matterbridge.svg" alt="Matterbridge Logo" width="64px" height="64px"> 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="
|
|
1
|
+
# <img src="frontend/public/matterbridge.svg" alt="Matterbridge Logo" width="64px" height="64px"> Matterbridge
|
|
2
2
|
|
|
3
3
|
[](https://www.npmjs.com/package/matterbridge)
|
|
4
4
|
[](https://www.npmjs.com/package/matterbridge)
|
package/README-PODMAN.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# <img src="
|
|
1
|
+
# <img src="frontend/public/matterbridge.svg" alt="Matterbridge Logo" width="64px" height="64px"> Matterbridge
|
|
2
2
|
|
|
3
3
|
[](https://www.npmjs.com/package/matterbridge)
|
|
4
4
|
[](https://www.npmjs.com/package/matterbridge)
|
package/README-SERVICE.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# <img src="
|
|
1
|
+
# <img src="frontend/public/matterbridge.svg" alt="Matterbridge Logo" width="64px" height="64px"> Matterbridge
|
|
2
2
|
|
|
3
3
|
[](https://www.npmjs.com/package/matterbridge)
|
|
4
4
|
[](https://www.npmjs.com/package/matterbridge)
|
package/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# <img src="
|
|
1
|
+
# <img src="frontend/public/matterbridge.svg" alt="Matterbridge Logo" width="64px" height="64px"> Matterbridge
|
|
2
2
|
|
|
3
3
|
[](https://www.npmjs.com/package/matterbridge)
|
|
4
4
|
[](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
|
-

|
|
125
126
|
|
|
126
127
|
Devices page:
|
|
127
|
-
[See the screenshot here](
|
|
128
|
+
[See the screenshot here](screenshot/Screenshot%20devices.jpg)
|
|
128
129
|
|
|
129
130
|
Logs page:
|
|
130
|
-
[See the screenshot here](
|
|
131
|
+
[See the screenshot here](screenshot/Screenshot%20logs.jpg)
|
|
131
132
|
|
|
132
133
|
Config editor:
|
|
133
|
-
[See the screenshot here](
|
|
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](
|
|
140
|
+
[Service configurations](README-SERVICE.md)
|
|
140
141
|
|
|
141
142
|
### Run matterbridge with docker and docker compose
|
|
142
143
|
|
|
143
|
-
[Docker configurations](
|
|
144
|
+
[Docker configurations](README-DOCKER.md)
|
|
144
145
|
|
|
145
146
|
### Run matterbridge with podman
|
|
146
147
|
|
|
147
|
-
[Podman configurations](
|
|
148
|
+
[Podman configurations](README-PODMAN.md)
|
|
148
149
|
|
|
149
150
|
### Run matterbridge with nginx
|
|
150
151
|
|
|
151
|
-
[Nginx configurations](
|
|
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](
|
|
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="
|
|
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="
|
|
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="
|
|
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
|
-

|
|
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
|
-

|
|
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
|
-

|
|
377
378
|
|
|
378
|
-

|
|
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
|
-

|
|
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({
|
package/dist/matterbridge.js
CHANGED
|
@@ -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,
|
|
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
|
|
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(`
|
|
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
|
-
|
|
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}`);
|
package/dist/pluginManager.js
CHANGED
|
@@ -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
|
+
}
|
package/dist/utils/export.js
CHANGED
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
export * from './
|
|
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';
|