matterbridge 2.2.8 → 2.2.9-dev.1
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 +20 -1
- 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 -326
- 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 +51 -747
- package/dist/matterbridgeAccessoryPlatform.js +0 -33
- package/dist/matterbridgeBehaviors.js +1 -32
- package/dist/matterbridgeDeviceTypes.js +11 -112
- package/dist/matterbridgeDynamicPlatform.js +0 -33
- package/dist/matterbridgeEndpoint.js +6 -720
- package/dist/matterbridgeEndpointHelpers.js +9 -118
- package/dist/matterbridgePlatform.js +7 -216
- 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 -45
- 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 -41
- 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.e11d6bb4.js → main.ff47208e.js} +3 -3
- package/frontend/build/static/js/{main.e11d6bb4.js.map → main.ff47208e.js.map} +1 -1
- package/npm-shrinkwrap.json +9 -9
- 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 -221
- 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 -425
- package/dist/matterbridge.d.ts.map +0 -1
- package/dist/matterbridge.js.map +0 -1
- package/dist/matterbridgeAccessoryPlatform.d.ts +0 -39
- package/dist/matterbridgeAccessoryPlatform.d.ts.map +0 -1
- package/dist/matterbridgeAccessoryPlatform.js.map +0 -1
- package/dist/matterbridgeBehaviors.d.ts +0 -1056
- package/dist/matterbridgeBehaviors.d.ts.map +0 -1
- package/dist/matterbridgeBehaviors.js.map +0 -1
- package/dist/matterbridgeDeviceTypes.d.ts +0 -177
- package/dist/matterbridgeDeviceTypes.d.ts.map +0 -1
- package/dist/matterbridgeDeviceTypes.js.map +0 -1
- package/dist/matterbridgeDynamicPlatform.d.ts +0 -39
- package/dist/matterbridgeDynamicPlatform.d.ts.map +0 -1
- package/dist/matterbridgeDynamicPlatform.js.map +0 -1
- package/dist/matterbridgeEndpoint.d.ts +0 -867
- package/dist/matterbridgeEndpoint.d.ts.map +0 -1
- package/dist/matterbridgeEndpoint.js.map +0 -1
- package/dist/matterbridgeEndpointHelpers.d.ts +0 -2275
- package/dist/matterbridgeEndpointHelpers.d.ts.map +0 -1
- package/dist/matterbridgeEndpointHelpers.js.map +0 -1
- package/dist/matterbridgePlatform.d.ts +0 -285
- package/dist/matterbridgePlatform.d.ts.map +0 -1
- package/dist/matterbridgePlatform.js.map +0 -1
- package/dist/matterbridgeTypes.d.ts +0 -183
- 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 -44
- 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.e11d6bb4.js.LICENSE.txt → main.ff47208e.js.LICENSE.txt} +0 -0
package/dist/matterbridge.js
CHANGED
|
@@ -1,35 +1,9 @@
|
|
|
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
|
-
// AnsiLogger module
|
|
29
5
|
import { AnsiLogger, UNDERLINE, UNDERLINEOFF, YELLOW, db, debugStringify, BRIGHT, RESET, er, nf, rs, wr, RED, GREEN, zb, CYAN } from './logger/export.js';
|
|
30
|
-
// NodeStorage module
|
|
31
6
|
import { NodeStorageManager } from './storage/export.js';
|
|
32
|
-
// Matterbridge
|
|
33
7
|
import { getParameter, getIntParameter, hasParameter, copyDirectory, withTimeout } from './utils/export.js';
|
|
34
8
|
import { logInterfaces, getGlobalNodeModules } from './utils/network.js';
|
|
35
9
|
import { PluginManager } from './pluginManager.js';
|
|
@@ -37,19 +11,14 @@ import { DeviceManager } from './deviceManager.js';
|
|
|
37
11
|
import { MatterbridgeEndpoint } from './matterbridgeEndpoint.js';
|
|
38
12
|
import { bridge } from './matterbridgeDeviceTypes.js';
|
|
39
13
|
import { Frontend } from './frontend.js';
|
|
40
|
-
// @matter
|
|
41
14
|
import { DeviceTypeId, Endpoint as EndpointNode, Logger, LogLevel as MatterLogLevel, LogFormat as MatterLogFormat, VendorId, StorageService, Environment, ServerNode } from '@matter/main';
|
|
42
15
|
import { DeviceCommissioner, FabricAction, MdnsService, PaseClient } from '@matter/main/protocol';
|
|
43
16
|
import { AggregatorEndpoint } from '@matter/main/endpoints';
|
|
44
17
|
import { BridgedDeviceBasicInformationServer } from '@matter/main/behaviors/bridged-device-basic-information';
|
|
45
18
|
import { BasicInformationServer } from '@matter/main/behaviors/basic-information';
|
|
46
|
-
// Default colors
|
|
47
19
|
const plg = '\u001B[38;5;33m';
|
|
48
20
|
const dev = '\u001B[38;5;79m';
|
|
49
21
|
const typ = '\u001B[38;5;207m';
|
|
50
|
-
/**
|
|
51
|
-
* Represents the Matterbridge application.
|
|
52
|
-
*/
|
|
53
22
|
export class Matterbridge extends EventEmitter {
|
|
54
23
|
systemInformation = {
|
|
55
24
|
interfaceName: '',
|
|
@@ -94,7 +63,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
94
63
|
shellySysUpdate: false,
|
|
95
64
|
shellyMainUpdate: false,
|
|
96
65
|
profile: getParameter('profile'),
|
|
97
|
-
loggerLevel: "info"
|
|
66
|
+
loggerLevel: "info",
|
|
98
67
|
fileLogger: false,
|
|
99
68
|
matterLoggerLevel: MatterLogLevel.INFO,
|
|
100
69
|
matterFileLogger: false,
|
|
@@ -132,11 +101,9 @@ export class Matterbridge extends EventEmitter {
|
|
|
132
101
|
plugins;
|
|
133
102
|
devices;
|
|
134
103
|
frontend = new Frontend(this);
|
|
135
|
-
// Matterbridge storage
|
|
136
104
|
nodeStorage;
|
|
137
105
|
nodeContext;
|
|
138
106
|
nodeStorageName = 'storage' + (getParameter('profile') ? '.' + getParameter('profile') : '');
|
|
139
|
-
// Cleanup
|
|
140
107
|
hasCleanupStarted = false;
|
|
141
108
|
initialized = false;
|
|
142
109
|
execRunningCount = 0;
|
|
@@ -149,21 +116,18 @@ export class Matterbridge extends EventEmitter {
|
|
|
149
116
|
sigtermHandler;
|
|
150
117
|
exceptionHandler;
|
|
151
118
|
rejectionHandler;
|
|
152
|
-
// Matter environment
|
|
153
119
|
environment = Environment.default;
|
|
154
|
-
// Matter storage
|
|
155
120
|
matterStorageName = 'matterstorage' + (getParameter('profile') ? '.' + getParameter('profile') : '');
|
|
156
121
|
matterStorageService;
|
|
157
122
|
matterStorageManager;
|
|
158
123
|
matterbridgeContext;
|
|
159
124
|
mattercontrollerContext;
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
discriminator; // first server node discriminator
|
|
125
|
+
mdnsInterface;
|
|
126
|
+
ipv4address;
|
|
127
|
+
ipv6address;
|
|
128
|
+
port;
|
|
129
|
+
passcode;
|
|
130
|
+
discriminator;
|
|
167
131
|
serverNode;
|
|
168
132
|
aggregatorNode;
|
|
169
133
|
aggregatorVendorId = VendorId(getIntParameter('vendorId') ?? 0xfff1);
|
|
@@ -171,50 +135,21 @@ export class Matterbridge extends EventEmitter {
|
|
|
171
135
|
aggregatorProductId = getIntParameter('productId') ?? 0x8000;
|
|
172
136
|
aggregatorProductName = getParameter('productName') ?? 'Matterbridge aggregator';
|
|
173
137
|
static instance;
|
|
174
|
-
// We load asyncronously so is private
|
|
175
138
|
constructor() {
|
|
176
139
|
super();
|
|
177
140
|
}
|
|
178
|
-
/**
|
|
179
|
-
* Emits an event of the specified type with the provided arguments.
|
|
180
|
-
*
|
|
181
|
-
* @template K - The type of the event.
|
|
182
|
-
* @param {K} eventName - The name of the event to emit.
|
|
183
|
-
* @param {...MatterbridgeEvent[K]} args - The arguments to pass to the event listeners.
|
|
184
|
-
* @returns {boolean} - Returns true if the event had listeners, false otherwise.
|
|
185
|
-
*/
|
|
186
141
|
emit(eventName, ...args) {
|
|
187
142
|
return super.emit(eventName, ...args);
|
|
188
143
|
}
|
|
189
|
-
/**
|
|
190
|
-
* Registers an event listener for the specified event type.
|
|
191
|
-
*
|
|
192
|
-
* @template K - The type of the event.
|
|
193
|
-
* @param {K} eventName - The name of the event to listen for.
|
|
194
|
-
* @param {(...args: MatterbridgeEvent[K]) => void} listener - The callback function to invoke when the event is emitted.
|
|
195
|
-
* @returns {this} - Returns the instance of the Matterbridge class.
|
|
196
|
-
*/
|
|
197
144
|
on(eventName, listener) {
|
|
198
145
|
return super.on(eventName, listener);
|
|
199
146
|
}
|
|
200
|
-
/**
|
|
201
|
-
* Retrieves the list of Matterbridge devices.
|
|
202
|
-
* @returns {MatterbridgeEndpoint[]} An array of MatterbridgeDevice objects.
|
|
203
|
-
*/
|
|
204
147
|
getDevices() {
|
|
205
148
|
return this.devices.array();
|
|
206
149
|
}
|
|
207
|
-
/**
|
|
208
|
-
* Retrieves the list of registered plugins.
|
|
209
|
-
* @returns {RegisteredPlugin[]} An array of RegisteredPlugin objects.
|
|
210
|
-
*/
|
|
211
150
|
getPlugins() {
|
|
212
151
|
return this.plugins.array();
|
|
213
152
|
}
|
|
214
|
-
/**
|
|
215
|
-
* Set the logger logLevel for the Matterbridge classes.
|
|
216
|
-
* @param {LogLevel} logLevel The logger logLevel to set.
|
|
217
|
-
*/
|
|
218
153
|
async setLogLevel(logLevel) {
|
|
219
154
|
if (this.log)
|
|
220
155
|
this.log.logLevel = logLevel;
|
|
@@ -228,31 +163,19 @@ export class Matterbridge extends EventEmitter {
|
|
|
228
163
|
for (const plugin of this.plugins) {
|
|
229
164
|
if (!plugin.platform || !plugin.platform.log || !plugin.platform.config)
|
|
230
165
|
continue;
|
|
231
|
-
plugin.platform.log.logLevel = plugin.platform.config.debug === true ? "debug"
|
|
232
|
-
await plugin.platform.onChangeLoggerLevel(plugin.platform.config.debug === true ? "debug"
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
callbackLogLevel = "debug" /* LogLevel.DEBUG */;
|
|
166
|
+
plugin.platform.log.logLevel = plugin.platform.config.debug === true ? "debug" : this.log.logLevel;
|
|
167
|
+
await plugin.platform.onChangeLoggerLevel(plugin.platform.config.debug === true ? "debug" : this.log.logLevel);
|
|
168
|
+
}
|
|
169
|
+
let callbackLogLevel = "notice";
|
|
170
|
+
if (this.matterbridgeInformation.loggerLevel === "info" || this.matterbridgeInformation.matterLoggerLevel === MatterLogLevel.INFO)
|
|
171
|
+
callbackLogLevel = "info";
|
|
172
|
+
if (this.matterbridgeInformation.loggerLevel === "debug" || this.matterbridgeInformation.matterLoggerLevel === MatterLogLevel.DEBUG)
|
|
173
|
+
callbackLogLevel = "debug";
|
|
240
174
|
AnsiLogger.setGlobalCallback(this.frontend.wssSendMessage.bind(this.frontend), callbackLogLevel);
|
|
241
175
|
this.log.debug(`WebSocketServer logger global callback set to ${callbackLogLevel}`);
|
|
242
176
|
}
|
|
243
|
-
/** ***********************************************************************************************************************************/
|
|
244
|
-
/** loadInstance() and cleanup() methods */
|
|
245
|
-
/** ***********************************************************************************************************************************/
|
|
246
|
-
/**
|
|
247
|
-
* Loads an instance of the Matterbridge class.
|
|
248
|
-
* If an instance already exists, return that instance.
|
|
249
|
-
*
|
|
250
|
-
* @param initialize - Whether to initialize the Matterbridge instance after loading.
|
|
251
|
-
* @returns The loaded Matterbridge instance.
|
|
252
|
-
*/
|
|
253
177
|
static async loadInstance(initialize = false) {
|
|
254
178
|
if (!Matterbridge.instance) {
|
|
255
|
-
// eslint-disable-next-line no-console
|
|
256
179
|
if (hasParameter('debug'))
|
|
257
180
|
console.log(GREEN + 'Creating a new instance of Matterbridge.', initialize ? 'Initializing...' : 'Not initializing...', rs);
|
|
258
181
|
Matterbridge.instance = new Matterbridge();
|
|
@@ -261,14 +184,8 @@ export class Matterbridge extends EventEmitter {
|
|
|
261
184
|
}
|
|
262
185
|
return Matterbridge.instance;
|
|
263
186
|
}
|
|
264
|
-
/**
|
|
265
|
-
* Call cleanup().
|
|
266
|
-
* @deprecated This method is deprecated and is only used for jest tests.
|
|
267
|
-
*
|
|
268
|
-
*/
|
|
269
187
|
async destroyInstance() {
|
|
270
188
|
this.log.info(`Destroy instance...`);
|
|
271
|
-
// Save server nodes to close
|
|
272
189
|
const servers = [];
|
|
273
190
|
if (this.bridgeMode === 'bridge') {
|
|
274
191
|
if (this.serverNode)
|
|
@@ -280,81 +197,55 @@ export class Matterbridge extends EventEmitter {
|
|
|
280
197
|
servers.push(plugin.serverNode);
|
|
281
198
|
}
|
|
282
199
|
}
|
|
283
|
-
// Cleanup
|
|
284
200
|
await this.cleanup('destroying instance...', false);
|
|
285
|
-
// Close servers mdns service
|
|
286
201
|
this.log.info(`Dispose ${servers.length} MdnsService...`);
|
|
287
202
|
for (const server of servers) {
|
|
288
203
|
await server.env.get(MdnsService)[Symbol.asyncDispose]();
|
|
289
204
|
this.log.info(`Closed ${server.id} MdnsService`);
|
|
290
205
|
}
|
|
291
|
-
// Wait for the cleanup to finish
|
|
292
206
|
await new Promise((resolve) => {
|
|
293
207
|
setTimeout(resolve, 1000);
|
|
294
208
|
});
|
|
295
209
|
}
|
|
296
|
-
/**
|
|
297
|
-
* Initializes the Matterbridge application.
|
|
298
|
-
*
|
|
299
|
-
* @remarks
|
|
300
|
-
* This method performs the necessary setup and initialization steps for the Matterbridge application.
|
|
301
|
-
* It displays the help information if the 'help' parameter is provided, sets up the logger, checks the
|
|
302
|
-
* node version, registers signal handlers, initializes storage, and parses the command line.
|
|
303
|
-
*
|
|
304
|
-
* @returns A Promise that resolves when the initialization is complete.
|
|
305
|
-
*/
|
|
306
210
|
async initialize() {
|
|
307
|
-
// Set the restart mode
|
|
308
211
|
if (hasParameter('service'))
|
|
309
212
|
this.restartMode = 'service';
|
|
310
213
|
if (hasParameter('docker'))
|
|
311
214
|
this.restartMode = 'docker';
|
|
312
|
-
// Set the matterbridge directory
|
|
313
215
|
this.homeDirectory = getParameter('homedir') ?? os.homedir();
|
|
314
216
|
this.matterbridgeDirectory = path.join(this.homeDirectory, '.matterbridge');
|
|
315
|
-
// Setup the matter environment
|
|
316
217
|
this.environment.vars.set('log.level', MatterLogLevel.INFO);
|
|
317
218
|
this.environment.vars.set('log.format', MatterLogFormat.ANSI);
|
|
318
219
|
this.environment.vars.set('path.root', path.join(this.matterbridgeDirectory, this.matterStorageName));
|
|
319
220
|
this.environment.vars.set('runtime.signals', false);
|
|
320
221
|
this.environment.vars.set('runtime.exitcode', false);
|
|
321
|
-
|
|
322
|
-
this.log = new AnsiLogger({ logName: 'Matterbridge', logTimestampFormat: 4 /* TimestampFormat.TIME_MILLIS */, logLevel: hasParameter('debug') ? "debug" /* LogLevel.DEBUG */ : "info" /* LogLevel.INFO */ });
|
|
323
|
-
// Register process handlers
|
|
222
|
+
this.log = new AnsiLogger({ logName: 'Matterbridge', logTimestampFormat: 4, logLevel: hasParameter('debug') ? "debug" : "info" });
|
|
324
223
|
this.registerProcessHandlers();
|
|
325
|
-
// Initialize nodeStorage and nodeContext
|
|
326
224
|
try {
|
|
327
225
|
this.log.debug(`Creating node storage manager: ${CYAN}${this.nodeStorageName}${db}`);
|
|
328
226
|
this.nodeStorage = new NodeStorageManager({ dir: path.join(this.matterbridgeDirectory, this.nodeStorageName), writeQueue: false, expiredInterval: undefined, logging: false });
|
|
329
227
|
this.log.debug('Creating node storage context for matterbridge');
|
|
330
228
|
this.nodeContext = await this.nodeStorage.createStorage('matterbridge');
|
|
331
|
-
// TODO: Remove this code when node-persist-manager is updated
|
|
332
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
333
229
|
const keys = (await this.nodeStorage?.storage.keys());
|
|
334
230
|
for (const key of keys) {
|
|
335
231
|
this.log.debug(`Checking node storage manager key: ${CYAN}${key}${db}`);
|
|
336
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
337
232
|
await this.nodeStorage?.storage.get(key);
|
|
338
233
|
}
|
|
339
234
|
const storages = await this.nodeStorage.getStorageNames();
|
|
340
235
|
for (const storage of storages) {
|
|
341
236
|
this.log.debug(`Checking storage: ${CYAN}${storage}${db}`);
|
|
342
237
|
const nodeContext = await this.nodeStorage?.createStorage(storage);
|
|
343
|
-
// TODO: Remove this code when node-persist-manager is updated
|
|
344
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
345
238
|
const keys = (await nodeContext?.storage.keys());
|
|
346
239
|
keys.forEach(async (key) => {
|
|
347
240
|
this.log.debug(`Checking key: ${CYAN}${storage}:${key}${db}`);
|
|
348
241
|
await nodeContext?.get(key);
|
|
349
242
|
});
|
|
350
243
|
}
|
|
351
|
-
// Creating a backup of the node storage since it is not corrupted
|
|
352
244
|
this.log.debug('Creating node storage backup...');
|
|
353
245
|
await copyDirectory(path.join(this.matterbridgeDirectory, this.nodeStorageName), path.join(this.matterbridgeDirectory, this.nodeStorageName + '.backup'));
|
|
354
246
|
this.log.debug('Created node storage backup');
|
|
355
247
|
}
|
|
356
248
|
catch (error) {
|
|
357
|
-
// Restoring the backup of the node storage since it is corrupted
|
|
358
249
|
this.log.error(`Error creating node storage manager and context: ${error instanceof Error ? error.message : error}`);
|
|
359
250
|
if (hasParameter('norestore')) {
|
|
360
251
|
this.log.fatal(`The matterbridge node storage is corrupted. Parameter -norestore found: exiting...`);
|
|
@@ -369,46 +260,41 @@ export class Matterbridge extends EventEmitter {
|
|
|
369
260
|
this.log.fatal('Fatal error creating node storage manager and context for matterbridge');
|
|
370
261
|
throw new Error('Fatal error creating node storage manager and context for matterbridge');
|
|
371
262
|
}
|
|
372
|
-
// Set the first port to use for the commissioning server (will be incremented in childbridge mode)
|
|
373
263
|
this.port = getIntParameter('port') ?? (await this.nodeContext.get('matterport', 5540)) ?? 5540;
|
|
374
|
-
// Set the first passcode to use for the commissioning server (will be incremented in childbridge mode)
|
|
375
264
|
this.passcode = getIntParameter('passcode') ?? (await this.nodeContext.get('matterpasscode')) ?? PaseClient.generateRandomPasscode();
|
|
376
|
-
// Set the first discriminator to use for the commissioning server (will be incremented in childbridge mode)
|
|
377
265
|
this.discriminator = getIntParameter('discriminator') ?? (await this.nodeContext.get('matterdiscriminator')) ?? PaseClient.generateRandomDiscriminator();
|
|
378
266
|
this.log.debug(`Initializing server node for Matterbridge... on port ${this.port} with passcode ${this.passcode} and discriminator ${this.discriminator}`);
|
|
379
|
-
// Set matterbridge logger level (context: matterbridgeLogLevel)
|
|
380
267
|
if (hasParameter('logger')) {
|
|
381
268
|
const level = getParameter('logger');
|
|
382
269
|
if (level === 'debug') {
|
|
383
|
-
this.log.logLevel = "debug"
|
|
270
|
+
this.log.logLevel = "debug";
|
|
384
271
|
}
|
|
385
272
|
else if (level === 'info') {
|
|
386
|
-
this.log.logLevel = "info"
|
|
273
|
+
this.log.logLevel = "info";
|
|
387
274
|
}
|
|
388
275
|
else if (level === 'notice') {
|
|
389
|
-
this.log.logLevel = "notice"
|
|
276
|
+
this.log.logLevel = "notice";
|
|
390
277
|
}
|
|
391
278
|
else if (level === 'warn') {
|
|
392
|
-
this.log.logLevel = "warn"
|
|
279
|
+
this.log.logLevel = "warn";
|
|
393
280
|
}
|
|
394
281
|
else if (level === 'error') {
|
|
395
|
-
this.log.logLevel = "error"
|
|
282
|
+
this.log.logLevel = "error";
|
|
396
283
|
}
|
|
397
284
|
else if (level === 'fatal') {
|
|
398
|
-
this.log.logLevel = "fatal"
|
|
285
|
+
this.log.logLevel = "fatal";
|
|
399
286
|
}
|
|
400
287
|
else {
|
|
401
288
|
this.log.warn(`Invalid matterbridge logger level: ${level}. Using default level "info".`);
|
|
402
|
-
this.log.logLevel = "info"
|
|
289
|
+
this.log.logLevel = "info";
|
|
403
290
|
}
|
|
404
291
|
}
|
|
405
292
|
else {
|
|
406
|
-
this.log.logLevel = await this.nodeContext.get('matterbridgeLogLevel', this.matterbridgeInformation.shellyBoard ? "notice"
|
|
293
|
+
this.log.logLevel = await this.nodeContext.get('matterbridgeLogLevel', this.matterbridgeInformation.shellyBoard ? "notice" : "info");
|
|
407
294
|
}
|
|
408
295
|
this.frontend.logLevel = this.log.logLevel;
|
|
409
296
|
MatterbridgeEndpoint.logLevel = this.log.logLevel;
|
|
410
297
|
this.matterbridgeInformation.loggerLevel = this.log.logLevel;
|
|
411
|
-
// Create the file logger for matterbridge (context: matterbridgeFileLog)
|
|
412
298
|
if (hasParameter('filelogger') || (await this.nodeContext.get('matterbridgeFileLog', false))) {
|
|
413
299
|
AnsiLogger.setGlobalLogfile(path.join(this.matterbridgeDirectory, this.matterbrideLoggerFile), this.log.logLevel, true);
|
|
414
300
|
this.matterbridgeInformation.fileLogger = true;
|
|
@@ -417,7 +303,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
417
303
|
this.log.debug(`Matterbridge logLevel: ${this.log.logLevel} fileLoger: ${this.matterbridgeInformation.fileLogger}.`);
|
|
418
304
|
if (this.profile !== undefined)
|
|
419
305
|
this.log.debug(`Matterbridge profile: ${this.profile}.`);
|
|
420
|
-
// Set matter.js logger level, format and logger (context: matterLogLevel)
|
|
421
306
|
if (hasParameter('matterlogger')) {
|
|
422
307
|
const level = getParameter('matterlogger');
|
|
423
308
|
if (level === 'debug') {
|
|
@@ -449,7 +334,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
449
334
|
Logger.format = MatterLogFormat.ANSI;
|
|
450
335
|
Logger.setLogger('default', this.createMatterLogger());
|
|
451
336
|
this.matterbridgeInformation.matterLoggerLevel = Logger.defaultLogLevel;
|
|
452
|
-
// Create the file logger for matter.js (context: matterFileLog)
|
|
453
337
|
if (hasParameter('matterfilelogger') || (await this.nodeContext.get('matterFileLog', false))) {
|
|
454
338
|
this.matterbridgeInformation.matterFileLogger = true;
|
|
455
339
|
Logger.addLogger('matterfilelogger', await this.createMatterFileLogger(path.join(this.matterbridgeDirectory, this.matterLoggerFile), true), {
|
|
@@ -458,7 +342,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
458
342
|
});
|
|
459
343
|
}
|
|
460
344
|
this.log.debug(`Matter logLevel: ${Logger.defaultLogLevel} fileLoger: ${this.matterbridgeInformation.matterFileLogger}.`);
|
|
461
|
-
// Log network interfaces
|
|
462
345
|
const networkInterfaces = os.networkInterfaces();
|
|
463
346
|
const availableAddresses = Object.entries(networkInterfaces);
|
|
464
347
|
const availableInterfaces = Object.keys(networkInterfaces);
|
|
@@ -470,7 +353,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
470
353
|
});
|
|
471
354
|
}
|
|
472
355
|
}
|
|
473
|
-
// Set the interface to use for matter server node mdnsInterface
|
|
474
356
|
if (hasParameter('mdnsinterface')) {
|
|
475
357
|
this.mdnsInterface = getParameter('mdnsinterface');
|
|
476
358
|
}
|
|
@@ -479,7 +361,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
479
361
|
if (this.mdnsInterface === '')
|
|
480
362
|
this.mdnsInterface = undefined;
|
|
481
363
|
}
|
|
482
|
-
// Validate mdnsInterface
|
|
483
364
|
if (this.mdnsInterface) {
|
|
484
365
|
if (!availableInterfaces.includes(this.mdnsInterface)) {
|
|
485
366
|
this.log.error(`Invalid mdnsInterface: ${this.mdnsInterface}. Available interfaces are: ${availableInterfaces.join(', ')}. Using all available interfaces.`);
|
|
@@ -491,7 +372,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
491
372
|
}
|
|
492
373
|
if (this.mdnsInterface)
|
|
493
374
|
this.environment.vars.set('mdns.networkInterface', this.mdnsInterface);
|
|
494
|
-
// Set the listeningAddressIpv4 for the matter commissioning server
|
|
495
375
|
if (hasParameter('ipv4address')) {
|
|
496
376
|
this.ipv4address = getParameter('ipv4address');
|
|
497
377
|
}
|
|
@@ -500,7 +380,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
500
380
|
if (this.ipv4address === '')
|
|
501
381
|
this.ipv4address = undefined;
|
|
502
382
|
}
|
|
503
|
-
// Validate ipv4address
|
|
504
383
|
if (this.ipv4address) {
|
|
505
384
|
let isValid = false;
|
|
506
385
|
for (const [ifaceName, ifaces] of availableAddresses) {
|
|
@@ -515,7 +394,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
515
394
|
this.ipv4address = undefined;
|
|
516
395
|
}
|
|
517
396
|
}
|
|
518
|
-
// Set the listeningAddressIpv6 for the matter commissioning server
|
|
519
397
|
if (hasParameter('ipv6address')) {
|
|
520
398
|
this.ipv6address = getParameter('ipv6address');
|
|
521
399
|
}
|
|
@@ -524,7 +402,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
524
402
|
if (this.ipv6address === '')
|
|
525
403
|
this.ipv6address = undefined;
|
|
526
404
|
}
|
|
527
|
-
// Validate ipv6address
|
|
528
405
|
if (this.ipv6address) {
|
|
529
406
|
let isValid = false;
|
|
530
407
|
for (const [ifaceName, ifaces] of availableAddresses) {
|
|
@@ -544,19 +421,14 @@ export class Matterbridge extends EventEmitter {
|
|
|
544
421
|
this.ipv6address = undefined;
|
|
545
422
|
}
|
|
546
423
|
}
|
|
547
|
-
// Initialize PluginManager
|
|
548
424
|
this.plugins = new PluginManager(this);
|
|
549
425
|
await this.plugins.loadFromStorage();
|
|
550
426
|
this.plugins.logLevel = this.log.logLevel;
|
|
551
|
-
// Initialize DeviceManager
|
|
552
427
|
this.devices = new DeviceManager(this, this.nodeContext);
|
|
553
428
|
this.devices.logLevel = this.log.logLevel;
|
|
554
|
-
// Get the plugins from node storage and create the plugins node storage contexts
|
|
555
429
|
for (const plugin of this.plugins) {
|
|
556
430
|
const packageJson = await this.plugins.parse(plugin);
|
|
557
431
|
if (packageJson === null && !hasParameter('add') && !hasParameter('remove') && !hasParameter('enable') && !hasParameter('disable') && !hasParameter('reset') && !hasParameter('factoryreset')) {
|
|
558
|
-
// Try to reinstall the plugin from npm (for Docker pull and external plugins)
|
|
559
|
-
// We don't do this when the add and other parameters are set because we shut down the process after adding the plugin
|
|
560
432
|
this.log.info(`Error parsing plugin ${plg}${plugin.name}${nf}. Trying to reinstall it from npm.`);
|
|
561
433
|
try {
|
|
562
434
|
await this.spawnCommand('npm', ['install', '-g', plugin.name, '--omit=dev', '--verbose']);
|
|
@@ -578,7 +450,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
578
450
|
await plugin.nodeContext.set('description', plugin.description);
|
|
579
451
|
await plugin.nodeContext.set('author', plugin.author);
|
|
580
452
|
}
|
|
581
|
-
// Log system info and create .matterbridge directory
|
|
582
453
|
await this.logNodeAndSystemInfo();
|
|
583
454
|
this.log.notice(`Matterbridge version ${this.matterbridgeVersion} ` +
|
|
584
455
|
`${hasParameter('bridge') || (!hasParameter('childbridge') && (await this.nodeContext?.get('bridgeMode', '')) === 'bridge') ? 'mode bridge ' : ''}` +
|
|
@@ -586,7 +457,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
586
457
|
`${hasParameter('controller') ? 'mode controller ' : ''}` +
|
|
587
458
|
`${this.restartMode !== '' ? 'restart mode ' + this.restartMode + ' ' : ''}` +
|
|
588
459
|
`running on ${this.systemInformation.osType} (v.${this.systemInformation.osRelease}) platform ${this.systemInformation.osPlatform} arch ${this.systemInformation.osArch}`);
|
|
589
|
-
// Check node version and throw error
|
|
590
460
|
const minNodeVersion = 18;
|
|
591
461
|
const nodeVersion = process.versions.node;
|
|
592
462
|
const versionMajor = parseInt(nodeVersion.split('.')[0]);
|
|
@@ -594,15 +464,9 @@ export class Matterbridge extends EventEmitter {
|
|
|
594
464
|
this.log.error(`Node version ${versionMajor} is not supported. Please upgrade to ${minNodeVersion} or above.`);
|
|
595
465
|
throw new Error(`Node version ${versionMajor} is not supported. Please upgrade to ${minNodeVersion} or above.`);
|
|
596
466
|
}
|
|
597
|
-
// Parse command line
|
|
598
467
|
await this.parseCommandLine();
|
|
599
468
|
this.initialized = true;
|
|
600
469
|
}
|
|
601
|
-
/**
|
|
602
|
-
* Parses the command line arguments and performs the corresponding actions.
|
|
603
|
-
* @private
|
|
604
|
-
* @returns {Promise<void>} A promise that resolves when the command line arguments have been processed, or the process exits.
|
|
605
|
-
*/
|
|
606
470
|
async parseCommandLine() {
|
|
607
471
|
if (hasParameter('help')) {
|
|
608
472
|
this.log.info(`\nUsage: matterbridge [options]\n
|
|
@@ -714,7 +578,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
714
578
|
this.shutdown = true;
|
|
715
579
|
return;
|
|
716
580
|
}
|
|
717
|
-
// Start the matter storage and create the matterbridge context
|
|
718
581
|
try {
|
|
719
582
|
await this.startMatterStorage();
|
|
720
583
|
}
|
|
@@ -722,14 +585,12 @@ export class Matterbridge extends EventEmitter {
|
|
|
722
585
|
this.log.fatal(`Fatal error creating matter storage: ${error instanceof Error ? error.message : error}`);
|
|
723
586
|
throw new Error(`Fatal error creating matter storage: ${error instanceof Error ? error.message : error}`);
|
|
724
587
|
}
|
|
725
|
-
// Clear the matterbridge context if the reset parameter is set
|
|
726
588
|
if (hasParameter('reset') && getParameter('reset') === undefined) {
|
|
727
589
|
this.initialized = true;
|
|
728
590
|
await this.shutdownProcessAndReset();
|
|
729
591
|
this.shutdown = true;
|
|
730
592
|
return;
|
|
731
593
|
}
|
|
732
|
-
// Clear matterbridge plugin context if the reset parameter is set
|
|
733
594
|
if (hasParameter('reset') && getParameter('reset') !== undefined) {
|
|
734
595
|
this.log.debug(`Reset plugin ${getParameter('reset')}`);
|
|
735
596
|
const plugin = this.plugins.get(getParameter('reset'));
|
|
@@ -754,37 +615,30 @@ export class Matterbridge extends EventEmitter {
|
|
|
754
615
|
this.shutdown = true;
|
|
755
616
|
return;
|
|
756
617
|
}
|
|
757
|
-
// Initialize frontend
|
|
758
618
|
if (getIntParameter('frontend') !== 0 || getIntParameter('frontend') === undefined)
|
|
759
619
|
await this.frontend.start(getIntParameter('frontend'));
|
|
760
|
-
// Check in 30 seconds the latest versions
|
|
761
620
|
this.checkUpdateTimeout = setTimeout(async () => {
|
|
762
621
|
const { checkUpdates } = await import('./update.js');
|
|
763
622
|
checkUpdates(this);
|
|
764
623
|
}, 30 * 1000).unref();
|
|
765
|
-
// Check each 24 hours the latest versions
|
|
766
624
|
this.checkUpdateInterval = setInterval(async () => {
|
|
767
625
|
const { checkUpdates } = await import('./update.js');
|
|
768
626
|
checkUpdates(this);
|
|
769
627
|
}, 24 * 60 * 60 * 1000).unref();
|
|
770
|
-
// Start the matterbridge in mode test
|
|
771
628
|
if (hasParameter('test')) {
|
|
772
629
|
this.bridgeMode = 'bridge';
|
|
773
630
|
MatterbridgeEndpoint.bridgeMode = 'bridge';
|
|
774
631
|
return;
|
|
775
632
|
}
|
|
776
|
-
// Start the matterbridge in mode controller
|
|
777
633
|
if (hasParameter('controller')) {
|
|
778
634
|
this.bridgeMode = 'controller';
|
|
779
635
|
await this.startController();
|
|
780
636
|
return;
|
|
781
637
|
}
|
|
782
|
-
// Check if the bridge mode is set and start matterbridge in bridge mode if not set
|
|
783
638
|
if (!hasParameter('bridge') && !hasParameter('childbridge') && (await this.nodeContext?.get('bridgeMode', '')) === '') {
|
|
784
639
|
this.log.info('Setting default matterbridge start mode to bridge');
|
|
785
640
|
await this.nodeContext?.set('bridgeMode', 'bridge');
|
|
786
641
|
}
|
|
787
|
-
// Start matterbridge in bridge mode
|
|
788
642
|
if (hasParameter('bridge') || (!hasParameter('childbridge') && (await this.nodeContext?.get('bridgeMode', '')) === 'bridge')) {
|
|
789
643
|
this.bridgeMode = 'bridge';
|
|
790
644
|
MatterbridgeEndpoint.bridgeMode = 'bridge';
|
|
@@ -792,7 +646,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
792
646
|
await this.startBridge();
|
|
793
647
|
return;
|
|
794
648
|
}
|
|
795
|
-
// Start matterbridge in childbridge mode
|
|
796
649
|
if (hasParameter('childbridge') || (!hasParameter('bridge') && (await this.nodeContext?.get('bridgeMode', '')) === 'childbridge')) {
|
|
797
650
|
this.bridgeMode = 'childbridge';
|
|
798
651
|
MatterbridgeEndpoint.bridgeMode = 'childbridge';
|
|
@@ -801,20 +654,10 @@ export class Matterbridge extends EventEmitter {
|
|
|
801
654
|
return;
|
|
802
655
|
}
|
|
803
656
|
}
|
|
804
|
-
/**
|
|
805
|
-
* Asynchronously loads and starts the registered plugins.
|
|
806
|
-
*
|
|
807
|
-
* This method is responsible for initializing and staarting all enabled plugins.
|
|
808
|
-
* It ensures that each plugin is properly loaded and started before the bridge starts.
|
|
809
|
-
*
|
|
810
|
-
* @returns {Promise<void>} A promise that resolves when all plugins have been loaded and started.
|
|
811
|
-
*/
|
|
812
657
|
async startPlugins() {
|
|
813
|
-
// Check, load and start the plugins
|
|
814
658
|
for (const plugin of this.plugins) {
|
|
815
659
|
plugin.configJson = await this.plugins.loadConfig(plugin);
|
|
816
660
|
plugin.schemaJson = await this.plugins.loadSchema(plugin);
|
|
817
|
-
// Check if the plugin is available
|
|
818
661
|
if (!(await this.plugins.resolve(plugin.path))) {
|
|
819
662
|
this.log.error(`Plugin ${plg}${plugin.name}${er} not found or not validated. Disabling it.`);
|
|
820
663
|
plugin.enabled = false;
|
|
@@ -834,26 +677,20 @@ export class Matterbridge extends EventEmitter {
|
|
|
834
677
|
plugin.addedDevices = undefined;
|
|
835
678
|
plugin.qrPairingCode = undefined;
|
|
836
679
|
plugin.manualPairingCode = undefined;
|
|
837
|
-
this.plugins.load(plugin, true, 'Matterbridge is starting');
|
|
680
|
+
this.plugins.load(plugin, true, 'Matterbridge is starting');
|
|
838
681
|
}
|
|
839
682
|
this.frontend.wssSendRefreshRequired('plugins');
|
|
840
683
|
}
|
|
841
|
-
/**
|
|
842
|
-
* Registers the process handlers for uncaughtException, unhandledRejection, SIGINT and SIGTERM.
|
|
843
|
-
* When either of these signals are received, the cleanup method is called with an appropriate message.
|
|
844
|
-
*/
|
|
845
684
|
registerProcessHandlers() {
|
|
846
685
|
this.log.debug(`Registering uncaughtException and unhandledRejection handlers...`);
|
|
847
686
|
process.removeAllListeners('uncaughtException');
|
|
848
687
|
process.removeAllListeners('unhandledRejection');
|
|
849
688
|
this.exceptionHandler = async (error) => {
|
|
850
689
|
this.log.error('Unhandled Exception detected at:', error.stack || error, rs);
|
|
851
|
-
// await this.cleanup('Unhandled Exception detected, cleaning up...');
|
|
852
690
|
};
|
|
853
691
|
process.on('uncaughtException', this.exceptionHandler);
|
|
854
692
|
this.rejectionHandler = async (reason, promise) => {
|
|
855
693
|
this.log.error('Unhandled Rejection detected at:', promise, 'reason:', reason instanceof Error ? reason.stack : reason, rs);
|
|
856
|
-
// await this.cleanup('Unhandled Rejection detected, cleaning up...');
|
|
857
694
|
};
|
|
858
695
|
process.on('unhandledRejection', this.rejectionHandler);
|
|
859
696
|
this.log.debug(`Registering SIGINT and SIGTERM signal handlers...`);
|
|
@@ -866,9 +703,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
866
703
|
};
|
|
867
704
|
process.on('SIGTERM', this.sigtermHandler);
|
|
868
705
|
}
|
|
869
|
-
/**
|
|
870
|
-
* Deregisters the process uncaughtException, unhandledRejection, SIGINT and SIGTERM signal handlers.
|
|
871
|
-
*/
|
|
872
706
|
deregisterProcesslHandlers() {
|
|
873
707
|
this.log.debug(`Deregistering uncaughtException and unhandledRejection handlers...`);
|
|
874
708
|
if (this.exceptionHandler)
|
|
@@ -885,17 +719,12 @@ export class Matterbridge extends EventEmitter {
|
|
|
885
719
|
process.off('SIGTERM', this.sigtermHandler);
|
|
886
720
|
this.sigtermHandler = undefined;
|
|
887
721
|
}
|
|
888
|
-
/**
|
|
889
|
-
* Logs the node and system information.
|
|
890
|
-
*/
|
|
891
722
|
async logNodeAndSystemInfo() {
|
|
892
|
-
// IP address information
|
|
893
723
|
const networkInterfaces = os.networkInterfaces();
|
|
894
724
|
this.systemInformation.interfaceName = '';
|
|
895
725
|
this.systemInformation.ipv4Address = '';
|
|
896
726
|
this.systemInformation.ipv6Address = '';
|
|
897
727
|
for (const [interfaceName, interfaceDetails] of Object.entries(networkInterfaces)) {
|
|
898
|
-
// this.log.debug(`Checking interface: '${interfaceName}' for '${this.mdnsInterface}'`);
|
|
899
728
|
if (this.mdnsInterface && interfaceName !== this.mdnsInterface)
|
|
900
729
|
continue;
|
|
901
730
|
if (!interfaceDetails) {
|
|
@@ -921,22 +750,19 @@ export class Matterbridge extends EventEmitter {
|
|
|
921
750
|
break;
|
|
922
751
|
}
|
|
923
752
|
}
|
|
924
|
-
// Node information
|
|
925
753
|
this.systemInformation.nodeVersion = process.versions.node;
|
|
926
754
|
const versionMajor = parseInt(this.systemInformation.nodeVersion.split('.')[0]);
|
|
927
755
|
const versionMinor = parseInt(this.systemInformation.nodeVersion.split('.')[1]);
|
|
928
756
|
const versionPatch = parseInt(this.systemInformation.nodeVersion.split('.')[2]);
|
|
929
|
-
// Host system information
|
|
930
757
|
this.systemInformation.hostname = os.hostname();
|
|
931
758
|
this.systemInformation.user = os.userInfo().username;
|
|
932
|
-
this.systemInformation.osType = os.type();
|
|
933
|
-
this.systemInformation.osRelease = os.release();
|
|
934
|
-
this.systemInformation.osPlatform = os.platform();
|
|
935
|
-
this.systemInformation.osArch = os.arch();
|
|
936
|
-
this.systemInformation.totalMemory = (os.totalmem() / 1024 / 1024 / 1024).toFixed(2) + ' GB';
|
|
937
|
-
this.systemInformation.freeMemory = (os.freemem() / 1024 / 1024 / 1024).toFixed(2) + ' GB';
|
|
938
|
-
this.systemInformation.systemUptime = (os.uptime() / 60 / 60).toFixed(2) + ' hours';
|
|
939
|
-
// Log the system information
|
|
759
|
+
this.systemInformation.osType = os.type();
|
|
760
|
+
this.systemInformation.osRelease = os.release();
|
|
761
|
+
this.systemInformation.osPlatform = os.platform();
|
|
762
|
+
this.systemInformation.osArch = os.arch();
|
|
763
|
+
this.systemInformation.totalMemory = (os.totalmem() / 1024 / 1024 / 1024).toFixed(2) + ' GB';
|
|
764
|
+
this.systemInformation.freeMemory = (os.freemem() / 1024 / 1024 / 1024).toFixed(2) + ' GB';
|
|
765
|
+
this.systemInformation.systemUptime = (os.uptime() / 60 / 60).toFixed(2) + ' hours';
|
|
940
766
|
this.log.debug('Host System Information:');
|
|
941
767
|
this.log.debug(`- Hostname: ${this.systemInformation.hostname}`);
|
|
942
768
|
this.log.debug(`- User: ${this.systemInformation.user}`);
|
|
@@ -952,20 +778,16 @@ export class Matterbridge extends EventEmitter {
|
|
|
952
778
|
this.log.debug(`- Total Memory: ${this.systemInformation.totalMemory}`);
|
|
953
779
|
this.log.debug(`- Free Memory: ${this.systemInformation.freeMemory}`);
|
|
954
780
|
this.log.debug(`- System Uptime: ${this.systemInformation.systemUptime}`);
|
|
955
|
-
// Home directory
|
|
956
781
|
this.homeDirectory = getParameter('homedir') ?? os.homedir();
|
|
957
782
|
this.matterbridgeInformation.homeDirectory = this.homeDirectory;
|
|
958
783
|
this.log.debug(`Home Directory: ${this.homeDirectory}`);
|
|
959
|
-
// Package root directory
|
|
960
784
|
const { fileURLToPath } = await import('node:url');
|
|
961
785
|
const currentFileDirectory = path.dirname(fileURLToPath(import.meta.url));
|
|
962
786
|
this.rootDirectory = path.resolve(currentFileDirectory, '../');
|
|
963
787
|
this.matterbridgeInformation.rootDirectory = this.rootDirectory;
|
|
964
788
|
this.log.debug(`Root Directory: ${this.rootDirectory}`);
|
|
965
|
-
// Global node_modules directory
|
|
966
789
|
if (this.nodeContext)
|
|
967
790
|
this.globalModulesDirectory = this.matterbridgeInformation.globalModulesDirectory = await this.nodeContext.get('globalModulesDirectory', '');
|
|
968
|
-
// First run of Matterbridge so the node storage is empty
|
|
969
791
|
if (this.globalModulesDirectory === '') {
|
|
970
792
|
try {
|
|
971
793
|
this.execRunningCount++;
|
|
@@ -981,20 +803,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
981
803
|
}
|
|
982
804
|
else
|
|
983
805
|
this.log.debug(`Global node_modules Directory: ${this.globalModulesDirectory}`);
|
|
984
|
-
/* removed cause is too expensive for the shelly board and not really needed. Why should it change the globalModulesDirectory?
|
|
985
|
-
else {
|
|
986
|
-
this.getGlobalNodeModules()
|
|
987
|
-
.then(async (globalModulesDirectory) => {
|
|
988
|
-
this.globalModulesDirectory = globalModulesDirectory;
|
|
989
|
-
this.matterbridgeInformation.globalModulesDirectory = this.globalModulesDirectory;
|
|
990
|
-
this.log.debug(`Global node_modules Directory: ${this.globalModulesDirectory}`);
|
|
991
|
-
await this.nodeContext?.set<string>('globalModulesDirectory', this.globalModulesDirectory);
|
|
992
|
-
})
|
|
993
|
-
.catch((error) => {
|
|
994
|
-
this.log.error(`Error getting global node_modules directory: ${error}`);
|
|
995
|
-
});
|
|
996
|
-
}*/
|
|
997
|
-
// Create the data directory .matterbridge in the home directory
|
|
998
806
|
this.matterbridgeDirectory = path.join(this.homeDirectory, '.matterbridge');
|
|
999
807
|
this.matterbridgeInformation.matterbridgeDirectory = this.matterbridgeDirectory;
|
|
1000
808
|
try {
|
|
@@ -1018,7 +826,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1018
826
|
}
|
|
1019
827
|
}
|
|
1020
828
|
this.log.debug(`Matterbridge Directory: ${this.matterbridgeDirectory}`);
|
|
1021
|
-
// Create the plugin directory Matterbridge in the home directory
|
|
1022
829
|
this.matterbridgePluginDirectory = path.join(this.homeDirectory, 'Matterbridge');
|
|
1023
830
|
this.matterbridgeInformation.matterbridgePluginDirectory = this.matterbridgePluginDirectory;
|
|
1024
831
|
try {
|
|
@@ -1042,7 +849,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1042
849
|
}
|
|
1043
850
|
}
|
|
1044
851
|
this.log.debug(`Matterbridge Plugin Directory: ${this.matterbridgePluginDirectory}`);
|
|
1045
|
-
// Create the matter cert directory in the home directory
|
|
1046
852
|
this.matterbridgeCertDirectory = path.join(this.homeDirectory, '.mattercert');
|
|
1047
853
|
this.matterbridgeInformation.matterbridgeCertDirectory = this.matterbridgeCertDirectory;
|
|
1048
854
|
try {
|
|
@@ -1066,68 +872,50 @@ export class Matterbridge extends EventEmitter {
|
|
|
1066
872
|
}
|
|
1067
873
|
}
|
|
1068
874
|
this.log.debug(`Matterbridge Matter Cert Directory: ${this.matterbridgeCertDirectory}`);
|
|
1069
|
-
// Matterbridge version
|
|
1070
875
|
const packageJson = JSON.parse(await fs.readFile(path.join(this.rootDirectory, 'package.json'), 'utf-8'));
|
|
1071
876
|
this.matterbridgeVersion = this.matterbridgeLatestVersion = packageJson.version;
|
|
1072
877
|
this.matterbridgeInformation.matterbridgeVersion = this.matterbridgeInformation.matterbridgeLatestVersion = this.matterbridgeVersion;
|
|
1073
878
|
this.log.debug(`Matterbridge Version: ${this.matterbridgeVersion}`);
|
|
1074
|
-
// Matterbridge latest version
|
|
1075
879
|
if (this.nodeContext)
|
|
1076
880
|
this.matterbridgeLatestVersion = await this.nodeContext.get('matterbridgeLatestVersion', this.matterbridgeVersion);
|
|
1077
881
|
this.log.debug(`Matterbridge Latest Version: ${this.matterbridgeLatestVersion}`);
|
|
1078
|
-
// this.getMatterbridgeLatestVersion();
|
|
1079
|
-
// Current working directory
|
|
1080
882
|
const currentDir = process.cwd();
|
|
1081
883
|
this.log.debug(`Current Working Directory: ${currentDir}`);
|
|
1082
|
-
// Command line arguments (excluding 'node' and the script name)
|
|
1083
884
|
const cmdArgs = process.argv.slice(2).join(' ');
|
|
1084
885
|
this.log.debug(`Command Line Arguments: ${cmdArgs}`);
|
|
1085
886
|
}
|
|
1086
|
-
/**
|
|
1087
|
-
* Creates a MatterLogger function to show the matter.js log messages in AnsiLogger (for the frontend).
|
|
1088
|
-
*
|
|
1089
|
-
* @returns {Function} The MatterLogger function.
|
|
1090
|
-
*/
|
|
1091
887
|
createMatterLogger() {
|
|
1092
|
-
const matterLogger = new AnsiLogger({ logName: 'Matter', logTimestampFormat: 4
|
|
888
|
+
const matterLogger = new AnsiLogger({ logName: 'Matter', logTimestampFormat: 4, logLevel: "debug" });
|
|
1093
889
|
return (_level, formattedLog) => {
|
|
1094
890
|
const logger = formattedLog.slice(44, 44 + 20).trim();
|
|
1095
891
|
const message = formattedLog.slice(65);
|
|
1096
892
|
matterLogger.logName = logger;
|
|
1097
893
|
switch (_level) {
|
|
1098
894
|
case MatterLogLevel.DEBUG:
|
|
1099
|
-
matterLogger.log("debug"
|
|
895
|
+
matterLogger.log("debug", message);
|
|
1100
896
|
break;
|
|
1101
897
|
case MatterLogLevel.INFO:
|
|
1102
|
-
matterLogger.log("info"
|
|
898
|
+
matterLogger.log("info", message);
|
|
1103
899
|
break;
|
|
1104
900
|
case MatterLogLevel.NOTICE:
|
|
1105
|
-
matterLogger.log("notice"
|
|
901
|
+
matterLogger.log("notice", message);
|
|
1106
902
|
break;
|
|
1107
903
|
case MatterLogLevel.WARN:
|
|
1108
|
-
matterLogger.log("warn"
|
|
904
|
+
matterLogger.log("warn", message);
|
|
1109
905
|
break;
|
|
1110
906
|
case MatterLogLevel.ERROR:
|
|
1111
|
-
matterLogger.log("error"
|
|
907
|
+
matterLogger.log("error", message);
|
|
1112
908
|
break;
|
|
1113
909
|
case MatterLogLevel.FATAL:
|
|
1114
|
-
matterLogger.log("fatal"
|
|
910
|
+
matterLogger.log("fatal", message);
|
|
1115
911
|
break;
|
|
1116
912
|
default:
|
|
1117
|
-
matterLogger.log("debug"
|
|
913
|
+
matterLogger.log("debug", message);
|
|
1118
914
|
break;
|
|
1119
915
|
}
|
|
1120
916
|
};
|
|
1121
917
|
}
|
|
1122
|
-
/**
|
|
1123
|
-
* Creates a Matter File Logger.
|
|
1124
|
-
*
|
|
1125
|
-
* @param {string} filePath - The path to the log file.
|
|
1126
|
-
* @param {boolean} [unlink=false] - Whether to unlink the log file before creating a new one.
|
|
1127
|
-
* @returns {Function} - A function that logs formatted messages to the log file.
|
|
1128
|
-
*/
|
|
1129
918
|
async createMatterFileLogger(filePath, unlink = false) {
|
|
1130
|
-
// 2024-08-21 08:55:19.488 DEBUG InteractionMessenger Sending DataReport chunk with 28 attributes and 0 events: 1004 bytes
|
|
1131
919
|
let fileSize = 0;
|
|
1132
920
|
if (unlink) {
|
|
1133
921
|
try {
|
|
@@ -1176,21 +964,12 @@ export class Matterbridge extends EventEmitter {
|
|
|
1176
964
|
}
|
|
1177
965
|
};
|
|
1178
966
|
}
|
|
1179
|
-
/**
|
|
1180
|
-
* Restarts the process by exiting the current instance and loading a new instance.
|
|
1181
|
-
*/
|
|
1182
967
|
async restartProcess() {
|
|
1183
968
|
await this.cleanup('restarting...', true);
|
|
1184
969
|
}
|
|
1185
|
-
/**
|
|
1186
|
-
* Shut down the process by exiting the current process.
|
|
1187
|
-
*/
|
|
1188
970
|
async shutdownProcess() {
|
|
1189
971
|
await this.cleanup('shutting down...', false);
|
|
1190
972
|
}
|
|
1191
|
-
/**
|
|
1192
|
-
* Update matterbridge and and shut down the process.
|
|
1193
|
-
*/
|
|
1194
973
|
async updateProcess() {
|
|
1195
974
|
this.log.info('Updating matterbridge...');
|
|
1196
975
|
try {
|
|
@@ -1203,72 +982,51 @@ export class Matterbridge extends EventEmitter {
|
|
|
1203
982
|
this.frontend.wssSendRestartRequired();
|
|
1204
983
|
await this.cleanup('updating...', false);
|
|
1205
984
|
}
|
|
1206
|
-
/**
|
|
1207
|
-
* Unregister all devices and shut down the process.
|
|
1208
|
-
*/
|
|
1209
985
|
async unregisterAndShutdownProcess() {
|
|
1210
986
|
this.log.info('Unregistering all devices and shutting down...');
|
|
1211
987
|
for (const plugin of this.plugins) {
|
|
1212
988
|
await this.removeAllBridgedEndpoints(plugin.name, 250);
|
|
1213
989
|
}
|
|
1214
990
|
this.log.debug('Waiting for the MessageExchange to finish...');
|
|
1215
|
-
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
991
|
+
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
1216
992
|
this.log.debug('Cleaning up and shutting down...');
|
|
1217
993
|
await this.cleanup('unregistered all devices and shutting down...', false);
|
|
1218
994
|
}
|
|
1219
|
-
/**
|
|
1220
|
-
* Reset commissioning and shut down the process.
|
|
1221
|
-
*/
|
|
1222
995
|
async shutdownProcessAndReset() {
|
|
1223
996
|
await this.cleanup('shutting down with reset...', false);
|
|
1224
997
|
}
|
|
1225
|
-
/**
|
|
1226
|
-
* Factory reset and shut down the process.
|
|
1227
|
-
*/
|
|
1228
998
|
async shutdownProcessAndFactoryReset() {
|
|
1229
999
|
await this.cleanup('shutting down with factory reset...', false);
|
|
1230
1000
|
}
|
|
1231
|
-
/**
|
|
1232
|
-
* Cleans up the Matterbridge instance.
|
|
1233
|
-
* @param message - The cleanup message.
|
|
1234
|
-
* @param restart - Indicates whether to restart the instance after cleanup. Default is `false`.
|
|
1235
|
-
* @returns A promise that resolves when the cleanup is completed.
|
|
1236
|
-
*/
|
|
1237
1001
|
async cleanup(message, restart = false) {
|
|
1238
1002
|
if (this.initialized && !this.hasCleanupStarted) {
|
|
1239
1003
|
this.hasCleanupStarted = true;
|
|
1240
1004
|
this.log.info(message);
|
|
1241
|
-
// Clear the start matter interval
|
|
1242
1005
|
if (this.startMatterInterval) {
|
|
1243
1006
|
clearInterval(this.startMatterInterval);
|
|
1244
1007
|
this.startMatterInterval = undefined;
|
|
1245
1008
|
this.log.debug('Start matter interval cleared');
|
|
1246
1009
|
}
|
|
1247
|
-
// Clear the check update timeout
|
|
1248
1010
|
if (this.checkUpdateTimeout) {
|
|
1249
1011
|
clearInterval(this.checkUpdateTimeout);
|
|
1250
1012
|
this.checkUpdateTimeout = undefined;
|
|
1251
1013
|
this.log.debug('Check update timeout cleared');
|
|
1252
1014
|
}
|
|
1253
|
-
// Clear the check update interval
|
|
1254
1015
|
if (this.checkUpdateInterval) {
|
|
1255
1016
|
clearInterval(this.checkUpdateInterval);
|
|
1256
1017
|
this.checkUpdateInterval = undefined;
|
|
1257
1018
|
this.log.debug('Check update interval cleared');
|
|
1258
1019
|
}
|
|
1259
|
-
// Clear the configure timeout
|
|
1260
1020
|
if (this.configureTimeout) {
|
|
1261
1021
|
clearTimeout(this.configureTimeout);
|
|
1262
1022
|
this.configureTimeout = undefined;
|
|
1263
1023
|
this.log.debug('Matterbridge configure timeout cleared');
|
|
1264
1024
|
}
|
|
1265
|
-
// Clear the reachability timeout
|
|
1266
1025
|
if (this.reachabilityTimeout) {
|
|
1267
1026
|
clearTimeout(this.reachabilityTimeout);
|
|
1268
1027
|
this.reachabilityTimeout = undefined;
|
|
1269
1028
|
this.log.debug('Matterbridge reachability timeout cleared');
|
|
1270
1029
|
}
|
|
1271
|
-
// Calling the shutdown method of each plugin and clear the plugins reachability timeout
|
|
1272
1030
|
for (const plugin of this.plugins) {
|
|
1273
1031
|
if (!plugin.enabled || plugin.error)
|
|
1274
1032
|
continue;
|
|
@@ -1279,10 +1037,9 @@ export class Matterbridge extends EventEmitter {
|
|
|
1279
1037
|
this.log.debug(`Plugin ${plg}${plugin.name}${db} reachability timeout cleared`);
|
|
1280
1038
|
}
|
|
1281
1039
|
}
|
|
1282
|
-
// Stopping matter server nodes
|
|
1283
1040
|
this.log.notice(`Stopping matter server nodes in ${this.bridgeMode} mode...`);
|
|
1284
1041
|
this.log.debug('Waiting for the MessageExchange to finish...');
|
|
1285
|
-
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
1042
|
+
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
1286
1043
|
if (this.bridgeMode === 'bridge') {
|
|
1287
1044
|
if (this.serverNode) {
|
|
1288
1045
|
await this.stopServerNode(this.serverNode);
|
|
@@ -1298,7 +1055,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1298
1055
|
}
|
|
1299
1056
|
}
|
|
1300
1057
|
this.log.notice('Stopped matter server nodes');
|
|
1301
|
-
// Matter commisioning reset
|
|
1302
1058
|
if (message === 'shutting down with reset...') {
|
|
1303
1059
|
this.log.info('Resetting Matterbridge commissioning information...');
|
|
1304
1060
|
await this.matterStorageManager?.createContext('events')?.clearAll();
|
|
@@ -1308,37 +1064,17 @@ export class Matterbridge extends EventEmitter {
|
|
|
1308
1064
|
await this.matterbridgeContext?.clearAll();
|
|
1309
1065
|
this.log.info('Matter storage reset done! Remove the bridge from the controller.');
|
|
1310
1066
|
}
|
|
1311
|
-
// Stop matter storage
|
|
1312
1067
|
await this.stopMatterStorage();
|
|
1313
|
-
// Stop the frontend
|
|
1314
1068
|
await this.frontend.stop();
|
|
1315
|
-
// Remove the matterfilelogger
|
|
1316
1069
|
try {
|
|
1317
1070
|
Logger.removeLogger('matterfilelogger');
|
|
1318
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
1319
1071
|
}
|
|
1320
1072
|
catch (error) {
|
|
1321
|
-
// this.log.debug(`Error removing the matterfilelogger for file ${CYAN}${path.join(this.matterbridgeDirectory, this.matterLoggerFile)}${er}: ${error instanceof Error ? error.message : error}`);
|
|
1322
1073
|
}
|
|
1323
|
-
// Serialize registeredDevices
|
|
1324
1074
|
if (this.nodeStorage && this.nodeContext) {
|
|
1325
|
-
/*
|
|
1326
|
-
TODO: Implement serialization of registered devices in edge mode
|
|
1327
|
-
this.log.info('Saving registered devices...');
|
|
1328
|
-
const serializedRegisteredDevices: SerializedMatterbridgeEndpoint[] = [];
|
|
1329
|
-
this.devices.forEach(async (device) => {
|
|
1330
|
-
const serializedMatterbridgeDevice = MatterbridgeEndpoint.serialize(device);
|
|
1331
|
-
// this.log.info(`- ${serializedMatterbridgeDevice.deviceName}${rs}\n`, serializedMatterbridgeDevice);
|
|
1332
|
-
if (serializedMatterbridgeDevice) serializedRegisteredDevices.push(serializedMatterbridgeDevice);
|
|
1333
|
-
});
|
|
1334
|
-
await this.nodeContext.set<SerializedMatterbridgeEndpoint[]>('devices', serializedRegisteredDevices);
|
|
1335
|
-
this.log.info(`Saved registered devices (${serializedRegisteredDevices?.length})`);
|
|
1336
|
-
*/
|
|
1337
|
-
// Clear nodeContext and nodeStorage (they just need 1000ms to write the data to disk)
|
|
1338
1075
|
this.log.debug(`Closing node storage context for ${plg}Matterbridge${db}...`);
|
|
1339
1076
|
await this.nodeContext.close();
|
|
1340
1077
|
this.nodeContext = undefined;
|
|
1341
|
-
// Clear nodeContext for each plugin (they just need 1000ms to write the data to disk)
|
|
1342
1078
|
for (const plugin of this.plugins) {
|
|
1343
1079
|
if (plugin.nodeContext) {
|
|
1344
1080
|
this.log.debug(`Closing node storage context for plugin ${plg}${plugin.name}${db}...`);
|
|
@@ -1355,10 +1091,8 @@ export class Matterbridge extends EventEmitter {
|
|
|
1355
1091
|
}
|
|
1356
1092
|
this.plugins.clear();
|
|
1357
1093
|
this.devices.clear();
|
|
1358
|
-
// Factory reset
|
|
1359
1094
|
if (message === 'shutting down with factory reset...') {
|
|
1360
1095
|
try {
|
|
1361
|
-
// Delete old matter storage file and backup
|
|
1362
1096
|
const file = path.join(this.matterbridgeDirectory, 'matterbridge' + (getParameter('profile') ? '.' + getParameter('profile') : '') + '.json');
|
|
1363
1097
|
this.log.info(`Unlinking old matter storage file: ${file}`);
|
|
1364
1098
|
await fs.unlink(file);
|
|
@@ -1372,7 +1106,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1372
1106
|
}
|
|
1373
1107
|
}
|
|
1374
1108
|
try {
|
|
1375
|
-
// Delete matter node storage directory with its subdirectories and backup
|
|
1376
1109
|
const dir = path.join(this.matterbridgeDirectory, 'matterstorage' + (getParameter('profile') ? '.' + getParameter('profile') : ''));
|
|
1377
1110
|
this.log.info(`Removing matter node storage directory: ${dir}`);
|
|
1378
1111
|
await fs.rm(dir, { recursive: true });
|
|
@@ -1386,7 +1119,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1386
1119
|
}
|
|
1387
1120
|
}
|
|
1388
1121
|
try {
|
|
1389
|
-
// Delete node storage directory with its subdirectories and backup
|
|
1390
1122
|
const dir = path.join(this.matterbridgeDirectory, 'storage' + (getParameter('profile') ? '.' + getParameter('profile') : ''));
|
|
1391
1123
|
this.log.info(`Removing storage directory: ${dir}`);
|
|
1392
1124
|
await fs.rm(dir, { recursive: true });
|
|
@@ -1401,13 +1133,12 @@ export class Matterbridge extends EventEmitter {
|
|
|
1401
1133
|
}
|
|
1402
1134
|
this.log.info('Factory reset done! Remove all paired fabrics from the controllers.');
|
|
1403
1135
|
}
|
|
1404
|
-
// Deregisters the process handlers
|
|
1405
1136
|
this.deregisterProcesslHandlers();
|
|
1406
1137
|
if (restart) {
|
|
1407
1138
|
if (message === 'updating...') {
|
|
1408
1139
|
this.log.info('Cleanup completed. Updating...');
|
|
1409
1140
|
Matterbridge.instance = undefined;
|
|
1410
|
-
this.emit('update');
|
|
1141
|
+
this.emit('update');
|
|
1411
1142
|
}
|
|
1412
1143
|
else if (message === 'restarting...') {
|
|
1413
1144
|
this.log.info('Cleanup completed. Restarting...');
|
|
@@ -1427,14 +1158,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1427
1158
|
this.log.debug('Cleanup already started...');
|
|
1428
1159
|
}
|
|
1429
1160
|
}
|
|
1430
|
-
/**
|
|
1431
|
-
* Creates and configures the server node for an accessory plugin for a given device.
|
|
1432
|
-
*
|
|
1433
|
-
* @param {RegisteredPlugin} plugin - The plugin to configure.
|
|
1434
|
-
* @param {MatterbridgeEndpoint} device - The device to associate with the plugin.
|
|
1435
|
-
* @param {boolean} [start=false] - Whether to start the server node after adding the device.
|
|
1436
|
-
* @returns {Promise<void>} A promise that resolves when the server node for the accessory plugin is created and configured.
|
|
1437
|
-
*/
|
|
1438
1161
|
async createAccessoryPlugin(plugin, device, start = false) {
|
|
1439
1162
|
if (!plugin.locked && device.deviceName && device.vendorId && device.productId && device.vendorName && device.productName) {
|
|
1440
1163
|
plugin.locked = true;
|
|
@@ -1447,13 +1170,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1447
1170
|
await this.startServerNode(plugin.serverNode);
|
|
1448
1171
|
}
|
|
1449
1172
|
}
|
|
1450
|
-
/**
|
|
1451
|
-
* Creates and configures the server node for a dynamic plugin.
|
|
1452
|
-
*
|
|
1453
|
-
* @param {RegisteredPlugin} plugin - The plugin to configure.
|
|
1454
|
-
* @param {boolean} [start=false] - Whether to start the server node after adding the aggregator node.
|
|
1455
|
-
* @returns {Promise<void>} A promise that resolves when the server node for the dynamic plugin is created and configured.
|
|
1456
|
-
*/
|
|
1457
1173
|
async createDynamicPlugin(plugin, start = false) {
|
|
1458
1174
|
if (!plugin.locked) {
|
|
1459
1175
|
plugin.locked = true;
|
|
@@ -1465,13 +1181,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
1465
1181
|
await this.startServerNode(plugin.serverNode);
|
|
1466
1182
|
}
|
|
1467
1183
|
}
|
|
1468
|
-
/**
|
|
1469
|
-
* Starts the Matterbridge in bridge mode.
|
|
1470
|
-
* @private
|
|
1471
|
-
* @returns {Promise<void>} A promise that resolves when the Matterbridge is started.
|
|
1472
|
-
*/
|
|
1473
1184
|
async startBridge() {
|
|
1474
|
-
// Plugins are configured by a timer when matter server is started and plugin.configured is set to true
|
|
1475
1185
|
if (!this.matterStorageManager)
|
|
1476
1186
|
throw new Error('No storage manager initialized');
|
|
1477
1187
|
if (!this.matterbridgeContext)
|
|
@@ -1509,9 +1219,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
1509
1219
|
clearInterval(this.startMatterInterval);
|
|
1510
1220
|
this.startMatterInterval = undefined;
|
|
1511
1221
|
this.log.debug('Cleared startMatterInterval interval for Matterbridge');
|
|
1512
|
-
// Start the Matter server node
|
|
1513
1222
|
this.startServerNode(this.serverNode);
|
|
1514
|
-
// Configure the plugins
|
|
1515
1223
|
this.configureTimeout = setTimeout(async () => {
|
|
1516
1224
|
for (const plugin of this.plugins) {
|
|
1517
1225
|
if (!plugin.enabled || !plugin.loaded || !plugin.started || plugin.error)
|
|
@@ -1529,7 +1237,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1529
1237
|
}
|
|
1530
1238
|
this.frontend.wssSendRefreshRequired('plugins');
|
|
1531
1239
|
}, 30 * 1000);
|
|
1532
|
-
// Setting reachability to true
|
|
1533
1240
|
this.reachabilityTimeout = setTimeout(() => {
|
|
1534
1241
|
this.log.info(`Setting reachability to true for ${plg}Matterbridge${db}`);
|
|
1535
1242
|
if (this.aggregatorNode)
|
|
@@ -1538,11 +1245,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1538
1245
|
}, 60 * 1000);
|
|
1539
1246
|
}, 1000);
|
|
1540
1247
|
}
|
|
1541
|
-
/**
|
|
1542
|
-
* Starts the Matterbridge in childbridge mode.
|
|
1543
|
-
* @private
|
|
1544
|
-
* @returns {Promise<void>} A promise that resolves when the Matterbridge is started.
|
|
1545
|
-
*/
|
|
1546
1248
|
async startChildbridge() {
|
|
1547
1249
|
if (!this.matterStorageManager)
|
|
1548
1250
|
throw new Error('No storage manager initialized');
|
|
@@ -1587,7 +1289,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1587
1289
|
clearInterval(this.startMatterInterval);
|
|
1588
1290
|
this.startMatterInterval = undefined;
|
|
1589
1291
|
this.log.debug('Cleared startMatterInterval interval in childbridge mode');
|
|
1590
|
-
// Configure the plugins
|
|
1591
1292
|
this.configureTimeout = setTimeout(async () => {
|
|
1592
1293
|
for (const plugin of this.plugins) {
|
|
1593
1294
|
if (!plugin.enabled || !plugin.loaded || !plugin.started || plugin.error)
|
|
@@ -1624,9 +1325,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
1624
1325
|
this.log.error(`Node storage context not found for plugin ${plg}${plugin.name}${er}`);
|
|
1625
1326
|
continue;
|
|
1626
1327
|
}
|
|
1627
|
-
// Start the Matter server node
|
|
1628
1328
|
this.startServerNode(plugin.serverNode);
|
|
1629
|
-
// Setting reachability to true
|
|
1630
1329
|
plugin.reachabilityTimeout = setTimeout(() => {
|
|
1631
1330
|
this.log.info(`Setting reachability to true for ${plg}${plugin.name}${db} type ${plugin.type} server node ${plugin.serverNode !== undefined} aggragator node ${plugin.aggregatorNode !== undefined} device ${plugin.device !== undefined}`);
|
|
1632
1331
|
if (plugin.type === 'DynamicPlatform' && plugin.aggregatorNode)
|
|
@@ -1636,219 +1335,9 @@ export class Matterbridge extends EventEmitter {
|
|
|
1636
1335
|
}
|
|
1637
1336
|
}, 1000);
|
|
1638
1337
|
}
|
|
1639
|
-
/**
|
|
1640
|
-
* Starts the Matterbridge controller.
|
|
1641
|
-
* @private
|
|
1642
|
-
* @returns {Promise<void>} A promise that resolves when the Matterbridge is started.
|
|
1643
|
-
*/
|
|
1644
1338
|
async startController() {
|
|
1645
|
-
/*
|
|
1646
|
-
if (!this.storageManager) {
|
|
1647
|
-
this.log.error('No storage manager initialized');
|
|
1648
|
-
await this.cleanup('No storage manager initialized');
|
|
1649
|
-
return;
|
|
1650
|
-
}
|
|
1651
|
-
this.log.info('Creating context: mattercontrollerContext');
|
|
1652
|
-
this.mattercontrollerContext = this.storageManager.createContext('mattercontrollerContext');
|
|
1653
|
-
if (!this.mattercontrollerContext) {
|
|
1654
|
-
this.log.error('No storage context mattercontrollerContext initialized');
|
|
1655
|
-
await this.cleanup('No storage context mattercontrollerContext initialized');
|
|
1656
|
-
return;
|
|
1657
|
-
}
|
|
1658
|
-
|
|
1659
|
-
this.log.debug('Starting matterbridge in mode', this.bridgeMode);
|
|
1660
|
-
this.matterServer = await this.createMatterServer(this.storageManager);
|
|
1661
|
-
this.log.info('Creating matter commissioning controller');
|
|
1662
|
-
this.commissioningController = new CommissioningController({
|
|
1663
|
-
autoConnect: false,
|
|
1664
|
-
});
|
|
1665
|
-
this.log.info('Adding matter commissioning controller to matter server');
|
|
1666
|
-
await this.matterServer.addCommissioningController(this.commissioningController);
|
|
1667
|
-
|
|
1668
|
-
this.log.info('Starting matter server');
|
|
1669
|
-
await this.matterServer.start();
|
|
1670
|
-
this.log.info('Matter server started');
|
|
1671
|
-
|
|
1672
|
-
if (hasParameter('pairingcode')) {
|
|
1673
|
-
this.log.info('Pairing device with pairingcode:', getParameter('pairingcode'));
|
|
1674
|
-
const pairingCode = getParameter('pairingcode');
|
|
1675
|
-
const ip = this.mattercontrollerContext.has('ip') ? this.mattercontrollerContext.get<string>('ip') : undefined;
|
|
1676
|
-
const port = this.mattercontrollerContext.has('port') ? this.mattercontrollerContext.get<number>('port') : undefined;
|
|
1677
|
-
|
|
1678
|
-
let longDiscriminator, setupPin, shortDiscriminator;
|
|
1679
|
-
if (pairingCode !== undefined) {
|
|
1680
|
-
const pairingCodeCodec = ManualPairingCodeCodec.decode(pairingCode);
|
|
1681
|
-
shortDiscriminator = pairingCodeCodec.shortDiscriminator;
|
|
1682
|
-
longDiscriminator = undefined;
|
|
1683
|
-
setupPin = pairingCodeCodec.passcode;
|
|
1684
|
-
this.log.info(`Data extracted from pairing code: ${Logger.toJSON(pairingCodeCodec)}`);
|
|
1685
|
-
} else {
|
|
1686
|
-
longDiscriminator = await this.mattercontrollerContext.get('longDiscriminator', 3840);
|
|
1687
|
-
if (longDiscriminator > 4095) throw new Error('Discriminator value must be less than 4096');
|
|
1688
|
-
setupPin = this.mattercontrollerContext.get('pin', 20202021);
|
|
1689
|
-
}
|
|
1690
|
-
if ((shortDiscriminator === undefined && longDiscriminator === undefined) || setupPin === undefined) {
|
|
1691
|
-
throw new Error('Please specify the longDiscriminator of the device to commission with -longDiscriminator or provide a valid passcode with -passcode');
|
|
1692
|
-
}
|
|
1693
|
-
|
|
1694
|
-
const commissioningOptions: ControllerCommissioningFlowOptions = {
|
|
1695
|
-
regulatoryLocation: GeneralCommissioning.RegulatoryLocationType.IndoorOutdoor,
|
|
1696
|
-
regulatoryCountryCode: 'XX',
|
|
1697
|
-
};
|
|
1698
|
-
const options = {
|
|
1699
|
-
commissioning: commissioningOptions,
|
|
1700
|
-
discovery: {
|
|
1701
|
-
knownAddress: ip !== undefined && port !== undefined ? { ip, port, type: 'udp' } : undefined,
|
|
1702
|
-
identifierData: longDiscriminator !== undefined ? { longDiscriminator } : shortDiscriminator !== undefined ? { shortDiscriminator } : {},
|
|
1703
|
-
},
|
|
1704
|
-
passcode: setupPin,
|
|
1705
|
-
} as NodeCommissioningOptions;
|
|
1706
|
-
this.log.info('Commissioning with options:', options);
|
|
1707
|
-
const nodeId = await this.commissioningController.commissionNode(options);
|
|
1708
|
-
this.log.info(`Commissioning successfully done with nodeId: ${nodeId}`);
|
|
1709
|
-
this.log.info('ActiveSessionInformation:', this.commissioningController.getActiveSessionInformation());
|
|
1710
|
-
} // (hasParameter('pairingcode'))
|
|
1711
|
-
|
|
1712
|
-
if (hasParameter('unpairall')) {
|
|
1713
|
-
this.log.info('***Commissioning controller unpairing all nodes...');
|
|
1714
|
-
const nodeIds = this.commissioningController.getCommissionedNodes();
|
|
1715
|
-
for (const nodeId of nodeIds) {
|
|
1716
|
-
this.log.info('***Commissioning controller unpairing node:', nodeId);
|
|
1717
|
-
await this.commissioningController.removeNode(nodeId);
|
|
1718
|
-
}
|
|
1719
|
-
return;
|
|
1720
|
-
}
|
|
1721
|
-
|
|
1722
|
-
if (hasParameter('discover')) {
|
|
1723
|
-
// const discover = await this.commissioningController.discoverCommissionableDevices({ productId: 0x8000, deviceType: 0xfff1 });
|
|
1724
|
-
// console.log(discover);
|
|
1725
|
-
}
|
|
1726
|
-
|
|
1727
|
-
if (!this.commissioningController.isCommissioned()) {
|
|
1728
|
-
this.log.info('***Commissioning controller is not commissioned: use matterbridge -controller -pairingcode [pairingcode] to commission a device');
|
|
1729
|
-
return;
|
|
1730
|
-
}
|
|
1731
|
-
|
|
1732
|
-
const nodeIds = this.commissioningController.getCommissionedNodes();
|
|
1733
|
-
this.log.info(`***Commissioning controller is commissioned ${this.commissioningController.isCommissioned()} and has ${nodeIds.length} nodes commisioned: `);
|
|
1734
|
-
for (const nodeId of nodeIds) {
|
|
1735
|
-
this.log.info(`***Connecting to commissioned node: ${nodeId}`);
|
|
1736
|
-
|
|
1737
|
-
const node = await this.commissioningController.connectNode(nodeId, {
|
|
1738
|
-
autoSubscribe: false,
|
|
1739
|
-
attributeChangedCallback: (peerNodeId, { path: { nodeId, clusterId, endpointId, attributeName }, value }) =>
|
|
1740
|
-
this.log.info(`***Commissioning controller attributeChangedCallback ${peerNodeId}: attribute ${nodeId}/${endpointId}/${clusterId}/${attributeName} changed to ${Logger.toJSON(value)}`),
|
|
1741
|
-
eventTriggeredCallback: (peerNodeId, { path: { nodeId, clusterId, endpointId, eventName }, events }) =>
|
|
1742
|
-
this.log.info(`***Commissioning controller eventTriggeredCallback ${peerNodeId}: Event ${nodeId}/${endpointId}/${clusterId}/${eventName} triggered with ${Logger.toJSON(events)}`),
|
|
1743
|
-
stateInformationCallback: (peerNodeId, info) => {
|
|
1744
|
-
switch (info) {
|
|
1745
|
-
case NodeStateInformation.Connected:
|
|
1746
|
-
this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} connected`);
|
|
1747
|
-
break;
|
|
1748
|
-
case NodeStateInformation.Disconnected:
|
|
1749
|
-
this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} disconnected`);
|
|
1750
|
-
break;
|
|
1751
|
-
case NodeStateInformation.Reconnecting:
|
|
1752
|
-
this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} reconnecting`);
|
|
1753
|
-
break;
|
|
1754
|
-
case NodeStateInformation.WaitingForDeviceDiscovery:
|
|
1755
|
-
this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} waiting for device discovery`);
|
|
1756
|
-
break;
|
|
1757
|
-
case NodeStateInformation.StructureChanged:
|
|
1758
|
-
this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} structure changed`);
|
|
1759
|
-
break;
|
|
1760
|
-
case NodeStateInformation.Decommissioned:
|
|
1761
|
-
this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} decommissioned`);
|
|
1762
|
-
break;
|
|
1763
|
-
default:
|
|
1764
|
-
this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} NodeStateInformation.${info}`);
|
|
1765
|
-
break;
|
|
1766
|
-
}
|
|
1767
|
-
},
|
|
1768
|
-
});
|
|
1769
|
-
|
|
1770
|
-
node.logStructure();
|
|
1771
|
-
|
|
1772
|
-
// Get the interaction client
|
|
1773
|
-
this.log.info('Getting the interaction client');
|
|
1774
|
-
const interactionClient = await node.getInteractionClient();
|
|
1775
|
-
let cluster;
|
|
1776
|
-
let attributes;
|
|
1777
|
-
|
|
1778
|
-
// Log BasicInformationCluster
|
|
1779
|
-
cluster = BasicInformationCluster;
|
|
1780
|
-
attributes = await interactionClient.getMultipleAttributes({
|
|
1781
|
-
attributes: [{ clusterId: cluster.id }],
|
|
1782
|
-
});
|
|
1783
|
-
if (attributes.length > 0) this.log.info(`Cluster: ${idn}${cluster.name}${rs}${nf} attributes:`);
|
|
1784
|
-
attributes.forEach((attribute) => {
|
|
1785
|
-
this.log.info(
|
|
1786
|
-
`- 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}`,
|
|
1787
|
-
);
|
|
1788
|
-
});
|
|
1789
|
-
|
|
1790
|
-
// Log PowerSourceCluster
|
|
1791
|
-
cluster = PowerSourceCluster;
|
|
1792
|
-
attributes = await interactionClient.getMultipleAttributes({
|
|
1793
|
-
attributes: [{ clusterId: cluster.id }],
|
|
1794
|
-
});
|
|
1795
|
-
if (attributes.length > 0) this.log.info(`Cluster: ${idn}${cluster.name}${rs}${nf} attributes:`);
|
|
1796
|
-
attributes.forEach((attribute) => {
|
|
1797
|
-
this.log.info(
|
|
1798
|
-
`- 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}`,
|
|
1799
|
-
);
|
|
1800
|
-
});
|
|
1801
|
-
|
|
1802
|
-
// Log ThreadNetworkDiagnostics
|
|
1803
|
-
cluster = ThreadNetworkDiagnosticsCluster;
|
|
1804
|
-
attributes = await interactionClient.getMultipleAttributes({
|
|
1805
|
-
attributes: [{ clusterId: cluster.id }],
|
|
1806
|
-
});
|
|
1807
|
-
if (attributes.length > 0) this.log.info(`Cluster: ${idn}${cluster.name}${rs}${nf} attributes:`);
|
|
1808
|
-
attributes.forEach((attribute) => {
|
|
1809
|
-
this.log.info(
|
|
1810
|
-
`- 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}`,
|
|
1811
|
-
);
|
|
1812
|
-
});
|
|
1813
|
-
|
|
1814
|
-
// Log SwitchCluster
|
|
1815
|
-
cluster = SwitchCluster;
|
|
1816
|
-
attributes = await interactionClient.getMultipleAttributes({
|
|
1817
|
-
attributes: [{ clusterId: cluster.id }],
|
|
1818
|
-
});
|
|
1819
|
-
if (attributes.length > 0) this.log.info(`Cluster: ${idn}${cluster.name}${rs}${nf} attributes:`);
|
|
1820
|
-
attributes.forEach((attribute) => {
|
|
1821
|
-
this.log.info(
|
|
1822
|
-
`- 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}`,
|
|
1823
|
-
);
|
|
1824
|
-
});
|
|
1825
|
-
|
|
1826
|
-
this.log.info('Subscribing to all attributes and events');
|
|
1827
|
-
await node.subscribeAllAttributesAndEvents({
|
|
1828
|
-
ignoreInitialTriggers: false,
|
|
1829
|
-
attributeChangedCallback: ({ path: { nodeId, clusterId, endpointId, attributeName }, version, value }) =>
|
|
1830
|
-
this.log.info(
|
|
1831
|
-
`***${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}`,
|
|
1832
|
-
),
|
|
1833
|
-
eventTriggeredCallback: ({ path: { nodeId, clusterId, endpointId, eventName }, events }) => {
|
|
1834
|
-
this.log.info(
|
|
1835
|
-
`***${db}Commissioning controller eventTriggeredCallback: event ${BLUE}${nodeId}${db}/${or}${endpointId}${db}/${hk}${getClusterNameById(clusterId)}${db}/${zb}${eventName}${db} triggered with ${debugStringify(events ?? { none: true })}`,
|
|
1836
|
-
);
|
|
1837
|
-
},
|
|
1838
|
-
});
|
|
1839
|
-
this.log.info('Subscribed to all attributes and events');
|
|
1840
|
-
}
|
|
1841
|
-
*/
|
|
1842
1339
|
}
|
|
1843
|
-
/** ***********************************************************************************************************************************/
|
|
1844
|
-
/** Matter.js methods */
|
|
1845
|
-
/** ***********************************************************************************************************************************/
|
|
1846
|
-
/**
|
|
1847
|
-
* Starts the matter storage process with name Matterbridge.
|
|
1848
|
-
* @returns {Promise<void>} - A promise that resolves when the storage process is started.
|
|
1849
|
-
*/
|
|
1850
1340
|
async startMatterStorage() {
|
|
1851
|
-
// Setup Matter storage
|
|
1852
1341
|
this.log.info(`Starting matter node storage...`);
|
|
1853
1342
|
this.matterStorageService = this.environment.get(StorageService);
|
|
1854
1343
|
this.log.info(`Matter node storage service created: ${this.matterStorageService.location}`);
|
|
@@ -1856,25 +1345,13 @@ export class Matterbridge extends EventEmitter {
|
|
|
1856
1345
|
this.log.info('Matter node storage manager "Matterbridge" created');
|
|
1857
1346
|
this.matterbridgeContext = await this.createServerNodeContext('Matterbridge', 'Matterbridge', bridge.code, this.aggregatorVendorId, this.aggregatorVendorName, this.aggregatorProductId, this.aggregatorProductName);
|
|
1858
1347
|
this.log.info('Matter node storage started');
|
|
1859
|
-
// Backup matter storage since it is created/opened correctly
|
|
1860
1348
|
await this.backupMatterStorage(path.join(this.matterbridgeDirectory, this.matterStorageName), path.join(this.matterbridgeDirectory, this.matterStorageName + '.backup'));
|
|
1861
1349
|
}
|
|
1862
|
-
/**
|
|
1863
|
-
* Makes a backup copy of the specified matter storage directory.
|
|
1864
|
-
*
|
|
1865
|
-
* @param storageName - The name of the storage directory to be backed up.
|
|
1866
|
-
* @param backupName - The name of the backup directory to be created.
|
|
1867
|
-
* @returns {Promise<void>} A promise that resolves when the has been done.
|
|
1868
|
-
*/
|
|
1869
1350
|
async backupMatterStorage(storageName, backupName) {
|
|
1870
1351
|
this.log.info('Creating matter node storage backup...');
|
|
1871
1352
|
await copyDirectory(storageName, backupName);
|
|
1872
1353
|
this.log.info('Created matter node storage backup');
|
|
1873
1354
|
}
|
|
1874
|
-
/**
|
|
1875
|
-
* Stops the matter storage.
|
|
1876
|
-
* @returns {Promise<void>} A promise that resolves when the storage is stopped.
|
|
1877
|
-
*/
|
|
1878
1355
|
async stopMatterStorage() {
|
|
1879
1356
|
this.log.info('Closing matter node storage...');
|
|
1880
1357
|
this.matterStorageManager?.close();
|
|
@@ -1883,19 +1360,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1883
1360
|
this.matterbridgeContext = undefined;
|
|
1884
1361
|
this.log.info('Matter node storage closed');
|
|
1885
1362
|
}
|
|
1886
|
-
/**
|
|
1887
|
-
* Creates a server node storage context.
|
|
1888
|
-
*
|
|
1889
|
-
* @param {string} pluginName - The name of the plugin.
|
|
1890
|
-
* @param {string} deviceName - The name of the device.
|
|
1891
|
-
* @param {DeviceTypeId} deviceType - The device type of the device.
|
|
1892
|
-
* @param {number} vendorId - The vendor ID.
|
|
1893
|
-
* @param {string} vendorName - The vendor name.
|
|
1894
|
-
* @param {number} productId - The product ID.
|
|
1895
|
-
* @param {string} productName - The product name.
|
|
1896
|
-
* @param {string} [serialNumber] - The serial number of the device (optional).
|
|
1897
|
-
* @returns {Promise<StorageContext>} The storage context for the commissioning server.
|
|
1898
|
-
*/
|
|
1899
1363
|
async createServerNodeContext(pluginName, deviceName, deviceType, vendorId, vendorName, productId, productName, serialNumber) {
|
|
1900
1364
|
const { randomBytes } = await import('node:crypto');
|
|
1901
1365
|
if (!this.matterStorageService)
|
|
@@ -1929,15 +1393,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1929
1393
|
this.log.debug(`- hardwareVersion: ${await storageContext.get('hardwareVersion')} hardwareVersionString: ${await storageContext.get('hardwareVersionString')}`);
|
|
1930
1394
|
return storageContext;
|
|
1931
1395
|
}
|
|
1932
|
-
/**
|
|
1933
|
-
* Creates a server node.
|
|
1934
|
-
*
|
|
1935
|
-
* @param {StorageContext} storageContext - The storage context for the server node.
|
|
1936
|
-
* @param {number} [port=5540] - The port number for the server node. Defaults to 5540.
|
|
1937
|
-
* @param {number} [passcode=20242025] - The passcode for the server node. Defaults to 20242025.
|
|
1938
|
-
* @param {number} [discriminator=3850] - The discriminator for the server node. Defaults to 3850.
|
|
1939
|
-
* @returns {Promise<ServerNode<ServerNode.RootEndpoint>>} A promise that resolves to the created server node.
|
|
1940
|
-
*/
|
|
1941
1396
|
async createServerNode(storageContext, port = 5540, passcode = 20242025, discriminator = 3850) {
|
|
1942
1397
|
const storeId = await storageContext.get('storeId');
|
|
1943
1398
|
this.log.notice(`Creating server node for ${storeId} on port ${port} with passcode ${passcode} and discriminator ${discriminator}...`);
|
|
@@ -1947,33 +1402,21 @@ export class Matterbridge extends EventEmitter {
|
|
|
1947
1402
|
this.log.debug(`- uniqueId: ${await storageContext.get('uniqueId')}`);
|
|
1948
1403
|
this.log.debug(`- softwareVersion: ${await storageContext.get('softwareVersion')} softwareVersionString: ${await storageContext.get('softwareVersionString')}`);
|
|
1949
1404
|
this.log.debug(`- hardwareVersion: ${await storageContext.get('hardwareVersion')} hardwareVersionString: ${await storageContext.get('hardwareVersionString')}`);
|
|
1950
|
-
/**
|
|
1951
|
-
* Create a Matter ServerNode, which contains the Root Endpoint and all relevant data and configuration
|
|
1952
|
-
*/
|
|
1953
1405
|
const serverNode = await ServerNode.create({
|
|
1954
|
-
// Required: Give the Node a unique ID which is used to store the state of this node
|
|
1955
1406
|
id: storeId,
|
|
1956
|
-
// Provide Network relevant configuration like the port
|
|
1957
|
-
// Optional when operating only one device on a host, Default port is 5540
|
|
1958
1407
|
network: {
|
|
1959
1408
|
listeningAddressIpv4: this.ipv4address,
|
|
1960
1409
|
listeningAddressIpv6: this.ipv6address,
|
|
1961
1410
|
port,
|
|
1962
1411
|
},
|
|
1963
|
-
// Provide Commissioning relevant settings
|
|
1964
|
-
// Optional for development/testing purposes
|
|
1965
1412
|
commissioning: {
|
|
1966
1413
|
passcode,
|
|
1967
1414
|
discriminator,
|
|
1968
1415
|
},
|
|
1969
|
-
// Provide Node announcement settings
|
|
1970
|
-
// Optional: If Ommitted some development defaults are used
|
|
1971
1416
|
productDescription: {
|
|
1972
1417
|
name: await storageContext.get('deviceName'),
|
|
1973
1418
|
deviceType: DeviceTypeId(await storageContext.get('deviceType')),
|
|
1974
1419
|
},
|
|
1975
|
-
// Provide defaults for the BasicInformation cluster on the Root endpoint
|
|
1976
|
-
// Optional: If Omitted some development defaults are used
|
|
1977
1420
|
basicInformation: {
|
|
1978
1421
|
vendorId: VendorId(await storageContext.get('vendorId')),
|
|
1979
1422
|
vendorName: await storageContext.get('vendorName'),
|
|
@@ -1990,13 +1433,12 @@ export class Matterbridge extends EventEmitter {
|
|
|
1990
1433
|
},
|
|
1991
1434
|
});
|
|
1992
1435
|
const sanitizeFabrics = (fabrics, resetSessions = false) => {
|
|
1993
|
-
// New type of fabric information: Record<FabricIndex, ExposedFabricInformation>
|
|
1994
1436
|
const sanitizedFabrics = this.sanitizeFabricInformations(Array.from(Object.values(fabrics)));
|
|
1995
1437
|
this.log.info(`Fabrics: ${debugStringify(sanitizedFabrics)}`);
|
|
1996
1438
|
if (this.bridgeMode === 'bridge') {
|
|
1997
1439
|
this.matterbridgeFabricInformations = sanitizedFabrics;
|
|
1998
1440
|
if (resetSessions)
|
|
1999
|
-
this.matterbridgeSessionInformations = undefined;
|
|
1441
|
+
this.matterbridgeSessionInformations = undefined;
|
|
2000
1442
|
this.matterbridgePaired = true;
|
|
2001
1443
|
}
|
|
2002
1444
|
if (this.bridgeMode === 'childbridge') {
|
|
@@ -2004,19 +1446,13 @@ export class Matterbridge extends EventEmitter {
|
|
|
2004
1446
|
if (plugin) {
|
|
2005
1447
|
plugin.fabricInformations = sanitizedFabrics;
|
|
2006
1448
|
if (resetSessions)
|
|
2007
|
-
plugin.sessionInformations = undefined;
|
|
1449
|
+
plugin.sessionInformations = undefined;
|
|
2008
1450
|
plugin.paired = true;
|
|
2009
1451
|
}
|
|
2010
1452
|
}
|
|
2011
1453
|
};
|
|
2012
|
-
/**
|
|
2013
|
-
* This event is triggered when the device is initially commissioned successfully.
|
|
2014
|
-
* This means: It is added to the first fabric.
|
|
2015
|
-
*/
|
|
2016
1454
|
serverNode.lifecycle.commissioned.on(() => this.log.notice(`Server node for ${storeId} was initially commissioned successfully!`));
|
|
2017
|
-
/** This event is triggered when all fabrics are removed from the device, usually it also does a factory reset then. */
|
|
2018
1455
|
serverNode.lifecycle.decommissioned.on(() => this.log.notice(`Server node for ${storeId} was fully decommissioned successfully!`));
|
|
2019
|
-
/** This event is triggered when the device went online. This means that it is discoverable in the network. */
|
|
2020
1456
|
serverNode.lifecycle.online.on(async () => {
|
|
2021
1457
|
this.log.notice(`Server node for ${storeId} is online`);
|
|
2022
1458
|
if (!serverNode.lifecycle.isCommissioned) {
|
|
@@ -2056,6 +1492,8 @@ export class Matterbridge extends EventEmitter {
|
|
|
2056
1492
|
}
|
|
2057
1493
|
}
|
|
2058
1494
|
setTimeout(() => {
|
|
1495
|
+
if (serverNode.lifecycle.isCommissioned)
|
|
1496
|
+
return;
|
|
2059
1497
|
if (this.bridgeMode === 'bridge') {
|
|
2060
1498
|
this.matterbridgeQrPairingCode = undefined;
|
|
2061
1499
|
this.matterbridgeManualPairingCode = undefined;
|
|
@@ -2069,6 +1507,9 @@ export class Matterbridge extends EventEmitter {
|
|
|
2069
1507
|
}
|
|
2070
1508
|
}
|
|
2071
1509
|
this.frontend.wssSendRefreshRequired('settings');
|
|
1510
|
+
this.frontend.wssSendRefreshRequired('plugins');
|
|
1511
|
+
this.frontend.wssSendRefreshRequired('fabrics');
|
|
1512
|
+
this.frontend.wssSendRefreshRequired('sessions');
|
|
2072
1513
|
this.frontend.wssSendSnackbarMessage(`Advertising on server node for ${storeId} stopped. Restart to commission.`, 0);
|
|
2073
1514
|
this.log.notice(`Advertising on server node for ${storeId} stopped. Restart to commission.`);
|
|
2074
1515
|
}, 15 * 60 * 1000).unref();
|
|
@@ -2081,7 +1522,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2081
1522
|
this.frontend.wssSendRefreshRequired('settings');
|
|
2082
1523
|
this.frontend.wssSendSnackbarMessage(`${storeId} is online`, 5, 'success');
|
|
2083
1524
|
});
|
|
2084
|
-
/** This event is triggered when the device went offline. it is not longer discoverable or connectable in the network. */
|
|
2085
1525
|
serverNode.lifecycle.offline.on(() => {
|
|
2086
1526
|
this.log.notice(`Server node for ${storeId} is offline`);
|
|
2087
1527
|
if (this.bridgeMode === 'bridge') {
|
|
@@ -2105,10 +1545,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2105
1545
|
this.frontend.wssSendRefreshRequired('settings');
|
|
2106
1546
|
this.frontend.wssSendSnackbarMessage(`${storeId} is offline`, 5, 'warning');
|
|
2107
1547
|
});
|
|
2108
|
-
/**
|
|
2109
|
-
* This event is triggered when a fabric is added, removed or updated on the device. Use this if more granular
|
|
2110
|
-
* information is needed.
|
|
2111
|
-
*/
|
|
2112
1548
|
serverNode.events.commissioning.fabricsChanged.on((fabricIndex, fabricAction) => {
|
|
2113
1549
|
let action = '';
|
|
2114
1550
|
switch (fabricAction) {
|
|
@@ -2142,24 +1578,16 @@ export class Matterbridge extends EventEmitter {
|
|
|
2142
1578
|
}
|
|
2143
1579
|
}
|
|
2144
1580
|
};
|
|
2145
|
-
/**
|
|
2146
|
-
* This event is triggered when an operative new session was opened by a Controller.
|
|
2147
|
-
* It is not triggered for the initial commissioning process, just afterwards for real connections.
|
|
2148
|
-
*/
|
|
2149
1581
|
serverNode.events.sessions.opened.on((session) => {
|
|
2150
1582
|
this.log.notice(`Session opened on server node for ${storeId}: ${debugStringify(session)}`);
|
|
2151
1583
|
sanitizeSessions(Object.values(serverNode.state.sessions.sessions));
|
|
2152
1584
|
this.frontend.wssSendRefreshRequired('sessions');
|
|
2153
1585
|
});
|
|
2154
|
-
/**
|
|
2155
|
-
* This event is triggered when an operative session is closed by a Controller or because the Device goes offline.
|
|
2156
|
-
*/
|
|
2157
1586
|
serverNode.events.sessions.closed.on((session) => {
|
|
2158
1587
|
this.log.notice(`Session closed on server node for ${storeId}: ${debugStringify(session)}`);
|
|
2159
1588
|
sanitizeSessions(Object.values(serverNode.state.sessions.sessions));
|
|
2160
1589
|
this.frontend.wssSendRefreshRequired('sessions');
|
|
2161
1590
|
});
|
|
2162
|
-
/** This event is triggered when a subscription gets added or removed on an operative session. */
|
|
2163
1591
|
serverNode.events.sessions.subscriptionsChanged.on((session) => {
|
|
2164
1592
|
this.log.notice(`Session subscriptions changed on server node for ${storeId}: ${debugStringify(session)}`);
|
|
2165
1593
|
sanitizeSessions(Object.values(serverNode.state.sessions.sessions));
|
|
@@ -2168,42 +1596,24 @@ export class Matterbridge extends EventEmitter {
|
|
|
2168
1596
|
this.log.info(`Created server node for ${storeId}`);
|
|
2169
1597
|
return serverNode;
|
|
2170
1598
|
}
|
|
2171
|
-
/**
|
|
2172
|
-
* Starts the specified server node.
|
|
2173
|
-
*
|
|
2174
|
-
* @param {ServerNode} [matterServerNode] - The server node to start.
|
|
2175
|
-
* @returns {Promise<void>} A promise that resolves when the server node has started.
|
|
2176
|
-
*/
|
|
2177
1599
|
async startServerNode(matterServerNode) {
|
|
2178
1600
|
if (!matterServerNode)
|
|
2179
1601
|
return;
|
|
2180
1602
|
this.log.notice(`Starting ${matterServerNode.id} server node`);
|
|
2181
1603
|
await matterServerNode.start();
|
|
2182
1604
|
}
|
|
2183
|
-
/**
|
|
2184
|
-
* Stops the specified server node.
|
|
2185
|
-
*
|
|
2186
|
-
* @param {ServerNode} matterServerNode - The server node to stop.
|
|
2187
|
-
* @returns {Promise<void>} A promise that resolves when the server node has stopped.
|
|
2188
|
-
*/
|
|
2189
1605
|
async stopServerNode(matterServerNode) {
|
|
2190
1606
|
if (!matterServerNode)
|
|
2191
1607
|
return;
|
|
2192
1608
|
this.log.notice(`Closing ${matterServerNode.id} server node`);
|
|
2193
1609
|
try {
|
|
2194
|
-
await withTimeout(matterServerNode.close(), 30000);
|
|
1610
|
+
await withTimeout(matterServerNode.close(), 30000);
|
|
2195
1611
|
this.log.info(`Closed ${matterServerNode.id} server node`);
|
|
2196
1612
|
}
|
|
2197
1613
|
catch (error) {
|
|
2198
1614
|
this.log.error(`Failed to close ${matterServerNode.id} server node: ${error instanceof Error ? error.message : error}`);
|
|
2199
1615
|
}
|
|
2200
1616
|
}
|
|
2201
|
-
/**
|
|
2202
|
-
* Advertises the specified server node.
|
|
2203
|
-
*
|
|
2204
|
-
* @param {ServerNode} [matterServerNode] - The server node to advertise.
|
|
2205
|
-
* @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.
|
|
2206
|
-
*/
|
|
2207
1617
|
async advertiseServerNode(matterServerNode) {
|
|
2208
1618
|
if (matterServerNode) {
|
|
2209
1619
|
await matterServerNode.env.get(DeviceCommissioner)?.allowBasicCommissioning();
|
|
@@ -2212,44 +1622,23 @@ export class Matterbridge extends EventEmitter {
|
|
|
2212
1622
|
return { qrPairingCode, manualPairingCode };
|
|
2213
1623
|
}
|
|
2214
1624
|
}
|
|
2215
|
-
/**
|
|
2216
|
-
* Stop advertise the specified server node.
|
|
2217
|
-
*
|
|
2218
|
-
* @param {ServerNode} [matterServerNode] - The server node to advertise.
|
|
2219
|
-
* @returns {Promise<void>} A promise that resolves when the server node has stopped advertising.
|
|
2220
|
-
*/
|
|
2221
1625
|
async stopAdvertiseServerNode(matterServerNode) {
|
|
2222
1626
|
if (matterServerNode && matterServerNode.lifecycle.isOnline) {
|
|
2223
1627
|
await matterServerNode.env.get(DeviceCommissioner)?.endCommissioning();
|
|
2224
1628
|
this.log.notice(`Stopped advertising for ${matterServerNode.id}`);
|
|
2225
1629
|
}
|
|
2226
1630
|
}
|
|
2227
|
-
/**
|
|
2228
|
-
* Creates an aggregator node with the specified storage context.
|
|
2229
|
-
*
|
|
2230
|
-
* @param {StorageContext} storageContext - The storage context for the aggregator node.
|
|
2231
|
-
* @returns {Promise<EndpointNode<AggregatorEndpoint>>} A promise that resolves to the created aggregator node.
|
|
2232
|
-
*/
|
|
2233
1631
|
async createAggregatorNode(storageContext) {
|
|
2234
1632
|
this.log.notice(`Creating ${await storageContext.get('storeId')} aggregator `);
|
|
2235
1633
|
const aggregatorNode = new EndpointNode(AggregatorEndpoint, { id: `${await storageContext.get('storeId')}` });
|
|
2236
1634
|
return aggregatorNode;
|
|
2237
1635
|
}
|
|
2238
|
-
/**
|
|
2239
|
-
* Adds a MatterbridgeEndpoint to the specified plugin.
|
|
2240
|
-
*
|
|
2241
|
-
* @param {string} pluginName - The name of the plugin.
|
|
2242
|
-
* @param {MatterbridgeEndpoint} device - The device to add as a bridged endpoint.
|
|
2243
|
-
* @returns {Promise<void>} A promise that resolves when the bridged endpoint has been added.
|
|
2244
|
-
*/
|
|
2245
1636
|
async addBridgedEndpoint(pluginName, device) {
|
|
2246
|
-
// Check if the plugin is registered
|
|
2247
1637
|
const plugin = this.plugins.get(pluginName);
|
|
2248
1638
|
if (!plugin) {
|
|
2249
1639
|
this.log.error(`Error adding bridged endpoint ${dev}${device.deviceName}${er} (${zb}${device.id}${er}) plugin ${plg}${pluginName}${er} not found`);
|
|
2250
1640
|
return;
|
|
2251
1641
|
}
|
|
2252
|
-
// Register and add the device to the matterbridge aggregator node
|
|
2253
1642
|
if (this.bridgeMode === 'bridge') {
|
|
2254
1643
|
this.log.debug(`Adding bridged endpoint ${plg}${pluginName}${db}:${dev}${device.deviceName}${db} to Matterbridge aggregator node`);
|
|
2255
1644
|
if (!this.aggregatorNode)
|
|
@@ -2272,28 +1661,17 @@ export class Matterbridge extends EventEmitter {
|
|
|
2272
1661
|
plugin.registeredDevices++;
|
|
2273
1662
|
if (plugin.addedDevices !== undefined)
|
|
2274
1663
|
plugin.addedDevices++;
|
|
2275
|
-
// Add the device to the DeviceManager
|
|
2276
1664
|
this.devices.set(device);
|
|
2277
|
-
// Subscribe to the reachable$Changed event
|
|
2278
1665
|
await this.subscribeAttributeChanged(plugin, device);
|
|
2279
1666
|
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}`);
|
|
2280
1667
|
}
|
|
2281
|
-
/**
|
|
2282
|
-
* Removes a MatterbridgeEndpoint from the specified plugin.
|
|
2283
|
-
*
|
|
2284
|
-
* @param {string} pluginName - The name of the plugin.
|
|
2285
|
-
* @param {MatterbridgeEndpoint} device - The device to remove as a bridged endpoint.
|
|
2286
|
-
* @returns {Promise<void>} A promise that resolves when the bridged endpoint has been removed.
|
|
2287
|
-
*/
|
|
2288
1668
|
async removeBridgedEndpoint(pluginName, device) {
|
|
2289
1669
|
this.log.debug(`Removing bridged endpoint ${plg}${pluginName}${db}:${dev}${device.deviceName}${db} (${zb}${device.name}${db}) for plugin ${plg}${pluginName}${db}`);
|
|
2290
|
-
// Check if the plugin is registered
|
|
2291
1670
|
const plugin = this.plugins.get(pluginName);
|
|
2292
1671
|
if (!plugin) {
|
|
2293
1672
|
this.log.error(`Error removing bridged endpoint ${dev}${device.deviceName}${er} (${zb}${device.name}${er}) for plugin ${plg}${pluginName}${er}: plugin not found`);
|
|
2294
1673
|
return;
|
|
2295
1674
|
}
|
|
2296
|
-
// Register and add the device to the matterbridge aggregator node
|
|
2297
1675
|
if (this.bridgeMode === 'bridge') {
|
|
2298
1676
|
if (!this.aggregatorNode) {
|
|
2299
1677
|
this.log.error(`Error removing bridged endpoint ${dev}${device.deviceName}${er} (${zb}${device.name}${er}) for plugin ${plg}${pluginName}${er}: aggregator node not found`);
|
|
@@ -2308,7 +1686,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2308
1686
|
}
|
|
2309
1687
|
else if (this.bridgeMode === 'childbridge') {
|
|
2310
1688
|
if (plugin.type === 'AccessoryPlatform') {
|
|
2311
|
-
// Nothing to do here since the server node has no aggregator node but only the device itself
|
|
2312
1689
|
}
|
|
2313
1690
|
else if (plugin.type === 'DynamicPlatform') {
|
|
2314
1691
|
if (!plugin.aggregatorNode) {
|
|
@@ -2323,16 +1700,8 @@ export class Matterbridge extends EventEmitter {
|
|
|
2323
1700
|
if (plugin.addedDevices !== undefined)
|
|
2324
1701
|
plugin.addedDevices--;
|
|
2325
1702
|
}
|
|
2326
|
-
// Remove the device from the DeviceManager
|
|
2327
1703
|
this.devices.remove(device);
|
|
2328
1704
|
}
|
|
2329
|
-
/**
|
|
2330
|
-
* Removes all bridged endpoints from the specified plugin.
|
|
2331
|
-
*
|
|
2332
|
-
* @param {string} pluginName - The name of the plugin.
|
|
2333
|
-
* @param {number} [delay=0] - The delay in milliseconds between removing each bridged endpoint (default: 0).
|
|
2334
|
-
* @returns {Promise<void>} A promise that resolves when all bridged endpoints have been removed.
|
|
2335
|
-
*/
|
|
2336
1705
|
async removeAllBridgedEndpoints(pluginName, delay = 0) {
|
|
2337
1706
|
this.log.debug(`Removing all bridged endpoints for plugin ${plg}${pluginName}${db}`);
|
|
2338
1707
|
for (const device of this.devices.array().filter((device) => device.plugin === pluginName)) {
|
|
@@ -2341,25 +1710,9 @@ export class Matterbridge extends EventEmitter {
|
|
|
2341
1710
|
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
2342
1711
|
}
|
|
2343
1712
|
}
|
|
2344
|
-
/**
|
|
2345
|
-
* Subscribes to the attribute change event for the given device and plugin.
|
|
2346
|
-
* Specifically, it listens for changes in the 'reachable' attribute of the
|
|
2347
|
-
* BridgedDeviceBasicInformationServer cluster server of the bridged device or BasicInformationServer cluster server of server node.
|
|
2348
|
-
*
|
|
2349
|
-
* @param {RegisteredPlugin} plugin - The plugin associated with the device.
|
|
2350
|
-
* @param {MatterbridgeEndpoint} device - The device to subscribe to attribute changes for.
|
|
2351
|
-
* @returns {Promise<void>} A promise that resolves when the subscription is set up.
|
|
2352
|
-
*/
|
|
2353
1713
|
async subscribeAttributeChanged(plugin, device) {
|
|
2354
1714
|
this.log.info(`Subscribing attributes for endpoint ${dev}${device.deviceName}${nf} (${dev}${device.id}${nf}) plugin ${plg}${plugin.name}${nf}`);
|
|
2355
1715
|
if (this.bridgeMode === 'childbridge' && plugin.type === 'AccessoryPlatform' && plugin.serverNode) {
|
|
2356
|
-
/*
|
|
2357
|
-
this.log.info(`Accessory endpoint ${dev}${device.deviceName}${nf} (${dev}${device.id}${nf}) subscribed to reachable$Changed`);
|
|
2358
|
-
setTimeout(async () => {
|
|
2359
|
-
this.log.info(`Accessory endpoint ${dev}${device.deviceName}${nf} (${dev}${device.id}${nf}) changed to reachable false`);
|
|
2360
|
-
await plugin.serverNode?.setStateOf(BasicInformationServer, { reachable: false });
|
|
2361
|
-
}, 60000).unref();
|
|
2362
|
-
*/
|
|
2363
1716
|
plugin.serverNode.eventsOf(BasicInformationServer).reachable$Changed?.on((reachable) => {
|
|
2364
1717
|
this.log.info(`Accessory endpoint ${dev}${device.deviceName}${nf} (${dev}${device.id}${nf}) is ${reachable ? 'reachable' : 'unreachable'}`);
|
|
2365
1718
|
this.frontend.wssSendAttributeChangedMessage(device.plugin, device.serialNumber, device.uniqueId, 'BasicInformationServer', 'reachable', reachable);
|
|
@@ -2372,12 +1725,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2372
1725
|
});
|
|
2373
1726
|
}
|
|
2374
1727
|
}
|
|
2375
|
-
/**
|
|
2376
|
-
* Sanitizes the fabric information by converting bigint properties to strings because `res.json` doesn't support bigint.
|
|
2377
|
-
*
|
|
2378
|
-
* @param {ExposedFabricInformation[]} fabricInfo - The array of exposed fabric information objects.
|
|
2379
|
-
* @returns {SanitizedExposedFabricInformation[]} An array of sanitized exposed fabric information objects.
|
|
2380
|
-
*/
|
|
2381
1728
|
sanitizeFabricInformations(fabricInfo) {
|
|
2382
1729
|
return fabricInfo.map((info) => {
|
|
2383
1730
|
return {
|
|
@@ -2391,12 +1738,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2391
1738
|
};
|
|
2392
1739
|
});
|
|
2393
1740
|
}
|
|
2394
|
-
/**
|
|
2395
|
-
* Sanitizes the session information by converting bigint properties to strings because `res.json` doesn't support bigint.
|
|
2396
|
-
*
|
|
2397
|
-
* @param {SessionInformation[]} sessionInfo - The array of session information objects.
|
|
2398
|
-
* @returns {SanitizedSessionInformation[]} An array of sanitized session information objects.
|
|
2399
|
-
*/
|
|
2400
1741
|
sanitizeSessionInformation(sessionInfo) {
|
|
2401
1742
|
return sessionInfo
|
|
2402
1743
|
.filter((session) => session.isPeerActive)
|
|
@@ -2424,20 +1765,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
2424
1765
|
};
|
|
2425
1766
|
});
|
|
2426
1767
|
}
|
|
2427
|
-
/**
|
|
2428
|
-
* Sets the reachability of the specified aggregator node bridged devices and trigger.
|
|
2429
|
-
* @param {EndpointNode<AggregatorEndpoint>} aggregatorNode - The aggregator node to set the reachability for.
|
|
2430
|
-
* @param {boolean} reachable - A boolean indicating the reachability status to set.
|
|
2431
|
-
*/
|
|
2432
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
2433
1768
|
async setAggregatorReachability(aggregatorNode, reachable) {
|
|
2434
|
-
/*
|
|
2435
|
-
for (const child of aggregatorNode.parts) {
|
|
2436
|
-
this.log.debug(`Setting reachability of ${(child as unknown as MatterbridgeEndpoint)?.deviceName} to ${reachable}`);
|
|
2437
|
-
await child.setStateOf(BridgedDeviceBasicInformationServer, { reachable });
|
|
2438
|
-
child.act((agent) => child.eventsOf(BridgedDeviceBasicInformationServer).reachableChanged.emit({ reachableNewValue: true }, agent.context));
|
|
2439
|
-
}
|
|
2440
|
-
*/
|
|
2441
1769
|
}
|
|
2442
1770
|
getVendorIdName = (vendorId) => {
|
|
2443
1771
|
if (!vendorId)
|
|
@@ -2480,37 +1808,14 @@ export class Matterbridge extends EventEmitter {
|
|
|
2480
1808
|
}
|
|
2481
1809
|
return vendorName;
|
|
2482
1810
|
};
|
|
2483
|
-
/**
|
|
2484
|
-
* Spawns a child process with the given command and arguments.
|
|
2485
|
-
* @param {string} command - The command to execute.
|
|
2486
|
-
* @param {string[]} args - The arguments to pass to the command (default: []).
|
|
2487
|
-
* @returns {Promise<boolean>} A promise that resolves when the child process exits successfully, or rejects if there is an error.
|
|
2488
|
-
*/
|
|
2489
1811
|
async spawnCommand(command, args = []) {
|
|
2490
1812
|
const { spawn } = await import('node:child_process');
|
|
2491
|
-
/*
|
|
2492
|
-
npm > npm.cmd on windows
|
|
2493
|
-
cmd.exe ['dir'] on windows
|
|
2494
|
-
await this.spawnCommand('npm', ['install', '-g', 'matterbridge']);
|
|
2495
|
-
process.on('unhandledRejection', (reason, promise) => {
|
|
2496
|
-
this.log.error('Unhandled Rejection at:', promise, 'reason:', reason);
|
|
2497
|
-
});
|
|
2498
|
-
|
|
2499
|
-
spawn - [14:27:21.125] [Matterbridge:spawn]: changed 38 packages in 4s
|
|
2500
|
-
spawn - [14:27:21.125] [Matterbridge:spawn]: 10 packages are looking for funding run `npm fund` for details
|
|
2501
|
-
debug - [14:27:21.131] [Matterbridge]: Child process exited with code 0 and signal null
|
|
2502
|
-
debug - [14:27:21.131] [Matterbridge]: Child process stdio streams have closed with code 0
|
|
2503
|
-
*/
|
|
2504
1813
|
const cmdLine = command + ' ' + args.join(' ');
|
|
2505
1814
|
if (process.platform === 'win32' && command === 'npm') {
|
|
2506
|
-
// Must be spawn('cmd.exe', ['/c', 'npm -g install <package>']);
|
|
2507
1815
|
const argstring = 'npm ' + args.join(' ');
|
|
2508
1816
|
args.splice(0, args.length, '/c', argstring);
|
|
2509
1817
|
command = 'cmd.exe';
|
|
2510
1818
|
}
|
|
2511
|
-
// Decide when using sudo on linux
|
|
2512
|
-
// When you need sudo: Spawn stderr: npm error Error: EACCES: permission denied
|
|
2513
|
-
// When you don't need sudo: Failed to start child process "npm install -g matterbridge-eve-door": spawn sudo ENOENT
|
|
2514
1819
|
if (hasParameter('sudo') || (process.platform === 'linux' && command === 'npm' && !hasParameter('docker') && !hasParameter('nosudo'))) {
|
|
2515
1820
|
args.unshift(command);
|
|
2516
1821
|
command = 'sudo';
|
|
@@ -2569,4 +1874,3 @@ export class Matterbridge extends EventEmitter {
|
|
|
2569
1874
|
});
|
|
2570
1875
|
}
|
|
2571
1876
|
}
|
|
2572
|
-
//# sourceMappingURL=matterbridge.js.map
|