@matterbridge/core 3.9.2-dev-20260620-9802e5b → 3.9.2-dev-20260621-75283ba
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/dist/backend.d.ts +3 -3
- package/dist/backend.js +8 -5
- package/dist/backendExpress.js +6 -6
- package/dist/backendWsServer.d.ts +5 -2
- package/dist/backendWsServer.js +24 -3
- package/dist/frontend.d.ts +5 -2
- package/dist/frontend.js +39 -4
- package/dist/helpers.js +1 -1
- package/dist/matterbridge.d.ts +7 -4
- package/dist/matterbridge.js +297 -208
- package/package.json +7 -7
package/dist/backend.d.ts
CHANGED
|
@@ -29,9 +29,9 @@ export declare class Backend extends EventEmitter<BackendEvents> {
|
|
|
29
29
|
private broadcastMsgHandler;
|
|
30
30
|
start(port?: number): Promise<void>;
|
|
31
31
|
stop(): Promise<void>;
|
|
32
|
-
getApiSettings(): ApiSettings
|
|
33
|
-
getApiPlugins(): ApiPlugin[]
|
|
34
|
-
getApiDevices(
|
|
32
|
+
getApiSettings(): Promise<ApiSettings>;
|
|
33
|
+
getApiPlugins(): Promise<ApiPlugin[]>;
|
|
34
|
+
getApiDevices(pluginName?: string): Promise<ApiDevice[]>;
|
|
35
35
|
generateDiagnostic(): Promise<void>;
|
|
36
36
|
}
|
|
37
37
|
export {};
|
package/dist/backend.js
CHANGED
|
@@ -292,13 +292,16 @@ export class Backend extends EventEmitter {
|
|
|
292
292
|
this.backendExpress = undefined;
|
|
293
293
|
this.log.debug('Backend stopped');
|
|
294
294
|
}
|
|
295
|
-
getApiSettings() {
|
|
296
|
-
|
|
295
|
+
async getApiSettings() {
|
|
296
|
+
const response = await this.server.fetch({ type: 'matterbridge_apisettings', src: 'frontend', dst: 'matterbridge', params: undefined });
|
|
297
|
+
return response.result.data;
|
|
297
298
|
}
|
|
298
|
-
getApiPlugins() {
|
|
299
|
-
|
|
299
|
+
async getApiPlugins() {
|
|
300
|
+
const response = await this.server.fetch({ type: 'plugins_apipluginarray', src: 'frontend', dst: 'plugins', params: undefined });
|
|
301
|
+
return response.result.plugins;
|
|
300
302
|
}
|
|
301
|
-
getApiDevices(
|
|
303
|
+
async getApiDevices(pluginName) {
|
|
304
|
+
const _response = await this.server.fetch({ type: 'devices_basearray', src: 'frontend', dst: 'devices', params: { pluginName } });
|
|
302
305
|
return [];
|
|
303
306
|
}
|
|
304
307
|
async generateDiagnostic() { }
|
package/dist/backendExpress.js
CHANGED
|
@@ -131,23 +131,23 @@ export class BackendExpress {
|
|
|
131
131
|
};
|
|
132
132
|
res.status(200).json(memoryReport);
|
|
133
133
|
});
|
|
134
|
-
this.expressApp.get('/api/settings', (req, res) => {
|
|
134
|
+
this.expressApp.get('/api/settings', async (req, res) => {
|
|
135
135
|
this.log.debug('The frontend sent /api/settings');
|
|
136
136
|
if (!this.validateReq(req, res))
|
|
137
137
|
return;
|
|
138
|
-
res.json(this.backend.getApiSettings());
|
|
138
|
+
res.json(await this.backend.getApiSettings());
|
|
139
139
|
});
|
|
140
|
-
this.expressApp.get('/api/plugins', (req, res) => {
|
|
140
|
+
this.expressApp.get('/api/plugins', async (req, res) => {
|
|
141
141
|
this.log.debug('The frontend sent /api/plugins');
|
|
142
142
|
if (!this.validateReq(req, res))
|
|
143
143
|
return;
|
|
144
|
-
res.json(this.backend.getApiPlugins());
|
|
144
|
+
res.json(await this.backend.getApiPlugins());
|
|
145
145
|
});
|
|
146
|
-
this.expressApp.get('/api/devices', (req, res) => {
|
|
146
|
+
this.expressApp.get('/api/devices', async (req, res) => {
|
|
147
147
|
this.log.debug('The frontend sent /api/devices');
|
|
148
148
|
if (!this.validateReq(req, res))
|
|
149
149
|
return;
|
|
150
|
-
res.json(this.backend.getApiDevices());
|
|
150
|
+
res.json(await this.backend.getApiDevices());
|
|
151
151
|
});
|
|
152
152
|
this.expressApp.get('/api/view-mblog', async (req, res) => {
|
|
153
153
|
this.log.debug('The frontend sent /api/view-mblog');
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { EndpointNumber } from '@matter/types/datatype';
|
|
2
|
-
import type { ApiMatter, RefreshRequiredChanged, SharedMatterbridge, WsMessageBroadcast } from '@matterbridge/types';
|
|
2
|
+
import type { ApiMatter, BridgeStatus, PluginStatusUpdate, RefreshRequiredChanged, SharedMatterbridge, WsMessageBroadcast } from '@matterbridge/types';
|
|
3
3
|
import { WebSocketServer } from 'ws';
|
|
4
4
|
import type { Backend } from './backend.js';
|
|
5
5
|
export declare class BackendWsServer {
|
|
@@ -25,7 +25,10 @@ export declare class BackendWsServer {
|
|
|
25
25
|
}): void;
|
|
26
26
|
wssSendRestartRequired(snackbar?: boolean, fixed?: boolean): void;
|
|
27
27
|
wssSendRestartNotRequired(snackbar?: boolean): void;
|
|
28
|
-
wssSendUpdateRequired(devVersion?: boolean): void;
|
|
28
|
+
wssSendUpdateRequired(version: string, devVersion?: boolean): void;
|
|
29
|
+
wssSendPluginUpdateRequired(plugin: string, version: string, devVersion?: boolean): void;
|
|
30
|
+
wssSendPluginStatusUpdate(plugin: string, status: PluginStatusUpdate): void;
|
|
31
|
+
wssSendMatterbridgeStatusUpdate(status: BridgeStatus): void;
|
|
29
32
|
wssSendCpuUpdate(cpuUsage: number, processCpuUsage: number): void;
|
|
30
33
|
wssSendMemoryUpdate(totalMemory: string, freeMemory: string, rss: string, heapTotal: string, heapUsed: string, external: string, arrayBuffers: string): void;
|
|
31
34
|
wssSendUptimeUpdate(systemUptime: string, processUptime: string): void;
|
package/dist/backendWsServer.js
CHANGED
|
@@ -232,12 +232,33 @@ export class BackendWsServer {
|
|
|
232
232
|
this.wssSendCloseSnackbarMessage(`Restart required`);
|
|
233
233
|
this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'restart_not_required', success: true });
|
|
234
234
|
}
|
|
235
|
-
wssSendUpdateRequired(devVersion = false) {
|
|
235
|
+
wssSendUpdateRequired(version, devVersion = false) {
|
|
236
236
|
if (!this.hasActiveClients())
|
|
237
237
|
return;
|
|
238
238
|
if (this.verbose)
|
|
239
|
-
this.log.debug('Sending
|
|
240
|
-
this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'update_required', success: true, response: { devVersion } });
|
|
239
|
+
this.log.debug('Sending a matterbridge version update required message to all connected clients');
|
|
240
|
+
this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'update_required', success: true, response: { version, devVersion } });
|
|
241
|
+
}
|
|
242
|
+
wssSendPluginUpdateRequired(plugin, version, devVersion = false) {
|
|
243
|
+
if (!this.hasActiveClients())
|
|
244
|
+
return;
|
|
245
|
+
if (this.verbose)
|
|
246
|
+
this.log.debug('Sending a plugin version update required message to all connected clients');
|
|
247
|
+
this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'plugin_update_required', success: true, response: { plugin, version, devVersion } });
|
|
248
|
+
}
|
|
249
|
+
wssSendPluginStatusUpdate(plugin, status) {
|
|
250
|
+
if (!this.hasActiveClients())
|
|
251
|
+
return;
|
|
252
|
+
if (this.verbose)
|
|
253
|
+
this.log.debug('Sending a plugin status update message to all connected clients');
|
|
254
|
+
this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'plugin_status_update', success: true, response: { plugin, status } });
|
|
255
|
+
}
|
|
256
|
+
wssSendMatterbridgeStatusUpdate(status) {
|
|
257
|
+
if (!this.hasActiveClients())
|
|
258
|
+
return;
|
|
259
|
+
if (this.verbose)
|
|
260
|
+
this.log.debug('Sending a matterbridge status update message to all connected clients');
|
|
261
|
+
this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'matterbridge_status_update', success: true, response: { status } });
|
|
241
262
|
}
|
|
242
263
|
wssSendCpuUpdate(cpuUsage, processCpuUsage) {
|
|
243
264
|
if (!this.hasActiveClients())
|
package/dist/frontend.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import EventEmitter from 'node:events';
|
|
2
2
|
import { type EndpointNumber } from '@matter/types/datatype';
|
|
3
|
-
import type { ApiClusters, ApiDevice, ApiMatter, ApiPlugin, ApiSettings, RefreshRequiredChanged, WsMessageBroadcast } from '@matterbridge/types';
|
|
3
|
+
import type { ApiClusters, ApiDevice, ApiMatter, ApiPlugin, ApiSettings, BridgeStatus, PluginStatusUpdate, RefreshRequiredChanged, WsMessageBroadcast } from '@matterbridge/types';
|
|
4
4
|
import { LogLevel } from 'node-ansi-logger';
|
|
5
5
|
import type { Matterbridge } from './matterbridge.js';
|
|
6
6
|
interface FrontendEvents {
|
|
@@ -57,7 +57,10 @@ export declare class Frontend extends EventEmitter<FrontendEvents> {
|
|
|
57
57
|
}): void;
|
|
58
58
|
wssSendRestartRequired(snackbar?: boolean, fixed?: boolean): void;
|
|
59
59
|
wssSendRestartNotRequired(snackbar?: boolean): void;
|
|
60
|
-
wssSendUpdateRequired(devVersion?: boolean): void;
|
|
60
|
+
wssSendUpdateRequired(version: string, devVersion?: boolean): void;
|
|
61
|
+
wssSendPluginUpdateRequired(plugin: string, version: string, devVersion?: boolean): void;
|
|
62
|
+
wssSendPluginStatusUpdate(plugin: string, status: PluginStatusUpdate): void;
|
|
63
|
+
wssSendMatterbridgeStatusUpdate(status: BridgeStatus): void;
|
|
61
64
|
wssSendCpuUpdate(cpuUsage: number, processCpuUsage: number): void;
|
|
62
65
|
wssSendMemoryUpdate(totalMemory: string, freeMemory: string, rss: string, heapTotal: string, heapUsed: string, external: string, arrayBuffers: string): void;
|
|
63
66
|
wssSendUptimeUpdate(systemUptime: string, processUptime: string): void;
|
package/dist/frontend.js
CHANGED
|
@@ -93,13 +93,29 @@ export class Frontend extends EventEmitter {
|
|
|
93
93
|
this.server.respond({ ...msg, result: { success: true } });
|
|
94
94
|
break;
|
|
95
95
|
case 'frontend_updaterequired':
|
|
96
|
-
this.wssSendUpdateRequired(msg.params.devVersion);
|
|
96
|
+
this.wssSendUpdateRequired(msg.params.version, msg.params.devVersion);
|
|
97
|
+
this.server.respond({ ...msg, result: { success: true } });
|
|
98
|
+
break;
|
|
99
|
+
case 'frontend_pluginupdaterequired':
|
|
100
|
+
this.wssSendPluginUpdateRequired(msg.params.plugin, msg.params.version, msg.params.devVersion);
|
|
101
|
+
this.server.respond({ ...msg, result: { success: true } });
|
|
102
|
+
break;
|
|
103
|
+
case 'frontend_pluginstatusupdate':
|
|
104
|
+
this.wssSendPluginStatusUpdate(msg.params.plugin, msg.params.status);
|
|
105
|
+
this.server.respond({ ...msg, result: { success: true } });
|
|
106
|
+
break;
|
|
107
|
+
case 'frontend_matterbridgestatusupdate':
|
|
108
|
+
this.wssSendMatterbridgeStatusUpdate(msg.params.status);
|
|
97
109
|
this.server.respond({ ...msg, result: { success: true } });
|
|
98
110
|
break;
|
|
99
111
|
case 'frontend_snackbarmessage':
|
|
100
112
|
this.wssSendSnackbarMessage(msg.params.message, msg.params.timeout, msg.params.severity);
|
|
101
113
|
this.server.respond({ ...msg, result: { success: true } });
|
|
102
114
|
break;
|
|
115
|
+
case 'frontend_closesnackbarmessage':
|
|
116
|
+
this.wssSendCloseSnackbarMessage(msg.params.message);
|
|
117
|
+
this.server.respond({ ...msg, result: { success: true } });
|
|
118
|
+
break;
|
|
103
119
|
case 'frontend_attributechanged':
|
|
104
120
|
this.wssSendAttributeChangedMessage(msg.params.plugin, msg.params.serialNumber, msg.params.uniqueId, msg.params.number, msg.params.id, msg.params.cluster, msg.params.attribute, msg.params.value);
|
|
105
121
|
this.server.respond({ ...msg, result: { success: true } });
|
|
@@ -931,6 +947,7 @@ export class Frontend extends EventEmitter {
|
|
|
931
947
|
bridgeMode: this.matterbridge.bridgeMode,
|
|
932
948
|
restartMode: this.matterbridge.restartMode,
|
|
933
949
|
virtualMode: this.matterbridge.virtualMode,
|
|
950
|
+
bridgeStatus: this.matterbridge.bridgeStatus,
|
|
934
951
|
profile: this.matterbridge.profile,
|
|
935
952
|
loggerLevel: this.matterbridge.logLevel,
|
|
936
953
|
fileLogger: this.matterbridge.fileLogger,
|
|
@@ -2113,12 +2130,30 @@ export class Frontend extends EventEmitter {
|
|
|
2113
2130
|
this.wssSendCloseSnackbarMessage(`Restart required`);
|
|
2114
2131
|
this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'restart_not_required', success: true });
|
|
2115
2132
|
}
|
|
2116
|
-
wssSendUpdateRequired(devVersion = false) {
|
|
2133
|
+
wssSendUpdateRequired(version, devVersion = false) {
|
|
2117
2134
|
if (!this.listening || this.webSocketServer?.clients.size === 0)
|
|
2118
2135
|
return;
|
|
2119
|
-
this.log.debug('Sending
|
|
2136
|
+
this.log.debug('Sending a matterbridge version update required message to all connected clients');
|
|
2120
2137
|
this.updateRequired = true;
|
|
2121
|
-
this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'update_required', success: true, response: { devVersion } });
|
|
2138
|
+
this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'update_required', success: true, response: { version, devVersion } });
|
|
2139
|
+
}
|
|
2140
|
+
wssSendPluginUpdateRequired(plugin, version, devVersion = false) {
|
|
2141
|
+
if (!this.listening || this.webSocketServer?.clients.size === 0)
|
|
2142
|
+
return;
|
|
2143
|
+
this.log.debug('Sending a plugin version update required message to all connected clients');
|
|
2144
|
+
this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'plugin_update_required', success: true, response: { plugin, version, devVersion } });
|
|
2145
|
+
}
|
|
2146
|
+
wssSendPluginStatusUpdate(plugin, status) {
|
|
2147
|
+
if (!this.listening || this.webSocketServer?.clients.size === 0)
|
|
2148
|
+
return;
|
|
2149
|
+
this.log.debug('Sending a plugin status update message to all connected clients');
|
|
2150
|
+
this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'plugin_status_update', success: true, response: { plugin, status } });
|
|
2151
|
+
}
|
|
2152
|
+
wssSendMatterbridgeStatusUpdate(status) {
|
|
2153
|
+
if (!this.listening || this.webSocketServer?.clients.size === 0)
|
|
2154
|
+
return;
|
|
2155
|
+
this.log.debug('Sending a matterbridge status update message to all connected clients');
|
|
2156
|
+
this.wssBroadcastMessage({ id: 0, src: 'Matterbridge', dst: 'Frontend', method: 'matterbridge_status_update', success: true, response: { status } });
|
|
2122
2157
|
}
|
|
2123
2158
|
wssSendCpuUpdate(cpuUsage, processCpuUsage) {
|
|
2124
2159
|
if (!this.listening || this.webSocketServer?.clients.size === 0)
|
package/dist/helpers.js
CHANGED
|
@@ -61,7 +61,7 @@ export async function addVirtualDevices(matterbridge, aggregatorEndpoint) {
|
|
|
61
61
|
if (matterbridge.virtualMode !== 'disabled' && matterbridge.bridgeMode === 'bridge' && aggregatorEndpoint) {
|
|
62
62
|
matterbridge.log.notice(`Creating virtual devices for Matterbridge server node...`);
|
|
63
63
|
await addVirtualDevice(aggregatorEndpoint, 'Restart Matterbridge', matterbridge.virtualMode, async () => {
|
|
64
|
-
if (matterbridge.restartMode === '')
|
|
64
|
+
if (matterbridge.restartMode === 'none')
|
|
65
65
|
await matterbridge.restartProcess();
|
|
66
66
|
else
|
|
67
67
|
await matterbridge.shutdownProcess();
|
package/dist/matterbridge.d.ts
CHANGED
|
@@ -5,7 +5,7 @@ import { Endpoint, ServerNode } from '@matter/node';
|
|
|
5
5
|
import { AggregatorEndpoint } from '@matter/node/endpoints/aggregator';
|
|
6
6
|
import { type DeviceCertification } from '@matter/protocol';
|
|
7
7
|
import { DeviceTypeId, VendorId } from '@matter/types/datatype';
|
|
8
|
-
import type { ApiMatter, MaybePromise, PlatformMatterbridge, SharedMatterbridge, SystemInformation } from '@matterbridge/types';
|
|
8
|
+
import type { ApiMatter, ApiSettings, BridgeMode, BridgeStatus, MaybePromise, PlatformMatterbridge, RestartMode, SharedMatterbridge, SystemInformation, VirtualMode } from '@matterbridge/types';
|
|
9
9
|
import { AnsiLogger, LogLevel } from 'node-ansi-logger';
|
|
10
10
|
import { type NodeStorage, NodeStorageManager } from 'node-persist-manager';
|
|
11
11
|
import { DeviceManager } from './deviceManager.js';
|
|
@@ -50,9 +50,10 @@ export declare class Matterbridge extends EventEmitter<MatterbridgeEvents> {
|
|
|
50
50
|
dockerVersion: string | undefined;
|
|
51
51
|
dockerLatestVersion: string | undefined;
|
|
52
52
|
dockerDevVersion: string | undefined;
|
|
53
|
-
bridgeMode:
|
|
54
|
-
restartMode:
|
|
55
|
-
virtualMode:
|
|
53
|
+
bridgeMode: BridgeMode;
|
|
54
|
+
restartMode: RestartMode;
|
|
55
|
+
virtualMode: VirtualMode;
|
|
56
|
+
bridgeStatus: BridgeStatus;
|
|
56
57
|
readonly profile: string | undefined;
|
|
57
58
|
readonly log: AnsiLogger;
|
|
58
59
|
logLevel: LogLevel;
|
|
@@ -116,6 +117,7 @@ export declare class Matterbridge extends EventEmitter<MatterbridgeEvents> {
|
|
|
116
117
|
destroy(): void;
|
|
117
118
|
getPlatformMatterbridge(): PlatformMatterbridge;
|
|
118
119
|
getSharedMatterbridge(): SharedMatterbridge;
|
|
120
|
+
getApiSettings(): ApiSettings;
|
|
119
121
|
private msgHandler;
|
|
120
122
|
static loadInstance(initialize?: boolean): Promise<Matterbridge>;
|
|
121
123
|
private initialize;
|
|
@@ -134,6 +136,7 @@ export declare class Matterbridge extends EventEmitter<MatterbridgeEvents> {
|
|
|
134
136
|
shutdownProcessAndReset(): Promise<void>;
|
|
135
137
|
shutdownProcessAndFactoryReset(): Promise<void>;
|
|
136
138
|
private cleanup;
|
|
139
|
+
private sendPluginStatusUpdate;
|
|
137
140
|
private startBridge;
|
|
138
141
|
private startChildbridge;
|
|
139
142
|
private startController;
|
package/dist/matterbridge.js
CHANGED
|
@@ -17,7 +17,7 @@ import { dev, MATTER_LOGGER_FILE, MATTER_STORAGE_DIR, MATTERBRIDGE_LOGGER_FILE,
|
|
|
17
17
|
import { getIntParameter, getParameter, hasAnyParameter, hasParameter } from '@matterbridge/utils/cli';
|
|
18
18
|
import { copyDirectory } from '@matterbridge/utils/copy-dir';
|
|
19
19
|
import { createDirectory } from '@matterbridge/utils/create-dir';
|
|
20
|
-
import { inspectError, logError } from '@matterbridge/utils/error';
|
|
20
|
+
import { getErrorMessage, inspectError, logError } from '@matterbridge/utils/error';
|
|
21
21
|
import { formatBytes, formatPercent, formatUptime } from '@matterbridge/utils/format';
|
|
22
22
|
import { logModuleLoaded } from '@matterbridge/utils/loader';
|
|
23
23
|
import { excludedInterfaceNamePattern } from '@matterbridge/utils/network';
|
|
@@ -72,9 +72,10 @@ export class Matterbridge extends EventEmitter {
|
|
|
72
72
|
dockerVersion;
|
|
73
73
|
dockerLatestVersion;
|
|
74
74
|
dockerDevVersion;
|
|
75
|
-
bridgeMode = '';
|
|
76
|
-
restartMode = '';
|
|
75
|
+
bridgeMode = 'none';
|
|
76
|
+
restartMode = 'none';
|
|
77
77
|
virtualMode = 'outlet';
|
|
78
|
+
bridgeStatus = 'inactive';
|
|
78
79
|
profile = getParameter('profile');
|
|
79
80
|
log = new AnsiLogger({
|
|
80
81
|
logName: 'Matterbridge',
|
|
@@ -209,6 +210,49 @@ export class Matterbridge extends EventEmitter {
|
|
|
209
210
|
passcode: this.passcode,
|
|
210
211
|
};
|
|
211
212
|
}
|
|
213
|
+
getApiSettings() {
|
|
214
|
+
return {
|
|
215
|
+
systemInformation: { ...this.systemInformation },
|
|
216
|
+
matterbridgeInformation: {
|
|
217
|
+
rootDirectory: this.rootDirectory,
|
|
218
|
+
homeDirectory: this.homeDirectory,
|
|
219
|
+
matterbridgeDirectory: this.matterbridgeDirectory,
|
|
220
|
+
matterbridgePluginDirectory: this.matterbridgePluginDirectory,
|
|
221
|
+
matterbridgeCertDirectory: this.matterbridgeCertDirectory,
|
|
222
|
+
globalModulesDirectory: this.globalModulesDirectory,
|
|
223
|
+
matterbridgeVersion: this.matterbridgeVersion,
|
|
224
|
+
matterbridgeLatestVersion: this.matterbridgeLatestVersion,
|
|
225
|
+
matterbridgeDevVersion: this.matterbridgeDevVersion,
|
|
226
|
+
frontendVersion: this.frontendVersion,
|
|
227
|
+
dockerDev: this.dockerDev,
|
|
228
|
+
dockerVersion: this.dockerVersion,
|
|
229
|
+
dockerLatestVersion: this.dockerLatestVersion,
|
|
230
|
+
dockerDevVersion: this.dockerDevVersion,
|
|
231
|
+
bridgeMode: this.bridgeMode,
|
|
232
|
+
restartMode: this.restartMode,
|
|
233
|
+
virtualMode: this.virtualMode,
|
|
234
|
+
bridgeStatus: this.bridgeStatus,
|
|
235
|
+
profile: this.profile,
|
|
236
|
+
readOnly: hasParameter('readonly'),
|
|
237
|
+
shellyBoard: false,
|
|
238
|
+
shellySysUpdate: false,
|
|
239
|
+
shellyMainUpdate: false,
|
|
240
|
+
loggerLevel: this.logLevel,
|
|
241
|
+
fileLogger: this.fileLogger,
|
|
242
|
+
matterLoggerLevel: this.matterLogLevel,
|
|
243
|
+
matterFileLogger: this.matterFileLogger,
|
|
244
|
+
matterMdnsInterface: this.mdnsInterface,
|
|
245
|
+
matterIpv4Address: this.ipv4Address,
|
|
246
|
+
matterIpv6Address: this.ipv6Address,
|
|
247
|
+
matterPort: this.port ?? 5540,
|
|
248
|
+
matterDiscriminator: this.discriminator,
|
|
249
|
+
matterPasscode: this.passcode,
|
|
250
|
+
restartRequired: false,
|
|
251
|
+
fixedRestartRequired: false,
|
|
252
|
+
updateRequired: false,
|
|
253
|
+
},
|
|
254
|
+
};
|
|
255
|
+
}
|
|
212
256
|
async msgHandler(msg) {
|
|
213
257
|
if (this.server.isWorkerRequest(msg) && (msg.dst === 'all' || msg.dst === 'matterbridge')) {
|
|
214
258
|
if (this.verbose)
|
|
@@ -249,6 +293,9 @@ export class Matterbridge extends EventEmitter {
|
|
|
249
293
|
case 'matterbridge_shared':
|
|
250
294
|
this.server.respond({ ...msg, result: { data: this.getSharedMatterbridge(), success: true } });
|
|
251
295
|
break;
|
|
296
|
+
case 'matterbridge_apisettings':
|
|
297
|
+
this.server.respond({ ...msg, result: { data: this.getApiSettings(), success: true } });
|
|
298
|
+
break;
|
|
252
299
|
case 'matterbridge_start_plugin_server':
|
|
253
300
|
{
|
|
254
301
|
const plugin = this.plugins.get(msg.params.pluginName);
|
|
@@ -299,7 +346,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
299
346
|
const packageName = msg.result.packageName.replace(/@.*$/, '');
|
|
300
347
|
if (packageName === 'matterbridge') {
|
|
301
348
|
this.log.info('Matterbridge has been updated. Full restart required.');
|
|
302
|
-
if (this.restartMode !== '')
|
|
349
|
+
if (this.restartMode !== 'none')
|
|
303
350
|
await this.cleanup('updating...', false);
|
|
304
351
|
}
|
|
305
352
|
}
|
|
@@ -440,7 +487,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
440
487
|
}
|
|
441
488
|
}
|
|
442
489
|
catch (error) {
|
|
443
|
-
this.log.debug(`Pairing file ${CYAN}${pairingFilePath}${db} not found: ${error
|
|
490
|
+
this.log.debug(`Pairing file ${CYAN}${pairingFilePath}${db} not found: ${getErrorMessage(error)}`);
|
|
444
491
|
}
|
|
445
492
|
await this.nodeContext.set('matterport', this.port);
|
|
446
493
|
await this.nodeContext.set('matterpasscode', this.passcode);
|
|
@@ -549,14 +596,14 @@ export class Matterbridge extends EventEmitter {
|
|
|
549
596
|
this.mdnsInterface = undefined;
|
|
550
597
|
}
|
|
551
598
|
if (this.mdnsInterface) {
|
|
552
|
-
if (
|
|
599
|
+
if (availableInterfaceNames.includes(this.mdnsInterface)) {
|
|
600
|
+
this.log.info(`Using mdnsinterface ${CYAN}${this.mdnsInterface}${nf} for the Matter MdnsBroadcaster.`);
|
|
601
|
+
}
|
|
602
|
+
else {
|
|
553
603
|
this.log.error(`Invalid mdnsinterface: ${this.mdnsInterface}. Available interfaces are: ${availableInterfaceNames.join(', ')}. Using all available interfaces.`);
|
|
554
604
|
this.mdnsInterface = undefined;
|
|
555
605
|
await this.nodeContext.remove('mattermdnsinterface');
|
|
556
606
|
}
|
|
557
|
-
else {
|
|
558
|
-
this.log.info(`Using mdnsinterface ${CYAN}${this.mdnsInterface}${nf} for the Matter MdnsBroadcaster.`);
|
|
559
|
-
}
|
|
560
607
|
}
|
|
561
608
|
if (this.mdnsInterface)
|
|
562
609
|
this.environment.vars.set('mdns.networkInterface', this.mdnsInterface);
|
|
@@ -683,12 +730,12 @@ export class Matterbridge extends EventEmitter {
|
|
|
683
730
|
await plugin.nodeContext.set('author', plugin.author);
|
|
684
731
|
}
|
|
685
732
|
await this.logNodeAndSystemInfo();
|
|
686
|
-
this.log.notice(
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
733
|
+
this.log.notice('Matterbridge version ' + this.matterbridgeVersion + ' ' +
|
|
734
|
+
(hasParameter('bridge') || (!hasParameter('childbridge') && (await this.nodeContext?.get('bridgeMode', '')) === 'bridge') ? 'mode bridge ' : '') +
|
|
735
|
+
(hasParameter('childbridge') || (!hasParameter('bridge') && (await this.nodeContext?.get('bridgeMode', '')) === 'childbridge') ? 'mode childbridge ' : '') +
|
|
736
|
+
(hasParameter('controller') ? 'mode controller ' : '') +
|
|
737
|
+
(this.restartMode === 'none' ? '' : 'restart mode ' + this.restartMode + ' ') +
|
|
738
|
+
'running on ' + this.systemInformation.osType + ' (v.' + this.systemInformation.osRelease + ') platform ' + this.systemInformation.osPlatform + ' arch ' + this.systemInformation.osArch);
|
|
692
739
|
const minNodeVersion = 20;
|
|
693
740
|
const nodeVersion = process.versions.node;
|
|
694
741
|
const [versionMajor] = nodeVersion.split('.').map(Number);
|
|
@@ -705,13 +752,13 @@ export class Matterbridge extends EventEmitter {
|
|
|
705
752
|
this.log.info(`│ Registered plugins (${this.plugins.length})`);
|
|
706
753
|
let index = 0;
|
|
707
754
|
for (const plugin of this.plugins) {
|
|
708
|
-
if (index
|
|
709
|
-
this.log.info(
|
|
710
|
-
this.log.info(
|
|
755
|
+
if (index === this.plugins.length - 1) {
|
|
756
|
+
this.log.info(`└─┬─ plugin ${plg}${plugin.name}${nf}: "${plg}${BRIGHT}${plugin.description}${RESET}${nf}" type: ${typ}${plugin.type}${nf} version: ${plg}${plugin.version}${nf} ${plugin.enabled ? GREEN : RED}${plugin.enabled ? 'enabled' : 'disabled'}${nf}`);
|
|
757
|
+
this.log.info(` └─ entry ${UNDERLINE}${db}${plugin.path}${UNDERLINEOFF}${db}`);
|
|
711
758
|
}
|
|
712
759
|
else {
|
|
713
|
-
this.log.info(
|
|
714
|
-
this.log.info(
|
|
760
|
+
this.log.info(`├─┬─ plugin ${plg}${plugin.name}${nf}: "${plg}${BRIGHT}${plugin.description}${RESET}${nf}" type: ${typ}${plugin.type}${nf} version: ${plg}${plugin.version}${nf} ${plugin.enabled ? GREEN : RED}${plugin.enabled ? 'enabled' : 'disabled'}${nf}`);
|
|
761
|
+
this.log.info(`│ └─ entry ${UNDERLINE}${db}${plugin.path}${UNDERLINEOFF}${db}`);
|
|
715
762
|
}
|
|
716
763
|
index++;
|
|
717
764
|
}
|
|
@@ -741,27 +788,31 @@ export class Matterbridge extends EventEmitter {
|
|
|
741
788
|
this.shutdown = true;
|
|
742
789
|
return;
|
|
743
790
|
}
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
791
|
+
const addPlugin = getParameter('add');
|
|
792
|
+
if (addPlugin) {
|
|
793
|
+
this.log.debug(`Adding plugin ${addPlugin}`);
|
|
794
|
+
await this.plugins.add(addPlugin);
|
|
747
795
|
this.shutdown = true;
|
|
748
796
|
return;
|
|
749
797
|
}
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
798
|
+
const removePlugin = getParameter('remove');
|
|
799
|
+
if (removePlugin) {
|
|
800
|
+
this.log.debug(`Removing plugin ${removePlugin}`);
|
|
801
|
+
await this.plugins.remove(removePlugin);
|
|
753
802
|
this.shutdown = true;
|
|
754
803
|
return;
|
|
755
804
|
}
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
805
|
+
const enablePlugin = getParameter('enable');
|
|
806
|
+
if (enablePlugin) {
|
|
807
|
+
this.log.debug(`Enabling plugin ${enablePlugin}`);
|
|
808
|
+
await this.plugins.enable(enablePlugin);
|
|
759
809
|
this.shutdown = true;
|
|
760
810
|
return;
|
|
761
811
|
}
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
812
|
+
const disablePlugin = getParameter('disable');
|
|
813
|
+
if (disablePlugin) {
|
|
814
|
+
this.log.debug(`Disabling plugin ${disablePlugin}`);
|
|
815
|
+
await this.plugins.disable(disablePlugin);
|
|
765
816
|
this.shutdown = true;
|
|
766
817
|
return;
|
|
767
818
|
}
|
|
@@ -783,8 +834,8 @@ export class Matterbridge extends EventEmitter {
|
|
|
783
834
|
}
|
|
784
835
|
}
|
|
785
836
|
catch (error) {
|
|
786
|
-
this.log.fatal(`Fatal error creating matter storage: ${error
|
|
787
|
-
throw new Error(`Fatal error creating matter storage: ${error
|
|
837
|
+
this.log.fatal(`Fatal error creating matter storage: ${getErrorMessage(error)}`);
|
|
838
|
+
throw new Error(`Fatal error creating matter storage: ${getErrorMessage(error)}`, { cause: error });
|
|
788
839
|
}
|
|
789
840
|
if (hasParameter('reset') && getParameter('reset') === undefined) {
|
|
790
841
|
this.initialized = true;
|
|
@@ -792,15 +843,13 @@ export class Matterbridge extends EventEmitter {
|
|
|
792
843
|
this.shutdown = true;
|
|
793
844
|
return;
|
|
794
845
|
}
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
846
|
+
const resetPlugin = getParameter('reset');
|
|
847
|
+
if (hasParameter('reset') && resetPlugin !== undefined) {
|
|
848
|
+
this.log.debug(`Reset plugin ${resetPlugin}`);
|
|
849
|
+
const plugin = this.plugins.get(resetPlugin);
|
|
798
850
|
if (plugin) {
|
|
799
851
|
const matterStorageManager = await this.matterStorageService?.open(plugin.name);
|
|
800
|
-
if (
|
|
801
|
-
this.log.error(`Plugin ${plg}${plugin.name}${er} storageManager not found`);
|
|
802
|
-
}
|
|
803
|
-
else {
|
|
852
|
+
if (matterStorageManager) {
|
|
804
853
|
await matterStorageManager.createContext('events')?.clearAll();
|
|
805
854
|
await matterStorageManager.createContext('fabrics')?.clearAll();
|
|
806
855
|
await matterStorageManager.createContext('root')?.clearAll();
|
|
@@ -808,6 +857,9 @@ export class Matterbridge extends EventEmitter {
|
|
|
808
857
|
await matterStorageManager.createContext('persist')?.clearAll();
|
|
809
858
|
this.log.notice(`Reset commissioning for plugin ${plg}${plugin.name}${nt} done! Remove the device from the controller.`);
|
|
810
859
|
}
|
|
860
|
+
else {
|
|
861
|
+
this.log.error(`Plugin ${plg}${plugin.name}${er} storageManager not found`);
|
|
862
|
+
}
|
|
811
863
|
}
|
|
812
864
|
else {
|
|
813
865
|
this.log.warn(`Plugin ${plg}${getParameter('reset')}${wr} not registerd in matterbridge`);
|
|
@@ -845,13 +897,13 @@ export class Matterbridge extends EventEmitter {
|
|
|
845
897
|
}
|
|
846
898
|
if (hasParameter('delay') && os.uptime() <= 60 * 5) {
|
|
847
899
|
const { wait } = await import('@matterbridge/utils/wait');
|
|
848
|
-
const delay = getIntParameter('delay')
|
|
900
|
+
const delay = getIntParameter('delay') ?? 120;
|
|
849
901
|
this.log.warn('Delay switch found with system uptime less then 5 minutes. Waiting for ' + delay + ' seconds before starting matterbridge...');
|
|
850
902
|
await wait(delay * 1000, 'Race condition delay', true);
|
|
851
903
|
}
|
|
852
904
|
if (hasParameter('fixed_delay')) {
|
|
853
905
|
const { wait } = await import('@matterbridge/utils/wait');
|
|
854
|
-
const delay = getIntParameter('fixed_delay')
|
|
906
|
+
const delay = getIntParameter('fixed_delay') ?? 120;
|
|
855
907
|
this.log.warn('Fixed delay switch found. Waiting for ' + delay + ' seconds before starting matterbridge...');
|
|
856
908
|
await wait(delay * 1000, 'Fixed race condition delay', true);
|
|
857
909
|
}
|
|
@@ -899,25 +951,29 @@ export class Matterbridge extends EventEmitter {
|
|
|
899
951
|
this.log.debug(`Registering uncaughtException and unhandledRejection handlers...`);
|
|
900
952
|
process.removeAllListeners('uncaughtException');
|
|
901
953
|
process.removeAllListeners('unhandledRejection');
|
|
902
|
-
this.exceptionHandler =
|
|
903
|
-
const errorMessage = error
|
|
954
|
+
this.exceptionHandler = (error) => {
|
|
955
|
+
const errorMessage = getErrorMessage(error);
|
|
904
956
|
const errorInspect = inspect(error, { depth: 10 });
|
|
905
|
-
this.log.error(`Unhandled Exception detected: ${errorMessage}\nstack: ${errorInspect}
|
|
957
|
+
this.log.error(`Unhandled Exception detected: ${errorMessage}\nstack: ${errorInspect}`);
|
|
906
958
|
};
|
|
907
959
|
process.on('uncaughtException', this.exceptionHandler);
|
|
908
|
-
this.rejectionHandler =
|
|
909
|
-
const errorMessage = reason
|
|
960
|
+
this.rejectionHandler = (reason, _promise) => {
|
|
961
|
+
const errorMessage = getErrorMessage(reason);
|
|
910
962
|
const errorInspect = inspect(reason, { depth: 10 });
|
|
911
|
-
this.log.error(`Unhandled Rejection detected: ${
|
|
963
|
+
this.log.error(`Unhandled Rejection detected: ${errorMessage}\nstack: ${errorInspect}`);
|
|
912
964
|
};
|
|
913
965
|
process.on('unhandledRejection', this.rejectionHandler);
|
|
914
966
|
this.log.debug(`Registering SIGINT and SIGTERM signal handlers...`);
|
|
915
|
-
this.sigintHandler =
|
|
916
|
-
|
|
967
|
+
this.sigintHandler = () => {
|
|
968
|
+
this.cleanup('SIGINT received, cleaning up...').catch((error) => {
|
|
969
|
+
this.log.error(`Error cleaning up on SIGINT: ${getErrorMessage(error)}`);
|
|
970
|
+
});
|
|
917
971
|
};
|
|
918
972
|
process.on('SIGINT', this.sigintHandler);
|
|
919
|
-
this.sigtermHandler =
|
|
920
|
-
|
|
973
|
+
this.sigtermHandler = () => {
|
|
974
|
+
this.cleanup('SIGTERM received, cleaning up...').catch((error) => {
|
|
975
|
+
this.log.error(`Error cleaning up on SIGTERM: ${getErrorMessage(error)}`);
|
|
976
|
+
});
|
|
921
977
|
};
|
|
922
978
|
process.on('SIGTERM', this.sigtermHandler);
|
|
923
979
|
}
|
|
@@ -1152,6 +1208,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
1152
1208
|
this.emit('cleanup_started');
|
|
1153
1209
|
this.hasCleanupStarted = true;
|
|
1154
1210
|
this.log.info(message);
|
|
1211
|
+
this.server.request({ type: 'frontend_matterbridgestatusupdate', src: 'matterbridge', dst: 'frontend', params: { status: (this.bridgeStatus = 'stopping') } });
|
|
1155
1212
|
if (this.startMatterInterval) {
|
|
1156
1213
|
clearInterval(this.startMatterInterval);
|
|
1157
1214
|
this.startMatterInterval = undefined;
|
|
@@ -1300,6 +1357,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
1300
1357
|
unlinkSafe(path.join(this.matterbridgeDirectory, MATTER_STORAGE_DIR, device.deviceName.replace(/[ .]/g, ''), 'root.subscriptions.subscriptions'), this.log);
|
|
1301
1358
|
}
|
|
1302
1359
|
}
|
|
1360
|
+
this.server.request({ type: 'frontend_matterbridgestatusupdate', src: 'matterbridge', dst: 'frontend', params: { status: (this.bridgeStatus = 'stopped') } });
|
|
1303
1361
|
await this.frontend.stop();
|
|
1304
1362
|
this.frontend.destroy();
|
|
1305
1363
|
this.plugins.destroy();
|
|
@@ -1388,93 +1446,119 @@ export class Matterbridge extends EventEmitter {
|
|
|
1388
1446
|
this.log.debug('Cleanup already started...');
|
|
1389
1447
|
}
|
|
1390
1448
|
}
|
|
1449
|
+
sendPluginStatusUpdate(plugin) {
|
|
1450
|
+
this.server.request({
|
|
1451
|
+
type: 'frontend_pluginstatusupdate',
|
|
1452
|
+
src: 'matterbridge',
|
|
1453
|
+
dst: 'frontend',
|
|
1454
|
+
params: {
|
|
1455
|
+
plugin: plugin.name,
|
|
1456
|
+
status: {
|
|
1457
|
+
locked: plugin.locked,
|
|
1458
|
+
error: plugin.error,
|
|
1459
|
+
enabled: plugin.enabled,
|
|
1460
|
+
loaded: plugin.loaded,
|
|
1461
|
+
started: plugin.started,
|
|
1462
|
+
configured: plugin.configured,
|
|
1463
|
+
registeredDevices: plugin.registeredDevices,
|
|
1464
|
+
},
|
|
1465
|
+
},
|
|
1466
|
+
});
|
|
1467
|
+
}
|
|
1391
1468
|
async startBridge() {
|
|
1392
1469
|
if (!this.matterStorageManager)
|
|
1393
1470
|
throw new Error('No storage manager initialized');
|
|
1394
1471
|
if (!this.matterbridgeContext)
|
|
1395
1472
|
throw new Error('No storage context initialized');
|
|
1473
|
+
this.server.request({ type: 'frontend_matterbridgestatusupdate', src: 'matterbridge', dst: 'frontend', params: { status: (this.bridgeStatus = 'starting') } });
|
|
1396
1474
|
this.serverNode = await this.createServerNode(this.matterbridgeContext, this.port ? this.port++ : undefined, this.passcode ? this.passcode++ : undefined, this.discriminator ? this.discriminator++ : undefined);
|
|
1397
1475
|
this.aggregatorNode = await this.createAggregatorNode(this.matterbridgeContext);
|
|
1398
1476
|
await this.serverNode.add(this.aggregatorNode);
|
|
1399
1477
|
await addVirtualDevices(this, this.aggregatorNode);
|
|
1400
|
-
await this.startPlugins();
|
|
1478
|
+
await this.startPlugins(false, true);
|
|
1401
1479
|
this.log.debug('Starting start matter interval in bridge mode...');
|
|
1402
1480
|
this.frontend.wssSendSnackbarMessage(`The bridge is starting...`, 0, 'info');
|
|
1403
1481
|
let failCount = 0;
|
|
1404
|
-
this.startMatterInterval = setInterval(
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
}
|
|
1409
|
-
for (const plugin of this.plugins) {
|
|
1410
|
-
if (!plugin.enabled)
|
|
1411
|
-
continue;
|
|
1412
|
-
if (plugin.error) {
|
|
1413
|
-
clearInterval(this.startMatterInterval);
|
|
1414
|
-
this.startMatterInterval = undefined;
|
|
1415
|
-
this.log.debug('Cleared startMatterInterval interval for Matterbridge for plugin in error state');
|
|
1416
|
-
this.log.error(`The plugin ${plg}${plugin.name}${er} is in error state.`);
|
|
1417
|
-
this.log.error('The bridge will not start until the problem is solved to prevent the controllers from deleting all registered devices.');
|
|
1418
|
-
this.log.error('If you want to start the bridge disable the plugin in error state and restart.');
|
|
1419
|
-
this.frontend.wssSendSnackbarMessage(`The plugin ${plugin.name} is in error state. Check the logs.`, 0, 'error');
|
|
1420
|
-
this.frontend.wssSendSnackbarMessage(`The bridge is offline. Startup halted due to plugin errors.`, 0, 'error');
|
|
1421
|
-
this.frontend.wssSendRefreshRequired('plugins');
|
|
1422
|
-
this.frontend.wssSendCloseSnackbarMessage(`The bridge is starting...`);
|
|
1423
|
-
return;
|
|
1424
|
-
}
|
|
1425
|
-
if (!plugin.loaded || !plugin.started) {
|
|
1426
|
-
this.log.debug(`Waiting (failSafeCount=${failCount}/${this.failCountLimit}) in startMatterInterval interval for plugin ${plg}${plugin.name}${db} loaded: ${plugin.loaded} started: ${plugin.started}...`);
|
|
1427
|
-
failCount++;
|
|
1428
|
-
if (failCount > this.failCountLimit) {
|
|
1429
|
-
this.log.error(`Error waiting for plugin ${plg}${plugin.name}${er} to load and start. Plugin is in error state.`);
|
|
1430
|
-
plugin.error = true;
|
|
1431
|
-
}
|
|
1432
|
-
return;
|
|
1482
|
+
this.startMatterInterval = setInterval(() => {
|
|
1483
|
+
void (async () => {
|
|
1484
|
+
if (failCount && failCount % 10 === 0) {
|
|
1485
|
+
this.frontend.wssSendSnackbarMessage(`The bridge is still starting...`, 10, 'info');
|
|
1433
1486
|
}
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
this.startMatterInterval = undefined;
|
|
1437
|
-
this.log.debug('Cleared startMatterInterval interval in bridge mode');
|
|
1438
|
-
fireAndForget(this.startServerNode(this.serverNode), this.log, 'Start server node');
|
|
1439
|
-
for (const device of this.devices.array()) {
|
|
1440
|
-
if (device.mode === 'server' && device.serverNode) {
|
|
1441
|
-
this.log.debug(`Starting server node for device ${dev}${device.deviceName}${db} in server mode...`);
|
|
1442
|
-
fireAndForget(this.startServerNode(device.serverNode), this.log, `Start server node for device ${device.deviceName}`);
|
|
1443
|
-
}
|
|
1444
|
-
}
|
|
1445
|
-
this.configureTimeout = setTimeout(async () => {
|
|
1446
|
-
for (const plugin of this.plugins.array()) {
|
|
1447
|
-
if (!plugin.enabled || !plugin.loaded || !plugin.started || plugin.error)
|
|
1487
|
+
for (const plugin of this.plugins) {
|
|
1488
|
+
if (!plugin.enabled)
|
|
1448
1489
|
continue;
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1490
|
+
if (plugin.error) {
|
|
1491
|
+
clearInterval(this.startMatterInterval);
|
|
1492
|
+
this.startMatterInterval = undefined;
|
|
1493
|
+
this.log.debug('Cleared startMatterInterval interval for Matterbridge for plugin in error state');
|
|
1494
|
+
this.log.error(`The plugin ${plg}${plugin.name}${er} is in error state.`);
|
|
1495
|
+
this.log.error('The bridge will not start until the problem is solved to prevent the controllers from deleting all registered devices.');
|
|
1496
|
+
this.log.error('If you want to start the bridge disable the plugin in error state and restart.');
|
|
1497
|
+
this.frontend.wssSendSnackbarMessage(`The plugin ${plugin.name} is in error state. Check the logs.`, 0, 'error');
|
|
1498
|
+
this.frontend.wssSendSnackbarMessage(`The bridge is offline. Startup halted due to plugin errors.`, 0, 'error');
|
|
1499
|
+
this.frontend.wssSendRefreshRequired('plugins');
|
|
1500
|
+
this.frontend.wssSendCloseSnackbarMessage(`The bridge is starting...`);
|
|
1501
|
+
this.server.request({ type: 'frontend_matterbridgestatusupdate', src: 'matterbridge', dst: 'frontend', params: { status: (this.bridgeStatus = 'error') } });
|
|
1502
|
+
return;
|
|
1503
|
+
}
|
|
1504
|
+
this.sendPluginStatusUpdate(plugin);
|
|
1505
|
+
if (!plugin.loaded || !plugin.started) {
|
|
1506
|
+
this.log.debug(`Waiting (failSafeCount=${failCount}/${this.failCountLimit}) in startMatterInterval interval for plugin ${plg}${plugin.name}${db} loaded: ${plugin.loaded} started: ${plugin.started}...`);
|
|
1507
|
+
failCount++;
|
|
1508
|
+
if (failCount > this.failCountLimit) {
|
|
1509
|
+
this.log.error(`Error waiting for plugin ${plg}${plugin.name}${er} to load and start. Plugin is in error state.`);
|
|
1510
|
+
plugin.error = true;
|
|
1453
1511
|
}
|
|
1512
|
+
return;
|
|
1454
1513
|
}
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1514
|
+
}
|
|
1515
|
+
clearInterval(this.startMatterInterval);
|
|
1516
|
+
this.startMatterInterval = undefined;
|
|
1517
|
+
this.log.debug('Cleared startMatterInterval interval in bridge mode');
|
|
1518
|
+
fireAndForget(this.startServerNode(this.serverNode), this.log, 'Start server node');
|
|
1519
|
+
for (const device of this.devices.array()) {
|
|
1520
|
+
if (device.mode === 'server' && device.serverNode) {
|
|
1521
|
+
this.log.debug(`Starting server node for device ${dev}${device.deviceName}${db} in server mode...`);
|
|
1522
|
+
fireAndForget(this.startServerNode(device.serverNode), this.log, `Start server node for device ${device.deviceName}`);
|
|
1458
1523
|
}
|
|
1459
1524
|
}
|
|
1460
|
-
this.
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1525
|
+
this.configureTimeout = setTimeout(() => {
|
|
1526
|
+
void (async () => {
|
|
1527
|
+
for (const plugin of this.plugins.array()) {
|
|
1528
|
+
if (!plugin.enabled || !plugin.loaded || !plugin.started || plugin.error)
|
|
1529
|
+
continue;
|
|
1530
|
+
try {
|
|
1531
|
+
if ((await this.plugins.configure(plugin)) === undefined) {
|
|
1532
|
+
if (plugin.configured !== true)
|
|
1533
|
+
this.frontend.wssSendSnackbarMessage(`The plugin ${plugin.name} failed to configure. Check the logs.`, 0, 'error');
|
|
1534
|
+
}
|
|
1535
|
+
}
|
|
1536
|
+
catch (error) {
|
|
1537
|
+
plugin.error = true;
|
|
1538
|
+
this.log.error(`Error configuring plugin ${plg}${plugin.name}${er}`, error);
|
|
1539
|
+
}
|
|
1540
|
+
this.sendPluginStatusUpdate(plugin);
|
|
1541
|
+
}
|
|
1542
|
+
})();
|
|
1543
|
+
}, 30 * 1000).unref();
|
|
1544
|
+
this.reachabilityTimeout = setTimeout(() => {
|
|
1545
|
+
this.log.info(`Setting reachability to true for ${plg}Matterbridge${db}`);
|
|
1546
|
+
if (this.aggregatorNode)
|
|
1547
|
+
fireAndForget(this.setAggregatorReachability(this.aggregatorNode, true), this.log, `Failed to set aggregator node reachability for Matterbridge`);
|
|
1548
|
+
}, 60 * 1000).unref();
|
|
1549
|
+
this.emit('bridge_started');
|
|
1550
|
+
this.log.notice('Matterbridge bridge started successfully');
|
|
1551
|
+
this.server.request({ type: 'frontend_matterbridgestatusupdate', src: 'matterbridge', dst: 'frontend', params: { status: (this.bridgeStatus = 'started') } });
|
|
1552
|
+
this.server.request({ type: 'frontend_refreshrequired', src: 'matterbridge', dst: 'frontend', params: { changed: 'plugins' } });
|
|
1553
|
+
this.server.request({ type: 'frontend_closesnackbarmessage', src: 'matterbridge', dst: 'frontend', params: { message: `The bridge is starting...` } });
|
|
1554
|
+
})();
|
|
1472
1555
|
}, Number(process.env['MATTERBRIDGE_START_MATTER_INTERVAL_MS']) || this.startMatterIntervalMs);
|
|
1473
1556
|
}
|
|
1474
1557
|
async startChildbridge(delay = 1000) {
|
|
1475
1558
|
if (!this.matterStorageManager)
|
|
1476
1559
|
throw new Error('No storage manager initialized');
|
|
1477
1560
|
const { wait } = await import('@matterbridge/utils/wait');
|
|
1561
|
+
this.server.request({ type: 'frontend_matterbridgestatusupdate', src: 'matterbridge', dst: 'frontend', params: { status: (this.bridgeStatus = 'starting') } });
|
|
1478
1562
|
this.log.debug('Loading all plugins in childbridge mode...');
|
|
1479
1563
|
await this.startPlugins(true, false);
|
|
1480
1564
|
this.log.debug('Creating server nodes for DynamicPlatform plugins and starting all plugins in childbridge mode...');
|
|
@@ -1486,100 +1570,105 @@ export class Matterbridge extends EventEmitter {
|
|
|
1486
1570
|
this.log.debug('Starting start matter interval in childbridge mode...');
|
|
1487
1571
|
this.frontend.wssSendSnackbarMessage(`The bridge is starting...`, 0, 'info');
|
|
1488
1572
|
let failCount = 0;
|
|
1489
|
-
this.startMatterInterval = setInterval(
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
}
|
|
1494
|
-
let allStarted = true;
|
|
1495
|
-
for (const plugin of this.plugins.array()) {
|
|
1496
|
-
if (!plugin.enabled)
|
|
1497
|
-
continue;
|
|
1498
|
-
if (plugin.error) {
|
|
1499
|
-
clearInterval(this.startMatterInterval);
|
|
1500
|
-
this.startMatterInterval = undefined;
|
|
1501
|
-
this.log.debug('Cleared startMatterInterval interval for a plugin in error state');
|
|
1502
|
-
this.log.error(`The plugin ${plg}${plugin.name}${er} is in error state.`);
|
|
1503
|
-
this.log.error('The bridge will not start until the problem is solved to prevent the controllers from deleting all registered devices.');
|
|
1504
|
-
this.log.error('If you want to start the bridge disable the plugin in error state and restart.');
|
|
1505
|
-
this.frontend.wssSendSnackbarMessage(`The plugin ${plugin.name} is in error state. Check the logs.`, 0, 'error');
|
|
1506
|
-
this.frontend.wssSendSnackbarMessage(`The bridge is offline. Startup halted due to plugin errors.`, 0, 'error');
|
|
1507
|
-
this.frontend.wssSendRefreshRequired('plugins');
|
|
1508
|
-
this.frontend.wssSendCloseSnackbarMessage(`The bridge is starting...`);
|
|
1509
|
-
return;
|
|
1573
|
+
this.startMatterInterval = setInterval(() => {
|
|
1574
|
+
void (async () => {
|
|
1575
|
+
if (failCount && failCount % 10 === 0) {
|
|
1576
|
+
this.frontend.wssSendSnackbarMessage(`The bridge is still starting...`, 10, 'info');
|
|
1510
1577
|
}
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
this.
|
|
1518
|
-
plugin
|
|
1578
|
+
let allStarted = true;
|
|
1579
|
+
for (const plugin of this.plugins.array()) {
|
|
1580
|
+
if (!plugin.enabled)
|
|
1581
|
+
continue;
|
|
1582
|
+
if (plugin.error) {
|
|
1583
|
+
clearInterval(this.startMatterInterval);
|
|
1584
|
+
this.startMatterInterval = undefined;
|
|
1585
|
+
this.log.debug('Cleared startMatterInterval interval for a plugin in error state');
|
|
1586
|
+
this.log.error(`The plugin ${plg}${plugin.name}${er} is in error state.`);
|
|
1587
|
+
this.log.error('The bridge will not start until the problem is solved to prevent the controllers from deleting all registered devices.');
|
|
1588
|
+
this.log.error('If you want to start the bridge disable the plugin in error state and restart.');
|
|
1589
|
+
this.frontend.wssSendSnackbarMessage(`The plugin ${plugin.name} is in error state. Check the logs.`, 0, 'error');
|
|
1590
|
+
this.frontend.wssSendSnackbarMessage(`The bridge is offline. Startup halted due to plugin errors.`, 0, 'error');
|
|
1591
|
+
this.frontend.wssSendRefreshRequired('plugins');
|
|
1592
|
+
this.frontend.wssSendCloseSnackbarMessage(`The bridge is starting...`);
|
|
1593
|
+
this.server.request({ type: 'frontend_matterbridgestatusupdate', src: 'matterbridge', dst: 'frontend', params: { status: (this.bridgeStatus = 'error') } });
|
|
1594
|
+
return;
|
|
1595
|
+
}
|
|
1596
|
+
this.sendPluginStatusUpdate(plugin);
|
|
1597
|
+
this.log.debug(`Checking plugin ${plg}${plugin.name}${db} to start matter in childbridge mode...`);
|
|
1598
|
+
if (!plugin.loaded || !plugin.started) {
|
|
1599
|
+
allStarted = false;
|
|
1600
|
+
this.log.debug(`Waiting (failSafeCount=${failCount}/${this.failCountLimit}) for plugin ${plg}${plugin.name}${db} to load (${plugin.loaded}) and start (${plugin.started}) ...`);
|
|
1601
|
+
failCount++;
|
|
1602
|
+
if (failCount > this.failCountLimit) {
|
|
1603
|
+
this.log.error(`Error waiting for plugin ${plg}${plugin.name}${er} to load and start. Plugin is in error state.`);
|
|
1604
|
+
plugin.error = true;
|
|
1605
|
+
}
|
|
1519
1606
|
}
|
|
1520
1607
|
}
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1608
|
+
if (!allStarted)
|
|
1609
|
+
return;
|
|
1610
|
+
clearInterval(this.startMatterInterval);
|
|
1611
|
+
this.startMatterInterval = undefined;
|
|
1612
|
+
if (delay > 0)
|
|
1613
|
+
await wait(Number(process.env['MATTERBRIDGE_PAUSE_MATTER_INTERVAL_MS']) || delay);
|
|
1614
|
+
this.log.debug('Cleared startMatterInterval interval in childbridge mode');
|
|
1615
|
+
this.configureTimeout = setTimeout(() => {
|
|
1616
|
+
void (async () => {
|
|
1617
|
+
for (const plugin of this.plugins.array()) {
|
|
1618
|
+
if (!plugin.enabled || !plugin.loaded || !plugin.started || plugin.error)
|
|
1619
|
+
continue;
|
|
1620
|
+
try {
|
|
1621
|
+
if ((await this.plugins.configure(plugin)) === undefined) {
|
|
1622
|
+
if (plugin.configured !== true)
|
|
1623
|
+
this.frontend.wssSendSnackbarMessage(`The plugin ${plugin.name} failed to configure. Check the logs.`, 0, 'error');
|
|
1624
|
+
}
|
|
1625
|
+
}
|
|
1626
|
+
catch (error) {
|
|
1627
|
+
plugin.error = true;
|
|
1628
|
+
this.log.error(`Error configuring plugin ${plg}${plugin.name}${er}`, error);
|
|
1629
|
+
}
|
|
1630
|
+
this.sendPluginStatusUpdate(plugin);
|
|
1631
|
+
}
|
|
1632
|
+
})();
|
|
1633
|
+
}, 30 * 1000).unref();
|
|
1530
1634
|
for (const plugin of this.plugins.array()) {
|
|
1531
|
-
if (!plugin.enabled ||
|
|
1635
|
+
if (!plugin.enabled || plugin.error)
|
|
1636
|
+
continue;
|
|
1637
|
+
if (plugin.type !== 'DynamicPlatform' && (!plugin.registeredDevices || plugin.registeredDevices === 0)) {
|
|
1638
|
+
this.log.error(`Plugin ${plg}${plugin.name}${er} didn't register any devices to Matterbridge. Verify the plugin configuration.`);
|
|
1532
1639
|
continue;
|
|
1533
|
-
try {
|
|
1534
|
-
if ((await this.plugins.configure(plugin)) === undefined) {
|
|
1535
|
-
if (plugin.configured !== true)
|
|
1536
|
-
this.frontend.wssSendSnackbarMessage(`The plugin ${plugin.name} failed to configure. Check the logs.`, 0, 'error');
|
|
1537
|
-
}
|
|
1538
1640
|
}
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1641
|
+
if (!plugin.serverNode) {
|
|
1642
|
+
this.log.error(`Server node not found for plugin ${plg}${plugin.name}${er}`);
|
|
1643
|
+
continue;
|
|
1542
1644
|
}
|
|
1645
|
+
if (!plugin.storageContext) {
|
|
1646
|
+
this.log.error(`Storage context not found for plugin ${plg}${plugin.name}${er}`);
|
|
1647
|
+
continue;
|
|
1648
|
+
}
|
|
1649
|
+
if (!plugin.nodeContext) {
|
|
1650
|
+
this.log.error(`Node storage context not found for plugin ${plg}${plugin.name}${er}`);
|
|
1651
|
+
continue;
|
|
1652
|
+
}
|
|
1653
|
+
fireAndForget(this.startServerNode(plugin.serverNode), this.log, `Start server node for plugin ${plugin.name}`);
|
|
1654
|
+
plugin.reachabilityTimeout = setTimeout(() => {
|
|
1655
|
+
this.log.info(`Setting reachability to true for ${plg}${plugin.name}${nf}`);
|
|
1656
|
+
if (plugin.type === 'DynamicPlatform' && plugin.aggregatorNode)
|
|
1657
|
+
fireAndForget(this.setAggregatorReachability(plugin.aggregatorNode, true), this.log, `Set aggregator node reachability for plugin ${plugin.name}`);
|
|
1658
|
+
}, 60 * 1000).unref();
|
|
1543
1659
|
}
|
|
1544
|
-
this.
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
if (plugin.type !== 'DynamicPlatform' && (!plugin.registeredDevices || plugin.registeredDevices === 0)) {
|
|
1550
|
-
this.log.error(`Plugin ${plg}${plugin.name}${er} didn't register any devices to Matterbridge. Verify the plugin configuration.`);
|
|
1551
|
-
continue;
|
|
1552
|
-
}
|
|
1553
|
-
if (!plugin.serverNode) {
|
|
1554
|
-
this.log.error(`Server node not found for plugin ${plg}${plugin.name}${er}`);
|
|
1555
|
-
continue;
|
|
1556
|
-
}
|
|
1557
|
-
if (!plugin.storageContext) {
|
|
1558
|
-
this.log.error(`Storage context not found for plugin ${plg}${plugin.name}${er}`);
|
|
1559
|
-
continue;
|
|
1560
|
-
}
|
|
1561
|
-
if (!plugin.nodeContext) {
|
|
1562
|
-
this.log.error(`Node storage context not found for plugin ${plg}${plugin.name}${er}`);
|
|
1563
|
-
continue;
|
|
1564
|
-
}
|
|
1565
|
-
fireAndForget(this.startServerNode(plugin.serverNode), this.log, `Start server node for plugin ${plugin.name}`);
|
|
1566
|
-
plugin.reachabilityTimeout = setTimeout(() => {
|
|
1567
|
-
this.log.info(`Setting reachability to true for ${plg}${plugin.name}${nf}`);
|
|
1568
|
-
if (plugin.type === 'DynamicPlatform' && plugin.aggregatorNode)
|
|
1569
|
-
fireAndForget(this.setAggregatorReachability(plugin.aggregatorNode, true), this.log, `Set aggregator node reachability for plugin ${plugin.name}`);
|
|
1570
|
-
}, 60 * 1000).unref();
|
|
1571
|
-
}
|
|
1572
|
-
for (const device of this.devices.array()) {
|
|
1573
|
-
if (device.mode === 'server' && device.serverNode) {
|
|
1574
|
-
this.log.debug(`Starting server node for device ${dev}${device.deviceName}${db} in server mode...`);
|
|
1575
|
-
fireAndForget(this.startServerNode(device.serverNode), this.log, `Start server node for device ${device.deviceName}`);
|
|
1660
|
+
for (const device of this.devices.array()) {
|
|
1661
|
+
if (device.mode === 'server' && device.serverNode) {
|
|
1662
|
+
this.log.debug(`Starting server node for device ${dev}${device.deviceName}${db} in server mode...`);
|
|
1663
|
+
fireAndForget(this.startServerNode(device.serverNode), this.log, `Start server node for device ${device.deviceName}`);
|
|
1664
|
+
}
|
|
1576
1665
|
}
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1666
|
+
this.emit('childbridge_started');
|
|
1667
|
+
this.log.notice('Matterbridge childbridge started successfully');
|
|
1668
|
+
this.server.request({ type: 'frontend_matterbridgestatusupdate', src: 'matterbridge', dst: 'frontend', params: { status: (this.bridgeStatus = 'started') } });
|
|
1669
|
+
this.server.request({ type: 'frontend_refreshrequired', src: 'matterbridge', dst: 'frontend', params: { changed: 'plugins' } });
|
|
1670
|
+
this.server.request({ type: 'frontend_closesnackbarmessage', src: 'matterbridge', dst: 'frontend', params: { message: `The bridge is starting...` } });
|
|
1671
|
+
})();
|
|
1583
1672
|
}, Number(process.env['MATTERBRIDGE_START_MATTER_INTERVAL_MS']) || this.startMatterIntervalMs);
|
|
1584
1673
|
}
|
|
1585
1674
|
async startController() {
|
|
@@ -1732,9 +1821,13 @@ export class Matterbridge extends EventEmitter {
|
|
|
1732
1821
|
this.frontend.wssSendSnackbarMessage(`${storeId} is offline`, 5, 'warning');
|
|
1733
1822
|
this.frontend.wssSendSnackbarMessage(`Server node for ${storeId} fully decommissioned successfully!`, 5, 'success');
|
|
1734
1823
|
});
|
|
1735
|
-
serverNode.lifecycle.online.on(
|
|
1824
|
+
serverNode.lifecycle.online.on(() => {
|
|
1736
1825
|
this.log.notice(`Server node for ${storeId} is online`);
|
|
1737
|
-
if (
|
|
1826
|
+
if (serverNode.lifecycle.isCommissioned) {
|
|
1827
|
+
this.log.notice(`Server node for ${storeId} is already commissioned.`);
|
|
1828
|
+
this.advertisingNodes.delete(storeId);
|
|
1829
|
+
}
|
|
1830
|
+
else {
|
|
1738
1831
|
this.log.notice(`Server node for ${storeId} is not commissioned. Pair to commission.`);
|
|
1739
1832
|
this.advertisingNodes.set(storeId, Date.now());
|
|
1740
1833
|
const { qrPairingCode, manualPairingCode } = serverNode.state.commissioning.pairingCodes;
|
|
@@ -1742,10 +1835,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1742
1835
|
this.log.notice(`QR Code URL: https://project-chip.github.io/connectedhomeip/qrcode.html?data=${qrPairingCode}`);
|
|
1743
1836
|
this.log.notice(`Manual pairing code ${CYAN}${manualPairingCode}${nt} discriminator ${CYAN}${discriminator}${nt} short discriminator ${CYAN}${pairingData.shortDiscriminator}${nt} passcode ${CYAN}${passcode}${nt}`);
|
|
1744
1837
|
}
|
|
1745
|
-
else {
|
|
1746
|
-
this.log.notice(`Server node for ${storeId} is already commissioned.`);
|
|
1747
|
-
this.advertisingNodes.delete(storeId);
|
|
1748
|
-
}
|
|
1749
1838
|
this.frontend.wssSendRefreshRequired('matter', { matter: { ...this.getServerNodeData(serverNode) } });
|
|
1750
1839
|
this.frontend.wssSendSnackbarMessage(`${storeId} is online`, 5, 'success');
|
|
1751
1840
|
this.emit('online', storeId);
|
|
@@ -1790,7 +1879,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
1790
1879
|
return serverNode;
|
|
1791
1880
|
}
|
|
1792
1881
|
getServerNodeData(serverNode) {
|
|
1793
|
-
const advertiseTime = this.advertisingNodes.get(serverNode.id)
|
|
1882
|
+
const advertiseTime = this.advertisingNodes.get(serverNode.id) ?? 0;
|
|
1794
1883
|
return {
|
|
1795
1884
|
id: serverNode.id,
|
|
1796
1885
|
online: serverNode.lifecycle.isOnline,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@matterbridge/core",
|
|
3
|
-
"version": "3.9.2-dev-
|
|
3
|
+
"version": "3.9.2-dev-20260621-75283ba",
|
|
4
4
|
"description": "Matterbridge core library",
|
|
5
5
|
"author": "https://github.com/Luligu",
|
|
6
6
|
"homepage": "https://matterbridge.io/",
|
|
@@ -130,16 +130,16 @@
|
|
|
130
130
|
],
|
|
131
131
|
"dependencies": {
|
|
132
132
|
"@matter/main": "0.17.3",
|
|
133
|
-
"@matterbridge/dgram": "3.9.2-dev-
|
|
134
|
-
"@matterbridge/thread": "3.9.2-dev-
|
|
135
|
-
"@matterbridge/types": "3.9.2-dev-
|
|
136
|
-
"@matterbridge/utils": "3.9.2-dev-
|
|
133
|
+
"@matterbridge/dgram": "3.9.2-dev-20260621-75283ba",
|
|
134
|
+
"@matterbridge/thread": "3.9.2-dev-20260621-75283ba",
|
|
135
|
+
"@matterbridge/types": "3.9.2-dev-20260621-75283ba",
|
|
136
|
+
"@matterbridge/utils": "3.9.2-dev-20260621-75283ba",
|
|
137
137
|
"escape-html": "1.0.3",
|
|
138
138
|
"express": "5.2.1",
|
|
139
139
|
"express-rate-limit": "8.5.2",
|
|
140
140
|
"multer": "2.2.0",
|
|
141
|
-
"node-ansi-logger": "3.3.0
|
|
142
|
-
"node-persist-manager": "2.1.0
|
|
141
|
+
"node-ansi-logger": "3.3.0",
|
|
142
|
+
"node-persist-manager": "2.1.0",
|
|
143
143
|
"ws": "8.21.0"
|
|
144
144
|
}
|
|
145
145
|
}
|