matterbridge 3.0.0 → 3.0.1-dev-20250501-4f463f9
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 +2 -0
- package/README.md +22 -4
- package/dist/cli.js +2 -37
- package/dist/cluster/export.js +0 -2
- package/dist/defaultConfigSchema.js +0 -23
- package/dist/deviceManager.js +1 -94
- package/dist/frontend.js +19 -352
- package/dist/index.js +1 -28
- package/dist/logger/export.js +0 -1
- package/dist/matter/behaviors.js +0 -2
- package/dist/matter/clusters.js +0 -2
- package/dist/matter/devices.js +0 -2
- package/dist/matter/endpoints.js +0 -2
- package/dist/matter/export.js +0 -2
- package/dist/matter/types.js +0 -2
- package/dist/matterbridge.js +46 -747
- package/dist/matterbridgeAccessoryPlatform.js +0 -34
- package/dist/matterbridgeBehaviors.js +1 -33
- package/dist/matterbridgeDeviceTypes.js +12 -431
- package/dist/matterbridgeDynamicPlatform.js +0 -34
- package/dist/matterbridgeEndpoint.js +11 -794
- package/dist/matterbridgeEndpointHelpers.js +9 -142
- package/dist/matterbridgePlatform.js +7 -225
- package/dist/matterbridgeTypes.js +0 -24
- package/dist/pluginManager.js +3 -262
- package/dist/shelly.js +6 -146
- package/dist/storage/export.js +0 -1
- package/dist/update.js +0 -52
- package/dist/utils/colorUtils.js +2 -205
- package/dist/utils/copyDirectory.js +1 -37
- package/dist/utils/createZip.js +2 -42
- package/dist/utils/deepCopy.js +0 -40
- package/dist/utils/deepEqual.js +1 -65
- package/dist/utils/export.js +0 -1
- package/dist/utils/isvalid.js +0 -86
- package/dist/utils/network.js +5 -76
- package/dist/utils/parameter.js +0 -53
- package/dist/utils/wait.js +5 -48
- package/frontend/build/asset-manifest.json +3 -3
- package/frontend/build/index.html +1 -1
- package/frontend/build/static/js/{main.1d983660.js → main.356788d7.js} +3 -3
- package/frontend/build/static/js/{main.1d983660.js.map → main.356788d7.js.map} +1 -1
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -2
- package/dist/cli.d.ts +0 -29
- 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 -114
- package/dist/deviceManager.d.ts.map +0 -1
- package/dist/deviceManager.js.map +0 -1
- package/dist/frontend.d.ts +0 -222
- package/dist/frontend.d.ts.map +0 -1
- package/dist/frontend.js.map +0 -1
- package/dist/index.d.ts +0 -35
- 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/behaviors.d.ts +0 -2
- package/dist/matter/behaviors.d.ts.map +0 -1
- package/dist/matter/behaviors.js.map +0 -1
- package/dist/matter/clusters.d.ts +0 -2
- package/dist/matter/clusters.d.ts.map +0 -1
- package/dist/matter/clusters.js.map +0 -1
- package/dist/matter/devices.d.ts +0 -2
- package/dist/matter/devices.d.ts.map +0 -1
- package/dist/matter/devices.js.map +0 -1
- package/dist/matter/endpoints.d.ts +0 -2
- package/dist/matter/endpoints.d.ts.map +0 -1
- package/dist/matter/endpoints.js.map +0 -1
- package/dist/matter/export.d.ts +0 -5
- package/dist/matter/export.d.ts.map +0 -1
- package/dist/matter/export.js.map +0 -1
- package/dist/matter/types.d.ts +0 -3
- package/dist/matter/types.d.ts.map +0 -1
- package/dist/matter/types.js.map +0 -1
- package/dist/matterbridge.d.ts +0 -431
- package/dist/matterbridge.d.ts.map +0 -1
- package/dist/matterbridge.js.map +0 -1
- package/dist/matterbridgeAccessoryPlatform.d.ts +0 -40
- package/dist/matterbridgeAccessoryPlatform.d.ts.map +0 -1
- package/dist/matterbridgeAccessoryPlatform.js.map +0 -1
- package/dist/matterbridgeBehaviors.d.ts +0 -1514
- package/dist/matterbridgeBehaviors.d.ts.map +0 -1
- package/dist/matterbridgeBehaviors.js.map +0 -1
- package/dist/matterbridgeDeviceTypes.d.ts +0 -494
- package/dist/matterbridgeDeviceTypes.d.ts.map +0 -1
- package/dist/matterbridgeDeviceTypes.js.map +0 -1
- package/dist/matterbridgeDynamicPlatform.d.ts +0 -40
- package/dist/matterbridgeDynamicPlatform.d.ts.map +0 -1
- package/dist/matterbridgeDynamicPlatform.js.map +0 -1
- package/dist/matterbridgeEndpoint.d.ts +0 -943
- package/dist/matterbridgeEndpoint.d.ts.map +0 -1
- package/dist/matterbridgeEndpoint.js.map +0 -1
- package/dist/matterbridgeEndpointHelpers.d.ts +0 -2706
- package/dist/matterbridgeEndpointHelpers.d.ts.map +0 -1
- package/dist/matterbridgeEndpointHelpers.js.map +0 -1
- package/dist/matterbridgePlatform.d.ts +0 -294
- package/dist/matterbridgePlatform.d.ts.map +0 -1
- package/dist/matterbridgePlatform.js.map +0 -1
- package/dist/matterbridgeTypes.d.ts +0 -187
- package/dist/matterbridgeTypes.d.ts.map +0 -1
- package/dist/matterbridgeTypes.js.map +0 -1
- package/dist/pluginManager.d.ts +0 -271
- package/dist/pluginManager.d.ts.map +0 -1
- package/dist/pluginManager.js.map +0 -1
- package/dist/shelly.d.ts +0 -92
- package/dist/shelly.d.ts.map +0 -1
- package/dist/shelly.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/update.d.ts +0 -32
- package/dist/update.d.ts.map +0 -1
- package/dist/update.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/copyDirectory.d.ts +0 -32
- package/dist/utils/copyDirectory.d.ts.map +0 -1
- package/dist/utils/copyDirectory.js.map +0 -1
- package/dist/utils/createZip.d.ts +0 -38
- package/dist/utils/createZip.d.ts.map +0 -1
- package/dist/utils/createZip.js.map +0 -1
- package/dist/utils/deepCopy.d.ts +0 -31
- package/dist/utils/deepCopy.d.ts.map +0 -1
- package/dist/utils/deepCopy.js.map +0 -1
- package/dist/utils/deepEqual.d.ts +0 -53
- package/dist/utils/deepEqual.d.ts.map +0 -1
- package/dist/utils/deepEqual.js.map +0 -1
- package/dist/utils/export.d.ts +0 -10
- package/dist/utils/export.d.ts.map +0 -1
- package/dist/utils/export.js.map +0 -1
- package/dist/utils/isvalid.d.ts +0 -87
- package/dist/utils/isvalid.d.ts.map +0 -1
- package/dist/utils/isvalid.js.map +0 -1
- package/dist/utils/network.d.ts +0 -69
- package/dist/utils/network.d.ts.map +0 -1
- package/dist/utils/network.js.map +0 -1
- package/dist/utils/parameter.d.ts +0 -58
- package/dist/utils/parameter.d.ts.map +0 -1
- package/dist/utils/parameter.js.map +0 -1
- package/dist/utils/wait.d.ts +0 -43
- package/dist/utils/wait.d.ts.map +0 -1
- package/dist/utils/wait.js.map +0 -1
- /package/frontend/build/static/js/{main.1d983660.js.LICENSE.txt → main.356788d7.js.LICENSE.txt} +0 -0
package/dist/matterbridge.js
CHANGED
|
@@ -1,36 +1,10 @@
|
|
|
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 os from 'node:os';
|
|
25
2
|
import path from 'node:path';
|
|
26
3
|
import { promises as fs } from 'node:fs';
|
|
27
4
|
import EventEmitter from 'node:events';
|
|
28
5
|
import { inspect } from 'node:util';
|
|
29
|
-
// AnsiLogger module
|
|
30
6
|
import { AnsiLogger, UNDERLINE, UNDERLINEOFF, YELLOW, db, debugStringify, BRIGHT, RESET, er, nf, rs, wr, RED, GREEN, zb, CYAN } from './logger/export.js';
|
|
31
|
-
// NodeStorage module
|
|
32
7
|
import { NodeStorageManager } from './storage/export.js';
|
|
33
|
-
// Matterbridge
|
|
34
8
|
import { getParameter, getIntParameter, hasParameter, copyDirectory, withTimeout, waiter } from './utils/export.js';
|
|
35
9
|
import { logInterfaces, getGlobalNodeModules } from './utils/network.js';
|
|
36
10
|
import { PluginManager } from './pluginManager.js';
|
|
@@ -38,19 +12,14 @@ import { DeviceManager } from './deviceManager.js';
|
|
|
38
12
|
import { MatterbridgeEndpoint } from './matterbridgeEndpoint.js';
|
|
39
13
|
import { bridge } from './matterbridgeDeviceTypes.js';
|
|
40
14
|
import { Frontend } from './frontend.js';
|
|
41
|
-
// @matter
|
|
42
15
|
import { DeviceTypeId, Endpoint as EndpointNode, Logger, LogLevel as MatterLogLevel, LogFormat as MatterLogFormat, VendorId, StorageService, Environment, ServerNode } from '@matter/main';
|
|
43
16
|
import { DeviceCommissioner, FabricAction, MdnsService, PaseClient } from '@matter/main/protocol';
|
|
44
17
|
import { AggregatorEndpoint } from '@matter/main/endpoints';
|
|
45
18
|
import { BasicInformationServer } from '@matter/main/behaviors/basic-information';
|
|
46
19
|
import { BridgedDeviceBasicInformationServer } from '@matter/main/behaviors/bridged-device-basic-information';
|
|
47
|
-
// Default colors
|
|
48
20
|
const plg = '\u001B[38;5;33m';
|
|
49
21
|
const dev = '\u001B[38;5;79m';
|
|
50
22
|
const typ = '\u001B[38;5;207m';
|
|
51
|
-
/**
|
|
52
|
-
* Represents the Matterbridge application.
|
|
53
|
-
*/
|
|
54
23
|
export class Matterbridge extends EventEmitter {
|
|
55
24
|
systemInformation = {
|
|
56
25
|
interfaceName: '',
|
|
@@ -97,7 +66,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
97
66
|
shellySysUpdate: false,
|
|
98
67
|
shellyMainUpdate: false,
|
|
99
68
|
profile: getParameter('profile'),
|
|
100
|
-
loggerLevel: "info"
|
|
69
|
+
loggerLevel: "info",
|
|
101
70
|
fileLogger: false,
|
|
102
71
|
matterLoggerLevel: MatterLogLevel.INFO,
|
|
103
72
|
matterFileLogger: false,
|
|
@@ -136,11 +105,9 @@ export class Matterbridge extends EventEmitter {
|
|
|
136
105
|
plugins;
|
|
137
106
|
devices;
|
|
138
107
|
frontend = new Frontend(this);
|
|
139
|
-
// Matterbridge storage
|
|
140
108
|
nodeStorage;
|
|
141
109
|
nodeContext;
|
|
142
110
|
nodeStorageName = 'storage' + (getParameter('profile') ? '.' + getParameter('profile') : '');
|
|
143
|
-
// Cleanup
|
|
144
111
|
hasCleanupStarted = false;
|
|
145
112
|
initialized = false;
|
|
146
113
|
execRunningCount = 0;
|
|
@@ -153,21 +120,18 @@ export class Matterbridge extends EventEmitter {
|
|
|
153
120
|
sigtermHandler;
|
|
154
121
|
exceptionHandler;
|
|
155
122
|
rejectionHandler;
|
|
156
|
-
// Matter environment
|
|
157
123
|
environment = Environment.default;
|
|
158
|
-
// Matter storage
|
|
159
124
|
matterStorageName = 'matterstorage' + (getParameter('profile') ? '.' + getParameter('profile') : '');
|
|
160
125
|
matterStorageService;
|
|
161
126
|
matterStorageManager;
|
|
162
127
|
matterbridgeContext;
|
|
163
128
|
controllerContext;
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
discriminator; // first server node discriminator
|
|
129
|
+
mdnsInterface;
|
|
130
|
+
ipv4address;
|
|
131
|
+
ipv6address;
|
|
132
|
+
port;
|
|
133
|
+
passcode;
|
|
134
|
+
discriminator;
|
|
171
135
|
serverNode;
|
|
172
136
|
aggregatorNode;
|
|
173
137
|
aggregatorVendorId = VendorId(getIntParameter('vendorId') ?? 0xfff1);
|
|
@@ -175,50 +139,21 @@ export class Matterbridge extends EventEmitter {
|
|
|
175
139
|
aggregatorProductId = getIntParameter('productId') ?? 0x8000;
|
|
176
140
|
aggregatorProductName = getParameter('productName') ?? 'Matterbridge aggregator';
|
|
177
141
|
static instance;
|
|
178
|
-
// We load asyncronously so is private
|
|
179
142
|
constructor() {
|
|
180
143
|
super();
|
|
181
144
|
}
|
|
182
|
-
/**
|
|
183
|
-
* Emits an event of the specified type with the provided arguments.
|
|
184
|
-
*
|
|
185
|
-
* @template K - The type of the event.
|
|
186
|
-
* @param {K} eventName - The name of the event to emit.
|
|
187
|
-
* @param {...MatterbridgeEvent[K]} args - The arguments to pass to the event listeners.
|
|
188
|
-
* @returns {boolean} - Returns true if the event had listeners, false otherwise.
|
|
189
|
-
*/
|
|
190
145
|
emit(eventName, ...args) {
|
|
191
146
|
return super.emit(eventName, ...args);
|
|
192
147
|
}
|
|
193
|
-
/**
|
|
194
|
-
* Registers an event listener for the specified event type.
|
|
195
|
-
*
|
|
196
|
-
* @template K - The type of the event.
|
|
197
|
-
* @param {K} eventName - The name of the event to listen for.
|
|
198
|
-
* @param {(...args: MatterbridgeEvent[K]) => void} listener - The callback function to invoke when the event is emitted.
|
|
199
|
-
* @returns {this} - Returns the instance of the Matterbridge class.
|
|
200
|
-
*/
|
|
201
148
|
on(eventName, listener) {
|
|
202
149
|
return super.on(eventName, listener);
|
|
203
150
|
}
|
|
204
|
-
/**
|
|
205
|
-
* Retrieves the list of Matterbridge devices.
|
|
206
|
-
* @returns {MatterbridgeEndpoint[]} An array of MatterbridgeDevice objects.
|
|
207
|
-
*/
|
|
208
151
|
getDevices() {
|
|
209
152
|
return this.devices.array();
|
|
210
153
|
}
|
|
211
|
-
/**
|
|
212
|
-
* Retrieves the list of registered plugins.
|
|
213
|
-
* @returns {RegisteredPlugin[]} An array of RegisteredPlugin objects.
|
|
214
|
-
*/
|
|
215
154
|
getPlugins() {
|
|
216
155
|
return this.plugins.array();
|
|
217
156
|
}
|
|
218
|
-
/**
|
|
219
|
-
* Set the logger logLevel for the Matterbridge classes.
|
|
220
|
-
* @param {LogLevel} logLevel The logger logLevel to set.
|
|
221
|
-
*/
|
|
222
157
|
async setLogLevel(logLevel) {
|
|
223
158
|
if (this.log)
|
|
224
159
|
this.log.logLevel = logLevel;
|
|
@@ -232,31 +167,19 @@ export class Matterbridge extends EventEmitter {
|
|
|
232
167
|
for (const plugin of this.plugins) {
|
|
233
168
|
if (!plugin.platform || !plugin.platform.log || !plugin.platform.config)
|
|
234
169
|
continue;
|
|
235
|
-
plugin.platform.log.logLevel = plugin.platform.config.debug === true ? "debug"
|
|
236
|
-
await plugin.platform.onChangeLoggerLevel(plugin.platform.config.debug === true ? "debug"
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
callbackLogLevel = "debug" /* LogLevel.DEBUG */;
|
|
170
|
+
plugin.platform.log.logLevel = plugin.platform.config.debug === true ? "debug" : this.log.logLevel;
|
|
171
|
+
await plugin.platform.onChangeLoggerLevel(plugin.platform.config.debug === true ? "debug" : this.log.logLevel);
|
|
172
|
+
}
|
|
173
|
+
let callbackLogLevel = "notice";
|
|
174
|
+
if (this.matterbridgeInformation.loggerLevel === "info" || this.matterbridgeInformation.matterLoggerLevel === MatterLogLevel.INFO)
|
|
175
|
+
callbackLogLevel = "info";
|
|
176
|
+
if (this.matterbridgeInformation.loggerLevel === "debug" || this.matterbridgeInformation.matterLoggerLevel === MatterLogLevel.DEBUG)
|
|
177
|
+
callbackLogLevel = "debug";
|
|
244
178
|
AnsiLogger.setGlobalCallback(this.frontend.wssSendMessage.bind(this.frontend), callbackLogLevel);
|
|
245
179
|
this.log.debug(`WebSocketServer logger global callback set to ${callbackLogLevel}`);
|
|
246
180
|
}
|
|
247
|
-
/** ***********************************************************************************************************************************/
|
|
248
|
-
/** loadInstance() and cleanup() methods */
|
|
249
|
-
/** ***********************************************************************************************************************************/
|
|
250
|
-
/**
|
|
251
|
-
* Loads an instance of the Matterbridge class.
|
|
252
|
-
* If an instance already exists, return that instance.
|
|
253
|
-
*
|
|
254
|
-
* @param initialize - Whether to initialize the Matterbridge instance after loading.
|
|
255
|
-
* @returns The loaded Matterbridge instance.
|
|
256
|
-
*/
|
|
257
181
|
static async loadInstance(initialize = false) {
|
|
258
182
|
if (!Matterbridge.instance) {
|
|
259
|
-
// eslint-disable-next-line no-console
|
|
260
183
|
if (hasParameter('debug'))
|
|
261
184
|
console.log(GREEN + 'Creating a new instance of Matterbridge.', initialize ? 'Initializing...' : 'Not initializing...', rs);
|
|
262
185
|
Matterbridge.instance = new Matterbridge();
|
|
@@ -265,14 +188,8 @@ export class Matterbridge extends EventEmitter {
|
|
|
265
188
|
}
|
|
266
189
|
return Matterbridge.instance;
|
|
267
190
|
}
|
|
268
|
-
/**
|
|
269
|
-
* Call cleanup().
|
|
270
|
-
* @deprecated This method is deprecated and is only used for jest tests.
|
|
271
|
-
*
|
|
272
|
-
*/
|
|
273
191
|
async destroyInstance() {
|
|
274
192
|
this.log.info(`Destroy instance...`);
|
|
275
|
-
// Save server nodes to close
|
|
276
193
|
const servers = [];
|
|
277
194
|
if (this.bridgeMode === 'bridge') {
|
|
278
195
|
if (this.serverNode)
|
|
@@ -284,81 +201,55 @@ export class Matterbridge extends EventEmitter {
|
|
|
284
201
|
servers.push(plugin.serverNode);
|
|
285
202
|
}
|
|
286
203
|
}
|
|
287
|
-
// Cleanup
|
|
288
204
|
await this.cleanup('destroying instance...', false);
|
|
289
|
-
// Close servers mdns service
|
|
290
205
|
this.log.info(`Dispose ${servers.length} MdnsService...`);
|
|
291
206
|
for (const server of servers) {
|
|
292
207
|
await server.env.get(MdnsService)[Symbol.asyncDispose]();
|
|
293
208
|
this.log.info(`Closed ${server.id} MdnsService`);
|
|
294
209
|
}
|
|
295
|
-
// Wait for the cleanup to finish
|
|
296
210
|
await new Promise((resolve) => {
|
|
297
211
|
setTimeout(resolve, 1000);
|
|
298
212
|
});
|
|
299
213
|
}
|
|
300
|
-
/**
|
|
301
|
-
* Initializes the Matterbridge application.
|
|
302
|
-
*
|
|
303
|
-
* @remarks
|
|
304
|
-
* This method performs the necessary setup and initialization steps for the Matterbridge application.
|
|
305
|
-
* It displays the help information if the 'help' parameter is provided, sets up the logger, checks the
|
|
306
|
-
* node version, registers signal handlers, initializes storage, and parses the command line.
|
|
307
|
-
*
|
|
308
|
-
* @returns A Promise that resolves when the initialization is complete.
|
|
309
|
-
*/
|
|
310
214
|
async initialize() {
|
|
311
|
-
// Set the restart mode
|
|
312
215
|
if (hasParameter('service'))
|
|
313
216
|
this.restartMode = 'service';
|
|
314
217
|
if (hasParameter('docker'))
|
|
315
218
|
this.restartMode = 'docker';
|
|
316
|
-
// Set the matterbridge directory
|
|
317
219
|
this.homeDirectory = getParameter('homedir') ?? os.homedir();
|
|
318
220
|
this.matterbridgeDirectory = path.join(this.homeDirectory, '.matterbridge');
|
|
319
|
-
// Setup the matter environment
|
|
320
221
|
this.environment.vars.set('log.level', MatterLogLevel.INFO);
|
|
321
222
|
this.environment.vars.set('log.format', MatterLogFormat.ANSI);
|
|
322
223
|
this.environment.vars.set('path.root', path.join(this.matterbridgeDirectory, this.matterStorageName));
|
|
323
224
|
this.environment.vars.set('runtime.signals', false);
|
|
324
225
|
this.environment.vars.set('runtime.exitcode', false);
|
|
325
|
-
|
|
326
|
-
this.log = new AnsiLogger({ logName: 'Matterbridge', logTimestampFormat: 4 /* TimestampFormat.TIME_MILLIS */, logLevel: hasParameter('debug') ? "debug" /* LogLevel.DEBUG */ : "info" /* LogLevel.INFO */ });
|
|
327
|
-
// Register process handlers
|
|
226
|
+
this.log = new AnsiLogger({ logName: 'Matterbridge', logTimestampFormat: 4, logLevel: hasParameter('debug') ? "debug" : "info" });
|
|
328
227
|
this.registerProcessHandlers();
|
|
329
|
-
// Initialize nodeStorage and nodeContext
|
|
330
228
|
try {
|
|
331
229
|
this.log.debug(`Creating node storage manager: ${CYAN}${this.nodeStorageName}${db}`);
|
|
332
230
|
this.nodeStorage = new NodeStorageManager({ dir: path.join(this.matterbridgeDirectory, this.nodeStorageName), writeQueue: false, expiredInterval: undefined, logging: false });
|
|
333
231
|
this.log.debug('Creating node storage context for matterbridge');
|
|
334
232
|
this.nodeContext = await this.nodeStorage.createStorage('matterbridge');
|
|
335
|
-
// TODO: Remove this code when node-persist-manager is updated
|
|
336
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
337
233
|
const keys = (await this.nodeStorage?.storage.keys());
|
|
338
234
|
for (const key of keys) {
|
|
339
235
|
this.log.debug(`Checking node storage manager key: ${CYAN}${key}${db}`);
|
|
340
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
341
236
|
await this.nodeStorage?.storage.get(key);
|
|
342
237
|
}
|
|
343
238
|
const storages = await this.nodeStorage.getStorageNames();
|
|
344
239
|
for (const storage of storages) {
|
|
345
240
|
this.log.debug(`Checking storage: ${CYAN}${storage}${db}`);
|
|
346
241
|
const nodeContext = await this.nodeStorage?.createStorage(storage);
|
|
347
|
-
// TODO: Remove this code when node-persist-manager is updated
|
|
348
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
349
242
|
const keys = (await nodeContext?.storage.keys());
|
|
350
243
|
keys.forEach(async (key) => {
|
|
351
244
|
this.log.debug(`Checking key: ${CYAN}${storage}:${key}${db}`);
|
|
352
245
|
await nodeContext?.get(key);
|
|
353
246
|
});
|
|
354
247
|
}
|
|
355
|
-
// Creating a backup of the node storage since it is not corrupted
|
|
356
248
|
this.log.debug('Creating node storage backup...');
|
|
357
249
|
await copyDirectory(path.join(this.matterbridgeDirectory, this.nodeStorageName), path.join(this.matterbridgeDirectory, this.nodeStorageName + '.backup'));
|
|
358
250
|
this.log.debug('Created node storage backup');
|
|
359
251
|
}
|
|
360
252
|
catch (error) {
|
|
361
|
-
// Restoring the backup of the node storage since it is corrupted
|
|
362
253
|
this.log.error(`Error creating node storage manager and context: ${error instanceof Error ? error.message : error}`);
|
|
363
254
|
if (hasParameter('norestore')) {
|
|
364
255
|
this.log.fatal(`The matterbridge node storage is corrupted. Parameter -norestore found: exiting...`);
|
|
@@ -373,46 +264,41 @@ export class Matterbridge extends EventEmitter {
|
|
|
373
264
|
this.log.fatal('Fatal error creating node storage manager and context for matterbridge');
|
|
374
265
|
throw new Error('Fatal error creating node storage manager and context for matterbridge');
|
|
375
266
|
}
|
|
376
|
-
// Set the first port to use for the commissioning server (will be incremented in childbridge mode)
|
|
377
267
|
this.port = getIntParameter('port') ?? (await this.nodeContext.get('matterport', 5540)) ?? 5540;
|
|
378
|
-
// Set the first passcode to use for the commissioning server (will be incremented in childbridge mode)
|
|
379
268
|
this.passcode = getIntParameter('passcode') ?? (await this.nodeContext.get('matterpasscode')) ?? PaseClient.generateRandomPasscode();
|
|
380
|
-
// Set the first discriminator to use for the commissioning server (will be incremented in childbridge mode)
|
|
381
269
|
this.discriminator = getIntParameter('discriminator') ?? (await this.nodeContext.get('matterdiscriminator')) ?? PaseClient.generateRandomDiscriminator();
|
|
382
270
|
this.log.debug(`Initializing server node for Matterbridge... on port ${this.port} with passcode ${this.passcode} and discriminator ${this.discriminator}`);
|
|
383
|
-
// Set matterbridge logger level (context: matterbridgeLogLevel)
|
|
384
271
|
if (hasParameter('logger')) {
|
|
385
272
|
const level = getParameter('logger');
|
|
386
273
|
if (level === 'debug') {
|
|
387
|
-
this.log.logLevel = "debug"
|
|
274
|
+
this.log.logLevel = "debug";
|
|
388
275
|
}
|
|
389
276
|
else if (level === 'info') {
|
|
390
|
-
this.log.logLevel = "info"
|
|
277
|
+
this.log.logLevel = "info";
|
|
391
278
|
}
|
|
392
279
|
else if (level === 'notice') {
|
|
393
|
-
this.log.logLevel = "notice"
|
|
280
|
+
this.log.logLevel = "notice";
|
|
394
281
|
}
|
|
395
282
|
else if (level === 'warn') {
|
|
396
|
-
this.log.logLevel = "warn"
|
|
283
|
+
this.log.logLevel = "warn";
|
|
397
284
|
}
|
|
398
285
|
else if (level === 'error') {
|
|
399
|
-
this.log.logLevel = "error"
|
|
286
|
+
this.log.logLevel = "error";
|
|
400
287
|
}
|
|
401
288
|
else if (level === 'fatal') {
|
|
402
|
-
this.log.logLevel = "fatal"
|
|
289
|
+
this.log.logLevel = "fatal";
|
|
403
290
|
}
|
|
404
291
|
else {
|
|
405
292
|
this.log.warn(`Invalid matterbridge logger level: ${level}. Using default level "info".`);
|
|
406
|
-
this.log.logLevel = "info"
|
|
293
|
+
this.log.logLevel = "info";
|
|
407
294
|
}
|
|
408
295
|
}
|
|
409
296
|
else {
|
|
410
|
-
this.log.logLevel = await this.nodeContext.get('matterbridgeLogLevel', this.matterbridgeInformation.shellyBoard ? "notice"
|
|
297
|
+
this.log.logLevel = await this.nodeContext.get('matterbridgeLogLevel', this.matterbridgeInformation.shellyBoard ? "notice" : "info");
|
|
411
298
|
}
|
|
412
299
|
this.frontend.logLevel = this.log.logLevel;
|
|
413
300
|
MatterbridgeEndpoint.logLevel = this.log.logLevel;
|
|
414
301
|
this.matterbridgeInformation.loggerLevel = this.log.logLevel;
|
|
415
|
-
// Create the file logger for matterbridge (context: matterbridgeFileLog)
|
|
416
302
|
if (hasParameter('filelogger') || (await this.nodeContext.get('matterbridgeFileLog', false))) {
|
|
417
303
|
AnsiLogger.setGlobalLogfile(path.join(this.matterbridgeDirectory, this.matterbrideLoggerFile), this.log.logLevel, true);
|
|
418
304
|
this.matterbridgeInformation.fileLogger = true;
|
|
@@ -421,7 +307,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
421
307
|
this.log.debug(`Matterbridge logLevel: ${this.log.logLevel} fileLoger: ${this.matterbridgeInformation.fileLogger}.`);
|
|
422
308
|
if (this.profile !== undefined)
|
|
423
309
|
this.log.debug(`Matterbridge profile: ${this.profile}.`);
|
|
424
|
-
// Set matter.js logger level, format and logger (context: matterLogLevel)
|
|
425
310
|
if (hasParameter('matterlogger')) {
|
|
426
311
|
const level = getParameter('matterlogger');
|
|
427
312
|
if (level === 'debug') {
|
|
@@ -453,7 +338,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
453
338
|
Logger.format = MatterLogFormat.ANSI;
|
|
454
339
|
Logger.setLogger('default', this.createMatterLogger());
|
|
455
340
|
this.matterbridgeInformation.matterLoggerLevel = Logger.defaultLogLevel;
|
|
456
|
-
// Create the file logger for matter.js (context: matterFileLog)
|
|
457
341
|
if (hasParameter('matterfilelogger') || (await this.nodeContext.get('matterFileLog', false))) {
|
|
458
342
|
this.matterbridgeInformation.matterFileLogger = true;
|
|
459
343
|
Logger.addLogger('matterfilelogger', await this.createMatterFileLogger(path.join(this.matterbridgeDirectory, this.matterLoggerFile), true), {
|
|
@@ -462,7 +346,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
462
346
|
});
|
|
463
347
|
}
|
|
464
348
|
this.log.debug(`Matter logLevel: ${Logger.defaultLogLevel} fileLoger: ${this.matterbridgeInformation.matterFileLogger}.`);
|
|
465
|
-
// Log network interfaces
|
|
466
349
|
const networkInterfaces = os.networkInterfaces();
|
|
467
350
|
const availableAddresses = Object.entries(networkInterfaces);
|
|
468
351
|
const availableInterfaces = Object.keys(networkInterfaces);
|
|
@@ -474,7 +357,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
474
357
|
});
|
|
475
358
|
}
|
|
476
359
|
}
|
|
477
|
-
// Set the interface to use for matter server node mdnsInterface
|
|
478
360
|
if (hasParameter('mdnsinterface')) {
|
|
479
361
|
this.mdnsInterface = getParameter('mdnsinterface');
|
|
480
362
|
}
|
|
@@ -483,7 +365,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
483
365
|
if (this.mdnsInterface === '')
|
|
484
366
|
this.mdnsInterface = undefined;
|
|
485
367
|
}
|
|
486
|
-
// Validate mdnsInterface
|
|
487
368
|
if (this.mdnsInterface) {
|
|
488
369
|
if (!availableInterfaces.includes(this.mdnsInterface)) {
|
|
489
370
|
this.log.error(`Invalid mdnsInterface: ${this.mdnsInterface}. Available interfaces are: ${availableInterfaces.join(', ')}. Using all available interfaces.`);
|
|
@@ -495,7 +376,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
495
376
|
}
|
|
496
377
|
if (this.mdnsInterface)
|
|
497
378
|
this.environment.vars.set('mdns.networkInterface', this.mdnsInterface);
|
|
498
|
-
// Set the listeningAddressIpv4 for the matter commissioning server
|
|
499
379
|
if (hasParameter('ipv4address')) {
|
|
500
380
|
this.ipv4address = getParameter('ipv4address');
|
|
501
381
|
}
|
|
@@ -504,7 +384,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
504
384
|
if (this.ipv4address === '')
|
|
505
385
|
this.ipv4address = undefined;
|
|
506
386
|
}
|
|
507
|
-
// Validate ipv4address
|
|
508
387
|
if (this.ipv4address) {
|
|
509
388
|
let isValid = false;
|
|
510
389
|
for (const [ifaceName, ifaces] of availableAddresses) {
|
|
@@ -519,7 +398,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
519
398
|
this.ipv4address = undefined;
|
|
520
399
|
}
|
|
521
400
|
}
|
|
522
|
-
// Set the listeningAddressIpv6 for the matter commissioning server
|
|
523
401
|
if (hasParameter('ipv6address')) {
|
|
524
402
|
this.ipv6address = getParameter('ipv6address');
|
|
525
403
|
}
|
|
@@ -528,7 +406,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
528
406
|
if (this.ipv6address === '')
|
|
529
407
|
this.ipv6address = undefined;
|
|
530
408
|
}
|
|
531
|
-
// Validate ipv6address
|
|
532
409
|
if (this.ipv6address) {
|
|
533
410
|
let isValid = false;
|
|
534
411
|
for (const [ifaceName, ifaces] of availableAddresses) {
|
|
@@ -548,19 +425,14 @@ export class Matterbridge extends EventEmitter {
|
|
|
548
425
|
this.ipv6address = undefined;
|
|
549
426
|
}
|
|
550
427
|
}
|
|
551
|
-
// Initialize PluginManager
|
|
552
428
|
this.plugins = new PluginManager(this);
|
|
553
429
|
await this.plugins.loadFromStorage();
|
|
554
430
|
this.plugins.logLevel = this.log.logLevel;
|
|
555
|
-
// Initialize DeviceManager
|
|
556
431
|
this.devices = new DeviceManager(this, this.nodeContext);
|
|
557
432
|
this.devices.logLevel = this.log.logLevel;
|
|
558
|
-
// Get the plugins from node storage and create the plugins node storage contexts
|
|
559
433
|
for (const plugin of this.plugins) {
|
|
560
434
|
const packageJson = await this.plugins.parse(plugin);
|
|
561
435
|
if (packageJson === null && !hasParameter('add') && !hasParameter('remove') && !hasParameter('enable') && !hasParameter('disable') && !hasParameter('reset') && !hasParameter('factoryreset')) {
|
|
562
|
-
// Try to reinstall the plugin from npm (for Docker pull and external plugins)
|
|
563
|
-
// We don't do this when the add and other parameters are set because we shut down the process after adding the plugin
|
|
564
436
|
this.log.info(`Error parsing plugin ${plg}${plugin.name}${nf}. Trying to reinstall it from npm.`);
|
|
565
437
|
try {
|
|
566
438
|
await this.spawnCommand('npm', ['install', '-g', plugin.name, '--omit=dev', '--verbose']);
|
|
@@ -582,7 +454,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
582
454
|
await plugin.nodeContext.set('description', plugin.description);
|
|
583
455
|
await plugin.nodeContext.set('author', plugin.author);
|
|
584
456
|
}
|
|
585
|
-
// Log system info and create .matterbridge directory
|
|
586
457
|
await this.logNodeAndSystemInfo();
|
|
587
458
|
this.log.notice(`Matterbridge version ${this.matterbridgeVersion} ` +
|
|
588
459
|
`${hasParameter('bridge') || (!hasParameter('childbridge') && (await this.nodeContext?.get('bridgeMode', '')) === 'bridge') ? 'mode bridge ' : ''}` +
|
|
@@ -590,7 +461,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
590
461
|
`${hasParameter('controller') ? 'mode controller ' : ''}` +
|
|
591
462
|
`${this.restartMode !== '' ? 'restart mode ' + this.restartMode + ' ' : ''}` +
|
|
592
463
|
`running on ${this.systemInformation.osType} (v.${this.systemInformation.osRelease}) platform ${this.systemInformation.osPlatform} arch ${this.systemInformation.osArch}`);
|
|
593
|
-
// Check node version and throw error
|
|
594
464
|
const minNodeVersion = 18;
|
|
595
465
|
const nodeVersion = process.versions.node;
|
|
596
466
|
const versionMajor = parseInt(nodeVersion.split('.')[0]);
|
|
@@ -598,15 +468,9 @@ export class Matterbridge extends EventEmitter {
|
|
|
598
468
|
this.log.error(`Node version ${versionMajor} is not supported. Please upgrade to ${minNodeVersion} or above.`);
|
|
599
469
|
throw new Error(`Node version ${versionMajor} is not supported. Please upgrade to ${minNodeVersion} or above.`);
|
|
600
470
|
}
|
|
601
|
-
// Parse command line
|
|
602
471
|
await this.parseCommandLine();
|
|
603
472
|
this.initialized = true;
|
|
604
473
|
}
|
|
605
|
-
/**
|
|
606
|
-
* Parses the command line arguments and performs the corresponding actions.
|
|
607
|
-
* @private
|
|
608
|
-
* @returns {Promise<void>} A promise that resolves when the command line arguments have been processed, or the process exits.
|
|
609
|
-
*/
|
|
610
474
|
async parseCommandLine() {
|
|
611
475
|
if (hasParameter('help')) {
|
|
612
476
|
this.log.info(`\nUsage: matterbridge [options]\n
|
|
@@ -725,7 +589,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
725
589
|
this.shutdown = true;
|
|
726
590
|
return;
|
|
727
591
|
}
|
|
728
|
-
// Start the matter storage and create the matterbridge context
|
|
729
592
|
try {
|
|
730
593
|
await this.startMatterStorage();
|
|
731
594
|
}
|
|
@@ -733,14 +596,12 @@ export class Matterbridge extends EventEmitter {
|
|
|
733
596
|
this.log.fatal(`Fatal error creating matter storage: ${error instanceof Error ? error.message : error}`);
|
|
734
597
|
throw new Error(`Fatal error creating matter storage: ${error instanceof Error ? error.message : error}`);
|
|
735
598
|
}
|
|
736
|
-
// Clear the matterbridge context if the reset parameter is set
|
|
737
599
|
if (hasParameter('reset') && getParameter('reset') === undefined) {
|
|
738
600
|
this.initialized = true;
|
|
739
601
|
await this.shutdownProcessAndReset();
|
|
740
602
|
this.shutdown = true;
|
|
741
603
|
return;
|
|
742
604
|
}
|
|
743
|
-
// Clear matterbridge plugin context if the reset parameter is set
|
|
744
605
|
if (hasParameter('reset') && getParameter('reset') !== undefined) {
|
|
745
606
|
this.log.debug(`Reset plugin ${getParameter('reset')}`);
|
|
746
607
|
const plugin = this.plugins.get(getParameter('reset'));
|
|
@@ -765,37 +626,30 @@ export class Matterbridge extends EventEmitter {
|
|
|
765
626
|
this.shutdown = true;
|
|
766
627
|
return;
|
|
767
628
|
}
|
|
768
|
-
// Initialize frontend
|
|
769
629
|
if (getIntParameter('frontend') !== 0 || getIntParameter('frontend') === undefined)
|
|
770
630
|
await this.frontend.start(getIntParameter('frontend'));
|
|
771
|
-
// Check in 30 seconds the latest versions
|
|
772
631
|
this.checkUpdateTimeout = setTimeout(async () => {
|
|
773
632
|
const { checkUpdates } = await import('./update.js');
|
|
774
633
|
checkUpdates(this);
|
|
775
634
|
}, 30 * 1000).unref();
|
|
776
|
-
// Check each 24 hours the latest versions
|
|
777
635
|
this.checkUpdateInterval = setInterval(async () => {
|
|
778
636
|
const { checkUpdates } = await import('./update.js');
|
|
779
637
|
checkUpdates(this);
|
|
780
638
|
}, 12 * 60 * 60 * 1000).unref();
|
|
781
|
-
// Start the matterbridge in mode test
|
|
782
639
|
if (hasParameter('test')) {
|
|
783
640
|
this.bridgeMode = 'bridge';
|
|
784
641
|
MatterbridgeEndpoint.bridgeMode = 'bridge';
|
|
785
642
|
return;
|
|
786
643
|
}
|
|
787
|
-
// Start the matterbridge in mode controller
|
|
788
644
|
if (hasParameter('controller')) {
|
|
789
645
|
this.bridgeMode = 'controller';
|
|
790
646
|
await this.startController();
|
|
791
647
|
return;
|
|
792
648
|
}
|
|
793
|
-
// Check if the bridge mode is set and start matterbridge in bridge mode if not set
|
|
794
649
|
if (!hasParameter('bridge') && !hasParameter('childbridge') && (await this.nodeContext?.get('bridgeMode', '')) === '') {
|
|
795
650
|
this.log.info('Setting default matterbridge start mode to bridge');
|
|
796
651
|
await this.nodeContext?.set('bridgeMode', 'bridge');
|
|
797
652
|
}
|
|
798
|
-
// Start matterbridge in bridge mode
|
|
799
653
|
if (hasParameter('bridge') || (!hasParameter('childbridge') && (await this.nodeContext?.get('bridgeMode', '')) === 'bridge')) {
|
|
800
654
|
this.bridgeMode = 'bridge';
|
|
801
655
|
MatterbridgeEndpoint.bridgeMode = 'bridge';
|
|
@@ -803,7 +657,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
803
657
|
await this.startBridge();
|
|
804
658
|
return;
|
|
805
659
|
}
|
|
806
|
-
// Start matterbridge in childbridge mode
|
|
807
660
|
if (hasParameter('childbridge') || (!hasParameter('bridge') && (await this.nodeContext?.get('bridgeMode', '')) === 'childbridge')) {
|
|
808
661
|
this.bridgeMode = 'childbridge';
|
|
809
662
|
MatterbridgeEndpoint.bridgeMode = 'childbridge';
|
|
@@ -812,20 +665,10 @@ export class Matterbridge extends EventEmitter {
|
|
|
812
665
|
return;
|
|
813
666
|
}
|
|
814
667
|
}
|
|
815
|
-
/**
|
|
816
|
-
* Asynchronously loads and starts the registered plugins.
|
|
817
|
-
*
|
|
818
|
-
* This method is responsible for initializing and staarting all enabled plugins.
|
|
819
|
-
* It ensures that each plugin is properly loaded and started before the bridge starts.
|
|
820
|
-
*
|
|
821
|
-
* @returns {Promise<void>} A promise that resolves when all plugins have been loaded and started.
|
|
822
|
-
*/
|
|
823
668
|
async startPlugins() {
|
|
824
|
-
// Check, load and start the plugins
|
|
825
669
|
for (const plugin of this.plugins) {
|
|
826
670
|
plugin.configJson = await this.plugins.loadConfig(plugin);
|
|
827
671
|
plugin.schemaJson = await this.plugins.loadSchema(plugin);
|
|
828
|
-
// Check if the plugin is available
|
|
829
672
|
if (!(await this.plugins.resolve(plugin.path))) {
|
|
830
673
|
this.log.error(`Plugin ${plg}${plugin.name}${er} not found or not validated. Disabling it.`);
|
|
831
674
|
plugin.enabled = false;
|
|
@@ -845,14 +688,10 @@ export class Matterbridge extends EventEmitter {
|
|
|
845
688
|
plugin.addedDevices = undefined;
|
|
846
689
|
plugin.qrPairingCode = undefined;
|
|
847
690
|
plugin.manualPairingCode = undefined;
|
|
848
|
-
this.plugins.load(plugin, true, 'Matterbridge is starting');
|
|
691
|
+
this.plugins.load(plugin, true, 'Matterbridge is starting');
|
|
849
692
|
}
|
|
850
693
|
this.frontend.wssSendRefreshRequired('plugins');
|
|
851
694
|
}
|
|
852
|
-
/**
|
|
853
|
-
* Registers the process handlers for uncaughtException, unhandledRejection, SIGINT and SIGTERM.
|
|
854
|
-
* When either of these signals are received, the cleanup method is called with an appropriate message.
|
|
855
|
-
*/
|
|
856
695
|
registerProcessHandlers() {
|
|
857
696
|
this.log.debug(`Registering uncaughtException and unhandledRejection handlers...`);
|
|
858
697
|
process.removeAllListeners('uncaughtException');
|
|
@@ -879,9 +718,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
879
718
|
};
|
|
880
719
|
process.on('SIGTERM', this.sigtermHandler);
|
|
881
720
|
}
|
|
882
|
-
/**
|
|
883
|
-
* Deregisters the process uncaughtException, unhandledRejection, SIGINT and SIGTERM signal handlers.
|
|
884
|
-
*/
|
|
885
721
|
deregisterProcesslHandlers() {
|
|
886
722
|
this.log.debug(`Deregistering uncaughtException and unhandledRejection handlers...`);
|
|
887
723
|
if (this.exceptionHandler)
|
|
@@ -898,17 +734,12 @@ export class Matterbridge extends EventEmitter {
|
|
|
898
734
|
process.off('SIGTERM', this.sigtermHandler);
|
|
899
735
|
this.sigtermHandler = undefined;
|
|
900
736
|
}
|
|
901
|
-
/**
|
|
902
|
-
* Logs the node and system information.
|
|
903
|
-
*/
|
|
904
737
|
async logNodeAndSystemInfo() {
|
|
905
|
-
// IP address information
|
|
906
738
|
const networkInterfaces = os.networkInterfaces();
|
|
907
739
|
this.systemInformation.interfaceName = '';
|
|
908
740
|
this.systemInformation.ipv4Address = '';
|
|
909
741
|
this.systemInformation.ipv6Address = '';
|
|
910
742
|
for (const [interfaceName, interfaceDetails] of Object.entries(networkInterfaces)) {
|
|
911
|
-
// this.log.debug(`Checking interface: '${interfaceName}' for '${this.mdnsInterface}'`);
|
|
912
743
|
if (this.mdnsInterface && interfaceName !== this.mdnsInterface)
|
|
913
744
|
continue;
|
|
914
745
|
if (!interfaceDetails) {
|
|
@@ -934,22 +765,19 @@ export class Matterbridge extends EventEmitter {
|
|
|
934
765
|
break;
|
|
935
766
|
}
|
|
936
767
|
}
|
|
937
|
-
// Node information
|
|
938
768
|
this.systemInformation.nodeVersion = process.versions.node;
|
|
939
769
|
const versionMajor = parseInt(this.systemInformation.nodeVersion.split('.')[0]);
|
|
940
770
|
const versionMinor = parseInt(this.systemInformation.nodeVersion.split('.')[1]);
|
|
941
771
|
const versionPatch = parseInt(this.systemInformation.nodeVersion.split('.')[2]);
|
|
942
|
-
// Host system information
|
|
943
772
|
this.systemInformation.hostname = os.hostname();
|
|
944
773
|
this.systemInformation.user = os.userInfo().username;
|
|
945
|
-
this.systemInformation.osType = os.type();
|
|
946
|
-
this.systemInformation.osRelease = os.release();
|
|
947
|
-
this.systemInformation.osPlatform = os.platform();
|
|
948
|
-
this.systemInformation.osArch = os.arch();
|
|
949
|
-
this.systemInformation.totalMemory = (os.totalmem() / 1024 / 1024 / 1024).toFixed(2) + ' GB';
|
|
950
|
-
this.systemInformation.freeMemory = (os.freemem() / 1024 / 1024 / 1024).toFixed(2) + ' GB';
|
|
951
|
-
this.systemInformation.systemUptime = (os.uptime() / 60 / 60).toFixed(2) + ' hours';
|
|
952
|
-
// Log the system information
|
|
774
|
+
this.systemInformation.osType = os.type();
|
|
775
|
+
this.systemInformation.osRelease = os.release();
|
|
776
|
+
this.systemInformation.osPlatform = os.platform();
|
|
777
|
+
this.systemInformation.osArch = os.arch();
|
|
778
|
+
this.systemInformation.totalMemory = (os.totalmem() / 1024 / 1024 / 1024).toFixed(2) + ' GB';
|
|
779
|
+
this.systemInformation.freeMemory = (os.freemem() / 1024 / 1024 / 1024).toFixed(2) + ' GB';
|
|
780
|
+
this.systemInformation.systemUptime = (os.uptime() / 60 / 60).toFixed(2) + ' hours';
|
|
953
781
|
this.log.debug('Host System Information:');
|
|
954
782
|
this.log.debug(`- Hostname: ${this.systemInformation.hostname}`);
|
|
955
783
|
this.log.debug(`- User: ${this.systemInformation.user}`);
|
|
@@ -965,20 +793,16 @@ export class Matterbridge extends EventEmitter {
|
|
|
965
793
|
this.log.debug(`- Total Memory: ${this.systemInformation.totalMemory}`);
|
|
966
794
|
this.log.debug(`- Free Memory: ${this.systemInformation.freeMemory}`);
|
|
967
795
|
this.log.debug(`- System Uptime: ${this.systemInformation.systemUptime}`);
|
|
968
|
-
// Home directory
|
|
969
796
|
this.homeDirectory = getParameter('homedir') ?? os.homedir();
|
|
970
797
|
this.matterbridgeInformation.homeDirectory = this.homeDirectory;
|
|
971
798
|
this.log.debug(`Home Directory: ${this.homeDirectory}`);
|
|
972
|
-
// Package root directory
|
|
973
799
|
const { fileURLToPath } = await import('node:url');
|
|
974
800
|
const currentFileDirectory = path.dirname(fileURLToPath(import.meta.url));
|
|
975
801
|
this.rootDirectory = path.resolve(currentFileDirectory, '../');
|
|
976
802
|
this.matterbridgeInformation.rootDirectory = this.rootDirectory;
|
|
977
803
|
this.log.debug(`Root Directory: ${this.rootDirectory}`);
|
|
978
|
-
// Global node_modules directory
|
|
979
804
|
if (this.nodeContext)
|
|
980
805
|
this.globalModulesDirectory = this.matterbridgeInformation.globalModulesDirectory = await this.nodeContext.get('globalModulesDirectory', '');
|
|
981
|
-
// First run of Matterbridge so the node storage is empty
|
|
982
806
|
if (this.globalModulesDirectory === '') {
|
|
983
807
|
try {
|
|
984
808
|
this.execRunningCount++;
|
|
@@ -994,20 +818,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
994
818
|
}
|
|
995
819
|
else
|
|
996
820
|
this.log.debug(`Global node_modules Directory: ${this.globalModulesDirectory}`);
|
|
997
|
-
/* removed cause is too expensive for the shelly board and not really needed. Why should it change the globalModulesDirectory?
|
|
998
|
-
else {
|
|
999
|
-
this.getGlobalNodeModules()
|
|
1000
|
-
.then(async (globalModulesDirectory) => {
|
|
1001
|
-
this.globalModulesDirectory = globalModulesDirectory;
|
|
1002
|
-
this.matterbridgeInformation.globalModulesDirectory = this.globalModulesDirectory;
|
|
1003
|
-
this.log.debug(`Global node_modules Directory: ${this.globalModulesDirectory}`);
|
|
1004
|
-
await this.nodeContext?.set<string>('globalModulesDirectory', this.globalModulesDirectory);
|
|
1005
|
-
})
|
|
1006
|
-
.catch((error) => {
|
|
1007
|
-
this.log.error(`Error getting global node_modules directory: ${error}`);
|
|
1008
|
-
});
|
|
1009
|
-
}*/
|
|
1010
|
-
// Create the data directory .matterbridge in the home directory
|
|
1011
821
|
this.matterbridgeDirectory = path.join(this.homeDirectory, '.matterbridge');
|
|
1012
822
|
this.matterbridgeInformation.matterbridgeDirectory = this.matterbridgeDirectory;
|
|
1013
823
|
try {
|
|
@@ -1031,7 +841,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1031
841
|
}
|
|
1032
842
|
}
|
|
1033
843
|
this.log.debug(`Matterbridge Directory: ${this.matterbridgeDirectory}`);
|
|
1034
|
-
// Create the plugin directory Matterbridge in the home directory
|
|
1035
844
|
this.matterbridgePluginDirectory = path.join(this.homeDirectory, 'Matterbridge');
|
|
1036
845
|
this.matterbridgeInformation.matterbridgePluginDirectory = this.matterbridgePluginDirectory;
|
|
1037
846
|
try {
|
|
@@ -1055,7 +864,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1055
864
|
}
|
|
1056
865
|
}
|
|
1057
866
|
this.log.debug(`Matterbridge Plugin Directory: ${this.matterbridgePluginDirectory}`);
|
|
1058
|
-
// Create the matter cert directory in the home directory
|
|
1059
867
|
this.matterbridgeCertDirectory = path.join(this.homeDirectory, '.mattercert');
|
|
1060
868
|
this.matterbridgeInformation.matterbridgeCertDirectory = this.matterbridgeCertDirectory;
|
|
1061
869
|
try {
|
|
@@ -1079,68 +887,50 @@ export class Matterbridge extends EventEmitter {
|
|
|
1079
887
|
}
|
|
1080
888
|
}
|
|
1081
889
|
this.log.debug(`Matterbridge Matter Cert Directory: ${this.matterbridgeCertDirectory}`);
|
|
1082
|
-
// Matterbridge version
|
|
1083
890
|
const packageJson = JSON.parse(await fs.readFile(path.join(this.rootDirectory, 'package.json'), 'utf-8'));
|
|
1084
891
|
this.matterbridgeVersion = this.matterbridgeLatestVersion = packageJson.version;
|
|
1085
892
|
this.matterbridgeInformation.matterbridgeVersion = this.matterbridgeInformation.matterbridgeLatestVersion = this.matterbridgeVersion;
|
|
1086
893
|
this.log.debug(`Matterbridge Version: ${this.matterbridgeVersion}`);
|
|
1087
|
-
// Matterbridge latest version
|
|
1088
894
|
if (this.nodeContext)
|
|
1089
895
|
this.matterbridgeLatestVersion = await this.nodeContext.get('matterbridgeLatestVersion', this.matterbridgeVersion);
|
|
1090
896
|
this.log.debug(`Matterbridge Latest Version: ${this.matterbridgeLatestVersion}`);
|
|
1091
|
-
// this.getMatterbridgeLatestVersion();
|
|
1092
|
-
// Current working directory
|
|
1093
897
|
const currentDir = process.cwd();
|
|
1094
898
|
this.log.debug(`Current Working Directory: ${currentDir}`);
|
|
1095
|
-
// Command line arguments (excluding 'node' and the script name)
|
|
1096
899
|
const cmdArgs = process.argv.slice(2).join(' ');
|
|
1097
900
|
this.log.debug(`Command Line Arguments: ${cmdArgs}`);
|
|
1098
901
|
}
|
|
1099
|
-
/**
|
|
1100
|
-
* Creates a MatterLogger function to show the matter.js log messages in AnsiLogger (for the frontend).
|
|
1101
|
-
*
|
|
1102
|
-
* @returns {Function} The MatterLogger function.
|
|
1103
|
-
*/
|
|
1104
902
|
createMatterLogger() {
|
|
1105
|
-
const matterLogger = new AnsiLogger({ logName: 'Matter', logTimestampFormat: 4
|
|
903
|
+
const matterLogger = new AnsiLogger({ logName: 'Matter', logTimestampFormat: 4, logLevel: "debug" });
|
|
1106
904
|
return (_level, formattedLog) => {
|
|
1107
905
|
const logger = formattedLog.slice(44, 44 + 20).trim();
|
|
1108
906
|
const message = formattedLog.slice(65);
|
|
1109
907
|
matterLogger.logName = logger;
|
|
1110
908
|
switch (_level) {
|
|
1111
909
|
case MatterLogLevel.DEBUG:
|
|
1112
|
-
matterLogger.log("debug"
|
|
910
|
+
matterLogger.log("debug", message);
|
|
1113
911
|
break;
|
|
1114
912
|
case MatterLogLevel.INFO:
|
|
1115
|
-
matterLogger.log("info"
|
|
913
|
+
matterLogger.log("info", message);
|
|
1116
914
|
break;
|
|
1117
915
|
case MatterLogLevel.NOTICE:
|
|
1118
|
-
matterLogger.log("notice"
|
|
916
|
+
matterLogger.log("notice", message);
|
|
1119
917
|
break;
|
|
1120
918
|
case MatterLogLevel.WARN:
|
|
1121
|
-
matterLogger.log("warn"
|
|
919
|
+
matterLogger.log("warn", message);
|
|
1122
920
|
break;
|
|
1123
921
|
case MatterLogLevel.ERROR:
|
|
1124
|
-
matterLogger.log("error"
|
|
922
|
+
matterLogger.log("error", message);
|
|
1125
923
|
break;
|
|
1126
924
|
case MatterLogLevel.FATAL:
|
|
1127
|
-
matterLogger.log("fatal"
|
|
925
|
+
matterLogger.log("fatal", message);
|
|
1128
926
|
break;
|
|
1129
927
|
default:
|
|
1130
|
-
matterLogger.log("debug"
|
|
928
|
+
matterLogger.log("debug", message);
|
|
1131
929
|
break;
|
|
1132
930
|
}
|
|
1133
931
|
};
|
|
1134
932
|
}
|
|
1135
|
-
/**
|
|
1136
|
-
* Creates a Matter File Logger.
|
|
1137
|
-
*
|
|
1138
|
-
* @param {string} filePath - The path to the log file.
|
|
1139
|
-
* @param {boolean} [unlink=false] - Whether to unlink the log file before creating a new one.
|
|
1140
|
-
* @returns {Function} - A function that logs formatted messages to the log file.
|
|
1141
|
-
*/
|
|
1142
933
|
async createMatterFileLogger(filePath, unlink = false) {
|
|
1143
|
-
// 2024-08-21 08:55:19.488 DEBUG InteractionMessenger Sending DataReport chunk with 28 attributes and 0 events: 1004 bytes
|
|
1144
934
|
let fileSize = 0;
|
|
1145
935
|
if (unlink) {
|
|
1146
936
|
try {
|
|
@@ -1189,21 +979,12 @@ export class Matterbridge extends EventEmitter {
|
|
|
1189
979
|
}
|
|
1190
980
|
};
|
|
1191
981
|
}
|
|
1192
|
-
/**
|
|
1193
|
-
* Restarts the process by exiting the current instance and loading a new instance.
|
|
1194
|
-
*/
|
|
1195
982
|
async restartProcess() {
|
|
1196
983
|
await this.cleanup('restarting...', true);
|
|
1197
984
|
}
|
|
1198
|
-
/**
|
|
1199
|
-
* Shut down the process by exiting the current process.
|
|
1200
|
-
*/
|
|
1201
985
|
async shutdownProcess() {
|
|
1202
986
|
await this.cleanup('shutting down...', false);
|
|
1203
987
|
}
|
|
1204
|
-
/**
|
|
1205
|
-
* Update matterbridge and and shut down the process.
|
|
1206
|
-
*/
|
|
1207
988
|
async updateProcess() {
|
|
1208
989
|
this.log.info('Updating matterbridge...');
|
|
1209
990
|
try {
|
|
@@ -1216,72 +997,51 @@ export class Matterbridge extends EventEmitter {
|
|
|
1216
997
|
this.frontend.wssSendRestartRequired();
|
|
1217
998
|
await this.cleanup('updating...', false);
|
|
1218
999
|
}
|
|
1219
|
-
/**
|
|
1220
|
-
* Unregister all devices and shut down the process.
|
|
1221
|
-
*/
|
|
1222
1000
|
async unregisterAndShutdownProcess() {
|
|
1223
1001
|
this.log.info('Unregistering all devices and shutting down...');
|
|
1224
1002
|
for (const plugin of this.plugins) {
|
|
1225
1003
|
await this.removeAllBridgedEndpoints(plugin.name, 250);
|
|
1226
1004
|
}
|
|
1227
1005
|
this.log.debug('Waiting for the MessageExchange to finish...');
|
|
1228
|
-
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
1006
|
+
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
1229
1007
|
this.log.debug('Cleaning up and shutting down...');
|
|
1230
1008
|
await this.cleanup('unregistered all devices and shutting down...', false);
|
|
1231
1009
|
}
|
|
1232
|
-
/**
|
|
1233
|
-
* Reset commissioning and shut down the process.
|
|
1234
|
-
*/
|
|
1235
1010
|
async shutdownProcessAndReset() {
|
|
1236
1011
|
await this.cleanup('shutting down with reset...', false);
|
|
1237
1012
|
}
|
|
1238
|
-
/**
|
|
1239
|
-
* Factory reset and shut down the process.
|
|
1240
|
-
*/
|
|
1241
1013
|
async shutdownProcessAndFactoryReset() {
|
|
1242
1014
|
await this.cleanup('shutting down with factory reset...', false);
|
|
1243
1015
|
}
|
|
1244
|
-
/**
|
|
1245
|
-
* Cleans up the Matterbridge instance.
|
|
1246
|
-
* @param message - The cleanup message.
|
|
1247
|
-
* @param restart - Indicates whether to restart the instance after cleanup. Default is `false`.
|
|
1248
|
-
* @returns A promise that resolves when the cleanup is completed.
|
|
1249
|
-
*/
|
|
1250
1016
|
async cleanup(message, restart = false) {
|
|
1251
1017
|
if (this.initialized && !this.hasCleanupStarted) {
|
|
1252
1018
|
this.hasCleanupStarted = true;
|
|
1253
1019
|
this.log.info(message);
|
|
1254
|
-
// Clear the start matter interval
|
|
1255
1020
|
if (this.startMatterInterval) {
|
|
1256
1021
|
clearInterval(this.startMatterInterval);
|
|
1257
1022
|
this.startMatterInterval = undefined;
|
|
1258
1023
|
this.log.debug('Start matter interval cleared');
|
|
1259
1024
|
}
|
|
1260
|
-
// Clear the check update timeout
|
|
1261
1025
|
if (this.checkUpdateTimeout) {
|
|
1262
1026
|
clearInterval(this.checkUpdateTimeout);
|
|
1263
1027
|
this.checkUpdateTimeout = undefined;
|
|
1264
1028
|
this.log.debug('Check update timeout cleared');
|
|
1265
1029
|
}
|
|
1266
|
-
// Clear the check update interval
|
|
1267
1030
|
if (this.checkUpdateInterval) {
|
|
1268
1031
|
clearInterval(this.checkUpdateInterval);
|
|
1269
1032
|
this.checkUpdateInterval = undefined;
|
|
1270
1033
|
this.log.debug('Check update interval cleared');
|
|
1271
1034
|
}
|
|
1272
|
-
// Clear the configure timeout
|
|
1273
1035
|
if (this.configureTimeout) {
|
|
1274
1036
|
clearTimeout(this.configureTimeout);
|
|
1275
1037
|
this.configureTimeout = undefined;
|
|
1276
1038
|
this.log.debug('Matterbridge configure timeout cleared');
|
|
1277
1039
|
}
|
|
1278
|
-
// Clear the reachability timeout
|
|
1279
1040
|
if (this.reachabilityTimeout) {
|
|
1280
1041
|
clearTimeout(this.reachabilityTimeout);
|
|
1281
1042
|
this.reachabilityTimeout = undefined;
|
|
1282
1043
|
this.log.debug('Matterbridge reachability timeout cleared');
|
|
1283
1044
|
}
|
|
1284
|
-
// Calling the shutdown method of each plugin and clear the plugins reachability timeout
|
|
1285
1045
|
for (const plugin of this.plugins) {
|
|
1286
1046
|
if (!plugin.enabled || plugin.error)
|
|
1287
1047
|
continue;
|
|
@@ -1292,10 +1052,9 @@ export class Matterbridge extends EventEmitter {
|
|
|
1292
1052
|
this.log.debug(`Plugin ${plg}${plugin.name}${db} reachability timeout cleared`);
|
|
1293
1053
|
}
|
|
1294
1054
|
}
|
|
1295
|
-
// Stopping matter server nodes
|
|
1296
1055
|
this.log.notice(`Stopping matter server nodes in ${this.bridgeMode} mode...`);
|
|
1297
1056
|
this.log.debug('Waiting for the MessageExchange to finish...');
|
|
1298
|
-
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
1057
|
+
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
1299
1058
|
if (this.bridgeMode === 'bridge') {
|
|
1300
1059
|
if (this.serverNode) {
|
|
1301
1060
|
await this.stopServerNode(this.serverNode);
|
|
@@ -1311,7 +1070,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1311
1070
|
}
|
|
1312
1071
|
}
|
|
1313
1072
|
this.log.notice('Stopped matter server nodes');
|
|
1314
|
-
// Matter commisioning reset
|
|
1315
1073
|
if (message === 'shutting down with reset...') {
|
|
1316
1074
|
this.log.info('Resetting Matterbridge commissioning information...');
|
|
1317
1075
|
await this.matterStorageManager?.createContext('events')?.clearAll();
|
|
@@ -1321,37 +1079,17 @@ export class Matterbridge extends EventEmitter {
|
|
|
1321
1079
|
await this.matterbridgeContext?.clearAll();
|
|
1322
1080
|
this.log.info('Matter storage reset done! Remove the bridge from the controller.');
|
|
1323
1081
|
}
|
|
1324
|
-
// Stop matter storage
|
|
1325
1082
|
await this.stopMatterStorage();
|
|
1326
|
-
// Stop the frontend
|
|
1327
1083
|
await this.frontend.stop();
|
|
1328
|
-
// Remove the matterfilelogger
|
|
1329
1084
|
try {
|
|
1330
1085
|
Logger.removeLogger('matterfilelogger');
|
|
1331
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
1332
1086
|
}
|
|
1333
1087
|
catch (error) {
|
|
1334
|
-
// this.log.debug(`Error removing the matterfilelogger for file ${CYAN}${path.join(this.matterbridgeDirectory, this.matterLoggerFile)}${er}: ${error instanceof Error ? error.message : error}`);
|
|
1335
1088
|
}
|
|
1336
|
-
// Serialize registeredDevices
|
|
1337
1089
|
if (this.nodeStorage && this.nodeContext) {
|
|
1338
|
-
/*
|
|
1339
|
-
TODO: Implement serialization of registered devices in edge mode
|
|
1340
|
-
this.log.info('Saving registered devices...');
|
|
1341
|
-
const serializedRegisteredDevices: SerializedMatterbridgeEndpoint[] = [];
|
|
1342
|
-
this.devices.forEach(async (device) => {
|
|
1343
|
-
const serializedMatterbridgeDevice = MatterbridgeEndpoint.serialize(device);
|
|
1344
|
-
// this.log.info(`- ${serializedMatterbridgeDevice.deviceName}${rs}\n`, serializedMatterbridgeDevice);
|
|
1345
|
-
if (serializedMatterbridgeDevice) serializedRegisteredDevices.push(serializedMatterbridgeDevice);
|
|
1346
|
-
});
|
|
1347
|
-
await this.nodeContext.set<SerializedMatterbridgeEndpoint[]>('devices', serializedRegisteredDevices);
|
|
1348
|
-
this.log.info(`Saved registered devices (${serializedRegisteredDevices?.length})`);
|
|
1349
|
-
*/
|
|
1350
|
-
// Clear nodeContext and nodeStorage (they just need 1000ms to write the data to disk)
|
|
1351
1090
|
this.log.debug(`Closing node storage context for ${plg}Matterbridge${db}...`);
|
|
1352
1091
|
await this.nodeContext.close();
|
|
1353
1092
|
this.nodeContext = undefined;
|
|
1354
|
-
// Clear nodeContext for each plugin (they just need 1000ms to write the data to disk)
|
|
1355
1093
|
for (const plugin of this.plugins) {
|
|
1356
1094
|
if (plugin.nodeContext) {
|
|
1357
1095
|
this.log.debug(`Closing node storage context for plugin ${plg}${plugin.name}${db}...`);
|
|
@@ -1368,10 +1106,8 @@ export class Matterbridge extends EventEmitter {
|
|
|
1368
1106
|
}
|
|
1369
1107
|
this.plugins.clear();
|
|
1370
1108
|
this.devices.clear();
|
|
1371
|
-
// Factory reset
|
|
1372
1109
|
if (message === 'shutting down with factory reset...') {
|
|
1373
1110
|
try {
|
|
1374
|
-
// Delete old matter storage file and backup
|
|
1375
1111
|
const file = path.join(this.matterbridgeDirectory, 'matterbridge' + (getParameter('profile') ? '.' + getParameter('profile') : '') + '.json');
|
|
1376
1112
|
this.log.info(`Unlinking old matter storage file: ${file}`);
|
|
1377
1113
|
await fs.unlink(file);
|
|
@@ -1385,7 +1121,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1385
1121
|
}
|
|
1386
1122
|
}
|
|
1387
1123
|
try {
|
|
1388
|
-
// Delete matter node storage directory with its subdirectories and backup
|
|
1389
1124
|
const dir = path.join(this.matterbridgeDirectory, 'matterstorage' + (getParameter('profile') ? '.' + getParameter('profile') : ''));
|
|
1390
1125
|
this.log.info(`Removing matter node storage directory: ${dir}`);
|
|
1391
1126
|
await fs.rm(dir, { recursive: true });
|
|
@@ -1399,7 +1134,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1399
1134
|
}
|
|
1400
1135
|
}
|
|
1401
1136
|
try {
|
|
1402
|
-
// Delete node storage directory with its subdirectories and backup
|
|
1403
1137
|
const dir = path.join(this.matterbridgeDirectory, 'storage' + (getParameter('profile') ? '.' + getParameter('profile') : ''));
|
|
1404
1138
|
this.log.info(`Removing storage directory: ${dir}`);
|
|
1405
1139
|
await fs.rm(dir, { recursive: true });
|
|
@@ -1414,13 +1148,12 @@ export class Matterbridge extends EventEmitter {
|
|
|
1414
1148
|
}
|
|
1415
1149
|
this.log.info('Factory reset done! Remove all paired fabrics from the controllers.');
|
|
1416
1150
|
}
|
|
1417
|
-
// Deregisters the process handlers
|
|
1418
1151
|
this.deregisterProcesslHandlers();
|
|
1419
1152
|
if (restart) {
|
|
1420
1153
|
if (message === 'updating...') {
|
|
1421
1154
|
this.log.info('Cleanup completed. Updating...');
|
|
1422
1155
|
Matterbridge.instance = undefined;
|
|
1423
|
-
this.emit('update');
|
|
1156
|
+
this.emit('update');
|
|
1424
1157
|
}
|
|
1425
1158
|
else if (message === 'restarting...') {
|
|
1426
1159
|
this.log.info('Cleanup completed. Restarting...');
|
|
@@ -1440,14 +1173,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1440
1173
|
this.log.debug('Cleanup already started...');
|
|
1441
1174
|
}
|
|
1442
1175
|
}
|
|
1443
|
-
/**
|
|
1444
|
-
* Creates and configures the server node for an accessory plugin for a given device.
|
|
1445
|
-
*
|
|
1446
|
-
* @param {RegisteredPlugin} plugin - The plugin to configure.
|
|
1447
|
-
* @param {MatterbridgeEndpoint} device - The device to associate with the plugin.
|
|
1448
|
-
* @param {boolean} [start=false] - Whether to start the server node after adding the device.
|
|
1449
|
-
* @returns {Promise<void>} A promise that resolves when the server node for the accessory plugin is created and configured.
|
|
1450
|
-
*/
|
|
1451
1176
|
async createAccessoryPlugin(plugin, device, start = false) {
|
|
1452
1177
|
if (!plugin.locked && device.deviceName && device.vendorId && device.productId && device.vendorName && device.productName) {
|
|
1453
1178
|
plugin.locked = true;
|
|
@@ -1461,13 +1186,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1461
1186
|
await this.startServerNode(plugin.serverNode);
|
|
1462
1187
|
}
|
|
1463
1188
|
}
|
|
1464
|
-
/**
|
|
1465
|
-
* Creates and configures the server node for a dynamic plugin.
|
|
1466
|
-
*
|
|
1467
|
-
* @param {RegisteredPlugin} plugin - The plugin to configure.
|
|
1468
|
-
* @param {boolean} [start=false] - Whether to start the server node after adding the aggregator node.
|
|
1469
|
-
* @returns {Promise<void>} A promise that resolves when the server node for the dynamic plugin is created and configured.
|
|
1470
|
-
*/
|
|
1471
1189
|
async createDynamicPlugin(plugin, start = false) {
|
|
1472
1190
|
if (!plugin.locked) {
|
|
1473
1191
|
plugin.locked = true;
|
|
@@ -1480,13 +1198,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
1480
1198
|
await this.startServerNode(plugin.serverNode);
|
|
1481
1199
|
}
|
|
1482
1200
|
}
|
|
1483
|
-
/**
|
|
1484
|
-
* Starts the Matterbridge in bridge mode.
|
|
1485
|
-
* @private
|
|
1486
|
-
* @returns {Promise<void>} A promise that resolves when the Matterbridge is started.
|
|
1487
|
-
*/
|
|
1488
1201
|
async startBridge() {
|
|
1489
|
-
// Plugins are configured by a timer when matter server is started and plugin.configured is set to true
|
|
1490
1202
|
if (!this.matterStorageManager)
|
|
1491
1203
|
throw new Error('No storage manager initialized');
|
|
1492
1204
|
if (!this.matterbridgeContext)
|
|
@@ -1524,9 +1236,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
1524
1236
|
clearInterval(this.startMatterInterval);
|
|
1525
1237
|
this.startMatterInterval = undefined;
|
|
1526
1238
|
this.log.debug('Cleared startMatterInterval interval for Matterbridge');
|
|
1527
|
-
// Start the Matter server node
|
|
1528
1239
|
this.startServerNode(this.serverNode);
|
|
1529
|
-
// Configure the plugins
|
|
1530
1240
|
this.configureTimeout = setTimeout(async () => {
|
|
1531
1241
|
for (const plugin of this.plugins) {
|
|
1532
1242
|
if (!plugin.enabled || !plugin.loaded || !plugin.started || plugin.error)
|
|
@@ -1544,7 +1254,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1544
1254
|
}
|
|
1545
1255
|
this.frontend.wssSendRefreshRequired('plugins');
|
|
1546
1256
|
}, 30 * 1000);
|
|
1547
|
-
// Setting reachability to true
|
|
1548
1257
|
this.reachabilityTimeout = setTimeout(() => {
|
|
1549
1258
|
this.log.info(`Setting reachability to true for ${plg}Matterbridge${db}`);
|
|
1550
1259
|
if (this.aggregatorNode)
|
|
@@ -1553,11 +1262,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1553
1262
|
}, 60 * 1000);
|
|
1554
1263
|
}, 1000);
|
|
1555
1264
|
}
|
|
1556
|
-
/**
|
|
1557
|
-
* Starts the Matterbridge in childbridge mode.
|
|
1558
|
-
* @private
|
|
1559
|
-
* @returns {Promise<void>} A promise that resolves when the Matterbridge is started.
|
|
1560
|
-
*/
|
|
1561
1265
|
async startChildbridge() {
|
|
1562
1266
|
if (!this.matterStorageManager)
|
|
1563
1267
|
throw new Error('No storage manager initialized');
|
|
@@ -1595,7 +1299,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1595
1299
|
clearInterval(this.startMatterInterval);
|
|
1596
1300
|
this.startMatterInterval = undefined;
|
|
1597
1301
|
this.log.debug('Cleared startMatterInterval interval in childbridge mode');
|
|
1598
|
-
// Configure the plugins
|
|
1599
1302
|
this.configureTimeout = setTimeout(async () => {
|
|
1600
1303
|
for (const plugin of this.plugins) {
|
|
1601
1304
|
if (!plugin.enabled || !plugin.loaded || !plugin.started || plugin.error)
|
|
@@ -1632,9 +1335,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
1632
1335
|
this.log.error(`Node storage context not found for plugin ${plg}${plugin.name}${er}`);
|
|
1633
1336
|
continue;
|
|
1634
1337
|
}
|
|
1635
|
-
// Start the Matter server node
|
|
1636
1338
|
this.startServerNode(plugin.serverNode);
|
|
1637
|
-
// Setting reachability to true
|
|
1638
1339
|
plugin.reachabilityTimeout = setTimeout(() => {
|
|
1639
1340
|
this.log.info(`Setting reachability to true for ${plg}${plugin.name}${nf} type ${plugin.type} server node ${plugin.serverNode !== undefined} aggregator node ${plugin.aggregatorNode !== undefined} device ${plugin.device !== undefined}`);
|
|
1640
1341
|
if (plugin.type === 'DynamicPlatform' && plugin.aggregatorNode)
|
|
@@ -1644,11 +1345,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1644
1345
|
}
|
|
1645
1346
|
}, 1000);
|
|
1646
1347
|
}
|
|
1647
|
-
/**
|
|
1648
|
-
* Starts the Matterbridge controller.
|
|
1649
|
-
* @private
|
|
1650
|
-
* @returns {Promise<void>} A promise that resolves when the Matterbridge is started.
|
|
1651
|
-
*/
|
|
1652
1348
|
async startController() {
|
|
1653
1349
|
if (!this.matterStorageManager) {
|
|
1654
1350
|
this.log.error('No storage manager initialized');
|
|
@@ -1663,207 +1359,8 @@ export class Matterbridge extends EventEmitter {
|
|
|
1663
1359
|
return;
|
|
1664
1360
|
}
|
|
1665
1361
|
this.log.debug('Starting matterbridge in mode', this.bridgeMode);
|
|
1666
|
-
/*
|
|
1667
|
-
this.matterServer = await this.createMatterServer(this.storageManager);
|
|
1668
|
-
this.log.info('Creating matter commissioning controller');
|
|
1669
|
-
this.commissioningController = new CommissioningController({
|
|
1670
|
-
autoConnect: false,
|
|
1671
|
-
});
|
|
1672
|
-
this.log.info('Adding matter commissioning controller to matter server');
|
|
1673
|
-
await this.matterServer.addCommissioningController(this.commissioningController);
|
|
1674
|
-
|
|
1675
|
-
this.log.info('Starting matter server');
|
|
1676
|
-
await this.matterServer.start();
|
|
1677
|
-
this.log.info('Matter server started');
|
|
1678
|
-
const commissioningOptions: ControllerCommissioningFlowOptions = {
|
|
1679
|
-
regulatoryLocation: GeneralCommissioning.RegulatoryLocationType.IndoorOutdoor,
|
|
1680
|
-
regulatoryCountryCode: 'XX',
|
|
1681
|
-
};
|
|
1682
|
-
const commissioningController = new CommissioningController({
|
|
1683
|
-
environment: {
|
|
1684
|
-
environment,
|
|
1685
|
-
id: uniqueId,
|
|
1686
|
-
},
|
|
1687
|
-
autoConnect: false, // Do not auto connect to the commissioned nodes
|
|
1688
|
-
adminFabricLabel,
|
|
1689
|
-
});
|
|
1690
|
-
|
|
1691
|
-
if (hasParameter('pairingcode')) {
|
|
1692
|
-
this.log.info('Pairing device with pairingcode:', getParameter('pairingcode'));
|
|
1693
|
-
const pairingCode = getParameter('pairingcode');
|
|
1694
|
-
const ip = this.controllerContext.has('ip') ? this.controllerContext.get<string>('ip') : undefined;
|
|
1695
|
-
const port = this.controllerContext.has('port') ? this.controllerContext.get<number>('port') : undefined;
|
|
1696
|
-
|
|
1697
|
-
let longDiscriminator, setupPin, shortDiscriminator;
|
|
1698
|
-
if (pairingCode !== undefined) {
|
|
1699
|
-
const pairingCodeCodec = ManualPairingCodeCodec.decode(pairingCode);
|
|
1700
|
-
shortDiscriminator = pairingCodeCodec.shortDiscriminator;
|
|
1701
|
-
longDiscriminator = undefined;
|
|
1702
|
-
setupPin = pairingCodeCodec.passcode;
|
|
1703
|
-
this.log.info(`Data extracted from pairing code: ${Logger.toJSON(pairingCodeCodec)}`);
|
|
1704
|
-
} else {
|
|
1705
|
-
longDiscriminator = await this.controllerContext.get('longDiscriminator', 3840);
|
|
1706
|
-
if (longDiscriminator > 4095) throw new Error('Discriminator value must be less than 4096');
|
|
1707
|
-
setupPin = this.controllerContext.get('pin', 20202021);
|
|
1708
|
-
}
|
|
1709
|
-
if ((shortDiscriminator === undefined && longDiscriminator === undefined) || setupPin === undefined) {
|
|
1710
|
-
throw new Error('Please specify the longDiscriminator of the device to commission with -longDiscriminator or provide a valid passcode with -passcode');
|
|
1711
|
-
}
|
|
1712
|
-
|
|
1713
|
-
const options = {
|
|
1714
|
-
commissioning: commissioningOptions,
|
|
1715
|
-
discovery: {
|
|
1716
|
-
knownAddress: ip !== undefined && port !== undefined ? { ip, port, type: 'udp' } : undefined,
|
|
1717
|
-
identifierData: longDiscriminator !== undefined ? { longDiscriminator } : shortDiscriminator !== undefined ? { shortDiscriminator } : {},
|
|
1718
|
-
},
|
|
1719
|
-
passcode: setupPin,
|
|
1720
|
-
} as NodeCommissioningOptions;
|
|
1721
|
-
this.log.info('Commissioning with options:', options);
|
|
1722
|
-
const nodeId = await this.commissioningController.commissionNode(options);
|
|
1723
|
-
this.log.info(`Commissioning successfully done with nodeId: ${nodeId}`);
|
|
1724
|
-
this.log.info('ActiveSessionInformation:', this.commissioningController.getActiveSessionInformation());
|
|
1725
|
-
} // (hasParameter('pairingcode'))
|
|
1726
|
-
|
|
1727
|
-
if (hasParameter('unpairall')) {
|
|
1728
|
-
this.log.info('***Commissioning controller unpairing all nodes...');
|
|
1729
|
-
const nodeIds = this.commissioningController.getCommissionedNodes();
|
|
1730
|
-
for (const nodeId of nodeIds) {
|
|
1731
|
-
this.log.info('***Commissioning controller unpairing node:', nodeId);
|
|
1732
|
-
await this.commissioningController.removeNode(nodeId);
|
|
1733
|
-
}
|
|
1734
|
-
return;
|
|
1735
|
-
}
|
|
1736
|
-
|
|
1737
|
-
if (hasParameter('discover')) {
|
|
1738
|
-
// const discover = await this.commissioningController.discoverCommissionableDevices({ productId: 0x8000, deviceType: 0xfff1 });
|
|
1739
|
-
// console.log(discover);
|
|
1740
|
-
}
|
|
1741
|
-
|
|
1742
|
-
if (!this.commissioningController.isCommissioned()) {
|
|
1743
|
-
this.log.info('***Commissioning controller is not commissioned: use matterbridge -controller -pairingcode [pairingcode] to commission a device');
|
|
1744
|
-
return;
|
|
1745
|
-
}
|
|
1746
|
-
|
|
1747
|
-
const nodeIds = this.commissioningController.getCommissionedNodes();
|
|
1748
|
-
this.log.info(`***Commissioning controller is commissioned ${this.commissioningController.isCommissioned()} and has ${nodeIds.length} nodes commisioned: `);
|
|
1749
|
-
for (const nodeId of nodeIds) {
|
|
1750
|
-
this.log.info(`***Connecting to commissioned node: ${nodeId}`);
|
|
1751
|
-
|
|
1752
|
-
const node = await this.commissioningController.connectNode(nodeId, {
|
|
1753
|
-
autoSubscribe: false,
|
|
1754
|
-
attributeChangedCallback: (peerNodeId, { path: { nodeId, clusterId, endpointId, attributeName }, value }) =>
|
|
1755
|
-
this.log.info(`***Commissioning controller attributeChangedCallback ${peerNodeId}: attribute ${nodeId}/${endpointId}/${clusterId}/${attributeName} changed to ${Logger.toJSON(value)}`),
|
|
1756
|
-
eventTriggeredCallback: (peerNodeId, { path: { nodeId, clusterId, endpointId, eventName }, events }) =>
|
|
1757
|
-
this.log.info(`***Commissioning controller eventTriggeredCallback ${peerNodeId}: Event ${nodeId}/${endpointId}/${clusterId}/${eventName} triggered with ${Logger.toJSON(events)}`),
|
|
1758
|
-
stateInformationCallback: (peerNodeId, info) => {
|
|
1759
|
-
switch (info) {
|
|
1760
|
-
case NodeStateInformation.Connected:
|
|
1761
|
-
this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} connected`);
|
|
1762
|
-
break;
|
|
1763
|
-
case NodeStateInformation.Disconnected:
|
|
1764
|
-
this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} disconnected`);
|
|
1765
|
-
break;
|
|
1766
|
-
case NodeStateInformation.Reconnecting:
|
|
1767
|
-
this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} reconnecting`);
|
|
1768
|
-
break;
|
|
1769
|
-
case NodeStateInformation.WaitingForDeviceDiscovery:
|
|
1770
|
-
this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} waiting for device discovery`);
|
|
1771
|
-
break;
|
|
1772
|
-
case NodeStateInformation.StructureChanged:
|
|
1773
|
-
this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} structure changed`);
|
|
1774
|
-
break;
|
|
1775
|
-
case NodeStateInformation.Decommissioned:
|
|
1776
|
-
this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} decommissioned`);
|
|
1777
|
-
break;
|
|
1778
|
-
default:
|
|
1779
|
-
this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} NodeStateInformation.${info}`);
|
|
1780
|
-
break;
|
|
1781
|
-
}
|
|
1782
|
-
},
|
|
1783
|
-
});
|
|
1784
|
-
|
|
1785
|
-
node.logStructure();
|
|
1786
|
-
|
|
1787
|
-
// Get the interaction client
|
|
1788
|
-
this.log.info('Getting the interaction client');
|
|
1789
|
-
const interactionClient = await node.getInteractionClient();
|
|
1790
|
-
let cluster;
|
|
1791
|
-
let attributes;
|
|
1792
|
-
|
|
1793
|
-
// Log BasicInformationCluster
|
|
1794
|
-
cluster = BasicInformationCluster;
|
|
1795
|
-
attributes = await interactionClient.getMultipleAttributes({
|
|
1796
|
-
attributes: [{ clusterId: cluster.id }],
|
|
1797
|
-
});
|
|
1798
|
-
if (attributes.length > 0) this.log.info(`Cluster: ${idn}${cluster.name}${rs}${nf} attributes:`);
|
|
1799
|
-
attributes.forEach((attribute) => {
|
|
1800
|
-
this.log.info(
|
|
1801
|
-
`- 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}`,
|
|
1802
|
-
);
|
|
1803
|
-
});
|
|
1804
|
-
|
|
1805
|
-
// Log PowerSourceCluster
|
|
1806
|
-
cluster = PowerSourceCluster;
|
|
1807
|
-
attributes = await interactionClient.getMultipleAttributes({
|
|
1808
|
-
attributes: [{ clusterId: cluster.id }],
|
|
1809
|
-
});
|
|
1810
|
-
if (attributes.length > 0) this.log.info(`Cluster: ${idn}${cluster.name}${rs}${nf} attributes:`);
|
|
1811
|
-
attributes.forEach((attribute) => {
|
|
1812
|
-
this.log.info(
|
|
1813
|
-
`- 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}`,
|
|
1814
|
-
);
|
|
1815
|
-
});
|
|
1816
|
-
|
|
1817
|
-
// Log ThreadNetworkDiagnostics
|
|
1818
|
-
cluster = ThreadNetworkDiagnosticsCluster;
|
|
1819
|
-
attributes = await interactionClient.getMultipleAttributes({
|
|
1820
|
-
attributes: [{ clusterId: cluster.id }],
|
|
1821
|
-
});
|
|
1822
|
-
if (attributes.length > 0) this.log.info(`Cluster: ${idn}${cluster.name}${rs}${nf} attributes:`);
|
|
1823
|
-
attributes.forEach((attribute) => {
|
|
1824
|
-
this.log.info(
|
|
1825
|
-
`- 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}`,
|
|
1826
|
-
);
|
|
1827
|
-
});
|
|
1828
|
-
|
|
1829
|
-
// Log SwitchCluster
|
|
1830
|
-
cluster = SwitchCluster;
|
|
1831
|
-
attributes = await interactionClient.getMultipleAttributes({
|
|
1832
|
-
attributes: [{ clusterId: cluster.id }],
|
|
1833
|
-
});
|
|
1834
|
-
if (attributes.length > 0) this.log.info(`Cluster: ${idn}${cluster.name}${rs}${nf} attributes:`);
|
|
1835
|
-
attributes.forEach((attribute) => {
|
|
1836
|
-
this.log.info(
|
|
1837
|
-
`- 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}`,
|
|
1838
|
-
);
|
|
1839
|
-
});
|
|
1840
|
-
|
|
1841
|
-
this.log.info('Subscribing to all attributes and events');
|
|
1842
|
-
await node.subscribeAllAttributesAndEvents({
|
|
1843
|
-
ignoreInitialTriggers: false,
|
|
1844
|
-
attributeChangedCallback: ({ path: { nodeId, clusterId, endpointId, attributeName }, version, value }) =>
|
|
1845
|
-
this.log.info(
|
|
1846
|
-
`***${db}Commissioning controller attributeChangedCallback version ${version}: attribute ${BLUE}${nodeId}${db}/${or}${endpointId}${db}/${hk}${getClusterNameById(clusterId)}${db}/${zb}${attributeName}${db} changed to ${typeof value === 'object' ? debugStringify(value ?? { none: true }) : value}`,
|
|
1847
|
-
),
|
|
1848
|
-
eventTriggeredCallback: ({ path: { nodeId, clusterId, endpointId, eventName }, events }) => {
|
|
1849
|
-
this.log.info(
|
|
1850
|
-
`***${db}Commissioning controller eventTriggeredCallback: event ${BLUE}${nodeId}${db}/${or}${endpointId}${db}/${hk}${getClusterNameById(clusterId)}${db}/${zb}${eventName}${db} triggered with ${debugStringify(events ?? { none: true })}`,
|
|
1851
|
-
);
|
|
1852
|
-
},
|
|
1853
|
-
});
|
|
1854
|
-
this.log.info('Subscribed to all attributes and events');
|
|
1855
|
-
}
|
|
1856
|
-
*/
|
|
1857
1362
|
}
|
|
1858
|
-
/** ***********************************************************************************************************************************/
|
|
1859
|
-
/** Matter.js methods */
|
|
1860
|
-
/** ***********************************************************************************************************************************/
|
|
1861
|
-
/**
|
|
1862
|
-
* Starts the matter storage process with name Matterbridge.
|
|
1863
|
-
* @returns {Promise<void>} - A promise that resolves when the storage process is started.
|
|
1864
|
-
*/
|
|
1865
1363
|
async startMatterStorage() {
|
|
1866
|
-
// Setup Matter storage
|
|
1867
1364
|
this.log.info(`Starting matter node storage...`);
|
|
1868
1365
|
this.matterStorageService = this.environment.get(StorageService);
|
|
1869
1366
|
this.log.info(`Matter node storage service created: ${this.matterStorageService.location}`);
|
|
@@ -1872,25 +1369,13 @@ export class Matterbridge extends EventEmitter {
|
|
|
1872
1369
|
this.matterbridgeContext = await this.createServerNodeContext('Matterbridge', 'Matterbridge', bridge.code, this.aggregatorVendorId, this.aggregatorVendorName, this.aggregatorProductId, this.aggregatorProductName);
|
|
1873
1370
|
this.matterbridgeInformation.matterbridgeSerialNumber = await this.matterbridgeContext.get('serialNumber', '');
|
|
1874
1371
|
this.log.info('Matter node storage started');
|
|
1875
|
-
// Backup matter storage since it is created/opened correctly
|
|
1876
1372
|
await this.backupMatterStorage(path.join(this.matterbridgeDirectory, this.matterStorageName), path.join(this.matterbridgeDirectory, this.matterStorageName + '.backup'));
|
|
1877
1373
|
}
|
|
1878
|
-
/**
|
|
1879
|
-
* Makes a backup copy of the specified matter storage directory.
|
|
1880
|
-
*
|
|
1881
|
-
* @param storageName - The name of the storage directory to be backed up.
|
|
1882
|
-
* @param backupName - The name of the backup directory to be created.
|
|
1883
|
-
* @returns {Promise<void>} A promise that resolves when the has been done.
|
|
1884
|
-
*/
|
|
1885
1374
|
async backupMatterStorage(storageName, backupName) {
|
|
1886
1375
|
this.log.info('Creating matter node storage backup...');
|
|
1887
1376
|
await copyDirectory(storageName, backupName);
|
|
1888
1377
|
this.log.info('Created matter node storage backup');
|
|
1889
1378
|
}
|
|
1890
|
-
/**
|
|
1891
|
-
* Stops the matter storage.
|
|
1892
|
-
* @returns {Promise<void>} A promise that resolves when the storage is stopped.
|
|
1893
|
-
*/
|
|
1894
1379
|
async stopMatterStorage() {
|
|
1895
1380
|
this.log.info('Closing matter node storage...');
|
|
1896
1381
|
await this.matterStorageManager?.close();
|
|
@@ -1899,19 +1384,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1899
1384
|
this.matterbridgeContext = undefined;
|
|
1900
1385
|
this.log.info('Matter node storage closed');
|
|
1901
1386
|
}
|
|
1902
|
-
/**
|
|
1903
|
-
* Creates a server node storage context.
|
|
1904
|
-
*
|
|
1905
|
-
* @param {string} pluginName - The name of the plugin.
|
|
1906
|
-
* @param {string} deviceName - The name of the device.
|
|
1907
|
-
* @param {DeviceTypeId} deviceType - The device type of the device.
|
|
1908
|
-
* @param {number} vendorId - The vendor ID.
|
|
1909
|
-
* @param {string} vendorName - The vendor name.
|
|
1910
|
-
* @param {number} productId - The product ID.
|
|
1911
|
-
* @param {string} productName - The product name.
|
|
1912
|
-
* @param {string} [serialNumber] - The serial number of the device (optional).
|
|
1913
|
-
* @returns {Promise<StorageContext>} The storage context for the commissioning server.
|
|
1914
|
-
*/
|
|
1915
1387
|
async createServerNodeContext(pluginName, deviceName, deviceType, vendorId, vendorName, productId, productName, serialNumber) {
|
|
1916
1388
|
const { randomBytes } = await import('node:crypto');
|
|
1917
1389
|
if (!this.matterStorageService)
|
|
@@ -1945,15 +1417,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1945
1417
|
this.log.debug(`- hardwareVersion: ${await storageContext.get('hardwareVersion')} hardwareVersionString: ${await storageContext.get('hardwareVersionString')}`);
|
|
1946
1418
|
return storageContext;
|
|
1947
1419
|
}
|
|
1948
|
-
/**
|
|
1949
|
-
* Creates a server node.
|
|
1950
|
-
*
|
|
1951
|
-
* @param {StorageContext} storageContext - The storage context for the server node.
|
|
1952
|
-
* @param {number} [port=5540] - The port number for the server node. Defaults to 5540.
|
|
1953
|
-
* @param {number} [passcode=20242025] - The passcode for the server node. Defaults to 20242025.
|
|
1954
|
-
* @param {number} [discriminator=3850] - The discriminator for the server node. Defaults to 3850.
|
|
1955
|
-
* @returns {Promise<ServerNode<ServerNode.RootEndpoint>>} A promise that resolves to the created server node.
|
|
1956
|
-
*/
|
|
1957
1420
|
async createServerNode(storageContext, port = 5540, passcode = 20242025, discriminator = 3850) {
|
|
1958
1421
|
const storeId = await storageContext.get('storeId');
|
|
1959
1422
|
this.log.notice(`Creating server node for ${storeId} on port ${port} with passcode ${passcode} and discriminator ${discriminator}...`);
|
|
@@ -1963,33 +1426,21 @@ export class Matterbridge extends EventEmitter {
|
|
|
1963
1426
|
this.log.debug(`- uniqueId: ${await storageContext.get('uniqueId')}`);
|
|
1964
1427
|
this.log.debug(`- softwareVersion: ${await storageContext.get('softwareVersion')} softwareVersionString: ${await storageContext.get('softwareVersionString')}`);
|
|
1965
1428
|
this.log.debug(`- hardwareVersion: ${await storageContext.get('hardwareVersion')} hardwareVersionString: ${await storageContext.get('hardwareVersionString')}`);
|
|
1966
|
-
/**
|
|
1967
|
-
* Create a Matter ServerNode, which contains the Root Endpoint and all relevant data and configuration
|
|
1968
|
-
*/
|
|
1969
1429
|
const serverNode = await ServerNode.create({
|
|
1970
|
-
// Required: Give the Node a unique ID which is used to store the state of this node
|
|
1971
1430
|
id: storeId,
|
|
1972
|
-
// Provide Network relevant configuration like the port
|
|
1973
|
-
// Optional when operating only one device on a host, Default port is 5540
|
|
1974
1431
|
network: {
|
|
1975
1432
|
listeningAddressIpv4: this.ipv4address,
|
|
1976
1433
|
listeningAddressIpv6: this.ipv6address,
|
|
1977
1434
|
port,
|
|
1978
1435
|
},
|
|
1979
|
-
// Provide Commissioning relevant settings
|
|
1980
|
-
// Optional for development/testing purposes
|
|
1981
1436
|
commissioning: {
|
|
1982
1437
|
passcode,
|
|
1983
1438
|
discriminator,
|
|
1984
1439
|
},
|
|
1985
|
-
// Provide Node announcement settings
|
|
1986
|
-
// Optional: If Ommitted some development defaults are used
|
|
1987
1440
|
productDescription: {
|
|
1988
1441
|
name: await storageContext.get('deviceName'),
|
|
1989
1442
|
deviceType: DeviceTypeId(await storageContext.get('deviceType')),
|
|
1990
1443
|
},
|
|
1991
|
-
// Provide defaults for the BasicInformation cluster on the Root endpoint
|
|
1992
|
-
// Optional: If Omitted some development defaults are used
|
|
1993
1444
|
basicInformation: {
|
|
1994
1445
|
vendorId: VendorId(await storageContext.get('vendorId')),
|
|
1995
1446
|
vendorName: await storageContext.get('vendorName'),
|
|
@@ -2007,13 +1458,12 @@ export class Matterbridge extends EventEmitter {
|
|
|
2007
1458
|
},
|
|
2008
1459
|
});
|
|
2009
1460
|
const sanitizeFabrics = (fabrics, resetSessions = false) => {
|
|
2010
|
-
// New type of fabric information: Record<FabricIndex, ExposedFabricInformation>
|
|
2011
1461
|
const sanitizedFabrics = this.sanitizeFabricInformations(Array.from(Object.values(fabrics)));
|
|
2012
1462
|
this.log.info(`Fabrics: ${debugStringify(sanitizedFabrics)}`);
|
|
2013
1463
|
if (this.bridgeMode === 'bridge') {
|
|
2014
1464
|
this.matterbridgeFabricInformations = sanitizedFabrics;
|
|
2015
1465
|
if (resetSessions)
|
|
2016
|
-
this.matterbridgeSessionInformations = undefined;
|
|
1466
|
+
this.matterbridgeSessionInformations = undefined;
|
|
2017
1467
|
this.matterbridgePaired = true;
|
|
2018
1468
|
}
|
|
2019
1469
|
if (this.bridgeMode === 'childbridge') {
|
|
@@ -2021,19 +1471,13 @@ export class Matterbridge extends EventEmitter {
|
|
|
2021
1471
|
if (plugin) {
|
|
2022
1472
|
plugin.fabricInformations = sanitizedFabrics;
|
|
2023
1473
|
if (resetSessions)
|
|
2024
|
-
plugin.sessionInformations = undefined;
|
|
1474
|
+
plugin.sessionInformations = undefined;
|
|
2025
1475
|
plugin.paired = true;
|
|
2026
1476
|
}
|
|
2027
1477
|
}
|
|
2028
1478
|
};
|
|
2029
|
-
/**
|
|
2030
|
-
* This event is triggered when the device is initially commissioned successfully.
|
|
2031
|
-
* This means: It is added to the first fabric.
|
|
2032
|
-
*/
|
|
2033
1479
|
serverNode.lifecycle.commissioned.on(() => this.log.notice(`Server node for ${storeId} was initially commissioned successfully!`));
|
|
2034
|
-
/** This event is triggered when all fabrics are removed from the device, usually it also does a factory reset then. */
|
|
2035
1480
|
serverNode.lifecycle.decommissioned.on(() => this.log.notice(`Server node for ${storeId} was fully decommissioned successfully!`));
|
|
2036
|
-
/** This event is triggered when the device went online. This means that it is discoverable in the network. */
|
|
2037
1481
|
serverNode.lifecycle.online.on(async () => {
|
|
2038
1482
|
this.log.notice(`Server node for ${storeId} is online`);
|
|
2039
1483
|
if (!serverNode.lifecycle.isCommissioned) {
|
|
@@ -2102,7 +1546,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2102
1546
|
this.frontend.wssSendRefreshRequired('settings');
|
|
2103
1547
|
this.frontend.wssSendSnackbarMessage(`${storeId} is online`, 5, 'success');
|
|
2104
1548
|
});
|
|
2105
|
-
/** This event is triggered when the device went offline. it is not longer discoverable or connectable in the network. */
|
|
2106
1549
|
serverNode.lifecycle.offline.on(() => {
|
|
2107
1550
|
this.log.notice(`Server node for ${storeId} is offline`);
|
|
2108
1551
|
if (this.bridgeMode === 'bridge') {
|
|
@@ -2126,10 +1569,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2126
1569
|
this.frontend.wssSendRefreshRequired('settings');
|
|
2127
1570
|
this.frontend.wssSendSnackbarMessage(`${storeId} is offline`, 5, 'warning');
|
|
2128
1571
|
});
|
|
2129
|
-
/**
|
|
2130
|
-
* This event is triggered when a fabric is added, removed or updated on the device. Use this if more granular
|
|
2131
|
-
* information is needed.
|
|
2132
|
-
*/
|
|
2133
1572
|
serverNode.events.commissioning.fabricsChanged.on((fabricIndex, fabricAction) => {
|
|
2134
1573
|
let action = '';
|
|
2135
1574
|
switch (fabricAction) {
|
|
@@ -2163,24 +1602,16 @@ export class Matterbridge extends EventEmitter {
|
|
|
2163
1602
|
}
|
|
2164
1603
|
}
|
|
2165
1604
|
};
|
|
2166
|
-
/**
|
|
2167
|
-
* This event is triggered when an operative new session was opened by a Controller.
|
|
2168
|
-
* It is not triggered for the initial commissioning process, just afterwards for real connections.
|
|
2169
|
-
*/
|
|
2170
1605
|
serverNode.events.sessions.opened.on((session) => {
|
|
2171
1606
|
this.log.notice(`Session opened on server node for ${storeId}: ${debugStringify(session)}`);
|
|
2172
1607
|
sanitizeSessions(Object.values(serverNode.state.sessions.sessions));
|
|
2173
1608
|
this.frontend.wssSendRefreshRequired('sessions');
|
|
2174
1609
|
});
|
|
2175
|
-
/**
|
|
2176
|
-
* This event is triggered when an operative session is closed by a Controller or because the Device goes offline.
|
|
2177
|
-
*/
|
|
2178
1610
|
serverNode.events.sessions.closed.on((session) => {
|
|
2179
1611
|
this.log.notice(`Session closed on server node for ${storeId}: ${debugStringify(session)}`);
|
|
2180
1612
|
sanitizeSessions(Object.values(serverNode.state.sessions.sessions));
|
|
2181
1613
|
this.frontend.wssSendRefreshRequired('sessions');
|
|
2182
1614
|
});
|
|
2183
|
-
/** This event is triggered when a subscription gets added or removed on an operative session. */
|
|
2184
1615
|
serverNode.events.sessions.subscriptionsChanged.on((session) => {
|
|
2185
1616
|
this.log.notice(`Session subscriptions changed on server node for ${storeId}: ${debugStringify(session)}`);
|
|
2186
1617
|
sanitizeSessions(Object.values(serverNode.state.sessions.sessions));
|
|
@@ -2189,42 +1620,24 @@ export class Matterbridge extends EventEmitter {
|
|
|
2189
1620
|
this.log.info(`Created server node for ${storeId}`);
|
|
2190
1621
|
return serverNode;
|
|
2191
1622
|
}
|
|
2192
|
-
/**
|
|
2193
|
-
* Starts the specified server node.
|
|
2194
|
-
*
|
|
2195
|
-
* @param {ServerNode} [matterServerNode] - The server node to start.
|
|
2196
|
-
* @returns {Promise<void>} A promise that resolves when the server node has started.
|
|
2197
|
-
*/
|
|
2198
1623
|
async startServerNode(matterServerNode) {
|
|
2199
1624
|
if (!matterServerNode)
|
|
2200
1625
|
return;
|
|
2201
1626
|
this.log.notice(`Starting ${matterServerNode.id} server node`);
|
|
2202
1627
|
await matterServerNode.start();
|
|
2203
1628
|
}
|
|
2204
|
-
/**
|
|
2205
|
-
* Stops the specified server node.
|
|
2206
|
-
*
|
|
2207
|
-
* @param {ServerNode} matterServerNode - The server node to stop.
|
|
2208
|
-
* @returns {Promise<void>} A promise that resolves when the server node has stopped.
|
|
2209
|
-
*/
|
|
2210
1629
|
async stopServerNode(matterServerNode) {
|
|
2211
1630
|
if (!matterServerNode)
|
|
2212
1631
|
return;
|
|
2213
1632
|
this.log.notice(`Closing ${matterServerNode.id} server node`);
|
|
2214
1633
|
try {
|
|
2215
|
-
await withTimeout(matterServerNode.close(), 30000);
|
|
1634
|
+
await withTimeout(matterServerNode.close(), 30000);
|
|
2216
1635
|
this.log.info(`Closed ${matterServerNode.id} server node`);
|
|
2217
1636
|
}
|
|
2218
1637
|
catch (error) {
|
|
2219
1638
|
this.log.error(`Failed to close ${matterServerNode.id} server node: ${error instanceof Error ? error.message : error}`);
|
|
2220
1639
|
}
|
|
2221
1640
|
}
|
|
2222
|
-
/**
|
|
2223
|
-
* Advertises the specified server node.
|
|
2224
|
-
*
|
|
2225
|
-
* @param {ServerNode} [matterServerNode] - The server node to advertise.
|
|
2226
|
-
* @returns {Promise<{ qrPairingCode: string, manualPairingCode: string } | undefined>} A promise that resolves to the pairing codes if the server node is advertised, or undefined if not.
|
|
2227
|
-
*/
|
|
2228
1641
|
async advertiseServerNode(matterServerNode) {
|
|
2229
1642
|
if (matterServerNode) {
|
|
2230
1643
|
await matterServerNode.env.get(DeviceCommissioner)?.allowBasicCommissioning();
|
|
@@ -2233,45 +1646,24 @@ export class Matterbridge extends EventEmitter {
|
|
|
2233
1646
|
return { qrPairingCode, manualPairingCode };
|
|
2234
1647
|
}
|
|
2235
1648
|
}
|
|
2236
|
-
/**
|
|
2237
|
-
* Stop advertise the specified server node.
|
|
2238
|
-
*
|
|
2239
|
-
* @param {ServerNode} [matterServerNode] - The server node to advertise.
|
|
2240
|
-
* @returns {Promise<void>} A promise that resolves when the server node has stopped advertising.
|
|
2241
|
-
*/
|
|
2242
1649
|
async stopAdvertiseServerNode(matterServerNode) {
|
|
2243
1650
|
if (matterServerNode && matterServerNode.lifecycle.isOnline) {
|
|
2244
1651
|
await matterServerNode.env.get(DeviceCommissioner)?.endCommissioning();
|
|
2245
1652
|
this.log.notice(`Stopped advertising for ${matterServerNode.id}`);
|
|
2246
1653
|
}
|
|
2247
1654
|
}
|
|
2248
|
-
/**
|
|
2249
|
-
* Creates an aggregator node with the specified storage context.
|
|
2250
|
-
*
|
|
2251
|
-
* @param {StorageContext} storageContext - The storage context for the aggregator node.
|
|
2252
|
-
* @returns {Promise<EndpointNode<AggregatorEndpoint>>} A promise that resolves to the created aggregator node.
|
|
2253
|
-
*/
|
|
2254
1655
|
async createAggregatorNode(storageContext) {
|
|
2255
1656
|
this.log.notice(`Creating ${await storageContext.get('storeId')} aggregator `);
|
|
2256
1657
|
const aggregatorNode = new EndpointNode(AggregatorEndpoint, { id: `${await storageContext.get('storeId')}` });
|
|
2257
1658
|
return aggregatorNode;
|
|
2258
1659
|
}
|
|
2259
|
-
/**
|
|
2260
|
-
* Adds a MatterbridgeEndpoint to the specified plugin.
|
|
2261
|
-
*
|
|
2262
|
-
* @param {string} pluginName - The name of the plugin.
|
|
2263
|
-
* @param {MatterbridgeEndpoint} device - The device to add as a bridged endpoint.
|
|
2264
|
-
* @returns {Promise<void>} A promise that resolves when the bridged endpoint has been added.
|
|
2265
|
-
*/
|
|
2266
1660
|
async addBridgedEndpoint(pluginName, device) {
|
|
2267
|
-
// Check if the plugin is registered
|
|
2268
1661
|
const plugin = this.plugins.get(pluginName);
|
|
2269
1662
|
if (!plugin) {
|
|
2270
1663
|
this.log.error(`Error adding bridged endpoint ${dev}${device.deviceName}${er} (${zb}${device.id}${er}) plugin ${plg}${pluginName}${er} not found`);
|
|
2271
1664
|
return;
|
|
2272
1665
|
}
|
|
2273
1666
|
if (this.bridgeMode === 'bridge') {
|
|
2274
|
-
// Register and add the device to the matterbridge aggregator node
|
|
2275
1667
|
this.log.debug(`Adding bridged endpoint ${plg}${pluginName}${db}:${dev}${device.deviceName}${db} to Matterbridge aggregator node`);
|
|
2276
1668
|
if (!this.aggregatorNode) {
|
|
2277
1669
|
this.log.error('Aggregator node not found for Matterbridge');
|
|
@@ -2288,7 +1680,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2288
1680
|
}
|
|
2289
1681
|
}
|
|
2290
1682
|
else if (this.bridgeMode === 'childbridge') {
|
|
2291
|
-
// Register and add the device to the plugin server node
|
|
2292
1683
|
if (plugin.type === 'AccessoryPlatform') {
|
|
2293
1684
|
try {
|
|
2294
1685
|
this.log.debug(`Creating endpoint ${dev}${device.deviceName}${db} for AccessoryPlatform plugin ${plg}${plugin.name}${db} server node`);
|
|
@@ -2305,12 +1696,10 @@ export class Matterbridge extends EventEmitter {
|
|
|
2305
1696
|
return;
|
|
2306
1697
|
}
|
|
2307
1698
|
}
|
|
2308
|
-
// Register and add the device to the plugin aggregator node
|
|
2309
1699
|
if (plugin.type === 'DynamicPlatform') {
|
|
2310
1700
|
try {
|
|
2311
1701
|
this.log.debug(`Adding bridged endpoint ${dev}${device.deviceName}${db} for DynamicPlatform plugin ${plg}${plugin.name}${db} aggregator node`);
|
|
2312
1702
|
await this.createDynamicPlugin(plugin);
|
|
2313
|
-
// Fast plugins can add another device before the server node is created
|
|
2314
1703
|
await waiter(`createDynamicPlugin(${plugin.name})`, () => plugin.serverNode?.hasParts === true);
|
|
2315
1704
|
if (!plugin.aggregatorNode) {
|
|
2316
1705
|
this.log.error(`Aggregator node not found for plugin ${plg}${plugin.name}${er}`);
|
|
@@ -2330,28 +1719,17 @@ export class Matterbridge extends EventEmitter {
|
|
|
2330
1719
|
plugin.registeredDevices++;
|
|
2331
1720
|
if (plugin.addedDevices !== undefined)
|
|
2332
1721
|
plugin.addedDevices++;
|
|
2333
|
-
// Add the device to the DeviceManager
|
|
2334
1722
|
this.devices.set(device);
|
|
2335
|
-
// Subscribe to the reachable$Changed event
|
|
2336
1723
|
await this.subscribeAttributeChanged(plugin, device);
|
|
2337
1724
|
this.log.info(`Added and registered bridged endpoint (${plugin.registeredDevices}/${plugin.addedDevices}) ${dev}${device.deviceName}${nf} (${dev}${device.id}${nf}) for plugin ${plg}${pluginName}${nf}`);
|
|
2338
1725
|
}
|
|
2339
|
-
/**
|
|
2340
|
-
* Removes a MatterbridgeEndpoint from the specified plugin.
|
|
2341
|
-
*
|
|
2342
|
-
* @param {string} pluginName - The name of the plugin.
|
|
2343
|
-
* @param {MatterbridgeEndpoint} device - The device to remove as a bridged endpoint.
|
|
2344
|
-
* @returns {Promise<void>} A promise that resolves when the bridged endpoint has been removed.
|
|
2345
|
-
*/
|
|
2346
1726
|
async removeBridgedEndpoint(pluginName, device) {
|
|
2347
1727
|
this.log.debug(`Removing bridged endpoint ${plg}${pluginName}${db}:${dev}${device.deviceName}${db} (${zb}${device.name}${db}) for plugin ${plg}${pluginName}${db}`);
|
|
2348
|
-
// Check if the plugin is registered
|
|
2349
1728
|
const plugin = this.plugins.get(pluginName);
|
|
2350
1729
|
if (!plugin) {
|
|
2351
1730
|
this.log.error(`Error removing bridged endpoint ${dev}${device.deviceName}${er} (${zb}${device.name}${er}) for plugin ${plg}${pluginName}${er}: plugin not found`);
|
|
2352
1731
|
return;
|
|
2353
1732
|
}
|
|
2354
|
-
// Register and add the device to the matterbridge aggregator node
|
|
2355
1733
|
if (this.bridgeMode === 'bridge') {
|
|
2356
1734
|
if (!this.aggregatorNode) {
|
|
2357
1735
|
this.log.error(`Error removing bridged endpoint ${dev}${device.deviceName}${er} (${zb}${device.name}${er}) for plugin ${plg}${pluginName}${er}: aggregator node not found`);
|
|
@@ -2366,7 +1744,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2366
1744
|
}
|
|
2367
1745
|
else if (this.bridgeMode === 'childbridge') {
|
|
2368
1746
|
if (plugin.type === 'AccessoryPlatform') {
|
|
2369
|
-
// Nothing to do here since the server node has no aggregator node but only the device itself
|
|
2370
1747
|
}
|
|
2371
1748
|
else if (plugin.type === 'DynamicPlatform') {
|
|
2372
1749
|
if (!plugin.aggregatorNode) {
|
|
@@ -2381,21 +1758,8 @@ export class Matterbridge extends EventEmitter {
|
|
|
2381
1758
|
if (plugin.addedDevices !== undefined)
|
|
2382
1759
|
plugin.addedDevices--;
|
|
2383
1760
|
}
|
|
2384
|
-
// Remove the device from the DeviceManager
|
|
2385
1761
|
this.devices.remove(device);
|
|
2386
1762
|
}
|
|
2387
|
-
/**
|
|
2388
|
-
* Removes all bridged endpoints from the specified plugin.
|
|
2389
|
-
*
|
|
2390
|
-
* @param {string} pluginName - The name of the plugin.
|
|
2391
|
-
* @param {number} [delay=0] - The delay in milliseconds between removing each bridged endpoint (default: 0).
|
|
2392
|
-
* @returns {Promise<void>} A promise that resolves when all bridged endpoints have been removed.
|
|
2393
|
-
*
|
|
2394
|
-
* @remarks
|
|
2395
|
-
* This method iterates through all devices in the DeviceManager and removes each bridged endpoint associated with the specified plugin.
|
|
2396
|
-
* It also applies a delay between each removal if specified.
|
|
2397
|
-
* The delay is useful to allow the controllers to receive a single subscription for each device removed.
|
|
2398
|
-
*/
|
|
2399
1763
|
async removeAllBridgedEndpoints(pluginName, delay = 0) {
|
|
2400
1764
|
this.log.debug(`Removing all bridged endpoints for plugin ${plg}${pluginName}${db}${delay > 0 ? ` with delay ${delay} ms` : ''}`);
|
|
2401
1765
|
for (const device of this.devices.array().filter((device) => device.plugin === pluginName)) {
|
|
@@ -2406,25 +1770,9 @@ export class Matterbridge extends EventEmitter {
|
|
|
2406
1770
|
if (delay > 0)
|
|
2407
1771
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
2408
1772
|
}
|
|
2409
|
-
/**
|
|
2410
|
-
* Subscribes to the attribute change event for the given device and plugin.
|
|
2411
|
-
* Specifically, it listens for changes in the 'reachable' attribute of the
|
|
2412
|
-
* BridgedDeviceBasicInformationServer cluster server of the bridged device or BasicInformationServer cluster server of server node.
|
|
2413
|
-
*
|
|
2414
|
-
* @param {RegisteredPlugin} plugin - The plugin associated with the device.
|
|
2415
|
-
* @param {MatterbridgeEndpoint} device - The device to subscribe to attribute changes for.
|
|
2416
|
-
* @returns {Promise<void>} A promise that resolves when the subscription is set up.
|
|
2417
|
-
*/
|
|
2418
1773
|
async subscribeAttributeChanged(plugin, device) {
|
|
2419
1774
|
this.log.info(`Subscribing attributes for endpoint ${dev}${device.deviceName}${nf} (${dev}${device.id}${nf}) plugin ${plg}${plugin.name}${nf}`);
|
|
2420
1775
|
if (this.bridgeMode === 'childbridge' && plugin.type === 'AccessoryPlatform' && plugin.serverNode) {
|
|
2421
|
-
/*
|
|
2422
|
-
this.log.info(`Accessory endpoint ${dev}${device.deviceName}${nf} (${dev}${device.id}${nf}) subscribed to reachable$Changed`);
|
|
2423
|
-
setTimeout(async () => {
|
|
2424
|
-
this.log.info(`Accessory endpoint ${dev}${device.deviceName}${nf} (${dev}${device.id}${nf}) changed to reachable false`);
|
|
2425
|
-
await plugin.serverNode?.setStateOf(BasicInformationServer, { reachable: false });
|
|
2426
|
-
}, 60000).unref();
|
|
2427
|
-
*/
|
|
2428
1776
|
plugin.serverNode.eventsOf(BasicInformationServer).reachable$Changed?.on((reachable) => {
|
|
2429
1777
|
this.log.info(`Accessory endpoint ${dev}${device.deviceName}${nf} (${dev}${device.id}${nf}) is ${reachable ? 'reachable' : 'unreachable'}`);
|
|
2430
1778
|
this.frontend.wssSendAttributeChangedMessage(device.plugin, device.serialNumber, device.uniqueId, 'BasicInformationServer', 'reachable', reachable);
|
|
@@ -2437,12 +1785,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2437
1785
|
});
|
|
2438
1786
|
}
|
|
2439
1787
|
}
|
|
2440
|
-
/**
|
|
2441
|
-
* Sanitizes the fabric information by converting bigint properties to strings because `res.json` doesn't support bigint.
|
|
2442
|
-
*
|
|
2443
|
-
* @param {ExposedFabricInformation[]} fabricInfo - The array of exposed fabric information objects.
|
|
2444
|
-
* @returns {SanitizedExposedFabricInformation[]} An array of sanitized exposed fabric information objects.
|
|
2445
|
-
*/
|
|
2446
1788
|
sanitizeFabricInformations(fabricInfo) {
|
|
2447
1789
|
return fabricInfo.map((info) => {
|
|
2448
1790
|
return {
|
|
@@ -2456,12 +1798,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2456
1798
|
};
|
|
2457
1799
|
});
|
|
2458
1800
|
}
|
|
2459
|
-
/**
|
|
2460
|
-
* Sanitizes the session information by converting bigint properties to strings because `res.json` doesn't support bigint.
|
|
2461
|
-
*
|
|
2462
|
-
* @param {SessionInformation[]} sessionInfo - The array of session information objects.
|
|
2463
|
-
* @returns {SanitizedSessionInformation[]} An array of sanitized session information objects.
|
|
2464
|
-
*/
|
|
2465
1801
|
sanitizeSessionInformation(sessionInfo) {
|
|
2466
1802
|
return sessionInfo
|
|
2467
1803
|
.filter((session) => session.isPeerActive)
|
|
@@ -2489,20 +1825,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
2489
1825
|
};
|
|
2490
1826
|
});
|
|
2491
1827
|
}
|
|
2492
|
-
/**
|
|
2493
|
-
* Sets the reachability of the specified aggregator node bridged devices and trigger.
|
|
2494
|
-
* @param {EndpointNode<AggregatorEndpoint>} aggregatorNode - The aggregator node to set the reachability for.
|
|
2495
|
-
* @param {boolean} reachable - A boolean indicating the reachability status to set.
|
|
2496
|
-
*/
|
|
2497
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
2498
1828
|
async setAggregatorReachability(aggregatorNode, reachable) {
|
|
2499
|
-
/*
|
|
2500
|
-
for (const child of aggregatorNode.parts) {
|
|
2501
|
-
this.log.debug(`Setting reachability of ${(child as unknown as MatterbridgeEndpoint)?.deviceName} to ${reachable}`);
|
|
2502
|
-
await child.setStateOf(BridgedDeviceBasicInformationServer, { reachable });
|
|
2503
|
-
child.act((agent) => child.eventsOf(BridgedDeviceBasicInformationServer).reachableChanged.emit({ reachableNewValue: true }, agent.context));
|
|
2504
|
-
}
|
|
2505
|
-
*/
|
|
2506
1829
|
}
|
|
2507
1830
|
getVendorIdName = (vendorId) => {
|
|
2508
1831
|
if (!vendorId)
|
|
@@ -2545,37 +1868,14 @@ export class Matterbridge extends EventEmitter {
|
|
|
2545
1868
|
}
|
|
2546
1869
|
return vendorName;
|
|
2547
1870
|
};
|
|
2548
|
-
/**
|
|
2549
|
-
* Spawns a child process with the given command and arguments.
|
|
2550
|
-
* @param {string} command - The command to execute.
|
|
2551
|
-
* @param {string[]} args - The arguments to pass to the command (default: []).
|
|
2552
|
-
* @returns {Promise<boolean>} A promise that resolves when the child process exits successfully, or rejects if there is an error.
|
|
2553
|
-
*/
|
|
2554
1871
|
async spawnCommand(command, args = []) {
|
|
2555
1872
|
const { spawn } = await import('node:child_process');
|
|
2556
|
-
/*
|
|
2557
|
-
npm > npm.cmd on windows
|
|
2558
|
-
cmd.exe ['dir'] on windows
|
|
2559
|
-
await this.spawnCommand('npm', ['install', '-g', 'matterbridge']);
|
|
2560
|
-
process.on('unhandledRejection', (reason, promise) => {
|
|
2561
|
-
this.log.error('Unhandled Rejection at:', promise, 'reason:', reason);
|
|
2562
|
-
});
|
|
2563
|
-
|
|
2564
|
-
spawn - [14:27:21.125] [Matterbridge:spawn]: changed 38 packages in 4s
|
|
2565
|
-
spawn - [14:27:21.125] [Matterbridge:spawn]: 10 packages are looking for funding run `npm fund` for details
|
|
2566
|
-
debug - [14:27:21.131] [Matterbridge]: Child process exited with code 0 and signal null
|
|
2567
|
-
debug - [14:27:21.131] [Matterbridge]: Child process stdio streams have closed with code 0
|
|
2568
|
-
*/
|
|
2569
1873
|
const cmdLine = command + ' ' + args.join(' ');
|
|
2570
1874
|
if (process.platform === 'win32' && command === 'npm') {
|
|
2571
|
-
// Must be spawn('cmd.exe', ['/c', 'npm -g install <package>']);
|
|
2572
1875
|
const argstring = 'npm ' + args.join(' ');
|
|
2573
1876
|
args.splice(0, args.length, '/c', argstring);
|
|
2574
1877
|
command = 'cmd.exe';
|
|
2575
1878
|
}
|
|
2576
|
-
// Decide when using sudo on linux
|
|
2577
|
-
// When you need sudo: Spawn stderr: npm error Error: EACCES: permission denied
|
|
2578
|
-
// When you don't need sudo: Failed to start child process "npm install -g matterbridge-eve-door": spawn sudo ENOENT
|
|
2579
1879
|
if (hasParameter('sudo') || (process.platform === 'linux' && command === 'npm' && !hasParameter('docker') && !hasParameter('nosudo'))) {
|
|
2580
1880
|
args.unshift(command);
|
|
2581
1881
|
command = 'sudo';
|
|
@@ -2634,4 +1934,3 @@ export class Matterbridge extends EventEmitter {
|
|
|
2634
1934
|
});
|
|
2635
1935
|
}
|
|
2636
1936
|
}
|
|
2637
|
-
//# sourceMappingURL=matterbridge.js.map
|