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