matterbridge 1.6.8-dev.6 → 1.6.8-dev.8
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 +2 -2
- package/dist/cli.js +0 -26
- package/dist/cluster/export.js +0 -2
- package/dist/defaultConfigSchema.js +0 -23
- package/dist/deviceManager.js +1 -26
- package/dist/index.js +0 -30
- package/dist/logger/export.js +0 -1
- package/dist/matter/export.js +0 -1
- package/dist/matterbridge.js +189 -812
- package/dist/matterbridgeAccessoryPlatform.js +0 -33
- package/dist/matterbridgeBehaviors.js +1 -29
- package/dist/matterbridgeDevice.js +10 -997
- package/dist/matterbridgeDeviceTypes.js +11 -82
- package/dist/matterbridgeDynamicPlatform.js +0 -33
- package/dist/matterbridgeEdge.js +0 -529
- package/dist/matterbridgeEndpoint.js +12 -1121
- package/dist/matterbridgePlatform.js +3 -99
- 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 +8 -253
- package/npm-shrinkwrap.json +8 -8
- 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 -473
- 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 -123
- 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
|
-
// @matter
|
|
47
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,39 +990,29 @@ export class Matterbridge extends EventEmitter {
|
|
|
1210
990
|
});
|
|
1211
991
|
this.webSocketServer = undefined;
|
|
1212
992
|
}
|
|
1213
|
-
|
|
1214
|
-
if (hasParameter('convert') && this.edge === false && this.matterbridgeContext && ['updating...', 'restarting...', 'shutting down...'].includes(message)) {
|
|
993
|
+
if (!hasParameter('nostorageconversion') && this.edge === false && this.matterbridgeContext && ['updating...', 'restarting...', 'shutting down...'].includes(message)) {
|
|
1215
994
|
await this.convertStorage(this.matterbridgeContext, 'Mattebridge');
|
|
1216
995
|
}
|
|
1217
|
-
// Closing matter
|
|
1218
996
|
await this.stopMatterServer();
|
|
1219
|
-
// Closing matter storage
|
|
1220
997
|
await this.stopMatterStorage();
|
|
1221
|
-
// Remove the matterfilelogger
|
|
1222
998
|
try {
|
|
1223
999
|
Logger.removeLogger('matterfilelogger');
|
|
1224
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
1225
1000
|
}
|
|
1226
1001
|
catch (error) {
|
|
1227
|
-
// this.log.debug(`Error removing the matterfilelogger for file ${CYAN}${path.join(this.matterbridgeDirectory, this.matterLoggerFile)}${er}: ${error instanceof Error ? error.message : error}`);
|
|
1228
1002
|
}
|
|
1229
|
-
// Serialize registeredDevices
|
|
1230
1003
|
if (this.nodeStorage && this.nodeContext) {
|
|
1231
1004
|
this.log.info('Saving registered devices...');
|
|
1232
1005
|
const serializedRegisteredDevices = [];
|
|
1233
1006
|
this.devices.forEach(async (device) => {
|
|
1234
1007
|
const serializedMatterbridgeDevice = device.serialize();
|
|
1235
|
-
// this.log.info(`- ${serializedMatterbridgeDevice.deviceName}${rs}\n`, serializedMatterbridgeDevice);
|
|
1236
1008
|
if (serializedMatterbridgeDevice)
|
|
1237
1009
|
serializedRegisteredDevices.push(serializedMatterbridgeDevice);
|
|
1238
1010
|
});
|
|
1239
1011
|
await this.nodeContext.set('devices', serializedRegisteredDevices);
|
|
1240
1012
|
this.log.info(`Saved registered devices (${serializedRegisteredDevices?.length})`);
|
|
1241
|
-
// Clear nodeContext and nodeStorage (they just need 1000ms to write the data to disk)
|
|
1242
1013
|
this.log.debug(`Closing node storage context for ${plg}Matterbridge${db}...`);
|
|
1243
1014
|
await this.nodeContext.close();
|
|
1244
1015
|
this.nodeContext = undefined;
|
|
1245
|
-
// Clear nodeContext for each plugin (they just need 1000ms to write the data to disk)
|
|
1246
1016
|
for (const plugin of this.plugins) {
|
|
1247
1017
|
if (plugin.nodeContext) {
|
|
1248
1018
|
this.log.debug(`Closing node storage context for plugin ${plg}${plugin.name}${db}...`);
|
|
@@ -1273,16 +1043,13 @@ export class Matterbridge extends EventEmitter {
|
|
|
1273
1043
|
}
|
|
1274
1044
|
else {
|
|
1275
1045
|
if (message === 'shutting down with reset...') {
|
|
1276
|
-
// Delete matter storage file
|
|
1277
1046
|
this.log.info('Resetting Matterbridge commissioning information...');
|
|
1278
1047
|
await fs.unlink(path.join(this.matterbridgeDirectory, this.matterStorageName));
|
|
1279
1048
|
this.log.info('Reset done! Remove all paired devices from the controllers.');
|
|
1280
1049
|
}
|
|
1281
1050
|
if (message === 'shutting down with factory reset...') {
|
|
1282
|
-
// Delete matter storage file
|
|
1283
1051
|
this.log.info('Resetting Matterbridge commissioning information...');
|
|
1284
1052
|
await fs.unlink(path.join(this.matterbridgeDirectory, this.matterStorageName));
|
|
1285
|
-
// Delete node storage directory with its subdirectories
|
|
1286
1053
|
this.log.info('Resetting Matterbridge storage...');
|
|
1287
1054
|
await fs.rm(path.join(this.matterbridgeDirectory, this.nodeStorageName), { recursive: true });
|
|
1288
1055
|
this.log.info('Factory reset done! Remove all paired devices from the controllers.');
|
|
@@ -1295,33 +1062,19 @@ export class Matterbridge extends EventEmitter {
|
|
|
1295
1062
|
this.initialized = false;
|
|
1296
1063
|
}
|
|
1297
1064
|
}
|
|
1298
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
1299
1065
|
async addBridgedEndpoint(pluginName, device) {
|
|
1300
|
-
// Nothing to do here
|
|
1301
1066
|
}
|
|
1302
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
1303
1067
|
async removeBridgedEndpoint(pluginName, device) {
|
|
1304
|
-
// Nothing to do here
|
|
1305
1068
|
}
|
|
1306
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
1307
1069
|
async removeAllBridgedEndpoints(pluginName) {
|
|
1308
|
-
// Nothing to do here
|
|
1309
1070
|
}
|
|
1310
|
-
/**
|
|
1311
|
-
* Adds a bridged device to the Matterbridge.
|
|
1312
|
-
* @param pluginName - The name of the plugin.
|
|
1313
|
-
* @param device - The bridged device to add.
|
|
1314
|
-
* @returns {Promise<void>} - A promise that resolves when the device is added.
|
|
1315
|
-
*/
|
|
1316
1071
|
async addBridgedDevice(pluginName, device) {
|
|
1317
1072
|
this.log.debug(`Adding bridged device ${dev}${device.deviceName}${db} (${zb}${device.name}${db}) for plugin ${plg}${pluginName}${db}`);
|
|
1318
|
-
// Check if the plugin is registered
|
|
1319
1073
|
const plugin = this.plugins.get(pluginName);
|
|
1320
1074
|
if (!plugin) {
|
|
1321
1075
|
this.log.error(`Error adding bridged device ${dev}${device.deviceName}${er} (${zb}${device.name}${er}) plugin ${plg}${pluginName}${er} not found`);
|
|
1322
1076
|
return;
|
|
1323
1077
|
}
|
|
1324
|
-
// Register and add the device to matterbridge aggregator in bridge mode
|
|
1325
1078
|
if (this.bridgeMode === 'bridge') {
|
|
1326
1079
|
if (!this.matterAggregator) {
|
|
1327
1080
|
this.log.error(`Adding bridged device ${dev}${device.deviceName}${er} (${zb}${device.name}${er}) for plugin ${plg}${pluginName}${er} error: matterAggregator not found`);
|
|
@@ -1329,11 +1082,8 @@ export class Matterbridge extends EventEmitter {
|
|
|
1329
1082
|
}
|
|
1330
1083
|
this.matterAggregator.addBridgedDevice(device);
|
|
1331
1084
|
}
|
|
1332
|
-
// The first time create the commissioning server and the aggregator for DynamicPlatform
|
|
1333
|
-
// Register and add the device in childbridge mode
|
|
1334
1085
|
if (this.bridgeMode === 'childbridge') {
|
|
1335
1086
|
if (plugin.type === 'AccessoryPlatform') {
|
|
1336
|
-
// Check if the plugin is locked with the commissioning server
|
|
1337
1087
|
if (!plugin.locked) {
|
|
1338
1088
|
plugin.locked = true;
|
|
1339
1089
|
plugin.storageContext = await this.importCommissioningServerContext(plugin.name, device);
|
|
@@ -1347,7 +1097,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1347
1097
|
}
|
|
1348
1098
|
}
|
|
1349
1099
|
if (plugin.type === 'DynamicPlatform') {
|
|
1350
|
-
// Check if the plugin is locked with the commissioning server and the aggregator
|
|
1351
1100
|
if (!plugin.locked) {
|
|
1352
1101
|
plugin.locked = true;
|
|
1353
1102
|
this.log.debug(`Creating commissioning server context for ${plg}${plugin.name}${db}`);
|
|
@@ -1368,25 +1117,16 @@ export class Matterbridge extends EventEmitter {
|
|
|
1368
1117
|
plugin.registeredDevices++;
|
|
1369
1118
|
if (plugin.addedDevices !== undefined)
|
|
1370
1119
|
plugin.addedDevices++;
|
|
1371
|
-
// Add the device to the DeviceManager
|
|
1372
1120
|
this.devices.set(device);
|
|
1373
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}`);
|
|
1374
1122
|
}
|
|
1375
|
-
/**
|
|
1376
|
-
* Removes a bridged device from the Matterbridge.
|
|
1377
|
-
* @param pluginName - The name of the plugin.
|
|
1378
|
-
* @param device - The device to be removed.
|
|
1379
|
-
* @returns A Promise that resolves when the device is successfully removed.
|
|
1380
|
-
*/
|
|
1381
1123
|
async removeBridgedDevice(pluginName, device) {
|
|
1382
1124
|
this.log.debug(`Removing bridged device ${dev}${device.deviceName}${db} (${zb}${device.name}${db}) for plugin ${plg}${pluginName}${db}`);
|
|
1383
|
-
// Check if the plugin is registered
|
|
1384
1125
|
const plugin = this.plugins.get(pluginName);
|
|
1385
1126
|
if (!plugin) {
|
|
1386
1127
|
this.log.error(`Error removing bridged device ${dev}${device.deviceName}${er} (${zb}${device.name}${er}) for plugin ${plg}${pluginName}${er}: plugin not found`);
|
|
1387
1128
|
return;
|
|
1388
1129
|
}
|
|
1389
|
-
// Remove the device from matterbridge aggregator in bridge mode
|
|
1390
1130
|
if (this.bridgeMode === 'bridge') {
|
|
1391
1131
|
if (!this.matterAggregator) {
|
|
1392
1132
|
this.log.error(`Error removing bridged device ${dev}${device.deviceName}${er} (${zb}${device.name}${er}) for plugin ${plg}${pluginName}${er}: matterAggregator not found`);
|
|
@@ -1396,8 +1136,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1396
1136
|
device.setBridgedDeviceReachability(false);
|
|
1397
1137
|
device.getClusterServerById(BridgedDeviceBasicInformation.Cluster.id)?.triggerReachableChangedEvent({ reachableNewValue: false });
|
|
1398
1138
|
}
|
|
1399
|
-
// device.getClusterServerById(BridgedDeviceBasicInformation.Cluster.id)?.triggerShutDownEvent({});
|
|
1400
|
-
// device.getClusterServerById(BridgedDeviceBasicInformation.Cluster.id)?.triggerLeaveEvent({});
|
|
1401
1139
|
this.matterAggregator?.removeBridgedDevice(device);
|
|
1402
1140
|
this.log.info(`Removed bridged device(${plugin.registeredDevices}/${plugin.addedDevices}) ${dev}${device.deviceName}${nf} (${zb}${device.name}${nf}) for plugin ${plg}${pluginName}${nf}`);
|
|
1403
1141
|
if (plugin.registeredDevices !== undefined)
|
|
@@ -1405,7 +1143,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1405
1143
|
if (plugin.addedDevices !== undefined)
|
|
1406
1144
|
plugin.addedDevices--;
|
|
1407
1145
|
}
|
|
1408
|
-
// Remove the device in childbridge mode
|
|
1409
1146
|
if (this.bridgeMode === 'childbridge') {
|
|
1410
1147
|
if (plugin.type === 'AccessoryPlatform') {
|
|
1411
1148
|
if (!plugin.commissioningServer) {
|
|
@@ -1429,22 +1166,14 @@ export class Matterbridge extends EventEmitter {
|
|
|
1429
1166
|
plugin.registeredDevices--;
|
|
1430
1167
|
if (plugin.addedDevices !== undefined)
|
|
1431
1168
|
plugin.addedDevices--;
|
|
1432
|
-
// Remove the commissioning server
|
|
1433
1169
|
if (plugin.registeredDevices === 0 && plugin.addedDevices === 0 && plugin.commissioningServer) {
|
|
1434
1170
|
this.matterServer?.removeCommissioningServer(plugin.commissioningServer);
|
|
1435
1171
|
plugin.commissioningServer = undefined;
|
|
1436
1172
|
this.log.info(`Removed commissioning server for plugin ${plg}${pluginName}${nf}`);
|
|
1437
1173
|
}
|
|
1438
1174
|
}
|
|
1439
|
-
// Remove the device from the DeviceManager
|
|
1440
1175
|
this.devices.remove(device);
|
|
1441
1176
|
}
|
|
1442
|
-
/**
|
|
1443
|
-
* Removes all bridged devices associated with a specific plugin.
|
|
1444
|
-
*
|
|
1445
|
-
* @param pluginName - The name of the plugin.
|
|
1446
|
-
* @returns A promise that resolves when all devices have been removed.
|
|
1447
|
-
*/
|
|
1448
1177
|
async removeAllBridgedDevices(pluginName) {
|
|
1449
1178
|
this.log.debug(`Removing all bridged devices for plugin ${plg}${pluginName}${db}`);
|
|
1450
1179
|
this.devices.forEach(async (device) => {
|
|
@@ -1453,13 +1182,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
1453
1182
|
}
|
|
1454
1183
|
});
|
|
1455
1184
|
}
|
|
1456
|
-
/**
|
|
1457
|
-
* Starts the Matterbridge in bridge mode.
|
|
1458
|
-
* @private
|
|
1459
|
-
* @returns {Promise<void>} A promise that resolves when the Matterbridge is started.
|
|
1460
|
-
*/
|
|
1461
1185
|
async startBridge() {
|
|
1462
|
-
// Plugins are configured by a timer when matter server is started and plugin.configured is set to true
|
|
1463
1186
|
if (!this.storageManager)
|
|
1464
1187
|
throw new Error('No storage manager initialized');
|
|
1465
1188
|
if (!this.matterbridgeContext)
|
|
@@ -1478,7 +1201,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1478
1201
|
let failCount = 0;
|
|
1479
1202
|
this.startMatterInterval = setInterval(async () => {
|
|
1480
1203
|
for (const plugin of this.plugins) {
|
|
1481
|
-
// new code to not start the bridge if one plugin is in error cause the controllers will delete the devices loosing all the configuration
|
|
1482
1204
|
if (!plugin.enabled)
|
|
1483
1205
|
continue;
|
|
1484
1206
|
if (plugin.error) {
|
|
@@ -1503,18 +1225,15 @@ export class Matterbridge extends EventEmitter {
|
|
|
1503
1225
|
clearInterval(this.startMatterInterval);
|
|
1504
1226
|
this.startMatterInterval = undefined;
|
|
1505
1227
|
this.log.debug('Cleared startMatterInterval interval for Matterbridge');
|
|
1506
|
-
// Start the Matter server
|
|
1507
1228
|
await this.startMatterServer();
|
|
1508
1229
|
this.log.notice('Matter server started');
|
|
1509
|
-
// Show the QR code for commissioning or log the already commissioned message
|
|
1510
1230
|
await this.showCommissioningQRCode(this.commissioningServer, this.matterbridgeContext, this.nodeContext, 'Matterbridge');
|
|
1511
|
-
// Configure the plugins
|
|
1512
1231
|
this.configureTimeout = setTimeout(async () => {
|
|
1513
1232
|
for (const plugin of this.plugins) {
|
|
1514
1233
|
if (!plugin.enabled || !plugin.loaded || !plugin.started || plugin.error)
|
|
1515
1234
|
continue;
|
|
1516
1235
|
try {
|
|
1517
|
-
await this.plugins.configure(plugin);
|
|
1236
|
+
await this.plugins.configure(plugin);
|
|
1518
1237
|
}
|
|
1519
1238
|
catch (error) {
|
|
1520
1239
|
plugin.error = true;
|
|
@@ -1523,7 +1242,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1523
1242
|
}
|
|
1524
1243
|
this.wssSendRefreshRequired();
|
|
1525
1244
|
}, 30 * 1000);
|
|
1526
|
-
// Setting reachability to true
|
|
1527
1245
|
this.reachabilityTimeout = setTimeout(() => {
|
|
1528
1246
|
this.log.info(`Setting reachability to true for ${plg}Matterbridge${db}`);
|
|
1529
1247
|
if (this.commissioningServer)
|
|
@@ -1533,14 +1251,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
1533
1251
|
}, 60 * 1000);
|
|
1534
1252
|
}, 1000);
|
|
1535
1253
|
}
|
|
1536
|
-
/**
|
|
1537
|
-
* Starts the Matterbridge in childbridge mode.
|
|
1538
|
-
* @private
|
|
1539
|
-
* @returns {Promise<void>} A promise that resolves when the Matterbridge is started.
|
|
1540
|
-
*/
|
|
1541
1254
|
async startChildbridge() {
|
|
1542
|
-
// Matterbridge.addBridgedDevice creates the commissionig servers and add the devices to the the commissioning server or to the aggregator
|
|
1543
|
-
// Plugins are configured by a timer when matter server is started and plugin.configured is set to true
|
|
1544
1255
|
if (!this.storageManager)
|
|
1545
1256
|
throw new Error('No storage manager initialized');
|
|
1546
1257
|
this.matterServer = this.createMatterServer(this.storageManager);
|
|
@@ -1550,7 +1261,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1550
1261
|
this.startMatterInterval = setInterval(async () => {
|
|
1551
1262
|
let allStarted = true;
|
|
1552
1263
|
for (const plugin of this.plugins) {
|
|
1553
|
-
// new code to not start the bridge if one plugin is in error cause the controllers will delete the devices loosing all the configuration
|
|
1554
1264
|
if (!plugin.enabled)
|
|
1555
1265
|
continue;
|
|
1556
1266
|
if (plugin.error) {
|
|
@@ -1578,16 +1288,14 @@ export class Matterbridge extends EventEmitter {
|
|
|
1578
1288
|
clearInterval(this.startMatterInterval);
|
|
1579
1289
|
this.startMatterInterval = undefined;
|
|
1580
1290
|
this.log.debug('Cleared startMatterInterval interval in childbridge mode');
|
|
1581
|
-
// Start the Matter server
|
|
1582
1291
|
await this.startMatterServer();
|
|
1583
1292
|
this.log.notice('Matter server started');
|
|
1584
|
-
// Configure the plugins
|
|
1585
1293
|
this.configureTimeout = setTimeout(async () => {
|
|
1586
1294
|
for (const plugin of this.plugins) {
|
|
1587
1295
|
if (!plugin.enabled || !plugin.loaded || !plugin.started || plugin.error)
|
|
1588
1296
|
continue;
|
|
1589
1297
|
try {
|
|
1590
|
-
await this.plugins.configure(plugin);
|
|
1298
|
+
await this.plugins.configure(plugin);
|
|
1591
1299
|
}
|
|
1592
1300
|
catch (error) {
|
|
1593
1301
|
plugin.error = true;
|
|
@@ -1616,7 +1324,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1616
1324
|
continue;
|
|
1617
1325
|
}
|
|
1618
1326
|
await this.showCommissioningQRCode(plugin.commissioningServer, plugin.storageContext, plugin.nodeContext, plugin.name);
|
|
1619
|
-
// Setting reachability to true
|
|
1620
1327
|
plugin.reachabilityTimeout = setTimeout(() => {
|
|
1621
1328
|
this.log.info(`Setting reachability to true for ${plg}${plugin.name}${db}`);
|
|
1622
1329
|
if (plugin.commissioningServer)
|
|
@@ -1629,11 +1336,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1629
1336
|
}
|
|
1630
1337
|
}, 1000);
|
|
1631
1338
|
}
|
|
1632
|
-
/**
|
|
1633
|
-
* Starts the Matterbridge controller.
|
|
1634
|
-
* @private
|
|
1635
|
-
* @returns {Promise<void>} A promise that resolves when the Matterbridge is started.
|
|
1636
|
-
*/
|
|
1637
1339
|
async startController() {
|
|
1638
1340
|
if (!this.storageManager) {
|
|
1639
1341
|
this.log.error('No storage manager initialized');
|
|
@@ -1696,7 +1398,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
1696
1398
|
const nodeId = await this.commissioningController.commissionNode(options);
|
|
1697
1399
|
this.log.info(`Commissioning successfully done with nodeId: ${nodeId}`);
|
|
1698
1400
|
this.log.info('ActiveSessionInformation:', this.commissioningController.getActiveSessionInformation());
|
|
1699
|
-
}
|
|
1401
|
+
}
|
|
1700
1402
|
if (hasParameter('unpairall')) {
|
|
1701
1403
|
this.log.info('***Commissioning controller unpairing all nodes...');
|
|
1702
1404
|
const nodeIds = this.commissioningController.getCommissionedNodes();
|
|
@@ -1707,8 +1409,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1707
1409
|
return;
|
|
1708
1410
|
}
|
|
1709
1411
|
if (hasParameter('discover')) {
|
|
1710
|
-
// const discover = await this.commissioningController.discoverCommissionableDevices({ productId: 0x8000, deviceType: 0xfff1 });
|
|
1711
|
-
// console.log(discover);
|
|
1712
1412
|
}
|
|
1713
1413
|
if (!this.commissioningController.isCommissioned()) {
|
|
1714
1414
|
this.log.info('***Commissioning controller is not commissioned: use matterbridge -controller -pairingcode [pairingcode] to commission a device');
|
|
@@ -1749,12 +1449,10 @@ export class Matterbridge extends EventEmitter {
|
|
|
1749
1449
|
},
|
|
1750
1450
|
});
|
|
1751
1451
|
node.logStructure();
|
|
1752
|
-
// Get the interaction client
|
|
1753
1452
|
this.log.info('Getting the interaction client');
|
|
1754
1453
|
const interactionClient = await node.getInteractionClient();
|
|
1755
1454
|
let cluster;
|
|
1756
1455
|
let attributes;
|
|
1757
|
-
// Log BasicInformationCluster
|
|
1758
1456
|
cluster = BasicInformationCluster;
|
|
1759
1457
|
attributes = await interactionClient.getMultipleAttributes({
|
|
1760
1458
|
attributes: [{ clusterId: cluster.id }],
|
|
@@ -1764,7 +1462,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1764
1462
|
attributes.forEach((attribute) => {
|
|
1765
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}`);
|
|
1766
1464
|
});
|
|
1767
|
-
// Log PowerSourceCluster
|
|
1768
1465
|
cluster = PowerSourceCluster;
|
|
1769
1466
|
attributes = await interactionClient.getMultipleAttributes({
|
|
1770
1467
|
attributes: [{ clusterId: cluster.id }],
|
|
@@ -1774,7 +1471,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1774
1471
|
attributes.forEach((attribute) => {
|
|
1775
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}`);
|
|
1776
1473
|
});
|
|
1777
|
-
// Log ThreadNetworkDiagnostics
|
|
1778
1474
|
cluster = ThreadNetworkDiagnosticsCluster;
|
|
1779
1475
|
attributes = await interactionClient.getMultipleAttributes({
|
|
1780
1476
|
attributes: [{ clusterId: cluster.id }],
|
|
@@ -1784,7 +1480,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1784
1480
|
attributes.forEach((attribute) => {
|
|
1785
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}`);
|
|
1786
1482
|
});
|
|
1787
|
-
// Log SwitchCluster
|
|
1788
1483
|
cluster = SwitchCluster;
|
|
1789
1484
|
attributes = await interactionClient.getMultipleAttributes({
|
|
1790
1485
|
attributes: [{ clusterId: cluster.id }],
|
|
@@ -1805,15 +1500,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1805
1500
|
this.log.info('Subscribed to all attributes and events');
|
|
1806
1501
|
}
|
|
1807
1502
|
}
|
|
1808
|
-
/** ***********************************************************************************************************************************/
|
|
1809
|
-
/** Matter.js methods */
|
|
1810
|
-
/** ***********************************************************************************************************************************/
|
|
1811
|
-
/**
|
|
1812
|
-
* Starts the matter storage process based on the specified storage type and name.
|
|
1813
|
-
* @param {string} storageType - The type of storage to start (e.g., 'disk', 'json').
|
|
1814
|
-
* @param {string} storageName - The name of the storage file.
|
|
1815
|
-
* @returns {Promise<void>} - A promise that resolves when the storage process is started.
|
|
1816
|
-
*/
|
|
1817
1503
|
async startMatterStorage(storageType, storageName) {
|
|
1818
1504
|
this.log.debug(`Starting matter ${storageType} storage ${CYAN}${storageName}${db}`);
|
|
1819
1505
|
if (storageType === 'disk') {
|
|
@@ -1862,166 +1548,136 @@ export class Matterbridge extends EventEmitter {
|
|
|
1862
1548
|
await this.matterbridgeContext.set('passcode', this.passcode);
|
|
1863
1549
|
await this.matterbridgeContext.set('discriminator', this.discriminator);
|
|
1864
1550
|
}
|
|
1865
|
-
/**
|
|
1866
|
-
* Convert the old API matter storage to the new API format.
|
|
1867
|
-
* @param {StorageContext} context - The context of Matterbridge or of the plugin.
|
|
1868
|
-
* @param {string} pluginName - The name of the plugin or Matterbridge.
|
|
1869
|
-
* @returns {Promise<void>} - A promise that resolves when the storage process is started.
|
|
1870
|
-
*/
|
|
1871
1551
|
async convertStorage(context, pluginName) {
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
else {
|
|
1880
|
-
this.log.notice(`Converting matter node storage to Matterbridge edge for ${plg}${pluginName}${nt}...`);
|
|
1881
|
-
}
|
|
1882
|
-
// Read FabricManager from the old storage and get FabricManager.fabrics and FabricManager.nextFabricIndex
|
|
1883
|
-
const fabricManagerContext = context.createContext('FabricManager');
|
|
1884
|
-
const fabrics = (await fabricManagerContext.get('fabrics', []));
|
|
1885
|
-
const nextFabricIndex = await fabricManagerContext.get('nextFabricIndex', 1);
|
|
1886
|
-
// Read EventHandler from the old storage
|
|
1887
|
-
const eventHandlerContext = context.createContext('EventHandler');
|
|
1888
|
-
// Read SessionManager from the old storage
|
|
1889
|
-
const sessionManagerContext = context.createContext('SessionManager');
|
|
1890
|
-
// Read EndpointStructure from the old storage
|
|
1891
|
-
const endpointStructureContext = context.createContext('EndpointStructure');
|
|
1892
|
-
// Read generalCommissioning from the old storage
|
|
1893
|
-
const generalCommissioningContext = context.createContext('Cluster-0-48');
|
|
1894
|
-
// Read basicInformation from the old storage
|
|
1895
|
-
const basicInformationContext = context.createContext('Cluster-0-40');
|
|
1896
|
-
const fabricInfo = {};
|
|
1897
|
-
const fabricInfoArray = [];
|
|
1898
|
-
const nocArray = [];
|
|
1899
|
-
const trcArray = [];
|
|
1900
|
-
const aclArray = [];
|
|
1901
|
-
this.log.info(`Found ${CYAN}${fabrics.length}${nf} fabrics (nextFabricIndex ${CYAN}${nextFabricIndex}${nf}) for ${plg}${pluginName}${nf}:`);
|
|
1902
|
-
for (const fabric of fabrics) {
|
|
1903
|
-
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}`);
|
|
1904
|
-
fabricInfo[fabric.fabricIndex] = {
|
|
1905
|
-
fabricIndex: fabric.fabricIndex,
|
|
1906
|
-
fabricId: fabric.fabricId,
|
|
1907
|
-
nodeId: fabric.nodeId,
|
|
1908
|
-
rootNodeId: fabric.rootNodeId,
|
|
1909
|
-
rootVendorId: fabric.rootVendorId,
|
|
1910
|
-
label: fabric.label,
|
|
1911
|
-
};
|
|
1912
|
-
fabricInfoArray.push({
|
|
1913
|
-
fabricIndex: fabric.fabricIndex,
|
|
1914
|
-
fabricId: fabric.fabricId,
|
|
1915
|
-
nodeId: fabric.nodeId,
|
|
1916
|
-
vendorId: fabric.rootVendorId,
|
|
1917
|
-
rootPublicKey: fabric.rootPublicKey,
|
|
1918
|
-
label: fabric.label,
|
|
1919
|
-
});
|
|
1920
|
-
nocArray.push({ noc: fabric.operationalCert, icac: null, fabricIndex: fabric.fabricIndex });
|
|
1921
|
-
// eslint-disable-next-line no-useless-escape
|
|
1922
|
-
trcArray.push('{\"__object__\":\"Uint8Array\",\"__value__\":\"' + Buffer.from(fabric.rootCert).toString('hex') + '\"}');
|
|
1923
|
-
// eslint-disable-next-line no-useless-escape
|
|
1924
|
-
aclArray.push({ fabricIndex: fabric.fabricIndex, privilege: 5, authMode: 2, subjects: ['{\"__object__\":\"BigInt\",\"__value__\":\"' + fabric.rootNodeId.toString().replace('n', '') + '\"}'], targets: null });
|
|
1925
|
-
// this.log.debug(`- fabricinfo ${fabric.fabricIndex}`, fabricInfo[fabric.fabricIndex]);
|
|
1926
|
-
}
|
|
1927
|
-
await nodeStorage.createContext('fabrics').set('fabrics', fabrics);
|
|
1928
|
-
await nodeStorage.createContext('fabrics').set('nextFabricIndex', nextFabricIndex);
|
|
1929
|
-
await nodeStorage.createContext('sessions').set('resumptionRecords', await sessionManagerContext.get('resumptionRecords', []));
|
|
1930
|
-
await nodeStorage.createContext('events').set('lastEventNumber', await eventHandlerContext.get('lastEventNumber', 1));
|
|
1931
|
-
await nodeStorage.createContext('root').set('__number__', 0);
|
|
1932
|
-
await nodeStorage.createContext('root').createContext('parts').createContext('Matterbridge').set('__number__', 1);
|
|
1933
|
-
await nodeStorage.createContext('root').createContext('commissioning').set('enabled', true);
|
|
1934
|
-
await nodeStorage.createContext('root').createContext('commissioning').set('commissioned', true);
|
|
1935
|
-
await nodeStorage.createContext('root').createContext('commissioning').set('fabrics', fabricInfo);
|
|
1936
|
-
await nodeStorage.createContext('root').createContext('operationalCredentials').set('commissionedFabrics', fabricInfoArray.length);
|
|
1937
|
-
await nodeStorage.createContext('root').createContext('operationalCredentials').set('fabrics', fabricInfoArray);
|
|
1938
|
-
// operationalCredentials.nocs ==>> [{noc: fabric.operationalCert, icac: null, fabricIndex: fabric.fabricIndex }]
|
|
1939
|
-
await nodeStorage.createContext('root').createContext('operationalCredentials').set('nocs', nocArray);
|
|
1940
|
-
// operationalCredentials.trustedRootCertificates ==>> ["{\"__object__\":\"Uint8Array\",\"__value__\":\"" + fabric.rootCert + "\"}"]
|
|
1941
|
-
await nodeStorage.createContext('root').createContext('operationalCredentials').set('trustedRootCertificates', trcArray);
|
|
1942
|
-
// ACL updated, updating ACL manager { fabricIndex: 3, privilege: 5, authMode: 2, subjects: [ 18446744060825763897 ], targets: null }
|
|
1943
|
-
// From fabric.rootNodeId
|
|
1944
|
-
// [{"fabricIndex":3,"privilege":5,"authMode":2,"subjects":["{\"__object__\":\"BigInt\",\"__value__\":\"18446744060825763897\"}"],"targets":null}]
|
|
1945
|
-
await nodeStorage.createContext('root').createContext('accessControl').set('acl', aclArray);
|
|
1946
|
-
await nodeStorage
|
|
1947
|
-
.createContext('root')
|
|
1948
|
-
.createContext('generalCommissioning')
|
|
1949
|
-
.set('breadcrumb', await generalCommissioningContext.get('breadcrumb', BigInt(0)));
|
|
1950
|
-
await nodeStorage
|
|
1951
|
-
.createContext('root')
|
|
1952
|
-
.createContext('basicInformation')
|
|
1953
|
-
.set('location', await basicInformationContext.get('location', 'XX'));
|
|
1954
|
-
await nodeStorage.createContext('root').createContext('network').set('ble', false);
|
|
1955
|
-
await nodeStorage.createContext('root').createContext('network').set('operationalPort', 5540);
|
|
1956
|
-
await nodeStorage.createContext('root').createContext('productDescription').set('productId', 0x8000);
|
|
1957
|
-
await nodeStorage.createContext('root').createContext('productDescription').set('vendorId', 0xfff1);
|
|
1958
|
-
/*
|
|
1959
|
-
"Matterbridge.EndpointStructure": {
|
|
1960
|
-
"unique_d60ca095a002f160-index_0": 1,
|
|
1961
|
-
"unique_d60ca095a002f160-index_0-custom_Switch0": 2,
|
|
1962
|
-
"unique_d60ca095a002f160-index_0-custom_Outlet0": 3,
|
|
1963
|
-
"unique_d60ca095a002f160-index_0-custom_Light0": 4,
|
|
1964
|
-
"unique_d60ca095a002f160-index_0-unique_7ddb4752b982ee108d5928e934f0fcfa": 2,
|
|
1965
|
-
"unique_d60ca095a002f160-index_0-unique_7ddb4752b982ee108d5928e934f0fcfa-custom_PowerSource": 3,
|
|
1966
|
-
"unique_d60ca095a002f160-index_0-unique_7ddb4752b982ee108d5928e934f0fcfa-custom_light:0": 4,
|
|
1967
|
-
"unique_d60ca095a002f160-index_0-unique_7ddb4752b982ee108d5928e934f0fcfa-custom_light:1": 5,
|
|
1968
|
-
"unique_d60ca095a002f160-index_0-unique_7ddb4752b982ee108d5928e934f0fcfa-custom_light:2": 6,
|
|
1969
|
-
"unique_d60ca095a002f160-index_0-unique_7ddb4752b982ee108d5928e934f0fcfa-custom_light:3": 7,
|
|
1970
|
-
"unique_d60ca095a002f160-index_0-unique_7ddb4752b982ee108d5928e934f0fcfa-custom_meter:0": 8,
|
|
1971
|
-
"unique_d60ca095a002f160-index_0-unique_7ddb4752b982ee108d5928e934f0fcfa-custom_meter:1": 9,
|
|
1972
|
-
"unique_d60ca095a002f160-index_0-unique_7ddb4752b982ee108d5928e934f0fcfa-custom_meter:2": 10,
|
|
1973
|
-
"unique_d60ca095a002f160-index_0-unique_7ddb4752b982ee108d5928e934f0fcfa-custom_meter:3": 11,
|
|
1974
|
-
"nextEndpointId": 5
|
|
1975
|
-
},
|
|
1976
|
-
*/
|
|
1977
|
-
for (const key of await endpointStructureContext.keys()) {
|
|
1978
|
-
if (key === 'nextEndpointId') {
|
|
1979
|
-
await nodeStorage.createContext('root').set('__nextNumber__', await endpointStructureContext.get(key));
|
|
1980
|
-
continue;
|
|
1552
|
+
try {
|
|
1553
|
+
const storageService = Environment.default.get(StorageService);
|
|
1554
|
+
Environment.default.vars.set('path.root', path.join(this.matterbridgeDirectory, 'matterstorage' + (this.profile ? '.' + this.profile : '')));
|
|
1555
|
+
const nodeStorage = await storageService.open('Matterbridge');
|
|
1556
|
+
if ((await nodeStorage.createContext('root').createContext('generalDiagnostics').get('rebootCount', 0)) > 0) {
|
|
1557
|
+
this.log.info(`Matter node storage already converted to Matterbridge edge for ${plg}${pluginName}${nf}`);
|
|
1558
|
+
return;
|
|
1981
1559
|
}
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
1560
|
+
else {
|
|
1561
|
+
this.log.notice(`Converting matter node storage to Matterbridge edge for ${plg}${pluginName}${nt}...`);
|
|
1562
|
+
}
|
|
1563
|
+
const fabricManagerContext = context.createContext('FabricManager');
|
|
1564
|
+
const fabrics = (await fabricManagerContext.get('fabrics', []));
|
|
1565
|
+
const nextFabricIndex = await fabricManagerContext.get('nextFabricIndex', 1);
|
|
1566
|
+
const eventHandlerContext = context.createContext('EventHandler');
|
|
1567
|
+
const sessionManagerContext = context.createContext('SessionManager');
|
|
1568
|
+
const endpointStructureContext = context.createContext('EndpointStructure');
|
|
1569
|
+
const generalCommissioningContext = context.createContext('Cluster-0-48');
|
|
1570
|
+
const basicInformationContext = context.createContext('Cluster-0-40');
|
|
1571
|
+
const fabricInfo = {};
|
|
1572
|
+
const fabricInfoArray = [];
|
|
1573
|
+
const nocArray = [];
|
|
1574
|
+
const trcArray = [];
|
|
1575
|
+
const aclArray = [];
|
|
1576
|
+
this.log.info(`Found ${CYAN}${fabrics.length}${nf} fabrics (nextFabricIndex ${CYAN}${nextFabricIndex}${nf}) for ${plg}${pluginName}${nf}:`);
|
|
1577
|
+
for (const fabric of fabrics) {
|
|
1578
|
+
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}`);
|
|
1579
|
+
fabricInfo[fabric.fabricIndex] = {
|
|
1580
|
+
fabricIndex: fabric.fabricIndex,
|
|
1581
|
+
fabricId: fabric.fabricId,
|
|
1582
|
+
nodeId: fabric.nodeId,
|
|
1583
|
+
rootNodeId: fabric.rootNodeId,
|
|
1584
|
+
rootVendorId: fabric.rootVendorId,
|
|
1585
|
+
label: fabric.label,
|
|
1586
|
+
};
|
|
1587
|
+
fabricInfoArray.push({
|
|
1588
|
+
fabricIndex: fabric.fabricIndex,
|
|
1589
|
+
fabricId: fabric.fabricId,
|
|
1590
|
+
nodeId: fabric.nodeId,
|
|
1591
|
+
vendorId: fabric.rootVendorId,
|
|
1592
|
+
rootPublicKey: fabric.rootPublicKey,
|
|
1593
|
+
label: fabric.label,
|
|
1594
|
+
});
|
|
1595
|
+
nocArray.push({ noc: fabric.operationalCert, icac: null, fabricIndex: fabric.fabricIndex });
|
|
1596
|
+
trcArray.push('{\"__object__\":\"Uint8Array\",\"__value__\":\"' + Buffer.from(fabric.rootCert).toString('hex') + '\"}');
|
|
1597
|
+
this.log.info(`- updating ACL for fabricIndex ${fabric.fabricIndex}:`, fabric.scopedClusterData);
|
|
1598
|
+
const acl = fabric.scopedClusterData.get(0x1f)?.get('acl');
|
|
1599
|
+
if (acl && acl.value.length > 0) {
|
|
1600
|
+
aclArray.push(acl.value[0]);
|
|
1601
|
+
this.log.info(`- ACL updated to ${debugStringify(acl.value)}${nf} for fabricIndex ${CYAN}${fabric.fabricIndex}${nf}`);
|
|
1602
|
+
}
|
|
1603
|
+
else {
|
|
1604
|
+
const defaultAcl = { fabricIndex: fabric.fabricIndex, privilege: 5, authMode: 2, subjects: [fabric.rootNodeId], targets: null };
|
|
1605
|
+
aclArray.push(defaultAcl);
|
|
1606
|
+
this.log.info(`- ACL updated to default ${debugStringify(defaultAcl)}${nf} for fabricIndex ${CYAN}${fabric.fabricIndex}${nf}`);
|
|
1607
|
+
}
|
|
1608
|
+
}
|
|
1609
|
+
await nodeStorage.createContext('fabrics').set('fabrics', fabrics);
|
|
1610
|
+
await nodeStorage.createContext('fabrics').set('nextFabricIndex', nextFabricIndex);
|
|
1611
|
+
await nodeStorage.createContext('sessions').set('resumptionRecords', await sessionManagerContext.get('resumptionRecords', []));
|
|
1612
|
+
await nodeStorage.createContext('events').set('lastEventNumber', await eventHandlerContext.get('lastEventNumber', 1));
|
|
1613
|
+
await nodeStorage.createContext('root').set('__number__', 0);
|
|
1614
|
+
await nodeStorage.createContext('root').createContext('parts').createContext('Matterbridge').set('__number__', 1);
|
|
1615
|
+
await nodeStorage.createContext('root').createContext('commissioning').set('enabled', true);
|
|
1616
|
+
await nodeStorage.createContext('root').createContext('commissioning').set('commissioned', true);
|
|
1617
|
+
await nodeStorage.createContext('root').createContext('commissioning').set('fabrics', fabricInfo);
|
|
1618
|
+
await nodeStorage.createContext('root').createContext('operationalCredentials').set('commissionedFabrics', fabricInfoArray.length);
|
|
1619
|
+
await nodeStorage.createContext('root').createContext('operationalCredentials').set('fabrics', fabricInfoArray);
|
|
1620
|
+
await nodeStorage.createContext('root').createContext('operationalCredentials').set('nocs', nocArray);
|
|
1621
|
+
await nodeStorage.createContext('root').createContext('operationalCredentials').set('trustedRootCertificates', trcArray);
|
|
1622
|
+
await nodeStorage.createContext('root').createContext('accessControl').set('acl', aclArray);
|
|
1623
|
+
await nodeStorage
|
|
1624
|
+
.createContext('root')
|
|
1625
|
+
.createContext('generalCommissioning')
|
|
1626
|
+
.set('breadcrumb', await generalCommissioningContext.get('breadcrumb', BigInt(0)));
|
|
1627
|
+
await nodeStorage
|
|
1628
|
+
.createContext('root')
|
|
1629
|
+
.createContext('basicInformation')
|
|
1630
|
+
.set('location', await basicInformationContext.get('location', 'XX'));
|
|
1631
|
+
await nodeStorage.createContext('root').createContext('network').set('ble', false);
|
|
1632
|
+
await nodeStorage.createContext('root').createContext('network').set('operationalPort', 5540);
|
|
1633
|
+
await nodeStorage.createContext('root').createContext('productDescription').set('productId', 0x8000);
|
|
1634
|
+
await nodeStorage.createContext('root').createContext('productDescription').set('vendorId', 0xfff1);
|
|
1635
|
+
for (const key of await endpointStructureContext.keys()) {
|
|
1636
|
+
if (key === 'nextEndpointId') {
|
|
1637
|
+
await nodeStorage.createContext('root').set('__nextNumber__', await endpointStructureContext.get(key));
|
|
1638
|
+
continue;
|
|
1639
|
+
}
|
|
1640
|
+
const parts = key.split('-');
|
|
1641
|
+
const number = await endpointStructureContext.get(key);
|
|
1642
|
+
if (parts.length === 2) {
|
|
1643
|
+
this.log.debug(`Converting Matterbridge.EndpointStructure:${key}:${number} to root.parts.Matterbridge.__number__:${number}`);
|
|
1644
|
+
await nodeStorage.createContext('root').createContext('parts').createContext('Matterbridge').set('__number__', number);
|
|
1645
|
+
}
|
|
1646
|
+
else if (parts.length === 3 && parts[2].startsWith('custom_')) {
|
|
1647
|
+
this.log.debug(`Converting Matterbridge.EndpointStructure:${key}:${number} to root.parts.Matterbridge.parts.${parts[2].replace('custom_', '')}.__number__:${number}`);
|
|
1648
|
+
await nodeStorage.createContext('root').createContext('parts').createContext('Matterbridge').createContext('parts').createContext(parts[2].replace('custom_', '')).set('__number__', number);
|
|
1649
|
+
}
|
|
1650
|
+
else if (parts.length === 3 && parts[2].startsWith('unique_')) {
|
|
1651
|
+
const device = this.devices.get(parts[2].replace('unique_', ''));
|
|
1652
|
+
if (device && device.deviceName && device.maybeNumber) {
|
|
1653
|
+
this.log.debug(`Converting Matterbridge.EndpointStructure:${key}:${number} to root.parts.Matterbridge.parts.${device.deviceName.replace(/[ .]/g, '')}.__number__:${device.maybeNumber}`);
|
|
1654
|
+
await nodeStorage.createContext('root').createContext('parts').createContext('Matterbridge').createContext('parts').createContext(device.deviceName.replace(/[ .]/g, '')).set('__number__', device.maybeNumber);
|
|
1655
|
+
}
|
|
1656
|
+
}
|
|
1657
|
+
}
|
|
1658
|
+
await nodeStorage.createContext('persist').set('converted', true);
|
|
1659
|
+
await nodeStorage.createContext('persist').set('deviceName', await context.get('deviceName'));
|
|
1660
|
+
await nodeStorage.createContext('persist').set('deviceType', await context.get('deviceType'));
|
|
1661
|
+
await nodeStorage.createContext('persist').set('vendorId', await context.get('vendorId'));
|
|
1662
|
+
await nodeStorage.createContext('persist').set('vendorName', await context.get('vendorName'));
|
|
1663
|
+
await nodeStorage.createContext('persist').set('productId', await context.get('productId'));
|
|
1664
|
+
await nodeStorage.createContext('persist').set('productName', await context.get('productName'));
|
|
1665
|
+
await nodeStorage.createContext('persist').set('nodeLabel', await context.get('nodeLabel'));
|
|
1666
|
+
await nodeStorage.createContext('persist').set('productLabel', await context.get('productLabel'));
|
|
1667
|
+
await nodeStorage.createContext('persist').set('serialNumber', 'SN' + (await context.get('serialNumber')));
|
|
1668
|
+
await nodeStorage.createContext('persist').set('uniqueId', await context.get('uniqueId'));
|
|
1669
|
+
await nodeStorage.createContext('persist').set('softwareVersion', await context.get('softwareVersion'));
|
|
1670
|
+
await nodeStorage.createContext('persist').set('softwareVersionString', await context.get('softwareVersionString'));
|
|
1671
|
+
await nodeStorage.createContext('persist').set('hardwareVersion', await context.get('hardwareVersion'));
|
|
1672
|
+
await nodeStorage.createContext('persist').set('hardwareVersionString', await context.get('hardwareVersionString'));
|
|
1673
|
+
await context.set('converted', true);
|
|
1674
|
+
this.log.notice(`Matter storage converted to Matterbridge edge for ${plg}${pluginName}${nt}`);
|
|
1675
|
+
this.log.notice(`If you want to try out matterbridge edge (beta) add -edge to the command line.`);
|
|
1676
|
+
}
|
|
1677
|
+
catch (error) {
|
|
1678
|
+
this.log.error(`convertStorage error converting matter storage to Matterbridge edge for ${plg}${pluginName}${er}:`, error);
|
|
1679
|
+
}
|
|
2018
1680
|
}
|
|
2019
|
-
/**
|
|
2020
|
-
* Makes a backup copy of the specified matter JSON storage file.
|
|
2021
|
-
*
|
|
2022
|
-
* @param storageName - The name of the JSON storage file to be backed up.
|
|
2023
|
-
* @param backupName - The name of the backup file to be created.
|
|
2024
|
-
*/
|
|
2025
1681
|
async backupMatterStorage(storageName, backupName) {
|
|
2026
1682
|
try {
|
|
2027
1683
|
this.log.debug(`Making backup copy of ${storageName}`);
|
|
@@ -2042,12 +1698,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2042
1698
|
}
|
|
2043
1699
|
}
|
|
2044
1700
|
}
|
|
2045
|
-
/**
|
|
2046
|
-
* Restore the specified matter JSON storage file.
|
|
2047
|
-
*
|
|
2048
|
-
* @param backupName - The name of the backup file to restore from.
|
|
2049
|
-
* @param storageName - The name of the JSON storage file to restored.
|
|
2050
|
-
*/
|
|
2051
1701
|
async restoreMatterStorage(backupName, storageName) {
|
|
2052
1702
|
try {
|
|
2053
1703
|
this.log.notice(`Restoring the backup copy of ${storageName}`);
|
|
@@ -2068,10 +1718,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2068
1718
|
}
|
|
2069
1719
|
}
|
|
2070
1720
|
}
|
|
2071
|
-
/**
|
|
2072
|
-
* Stops the matter storage.
|
|
2073
|
-
* @returns {Promise<void>} A promise that resolves when the storage is stopped.
|
|
2074
|
-
*/
|
|
2075
1721
|
async stopMatterStorage() {
|
|
2076
1722
|
this.log.debug('Stopping storage');
|
|
2077
1723
|
await this.storageManager?.close();
|
|
@@ -2080,14 +1726,8 @@ export class Matterbridge extends EventEmitter {
|
|
|
2080
1726
|
this.matterbridgeContext = undefined;
|
|
2081
1727
|
this.mattercontrollerContext = undefined;
|
|
2082
1728
|
}
|
|
2083
|
-
/**
|
|
2084
|
-
* Creates a Matter server using the provided storage manager and the provided mdnsInterface.
|
|
2085
|
-
* @param storageManager The storage manager to be used by the Matter server.
|
|
2086
|
-
*
|
|
2087
|
-
*/
|
|
2088
1729
|
createMatterServer(storageManager) {
|
|
2089
1730
|
this.log.debug('Creating matter server');
|
|
2090
|
-
// Validate mdnsInterface
|
|
2091
1731
|
if (this.mdnsInterface) {
|
|
2092
1732
|
const networkInterfaces = os.networkInterfaces();
|
|
2093
1733
|
const availableInterfaces = Object.keys(networkInterfaces);
|
|
@@ -2103,10 +1743,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2103
1743
|
this.log.debug(`Created matter server with mdnsInterface: ${this.mdnsInterface ?? 'all available interfaces'}`);
|
|
2104
1744
|
return matterServer;
|
|
2105
1745
|
}
|
|
2106
|
-
/**
|
|
2107
|
-
* Starts the Matter server.
|
|
2108
|
-
* If the Matter server is not initialized, it logs an error and performs cleanup.
|
|
2109
|
-
*/
|
|
2110
1746
|
async startMatterServer() {
|
|
2111
1747
|
if (!this.matterServer) {
|
|
2112
1748
|
this.log.error('No matter server initialized');
|
|
@@ -2116,11 +1752,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
2116
1752
|
this.log.debug('Starting matter server...');
|
|
2117
1753
|
await this.matterServer.start();
|
|
2118
1754
|
this.log.debug('Started matter server');
|
|
2119
|
-
// this.commissioningServer?.getRootEndpoint() && logEndpoint(this.commissioningServer?.getRootEndpoint());
|
|
2120
1755
|
}
|
|
2121
|
-
/**
|
|
2122
|
-
* Stops the Matter server, commissioningServer and commissioningController.
|
|
2123
|
-
*/
|
|
2124
1756
|
async stopMatterServer() {
|
|
2125
1757
|
this.log.debug('Stopping matter commissioningServer');
|
|
2126
1758
|
await this.commissioningServer?.close();
|
|
@@ -2134,35 +1766,23 @@ export class Matterbridge extends EventEmitter {
|
|
|
2134
1766
|
this.matterAggregator = undefined;
|
|
2135
1767
|
this.matterServer = undefined;
|
|
2136
1768
|
}
|
|
2137
|
-
/**
|
|
2138
|
-
* Creates a Matter Aggregator.
|
|
2139
|
-
* @param {StorageContext} context - The storage context.
|
|
2140
|
-
* @returns {Aggregator} - The created Matter Aggregator.
|
|
2141
|
-
*/
|
|
2142
1769
|
async createMatterAggregator(context, pluginName) {
|
|
2143
1770
|
this.log.debug(`Creating matter aggregator for ${plg}${pluginName}${db}`);
|
|
2144
1771
|
const matterAggregator = new Aggregator();
|
|
2145
1772
|
return matterAggregator;
|
|
2146
1773
|
}
|
|
2147
|
-
/**
|
|
2148
|
-
* Creates a matter commissioning server.
|
|
2149
|
-
*
|
|
2150
|
-
* @param {StorageContext} context - The storage context.
|
|
2151
|
-
* @param {string} pluginName - The name of the commissioning server.
|
|
2152
|
-
* @returns {CommissioningServer} The created commissioning server.
|
|
2153
|
-
*/
|
|
2154
1774
|
async createCommisioningServer(context, pluginName) {
|
|
2155
1775
|
this.log.debug(`Creating matter commissioning server for plugin ${plg}${pluginName}${db}`);
|
|
2156
1776
|
const deviceName = await context.get('deviceName');
|
|
2157
1777
|
const deviceType = await context.get('deviceType');
|
|
2158
1778
|
const vendorId = await context.get('vendorId');
|
|
2159
|
-
const vendorName = await context.get('vendorName');
|
|
1779
|
+
const vendorName = await context.get('vendorName');
|
|
2160
1780
|
const productId = await context.get('productId');
|
|
2161
|
-
const productName = await context.get('productName');
|
|
1781
|
+
const productName = await context.get('productName');
|
|
2162
1782
|
const serialNumber = await context.get('serialNumber');
|
|
2163
1783
|
const uniqueId = await context.get('uniqueId');
|
|
2164
1784
|
const softwareVersion = await context.get('softwareVersion', 1);
|
|
2165
|
-
const softwareVersionString = await context.get('softwareVersionString', '1.0.0');
|
|
1785
|
+
const softwareVersionString = await context.get('softwareVersionString', '1.0.0');
|
|
2166
1786
|
const hardwareVersion = await context.get('hardwareVersion', 1);
|
|
2167
1787
|
const hardwareVersionString = await context.get('hardwareVersionString', '1.0.0');
|
|
2168
1788
|
this.log.debug(`Creating matter commissioning server for plugin ${plg}${pluginName}${db} with deviceName '${deviceName}' deviceType ${deviceType}(0x${deviceType.toString(16).padStart(4, '0')})`);
|
|
@@ -2170,7 +1790,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2170
1790
|
this.log.debug(`Creating matter commissioning server for plugin ${plg}${pluginName}${db} with softwareVersion ${softwareVersion} softwareVersionString ${softwareVersionString}`);
|
|
2171
1791
|
this.log.debug(`Creating matter commissioning server for plugin ${plg}${pluginName}${db} with hardwareVersion ${hardwareVersion} hardwareVersionString ${hardwareVersionString}`);
|
|
2172
1792
|
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} `);
|
|
2173
|
-
// Validate ipv4address
|
|
2174
1793
|
if (this.ipv4address) {
|
|
2175
1794
|
const networkInterfaces = os.networkInterfaces();
|
|
2176
1795
|
const availableAddresses = Object.values(networkInterfaces)
|
|
@@ -2185,7 +1804,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2185
1804
|
this.log.info(`Using ipv4address '${this.ipv4address}' for the Matter commissioning server.`);
|
|
2186
1805
|
}
|
|
2187
1806
|
}
|
|
2188
|
-
// Validate ipv6address
|
|
2189
1807
|
if (this.ipv6address) {
|
|
2190
1808
|
const networkInterfaces = os.networkInterfaces();
|
|
2191
1809
|
const availableAddresses = Object.values(networkInterfaces)
|
|
@@ -2216,7 +1834,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
2216
1834
|
nodeLabel: productName,
|
|
2217
1835
|
productLabel: productName,
|
|
2218
1836
|
softwareVersion,
|
|
2219
|
-
softwareVersionString,
|
|
1837
|
+
softwareVersionString,
|
|
2220
1838
|
hardwareVersion,
|
|
2221
1839
|
hardwareVersionString,
|
|
2222
1840
|
uniqueId,
|
|
@@ -2312,24 +1930,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2312
1930
|
commissioningServer.addCommandHandler('testEventTrigger', async ({ request: { enableKey, eventTrigger } }) => this.log.info(`testEventTrigger called on GeneralDiagnostic cluster: ${enableKey} ${eventTrigger}`));
|
|
2313
1931
|
return commissioningServer;
|
|
2314
1932
|
}
|
|
2315
|
-
/**
|
|
2316
|
-
* Creates a commissioning server storage context.
|
|
2317
|
-
*
|
|
2318
|
-
* @param pluginName - The name of the plugin.
|
|
2319
|
-
* @param deviceName - The name of the device.
|
|
2320
|
-
* @param deviceType - The type of the device.
|
|
2321
|
-
* @param vendorId - The vendor ID.
|
|
2322
|
-
* @param vendorName - The vendor name.
|
|
2323
|
-
* @param productId - The product ID.
|
|
2324
|
-
* @param productName - The product name.
|
|
2325
|
-
* @param serialNumber - The serial number of the device (optional).
|
|
2326
|
-
* @param uniqueId - The unique ID of the device (optional).
|
|
2327
|
-
* @param softwareVersion - The software version of the device (optional).
|
|
2328
|
-
* @param softwareVersionString - The software version string of the device (optional).
|
|
2329
|
-
* @param hardwareVersion - The hardware version of the device (optional).
|
|
2330
|
-
* @param hardwareVersionString - The hardware version string of the device (optional).
|
|
2331
|
-
* @returns The storage context for the commissioning server.
|
|
2332
|
-
*/
|
|
2333
1933
|
async createCommissioningServerContext(pluginName, deviceName, deviceType, vendorId, vendorName, productId, productName) {
|
|
2334
1934
|
if (!this.storageManager)
|
|
2335
1935
|
throw new Error('No storage manager initialized');
|
|
@@ -2357,13 +1957,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2357
1957
|
this.log.debug(`- hardwareVersion: ${await storageContext.get('hardwareVersion')} hardwareVersionString: ${await storageContext.get('hardwareVersionString')}`);
|
|
2358
1958
|
return storageContext;
|
|
2359
1959
|
}
|
|
2360
|
-
/**
|
|
2361
|
-
* Imports the commissioning server context for a specific plugin and device.
|
|
2362
|
-
* @param pluginName - The name of the plugin.
|
|
2363
|
-
* @param device - The MatterbridgeDevice object representing the device.
|
|
2364
|
-
* @returns The commissioning server context.
|
|
2365
|
-
* @throws Error if the BasicInformationCluster is not found.
|
|
2366
|
-
*/
|
|
2367
1960
|
async importCommissioningServerContext(pluginName, device) {
|
|
2368
1961
|
this.log.debug(`Importing matter commissioning server storage context from device for ${plg}${pluginName}${db}`);
|
|
2369
1962
|
const basic = device.getClusterServer(BasicInformationCluster);
|
|
@@ -2398,14 +1991,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2398
1991
|
this.log.debug(`- hardwareVersion: ${await storageContext.get('hardwareVersion')} hardwareVersionString: ${await storageContext.get('hardwareVersionString')}`);
|
|
2399
1992
|
return storageContext;
|
|
2400
1993
|
}
|
|
2401
|
-
/**
|
|
2402
|
-
* Shows the commissioning server QR code for a given plugin.
|
|
2403
|
-
* @param {CommissioningServer} commissioningServer - The commissioning server instance.
|
|
2404
|
-
* @param {StorageContext} storageContext - The storage context instance.
|
|
2405
|
-
* @param {NodeStorage} nodeContext - The node storage instance.
|
|
2406
|
-
* @param {string} pluginName - The name of the plugin of Matterbridge in bridge mode.
|
|
2407
|
-
* @returns {Promise<void>} - A promise that resolves when the QR code is shown.
|
|
2408
|
-
*/
|
|
2409
1994
|
async showCommissioningQRCode(commissioningServer, storageContext, nodeContext, pluginName) {
|
|
2410
1995
|
if (!commissioningServer || !storageContext || !nodeContext || !pluginName) {
|
|
2411
1996
|
this.log.error(`showCommissioningQRCode error: commissioningServer: ${!commissioningServer} storageContext: ${!storageContext} nodeContext: ${!nodeContext} pluginName: ${pluginName}`);
|
|
@@ -2416,8 +2001,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
2416
2001
|
const { qrPairingCode, manualPairingCode } = commissioningServer.getPairingCode();
|
|
2417
2002
|
const QrCode = new QrCodeSchema();
|
|
2418
2003
|
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`);
|
|
2419
|
-
|
|
2420
|
-
if (this.log.logLevel === "debug" /* LogLevel.DEBUG */ || this.log.logLevel === "info" /* LogLevel.INFO */)
|
|
2004
|
+
if (this.log.logLevel === "debug" || this.log.logLevel === "info")
|
|
2421
2005
|
console.log(`${QrCode.encode(qrPairingCode)}\n`);
|
|
2422
2006
|
this.log.info(`${plg}${pluginName}${nf} \n\nqrPairingCode: ${qrPairingCode} \n\nManual pairing code: ${manualPairingCode}\n`);
|
|
2423
2007
|
if (pluginName === 'Matterbridge') {
|
|
@@ -2464,12 +2048,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2464
2048
|
}
|
|
2465
2049
|
this.wssSendRefreshRequired();
|
|
2466
2050
|
}
|
|
2467
|
-
/**
|
|
2468
|
-
* Sanitizes the fabric information by converting bigint properties to string cause res.json doesn't know bigint.
|
|
2469
|
-
*
|
|
2470
|
-
* @param fabricInfo - The array of exposed fabric information objects.
|
|
2471
|
-
* @returns An array of sanitized exposed fabric information objects.
|
|
2472
|
-
*/
|
|
2473
2051
|
sanitizeFabricInformations(fabricInfo) {
|
|
2474
2052
|
return fabricInfo.map((info) => {
|
|
2475
2053
|
return {
|
|
@@ -2483,12 +2061,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2483
2061
|
};
|
|
2484
2062
|
});
|
|
2485
2063
|
}
|
|
2486
|
-
/**
|
|
2487
|
-
* Sanitizes the session information by converting bigint properties to string.
|
|
2488
|
-
*
|
|
2489
|
-
* @param sessionInfo - The array of session information objects.
|
|
2490
|
-
* @returns An array of sanitized session information objects.
|
|
2491
|
-
*/
|
|
2492
2064
|
sanitizeSessionInformation(sessionInfo) {
|
|
2493
2065
|
return sessionInfo
|
|
2494
2066
|
.filter((session) => session.isPeerActive)
|
|
@@ -2516,12 +2088,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2516
2088
|
};
|
|
2517
2089
|
});
|
|
2518
2090
|
}
|
|
2519
|
-
/**
|
|
2520
|
-
* Sets the reachability of a commissioning server and trigger.
|
|
2521
|
-
*
|
|
2522
|
-
* @param {CommissioningServer} commissioningServer - The commissioning server to set the reachability for.
|
|
2523
|
-
* @param {boolean} reachable - The new reachability status.
|
|
2524
|
-
*/
|
|
2525
2091
|
setCommissioningServerReachability(commissioningServer, reachable) {
|
|
2526
2092
|
const basicInformationCluster = commissioningServer?.getRootClusterServer(BasicInformationCluster);
|
|
2527
2093
|
if (basicInformationCluster && basicInformationCluster.attributes.reachable !== undefined)
|
|
@@ -2529,11 +2095,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2529
2095
|
if (basicInformationCluster && basicInformationCluster.triggerReachableChangedEvent)
|
|
2530
2096
|
basicInformationCluster.triggerReachableChangedEvent({ reachableNewValue: reachable });
|
|
2531
2097
|
}
|
|
2532
|
-
/**
|
|
2533
|
-
* Sets the reachability of the specified matter aggregator and its bridged devices and trigger.
|
|
2534
|
-
* @param {Aggregator} matterAggregator - The matter aggregator to set the reachability for.
|
|
2535
|
-
* @param {boolean} reachable - A boolean indicating the reachability status to set.
|
|
2536
|
-
*/
|
|
2537
2098
|
setAggregatorReachability(matterAggregator, reachable) {
|
|
2538
2099
|
const basicInformationCluster = matterAggregator.getClusterServer(BasicInformationCluster);
|
|
2539
2100
|
if (basicInformationCluster && basicInformationCluster.attributes.reachable !== undefined)
|
|
@@ -2546,12 +2107,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2546
2107
|
device.getClusterServer(BridgedDeviceBasicInformationCluster)?.triggerReachableChangedEvent({ reachableNewValue: reachable });
|
|
2547
2108
|
});
|
|
2548
2109
|
}
|
|
2549
|
-
/**
|
|
2550
|
-
* Sets the reachability of a device and trigger.
|
|
2551
|
-
*
|
|
2552
|
-
* @param {MatterbridgeDevice} device - The device to set the reachability for.
|
|
2553
|
-
* @param {boolean} reachable - The new reachability status of the device.
|
|
2554
|
-
*/
|
|
2555
2110
|
setDeviceReachability(device, reachable) {
|
|
2556
2111
|
const basicInformationCluster = device.getClusterServer(BasicInformationCluster);
|
|
2557
2112
|
if (basicInformationCluster && basicInformationCluster.attributes.reachable !== undefined)
|
|
@@ -2600,10 +2155,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2600
2155
|
}
|
|
2601
2156
|
return vendorName;
|
|
2602
2157
|
};
|
|
2603
|
-
/**
|
|
2604
|
-
* Retrieves the base registered plugins sanitized for res.json().
|
|
2605
|
-
* @returns {BaseRegisteredPlugin[]} A promise that resolves to an array of BaseRegisteredPlugin objects.
|
|
2606
|
-
*/
|
|
2607
2158
|
async getBaseRegisteredPlugins() {
|
|
2608
2159
|
const baseRegisteredPlugins = [];
|
|
2609
2160
|
for (const plugin of this.plugins) {
|
|
@@ -2635,36 +2186,13 @@ export class Matterbridge extends EventEmitter {
|
|
|
2635
2186
|
}
|
|
2636
2187
|
return baseRegisteredPlugins;
|
|
2637
2188
|
}
|
|
2638
|
-
/**
|
|
2639
|
-
* Spawns a child process with the given command and arguments.
|
|
2640
|
-
* @param {string} command - The command to execute.
|
|
2641
|
-
* @param {string[]} args - The arguments to pass to the command (default: []).
|
|
2642
|
-
* @returns {Promise<boolean>} A promise that resolves when the child process exits successfully, or rejects if there is an error.
|
|
2643
|
-
*/
|
|
2644
2189
|
async spawnCommand(command, args = []) {
|
|
2645
|
-
/*
|
|
2646
|
-
npm > npm.cmd on windows
|
|
2647
|
-
cmd.exe ['dir'] on windows
|
|
2648
|
-
await this.spawnCommand('npm', ['install', '-g', 'matterbridge']);
|
|
2649
|
-
process.on('unhandledRejection', (reason, promise) => {
|
|
2650
|
-
this.log.error('Unhandled Rejection at:', promise, 'reason:', reason);
|
|
2651
|
-
});
|
|
2652
|
-
|
|
2653
|
-
spawn - [14:27:21.125] [Matterbridge:spawn]: changed 38 packages in 4s
|
|
2654
|
-
spawn - [14:27:21.125] [Matterbridge:spawn]: 10 packages are looking for funding run `npm fund` for details
|
|
2655
|
-
debug - [14:27:21.131] [Matterbridge]: Child process exited with code 0 and signal null
|
|
2656
|
-
debug - [14:27:21.131] [Matterbridge]: Child process stdio streams have closed with code 0
|
|
2657
|
-
*/
|
|
2658
2190
|
const cmdLine = command + ' ' + args.join(' ');
|
|
2659
2191
|
if (process.platform === 'win32' && command === 'npm') {
|
|
2660
|
-
// Must be spawn('cmd.exe', ['/c', 'npm -g install <package>']);
|
|
2661
2192
|
const argstring = 'npm ' + args.join(' ');
|
|
2662
2193
|
args.splice(0, args.length, '/c', argstring);
|
|
2663
2194
|
command = 'cmd.exe';
|
|
2664
2195
|
}
|
|
2665
|
-
// Decide when using sudo on linux
|
|
2666
|
-
// When you need sudo: Spawn stderr: npm error Error: EACCES: permission denied
|
|
2667
|
-
// When you don't need sudo: Failed to start child process "npm install -g matterbridge-eve-door": spawn sudo ENOENT
|
|
2668
2196
|
if (hasParameter('sudo') || (process.platform === 'linux' && command === 'npm' && !hasParameter('docker') && !hasParameter('nosudo'))) {
|
|
2669
2197
|
args.unshift(command);
|
|
2670
2198
|
command = 'sudo';
|
|
@@ -2722,102 +2250,55 @@ export class Matterbridge extends EventEmitter {
|
|
|
2722
2250
|
}
|
|
2723
2251
|
});
|
|
2724
2252
|
}
|
|
2725
|
-
/**
|
|
2726
|
-
* Sends a WebSocket message to all connected clients.
|
|
2727
|
-
*
|
|
2728
|
-
* @param {string} level - The logger level of the message: debug info notice warn error fatal...
|
|
2729
|
-
* @param {string} time - The time string of the message
|
|
2730
|
-
* @param {string} name - The logger name of the message
|
|
2731
|
-
* @param {string} message - The content of the message.
|
|
2732
|
-
*/
|
|
2733
2253
|
wssSendMessage(level, time, name, message) {
|
|
2734
2254
|
if (!level || !time || !name || !message)
|
|
2735
2255
|
return;
|
|
2736
|
-
// Remove ANSI escape codes from the message
|
|
2737
|
-
// eslint-disable-next-line no-control-regex
|
|
2738
2256
|
message = message.replace(/\x1B\[[0-9;]*[m|s|u|K]/g, '');
|
|
2739
|
-
// Remove leading asterisks from the message
|
|
2740
2257
|
message = message.replace(/^\*+/, '');
|
|
2741
|
-
// Replace all occurrences of \t and \n
|
|
2742
2258
|
message = message.replace(/[\t\n]/g, '');
|
|
2743
|
-
// Remove non-printable characters
|
|
2744
|
-
// eslint-disable-next-line no-control-regex
|
|
2745
2259
|
message = message.replace(/[\x00-\x1F\x7F]/g, '');
|
|
2746
|
-
// Replace all occurrences of \" with "
|
|
2747
2260
|
message = message.replace(/\\"/g, '"');
|
|
2748
|
-
// Define the maximum allowed length for continuous characters without a space
|
|
2749
2261
|
const maxContinuousLength = 100;
|
|
2750
2262
|
const keepStartLength = 20;
|
|
2751
2263
|
const keepEndLength = 20;
|
|
2752
|
-
// Split the message into words
|
|
2753
2264
|
message = message
|
|
2754
2265
|
.split(' ')
|
|
2755
2266
|
.map((word) => {
|
|
2756
|
-
// If the word length exceeds the max continuous length, insert spaces and truncate
|
|
2757
2267
|
if (word.length > maxContinuousLength) {
|
|
2758
2268
|
return word.slice(0, keepStartLength) + ' ... ' + word.slice(-keepEndLength);
|
|
2759
2269
|
}
|
|
2760
2270
|
return word;
|
|
2761
2271
|
})
|
|
2762
2272
|
.join(' ');
|
|
2763
|
-
// Send the message to all connected clients
|
|
2764
2273
|
this.webSocketServer?.clients.forEach((client) => {
|
|
2765
2274
|
if (client.readyState === WebSocket.OPEN) {
|
|
2766
2275
|
client.send(JSON.stringify({ id: WS_ID_LOG, src: 'Matterbridge', level, time, name, message }));
|
|
2767
2276
|
}
|
|
2768
2277
|
});
|
|
2769
2278
|
}
|
|
2770
|
-
/**
|
|
2771
|
-
* Sends a need to refresh WebSocket message to all connected clients.
|
|
2772
|
-
*
|
|
2773
|
-
*/
|
|
2774
2279
|
wssSendRefreshRequired() {
|
|
2775
2280
|
this.matterbridgeInformation.refreshRequired = true;
|
|
2776
|
-
// Send the message to all connected clients
|
|
2777
2281
|
this.webSocketServer?.clients.forEach((client) => {
|
|
2778
2282
|
if (client.readyState === WebSocket.OPEN) {
|
|
2779
2283
|
client.send(JSON.stringify({ id: WS_ID_REFRESH_NEEDED, src: 'Matterbridge', dst: 'Matterbridge', method: 'refresh_required', params: {} }));
|
|
2780
2284
|
}
|
|
2781
2285
|
});
|
|
2782
2286
|
}
|
|
2783
|
-
/**
|
|
2784
|
-
* Sends a need to restart WebSocket message to all connected clients.
|
|
2785
|
-
*
|
|
2786
|
-
*/
|
|
2787
2287
|
wssSendRestartRequired() {
|
|
2788
2288
|
this.matterbridgeInformation.restartRequired = true;
|
|
2789
|
-
// Send the message to all connected clients
|
|
2790
2289
|
this.webSocketServer?.clients.forEach((client) => {
|
|
2791
2290
|
if (client.readyState === WebSocket.OPEN) {
|
|
2792
2291
|
client.send(JSON.stringify({ id: WS_ID_RESTART_NEEDED, src: 'Matterbridge', dst: 'Matterbridge', method: 'restart_required', params: {} }));
|
|
2793
2292
|
}
|
|
2794
2293
|
});
|
|
2795
2294
|
}
|
|
2796
|
-
/**
|
|
2797
|
-
* Initializes the frontend of Matterbridge.
|
|
2798
|
-
*
|
|
2799
|
-
* @param port The port number to run the frontend server on. Default is 8283.
|
|
2800
|
-
*/
|
|
2801
2295
|
async initializeFrontend(port = 8283) {
|
|
2802
2296
|
let initializeError = false;
|
|
2803
2297
|
this.log.debug(`Initializing the frontend ${hasParameter('ssl') ? 'https' : 'http'} server on port ${YELLOW}${port}${db}`);
|
|
2804
|
-
// Create the express app that serves the frontend
|
|
2805
2298
|
this.expressApp = express();
|
|
2806
|
-
// Log all requests to the server for debugging
|
|
2807
|
-
/*
|
|
2808
|
-
if (hasParameter('homedir')) {
|
|
2809
|
-
this.expressApp.use((req, res, next) => {
|
|
2810
|
-
this.log.debug(`Received request on expressApp: ${req.method} ${req.url}`);
|
|
2811
|
-
next();
|
|
2812
|
-
});
|
|
2813
|
-
}
|
|
2814
|
-
*/
|
|
2815
|
-
// Serve static files from '/static' endpoint
|
|
2816
2299
|
this.expressApp.use(express.static(path.join(this.rootDirectory, 'frontend/build')));
|
|
2817
2300
|
if (!hasParameter('ssl')) {
|
|
2818
|
-
// Create an HTTP server and attach the express app
|
|
2819
2301
|
this.httpServer = createServer(this.expressApp);
|
|
2820
|
-
// Listen on the specified port
|
|
2821
2302
|
if (hasParameter('ingress')) {
|
|
2822
2303
|
this.httpServer.listen(port, '0.0.0.0', () => {
|
|
2823
2304
|
this.log.info(`The frontend http server is listening on ${UNDERLINE}http://0.0.0.0:${port}${UNDERLINEOFF}${rs}`);
|
|
@@ -2831,7 +2312,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2831
2312
|
this.log.info(`The frontend http server is listening on ${UNDERLINE}http://[${this.systemInformation.ipv6Address}]:${port}${UNDERLINEOFF}${rs}`);
|
|
2832
2313
|
});
|
|
2833
2314
|
}
|
|
2834
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
2835
2315
|
this.httpServer.on('error', (error) => {
|
|
2836
2316
|
this.log.error(`Frontend http server error listening on ${port}`);
|
|
2837
2317
|
switch (error.code) {
|
|
@@ -2847,7 +2327,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2847
2327
|
});
|
|
2848
2328
|
}
|
|
2849
2329
|
else {
|
|
2850
|
-
// Load the SSL certificate, the private key and optionally the CA certificate
|
|
2851
2330
|
let cert;
|
|
2852
2331
|
try {
|
|
2853
2332
|
cert = await fs.readFile(path.join(this.matterbridgeDirectory, 'certs/cert.pem'), 'utf8');
|
|
@@ -2875,9 +2354,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
2875
2354
|
this.log.info(`CA certificate file ${path.join(this.matterbridgeDirectory, 'certs/ca.pem')} not loaded: ${error}`);
|
|
2876
2355
|
}
|
|
2877
2356
|
const serverOptions = { cert, key, ca };
|
|
2878
|
-
// Create an HTTPS server with the SSL certificate and private key (ca is optional) and attach the express app
|
|
2879
2357
|
this.httpsServer = https.createServer(serverOptions, this.expressApp);
|
|
2880
|
-
// Listen on the specified port
|
|
2881
2358
|
if (hasParameter('ingress')) {
|
|
2882
2359
|
this.httpsServer.listen(port, '0.0.0.0', () => {
|
|
2883
2360
|
this.log.info(`The frontend https server is listening on ${UNDERLINE}https://0.0.0.0:${port}${UNDERLINEOFF}${rs}`);
|
|
@@ -2891,7 +2368,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2891
2368
|
this.log.info(`The frontend https server is listening on ${UNDERLINE}https://[${this.systemInformation.ipv6Address}]:${port}${UNDERLINEOFF}${rs}`);
|
|
2892
2369
|
});
|
|
2893
2370
|
}
|
|
2894
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
2895
2371
|
this.httpsServer.on('error', (error) => {
|
|
2896
2372
|
this.log.error(`Frontend https server error listening on ${port}`);
|
|
2897
2373
|
switch (error.code) {
|
|
@@ -2908,13 +2384,12 @@ export class Matterbridge extends EventEmitter {
|
|
|
2908
2384
|
}
|
|
2909
2385
|
if (initializeError)
|
|
2910
2386
|
return;
|
|
2911
|
-
// Createe a WebSocket server and attach it to the http or https server
|
|
2912
2387
|
const wssPort = port;
|
|
2913
2388
|
const wssHost = hasParameter('ssl') ? `wss://${this.systemInformation.ipv4Address}:${wssPort}` : `ws://${this.systemInformation.ipv4Address}:${wssPort}`;
|
|
2914
2389
|
this.webSocketServer = new WebSocketServer(hasParameter('ssl') ? { server: this.httpsServer } : { server: this.httpServer });
|
|
2915
2390
|
this.webSocketServer.on('connection', (ws, request) => {
|
|
2916
2391
|
const clientIp = request.socket.remoteAddress;
|
|
2917
|
-
AnsiLogger.setGlobalCallback(this.wssSendMessage.bind(this), "debug"
|
|
2392
|
+
AnsiLogger.setGlobalCallback(this.wssSendMessage.bind(this), "debug");
|
|
2918
2393
|
this.log.info(`WebSocketServer client "${clientIp}" connected to Matterbridge`);
|
|
2919
2394
|
ws.on('message', (message) => {
|
|
2920
2395
|
this.log.debug(`WebSocket client message: ${message}`);
|
|
@@ -2947,7 +2422,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2947
2422
|
this.webSocketServer.on('error', (ws, error) => {
|
|
2948
2423
|
this.log.error(`WebSocketServer error: ${error}`);
|
|
2949
2424
|
});
|
|
2950
|
-
// Endpoint to validate login code
|
|
2951
2425
|
this.expressApp.post('/api/login', express.json(), async (req, res) => {
|
|
2952
2426
|
const { password } = req.body;
|
|
2953
2427
|
this.log.debug('The frontend sent /api/login', password);
|
|
@@ -2966,14 +2440,12 @@ export class Matterbridge extends EventEmitter {
|
|
|
2966
2440
|
this.log.warn('/api/login error wrong password');
|
|
2967
2441
|
res.json({ valid: false });
|
|
2968
2442
|
}
|
|
2969
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
2970
2443
|
}
|
|
2971
2444
|
catch (error) {
|
|
2972
2445
|
this.log.error('/api/login error getting password');
|
|
2973
2446
|
res.json({ valid: false });
|
|
2974
2447
|
}
|
|
2975
2448
|
});
|
|
2976
|
-
// Endpoint to provide settings
|
|
2977
2449
|
this.expressApp.get('/api/settings', express.json(), async (req, res) => {
|
|
2978
2450
|
this.log.debug('The frontend sent /api/settings');
|
|
2979
2451
|
this.matterbridgeInformation.bridgeMode = this.bridgeMode;
|
|
@@ -2994,17 +2466,13 @@ export class Matterbridge extends EventEmitter {
|
|
|
2994
2466
|
this.matterbridgeInformation.matterbridgeSessionInformations = Array.from(this.matterbridgeSessionInformations.values());
|
|
2995
2467
|
this.matterbridgeInformation.profile = this.profile;
|
|
2996
2468
|
const response = { wssHost, ssl: hasParameter('ssl'), systemInformation: this.systemInformation, matterbridgeInformation: this.matterbridgeInformation };
|
|
2997
|
-
// this.log.debug('Response:', debugStringify(response));
|
|
2998
2469
|
res.json(response);
|
|
2999
2470
|
});
|
|
3000
|
-
// Endpoint to provide plugins
|
|
3001
2471
|
this.expressApp.get('/api/plugins', async (req, res) => {
|
|
3002
2472
|
this.log.debug('The frontend sent /api/plugins');
|
|
3003
2473
|
const response = await this.getBaseRegisteredPlugins();
|
|
3004
|
-
// this.log.debug('Response:', debugStringify(response));
|
|
3005
2474
|
res.json(response);
|
|
3006
2475
|
});
|
|
3007
|
-
// Endpoint to provide devices
|
|
3008
2476
|
this.expressApp.get('/api/devices', (req, res) => {
|
|
3009
2477
|
this.log.debug('The frontend sent /api/devices');
|
|
3010
2478
|
const devices = [];
|
|
@@ -3037,10 +2505,8 @@ export class Matterbridge extends EventEmitter {
|
|
|
3037
2505
|
cluster: cluster,
|
|
3038
2506
|
});
|
|
3039
2507
|
});
|
|
3040
|
-
// this.log.debug('Response:', debugStringify(data));
|
|
3041
2508
|
res.json(devices);
|
|
3042
2509
|
});
|
|
3043
|
-
// Endpoint to provide the cluster servers of the devices
|
|
3044
2510
|
this.expressApp.get('/api/devices_clusters/:selectedPluginName/:selectedDeviceEndpoint', (req, res) => {
|
|
3045
2511
|
const selectedPluginName = req.params.selectedPluginName;
|
|
3046
2512
|
const selectedDeviceEndpoint = parseInt(req.params.selectedDeviceEndpoint, 10);
|
|
@@ -3060,7 +2526,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
3060
2526
|
Object.entries(clusterServer.attributes).forEach(([key, value]) => {
|
|
3061
2527
|
if (clusterServer.name === 'EveHistory')
|
|
3062
2528
|
return;
|
|
3063
|
-
// this.log.debug(`***--clusterServer: ${clusterServer.name}(${clusterServer.id}) attribute:${key}(${value.id}) ${value.isFixed} ${value.isWritable} ${value.isWritable}`);
|
|
3064
2529
|
let attributeValue;
|
|
3065
2530
|
try {
|
|
3066
2531
|
if (typeof value.getLocal() === 'object')
|
|
@@ -3071,7 +2536,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
3071
2536
|
catch (error) {
|
|
3072
2537
|
attributeValue = 'Fabric-Scoped';
|
|
3073
2538
|
this.log.debug(`GetLocal value ${error} in clusterServer: ${clusterServer.name}(${clusterServer.id}) attribute: ${key}(${value.id})`);
|
|
3074
|
-
// console.log(error);
|
|
3075
2539
|
}
|
|
3076
2540
|
data.push({
|
|
3077
2541
|
endpoint: device.number ? device.number.toString() : '...',
|
|
@@ -3084,14 +2548,12 @@ export class Matterbridge extends EventEmitter {
|
|
|
3084
2548
|
});
|
|
3085
2549
|
});
|
|
3086
2550
|
device.getChildEndpoints().forEach((childEndpoint) => {
|
|
3087
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
3088
2551
|
const name = this.edge ? childEndpoint.endpoint?.id : childEndpoint.uniqueStorageKey;
|
|
3089
2552
|
const clusterServers = childEndpoint.getAllClusterServers();
|
|
3090
2553
|
clusterServers.forEach((clusterServer) => {
|
|
3091
2554
|
Object.entries(clusterServer.attributes).forEach(([key, value]) => {
|
|
3092
2555
|
if (clusterServer.name === 'EveHistory')
|
|
3093
2556
|
return;
|
|
3094
|
-
// this.log.debug(`***--clusterServer: ${clusterServer.name}(${clusterServer.id}) attribute:${key}(${value.id}) ${value.isFixed} ${value.isWritable} ${value.isWritable}`);
|
|
3095
2557
|
let attributeValue;
|
|
3096
2558
|
try {
|
|
3097
2559
|
if (typeof value.getLocal() === 'object')
|
|
@@ -3102,7 +2564,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
3102
2564
|
catch (error) {
|
|
3103
2565
|
attributeValue = 'Unavailable';
|
|
3104
2566
|
this.log.debug(`GetLocal error ${error} in clusterServer: ${clusterServer.name}(${clusterServer.id}) attribute: ${key}(${value.id})`);
|
|
3105
|
-
// console.log(error);
|
|
3106
2567
|
}
|
|
3107
2568
|
data.push({
|
|
3108
2569
|
endpoint: (childEndpoint.number ? childEndpoint.number.toString() : '...') + (name ? ' (' + name + ')' : ''),
|
|
@@ -3119,7 +2580,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
3119
2580
|
});
|
|
3120
2581
|
res.json(data);
|
|
3121
2582
|
});
|
|
3122
|
-
// Endpoint to view the log
|
|
3123
2583
|
this.expressApp.get('/api/view-log', async (req, res) => {
|
|
3124
2584
|
this.log.debug('The frontend sent /api/log');
|
|
3125
2585
|
try {
|
|
@@ -3132,12 +2592,10 @@ export class Matterbridge extends EventEmitter {
|
|
|
3132
2592
|
res.status(500).send('Error reading log file');
|
|
3133
2593
|
}
|
|
3134
2594
|
});
|
|
3135
|
-
// Endpoint to download the matterbridge log
|
|
3136
2595
|
this.expressApp.get('/api/download-mblog', async (req, res) => {
|
|
3137
2596
|
this.log.debug('The frontend sent /api/download-mblog');
|
|
3138
2597
|
try {
|
|
3139
2598
|
await fs.access(path.join(this.matterbridgeDirectory, this.matterbrideLoggerFile), fs.constants.F_OK);
|
|
3140
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
3141
2599
|
}
|
|
3142
2600
|
catch (error) {
|
|
3143
2601
|
fs.appendFile(path.join(this.matterbridgeDirectory, this.matterbrideLoggerFile), 'Enable the log on file in the settings to enable the file logger');
|
|
@@ -3149,12 +2607,10 @@ export class Matterbridge extends EventEmitter {
|
|
|
3149
2607
|
}
|
|
3150
2608
|
});
|
|
3151
2609
|
});
|
|
3152
|
-
// Endpoint to download the matter log
|
|
3153
2610
|
this.expressApp.get('/api/download-mjlog', async (req, res) => {
|
|
3154
2611
|
this.log.debug('The frontend sent /api/download-mjlog');
|
|
3155
2612
|
try {
|
|
3156
2613
|
await fs.access(path.join(this.matterbridgeDirectory, this.matterLoggerFile), fs.constants.F_OK);
|
|
3157
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
3158
2614
|
}
|
|
3159
2615
|
catch (error) {
|
|
3160
2616
|
fs.appendFile(path.join(this.matterbridgeDirectory, this.matterLoggerFile), 'Enable the log on file in the settings to enable the file logger');
|
|
@@ -3166,7 +2622,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
3166
2622
|
}
|
|
3167
2623
|
});
|
|
3168
2624
|
});
|
|
3169
|
-
// Endpoint to download the matter storage file
|
|
3170
2625
|
this.expressApp.get('/api/download-mjstorage', (req, res) => {
|
|
3171
2626
|
this.log.debug('The frontend sent /api/download-mjstorage');
|
|
3172
2627
|
res.download(path.join(this.matterbridgeDirectory, this.matterStorageName), 'matterbridge.json', (error) => {
|
|
@@ -3176,7 +2631,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
3176
2631
|
}
|
|
3177
2632
|
});
|
|
3178
2633
|
});
|
|
3179
|
-
// Endpoint to download the matterbridge storage directory
|
|
3180
2634
|
this.expressApp.get('/api/download-mbstorage', async (req, res) => {
|
|
3181
2635
|
this.log.debug('The frontend sent /api/download-mbstorage');
|
|
3182
2636
|
await createZip(path.join(os.tmpdir(), `matterbridge.${this.nodeStorageName}.zip`), path.join(this.matterbridgeDirectory, this.nodeStorageName));
|
|
@@ -3187,7 +2641,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
3187
2641
|
}
|
|
3188
2642
|
});
|
|
3189
2643
|
});
|
|
3190
|
-
// Endpoint to download the matterbridge plugin directory
|
|
3191
2644
|
this.expressApp.get('/api/download-pluginstorage', async (req, res) => {
|
|
3192
2645
|
this.log.debug('The frontend sent /api/download-pluginstorage');
|
|
3193
2646
|
await createZip(path.join(os.tmpdir(), `matterbridge.pluginstorage.zip`), this.matterbridgePluginDirectory);
|
|
@@ -3198,11 +2651,9 @@ export class Matterbridge extends EventEmitter {
|
|
|
3198
2651
|
}
|
|
3199
2652
|
});
|
|
3200
2653
|
});
|
|
3201
|
-
// Endpoint to download the matterbridge plugin config files
|
|
3202
2654
|
this.expressApp.get('/api/download-pluginconfig', async (req, res) => {
|
|
3203
2655
|
this.log.debug('The frontend sent /api/download-pluginconfig');
|
|
3204
2656
|
await createZip(path.join(os.tmpdir(), `matterbridge.pluginconfig.zip`), path.relative(process.cwd(), path.join(this.matterbridgeDirectory, '*.config.json')));
|
|
3205
|
-
// 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')));
|
|
3206
2657
|
res.download(path.join(os.tmpdir(), `matterbridge.pluginconfig.zip`), `matterbridge.pluginconfig.zip`, (error) => {
|
|
3207
2658
|
if (error) {
|
|
3208
2659
|
this.log.error(`Error downloading file matterbridge.pluginstorage.zip: ${error instanceof Error ? error.message : error}`);
|
|
@@ -3210,7 +2661,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
3210
2661
|
}
|
|
3211
2662
|
});
|
|
3212
2663
|
});
|
|
3213
|
-
// Endpoint to download the matterbridge plugin config files
|
|
3214
2664
|
this.expressApp.get('/api/download-backup', async (req, res) => {
|
|
3215
2665
|
this.log.debug('The frontend sent /api/download-backup');
|
|
3216
2666
|
res.download(path.join(os.tmpdir(), `matterbridge.backup.zip`), `matterbridge.backup.zip`, (error) => {
|
|
@@ -3220,7 +2670,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
3220
2670
|
}
|
|
3221
2671
|
});
|
|
3222
2672
|
});
|
|
3223
|
-
// Endpoint to receive commands
|
|
3224
2673
|
this.expressApp.post('/api/command/:command/:param', express.json(), async (req, res) => {
|
|
3225
2674
|
const command = req.params.command;
|
|
3226
2675
|
let param = req.params.param;
|
|
@@ -3230,15 +2679,13 @@ export class Matterbridge extends EventEmitter {
|
|
|
3230
2679
|
return;
|
|
3231
2680
|
}
|
|
3232
2681
|
this.log.debug(`Received frontend command: ${command}:${param}`);
|
|
3233
|
-
// Handle the command setpassword from Settings
|
|
3234
2682
|
if (command === 'setpassword') {
|
|
3235
|
-
const password = param.slice(1, -1);
|
|
2683
|
+
const password = param.slice(1, -1);
|
|
3236
2684
|
this.log.debug('setpassword', param, password);
|
|
3237
2685
|
await this.nodeContext?.set('password', password);
|
|
3238
2686
|
res.json({ message: 'Command received' });
|
|
3239
2687
|
return;
|
|
3240
2688
|
}
|
|
3241
|
-
// Handle the command setbridgemode from Settings
|
|
3242
2689
|
if (command === 'setbridgemode') {
|
|
3243
2690
|
this.log.debug(`setbridgemode: ${param}`);
|
|
3244
2691
|
this.wssSendRestartRequired();
|
|
@@ -3246,7 +2693,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
3246
2693
|
res.json({ message: 'Command received' });
|
|
3247
2694
|
return;
|
|
3248
2695
|
}
|
|
3249
|
-
// Handle the command backup from Settings
|
|
3250
2696
|
if (command === 'backup') {
|
|
3251
2697
|
this.log.notice(`Prepairing the backup...`);
|
|
3252
2698
|
await createZip(path.join(os.tmpdir(), `matterbridge.backup.zip`), path.join(this.matterbridgeDirectory), path.join(this.matterbridgePluginDirectory));
|
|
@@ -3254,26 +2700,25 @@ export class Matterbridge extends EventEmitter {
|
|
|
3254
2700
|
res.json({ message: 'Command received' });
|
|
3255
2701
|
return;
|
|
3256
2702
|
}
|
|
3257
|
-
// Handle the command setmbloglevel from Settings
|
|
3258
2703
|
if (command === 'setmbloglevel') {
|
|
3259
2704
|
this.log.debug('Matterbridge log level:', param);
|
|
3260
2705
|
if (param === 'Debug') {
|
|
3261
|
-
this.log.logLevel = "debug"
|
|
2706
|
+
this.log.logLevel = "debug";
|
|
3262
2707
|
}
|
|
3263
2708
|
else if (param === 'Info') {
|
|
3264
|
-
this.log.logLevel = "info"
|
|
2709
|
+
this.log.logLevel = "info";
|
|
3265
2710
|
}
|
|
3266
2711
|
else if (param === 'Notice') {
|
|
3267
|
-
this.log.logLevel = "notice"
|
|
2712
|
+
this.log.logLevel = "notice";
|
|
3268
2713
|
}
|
|
3269
2714
|
else if (param === 'Warn') {
|
|
3270
|
-
this.log.logLevel = "warn"
|
|
2715
|
+
this.log.logLevel = "warn";
|
|
3271
2716
|
}
|
|
3272
2717
|
else if (param === 'Error') {
|
|
3273
|
-
this.log.logLevel = "error"
|
|
2718
|
+
this.log.logLevel = "error";
|
|
3274
2719
|
}
|
|
3275
2720
|
else if (param === 'Fatal') {
|
|
3276
|
-
this.log.logLevel = "fatal"
|
|
2721
|
+
this.log.logLevel = "fatal";
|
|
3277
2722
|
}
|
|
3278
2723
|
await this.nodeContext?.set('matterbridgeLogLevel', this.log.logLevel);
|
|
3279
2724
|
MatterbridgeDevice.logLevel = this.log.logLevel;
|
|
@@ -3281,13 +2726,12 @@ export class Matterbridge extends EventEmitter {
|
|
|
3281
2726
|
for (const plugin of this.plugins) {
|
|
3282
2727
|
if (!plugin.platform || !plugin.platform.config)
|
|
3283
2728
|
continue;
|
|
3284
|
-
plugin.platform.log.logLevel = plugin.platform.config.debug ? "debug"
|
|
3285
|
-
await plugin.platform.onChangeLoggerLevel(plugin.platform.config.debug ? "debug"
|
|
2729
|
+
plugin.platform.log.logLevel = plugin.platform.config.debug ? "debug" : this.log.logLevel;
|
|
2730
|
+
await plugin.platform.onChangeLoggerLevel(plugin.platform.config.debug ? "debug" : this.log.logLevel);
|
|
3286
2731
|
}
|
|
3287
2732
|
res.json({ message: 'Command received' });
|
|
3288
2733
|
return;
|
|
3289
2734
|
}
|
|
3290
|
-
// Handle the command setmbloglevel from Settings
|
|
3291
2735
|
if (command === 'setmjloglevel') {
|
|
3292
2736
|
this.log.debug('Matter.js log level:', param);
|
|
3293
2737
|
if (param === 'Debug') {
|
|
@@ -3312,34 +2756,30 @@ export class Matterbridge extends EventEmitter {
|
|
|
3312
2756
|
res.json({ message: 'Command received' });
|
|
3313
2757
|
return;
|
|
3314
2758
|
}
|
|
3315
|
-
// Handle the command setmdnsinterface from Settings
|
|
3316
2759
|
if (command === 'setmdnsinterface') {
|
|
3317
|
-
param = param.slice(1, -1);
|
|
2760
|
+
param = param.slice(1, -1);
|
|
3318
2761
|
this.matterbridgeInformation.mattermdnsinterface = param;
|
|
3319
2762
|
this.log.debug('Matter.js mdns interface:', param === '' ? 'All interfaces' : param);
|
|
3320
2763
|
await this.nodeContext?.set('mattermdnsinterface', param);
|
|
3321
2764
|
res.json({ message: 'Command received' });
|
|
3322
2765
|
return;
|
|
3323
2766
|
}
|
|
3324
|
-
// Handle the command setipv4address from Settings
|
|
3325
2767
|
if (command === 'setipv4address') {
|
|
3326
|
-
param = param.slice(1, -1);
|
|
2768
|
+
param = param.slice(1, -1);
|
|
3327
2769
|
this.matterbridgeInformation.matteripv4address = param;
|
|
3328
2770
|
this.log.debug('Matter.js ipv4 address:', param === '' ? 'All ipv4 addresses' : param);
|
|
3329
2771
|
await this.nodeContext?.set('matteripv4address', param);
|
|
3330
2772
|
res.json({ message: 'Command received' });
|
|
3331
2773
|
return;
|
|
3332
2774
|
}
|
|
3333
|
-
// Handle the command setipv6address from Settings
|
|
3334
2775
|
if (command === 'setipv6address') {
|
|
3335
|
-
param = param.slice(1, -1);
|
|
2776
|
+
param = param.slice(1, -1);
|
|
3336
2777
|
this.matterbridgeInformation.matteripv6address = param;
|
|
3337
2778
|
this.log.debug('Matter.js ipv6 address:', param === '' ? 'All ipv6 addresses' : param);
|
|
3338
2779
|
await this.nodeContext?.set('matteripv6address', param);
|
|
3339
2780
|
res.json({ message: 'Command received' });
|
|
3340
2781
|
return;
|
|
3341
2782
|
}
|
|
3342
|
-
// Handle the command setmatterport from Settings
|
|
3343
2783
|
if (command === 'setmatterport') {
|
|
3344
2784
|
const port = Math.min(Math.max(parseInt(param), 5540), 5560);
|
|
3345
2785
|
this.matterbridgeInformation.matterPort = port;
|
|
@@ -3348,7 +2788,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
3348
2788
|
res.json({ message: 'Command received' });
|
|
3349
2789
|
return;
|
|
3350
2790
|
}
|
|
3351
|
-
// Handle the command setmatterdiscriminator from Settings
|
|
3352
2791
|
if (command === 'setmatterdiscriminator') {
|
|
3353
2792
|
const discriminator = Math.min(Math.max(parseInt(param), 1000), 4095);
|
|
3354
2793
|
this.matterbridgeInformation.matterDiscriminator = discriminator;
|
|
@@ -3357,7 +2796,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
3357
2796
|
res.json({ message: 'Command received' });
|
|
3358
2797
|
return;
|
|
3359
2798
|
}
|
|
3360
|
-
// Handle the command setmatterpasscode from Settings
|
|
3361
2799
|
if (command === 'setmatterpasscode') {
|
|
3362
2800
|
const passcode = Math.min(Math.max(parseInt(param), 10000000), 90000000);
|
|
3363
2801
|
this.matterbridgeInformation.matterPasscode = passcode;
|
|
@@ -3366,20 +2804,17 @@ export class Matterbridge extends EventEmitter {
|
|
|
3366
2804
|
res.json({ message: 'Command received' });
|
|
3367
2805
|
return;
|
|
3368
2806
|
}
|
|
3369
|
-
// Handle the command setmbloglevel from Settings
|
|
3370
2807
|
if (command === 'setmblogfile') {
|
|
3371
2808
|
this.log.debug('Matterbridge file log:', param);
|
|
3372
2809
|
this.matterbridgeInformation.fileLogger = param === 'true';
|
|
3373
2810
|
await this.nodeContext?.set('matterbridgeFileLog', param === 'true');
|
|
3374
|
-
// Create the file logger for matterbridge
|
|
3375
2811
|
if (param === 'true')
|
|
3376
|
-
AnsiLogger.setGlobalLogfile(path.join(this.matterbridgeDirectory, this.matterbrideLoggerFile), "debug"
|
|
2812
|
+
AnsiLogger.setGlobalLogfile(path.join(this.matterbridgeDirectory, this.matterbrideLoggerFile), "debug", true);
|
|
3377
2813
|
else
|
|
3378
2814
|
AnsiLogger.setGlobalLogfile(undefined);
|
|
3379
2815
|
res.json({ message: 'Command received' });
|
|
3380
2816
|
return;
|
|
3381
2817
|
}
|
|
3382
|
-
// Handle the command setmbloglevel from Settings
|
|
3383
2818
|
if (command === 'setmjlogfile') {
|
|
3384
2819
|
this.log.debug('Matter file log:', param);
|
|
3385
2820
|
this.matterbridgeInformation.matterFileLogger = param === 'true';
|
|
@@ -3406,43 +2841,36 @@ export class Matterbridge extends EventEmitter {
|
|
|
3406
2841
|
res.json({ message: 'Command received' });
|
|
3407
2842
|
return;
|
|
3408
2843
|
}
|
|
3409
|
-
// Handle the command unregister from Settings
|
|
3410
2844
|
if (command === 'unregister') {
|
|
3411
2845
|
await this.unregisterAndShutdownProcess();
|
|
3412
2846
|
res.json({ message: 'Command received' });
|
|
3413
2847
|
return;
|
|
3414
2848
|
}
|
|
3415
|
-
// Handle the command reset from Settings
|
|
3416
2849
|
if (command === 'reset') {
|
|
3417
2850
|
await this.shutdownProcessAndReset();
|
|
3418
2851
|
res.json({ message: 'Command received' });
|
|
3419
2852
|
return;
|
|
3420
2853
|
}
|
|
3421
|
-
// Handle the command factoryreset from Settings
|
|
3422
2854
|
if (command === 'factoryreset') {
|
|
3423
2855
|
await this.shutdownProcessAndFactoryReset();
|
|
3424
2856
|
res.json({ message: 'Command received' });
|
|
3425
2857
|
return;
|
|
3426
2858
|
}
|
|
3427
|
-
// Handle the command shutdown from Header
|
|
3428
2859
|
if (command === 'shutdown') {
|
|
3429
2860
|
await this.shutdownProcess();
|
|
3430
2861
|
res.json({ message: 'Command received' });
|
|
3431
2862
|
return;
|
|
3432
2863
|
}
|
|
3433
|
-
// Handle the command restart from Header
|
|
3434
2864
|
if (command === 'restart') {
|
|
3435
2865
|
await this.restartProcess();
|
|
3436
2866
|
res.json({ message: 'Command received' });
|
|
3437
2867
|
return;
|
|
3438
2868
|
}
|
|
3439
|
-
// Handle the command update from Header
|
|
3440
2869
|
if (command === 'update') {
|
|
3441
2870
|
this.log.info('Updating matterbridge...');
|
|
3442
2871
|
try {
|
|
3443
2872
|
await this.spawnCommand('npm', ['install', '-g', 'matterbridge', '--omit=dev', '--verbose']);
|
|
3444
2873
|
this.log.info('Matterbridge has been updated. Full restart required.');
|
|
3445
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
3446
2874
|
}
|
|
3447
2875
|
catch (error) {
|
|
3448
2876
|
this.log.error('Error updating matterbridge');
|
|
@@ -3452,11 +2880,9 @@ export class Matterbridge extends EventEmitter {
|
|
|
3452
2880
|
res.json({ message: 'Command received' });
|
|
3453
2881
|
return;
|
|
3454
2882
|
}
|
|
3455
|
-
// Handle the command saveconfig from Home
|
|
3456
2883
|
if (command === 'saveconfig') {
|
|
3457
2884
|
param = param.replace(/\*/g, '\\');
|
|
3458
2885
|
this.log.info(`Saving config for plugin ${plg}${param}${nf}...`);
|
|
3459
|
-
// console.log('Req.body:', JSON.stringify(req.body, null, 2));
|
|
3460
2886
|
if (!this.plugins.has(param)) {
|
|
3461
2887
|
this.log.warn(`Plugin ${plg}${param}${wr} not found in matterbridge`);
|
|
3462
2888
|
}
|
|
@@ -3470,39 +2896,33 @@ export class Matterbridge extends EventEmitter {
|
|
|
3470
2896
|
res.json({ message: 'Command received' });
|
|
3471
2897
|
return;
|
|
3472
2898
|
}
|
|
3473
|
-
// Handle the command installplugin from Home
|
|
3474
2899
|
if (command === 'installplugin') {
|
|
3475
2900
|
param = param.replace(/\*/g, '\\');
|
|
3476
2901
|
this.log.info(`Installing plugin ${plg}${param}${nf}...`);
|
|
3477
2902
|
try {
|
|
3478
2903
|
await this.spawnCommand('npm', ['install', '-g', param, '--omit=dev', '--verbose']);
|
|
3479
2904
|
this.log.info(`Plugin ${plg}${param}${nf} installed. Full restart required.`);
|
|
3480
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
3481
2905
|
}
|
|
3482
2906
|
catch (error) {
|
|
3483
2907
|
this.log.error(`Error installing plugin ${plg}${param}${er}`);
|
|
3484
2908
|
}
|
|
3485
2909
|
this.wssSendRestartRequired();
|
|
3486
2910
|
param = param.split('@')[0];
|
|
3487
|
-
// Also add the plugin to matterbridge so no return!
|
|
3488
2911
|
if (param === 'matterbridge') {
|
|
3489
|
-
// 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
|
|
3490
2912
|
res.json({ message: 'Command received' });
|
|
3491
2913
|
return;
|
|
3492
2914
|
}
|
|
3493
2915
|
}
|
|
3494
|
-
// Handle the command addplugin from Home
|
|
3495
2916
|
if (command === 'addplugin' || command === 'installplugin') {
|
|
3496
2917
|
param = param.replace(/\*/g, '\\');
|
|
3497
2918
|
const plugin = await this.plugins.add(param);
|
|
3498
2919
|
if (plugin) {
|
|
3499
|
-
this.plugins.load(plugin, true, 'The plugin has been added', true);
|
|
2920
|
+
this.plugins.load(plugin, true, 'The plugin has been added', true);
|
|
3500
2921
|
}
|
|
3501
2922
|
res.json({ message: 'Command received' });
|
|
3502
2923
|
this.wssSendRefreshRequired();
|
|
3503
2924
|
return;
|
|
3504
2925
|
}
|
|
3505
|
-
// Handle the command removeplugin from Home
|
|
3506
2926
|
if (command === 'removeplugin') {
|
|
3507
2927
|
if (!this.plugins.has(param)) {
|
|
3508
2928
|
this.log.warn(`Plugin ${plg}${param}${wr} not found in matterbridge`);
|
|
@@ -3516,7 +2936,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
3516
2936
|
this.wssSendRefreshRequired();
|
|
3517
2937
|
return;
|
|
3518
2938
|
}
|
|
3519
|
-
// Handle the command enableplugin from Home
|
|
3520
2939
|
if (command === 'enableplugin') {
|
|
3521
2940
|
if (!this.plugins.has(param)) {
|
|
3522
2941
|
this.log.warn(`Plugin ${plg}${param}${wr} not found in matterbridge`);
|
|
@@ -3534,14 +2953,13 @@ export class Matterbridge extends EventEmitter {
|
|
|
3534
2953
|
plugin.registeredDevices = undefined;
|
|
3535
2954
|
plugin.addedDevices = undefined;
|
|
3536
2955
|
await this.plugins.enable(param);
|
|
3537
|
-
this.plugins.load(plugin, true, 'The plugin has been enabled', true);
|
|
2956
|
+
this.plugins.load(plugin, true, 'The plugin has been enabled', true);
|
|
3538
2957
|
}
|
|
3539
2958
|
}
|
|
3540
2959
|
res.json({ message: 'Command received' });
|
|
3541
2960
|
this.wssSendRefreshRequired();
|
|
3542
2961
|
return;
|
|
3543
2962
|
}
|
|
3544
|
-
// Handle the command disableplugin from Home
|
|
3545
2963
|
if (command === 'disableplugin') {
|
|
3546
2964
|
if (!this.plugins.has(param)) {
|
|
3547
2965
|
this.log.warn(`Plugin ${plg}${param}${wr} not found in matterbridge`);
|
|
@@ -3558,7 +2976,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
3558
2976
|
return;
|
|
3559
2977
|
}
|
|
3560
2978
|
});
|
|
3561
|
-
// Fallback for routing (must be the last route)
|
|
3562
2979
|
this.expressApp.get('*', (req, res) => {
|
|
3563
2980
|
this.log.debug('The frontend sent:', req.url);
|
|
3564
2981
|
this.log.debug('Response send file:', path.join(this.rootDirectory, 'frontend/build/index.html'));
|
|
@@ -3566,11 +2983,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
3566
2983
|
});
|
|
3567
2984
|
this.log.debug(`Frontend initialized on port ${YELLOW}${port}${db} static ${UNDERLINE}${path.join(this.rootDirectory, 'frontend/build')}${UNDERLINEOFF}${rs}`);
|
|
3568
2985
|
}
|
|
3569
|
-
/**
|
|
3570
|
-
* Retrieves the cluster text description from a given device.
|
|
3571
|
-
* @param {MatterbridgeDevice} device - The MatterbridgeDevice object.
|
|
3572
|
-
* @returns {string} The attributes description of the cluster servers in the device.
|
|
3573
|
-
*/
|
|
3574
2986
|
getClusterTextFromDevice(device) {
|
|
3575
2987
|
const stringifyUserLabel = (endpoint) => {
|
|
3576
2988
|
const labelList = endpoint.getClusterServer(UserLabelCluster)?.attributes.labelList.getLocal();
|
|
@@ -3593,11 +3005,9 @@ export class Matterbridge extends EventEmitter {
|
|
|
3593
3005
|
return '';
|
|
3594
3006
|
};
|
|
3595
3007
|
let attributes = '';
|
|
3596
|
-
// this.log.debug(`***getClusterTextFromDevice: ${device.deviceName} (${device.name})`);
|
|
3597
3008
|
const clusterServers = device.getAllClusterServers();
|
|
3598
3009
|
clusterServers.forEach((clusterServer) => {
|
|
3599
3010
|
try {
|
|
3600
|
-
// this.log.debug(`**--clusterServer: ${clusterServer.id} (${clusterServer.name})`);
|
|
3601
3011
|
if (clusterServer.name === 'OnOff')
|
|
3602
3012
|
attributes += `OnOff: ${clusterServer.attributes.onOff.getLocal()} `;
|
|
3603
3013
|
if (clusterServer.name === 'Switch')
|
|
@@ -3648,30 +3058,18 @@ export class Matterbridge extends EventEmitter {
|
|
|
3648
3058
|
attributes += `${stringifyFixedLabel(device)} `;
|
|
3649
3059
|
if (clusterServer.name === 'UserLabel')
|
|
3650
3060
|
attributes += `${stringifyUserLabel(device)} `;
|
|
3651
|
-
// this.log.debug(`*--clusterServer: ${clusterServer.id} (${clusterServer.name})`);
|
|
3652
3061
|
}
|
|
3653
3062
|
catch (error) {
|
|
3654
3063
|
this.log.error(`getClusterTextFromDevice with ${clusterServer.name} error: ${error}`);
|
|
3655
3064
|
}
|
|
3656
3065
|
});
|
|
3657
|
-
// this.log.debug(`*getClusterTextFromDevice: ${device.deviceName} (${device.name})`);
|
|
3658
3066
|
return attributes;
|
|
3659
3067
|
}
|
|
3660
|
-
/**
|
|
3661
|
-
* Initializes the Matterbridge instance as extension for zigbee2mqtt.
|
|
3662
|
-
* @deprecated This method is deprecated and will be removed in a future version.
|
|
3663
|
-
*
|
|
3664
|
-
* @returns A Promise that resolves when the initialization is complete.
|
|
3665
|
-
*/
|
|
3666
3068
|
async startExtension(dataPath, extensionVersion, port = 5540) {
|
|
3667
|
-
// Set the bridge mode
|
|
3668
3069
|
this.bridgeMode = 'bridge';
|
|
3669
|
-
// Set the first port to use
|
|
3670
3070
|
this.port = port;
|
|
3671
|
-
|
|
3672
|
-
this.log = new AnsiLogger({ logName: 'Matterbridge', logTimestampFormat: 4 /* TimestampFormat.TIME_MILLIS */, logLevel: "info" /* LogLevel.INFO */ });
|
|
3071
|
+
this.log = new AnsiLogger({ logName: 'Matterbridge', logTimestampFormat: 4, logLevel: "info" });
|
|
3673
3072
|
this.log.debug('Matterbridge extension is starting...');
|
|
3674
|
-
// Initialize NodeStorage
|
|
3675
3073
|
this.matterbridgeDirectory = dataPath;
|
|
3676
3074
|
this.log.debug('Creating node storage manager dir: ' + path.join(this.matterbridgeDirectory, 'node_storage'));
|
|
3677
3075
|
this.nodeStorage = new NodeStorageManager({ dir: path.join(this.matterbridgeDirectory, 'node_storage'), logging: false });
|
|
@@ -3690,13 +3088,10 @@ export class Matterbridge extends EventEmitter {
|
|
|
3690
3088
|
};
|
|
3691
3089
|
this.plugins.set(plugin);
|
|
3692
3090
|
this.plugins.saveToStorage();
|
|
3693
|
-
// Log system info and create .matterbridge directory
|
|
3694
3091
|
await this.logNodeAndSystemInfo();
|
|
3695
3092
|
this.matterbridgeDirectory = dataPath;
|
|
3696
|
-
// Set matter.js logger level and format
|
|
3697
3093
|
Logger.defaultLogLevel = MatterLogLevel.INFO;
|
|
3698
3094
|
Logger.format = MatterLogFormat.ANSI;
|
|
3699
|
-
// Start the storage and create matterbridgeContext
|
|
3700
3095
|
await this.startMatterStorage('json', path.join(this.matterbridgeDirectory, this.matterStorageName));
|
|
3701
3096
|
if (!this.storageManager)
|
|
3702
3097
|
return false;
|
|
@@ -3706,7 +3101,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
3706
3101
|
await this.matterbridgeContext.set('softwareVersion', 1);
|
|
3707
3102
|
await this.matterbridgeContext.set('softwareVersionString', this.matterbridgeVersion);
|
|
3708
3103
|
await this.matterbridgeContext.set('hardwareVersion', 1);
|
|
3709
|
-
await this.matterbridgeContext.set('hardwareVersionString', extensionVersion);
|
|
3104
|
+
await this.matterbridgeContext.set('hardwareVersionString', extensionVersion);
|
|
3710
3105
|
this.matterServer = this.createMatterServer(this.storageManager);
|
|
3711
3106
|
this.log.debug(`Creating commissioning server for ${plg}Matterbridge${db}`);
|
|
3712
3107
|
this.commissioningServer = await this.createCommisioningServer(this.matterbridgeContext, 'Matterbridge');
|
|
@@ -3719,7 +3114,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
3719
3114
|
await this.startMatterServer();
|
|
3720
3115
|
this.log.info('Matter server started');
|
|
3721
3116
|
await this.showCommissioningQRCode(this.commissioningServer, this.matterbridgeContext, this.nodeContext, 'Matterbridge');
|
|
3722
|
-
// Set reachability to true and trigger event after 60 seconds
|
|
3723
3117
|
setTimeout(() => {
|
|
3724
3118
|
this.log.info(`Setting reachability to true for ${plg}Matterbridge${db}`);
|
|
3725
3119
|
if (this.commissioningServer)
|
|
@@ -3729,31 +3123,14 @@ export class Matterbridge extends EventEmitter {
|
|
|
3729
3123
|
}, 60 * 1000);
|
|
3730
3124
|
return this.commissioningServer.isCommissioned();
|
|
3731
3125
|
}
|
|
3732
|
-
/**
|
|
3733
|
-
* Close the Matterbridge instance as extension for zigbee2mqtt.
|
|
3734
|
-
* @deprecated This method is deprecated and will be removed in a future version.
|
|
3735
|
-
*
|
|
3736
|
-
* @returns A Promise that resolves when the initialization is complete.
|
|
3737
|
-
*/
|
|
3738
3126
|
async stopExtension() {
|
|
3739
|
-
// Closing matter
|
|
3740
3127
|
await this.stopMatterServer();
|
|
3741
|
-
// Clearing the session manager
|
|
3742
|
-
// this.matterbridgeContext?.createContext('SessionManager').clear();
|
|
3743
|
-
// Closing storage
|
|
3744
3128
|
await this.stopMatterStorage();
|
|
3745
3129
|
this.log.info('Matter server stopped');
|
|
3746
3130
|
}
|
|
3747
|
-
/**
|
|
3748
|
-
* Checks if the extension is commissioned.
|
|
3749
|
-
* @deprecated This method is deprecated and will be removed in a future version.
|
|
3750
|
-
*
|
|
3751
|
-
* @returns {boolean} Returns true if the extension is commissioned, false otherwise.
|
|
3752
|
-
*/
|
|
3753
3131
|
isExtensionCommissioned() {
|
|
3754
3132
|
if (!this.commissioningServer)
|
|
3755
3133
|
return false;
|
|
3756
3134
|
return this.commissioningServer.isCommissioned();
|
|
3757
3135
|
}
|
|
3758
3136
|
}
|
|
3759
|
-
//# sourceMappingURL=matterbridge.js.map
|