matterbridge 2.2.3 → 2.2.4-dev.2
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 +15 -0
- 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 +33 -306
- 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 +65 -725
- 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 -690
- package/dist/matterbridgeEndpointHelpers.js +9 -118
- package/dist/matterbridgePlatform.js +7 -185
- package/dist/matterbridgeTypes.js +0 -24
- package/dist/pluginManager.js +3 -229
- package/dist/shelly.js +6 -121
- 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 -77
- 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.6ffd2c31.js → main.819c0908.js} +4 -4
- package/frontend/build/static/js/{main.6ffd2c31.js.map → main.819c0908.js.map} +1 -1
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -2
- package/dist/cli.d.ts +0 -29
- package/dist/cli.d.ts.map +0 -1
- package/dist/cli.js.map +0 -1
- package/dist/cluster/export.d.ts +0 -2
- package/dist/cluster/export.d.ts.map +0 -1
- package/dist/cluster/export.js.map +0 -1
- package/dist/defaultConfigSchema.d.ts +0 -27
- package/dist/defaultConfigSchema.d.ts.map +0 -1
- package/dist/defaultConfigSchema.js.map +0 -1
- package/dist/deviceManager.d.ts +0 -114
- package/dist/deviceManager.d.ts.map +0 -1
- package/dist/deviceManager.js.map +0 -1
- package/dist/frontend.d.ts +0 -201
- 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 -412
- 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 -835
- 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 -251
- package/dist/matterbridgePlatform.d.ts.map +0 -1
- package/dist/matterbridgePlatform.js.map +0 -1
- package/dist/matterbridgeTypes.d.ts +0 -178
- package/dist/matterbridgeTypes.d.ts.map +0 -1
- package/dist/matterbridgeTypes.js.map +0 -1
- package/dist/pluginManager.d.ts +0 -236
- package/dist/pluginManager.d.ts.map +0 -1
- package/dist/pluginManager.js.map +0 -1
- package/dist/shelly.d.ts +0 -77
- 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 -70
- 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.6ffd2c31.js.LICENSE.txt → main.819c0908.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,18 +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
19
|
const plg = '\u001B[38;5;33m';
|
|
47
20
|
const dev = '\u001B[38;5;79m';
|
|
48
21
|
const typ = '\u001B[38;5;207m';
|
|
49
|
-
/**
|
|
50
|
-
* Represents the Matterbridge application.
|
|
51
|
-
*/
|
|
52
22
|
export class Matterbridge extends EventEmitter {
|
|
53
23
|
systemInformation = {
|
|
54
24
|
interfaceName: '',
|
|
@@ -92,7 +62,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
92
62
|
shellySysUpdate: false,
|
|
93
63
|
shellyMainUpdate: false,
|
|
94
64
|
profile: getParameter('profile'),
|
|
95
|
-
loggerLevel: "info"
|
|
65
|
+
loggerLevel: "info",
|
|
96
66
|
fileLogger: false,
|
|
97
67
|
matterLoggerLevel: MatterLogLevel.INFO,
|
|
98
68
|
matterFileLogger: false,
|
|
@@ -122,18 +92,16 @@ export class Matterbridge extends EventEmitter {
|
|
|
122
92
|
profile = getParameter('profile');
|
|
123
93
|
shutdown = false;
|
|
124
94
|
edge = true;
|
|
125
|
-
failCountLimit = hasParameter('shelly') ? 600 :
|
|
95
|
+
failCountLimit = hasParameter('shelly') ? 600 : 120;
|
|
126
96
|
log;
|
|
127
97
|
matterbrideLoggerFile = 'matterbridge' + (getParameter('profile') ? '.' + getParameter('profile') : '') + '.log';
|
|
128
98
|
matterLoggerFile = 'matter' + (getParameter('profile') ? '.' + getParameter('profile') : '') + '.log';
|
|
129
99
|
plugins;
|
|
130
100
|
devices;
|
|
131
101
|
frontend = new Frontend(this);
|
|
132
|
-
// Matterbridge storage
|
|
133
102
|
nodeStorage;
|
|
134
103
|
nodeContext;
|
|
135
104
|
nodeStorageName = 'storage' + (getParameter('profile') ? '.' + getParameter('profile') : '');
|
|
136
|
-
// Cleanup
|
|
137
105
|
hasCleanupStarted = false;
|
|
138
106
|
initialized = false;
|
|
139
107
|
execRunningCount = 0;
|
|
@@ -146,70 +114,38 @@ export class Matterbridge extends EventEmitter {
|
|
|
146
114
|
sigtermHandler;
|
|
147
115
|
exceptionHandler;
|
|
148
116
|
rejectionHandler;
|
|
149
|
-
// Matter environment
|
|
150
117
|
environment = Environment.default;
|
|
151
|
-
// Matter storage
|
|
152
118
|
matterStorageName = 'matterstorage' + (getParameter('profile') ? '.' + getParameter('profile') : '');
|
|
153
119
|
matterStorageService;
|
|
154
120
|
matterStorageManager;
|
|
155
121
|
matterbridgeContext;
|
|
156
122
|
mattercontrollerContext;
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
discriminator; // first server node discriminator
|
|
123
|
+
mdnsInterface;
|
|
124
|
+
ipv4address;
|
|
125
|
+
ipv6address;
|
|
126
|
+
port;
|
|
127
|
+
passcode;
|
|
128
|
+
discriminator;
|
|
164
129
|
serverNode;
|
|
165
130
|
aggregatorNode;
|
|
166
131
|
aggregatorVendorId = VendorId(getIntParameter('vendorId') ?? 0xfff1);
|
|
167
132
|
aggregatorProductId = getIntParameter('productId') ?? 0x8000;
|
|
168
133
|
static instance;
|
|
169
|
-
// We load asyncronously so is private
|
|
170
134
|
constructor() {
|
|
171
135
|
super();
|
|
172
136
|
}
|
|
173
|
-
/**
|
|
174
|
-
* Emits an event of the specified type with the provided arguments.
|
|
175
|
-
*
|
|
176
|
-
* @template K - The type of the event.
|
|
177
|
-
* @param {K} eventName - The name of the event to emit.
|
|
178
|
-
* @param {...MatterbridgeEvent[K]} args - The arguments to pass to the event listeners.
|
|
179
|
-
* @returns {boolean} - Returns true if the event had listeners, false otherwise.
|
|
180
|
-
*/
|
|
181
137
|
emit(eventName, ...args) {
|
|
182
138
|
return super.emit(eventName, ...args);
|
|
183
139
|
}
|
|
184
|
-
/**
|
|
185
|
-
* Registers an event listener for the specified event type.
|
|
186
|
-
*
|
|
187
|
-
* @template K - The type of the event.
|
|
188
|
-
* @param {K} eventName - The name of the event to listen for.
|
|
189
|
-
* @param {(...args: MatterbridgeEvent[K]) => void} listener - The callback function to invoke when the event is emitted.
|
|
190
|
-
* @returns {this} - Returns the instance of the Matterbridge class.
|
|
191
|
-
*/
|
|
192
140
|
on(eventName, listener) {
|
|
193
141
|
return super.on(eventName, listener);
|
|
194
142
|
}
|
|
195
|
-
/**
|
|
196
|
-
* Retrieves the list of Matterbridge devices.
|
|
197
|
-
* @returns {MatterbridgeEndpoint[]} An array of MatterbridgeDevice objects.
|
|
198
|
-
*/
|
|
199
143
|
getDevices() {
|
|
200
144
|
return this.devices.array();
|
|
201
145
|
}
|
|
202
|
-
/**
|
|
203
|
-
* Retrieves the list of registered plugins.
|
|
204
|
-
* @returns {RegisteredPlugin[]} An array of RegisteredPlugin objects.
|
|
205
|
-
*/
|
|
206
146
|
getPlugins() {
|
|
207
147
|
return this.plugins.array();
|
|
208
148
|
}
|
|
209
|
-
/**
|
|
210
|
-
* Set the logger logLevel for the Matterbridge classes.
|
|
211
|
-
* @param {LogLevel} logLevel The logger logLevel to set.
|
|
212
|
-
*/
|
|
213
149
|
async setLogLevel(logLevel) {
|
|
214
150
|
if (this.log)
|
|
215
151
|
this.log.logLevel = logLevel;
|
|
@@ -223,31 +159,19 @@ export class Matterbridge extends EventEmitter {
|
|
|
223
159
|
for (const plugin of this.plugins) {
|
|
224
160
|
if (!plugin.platform || !plugin.platform.log || !plugin.platform.config)
|
|
225
161
|
continue;
|
|
226
|
-
plugin.platform.log.logLevel = plugin.platform.config.debug === true ? "debug"
|
|
227
|
-
await plugin.platform.onChangeLoggerLevel(plugin.platform.config.debug === true ? "debug"
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
callbackLogLevel = "debug" /* LogLevel.DEBUG */;
|
|
162
|
+
plugin.platform.log.logLevel = plugin.platform.config.debug === true ? "debug" : this.log.logLevel;
|
|
163
|
+
await plugin.platform.onChangeLoggerLevel(plugin.platform.config.debug === true ? "debug" : this.log.logLevel);
|
|
164
|
+
}
|
|
165
|
+
let callbackLogLevel = "notice";
|
|
166
|
+
if (this.matterbridgeInformation.loggerLevel === "info" || this.matterbridgeInformation.matterLoggerLevel === MatterLogLevel.INFO)
|
|
167
|
+
callbackLogLevel = "info";
|
|
168
|
+
if (this.matterbridgeInformation.loggerLevel === "debug" || this.matterbridgeInformation.matterLoggerLevel === MatterLogLevel.DEBUG)
|
|
169
|
+
callbackLogLevel = "debug";
|
|
235
170
|
AnsiLogger.setGlobalCallback(this.frontend.wssSendMessage.bind(this.frontend), callbackLogLevel);
|
|
236
171
|
this.log.debug(`WebSocketServer logger global callback set to ${callbackLogLevel}`);
|
|
237
172
|
}
|
|
238
|
-
/** ***********************************************************************************************************************************/
|
|
239
|
-
/** loadInstance() and cleanup() methods */
|
|
240
|
-
/** ***********************************************************************************************************************************/
|
|
241
|
-
/**
|
|
242
|
-
* Loads an instance of the Matterbridge class.
|
|
243
|
-
* If an instance already exists, return that instance.
|
|
244
|
-
*
|
|
245
|
-
* @param initialize - Whether to initialize the Matterbridge instance after loading.
|
|
246
|
-
* @returns The loaded Matterbridge instance.
|
|
247
|
-
*/
|
|
248
173
|
static async loadInstance(initialize = false) {
|
|
249
174
|
if (!Matterbridge.instance) {
|
|
250
|
-
// eslint-disable-next-line no-console
|
|
251
175
|
if (hasParameter('debug'))
|
|
252
176
|
console.log(GREEN + 'Creating a new instance of Matterbridge.', initialize ? 'Initializing...' : 'Not initializing...', rs);
|
|
253
177
|
Matterbridge.instance = new Matterbridge();
|
|
@@ -256,14 +180,8 @@ export class Matterbridge extends EventEmitter {
|
|
|
256
180
|
}
|
|
257
181
|
return Matterbridge.instance;
|
|
258
182
|
}
|
|
259
|
-
/**
|
|
260
|
-
* Call cleanup().
|
|
261
|
-
* @deprecated This method is deprecated and is only used for jest tests.
|
|
262
|
-
*
|
|
263
|
-
*/
|
|
264
183
|
async destroyInstance() {
|
|
265
184
|
this.log.info(`Destroy instance...`);
|
|
266
|
-
// Save server nodes to close
|
|
267
185
|
const servers = [];
|
|
268
186
|
if (this.bridgeMode === 'bridge') {
|
|
269
187
|
if (this.serverNode)
|
|
@@ -275,81 +193,55 @@ export class Matterbridge extends EventEmitter {
|
|
|
275
193
|
servers.push(plugin.serverNode);
|
|
276
194
|
}
|
|
277
195
|
}
|
|
278
|
-
// Cleanup
|
|
279
196
|
await this.cleanup('destroying instance...', false);
|
|
280
|
-
// Close servers mdns service
|
|
281
197
|
this.log.info(`Dispose ${servers.length} MdnsService...`);
|
|
282
198
|
for (const server of servers) {
|
|
283
199
|
await server.env.get(MdnsService)[Symbol.asyncDispose]();
|
|
284
200
|
this.log.info(`Closed ${server.id} MdnsService`);
|
|
285
201
|
}
|
|
286
|
-
// Wait for the cleanup to finish
|
|
287
202
|
await new Promise((resolve) => {
|
|
288
203
|
setTimeout(resolve, 1000);
|
|
289
204
|
});
|
|
290
205
|
}
|
|
291
|
-
/**
|
|
292
|
-
* Initializes the Matterbridge application.
|
|
293
|
-
*
|
|
294
|
-
* @remarks
|
|
295
|
-
* This method performs the necessary setup and initialization steps for the Matterbridge application.
|
|
296
|
-
* It displays the help information if the 'help' parameter is provided, sets up the logger, checks the
|
|
297
|
-
* node version, registers signal handlers, initializes storage, and parses the command line.
|
|
298
|
-
*
|
|
299
|
-
* @returns A Promise that resolves when the initialization is complete.
|
|
300
|
-
*/
|
|
301
206
|
async initialize() {
|
|
302
|
-
// Set the restart mode
|
|
303
207
|
if (hasParameter('service'))
|
|
304
208
|
this.restartMode = 'service';
|
|
305
209
|
if (hasParameter('docker'))
|
|
306
210
|
this.restartMode = 'docker';
|
|
307
|
-
// Set the matterbridge directory
|
|
308
211
|
this.homeDirectory = getParameter('homedir') ?? os.homedir();
|
|
309
212
|
this.matterbridgeDirectory = path.join(this.homeDirectory, '.matterbridge');
|
|
310
|
-
// Setup the matter environment
|
|
311
213
|
this.environment.vars.set('log.level', MatterLogLevel.INFO);
|
|
312
214
|
this.environment.vars.set('log.format', MatterLogFormat.ANSI);
|
|
313
215
|
this.environment.vars.set('path.root', path.join(this.matterbridgeDirectory, this.matterStorageName));
|
|
314
216
|
this.environment.vars.set('runtime.signals', false);
|
|
315
217
|
this.environment.vars.set('runtime.exitcode', false);
|
|
316
|
-
|
|
317
|
-
this.log = new AnsiLogger({ logName: 'Matterbridge', logTimestampFormat: 4 /* TimestampFormat.TIME_MILLIS */, logLevel: hasParameter('debug') ? "debug" /* LogLevel.DEBUG */ : "info" /* LogLevel.INFO */ });
|
|
318
|
-
// Register process handlers
|
|
218
|
+
this.log = new AnsiLogger({ logName: 'Matterbridge', logTimestampFormat: 4, logLevel: hasParameter('debug') ? "debug" : "info" });
|
|
319
219
|
this.registerProcessHandlers();
|
|
320
|
-
// Initialize nodeStorage and nodeContext
|
|
321
220
|
try {
|
|
322
221
|
this.log.debug(`Creating node storage manager: ${CYAN}${this.nodeStorageName}${db}`);
|
|
323
222
|
this.nodeStorage = new NodeStorageManager({ dir: path.join(this.matterbridgeDirectory, this.nodeStorageName), writeQueue: false, expiredInterval: undefined, logging: false });
|
|
324
223
|
this.log.debug('Creating node storage context for matterbridge');
|
|
325
224
|
this.nodeContext = await this.nodeStorage.createStorage('matterbridge');
|
|
326
|
-
// TODO: Remove this code when node-persist-manager is updated
|
|
327
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
328
225
|
const keys = (await this.nodeStorage?.storage.keys());
|
|
329
226
|
for (const key of keys) {
|
|
330
227
|
this.log.debug(`Checking node storage manager key: ${CYAN}${key}${db}`);
|
|
331
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
332
228
|
await this.nodeStorage?.storage.get(key);
|
|
333
229
|
}
|
|
334
230
|
const storages = await this.nodeStorage.getStorageNames();
|
|
335
231
|
for (const storage of storages) {
|
|
336
232
|
this.log.debug(`Checking storage: ${CYAN}${storage}${db}`);
|
|
337
233
|
const nodeContext = await this.nodeStorage?.createStorage(storage);
|
|
338
|
-
// TODO: Remove this code when node-persist-manager is updated
|
|
339
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
340
234
|
const keys = (await nodeContext?.storage.keys());
|
|
341
235
|
keys.forEach(async (key) => {
|
|
342
236
|
this.log.debug(`Checking key: ${CYAN}${storage}:${key}${db}`);
|
|
343
237
|
await nodeContext?.get(key);
|
|
344
238
|
});
|
|
345
239
|
}
|
|
346
|
-
// Creating a backup of the node storage since it is not corrupted
|
|
347
240
|
this.log.debug('Creating node storage backup...');
|
|
348
241
|
await copyDirectory(path.join(this.matterbridgeDirectory, this.nodeStorageName), path.join(this.matterbridgeDirectory, this.nodeStorageName + '.backup'));
|
|
349
242
|
this.log.debug('Created node storage backup');
|
|
350
243
|
}
|
|
351
244
|
catch (error) {
|
|
352
|
-
// Restoring the backup of the node storage since it is corrupted
|
|
353
245
|
this.log.error(`Error creating node storage manager and context: ${error instanceof Error ? error.message : error}`);
|
|
354
246
|
if (hasParameter('norestore')) {
|
|
355
247
|
this.log.fatal(`The matterbridge node storage is corrupted. Parameter -norestore found: exiting...`);
|
|
@@ -364,46 +256,41 @@ export class Matterbridge extends EventEmitter {
|
|
|
364
256
|
this.log.fatal('Fatal error creating node storage manager and context for matterbridge');
|
|
365
257
|
throw new Error('Fatal error creating node storage manager and context for matterbridge');
|
|
366
258
|
}
|
|
367
|
-
// Set the first port to use for the commissioning server (will be incremented in childbridge mode)
|
|
368
259
|
this.port = getIntParameter('port') ?? (await this.nodeContext.get('matterport', 5540)) ?? 5540;
|
|
369
|
-
// Set the first passcode to use for the commissioning server (will be incremented in childbridge mode)
|
|
370
260
|
this.passcode = getIntParameter('passcode') ?? (await this.nodeContext.get('matterpasscode')) ?? PaseClient.generateRandomPasscode();
|
|
371
|
-
// Set the first discriminator to use for the commissioning server (will be incremented in childbridge mode)
|
|
372
261
|
this.discriminator = getIntParameter('discriminator') ?? (await this.nodeContext.get('matterdiscriminator')) ?? PaseClient.generateRandomDiscriminator();
|
|
373
262
|
this.log.debug(`Initializing server node for Matterbridge... on port ${this.port} with passcode ${this.passcode} and discriminator ${this.discriminator}`);
|
|
374
|
-
// Set matterbridge logger level (context: matterbridgeLogLevel)
|
|
375
263
|
if (hasParameter('logger')) {
|
|
376
264
|
const level = getParameter('logger');
|
|
377
265
|
if (level === 'debug') {
|
|
378
|
-
this.log.logLevel = "debug"
|
|
266
|
+
this.log.logLevel = "debug";
|
|
379
267
|
}
|
|
380
268
|
else if (level === 'info') {
|
|
381
|
-
this.log.logLevel = "info"
|
|
269
|
+
this.log.logLevel = "info";
|
|
382
270
|
}
|
|
383
271
|
else if (level === 'notice') {
|
|
384
|
-
this.log.logLevel = "notice"
|
|
272
|
+
this.log.logLevel = "notice";
|
|
385
273
|
}
|
|
386
274
|
else if (level === 'warn') {
|
|
387
|
-
this.log.logLevel = "warn"
|
|
275
|
+
this.log.logLevel = "warn";
|
|
388
276
|
}
|
|
389
277
|
else if (level === 'error') {
|
|
390
|
-
this.log.logLevel = "error"
|
|
278
|
+
this.log.logLevel = "error";
|
|
391
279
|
}
|
|
392
280
|
else if (level === 'fatal') {
|
|
393
|
-
this.log.logLevel = "fatal"
|
|
281
|
+
this.log.logLevel = "fatal";
|
|
394
282
|
}
|
|
395
283
|
else {
|
|
396
284
|
this.log.warn(`Invalid matterbridge logger level: ${level}. Using default level "info".`);
|
|
397
|
-
this.log.logLevel = "info"
|
|
285
|
+
this.log.logLevel = "info";
|
|
398
286
|
}
|
|
399
287
|
}
|
|
400
288
|
else {
|
|
401
|
-
this.log.logLevel = await this.nodeContext.get('matterbridgeLogLevel', this.matterbridgeInformation.shellyBoard ? "notice"
|
|
289
|
+
this.log.logLevel = await this.nodeContext.get('matterbridgeLogLevel', this.matterbridgeInformation.shellyBoard ? "notice" : "info");
|
|
402
290
|
}
|
|
403
291
|
this.frontend.logLevel = this.log.logLevel;
|
|
404
292
|
MatterbridgeEndpoint.logLevel = this.log.logLevel;
|
|
405
293
|
this.matterbridgeInformation.loggerLevel = this.log.logLevel;
|
|
406
|
-
// Create the file logger for matterbridge (context: matterbridgeFileLog)
|
|
407
294
|
if (hasParameter('filelogger') || (await this.nodeContext.get('matterbridgeFileLog', false))) {
|
|
408
295
|
AnsiLogger.setGlobalLogfile(path.join(this.matterbridgeDirectory, this.matterbrideLoggerFile), this.log.logLevel, true);
|
|
409
296
|
this.matterbridgeInformation.fileLogger = true;
|
|
@@ -412,7 +299,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
412
299
|
this.log.debug(`Matterbridge logLevel: ${this.log.logLevel} fileLoger: ${this.matterbridgeInformation.fileLogger}.`);
|
|
413
300
|
if (this.profile !== undefined)
|
|
414
301
|
this.log.debug(`Matterbridge profile: ${this.profile}.`);
|
|
415
|
-
// Set matter.js logger level, format and logger (context: matterLogLevel)
|
|
416
302
|
if (hasParameter('matterlogger')) {
|
|
417
303
|
const level = getParameter('matterlogger');
|
|
418
304
|
if (level === 'debug') {
|
|
@@ -444,7 +330,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
444
330
|
Logger.format = MatterLogFormat.ANSI;
|
|
445
331
|
Logger.setLogger('default', this.createMatterLogger());
|
|
446
332
|
this.matterbridgeInformation.matterLoggerLevel = Logger.defaultLogLevel;
|
|
447
|
-
// Create the file logger for matter.js (context: matterFileLog)
|
|
448
333
|
if (hasParameter('matterfilelogger') || (await this.nodeContext.get('matterFileLog', false))) {
|
|
449
334
|
this.matterbridgeInformation.matterFileLogger = true;
|
|
450
335
|
Logger.addLogger('matterfilelogger', await this.createMatterFileLogger(path.join(this.matterbridgeDirectory, this.matterLoggerFile), true), {
|
|
@@ -453,7 +338,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
453
338
|
});
|
|
454
339
|
}
|
|
455
340
|
this.log.debug(`Matter logLevel: ${Logger.defaultLogLevel} fileLoger: ${this.matterbridgeInformation.matterFileLogger}.`);
|
|
456
|
-
// Set the interface to use for matter server node mdnsInterface
|
|
457
341
|
if (hasParameter('mdnsinterface')) {
|
|
458
342
|
this.mdnsInterface = getParameter('mdnsinterface');
|
|
459
343
|
}
|
|
@@ -462,7 +346,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
462
346
|
if (this.mdnsInterface === '')
|
|
463
347
|
this.mdnsInterface = undefined;
|
|
464
348
|
}
|
|
465
|
-
// Validate mdnsInterface
|
|
466
349
|
if (this.mdnsInterface) {
|
|
467
350
|
const networkInterfaces = os.networkInterfaces();
|
|
468
351
|
const availableInterfaces = Object.keys(networkInterfaces);
|
|
@@ -476,7 +359,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
476
359
|
}
|
|
477
360
|
if (this.mdnsInterface)
|
|
478
361
|
this.environment.vars.set('mdns.networkInterface', this.mdnsInterface);
|
|
479
|
-
// Set the listeningAddressIpv4 for the matter commissioning server
|
|
480
362
|
if (hasParameter('ipv4address')) {
|
|
481
363
|
this.ipv4address = getParameter('ipv4address');
|
|
482
364
|
}
|
|
@@ -485,7 +367,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
485
367
|
if (this.ipv4address === '')
|
|
486
368
|
this.ipv4address = undefined;
|
|
487
369
|
}
|
|
488
|
-
// Set the listeningAddressIpv6 for the matter commissioning server
|
|
489
370
|
if (hasParameter('ipv6address')) {
|
|
490
371
|
this.ipv6address = getParameter('ipv6address');
|
|
491
372
|
}
|
|
@@ -494,19 +375,14 @@ export class Matterbridge extends EventEmitter {
|
|
|
494
375
|
if (this.ipv6address === '')
|
|
495
376
|
this.ipv6address = undefined;
|
|
496
377
|
}
|
|
497
|
-
// Initialize PluginManager
|
|
498
378
|
this.plugins = new PluginManager(this);
|
|
499
379
|
await this.plugins.loadFromStorage();
|
|
500
380
|
this.plugins.logLevel = this.log.logLevel;
|
|
501
|
-
// Initialize DeviceManager
|
|
502
381
|
this.devices = new DeviceManager(this, this.nodeContext);
|
|
503
382
|
this.devices.logLevel = this.log.logLevel;
|
|
504
|
-
// Get the plugins from node storage and create the plugins node storage contexts
|
|
505
383
|
for (const plugin of this.plugins) {
|
|
506
384
|
const packageJson = await this.plugins.parse(plugin);
|
|
507
385
|
if (packageJson === null && !hasParameter('add') && !hasParameter('remove') && !hasParameter('enable') && !hasParameter('disable') && !hasParameter('reset') && !hasParameter('factoryreset')) {
|
|
508
|
-
// Try to reinstall the plugin from npm (for Docker pull and external plugins)
|
|
509
|
-
// We don't do this when the add and other parameters are set because we shut down the process after adding the plugin
|
|
510
386
|
this.log.info(`Error parsing plugin ${plg}${plugin.name}${nf}. Trying to reinstall it from npm.`);
|
|
511
387
|
try {
|
|
512
388
|
await this.spawnCommand('npm', ['install', '-g', plugin.name, '--omit=dev', '--verbose']);
|
|
@@ -528,7 +404,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
528
404
|
await plugin.nodeContext.set('description', plugin.description);
|
|
529
405
|
await plugin.nodeContext.set('author', plugin.author);
|
|
530
406
|
}
|
|
531
|
-
// Log system info and create .matterbridge directory
|
|
532
407
|
await this.logNodeAndSystemInfo();
|
|
533
408
|
this.log.notice(`Matterbridge version ${this.matterbridgeVersion} ` +
|
|
534
409
|
`${hasParameter('bridge') || (!hasParameter('childbridge') && (await this.nodeContext?.get('bridgeMode', '')) === 'bridge') ? 'mode bridge ' : ''}` +
|
|
@@ -536,7 +411,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
536
411
|
`${hasParameter('controller') ? 'mode controller ' : ''}` +
|
|
537
412
|
`${this.restartMode !== '' ? 'restart mode ' + this.restartMode + ' ' : ''}` +
|
|
538
413
|
`running on ${this.systemInformation.osType} (v.${this.systemInformation.osRelease}) platform ${this.systemInformation.osPlatform} arch ${this.systemInformation.osArch}`);
|
|
539
|
-
// Check node version and throw error
|
|
540
414
|
const minNodeVersion = 18;
|
|
541
415
|
const nodeVersion = process.versions.node;
|
|
542
416
|
const versionMajor = parseInt(nodeVersion.split('.')[0]);
|
|
@@ -544,15 +418,9 @@ export class Matterbridge extends EventEmitter {
|
|
|
544
418
|
this.log.error(`Node version ${versionMajor} is not supported. Please upgrade to ${minNodeVersion} or above.`);
|
|
545
419
|
throw new Error(`Node version ${versionMajor} is not supported. Please upgrade to ${minNodeVersion} or above.`);
|
|
546
420
|
}
|
|
547
|
-
// Parse command line
|
|
548
421
|
await this.parseCommandLine();
|
|
549
422
|
this.initialized = true;
|
|
550
423
|
}
|
|
551
|
-
/**
|
|
552
|
-
* Parses the command line arguments and performs the corresponding actions.
|
|
553
|
-
* @private
|
|
554
|
-
* @returns {Promise<void>} A promise that resolves when the command line arguments have been processed, or the process exits.
|
|
555
|
-
*/
|
|
556
424
|
async parseCommandLine() {
|
|
557
425
|
if (hasParameter('help')) {
|
|
558
426
|
this.log.info(`\nUsage: matterbridge [options]\n
|
|
@@ -664,7 +532,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
664
532
|
this.shutdown = true;
|
|
665
533
|
return;
|
|
666
534
|
}
|
|
667
|
-
// Start the matter storage and create the matterbridge context
|
|
668
535
|
try {
|
|
669
536
|
await this.startMatterStorage();
|
|
670
537
|
}
|
|
@@ -672,14 +539,12 @@ export class Matterbridge extends EventEmitter {
|
|
|
672
539
|
this.log.fatal(`Fatal error creating matter storage: ${error instanceof Error ? error.message : error}`);
|
|
673
540
|
throw new Error(`Fatal error creating matter storage: ${error instanceof Error ? error.message : error}`);
|
|
674
541
|
}
|
|
675
|
-
// Clear the matterbridge context if the reset parameter is set
|
|
676
542
|
if (hasParameter('reset') && getParameter('reset') === undefined) {
|
|
677
543
|
this.initialized = true;
|
|
678
544
|
await this.shutdownProcessAndReset();
|
|
679
545
|
this.shutdown = true;
|
|
680
546
|
return;
|
|
681
547
|
}
|
|
682
|
-
// Clear matterbridge plugin context if the reset parameter is set
|
|
683
548
|
if (hasParameter('reset') && getParameter('reset') !== undefined) {
|
|
684
549
|
this.log.debug(`Reset plugin ${getParameter('reset')}`);
|
|
685
550
|
const plugin = this.plugins.get(getParameter('reset'));
|
|
@@ -704,37 +569,30 @@ export class Matterbridge extends EventEmitter {
|
|
|
704
569
|
this.shutdown = true;
|
|
705
570
|
return;
|
|
706
571
|
}
|
|
707
|
-
// Initialize frontend
|
|
708
572
|
if (getIntParameter('frontend') !== 0 || getIntParameter('frontend') === undefined)
|
|
709
573
|
await this.frontend.start(getIntParameter('frontend'));
|
|
710
|
-
// Check in 30 seconds the latest versions
|
|
711
574
|
this.checkUpdateTimeout = setTimeout(async () => {
|
|
712
575
|
const { checkUpdates } = await import('./update.js');
|
|
713
576
|
checkUpdates(this);
|
|
714
577
|
}, 30 * 1000).unref();
|
|
715
|
-
// Check each 24 hours the latest versions
|
|
716
578
|
this.checkUpdateInterval = setInterval(async () => {
|
|
717
579
|
const { checkUpdates } = await import('./update.js');
|
|
718
580
|
checkUpdates(this);
|
|
719
581
|
}, 24 * 60 * 60 * 1000).unref();
|
|
720
|
-
// Start the matterbridge in mode test
|
|
721
582
|
if (hasParameter('test')) {
|
|
722
583
|
this.bridgeMode = 'bridge';
|
|
723
584
|
MatterbridgeEndpoint.bridgeMode = 'bridge';
|
|
724
585
|
return;
|
|
725
586
|
}
|
|
726
|
-
// Start the matterbridge in mode controller
|
|
727
587
|
if (hasParameter('controller')) {
|
|
728
588
|
this.bridgeMode = 'controller';
|
|
729
589
|
await this.startController();
|
|
730
590
|
return;
|
|
731
591
|
}
|
|
732
|
-
// Check if the bridge mode is set and start matterbridge in bridge mode if not set
|
|
733
592
|
if (!hasParameter('bridge') && !hasParameter('childbridge') && (await this.nodeContext?.get('bridgeMode', '')) === '') {
|
|
734
593
|
this.log.info('Setting default matterbridge start mode to bridge');
|
|
735
594
|
await this.nodeContext?.set('bridgeMode', 'bridge');
|
|
736
595
|
}
|
|
737
|
-
// Start matterbridge in bridge mode
|
|
738
596
|
if (hasParameter('bridge') || (!hasParameter('childbridge') && (await this.nodeContext?.get('bridgeMode', '')) === 'bridge')) {
|
|
739
597
|
this.bridgeMode = 'bridge';
|
|
740
598
|
MatterbridgeEndpoint.bridgeMode = 'bridge';
|
|
@@ -742,7 +600,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
742
600
|
await this.startBridge();
|
|
743
601
|
return;
|
|
744
602
|
}
|
|
745
|
-
// Start matterbridge in childbridge mode
|
|
746
603
|
if (hasParameter('childbridge') || (!hasParameter('bridge') && (await this.nodeContext?.get('bridgeMode', '')) === 'childbridge')) {
|
|
747
604
|
this.bridgeMode = 'childbridge';
|
|
748
605
|
MatterbridgeEndpoint.bridgeMode = 'childbridge';
|
|
@@ -751,20 +608,10 @@ export class Matterbridge extends EventEmitter {
|
|
|
751
608
|
return;
|
|
752
609
|
}
|
|
753
610
|
}
|
|
754
|
-
/**
|
|
755
|
-
* Asynchronously loads and starts the registered plugins.
|
|
756
|
-
*
|
|
757
|
-
* This method is responsible for initializing and staarting all enabled plugins.
|
|
758
|
-
* It ensures that each plugin is properly loaded and started before the bridge starts.
|
|
759
|
-
*
|
|
760
|
-
* @returns {Promise<void>} A promise that resolves when all plugins have been loaded and started.
|
|
761
|
-
*/
|
|
762
611
|
async startPlugins() {
|
|
763
|
-
// Check, load and start the plugins
|
|
764
612
|
for (const plugin of this.plugins) {
|
|
765
613
|
plugin.configJson = await this.plugins.loadConfig(plugin);
|
|
766
614
|
plugin.schemaJson = await this.plugins.loadSchema(plugin);
|
|
767
|
-
// Check if the plugin is available
|
|
768
615
|
if (!(await this.plugins.resolve(plugin.path))) {
|
|
769
616
|
this.log.error(`Plugin ${plg}${plugin.name}${er} not found or not validated. Disabling it.`);
|
|
770
617
|
plugin.enabled = false;
|
|
@@ -784,26 +631,20 @@ export class Matterbridge extends EventEmitter {
|
|
|
784
631
|
plugin.addedDevices = undefined;
|
|
785
632
|
plugin.qrPairingCode = undefined;
|
|
786
633
|
plugin.manualPairingCode = undefined;
|
|
787
|
-
this.plugins.load(plugin, true, 'Matterbridge is starting');
|
|
634
|
+
this.plugins.load(plugin, true, 'Matterbridge is starting');
|
|
788
635
|
}
|
|
789
636
|
this.frontend.wssSendRefreshRequired('plugins');
|
|
790
637
|
}
|
|
791
|
-
/**
|
|
792
|
-
* Registers the process handlers for uncaughtException, unhandledRejection, SIGINT and SIGTERM.
|
|
793
|
-
* When either of these signals are received, the cleanup method is called with an appropriate message.
|
|
794
|
-
*/
|
|
795
638
|
registerProcessHandlers() {
|
|
796
639
|
this.log.debug(`Registering uncaughtException and unhandledRejection handlers...`);
|
|
797
640
|
process.removeAllListeners('uncaughtException');
|
|
798
641
|
process.removeAllListeners('unhandledRejection');
|
|
799
642
|
this.exceptionHandler = async (error) => {
|
|
800
643
|
this.log.error('Unhandled Exception detected at:', error.stack || error, rs);
|
|
801
|
-
// await this.cleanup('Unhandled Exception detected, cleaning up...');
|
|
802
644
|
};
|
|
803
645
|
process.on('uncaughtException', this.exceptionHandler);
|
|
804
646
|
this.rejectionHandler = async (reason, promise) => {
|
|
805
647
|
this.log.error('Unhandled Rejection detected at:', promise, 'reason:', reason instanceof Error ? reason.stack : reason, rs);
|
|
806
|
-
// await this.cleanup('Unhandled Rejection detected, cleaning up...');
|
|
807
648
|
};
|
|
808
649
|
process.on('unhandledRejection', this.rejectionHandler);
|
|
809
650
|
this.log.debug(`Registering SIGINT and SIGTERM signal handlers...`);
|
|
@@ -816,9 +657,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
816
657
|
};
|
|
817
658
|
process.on('SIGTERM', this.sigtermHandler);
|
|
818
659
|
}
|
|
819
|
-
/**
|
|
820
|
-
* Deregisters the process uncaughtException, unhandledRejection, SIGINT and SIGTERM signal handlers.
|
|
821
|
-
*/
|
|
822
660
|
deregisterProcesslHandlers() {
|
|
823
661
|
this.log.debug(`Deregistering uncaughtException and unhandledRejection handlers...`);
|
|
824
662
|
if (this.exceptionHandler)
|
|
@@ -835,17 +673,12 @@ export class Matterbridge extends EventEmitter {
|
|
|
835
673
|
process.off('SIGTERM', this.sigtermHandler);
|
|
836
674
|
this.sigtermHandler = undefined;
|
|
837
675
|
}
|
|
838
|
-
/**
|
|
839
|
-
* Logs the node and system information.
|
|
840
|
-
*/
|
|
841
676
|
async logNodeAndSystemInfo() {
|
|
842
|
-
// IP address information
|
|
843
677
|
const networkInterfaces = os.networkInterfaces();
|
|
844
678
|
this.systemInformation.interfaceName = '';
|
|
845
679
|
this.systemInformation.ipv4Address = '';
|
|
846
680
|
this.systemInformation.ipv6Address = '';
|
|
847
681
|
for (const [interfaceName, interfaceDetails] of Object.entries(networkInterfaces)) {
|
|
848
|
-
// this.log.debug(`Checking interface: '${interfaceName}' for '${this.mdnsInterface}'`);
|
|
849
682
|
if (this.mdnsInterface && interfaceName !== this.mdnsInterface)
|
|
850
683
|
continue;
|
|
851
684
|
if (!interfaceDetails) {
|
|
@@ -871,22 +704,19 @@ export class Matterbridge extends EventEmitter {
|
|
|
871
704
|
break;
|
|
872
705
|
}
|
|
873
706
|
}
|
|
874
|
-
// Node information
|
|
875
707
|
this.systemInformation.nodeVersion = process.versions.node;
|
|
876
708
|
const versionMajor = parseInt(this.systemInformation.nodeVersion.split('.')[0]);
|
|
877
709
|
const versionMinor = parseInt(this.systemInformation.nodeVersion.split('.')[1]);
|
|
878
710
|
const versionPatch = parseInt(this.systemInformation.nodeVersion.split('.')[2]);
|
|
879
|
-
// Host system information
|
|
880
711
|
this.systemInformation.hostname = os.hostname();
|
|
881
712
|
this.systemInformation.user = os.userInfo().username;
|
|
882
|
-
this.systemInformation.osType = os.type();
|
|
883
|
-
this.systemInformation.osRelease = os.release();
|
|
884
|
-
this.systemInformation.osPlatform = os.platform();
|
|
885
|
-
this.systemInformation.osArch = os.arch();
|
|
886
|
-
this.systemInformation.totalMemory = (os.totalmem() / 1024 / 1024 / 1024).toFixed(2) + ' GB';
|
|
887
|
-
this.systemInformation.freeMemory = (os.freemem() / 1024 / 1024 / 1024).toFixed(2) + ' GB';
|
|
888
|
-
this.systemInformation.systemUptime = (os.uptime() / 60 / 60).toFixed(2) + ' hours';
|
|
889
|
-
// Log the system information
|
|
713
|
+
this.systemInformation.osType = os.type();
|
|
714
|
+
this.systemInformation.osRelease = os.release();
|
|
715
|
+
this.systemInformation.osPlatform = os.platform();
|
|
716
|
+
this.systemInformation.osArch = os.arch();
|
|
717
|
+
this.systemInformation.totalMemory = (os.totalmem() / 1024 / 1024 / 1024).toFixed(2) + ' GB';
|
|
718
|
+
this.systemInformation.freeMemory = (os.freemem() / 1024 / 1024 / 1024).toFixed(2) + ' GB';
|
|
719
|
+
this.systemInformation.systemUptime = (os.uptime() / 60 / 60).toFixed(2) + ' hours';
|
|
890
720
|
this.log.debug('Host System Information:');
|
|
891
721
|
this.log.debug(`- Hostname: ${this.systemInformation.hostname}`);
|
|
892
722
|
this.log.debug(`- User: ${this.systemInformation.user}`);
|
|
@@ -902,20 +732,16 @@ export class Matterbridge extends EventEmitter {
|
|
|
902
732
|
this.log.debug(`- Total Memory: ${this.systemInformation.totalMemory}`);
|
|
903
733
|
this.log.debug(`- Free Memory: ${this.systemInformation.freeMemory}`);
|
|
904
734
|
this.log.debug(`- System Uptime: ${this.systemInformation.systemUptime}`);
|
|
905
|
-
// Home directory
|
|
906
735
|
this.homeDirectory = getParameter('homedir') ?? os.homedir();
|
|
907
736
|
this.matterbridgeInformation.homeDirectory = this.homeDirectory;
|
|
908
737
|
this.log.debug(`Home Directory: ${this.homeDirectory}`);
|
|
909
|
-
// Package root directory
|
|
910
738
|
const { fileURLToPath } = await import('node:url');
|
|
911
739
|
const currentFileDirectory = path.dirname(fileURLToPath(import.meta.url));
|
|
912
740
|
this.rootDirectory = path.resolve(currentFileDirectory, '../');
|
|
913
741
|
this.matterbridgeInformation.rootDirectory = this.rootDirectory;
|
|
914
742
|
this.log.debug(`Root Directory: ${this.rootDirectory}`);
|
|
915
|
-
// Global node_modules directory
|
|
916
743
|
if (this.nodeContext)
|
|
917
744
|
this.globalModulesDirectory = this.matterbridgeInformation.globalModulesDirectory = await this.nodeContext.get('globalModulesDirectory', '');
|
|
918
|
-
// First run of Matterbridge so the node storage is empty
|
|
919
745
|
if (this.globalModulesDirectory === '') {
|
|
920
746
|
try {
|
|
921
747
|
this.execRunningCount++;
|
|
@@ -931,20 +757,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
931
757
|
}
|
|
932
758
|
else
|
|
933
759
|
this.log.debug(`Global node_modules Directory: ${this.globalModulesDirectory}`);
|
|
934
|
-
/* removed cause is too expensive for the shelly board and not really needed. Why should it change the globalModulesDirectory?
|
|
935
|
-
else {
|
|
936
|
-
this.getGlobalNodeModules()
|
|
937
|
-
.then(async (globalModulesDirectory) => {
|
|
938
|
-
this.globalModulesDirectory = globalModulesDirectory;
|
|
939
|
-
this.matterbridgeInformation.globalModulesDirectory = this.globalModulesDirectory;
|
|
940
|
-
this.log.debug(`Global node_modules Directory: ${this.globalModulesDirectory}`);
|
|
941
|
-
await this.nodeContext?.set<string>('globalModulesDirectory', this.globalModulesDirectory);
|
|
942
|
-
})
|
|
943
|
-
.catch((error) => {
|
|
944
|
-
this.log.error(`Error getting global node_modules directory: ${error}`);
|
|
945
|
-
});
|
|
946
|
-
}*/
|
|
947
|
-
// Create the data directory .matterbridge in the home directory
|
|
948
760
|
this.matterbridgeDirectory = path.join(this.homeDirectory, '.matterbridge');
|
|
949
761
|
this.matterbridgeInformation.matterbridgeDirectory = this.matterbridgeDirectory;
|
|
950
762
|
try {
|
|
@@ -968,7 +780,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
968
780
|
}
|
|
969
781
|
}
|
|
970
782
|
this.log.debug(`Matterbridge Directory: ${this.matterbridgeDirectory}`);
|
|
971
|
-
// Create the plugin directory Matterbridge in the home directory
|
|
972
783
|
this.matterbridgePluginDirectory = path.join(this.homeDirectory, 'Matterbridge');
|
|
973
784
|
this.matterbridgeInformation.matterbridgePluginDirectory = this.matterbridgePluginDirectory;
|
|
974
785
|
try {
|
|
@@ -992,68 +803,50 @@ export class Matterbridge extends EventEmitter {
|
|
|
992
803
|
}
|
|
993
804
|
}
|
|
994
805
|
this.log.debug(`Matterbridge Plugin Directory: ${this.matterbridgePluginDirectory}`);
|
|
995
|
-
// Matterbridge version
|
|
996
806
|
const packageJson = JSON.parse(await fs.readFile(path.join(this.rootDirectory, 'package.json'), 'utf-8'));
|
|
997
807
|
this.matterbridgeVersion = this.matterbridgeLatestVersion = packageJson.version;
|
|
998
808
|
this.matterbridgeInformation.matterbridgeVersion = this.matterbridgeInformation.matterbridgeLatestVersion = this.matterbridgeVersion;
|
|
999
809
|
this.log.debug(`Matterbridge Version: ${this.matterbridgeVersion}`);
|
|
1000
|
-
// Matterbridge latest version
|
|
1001
810
|
if (this.nodeContext)
|
|
1002
811
|
this.matterbridgeLatestVersion = await this.nodeContext.get('matterbridgeLatestVersion', this.matterbridgeVersion);
|
|
1003
812
|
this.log.debug(`Matterbridge Latest Version: ${this.matterbridgeLatestVersion}`);
|
|
1004
|
-
// this.getMatterbridgeLatestVersion();
|
|
1005
|
-
// Current working directory
|
|
1006
813
|
const currentDir = process.cwd();
|
|
1007
814
|
this.log.debug(`Current Working Directory: ${currentDir}`);
|
|
1008
|
-
// Command line arguments (excluding 'node' and the script name)
|
|
1009
815
|
const cmdArgs = process.argv.slice(2).join(' ');
|
|
1010
816
|
this.log.debug(`Command Line Arguments: ${cmdArgs}`);
|
|
1011
817
|
}
|
|
1012
|
-
/**
|
|
1013
|
-
* Creates a MatterLogger function to show the matter.js log messages in AnsiLogger (for the frontend).
|
|
1014
|
-
*
|
|
1015
|
-
* @returns {Function} The MatterLogger function.
|
|
1016
|
-
*/
|
|
1017
818
|
createMatterLogger() {
|
|
1018
|
-
const matterLogger = new AnsiLogger({ logName: 'Matter', logTimestampFormat: 4
|
|
819
|
+
const matterLogger = new AnsiLogger({ logName: 'Matter', logTimestampFormat: 4, logLevel: "debug" });
|
|
1019
820
|
return (_level, formattedLog) => {
|
|
1020
821
|
const logger = formattedLog.slice(44, 44 + 20).trim();
|
|
1021
822
|
const message = formattedLog.slice(65);
|
|
1022
823
|
matterLogger.logName = logger;
|
|
1023
824
|
switch (_level) {
|
|
1024
825
|
case MatterLogLevel.DEBUG:
|
|
1025
|
-
matterLogger.log("debug"
|
|
826
|
+
matterLogger.log("debug", message);
|
|
1026
827
|
break;
|
|
1027
828
|
case MatterLogLevel.INFO:
|
|
1028
|
-
matterLogger.log("info"
|
|
829
|
+
matterLogger.log("info", message);
|
|
1029
830
|
break;
|
|
1030
831
|
case MatterLogLevel.NOTICE:
|
|
1031
|
-
matterLogger.log("notice"
|
|
832
|
+
matterLogger.log("notice", message);
|
|
1032
833
|
break;
|
|
1033
834
|
case MatterLogLevel.WARN:
|
|
1034
|
-
matterLogger.log("warn"
|
|
835
|
+
matterLogger.log("warn", message);
|
|
1035
836
|
break;
|
|
1036
837
|
case MatterLogLevel.ERROR:
|
|
1037
|
-
matterLogger.log("error"
|
|
838
|
+
matterLogger.log("error", message);
|
|
1038
839
|
break;
|
|
1039
840
|
case MatterLogLevel.FATAL:
|
|
1040
|
-
matterLogger.log("fatal"
|
|
841
|
+
matterLogger.log("fatal", message);
|
|
1041
842
|
break;
|
|
1042
843
|
default:
|
|
1043
|
-
matterLogger.log("debug"
|
|
844
|
+
matterLogger.log("debug", message);
|
|
1044
845
|
break;
|
|
1045
846
|
}
|
|
1046
847
|
};
|
|
1047
848
|
}
|
|
1048
|
-
/**
|
|
1049
|
-
* Creates a Matter File Logger.
|
|
1050
|
-
*
|
|
1051
|
-
* @param {string} filePath - The path to the log file.
|
|
1052
|
-
* @param {boolean} [unlink=false] - Whether to unlink the log file before creating a new one.
|
|
1053
|
-
* @returns {Function} - A function that logs formatted messages to the log file.
|
|
1054
|
-
*/
|
|
1055
849
|
async createMatterFileLogger(filePath, unlink = false) {
|
|
1056
|
-
// 2024-08-21 08:55:19.488 DEBUG InteractionMessenger Sending DataReport chunk with 28 attributes and 0 events: 1004 bytes
|
|
1057
850
|
let fileSize = 0;
|
|
1058
851
|
if (unlink) {
|
|
1059
852
|
try {
|
|
@@ -1102,21 +895,12 @@ export class Matterbridge extends EventEmitter {
|
|
|
1102
895
|
}
|
|
1103
896
|
};
|
|
1104
897
|
}
|
|
1105
|
-
/**
|
|
1106
|
-
* Restarts the process by exiting the current instance and loading a new instance.
|
|
1107
|
-
*/
|
|
1108
898
|
async restartProcess() {
|
|
1109
899
|
await this.cleanup('restarting...', true);
|
|
1110
900
|
}
|
|
1111
|
-
/**
|
|
1112
|
-
* Shut down the process by exiting the current process.
|
|
1113
|
-
*/
|
|
1114
901
|
async shutdownProcess() {
|
|
1115
902
|
await this.cleanup('shutting down...', false);
|
|
1116
903
|
}
|
|
1117
|
-
/**
|
|
1118
|
-
* Update matterbridge and and shut down the process.
|
|
1119
|
-
*/
|
|
1120
904
|
async updateProcess() {
|
|
1121
905
|
this.log.info('Updating matterbridge...');
|
|
1122
906
|
try {
|
|
@@ -1129,72 +913,51 @@ export class Matterbridge extends EventEmitter {
|
|
|
1129
913
|
this.frontend.wssSendRestartRequired();
|
|
1130
914
|
await this.cleanup('updating...', false);
|
|
1131
915
|
}
|
|
1132
|
-
/**
|
|
1133
|
-
* Unregister all devices and shut down the process.
|
|
1134
|
-
*/
|
|
1135
916
|
async unregisterAndShutdownProcess() {
|
|
1136
917
|
this.log.info('Unregistering all devices and shutting down...');
|
|
1137
918
|
for (const plugin of this.plugins) {
|
|
1138
919
|
await this.removeAllBridgedEndpoints(plugin.name, 250);
|
|
1139
920
|
}
|
|
1140
921
|
this.log.debug('Waiting for the MessageExchange to finish...');
|
|
1141
|
-
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
922
|
+
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
1142
923
|
this.log.debug('Cleaning up and shutting down...');
|
|
1143
924
|
await this.cleanup('unregistered all devices and shutting down...', false);
|
|
1144
925
|
}
|
|
1145
|
-
/**
|
|
1146
|
-
* Reset commissioning and shut down the process.
|
|
1147
|
-
*/
|
|
1148
926
|
async shutdownProcessAndReset() {
|
|
1149
927
|
await this.cleanup('shutting down with reset...', false);
|
|
1150
928
|
}
|
|
1151
|
-
/**
|
|
1152
|
-
* Factory reset and shut down the process.
|
|
1153
|
-
*/
|
|
1154
929
|
async shutdownProcessAndFactoryReset() {
|
|
1155
930
|
await this.cleanup('shutting down with factory reset...', false);
|
|
1156
931
|
}
|
|
1157
|
-
/**
|
|
1158
|
-
* Cleans up the Matterbridge instance.
|
|
1159
|
-
* @param message - The cleanup message.
|
|
1160
|
-
* @param restart - Indicates whether to restart the instance after cleanup. Default is `false`.
|
|
1161
|
-
* @returns A promise that resolves when the cleanup is completed.
|
|
1162
|
-
*/
|
|
1163
932
|
async cleanup(message, restart = false) {
|
|
1164
933
|
if (this.initialized && !this.hasCleanupStarted) {
|
|
1165
934
|
this.hasCleanupStarted = true;
|
|
1166
935
|
this.log.info(message);
|
|
1167
|
-
// Clear the start matter interval
|
|
1168
936
|
if (this.startMatterInterval) {
|
|
1169
937
|
clearInterval(this.startMatterInterval);
|
|
1170
938
|
this.startMatterInterval = undefined;
|
|
1171
939
|
this.log.debug('Start matter interval cleared');
|
|
1172
940
|
}
|
|
1173
|
-
// Clear the check update timeout
|
|
1174
941
|
if (this.checkUpdateTimeout) {
|
|
1175
942
|
clearInterval(this.checkUpdateTimeout);
|
|
1176
943
|
this.checkUpdateTimeout = undefined;
|
|
1177
944
|
this.log.debug('Check update timeout cleared');
|
|
1178
945
|
}
|
|
1179
|
-
// Clear the check update interval
|
|
1180
946
|
if (this.checkUpdateInterval) {
|
|
1181
947
|
clearInterval(this.checkUpdateInterval);
|
|
1182
948
|
this.checkUpdateInterval = undefined;
|
|
1183
949
|
this.log.debug('Check update interval cleared');
|
|
1184
950
|
}
|
|
1185
|
-
// Clear the configure timeout
|
|
1186
951
|
if (this.configureTimeout) {
|
|
1187
952
|
clearTimeout(this.configureTimeout);
|
|
1188
953
|
this.configureTimeout = undefined;
|
|
1189
954
|
this.log.debug('Matterbridge configure timeout cleared');
|
|
1190
955
|
}
|
|
1191
|
-
// Clear the reachability timeout
|
|
1192
956
|
if (this.reachabilityTimeout) {
|
|
1193
957
|
clearTimeout(this.reachabilityTimeout);
|
|
1194
958
|
this.reachabilityTimeout = undefined;
|
|
1195
959
|
this.log.debug('Matterbridge reachability timeout cleared');
|
|
1196
960
|
}
|
|
1197
|
-
// Calling the shutdown method of each plugin and clear the plugins reachability timeout
|
|
1198
961
|
for (const plugin of this.plugins) {
|
|
1199
962
|
if (!plugin.enabled || plugin.error)
|
|
1200
963
|
continue;
|
|
@@ -1205,10 +968,9 @@ export class Matterbridge extends EventEmitter {
|
|
|
1205
968
|
this.log.debug(`Plugin ${plg}${plugin.name}${db} reachability timeout cleared`);
|
|
1206
969
|
}
|
|
1207
970
|
}
|
|
1208
|
-
// Stopping matter server nodes
|
|
1209
971
|
this.log.notice(`Stopping matter server nodes in ${this.bridgeMode} mode...`);
|
|
1210
972
|
this.log.debug('Waiting for the MessageExchange to finish...');
|
|
1211
|
-
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
973
|
+
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
1212
974
|
if (this.bridgeMode === 'bridge') {
|
|
1213
975
|
if (this.serverNode) {
|
|
1214
976
|
await this.stopServerNode(this.serverNode);
|
|
@@ -1224,7 +986,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1224
986
|
}
|
|
1225
987
|
}
|
|
1226
988
|
this.log.notice('Stopped matter server nodes');
|
|
1227
|
-
// Matter commisioning reset
|
|
1228
989
|
if (message === 'shutting down with reset...') {
|
|
1229
990
|
this.log.info('Resetting Matterbridge commissioning information...');
|
|
1230
991
|
await this.matterStorageManager?.createContext('events')?.clearAll();
|
|
@@ -1234,37 +995,17 @@ export class Matterbridge extends EventEmitter {
|
|
|
1234
995
|
await this.matterbridgeContext?.clearAll();
|
|
1235
996
|
this.log.info('Matter storage reset done! Remove the bridge from the controller.');
|
|
1236
997
|
}
|
|
1237
|
-
// Stop matter storage
|
|
1238
998
|
await this.stopMatterStorage();
|
|
1239
|
-
// Stop the frontend
|
|
1240
999
|
await this.frontend.stop();
|
|
1241
|
-
// Remove the matterfilelogger
|
|
1242
1000
|
try {
|
|
1243
1001
|
Logger.removeLogger('matterfilelogger');
|
|
1244
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
1245
1002
|
}
|
|
1246
1003
|
catch (error) {
|
|
1247
|
-
// this.log.debug(`Error removing the matterfilelogger for file ${CYAN}${path.join(this.matterbridgeDirectory, this.matterLoggerFile)}${er}: ${error instanceof Error ? error.message : error}`);
|
|
1248
1004
|
}
|
|
1249
|
-
// Serialize registeredDevices
|
|
1250
1005
|
if (this.nodeStorage && this.nodeContext) {
|
|
1251
|
-
/*
|
|
1252
|
-
TODO: Implement serialization of registered devices in edge mode
|
|
1253
|
-
this.log.info('Saving registered devices...');
|
|
1254
|
-
const serializedRegisteredDevices: SerializedMatterbridgeEndpoint[] = [];
|
|
1255
|
-
this.devices.forEach(async (device) => {
|
|
1256
|
-
const serializedMatterbridgeDevice = MatterbridgeEndpoint.serialize(device);
|
|
1257
|
-
// this.log.info(`- ${serializedMatterbridgeDevice.deviceName}${rs}\n`, serializedMatterbridgeDevice);
|
|
1258
|
-
if (serializedMatterbridgeDevice) serializedRegisteredDevices.push(serializedMatterbridgeDevice);
|
|
1259
|
-
});
|
|
1260
|
-
await this.nodeContext.set<SerializedMatterbridgeEndpoint[]>('devices', serializedRegisteredDevices);
|
|
1261
|
-
this.log.info(`Saved registered devices (${serializedRegisteredDevices?.length})`);
|
|
1262
|
-
*/
|
|
1263
|
-
// Clear nodeContext and nodeStorage (they just need 1000ms to write the data to disk)
|
|
1264
1006
|
this.log.debug(`Closing node storage context for ${plg}Matterbridge${db}...`);
|
|
1265
1007
|
await this.nodeContext.close();
|
|
1266
1008
|
this.nodeContext = undefined;
|
|
1267
|
-
// Clear nodeContext for each plugin (they just need 1000ms to write the data to disk)
|
|
1268
1009
|
for (const plugin of this.plugins) {
|
|
1269
1010
|
if (plugin.nodeContext) {
|
|
1270
1011
|
this.log.debug(`Closing node storage context for plugin ${plg}${plugin.name}${db}...`);
|
|
@@ -1281,10 +1022,8 @@ export class Matterbridge extends EventEmitter {
|
|
|
1281
1022
|
}
|
|
1282
1023
|
this.plugins.clear();
|
|
1283
1024
|
this.devices.clear();
|
|
1284
|
-
// Factory reset
|
|
1285
1025
|
if (message === 'shutting down with factory reset...') {
|
|
1286
1026
|
try {
|
|
1287
|
-
// Delete old matter storage file and backup
|
|
1288
1027
|
const file = path.join(this.matterbridgeDirectory, 'matterbridge' + (getParameter('profile') ? '.' + getParameter('profile') : '') + '.json');
|
|
1289
1028
|
this.log.info(`Unlinking old matter storage file: ${file}`);
|
|
1290
1029
|
await fs.unlink(file);
|
|
@@ -1298,7 +1037,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1298
1037
|
}
|
|
1299
1038
|
}
|
|
1300
1039
|
try {
|
|
1301
|
-
// Delete matter node storage directory with its subdirectories and backup
|
|
1302
1040
|
const dir = path.join(this.matterbridgeDirectory, 'matterstorage' + (getParameter('profile') ? '.' + getParameter('profile') : ''));
|
|
1303
1041
|
this.log.info(`Removing matter node storage directory: ${dir}`);
|
|
1304
1042
|
await fs.rm(dir, { recursive: true });
|
|
@@ -1312,7 +1050,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1312
1050
|
}
|
|
1313
1051
|
}
|
|
1314
1052
|
try {
|
|
1315
|
-
// Delete node storage directory with its subdirectories and backup
|
|
1316
1053
|
const dir = path.join(this.matterbridgeDirectory, 'storage' + (getParameter('profile') ? '.' + getParameter('profile') : ''));
|
|
1317
1054
|
this.log.info(`Removing storage directory: ${dir}`);
|
|
1318
1055
|
await fs.rm(dir, { recursive: true });
|
|
@@ -1327,13 +1064,12 @@ export class Matterbridge extends EventEmitter {
|
|
|
1327
1064
|
}
|
|
1328
1065
|
this.log.info('Factory reset done! Remove all paired fabrics from the controllers.');
|
|
1329
1066
|
}
|
|
1330
|
-
// Deregisters the process handlers
|
|
1331
1067
|
this.deregisterProcesslHandlers();
|
|
1332
1068
|
if (restart) {
|
|
1333
1069
|
if (message === 'updating...') {
|
|
1334
1070
|
this.log.info('Cleanup completed. Updating...');
|
|
1335
1071
|
Matterbridge.instance = undefined;
|
|
1336
|
-
this.emit('update');
|
|
1072
|
+
this.emit('update');
|
|
1337
1073
|
}
|
|
1338
1074
|
else if (message === 'restarting...') {
|
|
1339
1075
|
this.log.info('Cleanup completed. Restarting...');
|
|
@@ -1353,14 +1089,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1353
1089
|
this.log.debug('Cleanup already started...');
|
|
1354
1090
|
}
|
|
1355
1091
|
}
|
|
1356
|
-
/**
|
|
1357
|
-
* Creates and configures the server node for an accessory plugin for a given device.
|
|
1358
|
-
*
|
|
1359
|
-
* @param {RegisteredPlugin} plugin - The plugin to configure.
|
|
1360
|
-
* @param {MatterbridgeEndpoint} device - The device to associate with the plugin.
|
|
1361
|
-
* @param {boolean} [start=false] - Whether to start the server node after adding the device.
|
|
1362
|
-
* @returns {Promise<void>} A promise that resolves when the server node for the accessory plugin is created and configured.
|
|
1363
|
-
*/
|
|
1364
1092
|
async createAccessoryPlugin(plugin, device, start = false) {
|
|
1365
1093
|
if (!plugin.locked && device.deviceName && device.vendorId && device.productId && device.vendorName && device.productName) {
|
|
1366
1094
|
plugin.locked = true;
|
|
@@ -1373,13 +1101,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1373
1101
|
await this.startServerNode(plugin.serverNode);
|
|
1374
1102
|
}
|
|
1375
1103
|
}
|
|
1376
|
-
/**
|
|
1377
|
-
* Creates and configures the server node for a dynamic plugin.
|
|
1378
|
-
*
|
|
1379
|
-
* @param {RegisteredPlugin} plugin - The plugin to configure.
|
|
1380
|
-
* @param {boolean} [start=false] - Whether to start the server node after adding the aggregator node.
|
|
1381
|
-
* @returns {Promise<void>} A promise that resolves when the server node for the dynamic plugin is created and configured.
|
|
1382
|
-
*/
|
|
1383
1104
|
async createDynamicPlugin(plugin, start = false) {
|
|
1384
1105
|
if (!plugin.locked) {
|
|
1385
1106
|
plugin.locked = true;
|
|
@@ -1391,13 +1112,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
1391
1112
|
await this.startServerNode(plugin.serverNode);
|
|
1392
1113
|
}
|
|
1393
1114
|
}
|
|
1394
|
-
/**
|
|
1395
|
-
* Starts the Matterbridge in bridge mode.
|
|
1396
|
-
* @private
|
|
1397
|
-
* @returns {Promise<void>} A promise that resolves when the Matterbridge is started.
|
|
1398
|
-
*/
|
|
1399
1115
|
async startBridge() {
|
|
1400
|
-
// Plugins are configured by a timer when matter server is started and plugin.configured is set to true
|
|
1401
1116
|
if (!this.matterStorageManager)
|
|
1402
1117
|
throw new Error('No storage manager initialized');
|
|
1403
1118
|
if (!this.matterbridgeContext)
|
|
@@ -1435,9 +1150,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
1435
1150
|
clearInterval(this.startMatterInterval);
|
|
1436
1151
|
this.startMatterInterval = undefined;
|
|
1437
1152
|
this.log.debug('Cleared startMatterInterval interval for Matterbridge');
|
|
1438
|
-
// Start the Matter server node
|
|
1439
1153
|
this.startServerNode(this.serverNode);
|
|
1440
|
-
// Configure the plugins
|
|
1441
1154
|
this.configureTimeout = setTimeout(async () => {
|
|
1442
1155
|
for (const plugin of this.plugins) {
|
|
1443
1156
|
if (!plugin.enabled || !plugin.loaded || !plugin.started || plugin.error)
|
|
@@ -1455,7 +1168,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1455
1168
|
}
|
|
1456
1169
|
this.frontend.wssSendRefreshRequired('plugins');
|
|
1457
1170
|
}, 30 * 1000);
|
|
1458
|
-
// Setting reachability to true
|
|
1459
1171
|
this.reachabilityTimeout = setTimeout(() => {
|
|
1460
1172
|
this.log.info(`Setting reachability to true for ${plg}Matterbridge${db}`);
|
|
1461
1173
|
if (this.aggregatorNode)
|
|
@@ -1464,11 +1176,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1464
1176
|
}, 60 * 1000);
|
|
1465
1177
|
}, 1000);
|
|
1466
1178
|
}
|
|
1467
|
-
/**
|
|
1468
|
-
* Starts the Matterbridge in childbridge mode.
|
|
1469
|
-
* @private
|
|
1470
|
-
* @returns {Promise<void>} A promise that resolves when the Matterbridge is started.
|
|
1471
|
-
*/
|
|
1472
1179
|
async startChildbridge() {
|
|
1473
1180
|
if (!this.matterStorageManager)
|
|
1474
1181
|
throw new Error('No storage manager initialized');
|
|
@@ -1513,7 +1220,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1513
1220
|
clearInterval(this.startMatterInterval);
|
|
1514
1221
|
this.startMatterInterval = undefined;
|
|
1515
1222
|
this.log.debug('Cleared startMatterInterval interval in childbridge mode');
|
|
1516
|
-
// Configure the plugins
|
|
1517
1223
|
this.configureTimeout = setTimeout(async () => {
|
|
1518
1224
|
for (const plugin of this.plugins) {
|
|
1519
1225
|
if (!plugin.enabled || !plugin.loaded || !plugin.started || plugin.error)
|
|
@@ -1550,9 +1256,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
1550
1256
|
this.log.error(`Node storage context not found for plugin ${plg}${plugin.name}${er}`);
|
|
1551
1257
|
continue;
|
|
1552
1258
|
}
|
|
1553
|
-
// Start the Matter server node
|
|
1554
1259
|
this.startServerNode(plugin.serverNode);
|
|
1555
|
-
// Setting reachability to true
|
|
1556
1260
|
plugin.reachabilityTimeout = setTimeout(() => {
|
|
1557
1261
|
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}`);
|
|
1558
1262
|
if (plugin.type === 'DynamicPlatform' && plugin.aggregatorNode)
|
|
@@ -1562,219 +1266,9 @@ export class Matterbridge extends EventEmitter {
|
|
|
1562
1266
|
}
|
|
1563
1267
|
}, 1000);
|
|
1564
1268
|
}
|
|
1565
|
-
/**
|
|
1566
|
-
* Starts the Matterbridge controller.
|
|
1567
|
-
* @private
|
|
1568
|
-
* @returns {Promise<void>} A promise that resolves when the Matterbridge is started.
|
|
1569
|
-
*/
|
|
1570
1269
|
async startController() {
|
|
1571
|
-
/*
|
|
1572
|
-
if (!this.storageManager) {
|
|
1573
|
-
this.log.error('No storage manager initialized');
|
|
1574
|
-
await this.cleanup('No storage manager initialized');
|
|
1575
|
-
return;
|
|
1576
|
-
}
|
|
1577
|
-
this.log.info('Creating context: mattercontrollerContext');
|
|
1578
|
-
this.mattercontrollerContext = this.storageManager.createContext('mattercontrollerContext');
|
|
1579
|
-
if (!this.mattercontrollerContext) {
|
|
1580
|
-
this.log.error('No storage context mattercontrollerContext initialized');
|
|
1581
|
-
await this.cleanup('No storage context mattercontrollerContext initialized');
|
|
1582
|
-
return;
|
|
1583
|
-
}
|
|
1584
|
-
|
|
1585
|
-
this.log.debug('Starting matterbridge in mode', this.bridgeMode);
|
|
1586
|
-
this.matterServer = await this.createMatterServer(this.storageManager);
|
|
1587
|
-
this.log.info('Creating matter commissioning controller');
|
|
1588
|
-
this.commissioningController = new CommissioningController({
|
|
1589
|
-
autoConnect: false,
|
|
1590
|
-
});
|
|
1591
|
-
this.log.info('Adding matter commissioning controller to matter server');
|
|
1592
|
-
await this.matterServer.addCommissioningController(this.commissioningController);
|
|
1593
|
-
|
|
1594
|
-
this.log.info('Starting matter server');
|
|
1595
|
-
await this.matterServer.start();
|
|
1596
|
-
this.log.info('Matter server started');
|
|
1597
|
-
|
|
1598
|
-
if (hasParameter('pairingcode')) {
|
|
1599
|
-
this.log.info('Pairing device with pairingcode:', getParameter('pairingcode'));
|
|
1600
|
-
const pairingCode = getParameter('pairingcode');
|
|
1601
|
-
const ip = this.mattercontrollerContext.has('ip') ? this.mattercontrollerContext.get<string>('ip') : undefined;
|
|
1602
|
-
const port = this.mattercontrollerContext.has('port') ? this.mattercontrollerContext.get<number>('port') : undefined;
|
|
1603
|
-
|
|
1604
|
-
let longDiscriminator, setupPin, shortDiscriminator;
|
|
1605
|
-
if (pairingCode !== undefined) {
|
|
1606
|
-
const pairingCodeCodec = ManualPairingCodeCodec.decode(pairingCode);
|
|
1607
|
-
shortDiscriminator = pairingCodeCodec.shortDiscriminator;
|
|
1608
|
-
longDiscriminator = undefined;
|
|
1609
|
-
setupPin = pairingCodeCodec.passcode;
|
|
1610
|
-
this.log.info(`Data extracted from pairing code: ${Logger.toJSON(pairingCodeCodec)}`);
|
|
1611
|
-
} else {
|
|
1612
|
-
longDiscriminator = await this.mattercontrollerContext.get('longDiscriminator', 3840);
|
|
1613
|
-
if (longDiscriminator > 4095) throw new Error('Discriminator value must be less than 4096');
|
|
1614
|
-
setupPin = this.mattercontrollerContext.get('pin', 20202021);
|
|
1615
|
-
}
|
|
1616
|
-
if ((shortDiscriminator === undefined && longDiscriminator === undefined) || setupPin === undefined) {
|
|
1617
|
-
throw new Error('Please specify the longDiscriminator of the device to commission with -longDiscriminator or provide a valid passcode with -passcode');
|
|
1618
|
-
}
|
|
1619
|
-
|
|
1620
|
-
const commissioningOptions: ControllerCommissioningFlowOptions = {
|
|
1621
|
-
regulatoryLocation: GeneralCommissioning.RegulatoryLocationType.IndoorOutdoor,
|
|
1622
|
-
regulatoryCountryCode: 'XX',
|
|
1623
|
-
};
|
|
1624
|
-
const options = {
|
|
1625
|
-
commissioning: commissioningOptions,
|
|
1626
|
-
discovery: {
|
|
1627
|
-
knownAddress: ip !== undefined && port !== undefined ? { ip, port, type: 'udp' } : undefined,
|
|
1628
|
-
identifierData: longDiscriminator !== undefined ? { longDiscriminator } : shortDiscriminator !== undefined ? { shortDiscriminator } : {},
|
|
1629
|
-
},
|
|
1630
|
-
passcode: setupPin,
|
|
1631
|
-
} as NodeCommissioningOptions;
|
|
1632
|
-
this.log.info('Commissioning with options:', options);
|
|
1633
|
-
const nodeId = await this.commissioningController.commissionNode(options);
|
|
1634
|
-
this.log.info(`Commissioning successfully done with nodeId: ${nodeId}`);
|
|
1635
|
-
this.log.info('ActiveSessionInformation:', this.commissioningController.getActiveSessionInformation());
|
|
1636
|
-
} // (hasParameter('pairingcode'))
|
|
1637
|
-
|
|
1638
|
-
if (hasParameter('unpairall')) {
|
|
1639
|
-
this.log.info('***Commissioning controller unpairing all nodes...');
|
|
1640
|
-
const nodeIds = this.commissioningController.getCommissionedNodes();
|
|
1641
|
-
for (const nodeId of nodeIds) {
|
|
1642
|
-
this.log.info('***Commissioning controller unpairing node:', nodeId);
|
|
1643
|
-
await this.commissioningController.removeNode(nodeId);
|
|
1644
|
-
}
|
|
1645
|
-
return;
|
|
1646
|
-
}
|
|
1647
|
-
|
|
1648
|
-
if (hasParameter('discover')) {
|
|
1649
|
-
// const discover = await this.commissioningController.discoverCommissionableDevices({ productId: 0x8000, deviceType: 0xfff1 });
|
|
1650
|
-
// console.log(discover);
|
|
1651
|
-
}
|
|
1652
|
-
|
|
1653
|
-
if (!this.commissioningController.isCommissioned()) {
|
|
1654
|
-
this.log.info('***Commissioning controller is not commissioned: use matterbridge -controller -pairingcode [pairingcode] to commission a device');
|
|
1655
|
-
return;
|
|
1656
|
-
}
|
|
1657
|
-
|
|
1658
|
-
const nodeIds = this.commissioningController.getCommissionedNodes();
|
|
1659
|
-
this.log.info(`***Commissioning controller is commissioned ${this.commissioningController.isCommissioned()} and has ${nodeIds.length} nodes commisioned: `);
|
|
1660
|
-
for (const nodeId of nodeIds) {
|
|
1661
|
-
this.log.info(`***Connecting to commissioned node: ${nodeId}`);
|
|
1662
|
-
|
|
1663
|
-
const node = await this.commissioningController.connectNode(nodeId, {
|
|
1664
|
-
autoSubscribe: false,
|
|
1665
|
-
attributeChangedCallback: (peerNodeId, { path: { nodeId, clusterId, endpointId, attributeName }, value }) =>
|
|
1666
|
-
this.log.info(`***Commissioning controller attributeChangedCallback ${peerNodeId}: attribute ${nodeId}/${endpointId}/${clusterId}/${attributeName} changed to ${Logger.toJSON(value)}`),
|
|
1667
|
-
eventTriggeredCallback: (peerNodeId, { path: { nodeId, clusterId, endpointId, eventName }, events }) =>
|
|
1668
|
-
this.log.info(`***Commissioning controller eventTriggeredCallback ${peerNodeId}: Event ${nodeId}/${endpointId}/${clusterId}/${eventName} triggered with ${Logger.toJSON(events)}`),
|
|
1669
|
-
stateInformationCallback: (peerNodeId, info) => {
|
|
1670
|
-
switch (info) {
|
|
1671
|
-
case NodeStateInformation.Connected:
|
|
1672
|
-
this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} connected`);
|
|
1673
|
-
break;
|
|
1674
|
-
case NodeStateInformation.Disconnected:
|
|
1675
|
-
this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} disconnected`);
|
|
1676
|
-
break;
|
|
1677
|
-
case NodeStateInformation.Reconnecting:
|
|
1678
|
-
this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} reconnecting`);
|
|
1679
|
-
break;
|
|
1680
|
-
case NodeStateInformation.WaitingForDeviceDiscovery:
|
|
1681
|
-
this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} waiting for device discovery`);
|
|
1682
|
-
break;
|
|
1683
|
-
case NodeStateInformation.StructureChanged:
|
|
1684
|
-
this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} structure changed`);
|
|
1685
|
-
break;
|
|
1686
|
-
case NodeStateInformation.Decommissioned:
|
|
1687
|
-
this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} decommissioned`);
|
|
1688
|
-
break;
|
|
1689
|
-
default:
|
|
1690
|
-
this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} NodeStateInformation.${info}`);
|
|
1691
|
-
break;
|
|
1692
|
-
}
|
|
1693
|
-
},
|
|
1694
|
-
});
|
|
1695
|
-
|
|
1696
|
-
node.logStructure();
|
|
1697
|
-
|
|
1698
|
-
// Get the interaction client
|
|
1699
|
-
this.log.info('Getting the interaction client');
|
|
1700
|
-
const interactionClient = await node.getInteractionClient();
|
|
1701
|
-
let cluster;
|
|
1702
|
-
let attributes;
|
|
1703
|
-
|
|
1704
|
-
// Log BasicInformationCluster
|
|
1705
|
-
cluster = BasicInformationCluster;
|
|
1706
|
-
attributes = await interactionClient.getMultipleAttributes({
|
|
1707
|
-
attributes: [{ clusterId: cluster.id }],
|
|
1708
|
-
});
|
|
1709
|
-
if (attributes.length > 0) this.log.info(`Cluster: ${idn}${cluster.name}${rs}${nf} attributes:`);
|
|
1710
|
-
attributes.forEach((attribute) => {
|
|
1711
|
-
this.log.info(
|
|
1712
|
-
`- 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}`,
|
|
1713
|
-
);
|
|
1714
|
-
});
|
|
1715
|
-
|
|
1716
|
-
// Log PowerSourceCluster
|
|
1717
|
-
cluster = PowerSourceCluster;
|
|
1718
|
-
attributes = await interactionClient.getMultipleAttributes({
|
|
1719
|
-
attributes: [{ clusterId: cluster.id }],
|
|
1720
|
-
});
|
|
1721
|
-
if (attributes.length > 0) this.log.info(`Cluster: ${idn}${cluster.name}${rs}${nf} attributes:`);
|
|
1722
|
-
attributes.forEach((attribute) => {
|
|
1723
|
-
this.log.info(
|
|
1724
|
-
`- 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}`,
|
|
1725
|
-
);
|
|
1726
|
-
});
|
|
1727
|
-
|
|
1728
|
-
// Log ThreadNetworkDiagnostics
|
|
1729
|
-
cluster = ThreadNetworkDiagnosticsCluster;
|
|
1730
|
-
attributes = await interactionClient.getMultipleAttributes({
|
|
1731
|
-
attributes: [{ clusterId: cluster.id }],
|
|
1732
|
-
});
|
|
1733
|
-
if (attributes.length > 0) this.log.info(`Cluster: ${idn}${cluster.name}${rs}${nf} attributes:`);
|
|
1734
|
-
attributes.forEach((attribute) => {
|
|
1735
|
-
this.log.info(
|
|
1736
|
-
`- 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}`,
|
|
1737
|
-
);
|
|
1738
|
-
});
|
|
1739
|
-
|
|
1740
|
-
// Log SwitchCluster
|
|
1741
|
-
cluster = SwitchCluster;
|
|
1742
|
-
attributes = await interactionClient.getMultipleAttributes({
|
|
1743
|
-
attributes: [{ clusterId: cluster.id }],
|
|
1744
|
-
});
|
|
1745
|
-
if (attributes.length > 0) this.log.info(`Cluster: ${idn}${cluster.name}${rs}${nf} attributes:`);
|
|
1746
|
-
attributes.forEach((attribute) => {
|
|
1747
|
-
this.log.info(
|
|
1748
|
-
`- 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}`,
|
|
1749
|
-
);
|
|
1750
|
-
});
|
|
1751
|
-
|
|
1752
|
-
this.log.info('Subscribing to all attributes and events');
|
|
1753
|
-
await node.subscribeAllAttributesAndEvents({
|
|
1754
|
-
ignoreInitialTriggers: false,
|
|
1755
|
-
attributeChangedCallback: ({ path: { nodeId, clusterId, endpointId, attributeName }, version, value }) =>
|
|
1756
|
-
this.log.info(
|
|
1757
|
-
`***${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}`,
|
|
1758
|
-
),
|
|
1759
|
-
eventTriggeredCallback: ({ path: { nodeId, clusterId, endpointId, eventName }, events }) => {
|
|
1760
|
-
this.log.info(
|
|
1761
|
-
`***${db}Commissioning controller eventTriggeredCallback: event ${BLUE}${nodeId}${db}/${or}${endpointId}${db}/${hk}${getClusterNameById(clusterId)}${db}/${zb}${eventName}${db} triggered with ${debugStringify(events ?? { none: true })}`,
|
|
1762
|
-
);
|
|
1763
|
-
},
|
|
1764
|
-
});
|
|
1765
|
-
this.log.info('Subscribed to all attributes and events');
|
|
1766
|
-
}
|
|
1767
|
-
*/
|
|
1768
1270
|
}
|
|
1769
|
-
/** ***********************************************************************************************************************************/
|
|
1770
|
-
/** Matter.js methods */
|
|
1771
|
-
/** ***********************************************************************************************************************************/
|
|
1772
|
-
/**
|
|
1773
|
-
* Starts the matter storage process with name Matterbridge.
|
|
1774
|
-
* @returns {Promise<void>} - A promise that resolves when the storage process is started.
|
|
1775
|
-
*/
|
|
1776
1271
|
async startMatterStorage() {
|
|
1777
|
-
// Setup Matter storage
|
|
1778
1272
|
this.log.info(`Starting matter node storage...`);
|
|
1779
1273
|
this.matterStorageService = this.environment.get(StorageService);
|
|
1780
1274
|
this.log.info(`Matter node storage service created: ${this.matterStorageService.location}`);
|
|
@@ -1782,25 +1276,13 @@ export class Matterbridge extends EventEmitter {
|
|
|
1782
1276
|
this.log.info('Matter node storage manager "Matterbridge" created');
|
|
1783
1277
|
this.matterbridgeContext = await this.createServerNodeContext('Matterbridge', 'Matterbridge', bridge.code, this.aggregatorVendorId, 'Matterbridge', this.aggregatorProductId, 'Matterbridge aggregator');
|
|
1784
1278
|
this.log.info('Matter node storage started');
|
|
1785
|
-
// Backup matter storage since it is created/opened correctly
|
|
1786
1279
|
await this.backupMatterStorage(path.join(this.matterbridgeDirectory, this.matterStorageName), path.join(this.matterbridgeDirectory, this.matterStorageName + '.backup'));
|
|
1787
1280
|
}
|
|
1788
|
-
/**
|
|
1789
|
-
* Makes a backup copy of the specified matter storage directory.
|
|
1790
|
-
*
|
|
1791
|
-
* @param storageName - The name of the storage directory to be backed up.
|
|
1792
|
-
* @param backupName - The name of the backup directory to be created.
|
|
1793
|
-
* @returns {Promise<void>} A promise that resolves when the has been done.
|
|
1794
|
-
*/
|
|
1795
1281
|
async backupMatterStorage(storageName, backupName) {
|
|
1796
1282
|
this.log.info('Creating matter node storage backup...');
|
|
1797
1283
|
await copyDirectory(storageName, backupName);
|
|
1798
1284
|
this.log.info('Created matter node storage backup');
|
|
1799
1285
|
}
|
|
1800
|
-
/**
|
|
1801
|
-
* Stops the matter storage.
|
|
1802
|
-
* @returns {Promise<void>} A promise that resolves when the storage is stopped.
|
|
1803
|
-
*/
|
|
1804
1286
|
async stopMatterStorage() {
|
|
1805
1287
|
this.log.info('Closing matter node storage...');
|
|
1806
1288
|
this.matterStorageManager?.close();
|
|
@@ -1809,19 +1291,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1809
1291
|
this.matterbridgeContext = undefined;
|
|
1810
1292
|
this.log.info('Matter node storage closed');
|
|
1811
1293
|
}
|
|
1812
|
-
/**
|
|
1813
|
-
* Creates a server node storage context.
|
|
1814
|
-
*
|
|
1815
|
-
* @param {string} pluginName - The name of the plugin.
|
|
1816
|
-
* @param {string} deviceName - The name of the device.
|
|
1817
|
-
* @param {DeviceTypeId} deviceType - The device type of the device.
|
|
1818
|
-
* @param {number} vendorId - The vendor ID.
|
|
1819
|
-
* @param {string} vendorName - The vendor name.
|
|
1820
|
-
* @param {number} productId - The product ID.
|
|
1821
|
-
* @param {string} productName - The product name.
|
|
1822
|
-
* @param {string} [serialNumber] - The serial number of the device (optional).
|
|
1823
|
-
* @returns {Promise<StorageContext>} The storage context for the commissioning server.
|
|
1824
|
-
*/
|
|
1825
1294
|
async createServerNodeContext(pluginName, deviceName, deviceType, vendorId, vendorName, productId, productName, serialNumber) {
|
|
1826
1295
|
const { randomBytes } = await import('node:crypto');
|
|
1827
1296
|
if (!this.matterStorageService)
|
|
@@ -1855,15 +1324,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1855
1324
|
this.log.debug(`- hardwareVersion: ${await storageContext.get('hardwareVersion')} hardwareVersionString: ${await storageContext.get('hardwareVersionString')}`);
|
|
1856
1325
|
return storageContext;
|
|
1857
1326
|
}
|
|
1858
|
-
/**
|
|
1859
|
-
* Creates a server node.
|
|
1860
|
-
*
|
|
1861
|
-
* @param {StorageContext} storageContext - The storage context for the server node.
|
|
1862
|
-
* @param {number} [port=5540] - The port number for the server node. Defaults to 5540.
|
|
1863
|
-
* @param {number} [passcode=20242025] - The passcode for the server node. Defaults to 20242025.
|
|
1864
|
-
* @param {number} [discriminator=3850] - The discriminator for the server node. Defaults to 3850.
|
|
1865
|
-
* @returns {Promise<ServerNode<ServerNode.RootEndpoint>>} A promise that resolves to the created server node.
|
|
1866
|
-
*/
|
|
1867
1327
|
async createServerNode(storageContext, port = 5540, passcode = 20242025, discriminator = 3850) {
|
|
1868
1328
|
const storeId = await storageContext.get('storeId');
|
|
1869
1329
|
this.log.notice(`Creating server node for ${storeId} on port ${port} with passcode ${passcode} and discriminator ${discriminator}...`);
|
|
@@ -1873,33 +1333,21 @@ export class Matterbridge extends EventEmitter {
|
|
|
1873
1333
|
this.log.debug(`- uniqueId: ${await storageContext.get('uniqueId')}`);
|
|
1874
1334
|
this.log.debug(`- softwareVersion: ${await storageContext.get('softwareVersion')} softwareVersionString: ${await storageContext.get('softwareVersionString')}`);
|
|
1875
1335
|
this.log.debug(`- hardwareVersion: ${await storageContext.get('hardwareVersion')} hardwareVersionString: ${await storageContext.get('hardwareVersionString')}`);
|
|
1876
|
-
/**
|
|
1877
|
-
* Create a Matter ServerNode, which contains the Root Endpoint and all relevant data and configuration
|
|
1878
|
-
*/
|
|
1879
1336
|
const serverNode = await ServerNode.create({
|
|
1880
|
-
// Required: Give the Node a unique ID which is used to store the state of this node
|
|
1881
1337
|
id: storeId,
|
|
1882
|
-
// Provide Network relevant configuration like the port
|
|
1883
|
-
// Optional when operating only one device on a host, Default port is 5540
|
|
1884
1338
|
network: {
|
|
1885
1339
|
listeningAddressIpv4: this.ipv4address,
|
|
1886
1340
|
listeningAddressIpv6: this.ipv6address,
|
|
1887
1341
|
port,
|
|
1888
1342
|
},
|
|
1889
|
-
// Provide Commissioning relevant settings
|
|
1890
|
-
// Optional for development/testing purposes
|
|
1891
1343
|
commissioning: {
|
|
1892
1344
|
passcode,
|
|
1893
1345
|
discriminator,
|
|
1894
1346
|
},
|
|
1895
|
-
// Provide Node announcement settings
|
|
1896
|
-
// Optional: If Ommitted some development defaults are used
|
|
1897
1347
|
productDescription: {
|
|
1898
1348
|
name: await storageContext.get('deviceName'),
|
|
1899
1349
|
deviceType: DeviceTypeId(await storageContext.get('deviceType')),
|
|
1900
1350
|
},
|
|
1901
|
-
// Provide defaults for the BasicInformation cluster on the Root endpoint
|
|
1902
|
-
// Optional: If Omitted some development defaults are used
|
|
1903
1351
|
basicInformation: {
|
|
1904
1352
|
vendorId: VendorId(await storageContext.get('vendorId')),
|
|
1905
1353
|
vendorName: await storageContext.get('vendorName'),
|
|
@@ -1916,13 +1364,12 @@ export class Matterbridge extends EventEmitter {
|
|
|
1916
1364
|
},
|
|
1917
1365
|
});
|
|
1918
1366
|
const sanitizeFabrics = (fabrics, resetSessions = false) => {
|
|
1919
|
-
// New type of fabric information: Record<FabricIndex, ExposedFabricInformation>
|
|
1920
1367
|
const sanitizedFabrics = this.sanitizeFabricInformations(Array.from(Object.values(fabrics)));
|
|
1921
1368
|
this.log.info(`Fabrics: ${debugStringify(sanitizedFabrics)}`);
|
|
1922
1369
|
if (this.bridgeMode === 'bridge') {
|
|
1923
1370
|
this.matterbridgeFabricInformations = sanitizedFabrics;
|
|
1924
1371
|
if (resetSessions)
|
|
1925
|
-
this.matterbridgeSessionInformations = undefined;
|
|
1372
|
+
this.matterbridgeSessionInformations = undefined;
|
|
1926
1373
|
this.matterbridgePaired = true;
|
|
1927
1374
|
}
|
|
1928
1375
|
if (this.bridgeMode === 'childbridge') {
|
|
@@ -1930,19 +1377,13 @@ export class Matterbridge extends EventEmitter {
|
|
|
1930
1377
|
if (plugin) {
|
|
1931
1378
|
plugin.fabricInformations = sanitizedFabrics;
|
|
1932
1379
|
if (resetSessions)
|
|
1933
|
-
plugin.sessionInformations = undefined;
|
|
1380
|
+
plugin.sessionInformations = undefined;
|
|
1934
1381
|
plugin.paired = true;
|
|
1935
1382
|
}
|
|
1936
1383
|
}
|
|
1937
1384
|
};
|
|
1938
|
-
/**
|
|
1939
|
-
* This event is triggered when the device is initially commissioned successfully.
|
|
1940
|
-
* This means: It is added to the first fabric.
|
|
1941
|
-
*/
|
|
1942
1385
|
serverNode.lifecycle.commissioned.on(() => this.log.notice(`Server node for ${storeId} was initially commissioned successfully!`));
|
|
1943
|
-
/** This event is triggered when all fabrics are removed from the device, usually it also does a factory reset then. */
|
|
1944
1386
|
serverNode.lifecycle.decommissioned.on(() => this.log.notice(`Server node for ${storeId} was fully decommissioned successfully!`));
|
|
1945
|
-
/** This event is triggered when the device went online. This means that it is discoverable in the network. */
|
|
1946
1387
|
serverNode.lifecycle.online.on(async () => {
|
|
1947
1388
|
this.log.notice(`Server node for ${storeId} is online`);
|
|
1948
1389
|
if (!serverNode.lifecycle.isCommissioned) {
|
|
@@ -1990,7 +1431,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1990
1431
|
this.frontend.wssSendRefreshRequired('settings');
|
|
1991
1432
|
this.frontend.wssSendSnackbarMessage(`${storeId} is online`, 5, 'success');
|
|
1992
1433
|
});
|
|
1993
|
-
/** This event is triggered when the device went offline. it is not longer discoverable or connectable in the network. */
|
|
1994
1434
|
serverNode.lifecycle.offline.on(() => {
|
|
1995
1435
|
this.log.notice(`Server node for ${storeId} is offline`);
|
|
1996
1436
|
if (this.bridgeMode === 'bridge') {
|
|
@@ -2014,10 +1454,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2014
1454
|
this.frontend.wssSendRefreshRequired('settings');
|
|
2015
1455
|
this.frontend.wssSendSnackbarMessage(`${storeId} is offline`, 5, 'warning');
|
|
2016
1456
|
});
|
|
2017
|
-
/**
|
|
2018
|
-
* This event is triggered when a fabric is added, removed or updated on the device. Use this if more granular
|
|
2019
|
-
* information is needed.
|
|
2020
|
-
*/
|
|
2021
1457
|
serverNode.events.commissioning.fabricsChanged.on((fabricIndex, fabricAction) => {
|
|
2022
1458
|
let action = '';
|
|
2023
1459
|
switch (fabricAction) {
|
|
@@ -2051,24 +1487,16 @@ export class Matterbridge extends EventEmitter {
|
|
|
2051
1487
|
}
|
|
2052
1488
|
}
|
|
2053
1489
|
};
|
|
2054
|
-
/**
|
|
2055
|
-
* This event is triggered when an operative new session was opened by a Controller.
|
|
2056
|
-
* It is not triggered for the initial commissioning process, just afterwards for real connections.
|
|
2057
|
-
*/
|
|
2058
1490
|
serverNode.events.sessions.opened.on((session) => {
|
|
2059
1491
|
this.log.notice(`Session opened on server node for ${storeId}: ${debugStringify(session)}`);
|
|
2060
1492
|
sanitizeSessions(Object.values(serverNode.state.sessions.sessions));
|
|
2061
1493
|
this.frontend.wssSendRefreshRequired('sessions');
|
|
2062
1494
|
});
|
|
2063
|
-
/**
|
|
2064
|
-
* This event is triggered when an operative session is closed by a Controller or because the Device goes offline.
|
|
2065
|
-
*/
|
|
2066
1495
|
serverNode.events.sessions.closed.on((session) => {
|
|
2067
1496
|
this.log.notice(`Session closed on server node for ${storeId}: ${debugStringify(session)}`);
|
|
2068
1497
|
sanitizeSessions(Object.values(serverNode.state.sessions.sessions));
|
|
2069
1498
|
this.frontend.wssSendRefreshRequired('sessions');
|
|
2070
1499
|
});
|
|
2071
|
-
/** This event is triggered when a subscription gets added or removed on an operative session. */
|
|
2072
1500
|
serverNode.events.sessions.subscriptionsChanged.on((session) => {
|
|
2073
1501
|
this.log.notice(`Session subscriptions changed on server node for ${storeId}: ${debugStringify(session)}`);
|
|
2074
1502
|
sanitizeSessions(Object.values(serverNode.state.sessions.sessions));
|
|
@@ -2077,42 +1505,24 @@ export class Matterbridge extends EventEmitter {
|
|
|
2077
1505
|
this.log.info(`Created server node for ${storeId}`);
|
|
2078
1506
|
return serverNode;
|
|
2079
1507
|
}
|
|
2080
|
-
/**
|
|
2081
|
-
* Starts the specified server node.
|
|
2082
|
-
*
|
|
2083
|
-
* @param {ServerNode} [matterServerNode] - The server node to start.
|
|
2084
|
-
* @returns {Promise<void>} A promise that resolves when the server node has started.
|
|
2085
|
-
*/
|
|
2086
1508
|
async startServerNode(matterServerNode) {
|
|
2087
1509
|
if (!matterServerNode)
|
|
2088
1510
|
return;
|
|
2089
1511
|
this.log.notice(`Starting ${matterServerNode.id} server node`);
|
|
2090
1512
|
await matterServerNode.start();
|
|
2091
1513
|
}
|
|
2092
|
-
/**
|
|
2093
|
-
* Stops the specified server node.
|
|
2094
|
-
*
|
|
2095
|
-
* @param {ServerNode} matterServerNode - The server node to stop.
|
|
2096
|
-
* @returns {Promise<void>} A promise that resolves when the server node has stopped.
|
|
2097
|
-
*/
|
|
2098
1514
|
async stopServerNode(matterServerNode) {
|
|
2099
1515
|
if (!matterServerNode)
|
|
2100
1516
|
return;
|
|
2101
1517
|
this.log.notice(`Closing ${matterServerNode.id} server node`);
|
|
2102
1518
|
try {
|
|
2103
|
-
await withTimeout(matterServerNode.close(), 30000);
|
|
1519
|
+
await withTimeout(matterServerNode.close(), 30000);
|
|
2104
1520
|
this.log.info(`Closed ${matterServerNode.id} server node`);
|
|
2105
1521
|
}
|
|
2106
1522
|
catch (error) {
|
|
2107
1523
|
this.log.error(`Failed to close ${matterServerNode.id} server node: ${error instanceof Error ? error.message : error}`);
|
|
2108
1524
|
}
|
|
2109
1525
|
}
|
|
2110
|
-
/**
|
|
2111
|
-
* Advertises the specified server node.
|
|
2112
|
-
*
|
|
2113
|
-
* @param {ServerNode} [matterServerNode] - The server node to advertise.
|
|
2114
|
-
* @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.
|
|
2115
|
-
*/
|
|
2116
1526
|
async advertiseServerNode(matterServerNode) {
|
|
2117
1527
|
if (matterServerNode) {
|
|
2118
1528
|
await matterServerNode.env.get(DeviceCommissioner)?.allowBasicCommissioning();
|
|
@@ -2121,44 +1531,23 @@ export class Matterbridge extends EventEmitter {
|
|
|
2121
1531
|
return { qrPairingCode, manualPairingCode };
|
|
2122
1532
|
}
|
|
2123
1533
|
}
|
|
2124
|
-
/**
|
|
2125
|
-
* Stop advertise the specified server node.
|
|
2126
|
-
*
|
|
2127
|
-
* @param {ServerNode} [matterServerNode] - The server node to advertise.
|
|
2128
|
-
* @returns {Promise<void>} A promise that resolves when the server node has stopped advertising.
|
|
2129
|
-
*/
|
|
2130
1534
|
async stopAdvertiseServerNode(matterServerNode) {
|
|
2131
1535
|
if (matterServerNode && matterServerNode.lifecycle.isOnline) {
|
|
2132
1536
|
await matterServerNode.env.get(DeviceCommissioner)?.endCommissioning();
|
|
2133
1537
|
this.log.notice(`Stopped advertising for ${matterServerNode.id}`);
|
|
2134
1538
|
}
|
|
2135
1539
|
}
|
|
2136
|
-
/**
|
|
2137
|
-
* Creates an aggregator node with the specified storage context.
|
|
2138
|
-
*
|
|
2139
|
-
* @param {StorageContext} storageContext - The storage context for the aggregator node.
|
|
2140
|
-
* @returns {Promise<EndpointNode<AggregatorEndpoint>>} A promise that resolves to the created aggregator node.
|
|
2141
|
-
*/
|
|
2142
1540
|
async createAggregatorNode(storageContext) {
|
|
2143
1541
|
this.log.notice(`Creating ${await storageContext.get('storeId')} aggregator `);
|
|
2144
1542
|
const aggregatorNode = new EndpointNode(AggregatorEndpoint, { id: `${await storageContext.get('storeId')}` });
|
|
2145
1543
|
return aggregatorNode;
|
|
2146
1544
|
}
|
|
2147
|
-
/**
|
|
2148
|
-
* Adds a MatterbridgeEndpoint to the specified plugin.
|
|
2149
|
-
*
|
|
2150
|
-
* @param {string} pluginName - The name of the plugin.
|
|
2151
|
-
* @param {MatterbridgeEndpoint} device - The device to add as a bridged endpoint.
|
|
2152
|
-
* @returns {Promise<void>} A promise that resolves when the bridged endpoint has been added.
|
|
2153
|
-
*/
|
|
2154
1545
|
async addBridgedEndpoint(pluginName, device) {
|
|
2155
|
-
// Check if the plugin is registered
|
|
2156
1546
|
const plugin = this.plugins.get(pluginName);
|
|
2157
1547
|
if (!plugin) {
|
|
2158
1548
|
this.log.error(`Error adding bridged endpoint ${dev}${device.deviceName}${er} (${zb}${device.id}${er}) plugin ${plg}${pluginName}${er} not found`);
|
|
2159
1549
|
return;
|
|
2160
1550
|
}
|
|
2161
|
-
// Register and add the device to the matterbridge aggregator node
|
|
2162
1551
|
if (this.bridgeMode === 'bridge') {
|
|
2163
1552
|
this.log.debug(`Adding bridged endpoint ${plg}${pluginName}${db}:${dev}${device.deviceName}${db} to Matterbridge aggregator node`);
|
|
2164
1553
|
if (!this.aggregatorNode)
|
|
@@ -2167,7 +1556,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
2167
1556
|
}
|
|
2168
1557
|
else if (this.bridgeMode === 'childbridge') {
|
|
2169
1558
|
if (plugin.type === 'AccessoryPlatform') {
|
|
2170
|
-
this.createAccessoryPlugin(plugin, device);
|
|
1559
|
+
await this.createAccessoryPlugin(plugin, device);
|
|
2171
1560
|
}
|
|
2172
1561
|
if (plugin.type === 'DynamicPlatform') {
|
|
2173
1562
|
plugin.locked = true;
|
|
@@ -2181,26 +1570,17 @@ export class Matterbridge extends EventEmitter {
|
|
|
2181
1570
|
plugin.registeredDevices++;
|
|
2182
1571
|
if (plugin.addedDevices !== undefined)
|
|
2183
1572
|
plugin.addedDevices++;
|
|
2184
|
-
// Add the device to the DeviceManager
|
|
2185
1573
|
this.devices.set(device);
|
|
1574
|
+
await this.subscribeAttributeChanged(plugin, device);
|
|
2186
1575
|
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}`);
|
|
2187
1576
|
}
|
|
2188
|
-
/**
|
|
2189
|
-
* Removes a MatterbridgeEndpoint from the specified plugin.
|
|
2190
|
-
*
|
|
2191
|
-
* @param {string} pluginName - The name of the plugin.
|
|
2192
|
-
* @param {MatterbridgeEndpoint} device - The device to remove as a bridged endpoint.
|
|
2193
|
-
* @returns {Promise<void>} A promise that resolves when the bridged endpoint has been removed.
|
|
2194
|
-
*/
|
|
2195
1577
|
async removeBridgedEndpoint(pluginName, device) {
|
|
2196
1578
|
this.log.debug(`Removing bridged endpoint ${plg}${pluginName}${db}:${dev}${device.deviceName}${db} (${zb}${device.name}${db}) for plugin ${plg}${pluginName}${db}`);
|
|
2197
|
-
// Check if the plugin is registered
|
|
2198
1579
|
const plugin = this.plugins.get(pluginName);
|
|
2199
1580
|
if (!plugin) {
|
|
2200
1581
|
this.log.error(`Error removing bridged endpoint ${dev}${device.deviceName}${er} (${zb}${device.name}${er}) for plugin ${plg}${pluginName}${er}: plugin not found`);
|
|
2201
1582
|
return;
|
|
2202
1583
|
}
|
|
2203
|
-
// Register and add the device to the matterbridge aggregator node
|
|
2204
1584
|
if (this.bridgeMode === 'bridge') {
|
|
2205
1585
|
if (!this.aggregatorNode) {
|
|
2206
1586
|
this.log.error(`Error removing bridged endpoint ${dev}${device.deviceName}${er} (${zb}${device.name}${er}) for plugin ${plg}${pluginName}${er}: aggregator node not found`);
|
|
@@ -2215,7 +1595,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2215
1595
|
}
|
|
2216
1596
|
else if (this.bridgeMode === 'childbridge') {
|
|
2217
1597
|
if (plugin.type === 'AccessoryPlatform') {
|
|
2218
|
-
// Nothing to do here since the server node has no aggregator node but only the device itself
|
|
2219
1598
|
}
|
|
2220
1599
|
else if (plugin.type === 'DynamicPlatform') {
|
|
2221
1600
|
if (!plugin.aggregatorNode) {
|
|
@@ -2230,16 +1609,8 @@ export class Matterbridge extends EventEmitter {
|
|
|
2230
1609
|
if (plugin.addedDevices !== undefined)
|
|
2231
1610
|
plugin.addedDevices--;
|
|
2232
1611
|
}
|
|
2233
|
-
// Remove the device from the DeviceManager
|
|
2234
1612
|
this.devices.remove(device);
|
|
2235
1613
|
}
|
|
2236
|
-
/**
|
|
2237
|
-
* Removes all bridged endpoints from the specified plugin.
|
|
2238
|
-
*
|
|
2239
|
-
* @param {string} pluginName - The name of the plugin.
|
|
2240
|
-
* @param {number} [delay=0] - The delay in milliseconds between removing each bridged endpoint (default: 0).
|
|
2241
|
-
* @returns {Promise<void>} A promise that resolves when all bridged endpoints have been removed.
|
|
2242
|
-
*/
|
|
2243
1614
|
async removeAllBridgedEndpoints(pluginName, delay = 0) {
|
|
2244
1615
|
this.log.debug(`Removing all bridged endpoints for plugin ${plg}${pluginName}${db}`);
|
|
2245
1616
|
for (const device of this.devices.array().filter((device) => device.plugin === pluginName)) {
|
|
@@ -2248,12 +1619,21 @@ export class Matterbridge extends EventEmitter {
|
|
|
2248
1619
|
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
2249
1620
|
}
|
|
2250
1621
|
}
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
|
|
1622
|
+
async subscribeAttributeChanged(plugin, device) {
|
|
1623
|
+
this.log.info(`Subscribing attributes for endpoint ${dev}${device.deviceName}${nf} (${dev}${device.id}${nf}) plugin ${plg}${plugin.name}${nf}`);
|
|
1624
|
+
if (this.bridgeMode === 'childbridge' && plugin.type === 'AccessoryPlatform' && plugin.serverNode) {
|
|
1625
|
+
plugin.serverNode.eventsOf(BasicInformationServer).reachable$Changed?.on((reachable) => {
|
|
1626
|
+
this.log.info(`Accessory endpoint ${dev}${device.deviceName}${nf} (${dev}${device.id}${nf}) is ${reachable ? 'reachable' : 'unreachable'}`);
|
|
1627
|
+
this.frontend.wssSendAttributeChangedMessage(device.plugin, device.serialNumber, device.uniqueId, 'BasicInformationServer', 'reachable', reachable);
|
|
1628
|
+
});
|
|
1629
|
+
}
|
|
1630
|
+
if (device.hasClusterServer(BridgedDeviceBasicInformationServer)) {
|
|
1631
|
+
device.eventsOf(BridgedDeviceBasicInformationServer).reachable$Changed.on((reachable) => {
|
|
1632
|
+
this.log.info(`Bridged endpoint ${dev}${device.deviceName}${nf} (${dev}${device.id}${nf}) is ${reachable ? 'reachable' : 'unreachable'}`);
|
|
1633
|
+
this.frontend.wssSendAttributeChangedMessage(device.plugin, device.serialNumber, device.uniqueId, 'BridgedDeviceBasicInformationServer', 'reachable', reachable);
|
|
1634
|
+
});
|
|
1635
|
+
}
|
|
1636
|
+
}
|
|
2257
1637
|
sanitizeFabricInformations(fabricInfo) {
|
|
2258
1638
|
return fabricInfo.map((info) => {
|
|
2259
1639
|
return {
|
|
@@ -2267,12 +1647,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2267
1647
|
};
|
|
2268
1648
|
});
|
|
2269
1649
|
}
|
|
2270
|
-
/**
|
|
2271
|
-
* Sanitizes the session information by converting bigint properties to strings because `res.json` doesn't support bigint.
|
|
2272
|
-
*
|
|
2273
|
-
* @param {SessionInformation[]} sessionInfo - The array of session information objects.
|
|
2274
|
-
* @returns {SanitizedSessionInformation[]} An array of sanitized session information objects.
|
|
2275
|
-
*/
|
|
2276
1650
|
sanitizeSessionInformation(sessionInfo) {
|
|
2277
1651
|
return sessionInfo
|
|
2278
1652
|
.filter((session) => session.isPeerActive)
|
|
@@ -2300,17 +1674,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
2300
1674
|
};
|
|
2301
1675
|
});
|
|
2302
1676
|
}
|
|
2303
|
-
/**
|
|
2304
|
-
* Sets the reachability of the specified aggregator node bridged devices and trigger.
|
|
2305
|
-
* @param {EndpointNode<AggregatorEndpoint>} aggregatorNode - The aggregator node to set the reachability for.
|
|
2306
|
-
* @param {boolean} reachable - A boolean indicating the reachability status to set.
|
|
2307
|
-
*/
|
|
2308
1677
|
async setAggregatorReachability(aggregatorNode, reachable) {
|
|
2309
|
-
for (const child of aggregatorNode.parts) {
|
|
2310
|
-
this.log.debug(`Setting reachability of ${child?.deviceName} to ${reachable}`);
|
|
2311
|
-
await child.setStateOf(BridgedDeviceBasicInformationServer, { reachable });
|
|
2312
|
-
child.act((agent) => child.eventsOf(BridgedDeviceBasicInformationServer).reachableChanged.emit({ reachableNewValue: true }, agent.context));
|
|
2313
|
-
}
|
|
2314
1678
|
}
|
|
2315
1679
|
getVendorIdName = (vendorId) => {
|
|
2316
1680
|
if (!vendorId)
|
|
@@ -2350,37 +1714,14 @@ export class Matterbridge extends EventEmitter {
|
|
|
2350
1714
|
}
|
|
2351
1715
|
return vendorName;
|
|
2352
1716
|
};
|
|
2353
|
-
/**
|
|
2354
|
-
* Spawns a child process with the given command and arguments.
|
|
2355
|
-
* @param {string} command - The command to execute.
|
|
2356
|
-
* @param {string[]} args - The arguments to pass to the command (default: []).
|
|
2357
|
-
* @returns {Promise<boolean>} A promise that resolves when the child process exits successfully, or rejects if there is an error.
|
|
2358
|
-
*/
|
|
2359
1717
|
async spawnCommand(command, args = []) {
|
|
2360
1718
|
const { spawn } = await import('node:child_process');
|
|
2361
|
-
/*
|
|
2362
|
-
npm > npm.cmd on windows
|
|
2363
|
-
cmd.exe ['dir'] on windows
|
|
2364
|
-
await this.spawnCommand('npm', ['install', '-g', 'matterbridge']);
|
|
2365
|
-
process.on('unhandledRejection', (reason, promise) => {
|
|
2366
|
-
this.log.error('Unhandled Rejection at:', promise, 'reason:', reason);
|
|
2367
|
-
});
|
|
2368
|
-
|
|
2369
|
-
spawn - [14:27:21.125] [Matterbridge:spawn]: changed 38 packages in 4s
|
|
2370
|
-
spawn - [14:27:21.125] [Matterbridge:spawn]: 10 packages are looking for funding run `npm fund` for details
|
|
2371
|
-
debug - [14:27:21.131] [Matterbridge]: Child process exited with code 0 and signal null
|
|
2372
|
-
debug - [14:27:21.131] [Matterbridge]: Child process stdio streams have closed with code 0
|
|
2373
|
-
*/
|
|
2374
1719
|
const cmdLine = command + ' ' + args.join(' ');
|
|
2375
1720
|
if (process.platform === 'win32' && command === 'npm') {
|
|
2376
|
-
// Must be spawn('cmd.exe', ['/c', 'npm -g install <package>']);
|
|
2377
1721
|
const argstring = 'npm ' + args.join(' ');
|
|
2378
1722
|
args.splice(0, args.length, '/c', argstring);
|
|
2379
1723
|
command = 'cmd.exe';
|
|
2380
1724
|
}
|
|
2381
|
-
// Decide when using sudo on linux
|
|
2382
|
-
// When you need sudo: Spawn stderr: npm error Error: EACCES: permission denied
|
|
2383
|
-
// When you don't need sudo: Failed to start child process "npm install -g matterbridge-eve-door": spawn sudo ENOENT
|
|
2384
1725
|
if (hasParameter('sudo') || (process.platform === 'linux' && command === 'npm' && !hasParameter('docker') && !hasParameter('nosudo'))) {
|
|
2385
1726
|
args.unshift(command);
|
|
2386
1727
|
command = 'sudo';
|
|
@@ -2439,4 +1780,3 @@ export class Matterbridge extends EventEmitter {
|
|
|
2439
1780
|
});
|
|
2440
1781
|
}
|
|
2441
1782
|
}
|
|
2442
|
-
//# sourceMappingURL=matterbridge.js.map
|