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