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