matterbridge 2.1.6-dev.8 → 2.2.0-dev.2

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
@@ -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.1.6] - 2025-02-20
36
+ ## [2.2.0] - 2025-02-25
37
37
 
38
38
  ### Added
39
39
 
@@ -41,11 +41,12 @@ 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.2.
44
+ - [frontend]: Frontend v.2.4.5. 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.
48
48
  - [utils]: Optimized memory and loading time.
49
+ - [shelly]: Added all shelly api to be used when matterbridge is running on the shelly matterbridge board.
49
50
 
50
51
  ### Changed
51
52
 
@@ -53,6 +54,9 @@ matterbridge-hass v. 0.0.8
53
54
  - [package]: Update matter.js to 0.12.4-alpha.0-20250213-1187f81eb
54
55
  - [package]: Update matter.js to 0.12.4-alpha.0-20250215-5af08a8d6
55
56
  - [package]: Update matter.js to 0.12.4-alpha.0-20250217-b0bba5179
57
+ - [package]: Update matter.js to 0.12.4-alpha.0-20250223-1e0341a1a
58
+ - [package]: Update matter.js to 0.12.4-alpha.0-20250224-46934b522
59
+ - [matterbridge]: The check for available updates now runs at restart and each 24 hours after.
56
60
 
57
61
  ### Fixed
58
62
 
package/README-NGINX.md CHANGED
@@ -16,18 +16,19 @@
16
16
 
17
17
  ## Run matterbridge with nginx
18
18
 
19
- ### Create the nginx configuration file
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 8099;
30
- server_name ubuntu.local;
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
- add matterbridge to enabled sites
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.local:8099/matterbridge/
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,27 @@ 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
+ }
1097
+ else if (data.method === '/api/reboot') {
1098
+ const { triggerShellyReboot } = await import('./shelly.js');
1099
+ triggerShellyReboot(this.matterbridge);
1100
+ return;
1101
+ }
1078
1102
  else if (data.method === '/api/restart') {
1079
1103
  this.wssSendSnackbarMessage(`Restarting matterbridge...`, 0);
1080
1104
  await this.matterbridge.restartProcess();
@@ -1242,7 +1266,7 @@ export class Frontend {
1242
1266
  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
1267
  return;
1244
1268
  }
1245
- else if (data.method === '/api/select') {
1269
+ else if (data.method === '/api/select' || data.method === '/api/select/devices') {
1246
1270
  if (!isValidString(data.params.plugin, 10)) {
1247
1271
  client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Wrong parameter plugin in /api/select' }));
1248
1272
  return;
@@ -1319,6 +1343,7 @@ export class Frontend {
1319
1343
  wssSendRestartRequired() {
1320
1344
  this.log.debug('Sending a restart required message to all connected clients');
1321
1345
  this.matterbridge.matterbridgeInformation.restartRequired = true;
1346
+ this.wssSendSnackbarMessage(`Restart required`, 0);
1322
1347
  this.webSocketServer?.clients.forEach((client) => {
1323
1348
  if (client.readyState === WebSocket.OPEN) {
1324
1349
  client.send(JSON.stringify({ id: WS_ID_RESTART_NEEDED, src: 'Matterbridge', dst: 'Frontend', method: 'restart_required', params: {} }));
@@ -1357,4 +1382,12 @@ export class Frontend {
1357
1382
  }
1358
1383
  });
1359
1384
  }
1385
+ wssBroadcastMessage(id, method, params) {
1386
+ this.log.debug(`Sending a broadcast message id ${id} method ${method} params ${debugStringify(params ?? {})} to all connected clients`);
1387
+ this.webSocketServer?.clients.forEach((client) => {
1388
+ if (client.readyState === WebSocket.OPEN) {
1389
+ client.send(JSON.stringify({ id, src: 'Matterbridge', dst: 'Frontend', method, params }));
1390
+ }
1391
+ });
1392
+ }
1360
1393
  }
@@ -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, nt } from './logger/export.js';
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, getNpmPackageVersion, getGlobalNodeModules } from './utils/network.js';
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,7 @@ 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 { BridgedDeviceBasicInformationServer } from '@matter/main/behaviors/bridged-device-basic-information';
17
18
  const plg = '\u001B[38;5;33m';
18
19
  const dev = '\u001B[38;5;79m';
19
20
  const typ = '\u001B[38;5;207m';
@@ -57,6 +58,8 @@ export class Matterbridge extends EventEmitter {
57
58
  restartMode: '',
58
59
  readOnly: hasParameter('readonly') || hasParameter('shelly'),
59
60
  shellyBoard: hasParameter('shelly'),
61
+ shellySysUpdate: false,
62
+ shellyMainUpdate: false,
60
63
  profile: getParameter('profile'),
61
64
  loggerLevel: "info",
62
65
  fileLogger: false,
@@ -102,6 +105,7 @@ export class Matterbridge extends EventEmitter {
102
105
  execRunningCount = 0;
103
106
  startMatterInterval;
104
107
  checkUpdateInterval;
108
+ checkUpdateTimeout;
105
109
  configureTimeout;
106
110
  reachabilityTimeout;
107
111
  sigintHandler;
@@ -565,17 +569,14 @@ export class Matterbridge extends EventEmitter {
565
569
  }
566
570
  if (getIntParameter('frontend') !== 0 || getIntParameter('frontend') === undefined)
567
571
  await this.frontend.start(getIntParameter('frontend'));
568
- this.getMatterbridgeLatestVersion();
569
- for (const plugin of this.plugins) {
570
- this.getPluginLatestVersion(plugin);
571
- }
572
- this.checkUpdateInterval = setInterval(() => {
573
- this.getMatterbridgeLatestVersion();
574
- for (const plugin of this.plugins) {
575
- this.getPluginLatestVersion(plugin);
576
- }
577
- this.frontend.wssSendRefreshRequired();
578
- }, 60 * 60 * 1000);
572
+ this.checkUpdateTimeout = setTimeout(async () => {
573
+ const { checkUpdates } = await import('./update.js');
574
+ checkUpdates(this);
575
+ }, 30 * 1000).unref();
576
+ this.checkUpdateInterval = setInterval(async () => {
577
+ const { checkUpdates } = await import('./update.js');
578
+ checkUpdates(this);
579
+ }, 24 * 60 * 60 * 1000).unref();
579
580
  if (hasParameter('test')) {
580
581
  this.bridgeMode = 'bridge';
581
582
  MatterbridgeEndpoint.bridgeMode = 'bridge';
@@ -812,37 +813,6 @@ export class Matterbridge extends EventEmitter {
812
813
  const cmdArgs = process.argv.slice(2).join(' ');
813
814
  this.log.debug(`Command Line Arguments: ${cmdArgs}`);
814
815
  }
815
- async getMatterbridgeLatestVersion() {
816
- getNpmPackageVersion('matterbridge')
817
- .then(async (version) => {
818
- this.matterbridgeLatestVersion = version;
819
- this.matterbridgeInformation.matterbridgeLatestVersion = version;
820
- await this.nodeContext?.set('matterbridgeLatestVersion', this.matterbridgeLatestVersion);
821
- if (this.matterbridgeVersion !== this.matterbridgeLatestVersion) {
822
- this.log.notice(`Matterbridge is out of date. Current version: ${this.matterbridgeVersion}. Latest version: ${this.matterbridgeLatestVersion}.`);
823
- }
824
- else {
825
- this.log.debug(`Matterbridge is up to date. Current version: ${this.matterbridgeVersion}. Latest version: ${this.matterbridgeLatestVersion}.`);
826
- }
827
- this.frontend.wssSendRefreshRequired();
828
- })
829
- .catch((error) => {
830
- this.log.warn(`Error getting Matterbridge latest version: ${error.message}`);
831
- });
832
- }
833
- async getPluginLatestVersion(plugin) {
834
- getNpmPackageVersion(plugin.name)
835
- .then((version) => {
836
- plugin.latestVersion = version;
837
- if (plugin.version !== plugin.latestVersion)
838
- this.log.notice(`The plugin ${plg}${plugin.name}${nt} is out of date. Current version: ${plugin.version}. Latest version: ${plugin.latestVersion}.`);
839
- else
840
- this.log.debug(`The plugin ${plg}${plugin.name}${db} is up to date. Current version: ${plugin.version}. Latest version: ${plugin.latestVersion}.`);
841
- })
842
- .catch((error) => {
843
- this.log.warn(`Error getting ${plg}${plugin.name}${er} latest version: ${error.message}`);
844
- });
845
- }
846
816
  createMatterLogger() {
847
817
  const matterLogger = new AnsiLogger({ logName: 'Matter', logTimestampFormat: 4, logLevel: "debug" });
848
818
  return (_level, formattedLog) => {
@@ -966,6 +936,11 @@ export class Matterbridge extends EventEmitter {
966
936
  this.startMatterInterval = undefined;
967
937
  this.log.debug('Start matter interval cleared');
968
938
  }
939
+ if (this.checkUpdateTimeout) {
940
+ clearInterval(this.checkUpdateTimeout);
941
+ this.checkUpdateTimeout = undefined;
942
+ this.log.debug('Check update timeout cleared');
943
+ }
969
944
  if (this.checkUpdateInterval) {
970
945
  clearInterval(this.checkUpdateInterval);
971
946
  this.checkUpdateInterval = undefined;
@@ -1115,6 +1090,7 @@ export class Matterbridge extends EventEmitter {
1115
1090
  async createAccessoryPlugin(plugin, device, start = false) {
1116
1091
  if (!plugin.locked && device.deviceName && device.vendorId && device.productId && device.vendorName && device.productName) {
1117
1092
  plugin.locked = true;
1093
+ plugin.device = device;
1118
1094
  plugin.storageContext = await this.createServerNodeContext(plugin.name, device.deviceName, DeviceTypeId(device.deviceType), device.vendorId, device.vendorName, device.productId, device.productName);
1119
1095
  plugin.serverNode = await this.createServerNode(plugin.storageContext, this.port ? this.port++ : undefined, this.passcode ? this.passcode++ : undefined, this.discriminator ? this.discriminator++ : undefined);
1120
1096
  this.log.debug(`Adding ${plg}${plugin.name}${db}:${dev}${device.deviceName}${db} to ${plg}${plugin.name}${db} server node`);
@@ -1188,8 +1164,6 @@ export class Matterbridge extends EventEmitter {
1188
1164
  }, 30 * 1000);
1189
1165
  this.reachabilityTimeout = setTimeout(() => {
1190
1166
  this.log.info(`Setting reachability to true for ${plg}Matterbridge${db}`);
1191
- if (this.serverNode)
1192
- this.setServerNodeReachability(this.serverNode, true);
1193
1167
  if (this.aggregatorNode)
1194
1168
  this.setAggregatorReachability(this.aggregatorNode, true);
1195
1169
  this.frontend.wssSendRefreshRequired();
@@ -1274,11 +1248,7 @@ export class Matterbridge extends EventEmitter {
1274
1248
  }
1275
1249
  this.startServerNode(plugin.serverNode);
1276
1250
  plugin.reachabilityTimeout = setTimeout(() => {
1277
- this.log.info(`Setting reachability to true for ${plg}${plugin.name}${db}`);
1278
- if (plugin.serverNode)
1279
- this.setServerNodeReachability(plugin.serverNode, true);
1280
- if (plugin.type === 'AccessoryPlatform' && plugin.device)
1281
- this.setDeviceReachability(plugin.device, true);
1251
+ this.log.info(`Setting reachability to true for ${plg}${plugin.name}${db} type ${plugin.type} server node ${plugin.serverNode !== undefined} aggragator node ${plugin.aggregatorNode !== undefined} device ${plugin.device !== undefined}`);
1282
1252
  if (plugin.type === 'DynamicPlatform' && plugin.aggregatorNode)
1283
1253
  this.setAggregatorReachability(plugin.aggregatorNode, true);
1284
1254
  this.frontend.wssSendRefreshRequired();
@@ -1673,11 +1643,12 @@ export class Matterbridge extends EventEmitter {
1673
1643
  };
1674
1644
  });
1675
1645
  }
1676
- setServerNodeReachability(serverNode, reachable) {
1677
- }
1678
- setAggregatorReachability(aggregatorNode, reachable) {
1679
- }
1680
- setDeviceReachability(device, reachable) {
1646
+ async setAggregatorReachability(aggregatorNode, reachable) {
1647
+ for (const child of aggregatorNode.parts) {
1648
+ this.log.debug(`Setting reachability of ${child?.deviceName} to ${reachable}`);
1649
+ await child.setStateOf(BridgedDeviceBasicInformationServer, { reachable });
1650
+ child.act((agent) => child.eventsOf(BridgedDeviceBasicInformationServer).reachableChanged.emit({ reachableNewValue: true }, agent.context));
1651
+ }
1681
1652
  }
1682
1653
  getVendorIdName = (vendorId) => {
1683
1654
  if (!vendorId)
package/dist/shelly.js ADDED
@@ -0,0 +1,190 @@
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
+ export async function triggerShellyReboot(matterbridge) {
92
+ matterbridge.log.debug(`Triggering Shelly system reboot`);
93
+ postShelly('/api/system/reboot', {}, 60 * 1000)
94
+ .then(async () => {
95
+ matterbridge.log.debug(`Triggered Shelly system reboot`);
96
+ })
97
+ .catch((error) => {
98
+ matterbridge.log.debug(`****Error triggering Shelly system reboot: ${error instanceof Error ? error.message : error}`);
99
+ })
100
+ .finally(() => {
101
+ matterbridge.log.notice(`Rebooting Shelly board...`);
102
+ matterbridge.frontend.wssSendSnackbarMessage('Rebooting Shelly board...');
103
+ });
104
+ }
105
+ async function getShelly(api, timeout = 60000) {
106
+ const http = await import('node:http');
107
+ return new Promise((resolve, reject) => {
108
+ const url = `http://127.0.0.1:8101${api}`;
109
+ const controller = new AbortController();
110
+ const timeoutId = setTimeout(() => {
111
+ controller.abort();
112
+ reject(new Error(`Request timed out after ${timeout / 1000} seconds`));
113
+ }, timeout).unref();
114
+ const req = http.get(url, { signal: controller.signal }, (res) => {
115
+ let data = '';
116
+ if (res.statusCode !== 200) {
117
+ clearTimeout(timeoutId);
118
+ res.resume();
119
+ req.destroy();
120
+ reject(new Error(`Failed to fetch data. Status code: ${res.statusCode}`));
121
+ return;
122
+ }
123
+ res.on('data', (chunk) => {
124
+ data += chunk;
125
+ });
126
+ res.on('end', () => {
127
+ clearTimeout(timeoutId);
128
+ try {
129
+ const jsonData = JSON.parse(data);
130
+ resolve(jsonData);
131
+ }
132
+ catch (error) {
133
+ reject(new Error(`Failed to parse response JSON: ${error instanceof Error ? error.message : error}`));
134
+ }
135
+ });
136
+ });
137
+ req.on('error', (error) => {
138
+ clearTimeout(timeoutId);
139
+ reject(new Error(`Request failed: ${error instanceof Error ? error.message : error}`));
140
+ });
141
+ });
142
+ }
143
+ async function postShelly(api, data, timeout = 60000) {
144
+ const http = await import('node:http');
145
+ return new Promise((resolve, reject) => {
146
+ const url = `http://127.0.0.1:8101${api}`;
147
+ const controller = new AbortController();
148
+ const timeoutId = setTimeout(() => {
149
+ controller.abort();
150
+ reject(new Error(`Request timed out after ${timeout / 1000} seconds`));
151
+ }, timeout).unref();
152
+ const jsonData = JSON.stringify(data);
153
+ const options = {
154
+ method: 'POST',
155
+ headers: {
156
+ 'Content-Type': 'application/json',
157
+ 'Content-Length': Buffer.byteLength(jsonData),
158
+ },
159
+ signal: controller.signal,
160
+ };
161
+ const req = http.request(url, options, (res) => {
162
+ let responseData = '';
163
+ if (res.statusCode && res.statusCode >= 300) {
164
+ clearTimeout(timeoutId);
165
+ res.resume();
166
+ req.destroy();
167
+ return reject(new Error(`Failed to post data. Status code: ${res.statusCode}`));
168
+ }
169
+ res.on('data', (chunk) => {
170
+ responseData += chunk;
171
+ });
172
+ res.on('end', () => {
173
+ clearTimeout(timeoutId);
174
+ try {
175
+ const jsonResponse = JSON.parse(responseData);
176
+ resolve(jsonResponse);
177
+ }
178
+ catch (err) {
179
+ reject(new Error(`Failed to parse response JSON: ${err instanceof Error ? err.message : err}`));
180
+ }
181
+ });
182
+ });
183
+ req.on('error', (error) => {
184
+ clearTimeout(timeoutId);
185
+ reject(new Error(`Request failed: ${error instanceof Error ? error.message : error}`));
186
+ });
187
+ req.write(jsonData);
188
+ req.end();
189
+ });
190
+ }
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
+ }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "files": {
3
3
  "main.css": "./static/css/main.cf25d33e.css",
4
- "main.js": "./static/js/main.f3c386d7.js",
4
+ "main.js": "./static/js/main.a462621b.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.f3c386d7.js.map": "./static/js/main.f3c386d7.js.map",
64
+ "main.a462621b.js.map": "./static/js/main.a462621b.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.f3c386d7.js"
69
+ "static/js/main.a462621b.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.f3c386d7.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>
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.a462621b.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>