matterbridge 3.0.1-dev-20250501-4f463f9 → 3.0.1-dev-20250502-6d36575
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 +11 -10
- package/README-DOCKER.md +27 -15
- package/dist/frontend.js +171 -259
- package/frontend/build/asset-manifest.json +3 -3
- package/frontend/build/index.html +1 -1
- package/frontend/build/static/js/{main.356788d7.js → main.2093c348.js} +3 -3
- package/frontend/build/static/js/{main.356788d7.js.map → main.2093c348.js.map} +1 -1
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -1
- /package/frontend/build/static/js/{main.356788d7.js.LICENSE.txt → main.2093c348.js.LICENSE.txt} +0 -0
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, deepCopy, isValidArray, isValidNumber, isValidObject, isValidString } from './utils/export.js';
|
|
11
|
+
import { createZip, deepCopy, 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';
|
|
@@ -21,6 +21,7 @@ export const WS_ID_UPTIME_UPDATE = 5;
|
|
|
21
21
|
export const WS_ID_SNACKBAR = 6;
|
|
22
22
|
export const WS_ID_UPDATE_NEEDED = 7;
|
|
23
23
|
export const WS_ID_STATEUPDATE = 8;
|
|
24
|
+
export const WS_ID_CLOSE_SNACKBAR = 9;
|
|
24
25
|
export const WS_ID_SHELLY_SYS_UPDATE = 100;
|
|
25
26
|
export const WS_ID_SHELLY_MAIN_UPDATE = 101;
|
|
26
27
|
export class Frontend {
|
|
@@ -257,16 +258,6 @@ export class Frontend {
|
|
|
257
258
|
};
|
|
258
259
|
res.status(200).json(memoryReport);
|
|
259
260
|
});
|
|
260
|
-
this.expressApp.get('/api/advertise', express.json(), async (req, res) => {
|
|
261
|
-
const pairingCodes = await this.matterbridge.advertiseServerNode(this.matterbridge.serverNode);
|
|
262
|
-
if (pairingCodes) {
|
|
263
|
-
const { manualPairingCode, qrPairingCode } = pairingCodes;
|
|
264
|
-
res.json({ manualPairingCode, qrPairingCode: 'https://project-chip.github.io/connectedhomeip/qrcode.html?data=' + qrPairingCode });
|
|
265
|
-
}
|
|
266
|
-
else {
|
|
267
|
-
res.status(500).json({ error: 'Failed to generate pairing codes' });
|
|
268
|
-
}
|
|
269
|
-
});
|
|
270
261
|
this.expressApp.get('/api/settings', express.json(), async (req, res) => {
|
|
271
262
|
this.log.debug('The frontend sent /api/settings');
|
|
272
263
|
res.json(await this.getApiSettings());
|
|
@@ -275,100 +266,11 @@ export class Frontend {
|
|
|
275
266
|
this.log.debug('The frontend sent /api/plugins');
|
|
276
267
|
res.json(this.getBaseRegisteredPlugins());
|
|
277
268
|
});
|
|
278
|
-
this.expressApp.get('/api/devices', (req, res) => {
|
|
269
|
+
this.expressApp.get('/api/devices', async (req, res) => {
|
|
279
270
|
this.log.debug('The frontend sent /api/devices');
|
|
280
|
-
const devices =
|
|
281
|
-
this.matterbridge.devices.forEach(async (device) => {
|
|
282
|
-
if (!device.plugin || !device.name || !device.deviceName || !device.serialNumber || !device.uniqueId || !device.lifecycle.isReady)
|
|
283
|
-
return;
|
|
284
|
-
const cluster = this.getClusterTextFromDevice(device);
|
|
285
|
-
devices.push({
|
|
286
|
-
pluginName: device.plugin,
|
|
287
|
-
type: device.name + ' (0x' + device.deviceType.toString(16).padStart(4, '0') + ')',
|
|
288
|
-
endpoint: device.number,
|
|
289
|
-
name: device.deviceName,
|
|
290
|
-
serial: device.serialNumber,
|
|
291
|
-
productUrl: device.productUrl,
|
|
292
|
-
configUrl: device.configUrl,
|
|
293
|
-
uniqueId: device.uniqueId,
|
|
294
|
-
reachable: this.getReachability(device),
|
|
295
|
-
cluster: cluster,
|
|
296
|
-
});
|
|
297
|
-
});
|
|
271
|
+
const devices = await this.getDevices();
|
|
298
272
|
res.json(devices);
|
|
299
273
|
});
|
|
300
|
-
this.expressApp.get('/api/devices_clusters/:selectedPluginName/:selectedDeviceEndpoint', (req, res) => {
|
|
301
|
-
const selectedPluginName = req.params.selectedPluginName;
|
|
302
|
-
const selectedDeviceEndpoint = parseInt(req.params.selectedDeviceEndpoint, 10);
|
|
303
|
-
this.log.debug(`The frontend sent /api/devices_clusters plugin:${selectedPluginName} endpoint:${selectedDeviceEndpoint}`);
|
|
304
|
-
if (selectedPluginName === 'none') {
|
|
305
|
-
res.json([]);
|
|
306
|
-
return;
|
|
307
|
-
}
|
|
308
|
-
const data = [];
|
|
309
|
-
this.matterbridge.devices.forEach(async (device) => {
|
|
310
|
-
const pluginName = device.plugin;
|
|
311
|
-
if (pluginName === selectedPluginName && device.number === selectedDeviceEndpoint) {
|
|
312
|
-
const endpointServer = EndpointServer.forEndpoint(device);
|
|
313
|
-
const clusterServers = endpointServer.getAllClusterServers();
|
|
314
|
-
clusterServers.forEach((clusterServer) => {
|
|
315
|
-
Object.entries(clusterServer.attributes).forEach(([key, value]) => {
|
|
316
|
-
if (clusterServer.name === 'EveHistory')
|
|
317
|
-
return;
|
|
318
|
-
let attributeValue;
|
|
319
|
-
try {
|
|
320
|
-
if (typeof value.getLocal() === 'object')
|
|
321
|
-
attributeValue = stringify(value.getLocal());
|
|
322
|
-
else
|
|
323
|
-
attributeValue = value.getLocal().toString();
|
|
324
|
-
}
|
|
325
|
-
catch (error) {
|
|
326
|
-
attributeValue = 'Fabric-Scoped';
|
|
327
|
-
this.log.debug(`GetLocal value ${error} in clusterServer: ${clusterServer.name}(${clusterServer.id}) attribute: ${key}(${value.id})`);
|
|
328
|
-
}
|
|
329
|
-
data.push({
|
|
330
|
-
endpoint: device.number ? device.number.toString() : '...',
|
|
331
|
-
clusterName: clusterServer.name,
|
|
332
|
-
clusterId: '0x' + clusterServer.id.toString(16).padStart(2, '0'),
|
|
333
|
-
attributeName: key,
|
|
334
|
-
attributeId: '0x' + value.id.toString(16).padStart(2, '0'),
|
|
335
|
-
attributeValue,
|
|
336
|
-
});
|
|
337
|
-
});
|
|
338
|
-
});
|
|
339
|
-
endpointServer.getChildEndpoints().forEach((childEndpoint) => {
|
|
340
|
-
const name = childEndpoint.name;
|
|
341
|
-
const clusterServers = childEndpoint.getAllClusterServers();
|
|
342
|
-
clusterServers.forEach((clusterServer) => {
|
|
343
|
-
Object.entries(clusterServer.attributes).forEach(([key, value]) => {
|
|
344
|
-
if (clusterServer.name === 'EveHistory')
|
|
345
|
-
return;
|
|
346
|
-
let attributeValue;
|
|
347
|
-
try {
|
|
348
|
-
if (typeof value.getLocal() === 'object')
|
|
349
|
-
attributeValue = stringify(value.getLocal());
|
|
350
|
-
else
|
|
351
|
-
attributeValue = value.getLocal().toString();
|
|
352
|
-
}
|
|
353
|
-
catch (error) {
|
|
354
|
-
attributeValue = 'Fabric-Scoped';
|
|
355
|
-
this.log.debug(`GetLocal error ${error} in clusterServer: ${clusterServer.name}(${clusterServer.id}) attribute: ${key}(${value.id})`);
|
|
356
|
-
}
|
|
357
|
-
data.push({
|
|
358
|
-
endpoint: (childEndpoint.number ? childEndpoint.number.toString() : '...') + (name ? ' (' + name + ')' : ''),
|
|
359
|
-
clusterName: clusterServer.name,
|
|
360
|
-
clusterId: '0x' + clusterServer.id.toString(16).padStart(2, '0'),
|
|
361
|
-
attributeName: key,
|
|
362
|
-
attributeId: '0x' + value.id.toString(16).padStart(2, '0'),
|
|
363
|
-
attributeValue,
|
|
364
|
-
});
|
|
365
|
-
});
|
|
366
|
-
});
|
|
367
|
-
});
|
|
368
|
-
}
|
|
369
|
-
});
|
|
370
|
-
res.json(data);
|
|
371
|
-
});
|
|
372
274
|
this.expressApp.get('/api/view-mblog', async (req, res) => {
|
|
373
275
|
this.log.debug('The frontend sent /api/view-mblog');
|
|
374
276
|
try {
|
|
@@ -679,37 +581,6 @@ export class Frontend {
|
|
|
679
581
|
res.json({ message: 'Command received' });
|
|
680
582
|
return;
|
|
681
583
|
}
|
|
682
|
-
if (command === 'unregister') {
|
|
683
|
-
await this.matterbridge.unregisterAndShutdownProcess();
|
|
684
|
-
res.json({ message: 'Command received' });
|
|
685
|
-
return;
|
|
686
|
-
}
|
|
687
|
-
if (command === 'reset') {
|
|
688
|
-
await this.matterbridge.shutdownProcessAndReset();
|
|
689
|
-
res.json({ message: 'Command received' });
|
|
690
|
-
return;
|
|
691
|
-
}
|
|
692
|
-
if (command === 'factoryreset') {
|
|
693
|
-
await this.matterbridge.shutdownProcessAndFactoryReset();
|
|
694
|
-
res.json({ message: 'Command received' });
|
|
695
|
-
return;
|
|
696
|
-
}
|
|
697
|
-
if (command === 'shutdown') {
|
|
698
|
-
await this.matterbridge.shutdownProcess();
|
|
699
|
-
res.json({ message: 'Command received' });
|
|
700
|
-
return;
|
|
701
|
-
}
|
|
702
|
-
if (command === 'restart') {
|
|
703
|
-
await this.matterbridge.restartProcess();
|
|
704
|
-
res.json({ message: 'Command received' });
|
|
705
|
-
return;
|
|
706
|
-
}
|
|
707
|
-
if (command === 'update') {
|
|
708
|
-
await this.matterbridge.updateProcess();
|
|
709
|
-
this.wssSendRestartRequired();
|
|
710
|
-
res.json({ message: 'Command received' });
|
|
711
|
-
return;
|
|
712
|
-
}
|
|
713
584
|
if (command === 'saveconfig') {
|
|
714
585
|
param = param.replace(/\*/g, '\\');
|
|
715
586
|
this.log.info(`Saving config for plugin ${plg}${param}${nf}...`);
|
|
@@ -727,99 +598,6 @@ export class Frontend {
|
|
|
727
598
|
res.json({ message: 'Command received' });
|
|
728
599
|
return;
|
|
729
600
|
}
|
|
730
|
-
if (command === 'installplugin') {
|
|
731
|
-
param = param.replace(/\*/g, '\\');
|
|
732
|
-
this.log.info(`Installing plugin ${plg}${param}${nf}...`);
|
|
733
|
-
this.wssSendSnackbarMessage(`Installing package ${param}. Please wait...`);
|
|
734
|
-
try {
|
|
735
|
-
await this.matterbridge.spawnCommand('npm', ['install', '-g', param, '--omit=dev', '--verbose']);
|
|
736
|
-
this.log.info(`Plugin ${plg}${param}${nf} installed. Full restart required.`);
|
|
737
|
-
this.wssSendSnackbarMessage(`Installed package ${param}`, 10, 'success');
|
|
738
|
-
}
|
|
739
|
-
catch (error) {
|
|
740
|
-
this.log.error(`Error installing plugin ${plg}${param}${er}`);
|
|
741
|
-
this.wssSendSnackbarMessage(`Package ${param} not installed`, 10, 'error');
|
|
742
|
-
}
|
|
743
|
-
this.wssSendRestartRequired();
|
|
744
|
-
param = param.split('@')[0];
|
|
745
|
-
if (param === 'matterbridge') {
|
|
746
|
-
res.json({ message: 'Command received' });
|
|
747
|
-
return;
|
|
748
|
-
}
|
|
749
|
-
}
|
|
750
|
-
if (command === 'addplugin' || command === 'installplugin') {
|
|
751
|
-
param = param.replace(/\*/g, '\\');
|
|
752
|
-
const plugin = await this.matterbridge.plugins.add(param);
|
|
753
|
-
if (plugin) {
|
|
754
|
-
this.wssSendSnackbarMessage(`Added plugin ${param}`);
|
|
755
|
-
if (this.matterbridge.bridgeMode === 'childbridge') {
|
|
756
|
-
this.matterbridge.createDynamicPlugin(plugin, true);
|
|
757
|
-
}
|
|
758
|
-
this.matterbridge.plugins.load(plugin, true, 'The plugin has been added', true).then(() => {
|
|
759
|
-
this.wssSendRefreshRequired('plugins');
|
|
760
|
-
});
|
|
761
|
-
}
|
|
762
|
-
res.json({ message: 'Command received' });
|
|
763
|
-
return;
|
|
764
|
-
}
|
|
765
|
-
if (command === 'removeplugin') {
|
|
766
|
-
if (!this.matterbridge.plugins.has(param)) {
|
|
767
|
-
this.log.warn(`Plugin ${plg}${param}${wr} not found in matterbridge`);
|
|
768
|
-
}
|
|
769
|
-
else {
|
|
770
|
-
const plugin = this.matterbridge.plugins.get(param);
|
|
771
|
-
await this.matterbridge.plugins.shutdown(plugin, 'The plugin has been removed.', true);
|
|
772
|
-
await this.matterbridge.plugins.remove(param);
|
|
773
|
-
this.wssSendSnackbarMessage(`Removed plugin ${param}`);
|
|
774
|
-
this.wssSendRefreshRequired('plugins');
|
|
775
|
-
}
|
|
776
|
-
res.json({ message: 'Command received' });
|
|
777
|
-
return;
|
|
778
|
-
}
|
|
779
|
-
if (command === 'enableplugin') {
|
|
780
|
-
if (!this.matterbridge.plugins.has(param)) {
|
|
781
|
-
this.log.warn(`Plugin ${plg}${param}${wr} not found in matterbridge`);
|
|
782
|
-
}
|
|
783
|
-
else {
|
|
784
|
-
const plugin = this.matterbridge.plugins.get(param);
|
|
785
|
-
if (plugin && !plugin.enabled) {
|
|
786
|
-
plugin.locked = undefined;
|
|
787
|
-
plugin.error = undefined;
|
|
788
|
-
plugin.loaded = undefined;
|
|
789
|
-
plugin.started = undefined;
|
|
790
|
-
plugin.configured = undefined;
|
|
791
|
-
plugin.platform = undefined;
|
|
792
|
-
plugin.registeredDevices = undefined;
|
|
793
|
-
plugin.addedDevices = undefined;
|
|
794
|
-
await this.matterbridge.plugins.enable(param);
|
|
795
|
-
this.wssSendSnackbarMessage(`Enabled plugin ${param}`);
|
|
796
|
-
if (this.matterbridge.bridgeMode === 'childbridge' && plugin.type === 'DynamicPlatform') {
|
|
797
|
-
this.matterbridge.createDynamicPlugin(plugin, true);
|
|
798
|
-
}
|
|
799
|
-
this.matterbridge.plugins.load(plugin, true, 'The plugin has been enabled', true).then(() => {
|
|
800
|
-
this.wssSendRefreshRequired('plugins');
|
|
801
|
-
});
|
|
802
|
-
}
|
|
803
|
-
}
|
|
804
|
-
res.json({ message: 'Command received' });
|
|
805
|
-
return;
|
|
806
|
-
}
|
|
807
|
-
if (command === 'disableplugin') {
|
|
808
|
-
if (!this.matterbridge.plugins.has(param)) {
|
|
809
|
-
this.log.warn(`Plugin ${plg}${param}${wr} not found in matterbridge`);
|
|
810
|
-
}
|
|
811
|
-
else {
|
|
812
|
-
const plugin = this.matterbridge.plugins.get(param);
|
|
813
|
-
if (plugin && plugin.enabled) {
|
|
814
|
-
await this.matterbridge.plugins.shutdown(plugin, 'The plugin has been disabled.', true);
|
|
815
|
-
await this.matterbridge.plugins.disable(param);
|
|
816
|
-
this.wssSendSnackbarMessage(`Disabled plugin ${param}`);
|
|
817
|
-
this.wssSendRefreshRequired('plugins');
|
|
818
|
-
}
|
|
819
|
-
}
|
|
820
|
-
res.json({ message: 'Command received' });
|
|
821
|
-
return;
|
|
822
|
-
}
|
|
823
601
|
});
|
|
824
602
|
this.expressApp.post('/api/uploadpackage', upload.single('file'), async (req, res) => {
|
|
825
603
|
const { filename } = req.body;
|
|
@@ -1147,6 +925,30 @@ export class Frontend {
|
|
|
1147
925
|
}
|
|
1148
926
|
return baseRegisteredPlugins;
|
|
1149
927
|
}
|
|
928
|
+
async getDevices(pluginName) {
|
|
929
|
+
const devices = [];
|
|
930
|
+
this.matterbridge.devices.forEach(async (device) => {
|
|
931
|
+
if (pluginName && pluginName !== device.plugin)
|
|
932
|
+
return;
|
|
933
|
+
if (!device.plugin || !device.name || !device.deviceName || !device.serialNumber || !device.uniqueId || !device.lifecycle.isReady)
|
|
934
|
+
return;
|
|
935
|
+
const cluster = this.getClusterTextFromDevice(device);
|
|
936
|
+
devices.push({
|
|
937
|
+
pluginName: device.plugin,
|
|
938
|
+
type: device.name + ' (0x' + device.deviceType.toString(16).padStart(4, '0') + ')',
|
|
939
|
+
endpoint: device.number,
|
|
940
|
+
name: device.deviceName,
|
|
941
|
+
serial: device.serialNumber,
|
|
942
|
+
productUrl: device.productUrl,
|
|
943
|
+
configUrl: device.configUrl,
|
|
944
|
+
uniqueId: device.uniqueId,
|
|
945
|
+
reachable: this.getReachability(device),
|
|
946
|
+
powerSource: this.getPowerSource(device),
|
|
947
|
+
cluster: cluster,
|
|
948
|
+
});
|
|
949
|
+
});
|
|
950
|
+
return devices;
|
|
951
|
+
}
|
|
1150
952
|
async wsMessageHandler(client, message) {
|
|
1151
953
|
let data;
|
|
1152
954
|
try {
|
|
@@ -1180,31 +982,49 @@ export class Frontend {
|
|
|
1180
982
|
}
|
|
1181
983
|
}
|
|
1182
984
|
else if (data.method === '/api/install') {
|
|
1183
|
-
if (!isValidString(data.params.packageName, 10)) {
|
|
1184
|
-
client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Wrong parameter
|
|
985
|
+
if (!isValidString(data.params.packageName, 10) || !isValidBoolean(data.params.restart)) {
|
|
986
|
+
client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Wrong parameter in /api/install' }));
|
|
1185
987
|
return;
|
|
1186
988
|
}
|
|
1187
|
-
this.wssSendSnackbarMessage(`Installing package ${data.params.packageName}
|
|
989
|
+
this.wssSendSnackbarMessage(`Installing package ${data.params.packageName}...`, 0);
|
|
1188
990
|
this.matterbridge
|
|
1189
991
|
.spawnCommand('npm', ['install', '-g', data.params.packageName, '--omit=dev', '--verbose'])
|
|
1190
992
|
.then((response) => {
|
|
1191
993
|
client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, response }));
|
|
1192
|
-
this.
|
|
1193
|
-
|
|
1194
|
-
|
|
994
|
+
this.wssSendCloseSnackbarMessage(`Installing package ${data.params.packageName}...`);
|
|
995
|
+
this.wssSendSnackbarMessage(`Installed package ${data.params.packageName}`, 5, 'success');
|
|
996
|
+
if (data.params.restart === false) {
|
|
997
|
+
data.params.packageName = data.params.packageName.replace(/@.*$/, '');
|
|
998
|
+
this.matterbridge.plugins.add(data.params.packageName).then((plugin) => {
|
|
999
|
+
if (plugin) {
|
|
1000
|
+
this.wssSendSnackbarMessage(`Added plugin ${data.params.pluginNameOrPath}`, 5, 'success');
|
|
1001
|
+
this.matterbridge.plugins.load(plugin, true, 'The plugin has been added', true).then(() => {
|
|
1002
|
+
this.wssSendSnackbarMessage(`Started plugin ${data.params.pluginNameOrPath}`, 5, 'success');
|
|
1003
|
+
this.wssSendRefreshRequired('plugins');
|
|
1004
|
+
});
|
|
1005
|
+
}
|
|
1006
|
+
else {
|
|
1007
|
+
this.wssSendSnackbarMessage(`Restart required`, 0);
|
|
1008
|
+
this.wssSendRefreshRequired('plugins');
|
|
1009
|
+
this.wssSendRestartRequired();
|
|
1010
|
+
}
|
|
1011
|
+
});
|
|
1195
1012
|
}
|
|
1196
1013
|
else {
|
|
1197
1014
|
if (this.matterbridge.restartMode !== '') {
|
|
1198
1015
|
this.wssSendSnackbarMessage(`Restarting matterbridge...`, 0);
|
|
1199
1016
|
this.matterbridge.shutdownProcess();
|
|
1200
1017
|
}
|
|
1201
|
-
else
|
|
1018
|
+
else {
|
|
1202
1019
|
this.wssSendSnackbarMessage(`Restart required`, 0);
|
|
1020
|
+
this.wssSendRestartRequired();
|
|
1021
|
+
}
|
|
1203
1022
|
}
|
|
1204
1023
|
})
|
|
1205
1024
|
.catch((error) => {
|
|
1206
1025
|
client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: error instanceof Error ? error.message : error }));
|
|
1207
|
-
this.
|
|
1026
|
+
this.wssSendCloseSnackbarMessage(`Installing package ${data.params.packageName}...`);
|
|
1027
|
+
this.wssSendSnackbarMessage(`Package ${data.params.packageName} not installed`, 10, 'error');
|
|
1208
1028
|
});
|
|
1209
1029
|
return;
|
|
1210
1030
|
}
|
|
@@ -1213,18 +1033,102 @@ export class Frontend {
|
|
|
1213
1033
|
client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Wrong parameter packageName in /api/uninstall' }));
|
|
1214
1034
|
return;
|
|
1215
1035
|
}
|
|
1216
|
-
this.
|
|
1036
|
+
const plugin = this.matterbridge.plugins.get(data.params.packageName);
|
|
1037
|
+
if (plugin) {
|
|
1038
|
+
await this.matterbridge.plugins.shutdown(plugin, 'The plugin has been removed.', true);
|
|
1039
|
+
await this.matterbridge.plugins.remove(data.params.packageName);
|
|
1040
|
+
this.wssSendSnackbarMessage(`Removed plugin ${data.params.packageName}`, 5, 'success');
|
|
1041
|
+
this.wssSendRefreshRequired('plugins');
|
|
1042
|
+
this.wssSendRefreshRequired('devices');
|
|
1043
|
+
}
|
|
1044
|
+
this.wssSendSnackbarMessage(`Uninstalling package ${data.params.packageName}...`, 0);
|
|
1217
1045
|
this.matterbridge
|
|
1218
1046
|
.spawnCommand('npm', ['uninstall', '-g', data.params.packageName, '--verbose'])
|
|
1219
1047
|
.then((response) => {
|
|
1220
1048
|
client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, response }));
|
|
1049
|
+
this.wssSendCloseSnackbarMessage(`Uninstalling package ${data.params.packageName}...`);
|
|
1050
|
+
this.wssSendSnackbarMessage(`Uninstalled package ${data.params.packageName}`, 5, 'success');
|
|
1221
1051
|
})
|
|
1222
1052
|
.catch((error) => {
|
|
1223
1053
|
client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: error instanceof Error ? error.message : error }));
|
|
1224
|
-
this.
|
|
1054
|
+
this.wssSendCloseSnackbarMessage(`Uninstalling package ${data.params.packageName}...`);
|
|
1055
|
+
this.wssSendSnackbarMessage(`Package ${data.params.packageName} not uninstalled`, 10, 'error');
|
|
1225
1056
|
this.wssSendSnackbarMessage(`Restart required`, 0);
|
|
1226
1057
|
});
|
|
1227
|
-
|
|
1058
|
+
}
|
|
1059
|
+
else if (data.method === '/api/addplugin') {
|
|
1060
|
+
if (!isValidString(data.params.pluginNameOrPath, 10)) {
|
|
1061
|
+
client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Wrong parameter pluginNameOrPath in /api/addplugin' }));
|
|
1062
|
+
return;
|
|
1063
|
+
}
|
|
1064
|
+
data.params.pluginNameOrPath = data.params.pluginNameOrPath.replace(/@.*$/, '');
|
|
1065
|
+
if (this.matterbridge.plugins.has(data.params.pluginNameOrPath)) {
|
|
1066
|
+
client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: `Plugin ${data.params.pluginNameOrPath} already added` }));
|
|
1067
|
+
this.wssSendSnackbarMessage(`Plugin ${data.params.pluginNameOrPath} already added`, 10, 'warning');
|
|
1068
|
+
return;
|
|
1069
|
+
}
|
|
1070
|
+
const plugin = await this.matterbridge.plugins.add(data.params.pluginNameOrPath);
|
|
1071
|
+
if (plugin) {
|
|
1072
|
+
client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, response: plugin.name }));
|
|
1073
|
+
this.wssSendSnackbarMessage(`Added plugin ${data.params.pluginNameOrPath}`, 5, 'success');
|
|
1074
|
+
this.matterbridge.plugins.load(plugin, true, 'The plugin has been added', true).then(() => {
|
|
1075
|
+
this.wssSendRefreshRequired('plugins');
|
|
1076
|
+
this.wssSendRefreshRequired('devices');
|
|
1077
|
+
this.wssSendSnackbarMessage(`Started plugin ${data.params.pluginNameOrPath}`, 5, 'success');
|
|
1078
|
+
});
|
|
1079
|
+
}
|
|
1080
|
+
else {
|
|
1081
|
+
client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: `Plugin ${data.params.pluginNameOrPath} not added` }));
|
|
1082
|
+
this.wssSendSnackbarMessage(`Plugin ${data.params.pluginNameOrPath} not added`, 10, 'error');
|
|
1083
|
+
}
|
|
1084
|
+
}
|
|
1085
|
+
else if (data.method === '/api/removeplugin') {
|
|
1086
|
+
if (!isValidString(data.params.pluginName, 10) || !this.matterbridge.plugins.has(data.params.pluginName)) {
|
|
1087
|
+
client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Wrong parameter pluginName in /api/removeplugin' }));
|
|
1088
|
+
return;
|
|
1089
|
+
}
|
|
1090
|
+
const plugin = this.matterbridge.plugins.get(data.params.pluginName);
|
|
1091
|
+
await this.matterbridge.plugins.shutdown(plugin, 'The plugin has been removed.', true);
|
|
1092
|
+
await this.matterbridge.plugins.remove(data.params.pluginName);
|
|
1093
|
+
this.wssSendSnackbarMessage(`Removed plugin ${data.params.pluginName}`, 5, 'success');
|
|
1094
|
+
this.wssSendRefreshRequired('plugins');
|
|
1095
|
+
this.wssSendRefreshRequired('devices');
|
|
1096
|
+
}
|
|
1097
|
+
else if (data.method === '/api/enableplugin') {
|
|
1098
|
+
if (!isValidString(data.params.pluginName, 10) || !this.matterbridge.plugins.has(data.params.pluginName)) {
|
|
1099
|
+
client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Wrong parameter pluginName in /api/enableplugin' }));
|
|
1100
|
+
return;
|
|
1101
|
+
}
|
|
1102
|
+
const plugin = this.matterbridge.plugins.get(data.params.pluginName);
|
|
1103
|
+
if (plugin && !plugin.enabled) {
|
|
1104
|
+
plugin.locked = undefined;
|
|
1105
|
+
plugin.error = undefined;
|
|
1106
|
+
plugin.loaded = undefined;
|
|
1107
|
+
plugin.started = undefined;
|
|
1108
|
+
plugin.configured = undefined;
|
|
1109
|
+
plugin.platform = undefined;
|
|
1110
|
+
plugin.registeredDevices = undefined;
|
|
1111
|
+
plugin.addedDevices = undefined;
|
|
1112
|
+
await this.matterbridge.plugins.enable(data.params.pluginName);
|
|
1113
|
+
this.wssSendSnackbarMessage(`Enabled plugin ${data.params.pluginName}`, 5, 'success');
|
|
1114
|
+
this.matterbridge.plugins.load(plugin, true, 'The plugin has been enabled', true).then(() => {
|
|
1115
|
+
this.wssSendRefreshRequired('plugins');
|
|
1116
|
+
this.wssSendRefreshRequired('devices');
|
|
1117
|
+
this.wssSendSnackbarMessage(`Started plugin ${data.params.pluginName}`, 5, 'success');
|
|
1118
|
+
});
|
|
1119
|
+
}
|
|
1120
|
+
}
|
|
1121
|
+
else if (data.method === '/api/disableplugin') {
|
|
1122
|
+
if (!isValidString(data.params.pluginName, 10) || !this.matterbridge.plugins.has(data.params.pluginName)) {
|
|
1123
|
+
client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, error: 'Wrong parameter pluginName in /api/disableplugin' }));
|
|
1124
|
+
return;
|
|
1125
|
+
}
|
|
1126
|
+
const plugin = this.matterbridge.plugins.get(data.params.pluginName);
|
|
1127
|
+
await this.matterbridge.plugins.shutdown(plugin, 'The plugin has been disabled.', true);
|
|
1128
|
+
await this.matterbridge.plugins.disable(data.params.pluginName);
|
|
1129
|
+
this.wssSendSnackbarMessage(`Disabled plugin ${data.params.pluginName}`, 5, 'success');
|
|
1130
|
+
this.wssSendRefreshRequired('plugins');
|
|
1131
|
+
this.wssSendRefreshRequired('devices');
|
|
1228
1132
|
}
|
|
1229
1133
|
else if (data.method === '/api/shellysysupdate') {
|
|
1230
1134
|
const { triggerShellySysUpdate } = await import('./shelly.js');
|
|
@@ -1272,6 +1176,26 @@ export class Frontend {
|
|
|
1272
1176
|
await this.matterbridge.shutdownProcess();
|
|
1273
1177
|
return;
|
|
1274
1178
|
}
|
|
1179
|
+
else if (data.method === '/api/create-backup') {
|
|
1180
|
+
this.wssSendSnackbarMessage('Creating backup...', 0);
|
|
1181
|
+
this.log.notice(`Creating the backup...`);
|
|
1182
|
+
await createZip(path.join(os.tmpdir(), `matterbridge.backup.zip`), path.join(this.matterbridge.matterbridgeDirectory), path.join(this.matterbridge.matterbridgePluginDirectory));
|
|
1183
|
+
this.log.notice(`Backup ready to be downloaded.`);
|
|
1184
|
+
this.wssSendCloseSnackbarMessage('Creating backup...');
|
|
1185
|
+
this.wssSendSnackbarMessage('Backup ready to be downloaded', 10);
|
|
1186
|
+
}
|
|
1187
|
+
else if (data.method === '/api/unregister') {
|
|
1188
|
+
this.wssSendSnackbarMessage('Uregistering all bridged devices...', 10);
|
|
1189
|
+
await this.matterbridge.unregisterAndShutdownProcess();
|
|
1190
|
+
}
|
|
1191
|
+
else if (data.method === '/api/reset') {
|
|
1192
|
+
this.wssSendSnackbarMessage('Resetting matterbridge commissioning...', 10);
|
|
1193
|
+
await this.matterbridge.shutdownProcessAndReset();
|
|
1194
|
+
}
|
|
1195
|
+
else if (data.method === '/api/factoryreset') {
|
|
1196
|
+
this.wssSendSnackbarMessage('Factory reset of matterbridge...', 10);
|
|
1197
|
+
await this.matterbridge.shutdownProcessAndFactoryReset();
|
|
1198
|
+
}
|
|
1275
1199
|
else if (data.method === '/api/advertise') {
|
|
1276
1200
|
const pairingCodes = await this.matterbridge.advertiseServerNode(this.matterbridge.serverNode);
|
|
1277
1201
|
this.matterbridge.matterbridgeInformation.matterbridgeAdvertise = true;
|
|
@@ -1300,27 +1224,7 @@ export class Frontend {
|
|
|
1300
1224
|
return;
|
|
1301
1225
|
}
|
|
1302
1226
|
else if (data.method === '/api/devices') {
|
|
1303
|
-
const devices =
|
|
1304
|
-
this.matterbridge.devices.forEach(async (device) => {
|
|
1305
|
-
if (data.params.pluginName && data.params.pluginName !== device.plugin)
|
|
1306
|
-
return;
|
|
1307
|
-
if (!device.plugin || !device.name || !device.deviceName || !device.serialNumber || !device.uniqueId || !device.lifecycle.isReady)
|
|
1308
|
-
return;
|
|
1309
|
-
const cluster = this.getClusterTextFromDevice(device);
|
|
1310
|
-
devices.push({
|
|
1311
|
-
pluginName: device.plugin,
|
|
1312
|
-
type: device.name + ' (0x' + device.deviceType.toString(16).padStart(4, '0') + ')',
|
|
1313
|
-
endpoint: device.number,
|
|
1314
|
-
name: device.deviceName,
|
|
1315
|
-
serial: device.serialNumber,
|
|
1316
|
-
productUrl: device.productUrl,
|
|
1317
|
-
configUrl: device.configUrl,
|
|
1318
|
-
uniqueId: device.uniqueId,
|
|
1319
|
-
reachable: this.getReachability(device),
|
|
1320
|
-
powerSource: this.getPowerSource(device),
|
|
1321
|
-
cluster: cluster,
|
|
1322
|
-
});
|
|
1323
|
-
});
|
|
1227
|
+
const devices = await this.getDevices(isValidString(data.params.pluginName) ? data.params.pluginName : undefined);
|
|
1324
1228
|
client.send(JSON.stringify({ id: data.id, method: data.method, src: 'Matterbridge', dst: data.src, response: devices }));
|
|
1325
1229
|
return;
|
|
1326
1230
|
}
|
|
@@ -1652,7 +1556,15 @@ export class Frontend {
|
|
|
1652
1556
|
this.log.debug('Sending a snackbar message to all connected clients');
|
|
1653
1557
|
this.webSocketServer?.clients.forEach((client) => {
|
|
1654
1558
|
if (client.readyState === WebSocket.OPEN) {
|
|
1655
|
-
client.send(JSON.stringify({ id: WS_ID_SNACKBAR, src: 'Matterbridge', dst: 'Frontend',
|
|
1559
|
+
client.send(JSON.stringify({ id: WS_ID_SNACKBAR, src: 'Matterbridge', dst: 'Frontend', params: { message, timeout, severity } }));
|
|
1560
|
+
}
|
|
1561
|
+
});
|
|
1562
|
+
}
|
|
1563
|
+
wssSendCloseSnackbarMessage(message) {
|
|
1564
|
+
this.log.debug('Sending a close snackbar message to all connected clients');
|
|
1565
|
+
this.webSocketServer?.clients.forEach((client) => {
|
|
1566
|
+
if (client.readyState === WebSocket.OPEN) {
|
|
1567
|
+
client.send(JSON.stringify({ id: WS_ID_CLOSE_SNACKBAR, src: 'Matterbridge', dst: 'Frontend', params: { message } }));
|
|
1656
1568
|
}
|
|
1657
1569
|
});
|
|
1658
1570
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"files": {
|
|
3
3
|
"main.css": "./static/css/main.944b63c3.css",
|
|
4
|
-
"main.js": "./static/js/main.
|
|
4
|
+
"main.js": "./static/js/main.2093c348.js",
|
|
5
5
|
"static/js/453.d855a71b.chunk.js": "./static/js/453.d855a71b.chunk.js",
|
|
6
6
|
"static/media/roboto-latin-700-normal.woff2": "./static/media/roboto-latin-700-normal.c4d6cab43bec89049809.woff2",
|
|
7
7
|
"static/media/roboto-latin-500-normal.woff2": "./static/media/roboto-latin-500-normal.599f66a60bdf974e578e.woff2",
|
|
@@ -77,11 +77,11 @@
|
|
|
77
77
|
"static/media/roboto-greek-ext-300-normal.woff": "./static/media/roboto-greek-ext-300-normal.60729cafbded24073dfb.woff",
|
|
78
78
|
"index.html": "./index.html",
|
|
79
79
|
"main.944b63c3.css.map": "./static/css/main.944b63c3.css.map",
|
|
80
|
-
"main.
|
|
80
|
+
"main.2093c348.js.map": "./static/js/main.2093c348.js.map",
|
|
81
81
|
"453.d855a71b.chunk.js.map": "./static/js/453.d855a71b.chunk.js.map"
|
|
82
82
|
},
|
|
83
83
|
"entrypoints": [
|
|
84
84
|
"static/css/main.944b63c3.css",
|
|
85
|
-
"static/js/main.
|
|
85
|
+
"static/js/main.2093c348.js"
|
|
86
86
|
]
|
|
87
87
|
}
|
|
@@ -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.2093c348.js"></script><link href="./static/css/main.944b63c3.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div></body></html>
|