matterbridge 3.0.2-dev-20250508-7214e17 → 3.0.2-dev-20250510-ae61aa7
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 +3 -5
- package/README.md +1 -1
- package/dist/frontend.js +22 -13
- package/dist/matterbridge.js +27 -1
- package/dist/matterbridgeBehaviors.js +1 -1
- package/dist/matterbridgeEndpoint.js +4 -1
- package/dist/matterbridgeEndpointHelpers.js +15 -0
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -8,15 +8,12 @@ If you like this project and find it useful, please consider giving it a star on
|
|
|
8
8
|
<img src="bmc-button.svg" alt="Buy me a coffee" width="120">
|
|
9
9
|
</a>
|
|
10
10
|
|
|
11
|
-
### Home Assistant
|
|
12
|
-
|
|
13
|
-
If you want to run Matterbridge in Home Assistant please use the official add-on https://github.com/Luligu/matterbridge-home-assistant-addon that also has Ingress and side panel.
|
|
14
|
-
It is also available the official Matterbridge Home Assistant plugin https://github.com/Luligu/matterbridge-hass.
|
|
15
|
-
|
|
16
11
|
## [3.0.2] - 2025-05-??
|
|
17
12
|
|
|
18
13
|
### Added
|
|
19
14
|
|
|
15
|
+
- [virtual] Added virtual devices Restart Matterbridge and Update Matterbridge.
|
|
16
|
+
|
|
20
17
|
### Changed
|
|
21
18
|
|
|
22
19
|
- [package]: Updated dependencies.
|
|
@@ -42,6 +39,7 @@ It is also available the official Matterbridge Home Assistant plugin https://git
|
|
|
42
39
|
- [frontend]: Moved all settings from express to web socket.
|
|
43
40
|
- [endpoint]: Added OperationalState cluster helper and behavior.
|
|
44
41
|
- [behaviors]: Added Jest test on MatterbridgeBehaviors.
|
|
42
|
+
- [docker]: Further optimized the dockerfile for the image with tag latest.
|
|
45
43
|
|
|
46
44
|
### Changed
|
|
47
45
|
|
package/README.md
CHANGED
|
@@ -33,7 +33,7 @@ No complex setup just copy paste the installation scripts.
|
|
|
33
33
|
|
|
34
34
|
Matterbridge is light weight and run also on slow Linux machine with 512MB of memory.
|
|
35
35
|
|
|
36
|
-
It runs perfectly on Windows too.
|
|
36
|
+
It runs perfectly on macOS and Windows too.
|
|
37
37
|
|
|
38
38
|
If you like this project and find it useful, please consider giving it a star on GitHub at https://github.com/Luligu/matterbridge and sponsoring it.
|
|
39
39
|
|
package/dist/frontend.js
CHANGED
|
@@ -8,7 +8,7 @@ import express from 'express';
|
|
|
8
8
|
import WebSocket, { WebSocketServer } from 'ws';
|
|
9
9
|
import multer from 'multer';
|
|
10
10
|
import { AnsiLogger, stringify, debugStringify, CYAN, db, er, nf, rs, UNDERLINE, UNDERLINEOFF, wr, YELLOW, nt } from './logger/export.js';
|
|
11
|
-
import { createZip,
|
|
11
|
+
import { createZip, isValidArray, isValidNumber, isValidObject, isValidString, isValidBoolean } from './utils/export.js';
|
|
12
12
|
import { plg } from './matterbridgeTypes.js';
|
|
13
13
|
import { hasParameter } from './utils/export.js';
|
|
14
14
|
import { BridgedDeviceBasicInformation, PowerSource } from '@matter/main/clusters';
|
|
@@ -33,11 +33,6 @@ export class Frontend {
|
|
|
33
33
|
httpServer;
|
|
34
34
|
httpsServer;
|
|
35
35
|
webSocketServer;
|
|
36
|
-
prevCpus = deepCopy(os.cpus());
|
|
37
|
-
lastCpuUsage = 0;
|
|
38
|
-
memoryData = [];
|
|
39
|
-
memoryInterval;
|
|
40
|
-
memoryTimeout;
|
|
41
36
|
constructor(matterbridge) {
|
|
42
37
|
this.matterbridge = matterbridge;
|
|
43
38
|
this.log = new AnsiLogger({ logName: 'Frontend', logTimestampFormat: 4, logLevel: hasParameter('debug') ? "debug" : "info" });
|
|
@@ -452,13 +447,27 @@ export class Frontend {
|
|
|
452
447
|
}
|
|
453
448
|
async stop() {
|
|
454
449
|
if (this.httpServer) {
|
|
455
|
-
this.httpServer.close()
|
|
450
|
+
this.httpServer.close((error) => {
|
|
451
|
+
if (error) {
|
|
452
|
+
this.log.error(`Error closing http server: ${error}`);
|
|
453
|
+
}
|
|
454
|
+
else {
|
|
455
|
+
this.log.debug('Http server closed successfully');
|
|
456
|
+
}
|
|
457
|
+
});
|
|
456
458
|
this.httpServer.removeAllListeners();
|
|
457
459
|
this.httpServer = undefined;
|
|
458
460
|
this.log.debug('Frontend http server closed successfully');
|
|
459
461
|
}
|
|
460
462
|
if (this.httpsServer) {
|
|
461
|
-
this.httpsServer.close()
|
|
463
|
+
this.httpsServer.close((error) => {
|
|
464
|
+
if (error) {
|
|
465
|
+
this.log.error(`Error closing https server: ${error}`);
|
|
466
|
+
}
|
|
467
|
+
else {
|
|
468
|
+
this.log.debug('Https server closed successfully');
|
|
469
|
+
}
|
|
470
|
+
});
|
|
462
471
|
this.httpsServer.removeAllListeners();
|
|
463
472
|
this.httpsServer = undefined;
|
|
464
473
|
this.log.debug('Frontend https server closed successfully');
|
|
@@ -1203,7 +1212,7 @@ export class Frontend {
|
|
|
1203
1212
|
}
|
|
1204
1213
|
else if (data.method === '/api/action') {
|
|
1205
1214
|
if (!isValidString(data.params.plugin, 5) || !isValidString(data.params.action, 1)) {
|
|
1206
|
-
client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Wrong parameter
|
|
1215
|
+
client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Wrong parameter in /api/action' }));
|
|
1207
1216
|
return;
|
|
1208
1217
|
}
|
|
1209
1218
|
const plugin = this.matterbridge.plugins.get(data.params.plugin);
|
|
@@ -1446,10 +1455,10 @@ export class Frontend {
|
|
|
1446
1455
|
}
|
|
1447
1456
|
if (isValidArray(config.blackList, 1)) {
|
|
1448
1457
|
if (select === 'serial' && config.blackList.includes(data.params.serial)) {
|
|
1449
|
-
config.blackList = config.blackList.filter((
|
|
1458
|
+
config.blackList = config.blackList.filter((item) => item !== data.params.serial);
|
|
1450
1459
|
}
|
|
1451
1460
|
else if (select === 'name' && config.blackList.includes(data.params.name)) {
|
|
1452
|
-
config.blackList = config.blackList.filter((
|
|
1461
|
+
config.blackList = config.blackList.filter((item) => item !== data.params.name);
|
|
1453
1462
|
}
|
|
1454
1463
|
}
|
|
1455
1464
|
if (plugin.platform)
|
|
@@ -1484,10 +1493,10 @@ export class Frontend {
|
|
|
1484
1493
|
}
|
|
1485
1494
|
if (isValidArray(config.whiteList, 1)) {
|
|
1486
1495
|
if (select === 'serial' && config.whiteList.includes(data.params.serial)) {
|
|
1487
|
-
config.whiteList = config.whiteList.filter((
|
|
1496
|
+
config.whiteList = config.whiteList.filter((item) => item !== data.params.serial);
|
|
1488
1497
|
}
|
|
1489
1498
|
else if (select === 'name' && config.whiteList.includes(data.params.name)) {
|
|
1490
|
-
config.whiteList = config.whiteList.filter((
|
|
1499
|
+
config.whiteList = config.whiteList.filter((item) => item !== data.params.name);
|
|
1491
1500
|
}
|
|
1492
1501
|
}
|
|
1493
1502
|
if (isValidArray(config.blackList)) {
|
package/dist/matterbridge.js
CHANGED
|
@@ -12,10 +12,12 @@ import { DeviceManager } from './deviceManager.js';
|
|
|
12
12
|
import { MatterbridgeEndpoint } from './matterbridgeEndpoint.js';
|
|
13
13
|
import { bridge } from './matterbridgeDeviceTypes.js';
|
|
14
14
|
import { Frontend } from './frontend.js';
|
|
15
|
-
import { DeviceTypeId, Endpoint as EndpointNode, Logger, LogLevel as MatterLogLevel, LogFormat as MatterLogFormat, VendorId, StorageService, Environment, ServerNode, UINT32_MAX, UINT16_MAX, } from '@matter/main';
|
|
15
|
+
import { DeviceTypeId, Endpoint as EndpointNode, Logger, LogLevel as MatterLogLevel, LogFormat as MatterLogFormat, VendorId, StorageService, Environment, ServerNode, UINT32_MAX, UINT16_MAX, Endpoint, } from '@matter/main';
|
|
16
16
|
import { DeviceCommissioner, FabricAction, MdnsService, PaseClient } from '@matter/main/protocol';
|
|
17
|
+
import { OnOffPlugInUnitDevice } from '@matter/main/devices/on-off-plug-in-unit';
|
|
17
18
|
import { AggregatorEndpoint } from '@matter/main/endpoints';
|
|
18
19
|
import { BasicInformationServer } from '@matter/main/behaviors/basic-information';
|
|
20
|
+
import { OnOffBaseServer } from '@matter/main/behaviors/on-off';
|
|
19
21
|
import { BridgedDeviceBasicInformationServer } from '@matter/main/behaviors/bridged-device-basic-information';
|
|
20
22
|
const plg = '\u001B[38;5;33m';
|
|
21
23
|
const dev = '\u001B[38;5;79m';
|
|
@@ -1519,6 +1521,30 @@ export class Matterbridge extends EventEmitter {
|
|
|
1519
1521
|
serverNode.lifecycle.decommissioned.on(() => this.log.notice(`Server node for ${storeId} was fully decommissioned successfully!`));
|
|
1520
1522
|
serverNode.lifecycle.online.on(async () => {
|
|
1521
1523
|
this.log.notice(`Server node for ${storeId} is online`);
|
|
1524
|
+
if (!hasParameter('novirtual') && this.bridgeMode === 'bridge') {
|
|
1525
|
+
this.log.notice(`Creating virtual devices for server node ${storeId}`);
|
|
1526
|
+
const virtualRestart = new Endpoint(OnOffPlugInUnitDevice.with(BridgedDeviceBasicInformationServer), { id: 'Restart Matterbridge', bridgedDeviceBasicInformation: { nodeLabel: 'Restart' } });
|
|
1527
|
+
virtualRestart.events.onOff.onOff$Changed.on(async (value) => {
|
|
1528
|
+
if (value) {
|
|
1529
|
+
await virtualRestart.setStateOf(OnOffBaseServer, { onOff: false });
|
|
1530
|
+
if (this.restartMode === '')
|
|
1531
|
+
this.restartProcess();
|
|
1532
|
+
else
|
|
1533
|
+
this.shutdownProcess();
|
|
1534
|
+
}
|
|
1535
|
+
});
|
|
1536
|
+
await this.aggregatorNode?.add(virtualRestart);
|
|
1537
|
+
await virtualRestart.setStateOf(OnOffBaseServer, { onOff: false });
|
|
1538
|
+
const virtualUpdate = new Endpoint(OnOffPlugInUnitDevice.with(BridgedDeviceBasicInformationServer), { id: 'Update Matterbridge', bridgedDeviceBasicInformation: { nodeLabel: 'Update' } });
|
|
1539
|
+
virtualUpdate.events.onOff.onOff$Changed.on(async (value) => {
|
|
1540
|
+
if (value) {
|
|
1541
|
+
await virtualUpdate.setStateOf(OnOffBaseServer, { onOff: false });
|
|
1542
|
+
this.updateProcess();
|
|
1543
|
+
}
|
|
1544
|
+
});
|
|
1545
|
+
await this.aggregatorNode?.add(virtualUpdate);
|
|
1546
|
+
await virtualUpdate.setStateOf(OnOffBaseServer, { onOff: false });
|
|
1547
|
+
}
|
|
1522
1548
|
if (!serverNode.lifecycle.isCommissioned) {
|
|
1523
1549
|
this.log.notice(`Server node for ${storeId} is not commissioned. Pair to commission ...`);
|
|
1524
1550
|
const { qrPairingCode, manualPairingCode } = serverNode.state.commissioning.pairingCodes;
|
|
@@ -11,6 +11,7 @@ import { OperationalState } from '@matter/main/clusters/operational-state';
|
|
|
11
11
|
import { ModeBase } from '@matter/main/clusters/mode-base';
|
|
12
12
|
import { RvcRunMode } from '@matter/main/clusters/rvc-run-mode';
|
|
13
13
|
import { RvcOperationalState } from '@matter/main/clusters/rvc-operational-state';
|
|
14
|
+
import { ServiceArea } from '@matter/main/clusters/service-area';
|
|
14
15
|
import { IdentifyServer } from '@matter/main/behaviors/identify';
|
|
15
16
|
import { OnOffServer } from '@matter/main/behaviors/on-off';
|
|
16
17
|
import { LevelControlServer } from '@matter/main/behaviors/level-control';
|
|
@@ -28,7 +29,6 @@ import { RvcRunModeServer } from '@matter/main/behaviors/rvc-run-mode';
|
|
|
28
29
|
import { RvcCleanModeServer } from '@matter/main/behaviors/rvc-clean-mode';
|
|
29
30
|
import { RvcOperationalStateServer } from '@matter/main/behaviors/rvc-operational-state';
|
|
30
31
|
import { ServiceAreaServer } from '@matter/main/behaviors/service-area';
|
|
31
|
-
import { ServiceArea } from '@matter/main/clusters/service-area';
|
|
32
32
|
export class MatterbridgeServerDevice {
|
|
33
33
|
log;
|
|
34
34
|
commandHandler;
|
|
@@ -2,7 +2,7 @@ import { AnsiLogger, BLUE, CYAN, YELLOW, db, debugStringify, er, hk, or, zb } fr
|
|
|
2
2
|
import { bridgedNode } from './matterbridgeDeviceTypes.js';
|
|
3
3
|
import { isValidNumber, isValidObject, isValidString } from './utils/export.js';
|
|
4
4
|
import { MatterbridgeServer, MatterbridgeServerDevice, MatterbridgeIdentifyServer, MatterbridgeOnOffServer, MatterbridgeLevelControlServer, MatterbridgeColorControlServer, MatterbridgeWindowCoveringServer, MatterbridgeThermostatServer, MatterbridgeFanControlServer, MatterbridgeDoorLockServer, MatterbridgeModeSelectServer, MatterbridgeValveConfigurationAndControlServer, MatterbridgeSmokeCoAlarmServer, MatterbridgeBooleanStateConfigurationServer, MatterbridgeSwitchServer, MatterbridgeOperationalStateServer, } from './matterbridgeBehaviors.js';
|
|
5
|
-
import { addClusterServers, addFixedLabel, addOptionalClusterServers, addRequiredClusterServers, addUserLabel, capitalizeFirstLetter, createUniqueId, getBehavior, getBehaviourTypesFromClusterClientIds, getBehaviourTypesFromClusterServerIds, getDefaultOperationalStateClusterServer, getDefaultFlowMeasurementClusterServer, getDefaultIlluminanceMeasurementClusterServer, getDefaultPressureMeasurementClusterServer, getDefaultRelativeHumidityMeasurementClusterServer, getDefaultTemperatureMeasurementClusterServer, getDefaultOccupancySensingClusterServer, lowercaseFirstLetter, updateAttribute, getClusterId, getAttributeId, setAttribute, getAttribute, checkNotLatinCharacters, generateUniqueId, subscribeAttribute, } from './matterbridgeEndpointHelpers.js';
|
|
5
|
+
import { addClusterServers, addFixedLabel, addOptionalClusterServers, addRequiredClusterServers, addUserLabel, capitalizeFirstLetter, createUniqueId, getBehavior, getBehaviourTypesFromClusterClientIds, getBehaviourTypesFromClusterServerIds, getDefaultOperationalStateClusterServer, getDefaultFlowMeasurementClusterServer, getDefaultIlluminanceMeasurementClusterServer, getDefaultPressureMeasurementClusterServer, getDefaultRelativeHumidityMeasurementClusterServer, getDefaultTemperatureMeasurementClusterServer, getDefaultOccupancySensingClusterServer, lowercaseFirstLetter, updateAttribute, getClusterId, getAttributeId, setAttribute, getAttribute, checkNotLatinCharacters, generateUniqueId, subscribeAttribute, invokeBehaviorCommand, } from './matterbridgeEndpointHelpers.js';
|
|
6
6
|
import { Endpoint, Lifecycle, MutableEndpoint, NamedHandler, SupportedBehaviors, UINT16_MAX, UINT32_MAX, VendorId } from '@matter/main';
|
|
7
7
|
import { getClusterNameById, MeasurementType } from '@matter/main/types';
|
|
8
8
|
import { Descriptor } from '@matter/main/clusters/descriptor';
|
|
@@ -216,6 +216,9 @@ export class MatterbridgeEndpoint extends Endpoint {
|
|
|
216
216
|
async executeCommandHandler(command, request) {
|
|
217
217
|
await this.commandHandler.executeHandler(command, { request });
|
|
218
218
|
}
|
|
219
|
+
async invokeBehaviorCommand(cluster, command, params) {
|
|
220
|
+
await invokeBehaviorCommand(this, cluster, command, params);
|
|
221
|
+
}
|
|
219
222
|
addRequiredClusterServers() {
|
|
220
223
|
addRequiredClusterServers(this);
|
|
221
224
|
return this;
|
|
@@ -224,6 +224,21 @@ export function getBehavior(endpoint, cluster) {
|
|
|
224
224
|
}
|
|
225
225
|
return behavior;
|
|
226
226
|
}
|
|
227
|
+
export async function invokeBehaviorCommand(endpoint, cluster, command, params) {
|
|
228
|
+
const behaviorId = getBehavior(endpoint, cluster)?.id;
|
|
229
|
+
if (!behaviorId) {
|
|
230
|
+
endpoint.log.error(`invokeBehaviorCommand error: command ${hk}${command}${er} not found on endpoint ${or}${endpoint.maybeId}${er}:${or}${endpoint.maybeNumber}${er}`);
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
await endpoint.act((agent) => {
|
|
234
|
+
const behavior = agent[behaviorId];
|
|
235
|
+
if (!(command in behavior) || typeof behavior[command] !== 'function') {
|
|
236
|
+
endpoint.log.error(`invokeBehaviorCommand error: command ${hk}${command}${er} not found on agent for endpoint ${or}${endpoint.maybeId}${er}:${or}${endpoint.maybeNumber}${er}`);
|
|
237
|
+
return;
|
|
238
|
+
}
|
|
239
|
+
behavior[command](params);
|
|
240
|
+
});
|
|
241
|
+
}
|
|
227
242
|
export function addRequiredClusterServers(endpoint) {
|
|
228
243
|
const requiredServerList = [];
|
|
229
244
|
endpoint.log.debug(`addRequiredClusterServers for ${CYAN}${endpoint.maybeId}${db}`);
|
package/npm-shrinkwrap.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "matterbridge",
|
|
3
|
-
"version": "3.0.2-dev-
|
|
3
|
+
"version": "3.0.2-dev-20250510-ae61aa7",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "matterbridge",
|
|
9
|
-
"version": "3.0.2-dev-
|
|
9
|
+
"version": "3.0.2-dev-20250510-ae61aa7",
|
|
10
10
|
"license": "Apache-2.0",
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"@matter/main": "0.13.0",
|
package/package.json
CHANGED