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