matterbridge 1.6.7 → 1.6.8-dev.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +15 -0
- package/dist/cli.js +0 -26
- package/dist/cluster/export.js +0 -2
- package/dist/defaultConfigSchema.js +0 -23
- package/dist/deviceManager.js +1 -26
- package/dist/index.js +0 -30
- package/dist/logger/export.js +0 -1
- package/dist/matter/export.js +5 -1
- package/dist/matterbridge.js +61 -659
- package/dist/matterbridgeAccessoryPlatform.js +0 -33
- package/dist/matterbridgeBehaviors.js +1 -29
- package/dist/matterbridgeDevice.js +9 -986
- package/dist/matterbridgeDeviceTypes.js +11 -82
- package/dist/matterbridgeDynamicPlatform.js +0 -33
- package/dist/matterbridgeEdge.js +1 -532
- package/dist/matterbridgeEndpoint.js +12 -1111
- package/dist/matterbridgePlatform.js +3 -91
- package/dist/matterbridgeTypes.js +0 -24
- package/dist/matterbridgeWebsocket.js +0 -45
- package/dist/pluginManager.js +3 -238
- package/dist/storage/export.js +0 -1
- package/dist/utils/colorUtils.js +2 -205
- package/dist/utils/export.js +0 -1
- package/dist/utils/utils.js +7 -252
- package/npm-shrinkwrap.json +7 -71
- package/package.json +1 -2
- package/dist/cli.d.ts +0 -25
- package/dist/cli.d.ts.map +0 -1
- package/dist/cli.js.map +0 -1
- package/dist/cluster/export.d.ts +0 -2
- package/dist/cluster/export.d.ts.map +0 -1
- package/dist/cluster/export.js.map +0 -1
- package/dist/defaultConfigSchema.d.ts +0 -27
- package/dist/defaultConfigSchema.d.ts.map +0 -1
- package/dist/defaultConfigSchema.js.map +0 -1
- package/dist/deviceManager.d.ts +0 -46
- package/dist/deviceManager.d.ts.map +0 -1
- package/dist/deviceManager.js.map +0 -1
- package/dist/index.d.ts +0 -40
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/logger/export.d.ts +0 -2
- package/dist/logger/export.d.ts.map +0 -1
- package/dist/logger/export.js.map +0 -1
- package/dist/matter/export.d.ts +0 -6
- package/dist/matter/export.d.ts.map +0 -1
- package/dist/matter/export.js.map +0 -1
- package/dist/matterbridge.d.ts +0 -466
- package/dist/matterbridge.d.ts.map +0 -1
- package/dist/matterbridge.js.map +0 -1
- package/dist/matterbridgeAccessoryPlatform.d.ts +0 -39
- package/dist/matterbridgeAccessoryPlatform.d.ts.map +0 -1
- package/dist/matterbridgeAccessoryPlatform.js.map +0 -1
- package/dist/matterbridgeBehaviors.d.ts +0 -942
- package/dist/matterbridgeBehaviors.d.ts.map +0 -1
- package/dist/matterbridgeBehaviors.js.map +0 -1
- package/dist/matterbridgeDevice.d.ts +0 -7063
- package/dist/matterbridgeDevice.d.ts.map +0 -1
- package/dist/matterbridgeDevice.js.map +0 -1
- package/dist/matterbridgeDeviceTypes.d.ts +0 -109
- package/dist/matterbridgeDeviceTypes.d.ts.map +0 -1
- package/dist/matterbridgeDeviceTypes.js.map +0 -1
- package/dist/matterbridgeDynamicPlatform.d.ts +0 -39
- package/dist/matterbridgeDynamicPlatform.d.ts.map +0 -1
- package/dist/matterbridgeDynamicPlatform.js.map +0 -1
- package/dist/matterbridgeEdge.d.ts +0 -92
- package/dist/matterbridgeEdge.d.ts.map +0 -1
- package/dist/matterbridgeEdge.js.map +0 -1
- package/dist/matterbridgeEndpoint.d.ts +0 -10164
- package/dist/matterbridgeEndpoint.d.ts.map +0 -1
- package/dist/matterbridgeEndpoint.js.map +0 -1
- package/dist/matterbridgePlatform.d.ts +0 -114
- package/dist/matterbridgePlatform.d.ts.map +0 -1
- package/dist/matterbridgePlatform.js.map +0 -1
- package/dist/matterbridgeTypes.d.ts +0 -162
- package/dist/matterbridgeTypes.d.ts.map +0 -1
- package/dist/matterbridgeTypes.js.map +0 -1
- package/dist/matterbridgeWebsocket.d.ts +0 -49
- package/dist/matterbridgeWebsocket.d.ts.map +0 -1
- package/dist/matterbridgeWebsocket.js.map +0 -1
- package/dist/pluginManager.d.ts +0 -238
- package/dist/pluginManager.d.ts.map +0 -1
- package/dist/pluginManager.js.map +0 -1
- package/dist/storage/export.d.ts +0 -2
- package/dist/storage/export.d.ts.map +0 -1
- package/dist/storage/export.js.map +0 -1
- package/dist/utils/colorUtils.d.ts +0 -61
- package/dist/utils/colorUtils.d.ts.map +0 -1
- package/dist/utils/colorUtils.js.map +0 -1
- package/dist/utils/export.d.ts +0 -3
- package/dist/utils/export.d.ts.map +0 -1
- package/dist/utils/export.js.map +0 -1
- package/dist/utils/utils.d.ts +0 -221
- package/dist/utils/utils.d.ts.map +0 -1
- package/dist/utils/utils.js.map +0 -1
package/dist/matterbridge.js
CHANGED
|
@@ -1,26 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* This file contains the class Matterbridge.
|
|
3
|
-
*
|
|
4
|
-
* @file matterbridge.ts
|
|
5
|
-
* @author Luca Liguori
|
|
6
|
-
* @date 2023-12-29
|
|
7
|
-
* @version 1.5.2
|
|
8
|
-
*
|
|
9
|
-
* Copyright 2023, 2024, 2025 Luca Liguori.
|
|
10
|
-
*
|
|
11
|
-
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
12
|
-
* you may not use this file except in compliance with the License.
|
|
13
|
-
* You may obtain a copy of the License at
|
|
14
|
-
*
|
|
15
|
-
* http://www.apache.org/licenses/LICENSE-2.0
|
|
16
|
-
*
|
|
17
|
-
* Unless required by applicable law or agreed to in writing, software
|
|
18
|
-
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
19
|
-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
20
|
-
* See the License for the specific language governing permissions and
|
|
21
|
-
* limitations under the License. *
|
|
22
|
-
*/
|
|
23
|
-
// Node.js modules
|
|
24
1
|
import { fileURLToPath } from 'url';
|
|
25
2
|
import { promises as fs } from 'fs';
|
|
26
3
|
import { exec, spawn } from 'child_process';
|
|
@@ -29,35 +6,26 @@ import EventEmitter from 'events';
|
|
|
29
6
|
import os from 'os';
|
|
30
7
|
import path from 'path';
|
|
31
8
|
import { randomBytes } from 'crypto';
|
|
32
|
-
// Package modules
|
|
33
9
|
import https from 'https';
|
|
34
10
|
import express from 'express';
|
|
35
11
|
import WebSocket, { WebSocketServer } from 'ws';
|
|
36
|
-
// NodeStorage and AnsiLogger modules
|
|
37
12
|
import { NodeStorageManager } from 'node-persist-manager';
|
|
38
13
|
import { AnsiLogger, UNDERLINE, UNDERLINEOFF, YELLOW, db, debugStringify, stringify, BRIGHT, RESET, er, nf, rs, wr, RED, GREEN, zb, CYAN, nt, idn, or, hk, BLUE } from 'node-ansi-logger';
|
|
39
|
-
// Matterbridge
|
|
40
14
|
import { MatterbridgeDevice } from './matterbridgeDevice.js';
|
|
41
15
|
import { WS_ID_LOG, WS_ID_REFRESH_NEEDED, WS_ID_RESTART_NEEDED, wsMessageHandler } from './matterbridgeWebsocket.js';
|
|
42
16
|
import { logInterfaces, wait, waiter, createZip, copyDirectory, getParameter, getIntParameter, hasParameter } from './utils/utils.js';
|
|
43
17
|
import { PluginManager } from './pluginManager.js';
|
|
44
18
|
import { DeviceManager } from './deviceManager.js';
|
|
45
19
|
import { MatterbridgeEndpoint } from './matterbridgeEndpoint.js';
|
|
46
|
-
// @matter
|
|
47
20
|
import { DeviceTypeId, Logger, LogLevel as MatterLogLevel, LogFormat as MatterLogFormat, VendorId, StorageManager, EndpointServer } from '@matter/main';
|
|
48
21
|
import { BasicInformationCluster, BridgedDeviceBasicInformation, BridgedDeviceBasicInformationCluster, FixedLabelCluster, GeneralCommissioning, PowerSourceCluster, SwitchCluster, ThreadNetworkDiagnosticsCluster, UserLabelCluster, } from '@matter/main/clusters';
|
|
49
22
|
import { getClusterNameById, ManualPairingCodeCodec, QrCodeSchema } from '@matter/main/types';
|
|
50
23
|
import { StorageBackendDisk, StorageBackendJsonFile } from '@matter/nodejs';
|
|
51
|
-
// @project-chip
|
|
52
24
|
import { CommissioningController, CommissioningServer, MatterServer } from '@project-chip/matter.js';
|
|
53
25
|
import { Aggregator, DeviceTypes, NodeStateInformation } from '@project-chip/matter.js/device';
|
|
54
|
-
// Default colors
|
|
55
26
|
const plg = '\u001B[38;5;33m';
|
|
56
27
|
const dev = '\u001B[38;5;79m';
|
|
57
28
|
const typ = '\u001B[38;5;207m';
|
|
58
|
-
/**
|
|
59
|
-
* Represents the Matterbridge application.
|
|
60
|
-
*/
|
|
61
29
|
export class Matterbridge extends EventEmitter {
|
|
62
30
|
systemInformation = {
|
|
63
31
|
interfaceName: '',
|
|
@@ -94,7 +62,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
94
62
|
edge: hasParameter('edge'),
|
|
95
63
|
readOnly: hasParameter('readonly'),
|
|
96
64
|
profile: getParameter('profile'),
|
|
97
|
-
loggerLevel: "info"
|
|
65
|
+
loggerLevel: "info",
|
|
98
66
|
fileLogger: false,
|
|
99
67
|
matterLoggerLevel: MatterLogLevel.INFO,
|
|
100
68
|
matterFileLogger: false,
|
|
@@ -133,7 +101,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
133
101
|
nodeContext;
|
|
134
102
|
matterStorageName = 'matterbridge' + (getParameter('profile') ? '.' + getParameter('profile') : '') + '.json';
|
|
135
103
|
nodeStorageName = 'storage' + (getParameter('profile') ? '.' + getParameter('profile') : '');
|
|
136
|
-
// Cleanup
|
|
137
104
|
hasCleanupStarted = false;
|
|
138
105
|
initialized = false;
|
|
139
106
|
execRunningCount = 0;
|
|
@@ -145,18 +112,16 @@ export class Matterbridge extends EventEmitter {
|
|
|
145
112
|
sigtermHandler;
|
|
146
113
|
exceptionHandler;
|
|
147
114
|
rejectionHandler;
|
|
148
|
-
// Frontend
|
|
149
115
|
expressApp;
|
|
150
116
|
httpServer;
|
|
151
117
|
httpsServer;
|
|
152
118
|
webSocketServer;
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
discriminator; // first commissioning server discriminator
|
|
119
|
+
mdnsInterface;
|
|
120
|
+
ipv4address;
|
|
121
|
+
ipv6address;
|
|
122
|
+
port = 5540;
|
|
123
|
+
passcode;
|
|
124
|
+
discriminator;
|
|
160
125
|
storageManager;
|
|
161
126
|
matterbridgeContext;
|
|
162
127
|
mattercontrollerContext;
|
|
@@ -167,26 +132,13 @@ export class Matterbridge extends EventEmitter {
|
|
|
167
132
|
aggregatorVendorId = VendorId(getIntParameter('vendorId') ?? 0xfff1);
|
|
168
133
|
aggregatorProductId = getIntParameter('productId') ?? 0x8000;
|
|
169
134
|
static instance;
|
|
170
|
-
// We load asyncronously so is private
|
|
171
135
|
constructor() {
|
|
172
136
|
super();
|
|
173
|
-
// Bind the handler to the instance
|
|
174
137
|
this.matterbridgeMessageHandler = wsMessageHandler.bind(this);
|
|
175
138
|
}
|
|
176
139
|
matterbridgeMessageHandler;
|
|
177
|
-
/** ***********************************************************************************************************************************/
|
|
178
|
-
/** loadInstance() and cleanup() methods */
|
|
179
|
-
/** ***********************************************************************************************************************************/
|
|
180
|
-
/**
|
|
181
|
-
* Loads an instance of the Matterbridge class.
|
|
182
|
-
* If an instance already exists, return that instance.
|
|
183
|
-
*
|
|
184
|
-
* @param initialize - Whether to initialize the Matterbridge instance after loading.
|
|
185
|
-
* @returns The loaded Matterbridge instance.
|
|
186
|
-
*/
|
|
187
140
|
static async loadInstance(initialize = false) {
|
|
188
141
|
if (!Matterbridge.instance) {
|
|
189
|
-
// eslint-disable-next-line no-console
|
|
190
142
|
if (hasParameter('debug'))
|
|
191
143
|
console.log(GREEN + 'Creating a new instance of Matterbridge.', initialize ? 'Initializing...' : 'Not initializing...', rs);
|
|
192
144
|
Matterbridge.instance = new Matterbridge();
|
|
@@ -195,11 +147,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
195
147
|
}
|
|
196
148
|
return Matterbridge.instance;
|
|
197
149
|
}
|
|
198
|
-
/**
|
|
199
|
-
* Call cleanup().
|
|
200
|
-
* @deprecated This method is deprecated and is only used for jest tests.
|
|
201
|
-
*
|
|
202
|
-
*/
|
|
203
150
|
async destroyInstance() {
|
|
204
151
|
await this.cleanup('destroying instance...', false);
|
|
205
152
|
await waiter('destroying instance...', () => {
|
|
@@ -207,60 +154,39 @@ export class Matterbridge extends EventEmitter {
|
|
|
207
154
|
}, false, 60000, 100, false);
|
|
208
155
|
await wait(1000, 'Wait for the global node_modules and matterbridge version', false);
|
|
209
156
|
}
|
|
210
|
-
/**
|
|
211
|
-
* Initializes the Matterbridge application.
|
|
212
|
-
*
|
|
213
|
-
* @remarks
|
|
214
|
-
* This method performs the necessary setup and initialization steps for the Matterbridge application.
|
|
215
|
-
* It displays the help information if the 'help' parameter is provided, sets up the logger, checks the
|
|
216
|
-
* node version, registers signal handlers, initializes storage, and parses the command line.
|
|
217
|
-
*
|
|
218
|
-
* @returns A Promise that resolves when the initialization is complete.
|
|
219
|
-
*/
|
|
220
157
|
async initialize() {
|
|
221
|
-
// Set the restart mode
|
|
222
158
|
if (hasParameter('service'))
|
|
223
159
|
this.restartMode = 'service';
|
|
224
160
|
if (hasParameter('docker'))
|
|
225
161
|
this.restartMode = 'docker';
|
|
226
|
-
// Set the matterbridge directory
|
|
227
162
|
this.homeDirectory = getParameter('homedir') ?? os.homedir();
|
|
228
163
|
this.matterbridgeDirectory = path.join(this.homeDirectory, '.matterbridge');
|
|
229
|
-
|
|
230
|
-
this.log = new AnsiLogger({ logName: 'Matterbridge', logTimestampFormat: 4 /* TimestampFormat.TIME_MILLIS */, logLevel: hasParameter('debug') ? "debug" /* LogLevel.DEBUG */ : "info" /* LogLevel.INFO */ });
|
|
231
|
-
// Initialize nodeStorage and nodeContext
|
|
164
|
+
this.log = new AnsiLogger({ logName: 'Matterbridge', logTimestampFormat: 4, logLevel: hasParameter('debug') ? "debug" : "info" });
|
|
232
165
|
try {
|
|
233
166
|
this.log.debug(`Creating node storage manager: ${CYAN}${this.nodeStorageName}${db}`);
|
|
234
167
|
this.nodeStorage = new NodeStorageManager({ dir: path.join(this.matterbridgeDirectory, this.nodeStorageName), writeQueue: false, expiredInterval: undefined, logging: false });
|
|
235
168
|
this.log.debug('Creating node storage context for matterbridge');
|
|
236
169
|
this.nodeContext = await this.nodeStorage.createStorage('matterbridge');
|
|
237
|
-
// TODO: Remove this code when node-persist-manager is updated
|
|
238
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
239
170
|
const keys = (await this.nodeStorage?.storage.keys());
|
|
240
171
|
for (const key of keys) {
|
|
241
172
|
this.log.debug(`Checking node storage manager key: ${CYAN}${key}${db}`);
|
|
242
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
243
173
|
await this.nodeStorage?.storage.get(key);
|
|
244
174
|
}
|
|
245
175
|
const storages = await this.nodeStorage.getStorageNames();
|
|
246
176
|
for (const storage of storages) {
|
|
247
177
|
this.log.debug(`Checking storage: ${CYAN}${storage}${db}`);
|
|
248
178
|
const nodeContext = await this.nodeStorage?.createStorage(storage);
|
|
249
|
-
// TODO: Remove this code when node-persist-manager is updated
|
|
250
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
251
179
|
const keys = (await nodeContext?.storage.keys());
|
|
252
180
|
keys.forEach(async (key) => {
|
|
253
181
|
this.log.debug(`Checking key: ${CYAN}${storage}:${key}${db}`);
|
|
254
182
|
await nodeContext?.get(key);
|
|
255
183
|
});
|
|
256
184
|
}
|
|
257
|
-
// Creating a backup of the node storage since it is not corrupted
|
|
258
185
|
this.log.debug('Creating node storage backup...');
|
|
259
186
|
await copyDirectory(path.join(this.matterbridgeDirectory, this.nodeStorageName), path.join(this.matterbridgeDirectory, this.nodeStorageName + '.backup'));
|
|
260
187
|
this.log.debug('Created node storage backup');
|
|
261
188
|
}
|
|
262
189
|
catch (error) {
|
|
263
|
-
// Restoring the backup of the node storage since it is corrupted
|
|
264
190
|
this.log.error(`Error creating node storage manager and context: ${error instanceof Error ? error.message : error}`);
|
|
265
191
|
if (hasParameter('norestore')) {
|
|
266
192
|
this.log.fatal(`The matterbridge node storage is corrupted. Parameter -norestore found: exiting...`);
|
|
@@ -275,51 +201,45 @@ export class Matterbridge extends EventEmitter {
|
|
|
275
201
|
this.log.fatal('Fatal error creating node storage manager and context for matterbridge');
|
|
276
202
|
throw new Error('Fatal error creating node storage manager and context for matterbridge');
|
|
277
203
|
}
|
|
278
|
-
// Set the first port to use for the commissioning server (will be incremented in childbridge mode)
|
|
279
204
|
this.port = getIntParameter('port') ?? (await this.nodeContext.get('matterport', 5540)) ?? 5540;
|
|
280
|
-
// Set the first passcode to use for the commissioning server (will be incremented in childbridge mode)
|
|
281
205
|
this.passcode = this.passcode ?? getIntParameter('passcode') ?? (await this.nodeContext.get('matterpasscode'));
|
|
282
|
-
// Set the first discriminator to use for the commissioning server (will be incremented in childbridge mode)
|
|
283
206
|
this.discriminator = this.discriminator ?? getIntParameter('discriminator') ?? (await this.nodeContext.get('matterdiscriminator'));
|
|
284
207
|
this.log.debug(`Initializing commissioning server for Matterbridge... on port ${this.port} with passcode ${this.passcode} and discriminator ${this.discriminator}`);
|
|
285
|
-
// Set matterbridge logger level (context: matterbridgeLogLevel)
|
|
286
208
|
if (hasParameter('logger')) {
|
|
287
209
|
const level = getParameter('logger');
|
|
288
210
|
if (level === 'debug') {
|
|
289
|
-
this.log.logLevel = "debug"
|
|
211
|
+
this.log.logLevel = "debug";
|
|
290
212
|
}
|
|
291
213
|
else if (level === 'info') {
|
|
292
|
-
this.log.logLevel = "info"
|
|
214
|
+
this.log.logLevel = "info";
|
|
293
215
|
}
|
|
294
216
|
else if (level === 'notice') {
|
|
295
|
-
this.log.logLevel = "notice"
|
|
217
|
+
this.log.logLevel = "notice";
|
|
296
218
|
}
|
|
297
219
|
else if (level === 'warn') {
|
|
298
|
-
this.log.logLevel = "warn"
|
|
220
|
+
this.log.logLevel = "warn";
|
|
299
221
|
}
|
|
300
222
|
else if (level === 'error') {
|
|
301
|
-
this.log.logLevel = "error"
|
|
223
|
+
this.log.logLevel = "error";
|
|
302
224
|
}
|
|
303
225
|
else if (level === 'fatal') {
|
|
304
|
-
this.log.logLevel = "fatal"
|
|
226
|
+
this.log.logLevel = "fatal";
|
|
305
227
|
}
|
|
306
228
|
else {
|
|
307
229
|
this.log.warn(`Invalid matterbridge logger level: ${level}. Using default level "info".`);
|
|
308
|
-
this.log.logLevel = "info"
|
|
230
|
+
this.log.logLevel = "info";
|
|
309
231
|
}
|
|
310
232
|
}
|
|
311
233
|
else {
|
|
312
|
-
this.log.logLevel = await this.nodeContext.get('matterbridgeLogLevel', "info"
|
|
234
|
+
this.log.logLevel = await this.nodeContext.get('matterbridgeLogLevel', "info");
|
|
313
235
|
}
|
|
314
236
|
MatterbridgeDevice.logLevel = this.log.logLevel;
|
|
315
|
-
// Create the file logger for matterbridge (context: matterbridgeFileLog)
|
|
316
237
|
if (hasParameter('filelogger') || (await this.nodeContext.get('matterbridgeFileLog', false))) {
|
|
317
238
|
AnsiLogger.setGlobalLogfile(path.join(this.matterbridgeDirectory, this.matterbrideLoggerFile), this.log.logLevel, true);
|
|
318
239
|
this.matterbridgeInformation.fileLogger = true;
|
|
319
240
|
}
|
|
320
241
|
this.log.notice('Matterbridge is starting...');
|
|
321
242
|
this.log.debug(`Matterbridge logLevel: ${this.log.logLevel} fileLoger: ${this.matterbridgeInformation.fileLogger}.`);
|
|
322
|
-
// Set matter.js logger level, format and logger (context: matterLogLevel)
|
|
323
243
|
if (hasParameter('matterlogger')) {
|
|
324
244
|
const level = getParameter('matterlogger');
|
|
325
245
|
if (level === 'debug') {
|
|
@@ -350,7 +270,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
350
270
|
}
|
|
351
271
|
Logger.format = MatterLogFormat.ANSI;
|
|
352
272
|
Logger.setLogger('default', this.createMatterLogger());
|
|
353
|
-
// Create the file logger for matter.js (context: matterFileLog)
|
|
354
273
|
if (hasParameter('matterfilelogger') || (await this.nodeContext.get('matterFileLog', false))) {
|
|
355
274
|
this.matterbridgeInformation.matterFileLogger = true;
|
|
356
275
|
Logger.addLogger('matterfilelogger', await this.createMatterFileLogger(path.join(this.matterbridgeDirectory, this.matterLoggerFile), true), {
|
|
@@ -359,7 +278,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
359
278
|
});
|
|
360
279
|
}
|
|
361
280
|
this.log.debug(`Matter logLevel: ${Logger.defaultLogLevel} fileLoger: ${this.matterbridgeInformation.matterFileLogger}.`);
|
|
362
|
-
// Set the interface to use for the matter server mdnsInterface
|
|
363
281
|
if (hasParameter('mdnsinterface')) {
|
|
364
282
|
this.mdnsInterface = getParameter('mdnsinterface');
|
|
365
283
|
}
|
|
@@ -368,7 +286,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
368
286
|
if (this.mdnsInterface === '')
|
|
369
287
|
this.mdnsInterface = undefined;
|
|
370
288
|
}
|
|
371
|
-
// Set the listeningAddressIpv4 for the matter commissioning server
|
|
372
289
|
if (hasParameter('ipv4address')) {
|
|
373
290
|
this.ipv4address = getParameter('ipv4address');
|
|
374
291
|
}
|
|
@@ -377,7 +294,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
377
294
|
if (this.ipv4address === '')
|
|
378
295
|
this.ipv4address = undefined;
|
|
379
296
|
}
|
|
380
|
-
// Set the listeningAddressIpv6 for the matter commissioning server
|
|
381
297
|
if (hasParameter('ipv6address')) {
|
|
382
298
|
this.ipv6address = getParameter('ipv6address');
|
|
383
299
|
}
|
|
@@ -386,23 +302,17 @@ export class Matterbridge extends EventEmitter {
|
|
|
386
302
|
if (this.ipv6address === '')
|
|
387
303
|
this.ipv6address = undefined;
|
|
388
304
|
}
|
|
389
|
-
// Initialize PluginManager
|
|
390
305
|
this.plugins = new PluginManager(this);
|
|
391
306
|
await this.plugins.loadFromStorage();
|
|
392
|
-
// Initialize DeviceManager
|
|
393
307
|
this.devices = new DeviceManager(this, this.nodeContext);
|
|
394
|
-
// Get the plugins from node storage and create the plugins node storage contexts
|
|
395
308
|
for (const plugin of this.plugins) {
|
|
396
309
|
const packageJson = await this.plugins.parse(plugin);
|
|
397
310
|
if (packageJson === null && !hasParameter('add')) {
|
|
398
|
-
// Try to reinstall the plugin from npm (for Docker pull and external plugins)
|
|
399
|
-
// We don't do this when the add parameter is set because we shut down the process after adding the plugin
|
|
400
311
|
this.log.info(`Error parsing plugin ${plg}${plugin.name}${nf}. Trying to reinstall it from npm.`);
|
|
401
312
|
try {
|
|
402
313
|
await this.spawnCommand('npm', ['install', '-g', plugin.name, '--omit=dev', '--verbose']);
|
|
403
314
|
this.log.info(`Plugin ${plg}${plugin.name}${nf} reinstalled.`);
|
|
404
315
|
plugin.error = false;
|
|
405
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
406
316
|
}
|
|
407
317
|
catch (error) {
|
|
408
318
|
plugin.error = true;
|
|
@@ -419,7 +329,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
419
329
|
await plugin.nodeContext.set('description', plugin.description);
|
|
420
330
|
await plugin.nodeContext.set('author', plugin.author);
|
|
421
331
|
}
|
|
422
|
-
// Log system info and create .matterbridge directory
|
|
423
332
|
await this.logNodeAndSystemInfo();
|
|
424
333
|
this.log.notice(`Matterbridge version ${this.matterbridgeVersion} ` +
|
|
425
334
|
`${hasParameter('bridge') || (!hasParameter('childbridge') && (await this.nodeContext?.get('bridgeMode', '')) === 'bridge') ? 'mode bridge ' : ''}` +
|
|
@@ -427,7 +336,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
427
336
|
`${hasParameter('controller') ? 'mode controller ' : ''}` +
|
|
428
337
|
`${this.restartMode !== '' ? 'restart mode ' + this.restartMode + ' ' : ''}` +
|
|
429
338
|
`running on ${this.systemInformation.osType} (v.${this.systemInformation.osRelease}) platform ${this.systemInformation.osPlatform} arch ${this.systemInformation.osArch}`);
|
|
430
|
-
// Check node version and throw error
|
|
431
339
|
const minNodeVersion = 18;
|
|
432
340
|
const nodeVersion = process.versions.node;
|
|
433
341
|
const versionMajor = parseInt(nodeVersion.split('.')[0]);
|
|
@@ -435,17 +343,10 @@ export class Matterbridge extends EventEmitter {
|
|
|
435
343
|
this.log.error(`Node version ${versionMajor} is not supported. Please upgrade to ${minNodeVersion} or above.`);
|
|
436
344
|
throw new Error(`Node version ${versionMajor} is not supported. Please upgrade to ${minNodeVersion} or above.`);
|
|
437
345
|
}
|
|
438
|
-
// Register process handlers
|
|
439
346
|
this.registerProcessHandlers();
|
|
440
|
-
// Parse command line
|
|
441
347
|
await this.parseCommandLine();
|
|
442
348
|
this.initialized = true;
|
|
443
349
|
}
|
|
444
|
-
/**
|
|
445
|
-
* Parses the command line arguments and performs the corresponding actions.
|
|
446
|
-
* @private
|
|
447
|
-
* @returns {Promise<void>} A promise that resolves when the command line arguments have been processed, or the process exits.
|
|
448
|
-
*/
|
|
449
350
|
async parseCommandLine() {
|
|
450
351
|
if (hasParameter('help')) {
|
|
451
352
|
this.log.info(`\nUsage: matterbridge [options]\n
|
|
@@ -553,14 +454,12 @@ export class Matterbridge extends EventEmitter {
|
|
|
553
454
|
}
|
|
554
455
|
if (hasParameter('factoryreset')) {
|
|
555
456
|
try {
|
|
556
|
-
// Delete matter storage file
|
|
557
457
|
await fs.unlink(path.join(this.matterbridgeDirectory, this.matterStorageName));
|
|
558
458
|
}
|
|
559
459
|
catch (err) {
|
|
560
460
|
this.log.error(`Error deleting storage: ${err}`);
|
|
561
461
|
}
|
|
562
462
|
try {
|
|
563
|
-
// Delete node storage directory with its subdirectories
|
|
564
463
|
await fs.rm(path.join(this.matterbridgeDirectory, this.nodeStorageName), { recursive: true });
|
|
565
464
|
}
|
|
566
465
|
catch (err) {
|
|
@@ -574,7 +473,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
574
473
|
this.emit('shutdown');
|
|
575
474
|
return;
|
|
576
475
|
}
|
|
577
|
-
// Start the matter storage and create the matterbridge context
|
|
578
476
|
try {
|
|
579
477
|
await this.startMatterStorage('json', path.join(this.matterbridgeDirectory, this.matterStorageName));
|
|
580
478
|
}
|
|
@@ -609,34 +507,28 @@ export class Matterbridge extends EventEmitter {
|
|
|
609
507
|
this.emit('shutdown');
|
|
610
508
|
return;
|
|
611
509
|
}
|
|
612
|
-
// Initialize frontend
|
|
613
510
|
if (getIntParameter('frontend') !== 0 || getIntParameter('frontend') === undefined)
|
|
614
511
|
await this.initializeFrontend(getIntParameter('frontend'));
|
|
615
|
-
// Check each 60 minutes the latest versions
|
|
616
512
|
this.checkUpdateInterval = setInterval(() => {
|
|
617
513
|
this.getMatterbridgeLatestVersion();
|
|
618
514
|
for (const plugin of this.plugins) {
|
|
619
515
|
this.getPluginLatestVersion(plugin);
|
|
620
516
|
}
|
|
621
517
|
}, 60 * 60 * 1000);
|
|
622
|
-
// Start the matterbridge in mode test
|
|
623
518
|
if (hasParameter('test')) {
|
|
624
519
|
this.bridgeMode = 'bridge';
|
|
625
520
|
MatterbridgeDevice.bridgeMode = 'bridge';
|
|
626
521
|
return;
|
|
627
522
|
}
|
|
628
|
-
// Start the matterbridge in mode controller
|
|
629
523
|
if (hasParameter('controller')) {
|
|
630
524
|
this.bridgeMode = 'controller';
|
|
631
525
|
await this.startController();
|
|
632
526
|
return;
|
|
633
527
|
}
|
|
634
|
-
// Check if the bridge mode is set and start matterbridge in bridge mode if not set
|
|
635
528
|
if (!hasParameter('bridge') && !hasParameter('childbridge') && (await this.nodeContext?.get('bridgeMode', '')) === '') {
|
|
636
529
|
this.log.info('Setting default matterbridge start mode to bridge');
|
|
637
530
|
await this.nodeContext?.set('bridgeMode', 'bridge');
|
|
638
531
|
}
|
|
639
|
-
// Start matterbridge in bridge mode
|
|
640
532
|
if (hasParameter('bridge') || (!hasParameter('childbridge') && (await this.nodeContext?.get('bridgeMode', '')) === 'bridge')) {
|
|
641
533
|
this.bridgeMode = 'bridge';
|
|
642
534
|
MatterbridgeDevice.bridgeMode = 'bridge';
|
|
@@ -645,7 +537,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
645
537
|
await this.startBridge();
|
|
646
538
|
return;
|
|
647
539
|
}
|
|
648
|
-
// Start matterbridge in childbridge mode
|
|
649
540
|
if (hasParameter('childbridge') || (!hasParameter('bridge') && (await this.nodeContext?.get('bridgeMode', '')) === 'childbridge')) {
|
|
650
541
|
this.bridgeMode = 'childbridge';
|
|
651
542
|
MatterbridgeDevice.bridgeMode = 'childbridge';
|
|
@@ -655,28 +546,17 @@ export class Matterbridge extends EventEmitter {
|
|
|
655
546
|
return;
|
|
656
547
|
}
|
|
657
548
|
}
|
|
658
|
-
/**
|
|
659
|
-
* Asynchronously loads and starts the registered plugins.
|
|
660
|
-
*
|
|
661
|
-
* This method is responsible for initializing and staarting all enabled plugins.
|
|
662
|
-
* It ensures that each plugin is properly loaded and started before the ridge starts.
|
|
663
|
-
*
|
|
664
|
-
* @returns {Promise<void>} A promise that resolves when all plugins have been loaded and started.
|
|
665
|
-
*/
|
|
666
549
|
async startPlugins() {
|
|
667
|
-
// Check, load and start the plugins
|
|
668
550
|
for (const plugin of this.plugins) {
|
|
669
551
|
plugin.configJson = await this.plugins.loadConfig(plugin);
|
|
670
552
|
plugin.schemaJson = await this.plugins.loadSchema(plugin);
|
|
671
|
-
// Check if the plugin is available
|
|
672
553
|
if (!(await this.plugins.resolve(plugin.path))) {
|
|
673
554
|
this.log.error(`Plugin ${plg}${plugin.name}${er} not found or not validated. Disabling it.`);
|
|
674
555
|
plugin.enabled = false;
|
|
675
556
|
plugin.error = true;
|
|
676
557
|
continue;
|
|
677
558
|
}
|
|
678
|
-
|
|
679
|
-
this.getPluginLatestVersion(plugin); // No await do it asyncronously
|
|
559
|
+
this.getPluginLatestVersion(plugin);
|
|
680
560
|
if (!plugin.enabled) {
|
|
681
561
|
this.log.info(`Plugin ${plg}${plugin.name}${nf} not enabled`);
|
|
682
562
|
continue;
|
|
@@ -691,26 +571,20 @@ export class Matterbridge extends EventEmitter {
|
|
|
691
571
|
plugin.addedDevices = undefined;
|
|
692
572
|
plugin.qrPairingCode = undefined;
|
|
693
573
|
plugin.manualPairingCode = undefined;
|
|
694
|
-
this.plugins.load(plugin, true, 'Matterbridge is starting');
|
|
574
|
+
this.plugins.load(plugin, true, 'Matterbridge is starting');
|
|
695
575
|
}
|
|
696
576
|
this.wssSendRefreshRequired();
|
|
697
577
|
}
|
|
698
|
-
/**
|
|
699
|
-
* Registers the process handlers for uncaughtException, unhandledRejection, SIGINT and SIGTERM.
|
|
700
|
-
* When either of these signals are received, the cleanup method is called with an appropriate message.
|
|
701
|
-
*/
|
|
702
578
|
registerProcessHandlers() {
|
|
703
579
|
this.log.debug(`Registering uncaughtException and unhandledRejection handlers...`);
|
|
704
580
|
process.removeAllListeners('uncaughtException');
|
|
705
581
|
process.removeAllListeners('unhandledRejection');
|
|
706
582
|
this.exceptionHandler = async (error) => {
|
|
707
583
|
this.log.fatal('Unhandled Exception detected at:', error.stack || error, rs);
|
|
708
|
-
// await this.cleanup('Unhandled Exception detected, cleaning up...');
|
|
709
584
|
};
|
|
710
585
|
process.on('uncaughtException', this.exceptionHandler);
|
|
711
586
|
this.rejectionHandler = async (reason, promise) => {
|
|
712
587
|
this.log.fatal('Unhandled Rejection detected at:', promise, 'reason:', reason instanceof Error ? reason.stack : reason, rs);
|
|
713
|
-
// await this.cleanup('Unhandled Rejection detected, cleaning up...');
|
|
714
588
|
};
|
|
715
589
|
process.on('unhandledRejection', this.rejectionHandler);
|
|
716
590
|
this.log.debug(`Registering SIGINT and SIGTERM signal handlers...`);
|
|
@@ -723,9 +597,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
723
597
|
};
|
|
724
598
|
process.on('SIGTERM', this.sigtermHandler);
|
|
725
599
|
}
|
|
726
|
-
/**
|
|
727
|
-
* Deregisters the process uncaughtException, unhandledRejection, SIGINT and SIGTERM signal handlers.
|
|
728
|
-
*/
|
|
729
600
|
deregisterProcesslHandlers() {
|
|
730
601
|
this.log.debug(`Deregistering uncaughtException and unhandledRejection handlers...`);
|
|
731
602
|
if (this.exceptionHandler)
|
|
@@ -742,11 +613,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
742
613
|
process.off('SIGTERM', this.sigtermHandler);
|
|
743
614
|
this.sigtermHandler = undefined;
|
|
744
615
|
}
|
|
745
|
-
/**
|
|
746
|
-
* Logs the node and system information.
|
|
747
|
-
*/
|
|
748
616
|
async logNodeAndSystemInfo() {
|
|
749
|
-
// IP address information
|
|
750
617
|
const networkInterfaces = os.networkInterfaces();
|
|
751
618
|
this.systemInformation.ipv4Address = '';
|
|
752
619
|
this.systemInformation.ipv6Address = '';
|
|
@@ -766,7 +633,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
766
633
|
this.systemInformation.macAddress = detail.mac;
|
|
767
634
|
}
|
|
768
635
|
}
|
|
769
|
-
if (this.systemInformation.ipv4Address !== ''
|
|
636
|
+
if (this.systemInformation.ipv4Address !== '') {
|
|
770
637
|
this.log.debug(`Using interface: '${this.systemInformation.interfaceName}'`);
|
|
771
638
|
this.log.debug(`- with MAC address: '${this.systemInformation.macAddress}'`);
|
|
772
639
|
this.log.debug(`- with IPv4 address: '${this.systemInformation.ipv4Address}'`);
|
|
@@ -774,22 +641,19 @@ export class Matterbridge extends EventEmitter {
|
|
|
774
641
|
break;
|
|
775
642
|
}
|
|
776
643
|
}
|
|
777
|
-
// Node information
|
|
778
644
|
this.systemInformation.nodeVersion = process.versions.node;
|
|
779
645
|
const versionMajor = parseInt(this.systemInformation.nodeVersion.split('.')[0]);
|
|
780
646
|
const versionMinor = parseInt(this.systemInformation.nodeVersion.split('.')[1]);
|
|
781
647
|
const versionPatch = parseInt(this.systemInformation.nodeVersion.split('.')[2]);
|
|
782
|
-
// Host system information
|
|
783
648
|
this.systemInformation.hostname = os.hostname();
|
|
784
649
|
this.systemInformation.user = os.userInfo().username;
|
|
785
|
-
this.systemInformation.osType = os.type();
|
|
786
|
-
this.systemInformation.osRelease = os.release();
|
|
787
|
-
this.systemInformation.osPlatform = os.platform();
|
|
788
|
-
this.systemInformation.osArch = os.arch();
|
|
789
|
-
this.systemInformation.totalMemory = (os.totalmem() / 1024 / 1024 / 1024).toFixed(2) + ' GB';
|
|
790
|
-
this.systemInformation.freeMemory = (os.freemem() / 1024 / 1024 / 1024).toFixed(2) + ' GB';
|
|
791
|
-
this.systemInformation.systemUptime = (os.uptime() / 60 / 60).toFixed(2) + ' hours';
|
|
792
|
-
// Log the system information
|
|
650
|
+
this.systemInformation.osType = os.type();
|
|
651
|
+
this.systemInformation.osRelease = os.release();
|
|
652
|
+
this.systemInformation.osPlatform = os.platform();
|
|
653
|
+
this.systemInformation.osArch = os.arch();
|
|
654
|
+
this.systemInformation.totalMemory = (os.totalmem() / 1024 / 1024 / 1024).toFixed(2) + ' GB';
|
|
655
|
+
this.systemInformation.freeMemory = (os.freemem() / 1024 / 1024 / 1024).toFixed(2) + ' GB';
|
|
656
|
+
this.systemInformation.systemUptime = (os.uptime() / 60 / 60).toFixed(2) + ' hours';
|
|
793
657
|
this.log.debug('Host System Information:');
|
|
794
658
|
this.log.debug(`- Hostname: ${this.systemInformation.hostname}`);
|
|
795
659
|
this.log.debug(`- User: ${this.systemInformation.user}`);
|
|
@@ -805,19 +669,15 @@ export class Matterbridge extends EventEmitter {
|
|
|
805
669
|
this.log.debug(`- Total Memory: ${this.systemInformation.totalMemory}`);
|
|
806
670
|
this.log.debug(`- Free Memory: ${this.systemInformation.freeMemory}`);
|
|
807
671
|
this.log.debug(`- System Uptime: ${this.systemInformation.systemUptime}`);
|
|
808
|
-
// Home directory
|
|
809
672
|
this.homeDirectory = getParameter('homedir') ?? os.homedir();
|
|
810
673
|
this.matterbridgeInformation.homeDirectory = this.homeDirectory;
|
|
811
674
|
this.log.debug(`Home Directory: ${this.homeDirectory}`);
|
|
812
|
-
// Package root directory
|
|
813
675
|
const currentFileDirectory = path.dirname(fileURLToPath(import.meta.url));
|
|
814
676
|
this.rootDirectory = path.resolve(currentFileDirectory, '../');
|
|
815
677
|
this.matterbridgeInformation.rootDirectory = this.rootDirectory;
|
|
816
678
|
this.log.debug(`Root Directory: ${this.rootDirectory}`);
|
|
817
|
-
// Global node_modules directory
|
|
818
679
|
if (this.nodeContext)
|
|
819
680
|
this.globalModulesDirectory = await this.nodeContext.get('globalModulesDirectory', '');
|
|
820
|
-
// First run of Matterbridge so the node storage is empty
|
|
821
681
|
if (this.globalModulesDirectory === '') {
|
|
822
682
|
try {
|
|
823
683
|
this.globalModulesDirectory = await this.getGlobalNodeModules();
|
|
@@ -841,7 +701,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
841
701
|
this.log.error(`Error getting global node_modules directory: ${error}`);
|
|
842
702
|
});
|
|
843
703
|
}
|
|
844
|
-
// Create the data directory .matterbridge in the home directory
|
|
845
704
|
this.matterbridgeDirectory = path.join(this.homeDirectory, '.matterbridge');
|
|
846
705
|
this.matterbridgeInformation.matterbridgeDirectory = this.matterbridgeDirectory;
|
|
847
706
|
try {
|
|
@@ -865,7 +724,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
865
724
|
}
|
|
866
725
|
}
|
|
867
726
|
this.log.debug(`Matterbridge Directory: ${this.matterbridgeDirectory}`);
|
|
868
|
-
// Create the plugin directory Matterbridge in the home directory
|
|
869
727
|
this.matterbridgePluginDirectory = path.join(this.homeDirectory, 'Matterbridge');
|
|
870
728
|
this.matterbridgeInformation.matterbridgePluginDirectory = this.matterbridgePluginDirectory;
|
|
871
729
|
try {
|
|
@@ -889,28 +747,19 @@ export class Matterbridge extends EventEmitter {
|
|
|
889
747
|
}
|
|
890
748
|
}
|
|
891
749
|
this.log.debug(`Matterbridge Plugin Directory: ${this.matterbridgePluginDirectory}`);
|
|
892
|
-
// Matterbridge version
|
|
893
750
|
const packageJson = JSON.parse(await fs.readFile(path.join(this.rootDirectory, 'package.json'), 'utf-8'));
|
|
894
751
|
this.matterbridgeVersion = packageJson.version;
|
|
895
752
|
this.matterbridgeInformation.matterbridgeVersion = this.matterbridgeVersion;
|
|
896
753
|
this.log.debug(`Matterbridge Version: ${this.matterbridgeVersion}`);
|
|
897
|
-
// Matterbridge latest version
|
|
898
754
|
if (this.nodeContext)
|
|
899
755
|
this.matterbridgeLatestVersion = await this.nodeContext.get('matterbridgeLatestVersion', '');
|
|
900
756
|
this.log.debug(`Matterbridge Latest Version: ${this.matterbridgeLatestVersion}`);
|
|
901
757
|
this.getMatterbridgeLatestVersion();
|
|
902
|
-
// Current working directory
|
|
903
758
|
const currentDir = process.cwd();
|
|
904
759
|
this.log.debug(`Current Working Directory: ${currentDir}`);
|
|
905
|
-
// Command line arguments (excluding 'node' and the script name)
|
|
906
760
|
const cmdArgs = process.argv.slice(2).join(' ');
|
|
907
761
|
this.log.debug(`Command Line Arguments: ${cmdArgs}`);
|
|
908
762
|
}
|
|
909
|
-
/**
|
|
910
|
-
* Retrieves the latest version of a package from the npm registry.
|
|
911
|
-
* @param packageName - The name of the package.
|
|
912
|
-
* @returns A Promise that resolves to the latest version of the package.
|
|
913
|
-
*/
|
|
914
763
|
async getLatestVersion(packageName) {
|
|
915
764
|
return new Promise((resolve, reject) => {
|
|
916
765
|
this.execRunningCount++;
|
|
@@ -925,10 +774,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
925
774
|
});
|
|
926
775
|
});
|
|
927
776
|
}
|
|
928
|
-
/**
|
|
929
|
-
* Retrieves the path to the global Node.js modules directory.
|
|
930
|
-
* @returns A promise that resolves to the path of the global Node.js modules directory.
|
|
931
|
-
*/
|
|
932
777
|
async getGlobalNodeModules() {
|
|
933
778
|
return new Promise((resolve, reject) => {
|
|
934
779
|
this.execRunningCount++;
|
|
@@ -943,11 +788,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
943
788
|
});
|
|
944
789
|
});
|
|
945
790
|
}
|
|
946
|
-
/**
|
|
947
|
-
* Retrieves the latest version of Matterbridge and performs necessary actions based on the version comparison.
|
|
948
|
-
* @private
|
|
949
|
-
* @returns {Promise<void>} A promise that resolves when the latest version is retrieved and actions are performed.
|
|
950
|
-
*/
|
|
951
791
|
async getMatterbridgeLatestVersion() {
|
|
952
792
|
this.getLatestVersion('matterbridge')
|
|
953
793
|
.then(async (matterbridgeLatestVersion) => {
|
|
@@ -964,19 +804,8 @@ export class Matterbridge extends EventEmitter {
|
|
|
964
804
|
})
|
|
965
805
|
.catch((error) => {
|
|
966
806
|
this.log.error(`Error getting Matterbridge latest version: ${error.message}`);
|
|
967
|
-
// error.stack && this.log.debug(error.stack);
|
|
968
807
|
});
|
|
969
808
|
}
|
|
970
|
-
/**
|
|
971
|
-
* Retrieves the latest version of a plugin and updates the plugin's latestVersion property.
|
|
972
|
-
* If the plugin's version is different from the latest version, logs a warning message.
|
|
973
|
-
* If the plugin's version is the same as the latest version, logs an info message.
|
|
974
|
-
* If there is an error retrieving the latest version, logs an error message.
|
|
975
|
-
*
|
|
976
|
-
* @private
|
|
977
|
-
* @param {RegisteredPlugin} plugin - The plugin for which to retrieve the latest version.
|
|
978
|
-
* @returns {Promise<void>} A promise that resolves when the latest version is retrieved and actions are performed.
|
|
979
|
-
*/
|
|
980
809
|
async getPluginLatestVersion(plugin) {
|
|
981
810
|
this.getLatestVersion(plugin.name)
|
|
982
811
|
.then(async (latestVersion) => {
|
|
@@ -988,54 +817,40 @@ export class Matterbridge extends EventEmitter {
|
|
|
988
817
|
})
|
|
989
818
|
.catch((error) => {
|
|
990
819
|
this.log.error(`Error getting ${plg}${plugin.name}${er} latest version: ${error.message}`);
|
|
991
|
-
// error.stack && this.log.debug(error.stack);
|
|
992
820
|
});
|
|
993
821
|
}
|
|
994
|
-
/**
|
|
995
|
-
* Creates a MatterLogger function to show the matter.js log messages in AnsiLogger (for the frontend).
|
|
996
|
-
*
|
|
997
|
-
* @returns {Function} The MatterLogger function.
|
|
998
|
-
*/
|
|
999
822
|
createMatterLogger() {
|
|
1000
|
-
const matterLogger = new AnsiLogger({ logName: 'Matter', logTimestampFormat: 4
|
|
823
|
+
const matterLogger = new AnsiLogger({ logName: 'Matter', logTimestampFormat: 4, logLevel: "debug" });
|
|
1001
824
|
return (_level, formattedLog) => {
|
|
1002
825
|
const logger = formattedLog.slice(44, 44 + 20).trim();
|
|
1003
826
|
const message = formattedLog.slice(65);
|
|
1004
827
|
matterLogger.logName = logger;
|
|
1005
828
|
switch (_level) {
|
|
1006
829
|
case MatterLogLevel.DEBUG:
|
|
1007
|
-
matterLogger.log("debug"
|
|
830
|
+
matterLogger.log("debug", message);
|
|
1008
831
|
break;
|
|
1009
832
|
case MatterLogLevel.INFO:
|
|
1010
|
-
matterLogger.log("info"
|
|
833
|
+
matterLogger.log("info", message);
|
|
1011
834
|
break;
|
|
1012
835
|
case MatterLogLevel.NOTICE:
|
|
1013
|
-
matterLogger.log("notice"
|
|
836
|
+
matterLogger.log("notice", message);
|
|
1014
837
|
break;
|
|
1015
838
|
case MatterLogLevel.WARN:
|
|
1016
|
-
matterLogger.log("warn"
|
|
839
|
+
matterLogger.log("warn", message);
|
|
1017
840
|
break;
|
|
1018
841
|
case MatterLogLevel.ERROR:
|
|
1019
|
-
matterLogger.log("error"
|
|
842
|
+
matterLogger.log("error", message);
|
|
1020
843
|
break;
|
|
1021
844
|
case MatterLogLevel.FATAL:
|
|
1022
|
-
matterLogger.log("fatal"
|
|
845
|
+
matterLogger.log("fatal", message);
|
|
1023
846
|
break;
|
|
1024
847
|
default:
|
|
1025
|
-
matterLogger.log("debug"
|
|
848
|
+
matterLogger.log("debug", message);
|
|
1026
849
|
break;
|
|
1027
850
|
}
|
|
1028
851
|
};
|
|
1029
852
|
}
|
|
1030
|
-
/**
|
|
1031
|
-
* Creates a Matter File Logger.
|
|
1032
|
-
*
|
|
1033
|
-
* @param {string} filePath - The path to the log file.
|
|
1034
|
-
* @param {boolean} [unlink=false] - Whether to unlink the log file before creating a new one.
|
|
1035
|
-
* @returns {Function} - A function that logs formatted messages to the log file.
|
|
1036
|
-
*/
|
|
1037
853
|
async createMatterFileLogger(filePath, unlink = false) {
|
|
1038
|
-
// 2024-08-21 08:55:19.488 DEBUG InteractionMessenger Sending DataReport chunk with 28 attributes and 0 events: 1004 bytes
|
|
1039
854
|
let fileSize = 0;
|
|
1040
855
|
if (unlink) {
|
|
1041
856
|
try {
|
|
@@ -1084,83 +899,53 @@ export class Matterbridge extends EventEmitter {
|
|
|
1084
899
|
}
|
|
1085
900
|
};
|
|
1086
901
|
}
|
|
1087
|
-
/**
|
|
1088
|
-
* Update matterbridge and cleanup.
|
|
1089
|
-
*/
|
|
1090
902
|
async updateProcess() {
|
|
1091
903
|
await this.cleanup('updating...', false);
|
|
1092
904
|
}
|
|
1093
|
-
/**
|
|
1094
|
-
* Restarts the process by spawning a new process and exiting the current process.
|
|
1095
|
-
*/
|
|
1096
905
|
async restartProcess() {
|
|
1097
906
|
await this.cleanup('restarting...', true);
|
|
1098
907
|
}
|
|
1099
|
-
/**
|
|
1100
|
-
* Shut down the process by exiting the current process.
|
|
1101
|
-
*/
|
|
1102
908
|
async shutdownProcess() {
|
|
1103
909
|
await this.cleanup('shutting down...', false);
|
|
1104
910
|
}
|
|
1105
|
-
/**
|
|
1106
|
-
* Shut down the process and reset.
|
|
1107
|
-
*/
|
|
1108
911
|
async unregisterAndShutdownProcess() {
|
|
1109
912
|
this.log.info('Unregistering all devices and shutting down...');
|
|
1110
|
-
for (const plugin of this.plugins
|
|
913
|
+
for (const plugin of this.plugins) {
|
|
1111
914
|
await this.removeAllBridgedDevices(plugin.name);
|
|
1112
915
|
}
|
|
1113
916
|
await this.cleanup('unregistered all devices and shutting down...', false);
|
|
1114
917
|
}
|
|
1115
|
-
/**
|
|
1116
|
-
* Shut down the process and reset.
|
|
1117
|
-
*/
|
|
1118
918
|
async shutdownProcessAndReset() {
|
|
1119
919
|
await this.cleanup('shutting down with reset...', false);
|
|
1120
920
|
}
|
|
1121
|
-
/**
|
|
1122
|
-
* Shut down the process and factory reset.
|
|
1123
|
-
*/
|
|
1124
921
|
async shutdownProcessAndFactoryReset() {
|
|
1125
922
|
await this.cleanup('shutting down with factory reset...', false);
|
|
1126
923
|
}
|
|
1127
|
-
/**
|
|
1128
|
-
* Cleans up the Matterbridge instance.
|
|
1129
|
-
* @param message - The cleanup message.
|
|
1130
|
-
* @param restart - Indicates whether to restart the instance after cleanup. Default is `false`.
|
|
1131
|
-
* @returns A promise that resolves when the cleanup is completed.
|
|
1132
|
-
*/
|
|
1133
924
|
async cleanup(message, restart = false) {
|
|
1134
925
|
if (this.initialized && !this.hasCleanupStarted) {
|
|
1135
926
|
this.hasCleanupStarted = true;
|
|
1136
927
|
this.log.info(message);
|
|
1137
|
-
// Deregisters the process handlers
|
|
1138
928
|
this.deregisterProcesslHandlers();
|
|
1139
|
-
// Clear the start matter interval
|
|
1140
929
|
if (this.startMatterInterval) {
|
|
1141
930
|
clearInterval(this.startMatterInterval);
|
|
1142
931
|
this.startMatterInterval = undefined;
|
|
1143
932
|
this.log.debug('Start matter interval cleared');
|
|
1144
933
|
}
|
|
1145
|
-
// Clear the check update interval
|
|
1146
934
|
if (this.checkUpdateInterval) {
|
|
1147
935
|
clearInterval(this.checkUpdateInterval);
|
|
1148
936
|
this.checkUpdateInterval = undefined;
|
|
1149
937
|
this.log.debug('Check update interval cleared');
|
|
1150
938
|
}
|
|
1151
|
-
// Clear the configure timeout
|
|
1152
939
|
if (this.configureTimeout) {
|
|
1153
940
|
clearTimeout(this.configureTimeout);
|
|
1154
941
|
this.configureTimeout = undefined;
|
|
1155
942
|
this.log.debug('Matterbridge configure timeout cleared');
|
|
1156
943
|
}
|
|
1157
|
-
// Clear the reachability timeout
|
|
1158
944
|
if (this.reachabilityTimeout) {
|
|
1159
945
|
clearTimeout(this.reachabilityTimeout);
|
|
1160
946
|
this.reachabilityTimeout = undefined;
|
|
1161
947
|
this.log.debug('Matterbridge reachability timeout cleared');
|
|
1162
948
|
}
|
|
1163
|
-
// Calling the shutdown method of each plugin and clear the reachability timeout
|
|
1164
949
|
for (const plugin of this.plugins) {
|
|
1165
950
|
if (!plugin.enabled || plugin.error)
|
|
1166
951
|
continue;
|
|
@@ -1171,29 +956,24 @@ export class Matterbridge extends EventEmitter {
|
|
|
1171
956
|
this.log.debug(`Plugin ${plg}${plugin.name}${db} reachability timeout cleared`);
|
|
1172
957
|
}
|
|
1173
958
|
}
|
|
1174
|
-
// Close the http server
|
|
1175
959
|
if (this.httpServer) {
|
|
1176
960
|
this.httpServer.close();
|
|
1177
961
|
this.httpServer.removeAllListeners();
|
|
1178
962
|
this.httpServer = undefined;
|
|
1179
963
|
this.log.debug('Frontend http server closed successfully');
|
|
1180
964
|
}
|
|
1181
|
-
// Close the https server
|
|
1182
965
|
if (this.httpsServer) {
|
|
1183
966
|
this.httpsServer.close();
|
|
1184
967
|
this.httpsServer.removeAllListeners();
|
|
1185
968
|
this.httpsServer = undefined;
|
|
1186
969
|
this.log.debug('Frontend https server closed successfully');
|
|
1187
970
|
}
|
|
1188
|
-
// Remove listeners from the express app
|
|
1189
971
|
if (this.expressApp) {
|
|
1190
972
|
this.expressApp.removeAllListeners();
|
|
1191
973
|
this.expressApp = undefined;
|
|
1192
974
|
this.log.debug('Frontend app closed successfully');
|
|
1193
975
|
}
|
|
1194
|
-
// Close the WebSocket server
|
|
1195
976
|
if (this.webSocketServer) {
|
|
1196
|
-
// Close all active connections
|
|
1197
977
|
this.webSocketServer.clients.forEach((client) => {
|
|
1198
978
|
if (client.readyState === WebSocket.OPEN) {
|
|
1199
979
|
client.close();
|
|
@@ -1209,35 +989,26 @@ export class Matterbridge extends EventEmitter {
|
|
|
1209
989
|
});
|
|
1210
990
|
this.webSocketServer = undefined;
|
|
1211
991
|
}
|
|
1212
|
-
// Closing matter
|
|
1213
992
|
await this.stopMatterServer();
|
|
1214
|
-
// Closing matter storage
|
|
1215
993
|
await this.stopMatterStorage();
|
|
1216
|
-
// Remove the matterfilelogger
|
|
1217
994
|
try {
|
|
1218
995
|
Logger.removeLogger('matterfilelogger');
|
|
1219
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
1220
996
|
}
|
|
1221
997
|
catch (error) {
|
|
1222
|
-
// this.log.debug(`Error removing the matterfilelogger for file ${CYAN}${path.join(this.matterbridgeDirectory, this.matterLoggerFile)}${er}: ${error instanceof Error ? error.message : error}`);
|
|
1223
998
|
}
|
|
1224
|
-
// Serialize registeredDevices
|
|
1225
999
|
if (this.nodeStorage && this.nodeContext) {
|
|
1226
1000
|
this.log.info('Saving registered devices...');
|
|
1227
1001
|
const serializedRegisteredDevices = [];
|
|
1228
1002
|
this.devices.forEach(async (device) => {
|
|
1229
1003
|
const serializedMatterbridgeDevice = device.serialize();
|
|
1230
|
-
// this.log.info(`- ${serializedMatterbridgeDevice.deviceName}${rs}\n`, serializedMatterbridgeDevice);
|
|
1231
1004
|
if (serializedMatterbridgeDevice)
|
|
1232
1005
|
serializedRegisteredDevices.push(serializedMatterbridgeDevice);
|
|
1233
1006
|
});
|
|
1234
1007
|
await this.nodeContext.set('devices', serializedRegisteredDevices);
|
|
1235
1008
|
this.log.info(`Saved registered devices (${serializedRegisteredDevices?.length})`);
|
|
1236
|
-
// Clear nodeContext and nodeStorage (they just need 1000ms to write the data to disk)
|
|
1237
1009
|
this.log.debug(`Closing node storage context for ${plg}Matterbridge${db}...`);
|
|
1238
1010
|
await this.nodeContext.close();
|
|
1239
1011
|
this.nodeContext = undefined;
|
|
1240
|
-
// Clear nodeContext for each plugin (they just need 1000ms to write the data to disk)
|
|
1241
1012
|
for (const plugin of this.plugins) {
|
|
1242
1013
|
if (plugin.nodeContext) {
|
|
1243
1014
|
this.log.debug(`Closing node storage context for plugin ${plg}${plugin.name}${db}...`);
|
|
@@ -1268,16 +1039,13 @@ export class Matterbridge extends EventEmitter {
|
|
|
1268
1039
|
}
|
|
1269
1040
|
else {
|
|
1270
1041
|
if (message === 'shutting down with reset...') {
|
|
1271
|
-
// Delete matter storage file
|
|
1272
1042
|
this.log.info('Resetting Matterbridge commissioning information...');
|
|
1273
1043
|
await fs.unlink(path.join(this.matterbridgeDirectory, this.matterStorageName));
|
|
1274
1044
|
this.log.info('Reset done! Remove all paired devices from the controllers.');
|
|
1275
1045
|
}
|
|
1276
1046
|
if (message === 'shutting down with factory reset...') {
|
|
1277
|
-
// Delete matter storage file
|
|
1278
1047
|
this.log.info('Resetting Matterbridge commissioning information...');
|
|
1279
1048
|
await fs.unlink(path.join(this.matterbridgeDirectory, this.matterStorageName));
|
|
1280
|
-
// Delete node storage directory with its subdirectories
|
|
1281
1049
|
this.log.info('Resetting Matterbridge storage...');
|
|
1282
1050
|
await fs.rm(path.join(this.matterbridgeDirectory, this.nodeStorageName), { recursive: true });
|
|
1283
1051
|
this.log.info('Factory reset done! Remove all paired devices from the controllers.');
|
|
@@ -1290,33 +1058,19 @@ export class Matterbridge extends EventEmitter {
|
|
|
1290
1058
|
this.initialized = false;
|
|
1291
1059
|
}
|
|
1292
1060
|
}
|
|
1293
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
1294
1061
|
async addBridgedEndpoint(pluginName, device) {
|
|
1295
|
-
// Nothing to do here
|
|
1296
1062
|
}
|
|
1297
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
1298
1063
|
async removeBridgedEndpoint(pluginName, device) {
|
|
1299
|
-
// Nothing to do here
|
|
1300
1064
|
}
|
|
1301
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
1302
1065
|
async removeAllBridgedEndpoints(pluginName) {
|
|
1303
|
-
// Nothing to do here
|
|
1304
1066
|
}
|
|
1305
|
-
/**
|
|
1306
|
-
* Adds a bridged device to the Matterbridge.
|
|
1307
|
-
* @param pluginName - The name of the plugin.
|
|
1308
|
-
* @param device - The bridged device to add.
|
|
1309
|
-
* @returns {Promise<void>} - A promise that resolves when the device is added.
|
|
1310
|
-
*/
|
|
1311
1067
|
async addBridgedDevice(pluginName, device) {
|
|
1312
1068
|
this.log.debug(`Adding bridged device ${dev}${device.deviceName}${db} (${zb}${device.name}${db}) for plugin ${plg}${pluginName}${db}`);
|
|
1313
|
-
// Check if the plugin is registered
|
|
1314
1069
|
const plugin = this.plugins.get(pluginName);
|
|
1315
1070
|
if (!plugin) {
|
|
1316
1071
|
this.log.error(`Error adding bridged device ${dev}${device.deviceName}${er} (${zb}${device.name}${er}) plugin ${plg}${pluginName}${er} not found`);
|
|
1317
1072
|
return;
|
|
1318
1073
|
}
|
|
1319
|
-
// Register and add the device to matterbridge aggregator in bridge mode
|
|
1320
1074
|
if (this.bridgeMode === 'bridge') {
|
|
1321
1075
|
if (!this.matterAggregator) {
|
|
1322
1076
|
this.log.error(`Adding bridged device ${dev}${device.deviceName}${er} (${zb}${device.name}${er}) for plugin ${plg}${pluginName}${er} error: matterAggregator not found`);
|
|
@@ -1324,11 +1078,8 @@ export class Matterbridge extends EventEmitter {
|
|
|
1324
1078
|
}
|
|
1325
1079
|
this.matterAggregator.addBridgedDevice(device);
|
|
1326
1080
|
}
|
|
1327
|
-
// The first time create the commissioning server and the aggregator for DynamicPlatform
|
|
1328
|
-
// Register and add the device in childbridge mode
|
|
1329
1081
|
if (this.bridgeMode === 'childbridge') {
|
|
1330
1082
|
if (plugin.type === 'AccessoryPlatform') {
|
|
1331
|
-
// Check if the plugin is locked with the commissioning server
|
|
1332
1083
|
if (!plugin.locked) {
|
|
1333
1084
|
plugin.locked = true;
|
|
1334
1085
|
plugin.storageContext = await this.importCommissioningServerContext(plugin.name, device);
|
|
@@ -1342,7 +1093,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1342
1093
|
}
|
|
1343
1094
|
}
|
|
1344
1095
|
if (plugin.type === 'DynamicPlatform') {
|
|
1345
|
-
// Check if the plugin is locked with the commissioning server and the aggregator
|
|
1346
1096
|
if (!plugin.locked) {
|
|
1347
1097
|
plugin.locked = true;
|
|
1348
1098
|
this.log.debug(`Creating commissioning server context for ${plg}${plugin.name}${db}`);
|
|
@@ -1363,25 +1113,16 @@ export class Matterbridge extends EventEmitter {
|
|
|
1363
1113
|
plugin.registeredDevices++;
|
|
1364
1114
|
if (plugin.addedDevices !== undefined)
|
|
1365
1115
|
plugin.addedDevices++;
|
|
1366
|
-
// Add the device to the DeviceManager
|
|
1367
1116
|
this.devices.set(device);
|
|
1368
1117
|
this.log.info(`Added and registered bridged device (${plugin.registeredDevices}/${plugin.addedDevices}) ${dev}${device.deviceName}${nf} (${dev}${device.name}${nf}) for plugin ${plg}${pluginName}${nf}`);
|
|
1369
1118
|
}
|
|
1370
|
-
/**
|
|
1371
|
-
* Removes a bridged device from the Matterbridge.
|
|
1372
|
-
* @param pluginName - The name of the plugin.
|
|
1373
|
-
* @param device - The device to be removed.
|
|
1374
|
-
* @returns A Promise that resolves when the device is successfully removed.
|
|
1375
|
-
*/
|
|
1376
1119
|
async removeBridgedDevice(pluginName, device) {
|
|
1377
1120
|
this.log.debug(`Removing bridged device ${dev}${device.deviceName}${db} (${zb}${device.name}${db}) for plugin ${plg}${pluginName}${db}`);
|
|
1378
|
-
// Check if the plugin is registered
|
|
1379
1121
|
const plugin = this.plugins.get(pluginName);
|
|
1380
1122
|
if (!plugin) {
|
|
1381
1123
|
this.log.error(`Error removing bridged device ${dev}${device.deviceName}${er} (${zb}${device.name}${er}) for plugin ${plg}${pluginName}${er}: plugin not found`);
|
|
1382
1124
|
return;
|
|
1383
1125
|
}
|
|
1384
|
-
// Remove the device from matterbridge aggregator in bridge mode
|
|
1385
1126
|
if (this.bridgeMode === 'bridge') {
|
|
1386
1127
|
if (!this.matterAggregator) {
|
|
1387
1128
|
this.log.error(`Error removing bridged device ${dev}${device.deviceName}${er} (${zb}${device.name}${er}) for plugin ${plg}${pluginName}${er}: matterAggregator not found`);
|
|
@@ -1391,8 +1132,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1391
1132
|
device.setBridgedDeviceReachability(false);
|
|
1392
1133
|
device.getClusterServerById(BridgedDeviceBasicInformation.Cluster.id)?.triggerReachableChangedEvent({ reachableNewValue: false });
|
|
1393
1134
|
}
|
|
1394
|
-
// device.getClusterServerById(BridgedDeviceBasicInformation.Cluster.id)?.triggerShutDownEvent({});
|
|
1395
|
-
// device.getClusterServerById(BridgedDeviceBasicInformation.Cluster.id)?.triggerLeaveEvent({});
|
|
1396
1135
|
this.matterAggregator?.removeBridgedDevice(device);
|
|
1397
1136
|
this.log.info(`Removed bridged device(${plugin.registeredDevices}/${plugin.addedDevices}) ${dev}${device.deviceName}${nf} (${zb}${device.name}${nf}) for plugin ${plg}${pluginName}${nf}`);
|
|
1398
1137
|
if (plugin.registeredDevices !== undefined)
|
|
@@ -1400,7 +1139,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1400
1139
|
if (plugin.addedDevices !== undefined)
|
|
1401
1140
|
plugin.addedDevices--;
|
|
1402
1141
|
}
|
|
1403
|
-
// Remove the device in childbridge mode
|
|
1404
1142
|
if (this.bridgeMode === 'childbridge') {
|
|
1405
1143
|
if (plugin.type === 'AccessoryPlatform') {
|
|
1406
1144
|
if (!plugin.commissioningServer) {
|
|
@@ -1424,22 +1162,14 @@ export class Matterbridge extends EventEmitter {
|
|
|
1424
1162
|
plugin.registeredDevices--;
|
|
1425
1163
|
if (plugin.addedDevices !== undefined)
|
|
1426
1164
|
plugin.addedDevices--;
|
|
1427
|
-
// Remove the commissioning server
|
|
1428
1165
|
if (plugin.registeredDevices === 0 && plugin.addedDevices === 0 && plugin.commissioningServer) {
|
|
1429
1166
|
this.matterServer?.removeCommissioningServer(plugin.commissioningServer);
|
|
1430
1167
|
plugin.commissioningServer = undefined;
|
|
1431
1168
|
this.log.info(`Removed commissioning server for plugin ${plg}${pluginName}${nf}`);
|
|
1432
1169
|
}
|
|
1433
1170
|
}
|
|
1434
|
-
// Remove the device from the DeviceManager
|
|
1435
1171
|
this.devices.remove(device);
|
|
1436
1172
|
}
|
|
1437
|
-
/**
|
|
1438
|
-
* Removes all bridged devices associated with a specific plugin.
|
|
1439
|
-
*
|
|
1440
|
-
* @param pluginName - The name of the plugin.
|
|
1441
|
-
* @returns A promise that resolves when all devices have been removed.
|
|
1442
|
-
*/
|
|
1443
1173
|
async removeAllBridgedDevices(pluginName) {
|
|
1444
1174
|
this.log.debug(`Removing all bridged devices for plugin ${plg}${pluginName}${db}`);
|
|
1445
1175
|
this.devices.forEach(async (device) => {
|
|
@@ -1448,13 +1178,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
1448
1178
|
}
|
|
1449
1179
|
});
|
|
1450
1180
|
}
|
|
1451
|
-
/**
|
|
1452
|
-
* Starts the Matterbridge in bridge mode.
|
|
1453
|
-
* @private
|
|
1454
|
-
* @returns {Promise<void>} A promise that resolves when the Matterbridge is started.
|
|
1455
|
-
*/
|
|
1456
1181
|
async startBridge() {
|
|
1457
|
-
// Plugins are configured by a timer when matter server is started and plugin.configured is set to true
|
|
1458
1182
|
if (!this.storageManager)
|
|
1459
1183
|
throw new Error('No storage manager initialized');
|
|
1460
1184
|
if (!this.matterbridgeContext)
|
|
@@ -1473,7 +1197,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1473
1197
|
let failCount = 0;
|
|
1474
1198
|
this.startMatterInterval = setInterval(async () => {
|
|
1475
1199
|
for (const plugin of this.plugins) {
|
|
1476
|
-
// new code to not start the bridge if one plugin is in error cause the controllers will delete the devices loosing all the configuration
|
|
1477
1200
|
if (!plugin.enabled)
|
|
1478
1201
|
continue;
|
|
1479
1202
|
if (plugin.error) {
|
|
@@ -1498,18 +1221,15 @@ export class Matterbridge extends EventEmitter {
|
|
|
1498
1221
|
clearInterval(this.startMatterInterval);
|
|
1499
1222
|
this.startMatterInterval = undefined;
|
|
1500
1223
|
this.log.debug('Cleared startMatterInterval interval for Matterbridge');
|
|
1501
|
-
// Start the Matter server
|
|
1502
1224
|
await this.startMatterServer();
|
|
1503
1225
|
this.log.notice('Matter server started');
|
|
1504
|
-
// Show the QR code for commissioning or log the already commissioned message
|
|
1505
1226
|
await this.showCommissioningQRCode(this.commissioningServer, this.matterbridgeContext, this.nodeContext, 'Matterbridge');
|
|
1506
|
-
// Configure the plugins
|
|
1507
1227
|
this.configureTimeout = setTimeout(async () => {
|
|
1508
1228
|
for (const plugin of this.plugins) {
|
|
1509
1229
|
if (!plugin.enabled || !plugin.loaded || !plugin.started || plugin.error)
|
|
1510
1230
|
continue;
|
|
1511
1231
|
try {
|
|
1512
|
-
await this.plugins.configure(plugin);
|
|
1232
|
+
await this.plugins.configure(plugin);
|
|
1513
1233
|
}
|
|
1514
1234
|
catch (error) {
|
|
1515
1235
|
plugin.error = true;
|
|
@@ -1518,7 +1238,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1518
1238
|
}
|
|
1519
1239
|
this.wssSendRefreshRequired();
|
|
1520
1240
|
}, 30 * 1000);
|
|
1521
|
-
// Setting reachability to true
|
|
1522
1241
|
this.reachabilityTimeout = setTimeout(() => {
|
|
1523
1242
|
this.log.info(`Setting reachability to true for ${plg}Matterbridge${db}`);
|
|
1524
1243
|
if (this.commissioningServer)
|
|
@@ -1528,14 +1247,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
1528
1247
|
}, 60 * 1000);
|
|
1529
1248
|
}, 1000);
|
|
1530
1249
|
}
|
|
1531
|
-
/**
|
|
1532
|
-
* Starts the Matterbridge in childbridge mode.
|
|
1533
|
-
* @private
|
|
1534
|
-
* @returns {Promise<void>} A promise that resolves when the Matterbridge is started.
|
|
1535
|
-
*/
|
|
1536
1250
|
async startChildbridge() {
|
|
1537
|
-
// Matterbridge.addBridgedDevice creates the commissionig servers and add the devices to the the commissioning server or to the aggregator
|
|
1538
|
-
// Plugins are configured by a timer when matter server is started and plugin.configured is set to true
|
|
1539
1251
|
if (!this.storageManager)
|
|
1540
1252
|
throw new Error('No storage manager initialized');
|
|
1541
1253
|
this.matterServer = this.createMatterServer(this.storageManager);
|
|
@@ -1545,7 +1257,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1545
1257
|
this.startMatterInterval = setInterval(async () => {
|
|
1546
1258
|
let allStarted = true;
|
|
1547
1259
|
for (const plugin of this.plugins) {
|
|
1548
|
-
// new code to not start the bridge if one plugin is in error cause the controllers will delete the devices loosing all the configuration
|
|
1549
1260
|
if (!plugin.enabled)
|
|
1550
1261
|
continue;
|
|
1551
1262
|
if (plugin.error) {
|
|
@@ -1573,16 +1284,14 @@ export class Matterbridge extends EventEmitter {
|
|
|
1573
1284
|
clearInterval(this.startMatterInterval);
|
|
1574
1285
|
this.startMatterInterval = undefined;
|
|
1575
1286
|
this.log.debug('Cleared startMatterInterval interval in childbridge mode');
|
|
1576
|
-
// Start the Matter server
|
|
1577
1287
|
await this.startMatterServer();
|
|
1578
1288
|
this.log.notice('Matter server started');
|
|
1579
|
-
// Configure the plugins
|
|
1580
1289
|
this.configureTimeout = setTimeout(async () => {
|
|
1581
1290
|
for (const plugin of this.plugins) {
|
|
1582
1291
|
if (!plugin.enabled || !plugin.loaded || !plugin.started || plugin.error)
|
|
1583
1292
|
continue;
|
|
1584
1293
|
try {
|
|
1585
|
-
await this.plugins.configure(plugin);
|
|
1294
|
+
await this.plugins.configure(plugin);
|
|
1586
1295
|
}
|
|
1587
1296
|
catch (error) {
|
|
1588
1297
|
plugin.error = true;
|
|
@@ -1611,7 +1320,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1611
1320
|
continue;
|
|
1612
1321
|
}
|
|
1613
1322
|
await this.showCommissioningQRCode(plugin.commissioningServer, plugin.storageContext, plugin.nodeContext, plugin.name);
|
|
1614
|
-
// Setting reachability to true
|
|
1615
1323
|
plugin.reachabilityTimeout = setTimeout(() => {
|
|
1616
1324
|
this.log.info(`Setting reachability to true for ${plg}${plugin.name}${db}`);
|
|
1617
1325
|
if (plugin.commissioningServer)
|
|
@@ -1624,11 +1332,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1624
1332
|
}
|
|
1625
1333
|
}, 1000);
|
|
1626
1334
|
}
|
|
1627
|
-
/**
|
|
1628
|
-
* Starts the Matterbridge controller.
|
|
1629
|
-
* @private
|
|
1630
|
-
* @returns {Promise<void>} A promise that resolves when the Matterbridge is started.
|
|
1631
|
-
*/
|
|
1632
1335
|
async startController() {
|
|
1633
1336
|
if (!this.storageManager) {
|
|
1634
1337
|
this.log.error('No storage manager initialized');
|
|
@@ -1691,7 +1394,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
1691
1394
|
const nodeId = await this.commissioningController.commissionNode(options);
|
|
1692
1395
|
this.log.info(`Commissioning successfully done with nodeId: ${nodeId}`);
|
|
1693
1396
|
this.log.info('ActiveSessionInformation:', this.commissioningController.getActiveSessionInformation());
|
|
1694
|
-
}
|
|
1397
|
+
}
|
|
1695
1398
|
if (hasParameter('unpairall')) {
|
|
1696
1399
|
this.log.info('***Commissioning controller unpairing all nodes...');
|
|
1697
1400
|
const nodeIds = this.commissioningController.getCommissionedNodes();
|
|
@@ -1702,8 +1405,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1702
1405
|
return;
|
|
1703
1406
|
}
|
|
1704
1407
|
if (hasParameter('discover')) {
|
|
1705
|
-
// const discover = await this.commissioningController.discoverCommissionableDevices({ productId: 0x8000, deviceType: 0xfff1 });
|
|
1706
|
-
// console.log(discover);
|
|
1707
1408
|
}
|
|
1708
1409
|
if (!this.commissioningController.isCommissioned()) {
|
|
1709
1410
|
this.log.info('***Commissioning controller is not commissioned: use matterbridge -controller -pairingcode [pairingcode] to commission a device');
|
|
@@ -1744,12 +1445,10 @@ export class Matterbridge extends EventEmitter {
|
|
|
1744
1445
|
},
|
|
1745
1446
|
});
|
|
1746
1447
|
node.logStructure();
|
|
1747
|
-
// Get the interaction client
|
|
1748
1448
|
this.log.info('Getting the interaction client');
|
|
1749
1449
|
const interactionClient = await node.getInteractionClient();
|
|
1750
1450
|
let cluster;
|
|
1751
1451
|
let attributes;
|
|
1752
|
-
// Log BasicInformationCluster
|
|
1753
1452
|
cluster = BasicInformationCluster;
|
|
1754
1453
|
attributes = await interactionClient.getMultipleAttributes({
|
|
1755
1454
|
attributes: [{ clusterId: cluster.id }],
|
|
@@ -1759,7 +1458,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1759
1458
|
attributes.forEach((attribute) => {
|
|
1760
1459
|
this.log.info(`- endpoint ${or}${attribute.path.endpointId}${nf} cluster ${hk}${getClusterNameById(attribute.path.clusterId)}${nf} (${hk}0x${attribute.path.clusterId.toString(16)}${nf}) attribute ${zb}${attribute.path.attributeName}${nf} (${zb}0x${attribute.path.attributeId.toString(16)}${nf}): ${typeof attribute.value === 'object' ? stringify(attribute.value) : attribute.value}`);
|
|
1761
1460
|
});
|
|
1762
|
-
// Log PowerSourceCluster
|
|
1763
1461
|
cluster = PowerSourceCluster;
|
|
1764
1462
|
attributes = await interactionClient.getMultipleAttributes({
|
|
1765
1463
|
attributes: [{ clusterId: cluster.id }],
|
|
@@ -1769,7 +1467,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1769
1467
|
attributes.forEach((attribute) => {
|
|
1770
1468
|
this.log.info(`- endpoint ${or}${attribute.path.endpointId}${nf} cluster ${hk}${getClusterNameById(attribute.path.clusterId)}${nf} (${hk}0x${attribute.path.clusterId.toString(16)}${nf}) attribute ${zb}${attribute.path.attributeName}${nf} (${zb}0x${attribute.path.attributeId.toString(16)}${nf}): ${typeof attribute.value === 'object' ? stringify(attribute.value) : attribute.value}`);
|
|
1771
1469
|
});
|
|
1772
|
-
// Log ThreadNetworkDiagnostics
|
|
1773
1470
|
cluster = ThreadNetworkDiagnosticsCluster;
|
|
1774
1471
|
attributes = await interactionClient.getMultipleAttributes({
|
|
1775
1472
|
attributes: [{ clusterId: cluster.id }],
|
|
@@ -1779,7 +1476,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1779
1476
|
attributes.forEach((attribute) => {
|
|
1780
1477
|
this.log.info(`- endpoint ${or}${attribute.path.endpointId}${nf} cluster ${hk}${getClusterNameById(attribute.path.clusterId)}${nf} (${hk}0x${attribute.path.clusterId.toString(16)}${nf}) attribute ${zb}${attribute.path.attributeName}${nf} (${zb}0x${attribute.path.attributeId.toString(16)}${nf}): ${typeof attribute.value === 'object' ? stringify(attribute.value) : attribute.value}`);
|
|
1781
1478
|
});
|
|
1782
|
-
// Log SwitchCluster
|
|
1783
1479
|
cluster = SwitchCluster;
|
|
1784
1480
|
attributes = await interactionClient.getMultipleAttributes({
|
|
1785
1481
|
attributes: [{ clusterId: cluster.id }],
|
|
@@ -1800,15 +1496,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1800
1496
|
this.log.info('Subscribed to all attributes and events');
|
|
1801
1497
|
}
|
|
1802
1498
|
}
|
|
1803
|
-
/** ***********************************************************************************************************************************/
|
|
1804
|
-
/** Matter.js methods */
|
|
1805
|
-
/** ***********************************************************************************************************************************/
|
|
1806
|
-
/**
|
|
1807
|
-
* Starts the matter storage process based on the specified storage type and name.
|
|
1808
|
-
* @param {string} storageType - The type of storage to start (e.g., 'disk', 'json').
|
|
1809
|
-
* @param {string} storageName - The name of the storage file.
|
|
1810
|
-
* @returns {Promise<void>} - A promise that resolves when the storage process is started.
|
|
1811
|
-
*/
|
|
1812
1499
|
async startMatterStorage(storageType, storageName) {
|
|
1813
1500
|
this.log.debug(`Starting matter ${storageType} storage ${CYAN}${storageName}${db}`);
|
|
1814
1501
|
if (storageType === 'disk') {
|
|
@@ -1854,12 +1541,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1854
1541
|
this.log.debug(`Creating commissioning server context for ${plg}Matterbridge${db}`);
|
|
1855
1542
|
this.matterbridgeContext = await this.createCommissioningServerContext('Matterbridge', 'Matterbridge', DeviceTypes.AGGREGATOR.code, 0xfff1, 'Matterbridge', 0x8000, 'Matterbridge aggregator');
|
|
1856
1543
|
}
|
|
1857
|
-
/**
|
|
1858
|
-
* Makes a backup copy of the specified matter JSON storage file.
|
|
1859
|
-
*
|
|
1860
|
-
* @param storageName - The name of the JSON storage file to be backed up.
|
|
1861
|
-
* @param backupName - The name of the backup file to be created.
|
|
1862
|
-
*/
|
|
1863
1544
|
async backupMatterStorage(storageName, backupName) {
|
|
1864
1545
|
try {
|
|
1865
1546
|
this.log.debug(`Making backup copy of ${storageName}`);
|
|
@@ -1880,12 +1561,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1880
1561
|
}
|
|
1881
1562
|
}
|
|
1882
1563
|
}
|
|
1883
|
-
/**
|
|
1884
|
-
* Restore the specified matter JSON storage file.
|
|
1885
|
-
*
|
|
1886
|
-
* @param backupName - The name of the backup file to restore from.
|
|
1887
|
-
* @param storageName - The name of the JSON storage file to restored.
|
|
1888
|
-
*/
|
|
1889
1564
|
async restoreMatterStorage(backupName, storageName) {
|
|
1890
1565
|
try {
|
|
1891
1566
|
this.log.notice(`Restoring the backup copy of ${storageName}`);
|
|
@@ -1906,10 +1581,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1906
1581
|
}
|
|
1907
1582
|
}
|
|
1908
1583
|
}
|
|
1909
|
-
/**
|
|
1910
|
-
* Stops the matter storage.
|
|
1911
|
-
* @returns {Promise<void>} A promise that resolves when the storage is stopped.
|
|
1912
|
-
*/
|
|
1913
1584
|
async stopMatterStorage() {
|
|
1914
1585
|
this.log.debug('Stopping storage');
|
|
1915
1586
|
await this.storageManager?.close();
|
|
@@ -1918,14 +1589,8 @@ export class Matterbridge extends EventEmitter {
|
|
|
1918
1589
|
this.matterbridgeContext = undefined;
|
|
1919
1590
|
this.mattercontrollerContext = undefined;
|
|
1920
1591
|
}
|
|
1921
|
-
/**
|
|
1922
|
-
* Creates a Matter server using the provided storage manager and the provided mdnsInterface.
|
|
1923
|
-
* @param storageManager The storage manager to be used by the Matter server.
|
|
1924
|
-
*
|
|
1925
|
-
*/
|
|
1926
1592
|
createMatterServer(storageManager) {
|
|
1927
1593
|
this.log.debug('Creating matter server');
|
|
1928
|
-
// Validate mdnsInterface
|
|
1929
1594
|
if (this.mdnsInterface) {
|
|
1930
1595
|
const networkInterfaces = os.networkInterfaces();
|
|
1931
1596
|
const availableInterfaces = Object.keys(networkInterfaces);
|
|
@@ -1941,10 +1606,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1941
1606
|
this.log.debug(`Created matter server with mdnsInterface: ${this.mdnsInterface ?? 'all available interfaces'}`);
|
|
1942
1607
|
return matterServer;
|
|
1943
1608
|
}
|
|
1944
|
-
/**
|
|
1945
|
-
* Starts the Matter server.
|
|
1946
|
-
* If the Matter server is not initialized, it logs an error and performs cleanup.
|
|
1947
|
-
*/
|
|
1948
1609
|
async startMatterServer() {
|
|
1949
1610
|
if (!this.matterServer) {
|
|
1950
1611
|
this.log.error('No matter server initialized');
|
|
@@ -1954,11 +1615,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
1954
1615
|
this.log.debug('Starting matter server...');
|
|
1955
1616
|
await this.matterServer.start();
|
|
1956
1617
|
this.log.debug('Started matter server');
|
|
1957
|
-
// this.commissioningServer?.getRootEndpoint() && logEndpoint(this.commissioningServer?.getRootEndpoint());
|
|
1958
1618
|
}
|
|
1959
|
-
/**
|
|
1960
|
-
* Stops the Matter server, commissioningServer and commissioningController.
|
|
1961
|
-
*/
|
|
1962
1619
|
async stopMatterServer() {
|
|
1963
1620
|
this.log.debug('Stopping matter commissioningServer');
|
|
1964
1621
|
await this.commissioningServer?.close();
|
|
@@ -1972,35 +1629,23 @@ export class Matterbridge extends EventEmitter {
|
|
|
1972
1629
|
this.matterAggregator = undefined;
|
|
1973
1630
|
this.matterServer = undefined;
|
|
1974
1631
|
}
|
|
1975
|
-
/**
|
|
1976
|
-
* Creates a Matter Aggregator.
|
|
1977
|
-
* @param {StorageContext} context - The storage context.
|
|
1978
|
-
* @returns {Aggregator} - The created Matter Aggregator.
|
|
1979
|
-
*/
|
|
1980
1632
|
async createMatterAggregator(context, pluginName) {
|
|
1981
1633
|
this.log.debug(`Creating matter aggregator for ${plg}${pluginName}${db}`);
|
|
1982
1634
|
const matterAggregator = new Aggregator();
|
|
1983
1635
|
return matterAggregator;
|
|
1984
1636
|
}
|
|
1985
|
-
/**
|
|
1986
|
-
* Creates a matter commissioning server.
|
|
1987
|
-
*
|
|
1988
|
-
* @param {StorageContext} context - The storage context.
|
|
1989
|
-
* @param {string} pluginName - The name of the commissioning server.
|
|
1990
|
-
* @returns {CommissioningServer} The created commissioning server.
|
|
1991
|
-
*/
|
|
1992
1637
|
async createCommisioningServer(context, pluginName) {
|
|
1993
1638
|
this.log.debug(`Creating matter commissioning server for plugin ${plg}${pluginName}${db}`);
|
|
1994
1639
|
const deviceName = await context.get('deviceName');
|
|
1995
1640
|
const deviceType = await context.get('deviceType');
|
|
1996
1641
|
const vendorId = await context.get('vendorId');
|
|
1997
|
-
const vendorName = await context.get('vendorName');
|
|
1642
|
+
const vendorName = await context.get('vendorName');
|
|
1998
1643
|
const productId = await context.get('productId');
|
|
1999
|
-
const productName = await context.get('productName');
|
|
1644
|
+
const productName = await context.get('productName');
|
|
2000
1645
|
const serialNumber = await context.get('serialNumber');
|
|
2001
1646
|
const uniqueId = await context.get('uniqueId');
|
|
2002
1647
|
const softwareVersion = await context.get('softwareVersion', 1);
|
|
2003
|
-
const softwareVersionString = await context.get('softwareVersionString', '1.0.0');
|
|
1648
|
+
const softwareVersionString = await context.get('softwareVersionString', '1.0.0');
|
|
2004
1649
|
const hardwareVersion = await context.get('hardwareVersion', 1);
|
|
2005
1650
|
const hardwareVersionString = await context.get('hardwareVersionString', '1.0.0');
|
|
2006
1651
|
this.log.debug(`Creating matter commissioning server for plugin ${plg}${pluginName}${db} with deviceName '${deviceName}' deviceType ${deviceType}(0x${deviceType.toString(16).padStart(4, '0')})`);
|
|
@@ -2008,7 +1653,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2008
1653
|
this.log.debug(`Creating matter commissioning server for plugin ${plg}${pluginName}${db} with softwareVersion ${softwareVersion} softwareVersionString ${softwareVersionString}`);
|
|
2009
1654
|
this.log.debug(`Creating matter commissioning server for plugin ${plg}${pluginName}${db} with hardwareVersion ${hardwareVersion} hardwareVersionString ${hardwareVersionString}`);
|
|
2010
1655
|
this.log.debug(`Creating matter commissioning server for plugin ${plg}${pluginName}${db} with nodeLabel '${productName}' port ${CYAN}${this.port}${db} discriminator ${CYAN}${this.discriminator}${db} passcode ${CYAN}${this.passcode}${db} `);
|
|
2011
|
-
// Validate ipv4address
|
|
2012
1656
|
if (this.ipv4address) {
|
|
2013
1657
|
const networkInterfaces = os.networkInterfaces();
|
|
2014
1658
|
const availableAddresses = Object.values(networkInterfaces)
|
|
@@ -2023,7 +1667,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2023
1667
|
this.log.info(`Using ipv4address '${this.ipv4address}' for the Matter commissioning server.`);
|
|
2024
1668
|
}
|
|
2025
1669
|
}
|
|
2026
|
-
// Validate ipv6address
|
|
2027
1670
|
if (this.ipv6address) {
|
|
2028
1671
|
const networkInterfaces = os.networkInterfaces();
|
|
2029
1672
|
const availableAddresses = Object.values(networkInterfaces)
|
|
@@ -2054,7 +1697,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
2054
1697
|
nodeLabel: productName,
|
|
2055
1698
|
productLabel: productName,
|
|
2056
1699
|
softwareVersion,
|
|
2057
|
-
softwareVersionString,
|
|
1700
|
+
softwareVersionString,
|
|
2058
1701
|
hardwareVersion,
|
|
2059
1702
|
hardwareVersionString,
|
|
2060
1703
|
uniqueId,
|
|
@@ -2150,24 +1793,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2150
1793
|
commissioningServer.addCommandHandler('testEventTrigger', async ({ request: { enableKey, eventTrigger } }) => this.log.info(`testEventTrigger called on GeneralDiagnostic cluster: ${enableKey} ${eventTrigger}`));
|
|
2151
1794
|
return commissioningServer;
|
|
2152
1795
|
}
|
|
2153
|
-
/**
|
|
2154
|
-
* Creates a commissioning server storage context.
|
|
2155
|
-
*
|
|
2156
|
-
* @param pluginName - The name of the plugin.
|
|
2157
|
-
* @param deviceName - The name of the device.
|
|
2158
|
-
* @param deviceType - The type of the device.
|
|
2159
|
-
* @param vendorId - The vendor ID.
|
|
2160
|
-
* @param vendorName - The vendor name.
|
|
2161
|
-
* @param productId - The product ID.
|
|
2162
|
-
* @param productName - The product name.
|
|
2163
|
-
* @param serialNumber - The serial number of the device (optional).
|
|
2164
|
-
* @param uniqueId - The unique ID of the device (optional).
|
|
2165
|
-
* @param softwareVersion - The software version of the device (optional).
|
|
2166
|
-
* @param softwareVersionString - The software version string of the device (optional).
|
|
2167
|
-
* @param hardwareVersion - The hardware version of the device (optional).
|
|
2168
|
-
* @param hardwareVersionString - The hardware version string of the device (optional).
|
|
2169
|
-
* @returns The storage context for the commissioning server.
|
|
2170
|
-
*/
|
|
2171
1796
|
async createCommissioningServerContext(pluginName, deviceName, deviceType, vendorId, vendorName, productId, productName) {
|
|
2172
1797
|
if (!this.storageManager)
|
|
2173
1798
|
throw new Error('No storage manager initialized');
|
|
@@ -2195,13 +1820,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2195
1820
|
this.log.debug(`- hardwareVersion: ${await storageContext.get('hardwareVersion')} hardwareVersionString: ${await storageContext.get('hardwareVersionString')}`);
|
|
2196
1821
|
return storageContext;
|
|
2197
1822
|
}
|
|
2198
|
-
/**
|
|
2199
|
-
* Imports the commissioning server context for a specific plugin and device.
|
|
2200
|
-
* @param pluginName - The name of the plugin.
|
|
2201
|
-
* @param device - The MatterbridgeDevice object representing the device.
|
|
2202
|
-
* @returns The commissioning server context.
|
|
2203
|
-
* @throws Error if the BasicInformationCluster is not found.
|
|
2204
|
-
*/
|
|
2205
1823
|
async importCommissioningServerContext(pluginName, device) {
|
|
2206
1824
|
this.log.debug(`Importing matter commissioning server storage context from device for ${plg}${pluginName}${db}`);
|
|
2207
1825
|
const basic = device.getClusterServer(BasicInformationCluster);
|
|
@@ -2236,14 +1854,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2236
1854
|
this.log.debug(`- hardwareVersion: ${await storageContext.get('hardwareVersion')} hardwareVersionString: ${await storageContext.get('hardwareVersionString')}`);
|
|
2237
1855
|
return storageContext;
|
|
2238
1856
|
}
|
|
2239
|
-
/**
|
|
2240
|
-
* Shows the commissioning server QR code for a given plugin.
|
|
2241
|
-
* @param {CommissioningServer} commissioningServer - The commissioning server instance.
|
|
2242
|
-
* @param {StorageContext} storageContext - The storage context instance.
|
|
2243
|
-
* @param {NodeStorage} nodeContext - The node storage instance.
|
|
2244
|
-
* @param {string} pluginName - The name of the plugin of Matterbridge in bridge mode.
|
|
2245
|
-
* @returns {Promise<void>} - A promise that resolves when the QR code is shown.
|
|
2246
|
-
*/
|
|
2247
1857
|
async showCommissioningQRCode(commissioningServer, storageContext, nodeContext, pluginName) {
|
|
2248
1858
|
if (!commissioningServer || !storageContext || !nodeContext || !pluginName) {
|
|
2249
1859
|
this.log.error(`showCommissioningQRCode error: commissioningServer: ${!commissioningServer} storageContext: ${!storageContext} nodeContext: ${!nodeContext} pluginName: ${pluginName}`);
|
|
@@ -2254,8 +1864,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
2254
1864
|
const { qrPairingCode, manualPairingCode } = commissioningServer.getPairingCode();
|
|
2255
1865
|
const QrCode = new QrCodeSchema();
|
|
2256
1866
|
this.log.info(`*The commissioning server on port ${commissioningServer.getPort()} for ${plg}${pluginName}${nf} is not commissioned. Pair it scanning the QR code:\n\n`);
|
|
2257
|
-
|
|
2258
|
-
if (this.log.logLevel === "debug" /* LogLevel.DEBUG */ || this.log.logLevel === "info" /* LogLevel.INFO */)
|
|
1867
|
+
if (this.log.logLevel === "debug" || this.log.logLevel === "info")
|
|
2259
1868
|
console.log(`${QrCode.encode(qrPairingCode)}\n`);
|
|
2260
1869
|
this.log.info(`${plg}${pluginName}${nf} \n\nqrPairingCode: ${qrPairingCode} \n\nManual pairing code: ${manualPairingCode}\n`);
|
|
2261
1870
|
if (pluginName === 'Matterbridge') {
|
|
@@ -2302,12 +1911,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2302
1911
|
}
|
|
2303
1912
|
this.wssSendRefreshRequired();
|
|
2304
1913
|
}
|
|
2305
|
-
/**
|
|
2306
|
-
* Sanitizes the fabric information by converting bigint properties to string cause res.json doesn't know bigint.
|
|
2307
|
-
*
|
|
2308
|
-
* @param fabricInfo - The array of exposed fabric information objects.
|
|
2309
|
-
* @returns An array of sanitized exposed fabric information objects.
|
|
2310
|
-
*/
|
|
2311
1914
|
sanitizeFabricInformations(fabricInfo) {
|
|
2312
1915
|
return fabricInfo.map((info) => {
|
|
2313
1916
|
return {
|
|
@@ -2321,12 +1924,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2321
1924
|
};
|
|
2322
1925
|
});
|
|
2323
1926
|
}
|
|
2324
|
-
/**
|
|
2325
|
-
* Sanitizes the session information by converting bigint properties to string.
|
|
2326
|
-
*
|
|
2327
|
-
* @param sessionInfo - The array of session information objects.
|
|
2328
|
-
* @returns An array of sanitized session information objects.
|
|
2329
|
-
*/
|
|
2330
1927
|
sanitizeSessionInformation(sessionInfo) {
|
|
2331
1928
|
return sessionInfo
|
|
2332
1929
|
.filter((session) => session.isPeerActive)
|
|
@@ -2354,12 +1951,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2354
1951
|
};
|
|
2355
1952
|
});
|
|
2356
1953
|
}
|
|
2357
|
-
/**
|
|
2358
|
-
* Sets the reachability of a commissioning server and trigger.
|
|
2359
|
-
*
|
|
2360
|
-
* @param {CommissioningServer} commissioningServer - The commissioning server to set the reachability for.
|
|
2361
|
-
* @param {boolean} reachable - The new reachability status.
|
|
2362
|
-
*/
|
|
2363
1954
|
setCommissioningServerReachability(commissioningServer, reachable) {
|
|
2364
1955
|
const basicInformationCluster = commissioningServer?.getRootClusterServer(BasicInformationCluster);
|
|
2365
1956
|
if (basicInformationCluster && basicInformationCluster.attributes.reachable !== undefined)
|
|
@@ -2367,11 +1958,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2367
1958
|
if (basicInformationCluster && basicInformationCluster.triggerReachableChangedEvent)
|
|
2368
1959
|
basicInformationCluster.triggerReachableChangedEvent({ reachableNewValue: reachable });
|
|
2369
1960
|
}
|
|
2370
|
-
/**
|
|
2371
|
-
* Sets the reachability of the specified matter aggregator and its bridged devices and trigger.
|
|
2372
|
-
* @param {Aggregator} matterAggregator - The matter aggregator to set the reachability for.
|
|
2373
|
-
* @param {boolean} reachable - A boolean indicating the reachability status to set.
|
|
2374
|
-
*/
|
|
2375
1961
|
setAggregatorReachability(matterAggregator, reachable) {
|
|
2376
1962
|
const basicInformationCluster = matterAggregator.getClusterServer(BasicInformationCluster);
|
|
2377
1963
|
if (basicInformationCluster && basicInformationCluster.attributes.reachable !== undefined)
|
|
@@ -2384,12 +1970,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2384
1970
|
device.getClusterServer(BridgedDeviceBasicInformationCluster)?.triggerReachableChangedEvent({ reachableNewValue: reachable });
|
|
2385
1971
|
});
|
|
2386
1972
|
}
|
|
2387
|
-
/**
|
|
2388
|
-
* Sets the reachability of a device and trigger.
|
|
2389
|
-
*
|
|
2390
|
-
* @param {MatterbridgeDevice} device - The device to set the reachability for.
|
|
2391
|
-
* @param {boolean} reachable - The new reachability status of the device.
|
|
2392
|
-
*/
|
|
2393
1973
|
setDeviceReachability(device, reachable) {
|
|
2394
1974
|
const basicInformationCluster = device.getClusterServer(BasicInformationCluster);
|
|
2395
1975
|
if (basicInformationCluster && basicInformationCluster.attributes.reachable !== undefined)
|
|
@@ -2438,10 +2018,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2438
2018
|
}
|
|
2439
2019
|
return vendorName;
|
|
2440
2020
|
};
|
|
2441
|
-
/**
|
|
2442
|
-
* Retrieves the base registered plugins sanitized for res.json().
|
|
2443
|
-
* @returns {BaseRegisteredPlugin[]} A promise that resolves to an array of BaseRegisteredPlugin objects.
|
|
2444
|
-
*/
|
|
2445
2021
|
async getBaseRegisteredPlugins() {
|
|
2446
2022
|
const baseRegisteredPlugins = [];
|
|
2447
2023
|
for (const plugin of this.plugins) {
|
|
@@ -2473,36 +2049,13 @@ export class Matterbridge extends EventEmitter {
|
|
|
2473
2049
|
}
|
|
2474
2050
|
return baseRegisteredPlugins;
|
|
2475
2051
|
}
|
|
2476
|
-
/**
|
|
2477
|
-
* Spawns a child process with the given command and arguments.
|
|
2478
|
-
* @param {string} command - The command to execute.
|
|
2479
|
-
* @param {string[]} args - The arguments to pass to the command (default: []).
|
|
2480
|
-
* @returns {Promise<boolean>} A promise that resolves when the child process exits successfully, or rejects if there is an error.
|
|
2481
|
-
*/
|
|
2482
2052
|
async spawnCommand(command, args = []) {
|
|
2483
|
-
/*
|
|
2484
|
-
npm > npm.cmd on windows
|
|
2485
|
-
cmd.exe ['dir'] on windows
|
|
2486
|
-
await this.spawnCommand('npm', ['install', '-g', 'matterbridge']);
|
|
2487
|
-
process.on('unhandledRejection', (reason, promise) => {
|
|
2488
|
-
this.log.error('Unhandled Rejection at:', promise, 'reason:', reason);
|
|
2489
|
-
});
|
|
2490
|
-
|
|
2491
|
-
spawn - [14:27:21.125] [Matterbridge:spawn]: changed 38 packages in 4s
|
|
2492
|
-
spawn - [14:27:21.125] [Matterbridge:spawn]: 10 packages are looking for funding run `npm fund` for details
|
|
2493
|
-
debug - [14:27:21.131] [Matterbridge]: Child process exited with code 0 and signal null
|
|
2494
|
-
debug - [14:27:21.131] [Matterbridge]: Child process stdio streams have closed with code 0
|
|
2495
|
-
*/
|
|
2496
2053
|
const cmdLine = command + ' ' + args.join(' ');
|
|
2497
2054
|
if (process.platform === 'win32' && command === 'npm') {
|
|
2498
|
-
// Must be spawn('cmd.exe', ['/c', 'npm -g install <package>']);
|
|
2499
2055
|
const argstring = 'npm ' + args.join(' ');
|
|
2500
2056
|
args.splice(0, args.length, '/c', argstring);
|
|
2501
2057
|
command = 'cmd.exe';
|
|
2502
2058
|
}
|
|
2503
|
-
// Decide when using sudo on linux
|
|
2504
|
-
// When you need sudo: Spawn stderr: npm error Error: EACCES: permission denied
|
|
2505
|
-
// When you don't need sudo: Failed to start child process "npm install -g matterbridge-eve-door": spawn sudo ENOENT
|
|
2506
2059
|
if (hasParameter('sudo') || (process.platform === 'linux' && command === 'npm' && !hasParameter('docker') && !hasParameter('nosudo'))) {
|
|
2507
2060
|
args.unshift(command);
|
|
2508
2061
|
command = 'sudo';
|
|
@@ -2560,102 +2113,55 @@ export class Matterbridge extends EventEmitter {
|
|
|
2560
2113
|
}
|
|
2561
2114
|
});
|
|
2562
2115
|
}
|
|
2563
|
-
/**
|
|
2564
|
-
* Sends a WebSocket message to all connected clients.
|
|
2565
|
-
*
|
|
2566
|
-
* @param {string} level - The logger level of the message: debug info notice warn error fatal...
|
|
2567
|
-
* @param {string} time - The time string of the message
|
|
2568
|
-
* @param {string} name - The logger name of the message
|
|
2569
|
-
* @param {string} message - The content of the message.
|
|
2570
|
-
*/
|
|
2571
2116
|
wssSendMessage(level, time, name, message) {
|
|
2572
2117
|
if (!level || !time || !name || !message)
|
|
2573
2118
|
return;
|
|
2574
|
-
// Remove ANSI escape codes from the message
|
|
2575
|
-
// eslint-disable-next-line no-control-regex
|
|
2576
2119
|
message = message.replace(/\x1B\[[0-9;]*[m|s|u|K]/g, '');
|
|
2577
|
-
// Remove leading asterisks from the message
|
|
2578
2120
|
message = message.replace(/^\*+/, '');
|
|
2579
|
-
// Replace all occurrences of \t and \n
|
|
2580
2121
|
message = message.replace(/[\t\n]/g, '');
|
|
2581
|
-
// Remove non-printable characters
|
|
2582
|
-
// eslint-disable-next-line no-control-regex
|
|
2583
2122
|
message = message.replace(/[\x00-\x1F\x7F]/g, '');
|
|
2584
|
-
// Replace all occurrences of \" with "
|
|
2585
2123
|
message = message.replace(/\\"/g, '"');
|
|
2586
|
-
// Define the maximum allowed length for continuous characters without a space
|
|
2587
2124
|
const maxContinuousLength = 100;
|
|
2588
2125
|
const keepStartLength = 20;
|
|
2589
2126
|
const keepEndLength = 20;
|
|
2590
|
-
// Split the message into words
|
|
2591
2127
|
message = message
|
|
2592
2128
|
.split(' ')
|
|
2593
2129
|
.map((word) => {
|
|
2594
|
-
// If the word length exceeds the max continuous length, insert spaces and truncate
|
|
2595
2130
|
if (word.length > maxContinuousLength) {
|
|
2596
2131
|
return word.slice(0, keepStartLength) + ' ... ' + word.slice(-keepEndLength);
|
|
2597
2132
|
}
|
|
2598
2133
|
return word;
|
|
2599
2134
|
})
|
|
2600
2135
|
.join(' ');
|
|
2601
|
-
// Send the message to all connected clients
|
|
2602
2136
|
this.webSocketServer?.clients.forEach((client) => {
|
|
2603
2137
|
if (client.readyState === WebSocket.OPEN) {
|
|
2604
2138
|
client.send(JSON.stringify({ id: WS_ID_LOG, src: 'Matterbridge', level, time, name, message }));
|
|
2605
2139
|
}
|
|
2606
2140
|
});
|
|
2607
2141
|
}
|
|
2608
|
-
/**
|
|
2609
|
-
* Sends a need to refresh WebSocket message to all connected clients.
|
|
2610
|
-
*
|
|
2611
|
-
*/
|
|
2612
2142
|
wssSendRefreshRequired() {
|
|
2613
2143
|
this.matterbridgeInformation.refreshRequired = true;
|
|
2614
|
-
// Send the message to all connected clients
|
|
2615
2144
|
this.webSocketServer?.clients.forEach((client) => {
|
|
2616
2145
|
if (client.readyState === WebSocket.OPEN) {
|
|
2617
2146
|
client.send(JSON.stringify({ id: WS_ID_REFRESH_NEEDED, src: 'Matterbridge', dst: 'Matterbridge', method: 'refresh_required', params: {} }));
|
|
2618
2147
|
}
|
|
2619
2148
|
});
|
|
2620
2149
|
}
|
|
2621
|
-
/**
|
|
2622
|
-
* Sends a need to restart WebSocket message to all connected clients.
|
|
2623
|
-
*
|
|
2624
|
-
*/
|
|
2625
2150
|
wssSendRestartRequired() {
|
|
2626
2151
|
this.matterbridgeInformation.restartRequired = true;
|
|
2627
|
-
// Send the message to all connected clients
|
|
2628
2152
|
this.webSocketServer?.clients.forEach((client) => {
|
|
2629
2153
|
if (client.readyState === WebSocket.OPEN) {
|
|
2630
2154
|
client.send(JSON.stringify({ id: WS_ID_RESTART_NEEDED, src: 'Matterbridge', dst: 'Matterbridge', method: 'restart_required', params: {} }));
|
|
2631
2155
|
}
|
|
2632
2156
|
});
|
|
2633
2157
|
}
|
|
2634
|
-
/**
|
|
2635
|
-
* Initializes the frontend of Matterbridge.
|
|
2636
|
-
*
|
|
2637
|
-
* @param port The port number to run the frontend server on. Default is 8283.
|
|
2638
|
-
*/
|
|
2639
2158
|
async initializeFrontend(port = 8283) {
|
|
2640
2159
|
let initializeError = false;
|
|
2641
2160
|
this.log.debug(`Initializing the frontend ${hasParameter('ssl') ? 'https' : 'http'} server on port ${YELLOW}${port}${db}`);
|
|
2642
|
-
// Create the express app that serves the frontend
|
|
2643
2161
|
this.expressApp = express();
|
|
2644
|
-
// Log all requests to the server for debugging
|
|
2645
|
-
/*
|
|
2646
|
-
if (hasParameter('homedir')) {
|
|
2647
|
-
this.expressApp.use((req, res, next) => {
|
|
2648
|
-
this.log.debug(`Received request on expressApp: ${req.method} ${req.url}`);
|
|
2649
|
-
next();
|
|
2650
|
-
});
|
|
2651
|
-
}
|
|
2652
|
-
*/
|
|
2653
|
-
// Serve static files from '/static' endpoint
|
|
2654
2162
|
this.expressApp.use(express.static(path.join(this.rootDirectory, 'frontend/build')));
|
|
2655
2163
|
if (!hasParameter('ssl')) {
|
|
2656
|
-
// Create an HTTP server and attach the express app
|
|
2657
2164
|
this.httpServer = createServer(this.expressApp);
|
|
2658
|
-
// Listen on the specified port
|
|
2659
2165
|
if (hasParameter('ingress')) {
|
|
2660
2166
|
this.httpServer.listen(port, '0.0.0.0', () => {
|
|
2661
2167
|
this.log.info(`The frontend http server is listening on ${UNDERLINE}http://0.0.0.0:${port}${UNDERLINEOFF}${rs}`);
|
|
@@ -2669,7 +2175,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2669
2175
|
this.log.info(`The frontend http server is listening on ${UNDERLINE}http://[${this.systemInformation.ipv6Address}]:${port}${UNDERLINEOFF}${rs}`);
|
|
2670
2176
|
});
|
|
2671
2177
|
}
|
|
2672
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
2673
2178
|
this.httpServer.on('error', (error) => {
|
|
2674
2179
|
this.log.error(`Frontend http server error listening on ${port}`);
|
|
2675
2180
|
switch (error.code) {
|
|
@@ -2685,7 +2190,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2685
2190
|
});
|
|
2686
2191
|
}
|
|
2687
2192
|
else {
|
|
2688
|
-
// Load the SSL certificate, the private key and optionally the CA certificate
|
|
2689
2193
|
let cert;
|
|
2690
2194
|
try {
|
|
2691
2195
|
cert = await fs.readFile(path.join(this.matterbridgeDirectory, 'certs/cert.pem'), 'utf8');
|
|
@@ -2713,9 +2217,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
2713
2217
|
this.log.info(`CA certificate file ${path.join(this.matterbridgeDirectory, 'certs/ca.pem')} not loaded: ${error}`);
|
|
2714
2218
|
}
|
|
2715
2219
|
const serverOptions = { cert, key, ca };
|
|
2716
|
-
// Create an HTTPS server with the SSL certificate and private key (ca is optional) and attach the express app
|
|
2717
2220
|
this.httpsServer = https.createServer(serverOptions, this.expressApp);
|
|
2718
|
-
// Listen on the specified port
|
|
2719
2221
|
if (hasParameter('ingress')) {
|
|
2720
2222
|
this.httpsServer.listen(port, '0.0.0.0', () => {
|
|
2721
2223
|
this.log.info(`The frontend https server is listening on ${UNDERLINE}https://0.0.0.0:${port}${UNDERLINEOFF}${rs}`);
|
|
@@ -2729,7 +2231,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2729
2231
|
this.log.info(`The frontend https server is listening on ${UNDERLINE}https://[${this.systemInformation.ipv6Address}]:${port}${UNDERLINEOFF}${rs}`);
|
|
2730
2232
|
});
|
|
2731
2233
|
}
|
|
2732
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
2733
2234
|
this.httpsServer.on('error', (error) => {
|
|
2734
2235
|
this.log.error(`Frontend https server error listening on ${port}`);
|
|
2735
2236
|
switch (error.code) {
|
|
@@ -2746,13 +2247,12 @@ export class Matterbridge extends EventEmitter {
|
|
|
2746
2247
|
}
|
|
2747
2248
|
if (initializeError)
|
|
2748
2249
|
return;
|
|
2749
|
-
// Createe a WebSocket server and attach it to the http or https server
|
|
2750
2250
|
const wssPort = port;
|
|
2751
2251
|
const wssHost = hasParameter('ssl') ? `wss://${this.systemInformation.ipv4Address}:${wssPort}` : `ws://${this.systemInformation.ipv4Address}:${wssPort}`;
|
|
2752
2252
|
this.webSocketServer = new WebSocketServer(hasParameter('ssl') ? { server: this.httpsServer } : { server: this.httpServer });
|
|
2753
2253
|
this.webSocketServer.on('connection', (ws, request) => {
|
|
2754
2254
|
const clientIp = request.socket.remoteAddress;
|
|
2755
|
-
AnsiLogger.setGlobalCallback(this.wssSendMessage.bind(this), "debug"
|
|
2255
|
+
AnsiLogger.setGlobalCallback(this.wssSendMessage.bind(this), "debug");
|
|
2756
2256
|
this.log.info(`WebSocketServer client "${clientIp}" connected to Matterbridge`);
|
|
2757
2257
|
ws.on('message', (message) => {
|
|
2758
2258
|
this.log.debug(`WebSocket client message: ${message}`);
|
|
@@ -2785,7 +2285,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2785
2285
|
this.webSocketServer.on('error', (ws, error) => {
|
|
2786
2286
|
this.log.error(`WebSocketServer error: ${error}`);
|
|
2787
2287
|
});
|
|
2788
|
-
// Endpoint to validate login code
|
|
2789
2288
|
this.expressApp.post('/api/login', express.json(), async (req, res) => {
|
|
2790
2289
|
const { password } = req.body;
|
|
2791
2290
|
this.log.debug('The frontend sent /api/login', password);
|
|
@@ -2804,14 +2303,12 @@ export class Matterbridge extends EventEmitter {
|
|
|
2804
2303
|
this.log.warn('/api/login error wrong password');
|
|
2805
2304
|
res.json({ valid: false });
|
|
2806
2305
|
}
|
|
2807
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
2808
2306
|
}
|
|
2809
2307
|
catch (error) {
|
|
2810
2308
|
this.log.error('/api/login error getting password');
|
|
2811
2309
|
res.json({ valid: false });
|
|
2812
2310
|
}
|
|
2813
2311
|
});
|
|
2814
|
-
// Endpoint to provide settings
|
|
2815
2312
|
this.expressApp.get('/api/settings', express.json(), async (req, res) => {
|
|
2816
2313
|
this.log.debug('The frontend sent /api/settings');
|
|
2817
2314
|
this.matterbridgeInformation.bridgeMode = this.bridgeMode;
|
|
@@ -2832,17 +2329,13 @@ export class Matterbridge extends EventEmitter {
|
|
|
2832
2329
|
this.matterbridgeInformation.matterbridgeSessionInformations = Array.from(this.matterbridgeSessionInformations.values());
|
|
2833
2330
|
this.matterbridgeInformation.profile = this.profile;
|
|
2834
2331
|
const response = { wssHost, ssl: hasParameter('ssl'), systemInformation: this.systemInformation, matterbridgeInformation: this.matterbridgeInformation };
|
|
2835
|
-
// this.log.debug('Response:', debugStringify(response));
|
|
2836
2332
|
res.json(response);
|
|
2837
2333
|
});
|
|
2838
|
-
// Endpoint to provide plugins
|
|
2839
2334
|
this.expressApp.get('/api/plugins', async (req, res) => {
|
|
2840
2335
|
this.log.debug('The frontend sent /api/plugins');
|
|
2841
2336
|
const response = await this.getBaseRegisteredPlugins();
|
|
2842
|
-
// this.log.debug('Response:', debugStringify(response));
|
|
2843
2337
|
res.json(response);
|
|
2844
2338
|
});
|
|
2845
|
-
// Endpoint to provide devices
|
|
2846
2339
|
this.expressApp.get('/api/devices', (req, res) => {
|
|
2847
2340
|
this.log.debug('The frontend sent /api/devices');
|
|
2848
2341
|
const devices = [];
|
|
@@ -2875,10 +2368,8 @@ export class Matterbridge extends EventEmitter {
|
|
|
2875
2368
|
cluster: cluster,
|
|
2876
2369
|
});
|
|
2877
2370
|
});
|
|
2878
|
-
// this.log.debug('Response:', debugStringify(data));
|
|
2879
2371
|
res.json(devices);
|
|
2880
2372
|
});
|
|
2881
|
-
// Endpoint to provide the cluster servers of the devices
|
|
2882
2373
|
this.expressApp.get('/api/devices_clusters/:selectedPluginName/:selectedDeviceEndpoint', (req, res) => {
|
|
2883
2374
|
const selectedPluginName = req.params.selectedPluginName;
|
|
2884
2375
|
const selectedDeviceEndpoint = parseInt(req.params.selectedDeviceEndpoint, 10);
|
|
@@ -2898,7 +2389,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2898
2389
|
Object.entries(clusterServer.attributes).forEach(([key, value]) => {
|
|
2899
2390
|
if (clusterServer.name === 'EveHistory')
|
|
2900
2391
|
return;
|
|
2901
|
-
// this.log.debug(`***--clusterServer: ${clusterServer.name}(${clusterServer.id}) attribute:${key}(${value.id}) ${value.isFixed} ${value.isWritable} ${value.isWritable}`);
|
|
2902
2392
|
let attributeValue;
|
|
2903
2393
|
try {
|
|
2904
2394
|
if (typeof value.getLocal() === 'object')
|
|
@@ -2909,7 +2399,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2909
2399
|
catch (error) {
|
|
2910
2400
|
attributeValue = 'Fabric-Scoped';
|
|
2911
2401
|
this.log.debug(`GetLocal value ${error} in clusterServer: ${clusterServer.name}(${clusterServer.id}) attribute: ${key}(${value.id})`);
|
|
2912
|
-
// console.log(error);
|
|
2913
2402
|
}
|
|
2914
2403
|
data.push({
|
|
2915
2404
|
endpoint: device.number ? device.number.toString() : '...',
|
|
@@ -2922,14 +2411,12 @@ export class Matterbridge extends EventEmitter {
|
|
|
2922
2411
|
});
|
|
2923
2412
|
});
|
|
2924
2413
|
device.getChildEndpoints().forEach((childEndpoint) => {
|
|
2925
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
2926
2414
|
const name = this.edge ? childEndpoint.endpoint?.id : childEndpoint.uniqueStorageKey;
|
|
2927
2415
|
const clusterServers = childEndpoint.getAllClusterServers();
|
|
2928
2416
|
clusterServers.forEach((clusterServer) => {
|
|
2929
2417
|
Object.entries(clusterServer.attributes).forEach(([key, value]) => {
|
|
2930
2418
|
if (clusterServer.name === 'EveHistory')
|
|
2931
2419
|
return;
|
|
2932
|
-
// this.log.debug(`***--clusterServer: ${clusterServer.name}(${clusterServer.id}) attribute:${key}(${value.id}) ${value.isFixed} ${value.isWritable} ${value.isWritable}`);
|
|
2933
2420
|
let attributeValue;
|
|
2934
2421
|
try {
|
|
2935
2422
|
if (typeof value.getLocal() === 'object')
|
|
@@ -2940,7 +2427,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2940
2427
|
catch (error) {
|
|
2941
2428
|
attributeValue = 'Unavailable';
|
|
2942
2429
|
this.log.debug(`GetLocal error ${error} in clusterServer: ${clusterServer.name}(${clusterServer.id}) attribute: ${key}(${value.id})`);
|
|
2943
|
-
// console.log(error);
|
|
2944
2430
|
}
|
|
2945
2431
|
data.push({
|
|
2946
2432
|
endpoint: (childEndpoint.number ? childEndpoint.number.toString() : '...') + (name ? ' (' + name + ')' : ''),
|
|
@@ -2957,7 +2443,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2957
2443
|
});
|
|
2958
2444
|
res.json(data);
|
|
2959
2445
|
});
|
|
2960
|
-
// Endpoint to view the log
|
|
2961
2446
|
this.expressApp.get('/api/view-log', async (req, res) => {
|
|
2962
2447
|
this.log.debug('The frontend sent /api/log');
|
|
2963
2448
|
try {
|
|
@@ -2970,12 +2455,10 @@ export class Matterbridge extends EventEmitter {
|
|
|
2970
2455
|
res.status(500).send('Error reading log file');
|
|
2971
2456
|
}
|
|
2972
2457
|
});
|
|
2973
|
-
// Endpoint to download the matterbridge log
|
|
2974
2458
|
this.expressApp.get('/api/download-mblog', async (req, res) => {
|
|
2975
2459
|
this.log.debug('The frontend sent /api/download-mblog');
|
|
2976
2460
|
try {
|
|
2977
2461
|
await fs.access(path.join(this.matterbridgeDirectory, this.matterbrideLoggerFile), fs.constants.F_OK);
|
|
2978
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
2979
2462
|
}
|
|
2980
2463
|
catch (error) {
|
|
2981
2464
|
fs.appendFile(path.join(this.matterbridgeDirectory, this.matterbrideLoggerFile), 'Enable the log on file in the settings to enable the file logger');
|
|
@@ -2987,12 +2470,10 @@ export class Matterbridge extends EventEmitter {
|
|
|
2987
2470
|
}
|
|
2988
2471
|
});
|
|
2989
2472
|
});
|
|
2990
|
-
// Endpoint to download the matter log
|
|
2991
2473
|
this.expressApp.get('/api/download-mjlog', async (req, res) => {
|
|
2992
2474
|
this.log.debug('The frontend sent /api/download-mjlog');
|
|
2993
2475
|
try {
|
|
2994
2476
|
await fs.access(path.join(this.matterbridgeDirectory, this.matterLoggerFile), fs.constants.F_OK);
|
|
2995
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
2996
2477
|
}
|
|
2997
2478
|
catch (error) {
|
|
2998
2479
|
fs.appendFile(path.join(this.matterbridgeDirectory, this.matterLoggerFile), 'Enable the log on file in the settings to enable the file logger');
|
|
@@ -3004,7 +2485,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
3004
2485
|
}
|
|
3005
2486
|
});
|
|
3006
2487
|
});
|
|
3007
|
-
// Endpoint to download the matter storage file
|
|
3008
2488
|
this.expressApp.get('/api/download-mjstorage', (req, res) => {
|
|
3009
2489
|
this.log.debug('The frontend sent /api/download-mjstorage');
|
|
3010
2490
|
res.download(path.join(this.matterbridgeDirectory, this.matterStorageName), 'matterbridge.json', (error) => {
|
|
@@ -3014,7 +2494,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
3014
2494
|
}
|
|
3015
2495
|
});
|
|
3016
2496
|
});
|
|
3017
|
-
// Endpoint to download the matterbridge storage directory
|
|
3018
2497
|
this.expressApp.get('/api/download-mbstorage', async (req, res) => {
|
|
3019
2498
|
this.log.debug('The frontend sent /api/download-mbstorage');
|
|
3020
2499
|
await createZip(path.join(os.tmpdir(), `matterbridge.${this.nodeStorageName}.zip`), path.join(this.matterbridgeDirectory, this.nodeStorageName));
|
|
@@ -3025,7 +2504,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
3025
2504
|
}
|
|
3026
2505
|
});
|
|
3027
2506
|
});
|
|
3028
|
-
// Endpoint to download the matterbridge plugin directory
|
|
3029
2507
|
this.expressApp.get('/api/download-pluginstorage', async (req, res) => {
|
|
3030
2508
|
this.log.debug('The frontend sent /api/download-pluginstorage');
|
|
3031
2509
|
await createZip(path.join(os.tmpdir(), `matterbridge.pluginstorage.zip`), this.matterbridgePluginDirectory);
|
|
@@ -3036,11 +2514,9 @@ export class Matterbridge extends EventEmitter {
|
|
|
3036
2514
|
}
|
|
3037
2515
|
});
|
|
3038
2516
|
});
|
|
3039
|
-
// Endpoint to download the matterbridge plugin config files
|
|
3040
2517
|
this.expressApp.get('/api/download-pluginconfig', async (req, res) => {
|
|
3041
2518
|
this.log.debug('The frontend sent /api/download-pluginconfig');
|
|
3042
2519
|
await createZip(path.join(os.tmpdir(), `matterbridge.pluginconfig.zip`), path.relative(process.cwd(), path.join(this.matterbridgeDirectory, '*.config.json')));
|
|
3043
|
-
// await createZip(path.join(os.tmpdir(), `matterbridge.pluginconfig.zip`), path.relative(process.cwd(), path.join(this.matterbridgeDirectory, 'certs', '*.*')), path.relative(process.cwd(), path.join(this.matterbridgeDirectory, '*.config.json')));
|
|
3044
2520
|
res.download(path.join(os.tmpdir(), `matterbridge.pluginconfig.zip`), `matterbridge.pluginconfig.zip`, (error) => {
|
|
3045
2521
|
if (error) {
|
|
3046
2522
|
this.log.error(`Error downloading file matterbridge.pluginstorage.zip: ${error instanceof Error ? error.message : error}`);
|
|
@@ -3048,7 +2524,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
3048
2524
|
}
|
|
3049
2525
|
});
|
|
3050
2526
|
});
|
|
3051
|
-
// Endpoint to download the matterbridge plugin config files
|
|
3052
2527
|
this.expressApp.get('/api/download-backup', async (req, res) => {
|
|
3053
2528
|
this.log.debug('The frontend sent /api/download-backup');
|
|
3054
2529
|
res.download(path.join(os.tmpdir(), `matterbridge.backup.zip`), `matterbridge.backup.zip`, (error) => {
|
|
@@ -3058,7 +2533,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
3058
2533
|
}
|
|
3059
2534
|
});
|
|
3060
2535
|
});
|
|
3061
|
-
// Endpoint to receive commands
|
|
3062
2536
|
this.expressApp.post('/api/command/:command/:param', express.json(), async (req, res) => {
|
|
3063
2537
|
const command = req.params.command;
|
|
3064
2538
|
let param = req.params.param;
|
|
@@ -3068,15 +2542,13 @@ export class Matterbridge extends EventEmitter {
|
|
|
3068
2542
|
return;
|
|
3069
2543
|
}
|
|
3070
2544
|
this.log.debug(`Received frontend command: ${command}:${param}`);
|
|
3071
|
-
// Handle the command setpassword from Settings
|
|
3072
2545
|
if (command === 'setpassword') {
|
|
3073
|
-
const password = param.slice(1, -1);
|
|
2546
|
+
const password = param.slice(1, -1);
|
|
3074
2547
|
this.log.debug('setpassword', param, password);
|
|
3075
2548
|
await this.nodeContext?.set('password', password);
|
|
3076
2549
|
res.json({ message: 'Command received' });
|
|
3077
2550
|
return;
|
|
3078
2551
|
}
|
|
3079
|
-
// Handle the command setbridgemode from Settings
|
|
3080
2552
|
if (command === 'setbridgemode') {
|
|
3081
2553
|
this.log.debug(`setbridgemode: ${param}`);
|
|
3082
2554
|
this.wssSendRestartRequired();
|
|
@@ -3084,7 +2556,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
3084
2556
|
res.json({ message: 'Command received' });
|
|
3085
2557
|
return;
|
|
3086
2558
|
}
|
|
3087
|
-
// Handle the command backup from Settings
|
|
3088
2559
|
if (command === 'backup') {
|
|
3089
2560
|
this.log.notice(`Prepairing the backup...`);
|
|
3090
2561
|
await createZip(path.join(os.tmpdir(), `matterbridge.backup.zip`), path.join(this.matterbridgeDirectory), path.join(this.matterbridgePluginDirectory));
|
|
@@ -3092,26 +2563,25 @@ export class Matterbridge extends EventEmitter {
|
|
|
3092
2563
|
res.json({ message: 'Command received' });
|
|
3093
2564
|
return;
|
|
3094
2565
|
}
|
|
3095
|
-
// Handle the command setmbloglevel from Settings
|
|
3096
2566
|
if (command === 'setmbloglevel') {
|
|
3097
2567
|
this.log.debug('Matterbridge log level:', param);
|
|
3098
2568
|
if (param === 'Debug') {
|
|
3099
|
-
this.log.logLevel = "debug"
|
|
2569
|
+
this.log.logLevel = "debug";
|
|
3100
2570
|
}
|
|
3101
2571
|
else if (param === 'Info') {
|
|
3102
|
-
this.log.logLevel = "info"
|
|
2572
|
+
this.log.logLevel = "info";
|
|
3103
2573
|
}
|
|
3104
2574
|
else if (param === 'Notice') {
|
|
3105
|
-
this.log.logLevel = "notice"
|
|
2575
|
+
this.log.logLevel = "notice";
|
|
3106
2576
|
}
|
|
3107
2577
|
else if (param === 'Warn') {
|
|
3108
|
-
this.log.logLevel = "warn"
|
|
2578
|
+
this.log.logLevel = "warn";
|
|
3109
2579
|
}
|
|
3110
2580
|
else if (param === 'Error') {
|
|
3111
|
-
this.log.logLevel = "error"
|
|
2581
|
+
this.log.logLevel = "error";
|
|
3112
2582
|
}
|
|
3113
2583
|
else if (param === 'Fatal') {
|
|
3114
|
-
this.log.logLevel = "fatal"
|
|
2584
|
+
this.log.logLevel = "fatal";
|
|
3115
2585
|
}
|
|
3116
2586
|
await this.nodeContext?.set('matterbridgeLogLevel', this.log.logLevel);
|
|
3117
2587
|
MatterbridgeDevice.logLevel = this.log.logLevel;
|
|
@@ -3119,13 +2589,12 @@ export class Matterbridge extends EventEmitter {
|
|
|
3119
2589
|
for (const plugin of this.plugins) {
|
|
3120
2590
|
if (!plugin.platform || !plugin.platform.config)
|
|
3121
2591
|
continue;
|
|
3122
|
-
plugin.platform.log.logLevel = plugin.platform.config.debug ? "debug"
|
|
3123
|
-
await plugin.platform.onChangeLoggerLevel(plugin.platform.config.debug ? "debug"
|
|
2592
|
+
plugin.platform.log.logLevel = plugin.platform.config.debug ? "debug" : this.log.logLevel;
|
|
2593
|
+
await plugin.platform.onChangeLoggerLevel(plugin.platform.config.debug ? "debug" : this.log.logLevel);
|
|
3124
2594
|
}
|
|
3125
2595
|
res.json({ message: 'Command received' });
|
|
3126
2596
|
return;
|
|
3127
2597
|
}
|
|
3128
|
-
// Handle the command setmbloglevel from Settings
|
|
3129
2598
|
if (command === 'setmjloglevel') {
|
|
3130
2599
|
this.log.debug('Matter.js log level:', param);
|
|
3131
2600
|
if (param === 'Debug') {
|
|
@@ -3150,34 +2619,30 @@ export class Matterbridge extends EventEmitter {
|
|
|
3150
2619
|
res.json({ message: 'Command received' });
|
|
3151
2620
|
return;
|
|
3152
2621
|
}
|
|
3153
|
-
// Handle the command setmdnsinterface from Settings
|
|
3154
2622
|
if (command === 'setmdnsinterface') {
|
|
3155
|
-
param = param.slice(1, -1);
|
|
2623
|
+
param = param.slice(1, -1);
|
|
3156
2624
|
this.matterbridgeInformation.mattermdnsinterface = param;
|
|
3157
2625
|
this.log.debug('Matter.js mdns interface:', param === '' ? 'All interfaces' : param);
|
|
3158
2626
|
await this.nodeContext?.set('mattermdnsinterface', param);
|
|
3159
2627
|
res.json({ message: 'Command received' });
|
|
3160
2628
|
return;
|
|
3161
2629
|
}
|
|
3162
|
-
// Handle the command setipv4address from Settings
|
|
3163
2630
|
if (command === 'setipv4address') {
|
|
3164
|
-
param = param.slice(1, -1);
|
|
2631
|
+
param = param.slice(1, -1);
|
|
3165
2632
|
this.matterbridgeInformation.matteripv4address = param;
|
|
3166
2633
|
this.log.debug('Matter.js ipv4 address:', param === '' ? 'All ipv4 addresses' : param);
|
|
3167
2634
|
await this.nodeContext?.set('matteripv4address', param);
|
|
3168
2635
|
res.json({ message: 'Command received' });
|
|
3169
2636
|
return;
|
|
3170
2637
|
}
|
|
3171
|
-
// Handle the command setipv6address from Settings
|
|
3172
2638
|
if (command === 'setipv6address') {
|
|
3173
|
-
param = param.slice(1, -1);
|
|
2639
|
+
param = param.slice(1, -1);
|
|
3174
2640
|
this.matterbridgeInformation.matteripv6address = param;
|
|
3175
2641
|
this.log.debug('Matter.js ipv6 address:', param === '' ? 'All ipv6 addresses' : param);
|
|
3176
2642
|
await this.nodeContext?.set('matteripv6address', param);
|
|
3177
2643
|
res.json({ message: 'Command received' });
|
|
3178
2644
|
return;
|
|
3179
2645
|
}
|
|
3180
|
-
// Handle the command setmatterport from Settings
|
|
3181
2646
|
if (command === 'setmatterport') {
|
|
3182
2647
|
const port = Math.min(Math.max(parseInt(param), 5540), 5560);
|
|
3183
2648
|
this.matterbridgeInformation.matterPort = port;
|
|
@@ -3186,7 +2651,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
3186
2651
|
res.json({ message: 'Command received' });
|
|
3187
2652
|
return;
|
|
3188
2653
|
}
|
|
3189
|
-
// Handle the command setmatterdiscriminator from Settings
|
|
3190
2654
|
if (command === 'setmatterdiscriminator') {
|
|
3191
2655
|
const discriminator = Math.min(Math.max(parseInt(param), 1000), 4095);
|
|
3192
2656
|
this.matterbridgeInformation.matterDiscriminator = discriminator;
|
|
@@ -3195,7 +2659,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
3195
2659
|
res.json({ message: 'Command received' });
|
|
3196
2660
|
return;
|
|
3197
2661
|
}
|
|
3198
|
-
// Handle the command setmatterpasscode from Settings
|
|
3199
2662
|
if (command === 'setmatterpasscode') {
|
|
3200
2663
|
const passcode = Math.min(Math.max(parseInt(param), 10000000), 90000000);
|
|
3201
2664
|
this.matterbridgeInformation.matterPasscode = passcode;
|
|
@@ -3204,20 +2667,17 @@ export class Matterbridge extends EventEmitter {
|
|
|
3204
2667
|
res.json({ message: 'Command received' });
|
|
3205
2668
|
return;
|
|
3206
2669
|
}
|
|
3207
|
-
// Handle the command setmbloglevel from Settings
|
|
3208
2670
|
if (command === 'setmblogfile') {
|
|
3209
2671
|
this.log.debug('Matterbridge file log:', param);
|
|
3210
2672
|
this.matterbridgeInformation.fileLogger = param === 'true';
|
|
3211
2673
|
await this.nodeContext?.set('matterbridgeFileLog', param === 'true');
|
|
3212
|
-
// Create the file logger for matterbridge
|
|
3213
2674
|
if (param === 'true')
|
|
3214
|
-
AnsiLogger.setGlobalLogfile(path.join(this.matterbridgeDirectory, this.matterbrideLoggerFile), "debug"
|
|
2675
|
+
AnsiLogger.setGlobalLogfile(path.join(this.matterbridgeDirectory, this.matterbrideLoggerFile), "debug", true);
|
|
3215
2676
|
else
|
|
3216
2677
|
AnsiLogger.setGlobalLogfile(undefined);
|
|
3217
2678
|
res.json({ message: 'Command received' });
|
|
3218
2679
|
return;
|
|
3219
2680
|
}
|
|
3220
|
-
// Handle the command setmbloglevel from Settings
|
|
3221
2681
|
if (command === 'setmjlogfile') {
|
|
3222
2682
|
this.log.debug('Matter file log:', param);
|
|
3223
2683
|
this.matterbridgeInformation.matterFileLogger = param === 'true';
|
|
@@ -3244,43 +2704,36 @@ export class Matterbridge extends EventEmitter {
|
|
|
3244
2704
|
res.json({ message: 'Command received' });
|
|
3245
2705
|
return;
|
|
3246
2706
|
}
|
|
3247
|
-
// Handle the command unregister from Settings
|
|
3248
2707
|
if (command === 'unregister') {
|
|
3249
2708
|
await this.unregisterAndShutdownProcess();
|
|
3250
2709
|
res.json({ message: 'Command received' });
|
|
3251
2710
|
return;
|
|
3252
2711
|
}
|
|
3253
|
-
// Handle the command reset from Settings
|
|
3254
2712
|
if (command === 'reset') {
|
|
3255
2713
|
await this.shutdownProcessAndReset();
|
|
3256
2714
|
res.json({ message: 'Command received' });
|
|
3257
2715
|
return;
|
|
3258
2716
|
}
|
|
3259
|
-
// Handle the command factoryreset from Settings
|
|
3260
2717
|
if (command === 'factoryreset') {
|
|
3261
2718
|
await this.shutdownProcessAndFactoryReset();
|
|
3262
2719
|
res.json({ message: 'Command received' });
|
|
3263
2720
|
return;
|
|
3264
2721
|
}
|
|
3265
|
-
// Handle the command shutdown from Header
|
|
3266
2722
|
if (command === 'shutdown') {
|
|
3267
2723
|
await this.shutdownProcess();
|
|
3268
2724
|
res.json({ message: 'Command received' });
|
|
3269
2725
|
return;
|
|
3270
2726
|
}
|
|
3271
|
-
// Handle the command restart from Header
|
|
3272
2727
|
if (command === 'restart') {
|
|
3273
2728
|
await this.restartProcess();
|
|
3274
2729
|
res.json({ message: 'Command received' });
|
|
3275
2730
|
return;
|
|
3276
2731
|
}
|
|
3277
|
-
// Handle the command update from Header
|
|
3278
2732
|
if (command === 'update') {
|
|
3279
2733
|
this.log.info('Updating matterbridge...');
|
|
3280
2734
|
try {
|
|
3281
2735
|
await this.spawnCommand('npm', ['install', '-g', 'matterbridge', '--omit=dev', '--verbose']);
|
|
3282
2736
|
this.log.info('Matterbridge has been updated. Full restart required.');
|
|
3283
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
3284
2737
|
}
|
|
3285
2738
|
catch (error) {
|
|
3286
2739
|
this.log.error('Error updating matterbridge');
|
|
@@ -3290,11 +2743,9 @@ export class Matterbridge extends EventEmitter {
|
|
|
3290
2743
|
res.json({ message: 'Command received' });
|
|
3291
2744
|
return;
|
|
3292
2745
|
}
|
|
3293
|
-
// Handle the command saveconfig from Home
|
|
3294
2746
|
if (command === 'saveconfig') {
|
|
3295
2747
|
param = param.replace(/\*/g, '\\');
|
|
3296
2748
|
this.log.info(`Saving config for plugin ${plg}${param}${nf}...`);
|
|
3297
|
-
// console.log('Req.body:', JSON.stringify(req.body, null, 2));
|
|
3298
2749
|
if (!this.plugins.has(param)) {
|
|
3299
2750
|
this.log.warn(`Plugin ${plg}${param}${wr} not found in matterbridge`);
|
|
3300
2751
|
}
|
|
@@ -3308,39 +2759,33 @@ export class Matterbridge extends EventEmitter {
|
|
|
3308
2759
|
res.json({ message: 'Command received' });
|
|
3309
2760
|
return;
|
|
3310
2761
|
}
|
|
3311
|
-
// Handle the command installplugin from Home
|
|
3312
2762
|
if (command === 'installplugin') {
|
|
3313
2763
|
param = param.replace(/\*/g, '\\');
|
|
3314
2764
|
this.log.info(`Installing plugin ${plg}${param}${nf}...`);
|
|
3315
2765
|
try {
|
|
3316
2766
|
await this.spawnCommand('npm', ['install', '-g', param, '--omit=dev', '--verbose']);
|
|
3317
2767
|
this.log.info(`Plugin ${plg}${param}${nf} installed. Full restart required.`);
|
|
3318
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
3319
2768
|
}
|
|
3320
2769
|
catch (error) {
|
|
3321
2770
|
this.log.error(`Error installing plugin ${plg}${param}${er}`);
|
|
3322
2771
|
}
|
|
3323
2772
|
this.wssSendRestartRequired();
|
|
3324
2773
|
param = param.split('@')[0];
|
|
3325
|
-
// Also add the plugin to matterbridge so no return!
|
|
3326
2774
|
if (param === 'matterbridge') {
|
|
3327
|
-
// If we used the command installplugin to install a dev or a specific version of matterbridge we don't want to add it to matterbridge
|
|
3328
2775
|
res.json({ message: 'Command received' });
|
|
3329
2776
|
return;
|
|
3330
2777
|
}
|
|
3331
2778
|
}
|
|
3332
|
-
// Handle the command addplugin from Home
|
|
3333
2779
|
if (command === 'addplugin' || command === 'installplugin') {
|
|
3334
2780
|
param = param.replace(/\*/g, '\\');
|
|
3335
2781
|
const plugin = await this.plugins.add(param);
|
|
3336
2782
|
if (plugin) {
|
|
3337
|
-
this.plugins.load(plugin, true, 'The plugin has been added', true);
|
|
2783
|
+
this.plugins.load(plugin, true, 'The plugin has been added', true);
|
|
3338
2784
|
}
|
|
3339
2785
|
res.json({ message: 'Command received' });
|
|
3340
2786
|
this.wssSendRefreshRequired();
|
|
3341
2787
|
return;
|
|
3342
2788
|
}
|
|
3343
|
-
// Handle the command removeplugin from Home
|
|
3344
2789
|
if (command === 'removeplugin') {
|
|
3345
2790
|
if (!this.plugins.has(param)) {
|
|
3346
2791
|
this.log.warn(`Plugin ${plg}${param}${wr} not found in matterbridge`);
|
|
@@ -3354,7 +2799,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
3354
2799
|
this.wssSendRefreshRequired();
|
|
3355
2800
|
return;
|
|
3356
2801
|
}
|
|
3357
|
-
// Handle the command enableplugin from Home
|
|
3358
2802
|
if (command === 'enableplugin') {
|
|
3359
2803
|
if (!this.plugins.has(param)) {
|
|
3360
2804
|
this.log.warn(`Plugin ${plg}${param}${wr} not found in matterbridge`);
|
|
@@ -3372,14 +2816,13 @@ export class Matterbridge extends EventEmitter {
|
|
|
3372
2816
|
plugin.registeredDevices = undefined;
|
|
3373
2817
|
plugin.addedDevices = undefined;
|
|
3374
2818
|
await this.plugins.enable(param);
|
|
3375
|
-
this.plugins.load(plugin, true, 'The plugin has been enabled', true);
|
|
2819
|
+
this.plugins.load(plugin, true, 'The plugin has been enabled', true);
|
|
3376
2820
|
}
|
|
3377
2821
|
}
|
|
3378
2822
|
res.json({ message: 'Command received' });
|
|
3379
2823
|
this.wssSendRefreshRequired();
|
|
3380
2824
|
return;
|
|
3381
2825
|
}
|
|
3382
|
-
// Handle the command disableplugin from Home
|
|
3383
2826
|
if (command === 'disableplugin') {
|
|
3384
2827
|
if (!this.plugins.has(param)) {
|
|
3385
2828
|
this.log.warn(`Plugin ${plg}${param}${wr} not found in matterbridge`);
|
|
@@ -3396,7 +2839,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
3396
2839
|
return;
|
|
3397
2840
|
}
|
|
3398
2841
|
});
|
|
3399
|
-
// Fallback for routing (must be the last route)
|
|
3400
2842
|
this.expressApp.get('*', (req, res) => {
|
|
3401
2843
|
this.log.debug('The frontend sent:', req.url);
|
|
3402
2844
|
this.log.debug('Response send file:', path.join(this.rootDirectory, 'frontend/build/index.html'));
|
|
@@ -3404,11 +2846,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
3404
2846
|
});
|
|
3405
2847
|
this.log.debug(`Frontend initialized on port ${YELLOW}${port}${db} static ${UNDERLINE}${path.join(this.rootDirectory, 'frontend/build')}${UNDERLINEOFF}${rs}`);
|
|
3406
2848
|
}
|
|
3407
|
-
/**
|
|
3408
|
-
* Retrieves the cluster text description from a given device.
|
|
3409
|
-
* @param {MatterbridgeDevice} device - The MatterbridgeDevice object.
|
|
3410
|
-
* @returns {string} The attributes description of the cluster servers in the device.
|
|
3411
|
-
*/
|
|
3412
2849
|
getClusterTextFromDevice(device) {
|
|
3413
2850
|
const stringifyUserLabel = (endpoint) => {
|
|
3414
2851
|
const labelList = endpoint.getClusterServer(UserLabelCluster)?.attributes.labelList.getLocal();
|
|
@@ -3431,11 +2868,9 @@ export class Matterbridge extends EventEmitter {
|
|
|
3431
2868
|
return '';
|
|
3432
2869
|
};
|
|
3433
2870
|
let attributes = '';
|
|
3434
|
-
// this.log.debug(`***getClusterTextFromDevice: ${device.deviceName} (${device.name})`);
|
|
3435
2871
|
const clusterServers = device.getAllClusterServers();
|
|
3436
2872
|
clusterServers.forEach((clusterServer) => {
|
|
3437
2873
|
try {
|
|
3438
|
-
// this.log.debug(`**--clusterServer: ${clusterServer.id} (${clusterServer.name})`);
|
|
3439
2874
|
if (clusterServer.name === 'OnOff')
|
|
3440
2875
|
attributes += `OnOff: ${clusterServer.attributes.onOff.getLocal()} `;
|
|
3441
2876
|
if (clusterServer.name === 'Switch')
|
|
@@ -3486,30 +2921,18 @@ export class Matterbridge extends EventEmitter {
|
|
|
3486
2921
|
attributes += `${stringifyFixedLabel(device)} `;
|
|
3487
2922
|
if (clusterServer.name === 'UserLabel')
|
|
3488
2923
|
attributes += `${stringifyUserLabel(device)} `;
|
|
3489
|
-
// this.log.debug(`*--clusterServer: ${clusterServer.id} (${clusterServer.name})`);
|
|
3490
2924
|
}
|
|
3491
2925
|
catch (error) {
|
|
3492
2926
|
this.log.error(`getClusterTextFromDevice with ${clusterServer.name} error: ${error}`);
|
|
3493
2927
|
}
|
|
3494
2928
|
});
|
|
3495
|
-
// this.log.debug(`*getClusterTextFromDevice: ${device.deviceName} (${device.name})`);
|
|
3496
2929
|
return attributes;
|
|
3497
2930
|
}
|
|
3498
|
-
/**
|
|
3499
|
-
* Initializes the Matterbridge instance as extension for zigbee2mqtt.
|
|
3500
|
-
* @deprecated This method is deprecated and will be removed in a future version.
|
|
3501
|
-
*
|
|
3502
|
-
* @returns A Promise that resolves when the initialization is complete.
|
|
3503
|
-
*/
|
|
3504
2931
|
async startExtension(dataPath, extensionVersion, port = 5540) {
|
|
3505
|
-
// Set the bridge mode
|
|
3506
2932
|
this.bridgeMode = 'bridge';
|
|
3507
|
-
// Set the first port to use
|
|
3508
2933
|
this.port = port;
|
|
3509
|
-
|
|
3510
|
-
this.log = new AnsiLogger({ logName: 'Matterbridge', logTimestampFormat: 4 /* TimestampFormat.TIME_MILLIS */, logLevel: "info" /* LogLevel.INFO */ });
|
|
2934
|
+
this.log = new AnsiLogger({ logName: 'Matterbridge', logTimestampFormat: 4, logLevel: "info" });
|
|
3511
2935
|
this.log.debug('Matterbridge extension is starting...');
|
|
3512
|
-
// Initialize NodeStorage
|
|
3513
2936
|
this.matterbridgeDirectory = dataPath;
|
|
3514
2937
|
this.log.debug('Creating node storage manager dir: ' + path.join(this.matterbridgeDirectory, 'node_storage'));
|
|
3515
2938
|
this.nodeStorage = new NodeStorageManager({ dir: path.join(this.matterbridgeDirectory, 'node_storage'), logging: false });
|
|
@@ -3528,13 +2951,10 @@ export class Matterbridge extends EventEmitter {
|
|
|
3528
2951
|
};
|
|
3529
2952
|
this.plugins.set(plugin);
|
|
3530
2953
|
this.plugins.saveToStorage();
|
|
3531
|
-
// Log system info and create .matterbridge directory
|
|
3532
2954
|
await this.logNodeAndSystemInfo();
|
|
3533
2955
|
this.matterbridgeDirectory = dataPath;
|
|
3534
|
-
// Set matter.js logger level and format
|
|
3535
2956
|
Logger.defaultLogLevel = MatterLogLevel.INFO;
|
|
3536
2957
|
Logger.format = MatterLogFormat.ANSI;
|
|
3537
|
-
// Start the storage and create matterbridgeContext
|
|
3538
2958
|
await this.startMatterStorage('json', path.join(this.matterbridgeDirectory, this.matterStorageName));
|
|
3539
2959
|
if (!this.storageManager)
|
|
3540
2960
|
return false;
|
|
@@ -3544,7 +2964,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
3544
2964
|
await this.matterbridgeContext.set('softwareVersion', 1);
|
|
3545
2965
|
await this.matterbridgeContext.set('softwareVersionString', this.matterbridgeVersion);
|
|
3546
2966
|
await this.matterbridgeContext.set('hardwareVersion', 1);
|
|
3547
|
-
await this.matterbridgeContext.set('hardwareVersionString', extensionVersion);
|
|
2967
|
+
await this.matterbridgeContext.set('hardwareVersionString', extensionVersion);
|
|
3548
2968
|
this.matterServer = this.createMatterServer(this.storageManager);
|
|
3549
2969
|
this.log.debug(`Creating commissioning server for ${plg}Matterbridge${db}`);
|
|
3550
2970
|
this.commissioningServer = await this.createCommisioningServer(this.matterbridgeContext, 'Matterbridge');
|
|
@@ -3557,7 +2977,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
3557
2977
|
await this.startMatterServer();
|
|
3558
2978
|
this.log.info('Matter server started');
|
|
3559
2979
|
await this.showCommissioningQRCode(this.commissioningServer, this.matterbridgeContext, this.nodeContext, 'Matterbridge');
|
|
3560
|
-
// Set reachability to true and trigger event after 60 seconds
|
|
3561
2980
|
setTimeout(() => {
|
|
3562
2981
|
this.log.info(`Setting reachability to true for ${plg}Matterbridge${db}`);
|
|
3563
2982
|
if (this.commissioningServer)
|
|
@@ -3567,31 +2986,14 @@ export class Matterbridge extends EventEmitter {
|
|
|
3567
2986
|
}, 60 * 1000);
|
|
3568
2987
|
return this.commissioningServer.isCommissioned();
|
|
3569
2988
|
}
|
|
3570
|
-
/**
|
|
3571
|
-
* Close the Matterbridge instance as extension for zigbee2mqtt.
|
|
3572
|
-
* @deprecated This method is deprecated and will be removed in a future version.
|
|
3573
|
-
*
|
|
3574
|
-
* @returns A Promise that resolves when the initialization is complete.
|
|
3575
|
-
*/
|
|
3576
2989
|
async stopExtension() {
|
|
3577
|
-
// Closing matter
|
|
3578
2990
|
await this.stopMatterServer();
|
|
3579
|
-
// Clearing the session manager
|
|
3580
|
-
// this.matterbridgeContext?.createContext('SessionManager').clear();
|
|
3581
|
-
// Closing storage
|
|
3582
2991
|
await this.stopMatterStorage();
|
|
3583
2992
|
this.log.info('Matter server stopped');
|
|
3584
2993
|
}
|
|
3585
|
-
/**
|
|
3586
|
-
* Checks if the extension is commissioned.
|
|
3587
|
-
* @deprecated This method is deprecated and will be removed in a future version.
|
|
3588
|
-
*
|
|
3589
|
-
* @returns {boolean} Returns true if the extension is commissioned, false otherwise.
|
|
3590
|
-
*/
|
|
3591
2994
|
isExtensionCommissioned() {
|
|
3592
2995
|
if (!this.commissioningServer)
|
|
3593
2996
|
return false;
|
|
3594
2997
|
return this.commissioningServer.isCommissioned();
|
|
3595
2998
|
}
|
|
3596
2999
|
}
|
|
3597
|
-
//# sourceMappingURL=matterbridge.js.map
|