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