matterbridge 2.1.6-dev.7 → 2.2.0-dev.1
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 +4 -2
- package/README-NGINX.md +76 -6
- package/dist/frontend.js +30 -2
- package/dist/matterbridge.js +33 -48
- package/dist/shelly.js +176 -0
- package/dist/update.js +50 -0
- package/frontend/build/Shelly.svg +1 -0
- package/frontend/build/asset-manifest.json +3 -3
- package/frontend/build/index.html +1 -1
- package/frontend/build/static/js/{main.82e3eb54.js → main.fed77626.js} +13 -13
- package/frontend/build/static/js/main.fed77626.js.map +1 -0
- package/npm-shrinkwrap.json +61 -61
- package/package.json +3 -3
- package/frontend/build/static/js/main.82e3eb54.js.map +0 -1
- /package/frontend/build/static/js/{main.82e3eb54.js.LICENSE.txt → main.fed77626.js.LICENSE.txt} +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -33,7 +33,7 @@ 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.
|
|
36
|
+
## [2.2.0] - 2025-02-25
|
|
37
37
|
|
|
38
38
|
### Added
|
|
39
39
|
|
|
@@ -41,7 +41,7 @@ matterbridge-hass v. 0.0.8
|
|
|
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
|
-
- [frontend]: Frontend v.2.4.
|
|
44
|
+
- [frontend]: Frontend v.2.4.3. Please refresh the frontend page after the update.
|
|
45
45
|
- [frontend]: Added processUptime.
|
|
46
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
47
|
- [frontend]: Added subscriptions to QRDiv.
|
|
@@ -53,6 +53,8 @@ matterbridge-hass v. 0.0.8
|
|
|
53
53
|
- [package]: Update matter.js to 0.12.4-alpha.0-20250213-1187f81eb
|
|
54
54
|
- [package]: Update matter.js to 0.12.4-alpha.0-20250215-5af08a8d6
|
|
55
55
|
- [package]: Update matter.js to 0.12.4-alpha.0-20250217-b0bba5179
|
|
56
|
+
- [package]: Update matter.js to 0.12.4-alpha.0-20250223-1e0341a1a
|
|
57
|
+
- [package]: Update matter.js to 0.12.4-alpha.0-20250224-46934b522
|
|
56
58
|
|
|
57
59
|
### Fixed
|
|
58
60
|
|
package/README-NGINX.md
CHANGED
|
@@ -16,18 +16,19 @@
|
|
|
16
16
|
|
|
17
17
|
## Run matterbridge with nginx
|
|
18
18
|
|
|
19
|
-
### Create
|
|
19
|
+
### Create a basic nginx configuration file
|
|
20
20
|
|
|
21
21
|
```
|
|
22
22
|
sudo nano /etc/nginx/sites-available/matterbridge
|
|
23
23
|
```
|
|
24
24
|
|
|
25
|
-
paste this configuration and change the port to listen and the server_name using yours:
|
|
25
|
+
paste this configuration and if desired change the port to listen (here is 80) and the server_name using yours:
|
|
26
26
|
|
|
27
27
|
```
|
|
28
28
|
server {
|
|
29
|
-
listen
|
|
30
|
-
|
|
29
|
+
listen 80 default_server;
|
|
30
|
+
listen [::]:80 default_server;
|
|
31
|
+
server_name _;
|
|
31
32
|
|
|
32
33
|
location /matterbridge/ {
|
|
33
34
|
# Redirect to Matterbridge frontend
|
|
@@ -45,7 +46,72 @@ server {
|
|
|
45
46
|
}
|
|
46
47
|
```
|
|
47
48
|
|
|
48
|
-
|
|
49
|
+
Add matterbridge to enabled sites
|
|
50
|
+
|
|
51
|
+
```
|
|
52
|
+
sudo ln -s /etc/nginx/sites-available/matterbridge /etc/nginx/sites-enabled/
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### Create an advanced nginx configuration file that redirect to ssl
|
|
56
|
+
|
|
57
|
+
```
|
|
58
|
+
sudo nano /etc/nginx/sites-available/matterbridge
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
paste this configuration adding your certificates:
|
|
62
|
+
|
|
63
|
+
```
|
|
64
|
+
# Redirect all HTTP requests to HTTPS
|
|
65
|
+
server {
|
|
66
|
+
listen 80 default_server;
|
|
67
|
+
listen [::]:80 default_server;
|
|
68
|
+
server_name _;
|
|
69
|
+
|
|
70
|
+
return 301 https://$host$request_uri;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
# HTTPS server configuration
|
|
74
|
+
server {
|
|
75
|
+
listen 443 ssl default_server;
|
|
76
|
+
listen [::]:443 ssl default_server;
|
|
77
|
+
http2 on;
|
|
78
|
+
server_name _;
|
|
79
|
+
|
|
80
|
+
# SSL certificate paths
|
|
81
|
+
ssl_certificate /etc/nginx/certs/cert.pem;
|
|
82
|
+
ssl_certificate_key /etc/nginx/certs/key.pem;
|
|
83
|
+
|
|
84
|
+
# SSL security settings
|
|
85
|
+
ssl_protocols TLSv1.2 TLSv1.3;
|
|
86
|
+
ssl_ciphers HIGH:!aNULL:!MD5;
|
|
87
|
+
ssl_prefer_server_ciphers on;
|
|
88
|
+
|
|
89
|
+
root /var/www/html;
|
|
90
|
+
index index.html index.htm index.nginx-debian.html;
|
|
91
|
+
|
|
92
|
+
location / {
|
|
93
|
+
# First attempt to serve request as file, then
|
|
94
|
+
# as directory, then fall back to displaying a 404.
|
|
95
|
+
try_files $uri $uri/ =404;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
location /matterbridge/ {
|
|
99
|
+
# Redirect to Matterbridge frontend
|
|
100
|
+
proxy_pass http://localhost:8283/;
|
|
101
|
+
proxy_set_header Host $host;
|
|
102
|
+
proxy_set_header X-Real-IP $remote_addr;
|
|
103
|
+
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
104
|
+
proxy_set_header X-Forwarded-Proto $scheme;
|
|
105
|
+
|
|
106
|
+
# WebSocket support
|
|
107
|
+
proxy_http_version 1.1;
|
|
108
|
+
proxy_set_header Upgrade $http_upgrade;
|
|
109
|
+
proxy_set_header Connection "upgrade";
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
Add matterbridge to enabled sites
|
|
49
115
|
|
|
50
116
|
```
|
|
51
117
|
sudo ln -s /etc/nginx/sites-available/matterbridge /etc/nginx/sites-enabled/
|
|
@@ -60,4 +126,8 @@ sudo nginx -t
|
|
|
60
126
|
|
|
61
127
|
### Use matterbridge with nginx
|
|
62
128
|
|
|
63
|
-
http://ubuntu
|
|
129
|
+
http://ubuntu/matterbridge/
|
|
130
|
+
|
|
131
|
+
or
|
|
132
|
+
|
|
133
|
+
https://ubuntu/matterbridge/
|
package/dist/frontend.js
CHANGED
|
@@ -17,6 +17,8 @@ export const WS_ID_CPU_UPDATE = 3;
|
|
|
17
17
|
export const WS_ID_MEMORY_UPDATE = 4;
|
|
18
18
|
export const WS_ID_UPTIME_UPDATE = 5;
|
|
19
19
|
export const WS_ID_SNACKBAR = 6;
|
|
20
|
+
export const WS_ID_SHELLY_SYS_UPDATE = 100;
|
|
21
|
+
export const WS_ID_SHELLY_MAIN_UPDATE = 101;
|
|
20
22
|
export class Frontend {
|
|
21
23
|
matterbridge;
|
|
22
24
|
log;
|
|
@@ -656,8 +658,9 @@ export class Frontend {
|
|
|
656
658
|
if (!plugin)
|
|
657
659
|
return;
|
|
658
660
|
this.matterbridge.plugins.saveConfigFromJson(plugin, req.body);
|
|
661
|
+
this.wssSendSnackbarMessage(`Saved config for plugin ${param}`);
|
|
662
|
+
this.wssSendRestartRequired();
|
|
659
663
|
}
|
|
660
|
-
this.wssSendRestartRequired();
|
|
661
664
|
res.json({ message: 'Command received' });
|
|
662
665
|
return;
|
|
663
666
|
}
|
|
@@ -1075,6 +1078,22 @@ export class Frontend {
|
|
|
1075
1078
|
});
|
|
1076
1079
|
return;
|
|
1077
1080
|
}
|
|
1081
|
+
else if (data.method === '/api/shellysysupdate') {
|
|
1082
|
+
const { triggerShellySysUpdate } = await import('./shelly.js');
|
|
1083
|
+
triggerShellySysUpdate(this.matterbridge);
|
|
1084
|
+
return;
|
|
1085
|
+
}
|
|
1086
|
+
else if (data.method === '/api/shellymainupdate') {
|
|
1087
|
+
const { triggerShellyMainUpdate } = await import('./shelly.js');
|
|
1088
|
+
triggerShellyMainUpdate(this.matterbridge);
|
|
1089
|
+
return;
|
|
1090
|
+
}
|
|
1091
|
+
else if (data.method === '/api/shellynetconfig') {
|
|
1092
|
+
this.log.debug('/api/shellynetconfig:', data.params);
|
|
1093
|
+
const { triggerShellyChangeIp: triggerShellyChangeNet } = await import('./shelly.js');
|
|
1094
|
+
triggerShellyChangeNet(this.matterbridge, data.params);
|
|
1095
|
+
return;
|
|
1096
|
+
}
|
|
1078
1097
|
else if (data.method === '/api/restart') {
|
|
1079
1098
|
this.wssSendSnackbarMessage(`Restarting matterbridge...`, 0);
|
|
1080
1099
|
await this.matterbridge.restartProcess();
|
|
@@ -1242,7 +1261,7 @@ export class Frontend {
|
|
|
1242
1261
|
client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, plugin: data.params.plugin, deviceName, serialNumber, endpoint: data.params.endpoint, deviceTypes, response: clusters }));
|
|
1243
1262
|
return;
|
|
1244
1263
|
}
|
|
1245
|
-
else if (data.method === '/api/select') {
|
|
1264
|
+
else if (data.method === '/api/select' || data.method === '/api/select/devices') {
|
|
1246
1265
|
if (!isValidString(data.params.plugin, 10)) {
|
|
1247
1266
|
client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Wrong parameter plugin in /api/select' }));
|
|
1248
1267
|
return;
|
|
@@ -1319,6 +1338,7 @@ export class Frontend {
|
|
|
1319
1338
|
wssSendRestartRequired() {
|
|
1320
1339
|
this.log.debug('Sending a restart required message to all connected clients');
|
|
1321
1340
|
this.matterbridge.matterbridgeInformation.restartRequired = true;
|
|
1341
|
+
this.wssSendSnackbarMessage(`Restart required`, 0);
|
|
1322
1342
|
this.webSocketServer?.clients.forEach((client) => {
|
|
1323
1343
|
if (client.readyState === WebSocket.OPEN) {
|
|
1324
1344
|
client.send(JSON.stringify({ id: WS_ID_RESTART_NEEDED, src: 'Matterbridge', dst: 'Frontend', method: 'restart_required', params: {} }));
|
|
@@ -1357,4 +1377,12 @@ export class Frontend {
|
|
|
1357
1377
|
}
|
|
1358
1378
|
});
|
|
1359
1379
|
}
|
|
1380
|
+
wssBroadcastMessage(id, method, params) {
|
|
1381
|
+
this.log.debug(`Sending a broadcast message id ${id} method ${method} params ${debugStringify(params ?? {})} to all connected clients`);
|
|
1382
|
+
this.webSocketServer?.clients.forEach((client) => {
|
|
1383
|
+
if (client.readyState === WebSocket.OPEN) {
|
|
1384
|
+
client.send(JSON.stringify({ id, src: 'Matterbridge', dst: 'Frontend', method, params }));
|
|
1385
|
+
}
|
|
1386
|
+
});
|
|
1387
|
+
}
|
|
1360
1388
|
}
|
package/dist/matterbridge.js
CHANGED
|
@@ -2,10 +2,10 @@ import os from 'node:os';
|
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import { promises as fs } from 'node:fs';
|
|
4
4
|
import EventEmitter from 'node:events';
|
|
5
|
-
import { AnsiLogger, UNDERLINE, UNDERLINEOFF, YELLOW, db, debugStringify, BRIGHT, RESET, er, nf, rs, wr, RED, GREEN, zb, CYAN
|
|
5
|
+
import { AnsiLogger, UNDERLINE, UNDERLINEOFF, YELLOW, db, debugStringify, BRIGHT, RESET, er, nf, rs, wr, RED, GREEN, zb, CYAN } from './logger/export.js';
|
|
6
6
|
import { NodeStorageManager } from './storage/export.js';
|
|
7
7
|
import { getParameter, getIntParameter, hasParameter, copyDirectory, withTimeout } from './utils/export.js';
|
|
8
|
-
import { logInterfaces,
|
|
8
|
+
import { logInterfaces, 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';
|
|
@@ -14,6 +14,9 @@ import { Frontend } from './frontend.js';
|
|
|
14
14
|
import { DeviceTypeId, Endpoint as EndpointNode, Logger, LogLevel as MatterLogLevel, LogFormat as MatterLogFormat, VendorId, StorageService, Environment, ServerNode } from '@matter/main';
|
|
15
15
|
import { DeviceCommissioner, FabricAction, MdnsService, PaseClient } from '@matter/main/protocol';
|
|
16
16
|
import { AggregatorEndpoint } from '@matter/main/endpoints';
|
|
17
|
+
import { BasicInformationServer } from '@matter/main/behaviors/basic-information';
|
|
18
|
+
import { BridgedDeviceBasicInformation } from '@matter/main/clusters/bridged-device-basic-information';
|
|
19
|
+
import { BasicInformation } from '@matter/main/clusters/basic-information';
|
|
17
20
|
const plg = '\u001B[38;5;33m';
|
|
18
21
|
const dev = '\u001B[38;5;79m';
|
|
19
22
|
const typ = '\u001B[38;5;207m';
|
|
@@ -55,7 +58,10 @@ export class Matterbridge extends EventEmitter {
|
|
|
55
58
|
matterbridgeAdvertise: false,
|
|
56
59
|
bridgeMode: '',
|
|
57
60
|
restartMode: '',
|
|
58
|
-
readOnly: hasParameter('readonly'),
|
|
61
|
+
readOnly: hasParameter('readonly') || hasParameter('shelly'),
|
|
62
|
+
shellyBoard: hasParameter('shelly'),
|
|
63
|
+
shellySysUpdate: false,
|
|
64
|
+
shellyMainUpdate: false,
|
|
59
65
|
profile: getParameter('profile'),
|
|
60
66
|
loggerLevel: "info",
|
|
61
67
|
fileLogger: false,
|
|
@@ -101,6 +107,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
101
107
|
execRunningCount = 0;
|
|
102
108
|
startMatterInterval;
|
|
103
109
|
checkUpdateInterval;
|
|
110
|
+
checkUpdateTimeout;
|
|
104
111
|
configureTimeout;
|
|
105
112
|
reachabilityTimeout;
|
|
106
113
|
sigintHandler;
|
|
@@ -564,17 +571,14 @@ export class Matterbridge extends EventEmitter {
|
|
|
564
571
|
}
|
|
565
572
|
if (getIntParameter('frontend') !== 0 || getIntParameter('frontend') === undefined)
|
|
566
573
|
await this.frontend.start(getIntParameter('frontend'));
|
|
567
|
-
this.
|
|
568
|
-
|
|
569
|
-
this
|
|
570
|
-
}
|
|
571
|
-
this.checkUpdateInterval = setInterval(() => {
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
}
|
|
576
|
-
this.frontend.wssSendRefreshRequired();
|
|
577
|
-
}, 60 * 60 * 1000);
|
|
574
|
+
this.checkUpdateTimeout = setTimeout(async () => {
|
|
575
|
+
const { checkUpdates } = await import('./update.js');
|
|
576
|
+
checkUpdates(this);
|
|
577
|
+
}, 30 * 1000).unref();
|
|
578
|
+
this.checkUpdateInterval = setInterval(async () => {
|
|
579
|
+
const { checkUpdates } = await import('./update.js');
|
|
580
|
+
checkUpdates(this);
|
|
581
|
+
}, 24 * 60 * 60 * 1000).unref();
|
|
578
582
|
if (hasParameter('test')) {
|
|
579
583
|
this.bridgeMode = 'bridge';
|
|
580
584
|
MatterbridgeEndpoint.bridgeMode = 'bridge';
|
|
@@ -811,37 +815,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
811
815
|
const cmdArgs = process.argv.slice(2).join(' ');
|
|
812
816
|
this.log.debug(`Command Line Arguments: ${cmdArgs}`);
|
|
813
817
|
}
|
|
814
|
-
async getMatterbridgeLatestVersion() {
|
|
815
|
-
getNpmPackageVersion('matterbridge')
|
|
816
|
-
.then(async (version) => {
|
|
817
|
-
this.matterbridgeLatestVersion = version;
|
|
818
|
-
this.matterbridgeInformation.matterbridgeLatestVersion = version;
|
|
819
|
-
await this.nodeContext?.set('matterbridgeLatestVersion', this.matterbridgeLatestVersion);
|
|
820
|
-
if (this.matterbridgeVersion !== this.matterbridgeLatestVersion) {
|
|
821
|
-
this.log.notice(`Matterbridge is out of date. Current version: ${this.matterbridgeVersion}. Latest version: ${this.matterbridgeLatestVersion}.`);
|
|
822
|
-
}
|
|
823
|
-
else {
|
|
824
|
-
this.log.debug(`Matterbridge is up to date. Current version: ${this.matterbridgeVersion}. Latest version: ${this.matterbridgeLatestVersion}.`);
|
|
825
|
-
}
|
|
826
|
-
this.frontend.wssSendRefreshRequired();
|
|
827
|
-
})
|
|
828
|
-
.catch((error) => {
|
|
829
|
-
this.log.warn(`Error getting Matterbridge latest version: ${error.message}`);
|
|
830
|
-
});
|
|
831
|
-
}
|
|
832
|
-
async getPluginLatestVersion(plugin) {
|
|
833
|
-
getNpmPackageVersion(plugin.name)
|
|
834
|
-
.then((version) => {
|
|
835
|
-
plugin.latestVersion = version;
|
|
836
|
-
if (plugin.version !== plugin.latestVersion)
|
|
837
|
-
this.log.notice(`The plugin ${plg}${plugin.name}${nt} is out of date. Current version: ${plugin.version}. Latest version: ${plugin.latestVersion}.`);
|
|
838
|
-
else
|
|
839
|
-
this.log.debug(`The plugin ${plg}${plugin.name}${db} is up to date. Current version: ${plugin.version}. Latest version: ${plugin.latestVersion}.`);
|
|
840
|
-
})
|
|
841
|
-
.catch((error) => {
|
|
842
|
-
this.log.warn(`Error getting ${plg}${plugin.name}${er} latest version: ${error.message}`);
|
|
843
|
-
});
|
|
844
|
-
}
|
|
845
818
|
createMatterLogger() {
|
|
846
819
|
const matterLogger = new AnsiLogger({ logName: 'Matter', logTimestampFormat: 4, logLevel: "debug" });
|
|
847
820
|
return (_level, formattedLog) => {
|
|
@@ -965,6 +938,11 @@ export class Matterbridge extends EventEmitter {
|
|
|
965
938
|
this.startMatterInterval = undefined;
|
|
966
939
|
this.log.debug('Start matter interval cleared');
|
|
967
940
|
}
|
|
941
|
+
if (this.checkUpdateTimeout) {
|
|
942
|
+
clearInterval(this.checkUpdateTimeout);
|
|
943
|
+
this.checkUpdateTimeout = undefined;
|
|
944
|
+
this.log.debug('Check update timeout cleared');
|
|
945
|
+
}
|
|
968
946
|
if (this.checkUpdateInterval) {
|
|
969
947
|
clearInterval(this.checkUpdateInterval);
|
|
970
948
|
this.checkUpdateInterval = undefined;
|
|
@@ -1672,11 +1650,18 @@ export class Matterbridge extends EventEmitter {
|
|
|
1672
1650
|
};
|
|
1673
1651
|
});
|
|
1674
1652
|
}
|
|
1675
|
-
setServerNodeReachability(serverNode, reachable) {
|
|
1653
|
+
async setServerNodeReachability(serverNode, reachable) {
|
|
1654
|
+
await serverNode.setStateOf(BasicInformationServer, { reachable });
|
|
1676
1655
|
}
|
|
1677
|
-
setAggregatorReachability(aggregatorNode, reachable) {
|
|
1656
|
+
async setAggregatorReachability(aggregatorNode, reachable) {
|
|
1657
|
+
for (const child of aggregatorNode.parts) {
|
|
1658
|
+
await child.setAttribute(BridgedDeviceBasicInformation.Cluster.id, 'reachable', reachable);
|
|
1659
|
+
await child.triggerEvent(BridgedDeviceBasicInformation.Cluster.id, 'reachableChanged', { reachableNewValue: reachable });
|
|
1660
|
+
}
|
|
1678
1661
|
}
|
|
1679
|
-
setDeviceReachability(device, reachable) {
|
|
1662
|
+
async setDeviceReachability(device, reachable) {
|
|
1663
|
+
await device.setAttribute(BasicInformation.Cluster.id, 'reachable', reachable);
|
|
1664
|
+
await device.triggerEvent(BasicInformation.Cluster.id, 'reachableChanged', { reachableNewValue: reachable });
|
|
1680
1665
|
}
|
|
1681
1666
|
getVendorIdName = (vendorId) => {
|
|
1682
1667
|
if (!vendorId)
|
package/dist/shelly.js
ADDED
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
import { WS_ID_SHELLY_MAIN_UPDATE, WS_ID_SHELLY_SYS_UPDATE } from './frontend.js';
|
|
2
|
+
import { debugStringify } from './logger/export.js';
|
|
3
|
+
export async function getShellySysUpdate(matterbridge) {
|
|
4
|
+
getShelly('/api/updates/sys/check', 60 * 1000)
|
|
5
|
+
.then(async (data) => {
|
|
6
|
+
if (data.length > 0) {
|
|
7
|
+
matterbridge.matterbridgeInformation.shellySysUpdate = true;
|
|
8
|
+
matterbridge.log.notice(`Shelly system update available: ${debugStringify(data)}`);
|
|
9
|
+
matterbridge.frontend.wssSendSnackbarMessage('Shelly system update available', 60);
|
|
10
|
+
matterbridge.frontend.wssBroadcastMessage(WS_ID_SHELLY_SYS_UPDATE, 'shelly-sys-update', { available: true });
|
|
11
|
+
for (const update of data) {
|
|
12
|
+
if (update.name)
|
|
13
|
+
matterbridge.frontend.wssSendSnackbarMessage('Shelly system update available: ' + update.name, 10);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
})
|
|
17
|
+
.catch((error) => {
|
|
18
|
+
matterbridge.log.warn(`Error getting Shelly system updates: ${error instanceof Error ? error.message : error}`);
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
export async function triggerShellySysUpdate(matterbridge) {
|
|
22
|
+
getShelly('/api/updates/sys/perform', 10 * 1000)
|
|
23
|
+
.then(async () => {
|
|
24
|
+
matterbridge.log.debug(`Triggered Shelly system updates`);
|
|
25
|
+
})
|
|
26
|
+
.catch((error) => {
|
|
27
|
+
matterbridge.log.debug(`****Error triggering Shelly system updates: ${error instanceof Error ? error.message : error}`);
|
|
28
|
+
})
|
|
29
|
+
.finally(() => {
|
|
30
|
+
matterbridge.matterbridgeInformation.shellySysUpdate = false;
|
|
31
|
+
matterbridge.log.notice(`Installing Shelly system update...`);
|
|
32
|
+
matterbridge.frontend.wssSendSnackbarMessage('Installing Shelly system update...', 60);
|
|
33
|
+
matterbridge.frontend.wssBroadcastMessage(WS_ID_SHELLY_SYS_UPDATE, 'shelly-sys-update', { available: false });
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
export async function getShellyMainUpdate(matterbridge) {
|
|
37
|
+
getShelly('/api/updates/main/check', 60 * 1000)
|
|
38
|
+
.then(async (data) => {
|
|
39
|
+
if (data.length > 0) {
|
|
40
|
+
matterbridge.matterbridgeInformation.shellyMainUpdate = true;
|
|
41
|
+
matterbridge.log.notice(`Shelly software update available: ${debugStringify(data)}`);
|
|
42
|
+
matterbridge.frontend.wssSendSnackbarMessage('Shelly software update available', 60);
|
|
43
|
+
matterbridge.frontend.wssBroadcastMessage(WS_ID_SHELLY_MAIN_UPDATE, 'shelly-main-update', { available: true });
|
|
44
|
+
for (const update of data) {
|
|
45
|
+
if (update.name)
|
|
46
|
+
matterbridge.frontend.wssSendSnackbarMessage('Shelly software update available: ' + update.name, 10);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
})
|
|
50
|
+
.catch((error) => {
|
|
51
|
+
matterbridge.log.warn(`Error getting Shelly main updates: ${error instanceof Error ? error.message : error}`);
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
export async function triggerShellyMainUpdate(matterbridge) {
|
|
55
|
+
getShelly('/api/updates/main/perform', 10 * 1000)
|
|
56
|
+
.then(async () => {
|
|
57
|
+
matterbridge.log.debug(`Triggered Shelly main updates`);
|
|
58
|
+
})
|
|
59
|
+
.catch((error) => {
|
|
60
|
+
matterbridge.log.debug(`****Error triggering Shelly main updates: ${error instanceof Error ? error.message : error}`);
|
|
61
|
+
})
|
|
62
|
+
.finally(() => {
|
|
63
|
+
matterbridge.matterbridgeInformation.shellyMainUpdate = false;
|
|
64
|
+
matterbridge.log.notice(`Installing Shelly software update...`);
|
|
65
|
+
matterbridge.frontend.wssSendSnackbarMessage('Installing Shelly software update...', 60);
|
|
66
|
+
matterbridge.frontend.wssBroadcastMessage(WS_ID_SHELLY_MAIN_UPDATE, 'shelly-main-update', { available: false });
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
export async function triggerShellyChangeIp(matterbridge, config) {
|
|
70
|
+
const api = config.type === 'static' ? '/api/network/connection/static' : '/api/network/connection/dynamic';
|
|
71
|
+
const data = { interface: 'end0' };
|
|
72
|
+
if (config.type === 'static') {
|
|
73
|
+
data['addr'] = config.ip;
|
|
74
|
+
data['mask'] = config.subnet;
|
|
75
|
+
data['gw'] = config.gateway;
|
|
76
|
+
data['dns'] = config.dns;
|
|
77
|
+
}
|
|
78
|
+
matterbridge.log.debug(`Triggering Shelly network configuration change: ${debugStringify(config)}`);
|
|
79
|
+
postShelly(api, data, 60 * 1000)
|
|
80
|
+
.then(async () => {
|
|
81
|
+
matterbridge.log.debug(`Triggered Shelly network configuration change: ${debugStringify(config)}`);
|
|
82
|
+
})
|
|
83
|
+
.catch((error) => {
|
|
84
|
+
matterbridge.log.debug(`****Error triggering Shelly network configuration change: ${error instanceof Error ? error.message : error}`);
|
|
85
|
+
})
|
|
86
|
+
.finally(() => {
|
|
87
|
+
matterbridge.log.notice(`Changed Shelly network configuration`);
|
|
88
|
+
matterbridge.frontend.wssSendSnackbarMessage('Changed Shelly network configuration');
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
async function getShelly(api, timeout = 60000) {
|
|
92
|
+
const http = await import('node:http');
|
|
93
|
+
return new Promise((resolve, reject) => {
|
|
94
|
+
const url = `http://127.0.0.1:8101${api}`;
|
|
95
|
+
const controller = new AbortController();
|
|
96
|
+
const timeoutId = setTimeout(() => {
|
|
97
|
+
controller.abort();
|
|
98
|
+
reject(new Error(`Request timed out after ${timeout / 1000} seconds`));
|
|
99
|
+
}, timeout).unref();
|
|
100
|
+
const req = http.get(url, { signal: controller.signal }, (res) => {
|
|
101
|
+
let data = '';
|
|
102
|
+
if (res.statusCode !== 200) {
|
|
103
|
+
clearTimeout(timeoutId);
|
|
104
|
+
res.resume();
|
|
105
|
+
req.destroy();
|
|
106
|
+
reject(new Error(`Failed to fetch data. Status code: ${res.statusCode}`));
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
res.on('data', (chunk) => {
|
|
110
|
+
data += chunk;
|
|
111
|
+
});
|
|
112
|
+
res.on('end', () => {
|
|
113
|
+
clearTimeout(timeoutId);
|
|
114
|
+
try {
|
|
115
|
+
const jsonData = JSON.parse(data);
|
|
116
|
+
resolve(jsonData);
|
|
117
|
+
}
|
|
118
|
+
catch (error) {
|
|
119
|
+
reject(new Error(`Failed to parse response JSON: ${error instanceof Error ? error.message : error}`));
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
});
|
|
123
|
+
req.on('error', (error) => {
|
|
124
|
+
clearTimeout(timeoutId);
|
|
125
|
+
reject(new Error(`Request failed: ${error instanceof Error ? error.message : error}`));
|
|
126
|
+
});
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
async function postShelly(api, data, timeout = 60000) {
|
|
130
|
+
const http = await import('node:http');
|
|
131
|
+
return new Promise((resolve, reject) => {
|
|
132
|
+
const url = `http://127.0.0.1:8101${api}`;
|
|
133
|
+
const controller = new AbortController();
|
|
134
|
+
const timeoutId = setTimeout(() => {
|
|
135
|
+
controller.abort();
|
|
136
|
+
reject(new Error(`Request timed out after ${timeout / 1000} seconds`));
|
|
137
|
+
}, timeout).unref();
|
|
138
|
+
const jsonData = JSON.stringify(data);
|
|
139
|
+
const options = {
|
|
140
|
+
method: 'POST',
|
|
141
|
+
headers: {
|
|
142
|
+
'Content-Type': 'application/json',
|
|
143
|
+
'Content-Length': Buffer.byteLength(jsonData),
|
|
144
|
+
},
|
|
145
|
+
signal: controller.signal,
|
|
146
|
+
};
|
|
147
|
+
const req = http.request(url, options, (res) => {
|
|
148
|
+
let responseData = '';
|
|
149
|
+
if (res.statusCode && res.statusCode >= 300) {
|
|
150
|
+
clearTimeout(timeoutId);
|
|
151
|
+
res.resume();
|
|
152
|
+
req.destroy();
|
|
153
|
+
return reject(new Error(`Failed to post data. Status code: ${res.statusCode}`));
|
|
154
|
+
}
|
|
155
|
+
res.on('data', (chunk) => {
|
|
156
|
+
responseData += chunk;
|
|
157
|
+
});
|
|
158
|
+
res.on('end', () => {
|
|
159
|
+
clearTimeout(timeoutId);
|
|
160
|
+
try {
|
|
161
|
+
const jsonResponse = JSON.parse(responseData);
|
|
162
|
+
resolve(jsonResponse);
|
|
163
|
+
}
|
|
164
|
+
catch (err) {
|
|
165
|
+
reject(new Error(`Failed to parse response JSON: ${err instanceof Error ? err.message : err}`));
|
|
166
|
+
}
|
|
167
|
+
});
|
|
168
|
+
});
|
|
169
|
+
req.on('error', (error) => {
|
|
170
|
+
clearTimeout(timeoutId);
|
|
171
|
+
reject(new Error(`Request failed: ${error instanceof Error ? error.message : error}`));
|
|
172
|
+
});
|
|
173
|
+
req.write(jsonData);
|
|
174
|
+
req.end();
|
|
175
|
+
});
|
|
176
|
+
}
|
package/dist/update.js
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { plg } from './matterbridgeTypes.js';
|
|
2
|
+
import { db, er, nt } from './logger/export.js';
|
|
3
|
+
export async function checkUpdates(matterbridge) {
|
|
4
|
+
const { hasParameter } = await import('./utils/parameter.js');
|
|
5
|
+
getMatterbridgeLatestVersion(matterbridge);
|
|
6
|
+
for (const plugin of matterbridge.plugins) {
|
|
7
|
+
getPluginLatestVersion(matterbridge, plugin);
|
|
8
|
+
}
|
|
9
|
+
if (hasParameter('shelly')) {
|
|
10
|
+
const { getShellySysUpdate, getShellyMainUpdate } = await import('./shelly.js');
|
|
11
|
+
getShellySysUpdate(matterbridge);
|
|
12
|
+
getShellyMainUpdate(matterbridge);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
async function getMatterbridgeLatestVersion(matterbridge) {
|
|
16
|
+
const { getNpmPackageVersion } = await import('./utils/network.js');
|
|
17
|
+
getNpmPackageVersion('matterbridge')
|
|
18
|
+
.then(async (version) => {
|
|
19
|
+
matterbridge.matterbridgeLatestVersion = version;
|
|
20
|
+
matterbridge.matterbridgeInformation.matterbridgeLatestVersion = version;
|
|
21
|
+
await matterbridge.nodeContext?.set('matterbridgeLatestVersion', matterbridge.matterbridgeLatestVersion);
|
|
22
|
+
if (matterbridge.matterbridgeVersion !== matterbridge.matterbridgeLatestVersion) {
|
|
23
|
+
matterbridge.log.notice(`Matterbridge is out of date. Current version: ${matterbridge.matterbridgeVersion}. Latest version: ${matterbridge.matterbridgeLatestVersion}.`);
|
|
24
|
+
matterbridge.frontend.wssSendRefreshRequired();
|
|
25
|
+
}
|
|
26
|
+
else {
|
|
27
|
+
matterbridge.log.debug(`Matterbridge is up to date. Current version: ${matterbridge.matterbridgeVersion}. Latest version: ${matterbridge.matterbridgeLatestVersion}.`);
|
|
28
|
+
}
|
|
29
|
+
})
|
|
30
|
+
.catch((error) => {
|
|
31
|
+
matterbridge.log.warn(`Error getting Matterbridge latest version: ${error.message}`);
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
async function getPluginLatestVersion(matterbridge, plugin) {
|
|
35
|
+
const { getNpmPackageVersion } = await import('./utils/network.js');
|
|
36
|
+
getNpmPackageVersion(plugin.name)
|
|
37
|
+
.then((version) => {
|
|
38
|
+
plugin.latestVersion = version;
|
|
39
|
+
if (plugin.version !== plugin.latestVersion) {
|
|
40
|
+
matterbridge.log.notice(`The plugin ${plg}${plugin.name}${nt} is out of date. Current version: ${plugin.version}. Latest version: ${plugin.latestVersion}.`);
|
|
41
|
+
matterbridge.frontend.wssSendRefreshRequired();
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
matterbridge.log.debug(`The plugin ${plg}${plugin.name}${db} is up to date. Current version: ${plugin.version}. Latest version: ${plugin.latestVersion}.`);
|
|
45
|
+
}
|
|
46
|
+
})
|
|
47
|
+
.catch((error) => {
|
|
48
|
+
matterbridge.log.warn(`Error getting ${plg}${plugin.name}${er} latest version: ${error.message}`);
|
|
49
|
+
});
|
|
50
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" fill="none" width="1024" viewBox="0.7 0 86.6 24"><path fill="#4594D1" d="m82 15.5 2.4-6.6h-4.6l-2.3 6.6h-2.6l2.4-6.6h-4.7L70.4 15v.5h-3.7l4.8-13.3H67l-4.7 13.3h-4L63 2.2h-4.5l-4.7 13.3h-7.1c-1.1 0-1.7-.3-1.7-1l.1-.4 1-3H49l-1.7 4.5s3.3-1.3 4.4-1.8c1-.5 1.3-1.4 1.3-2.3 0-.8-.6-1.5-1.7-2-.8-.3-1.7-.4-2.8-.4h-1.8c-1.7 0-3 .4-4.3 1.1-1.3 1-2 2.1-2 3.7 0 .6.1 1.2.4 1.7h-4.3l1.4-4c.2-.5.4-1.1.4-1.5 0-.8-.8-1-1.7-1-1.2 0-2.3.4-3.3 1.2l-3.4 2.8 3.8-10.6H29l-5.4 15h2.6c.6 0 1.3-.2 1.8-.7L33 13l-1 2.7-.1 1c0 .6.5 1 1.4 1h27.3a4 4 0 0 0 1.9-.6c.3.4.8.5 1.6.5h5a4 4 0 0 0 1.9-.5c.3.4.8.5 1.4.5h1c-.6.4-1 .9-1.2 1.5a4.7 4.7 0 0 0-.5 2c0 .8.3 1.5.8 2 .6.7 1.4 1 2.4 1 1.7 0 3.1-.7 4.3-2 .8-1 1.6-2.5 2.3-4.5h3.1c1.4 0 2.3-.6 2.8-2h-5.2ZM75.2 22c-.5-.4-.7-.8-.7-1.4 0-.6.2-1.2.7-1.9.5-.6 1-1 1.6-1L75 22.2ZM22 5l1.5-2.9h-2.8c-1.2 0-1.9.5-1.9 1.6 0 .5.4 1.8 1.1 3.6A13 13 0 0 1 21 12c0 2.2-1.3 4-3.8 5a16 16 0 0 1-6.6 1.3 18 18 0 0 1-6.2-1C2 16.5.7 15 .7 13.3.7 10.3 3 9 7.7 9h6c-.2.6-.6 1.1-1.2 1.5-.6.3-1.2.5-2 .5h-1c-.9 0-1.7.1-2.4.5-.9.4-1.3 1-1.3 1.8 0 1 .6 1.7 1.8 2.3 1 .4 2 .6 3.1.6 1.4 0 2.6-.3 3.5-1 1-.6 1.6-1.6 1.6-3 0-.9-.4-2.1-1-3.8-.7-1.7-1-3-1-4C13.7 1.7 16.1.2 21 0h2c1.2 0 2.2.2 3.1.5 1.2.5 1.8 1.2 1.9 2.2 0 1-.5 1.8-1.5 2.5H22Z"/></svg>
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"files": {
|
|
3
3
|
"main.css": "./static/css/main.cf25d33e.css",
|
|
4
|
-
"main.js": "./static/js/main.
|
|
4
|
+
"main.js": "./static/js/main.fed77626.js",
|
|
5
5
|
"static/js/453.abd36b29.chunk.js": "./static/js/453.abd36b29.chunk.js",
|
|
6
6
|
"static/media/roboto-latin-700-normal.woff2": "./static/media/roboto-latin-700-normal.4535474e1cf8598695ad.woff2",
|
|
7
7
|
"static/media/roboto-latin-500-normal.woff2": "./static/media/roboto-latin-500-normal.7077203b1982951ecf76.woff2",
|
|
@@ -61,11 +61,11 @@
|
|
|
61
61
|
"static/media/roboto-greek-ext-400-normal.woff": "./static/media/roboto-greek-ext-400-normal.16eb83b4a3b1ea994243.woff",
|
|
62
62
|
"index.html": "./index.html",
|
|
63
63
|
"main.cf25d33e.css.map": "./static/css/main.cf25d33e.css.map",
|
|
64
|
-
"main.
|
|
64
|
+
"main.fed77626.js.map": "./static/js/main.fed77626.js.map",
|
|
65
65
|
"453.abd36b29.chunk.js.map": "./static/js/453.abd36b29.chunk.js.map"
|
|
66
66
|
},
|
|
67
67
|
"entrypoints": [
|
|
68
68
|
"static/css/main.cf25d33e.css",
|
|
69
|
-
"static/js/main.
|
|
69
|
+
"static/js/main.fed77626.js"
|
|
70
70
|
]
|
|
71
71
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
<!doctype html><html lang="en"><head><meta charset="utf-8"/><base href="./"><link rel="icon" href="./matterbridge 32x32.png"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><title>Matterbridge</title><link rel="manifest" href="./manifest.json"/><script defer="defer" src="./static/js/main.
|
|
1
|
+
<!doctype html><html lang="en"><head><meta charset="utf-8"/><base href="./"><link rel="icon" href="./matterbridge 32x32.png"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><title>Matterbridge</title><link rel="manifest" href="./manifest.json"/><script defer="defer" src="./static/js/main.fed77626.js"></script><link href="./static/css/main.cf25d33e.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div></body></html>
|