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