matterbridge 3.1.4 → 3.1.5-dev-20250718-054cd80
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +20 -0
- package/README-DEV.md +59 -0
- package/dist/cli.js +2 -91
- package/dist/cliEmitter.js +0 -30
- package/dist/clusters/export.js +0 -2
- package/dist/defaultConfigSchema.js +0 -24
- package/dist/deviceManager.js +1 -94
- package/dist/devices/batteryStorage.js +1 -48
- package/dist/devices/evse.js +10 -74
- package/dist/devices/export.js +0 -2
- package/dist/devices/heatPump.js +2 -50
- package/dist/devices/laundryDryer.js +6 -83
- package/dist/devices/laundryWasher.js +7 -91
- package/dist/devices/roboticVacuumCleaner.js +6 -89
- package/dist/devices/solarPower.js +0 -38
- package/dist/devices/waterHeater.js +2 -82
- package/dist/frontend.js +21 -429
- package/dist/globalMatterbridge.js +0 -47
- package/dist/helpers.js +0 -53
- package/dist/index.js +1 -30
- 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 -3
- package/dist/matter/types.js +0 -3
- package/dist/matterbridge.js +66 -802
- package/dist/matterbridgeAccessoryPlatform.js +0 -36
- package/dist/matterbridgeBehaviors.js +1 -61
- package/dist/matterbridgeDeviceTypes.js +15 -579
- package/dist/matterbridgeDynamicPlatform.js +0 -36
- package/dist/matterbridgeEndpoint.js +42 -1106
- package/dist/matterbridgeEndpointHelpers.js +12 -322
- package/dist/matterbridgePlatform.js +0 -233
- package/dist/matterbridgeTypes.js +0 -25
- package/dist/pluginManager.js +46 -350
- package/dist/shelly.js +7 -168
- package/dist/storage/export.js +0 -1
- package/dist/update.js +0 -54
- package/dist/utils/colorUtils.js +2 -263
- package/dist/utils/commandLine.js +0 -54
- package/dist/utils/copyDirectory.js +1 -38
- package/dist/utils/createDirectory.js +0 -33
- package/dist/utils/createZip.js +2 -47
- package/dist/utils/deepCopy.js +0 -39
- package/dist/utils/deepEqual.js +1 -72
- package/dist/utils/error.js +9 -0
- package/dist/utils/export.js +0 -1
- package/dist/utils/hex.js +0 -58
- package/dist/utils/isvalid.js +0 -101
- package/dist/utils/network.js +5 -81
- package/dist/utils/spawn.js +0 -18
- package/dist/utils/wait.js +9 -62
- package/npm-shrinkwrap.json +5 -5
- package/package.json +1 -2
- package/dist/cli.d.ts +0 -26
- package/dist/cli.d.ts.map +0 -1
- package/dist/cli.js.map +0 -1
- package/dist/cliEmitter.d.ts +0 -34
- package/dist/cliEmitter.d.ts.map +0 -1
- package/dist/cliEmitter.js.map +0 -1
- package/dist/clusters/export.d.ts +0 -2
- package/dist/clusters/export.d.ts.map +0 -1
- package/dist/clusters/export.js.map +0 -1
- package/dist/defaultConfigSchema.d.ts +0 -28
- package/dist/defaultConfigSchema.d.ts.map +0 -1
- package/dist/defaultConfigSchema.js.map +0 -1
- package/dist/deviceManager.d.ts +0 -112
- package/dist/deviceManager.d.ts.map +0 -1
- package/dist/deviceManager.js.map +0 -1
- package/dist/devices/batteryStorage.d.ts +0 -48
- package/dist/devices/batteryStorage.d.ts.map +0 -1
- package/dist/devices/batteryStorage.js.map +0 -1
- package/dist/devices/evse.d.ts +0 -75
- package/dist/devices/evse.d.ts.map +0 -1
- package/dist/devices/evse.js.map +0 -1
- package/dist/devices/export.d.ts +0 -9
- package/dist/devices/export.d.ts.map +0 -1
- package/dist/devices/export.js.map +0 -1
- package/dist/devices/heatPump.d.ts +0 -47
- package/dist/devices/heatPump.d.ts.map +0 -1
- package/dist/devices/heatPump.js.map +0 -1
- package/dist/devices/laundryDryer.d.ts +0 -87
- package/dist/devices/laundryDryer.d.ts.map +0 -1
- package/dist/devices/laundryDryer.js.map +0 -1
- package/dist/devices/laundryWasher.d.ts +0 -242
- package/dist/devices/laundryWasher.d.ts.map +0 -1
- package/dist/devices/laundryWasher.js.map +0 -1
- package/dist/devices/roboticVacuumCleaner.d.ts +0 -110
- package/dist/devices/roboticVacuumCleaner.d.ts.map +0 -1
- package/dist/devices/roboticVacuumCleaner.js.map +0 -1
- package/dist/devices/solarPower.d.ts +0 -40
- package/dist/devices/solarPower.d.ts.map +0 -1
- package/dist/devices/solarPower.js.map +0 -1
- package/dist/devices/waterHeater.d.ts +0 -111
- package/dist/devices/waterHeater.d.ts.map +0 -1
- package/dist/devices/waterHeater.js.map +0 -1
- package/dist/frontend.d.ts +0 -304
- package/dist/frontend.d.ts.map +0 -1
- package/dist/frontend.js.map +0 -1
- package/dist/globalMatterbridge.d.ts +0 -59
- package/dist/globalMatterbridge.d.ts.map +0 -1
- package/dist/globalMatterbridge.js.map +0 -1
- package/dist/helpers.d.ts +0 -48
- package/dist/helpers.d.ts.map +0 -1
- package/dist/helpers.js.map +0 -1
- package/dist/index.d.ts +0 -33
- 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 -444
- package/dist/matterbridge.d.ts.map +0 -1
- package/dist/matterbridge.js.map +0 -1
- package/dist/matterbridgeAccessoryPlatform.d.ts +0 -42
- package/dist/matterbridgeAccessoryPlatform.d.ts.map +0 -1
- package/dist/matterbridgeAccessoryPlatform.js.map +0 -1
- package/dist/matterbridgeBehaviors.d.ts +0 -1340
- package/dist/matterbridgeBehaviors.d.ts.map +0 -1
- package/dist/matterbridgeBehaviors.js.map +0 -1
- package/dist/matterbridgeDeviceTypes.d.ts +0 -709
- package/dist/matterbridgeDeviceTypes.d.ts.map +0 -1
- package/dist/matterbridgeDeviceTypes.js.map +0 -1
- package/dist/matterbridgeDynamicPlatform.d.ts +0 -42
- package/dist/matterbridgeDynamicPlatform.d.ts.map +0 -1
- package/dist/matterbridgeDynamicPlatform.js.map +0 -1
- package/dist/matterbridgeEndpoint.d.ts +0 -1250
- package/dist/matterbridgeEndpoint.d.ts.map +0 -1
- package/dist/matterbridgeEndpoint.js.map +0 -1
- package/dist/matterbridgeEndpointHelpers.d.ts +0 -3198
- package/dist/matterbridgeEndpointHelpers.d.ts.map +0 -1
- package/dist/matterbridgeEndpointHelpers.js.map +0 -1
- package/dist/matterbridgePlatform.d.ts +0 -310
- package/dist/matterbridgePlatform.d.ts.map +0 -1
- package/dist/matterbridgePlatform.js.map +0 -1
- package/dist/matterbridgeTypes.d.ts +0 -195
- package/dist/matterbridgeTypes.d.ts.map +0 -1
- package/dist/matterbridgeTypes.js.map +0 -1
- package/dist/pluginManager.d.ts +0 -291
- package/dist/pluginManager.d.ts.map +0 -1
- package/dist/pluginManager.js.map +0 -1
- package/dist/shelly.d.ts +0 -174
- 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 -59
- package/dist/update.d.ts.map +0 -1
- package/dist/update.js.map +0 -1
- package/dist/utils/colorUtils.d.ts +0 -117
- package/dist/utils/colorUtils.d.ts.map +0 -1
- package/dist/utils/colorUtils.js.map +0 -1
- package/dist/utils/commandLine.d.ts +0 -59
- package/dist/utils/commandLine.d.ts.map +0 -1
- package/dist/utils/commandLine.js.map +0 -1
- package/dist/utils/copyDirectory.d.ts +0 -33
- package/dist/utils/copyDirectory.d.ts.map +0 -1
- package/dist/utils/copyDirectory.js.map +0 -1
- package/dist/utils/createDirectory.d.ts +0 -34
- package/dist/utils/createDirectory.d.ts.map +0 -1
- package/dist/utils/createDirectory.js.map +0 -1
- package/dist/utils/createZip.d.ts +0 -39
- package/dist/utils/createZip.d.ts.map +0 -1
- package/dist/utils/createZip.js.map +0 -1
- package/dist/utils/deepCopy.d.ts +0 -32
- package/dist/utils/deepCopy.d.ts.map +0 -1
- package/dist/utils/deepCopy.js.map +0 -1
- package/dist/utils/deepEqual.d.ts +0 -54
- package/dist/utils/deepEqual.d.ts.map +0 -1
- package/dist/utils/deepEqual.js.map +0 -1
- package/dist/utils/export.d.ts +0 -12
- package/dist/utils/export.d.ts.map +0 -1
- package/dist/utils/export.js.map +0 -1
- package/dist/utils/hex.d.ts +0 -49
- package/dist/utils/hex.d.ts.map +0 -1
- package/dist/utils/hex.js.map +0 -1
- package/dist/utils/isvalid.d.ts +0 -103
- package/dist/utils/isvalid.d.ts.map +0 -1
- package/dist/utils/isvalid.js.map +0 -1
- package/dist/utils/network.d.ts +0 -74
- package/dist/utils/network.d.ts.map +0 -1
- package/dist/utils/network.js.map +0 -1
- package/dist/utils/spawn.d.ts +0 -11
- package/dist/utils/spawn.d.ts.map +0 -1
- package/dist/utils/spawn.js.map +0 -1
- package/dist/utils/wait.d.ts +0 -56
- package/dist/utils/wait.d.ts.map +0 -1
- package/dist/utils/wait.js.map +0 -1
package/dist/matterbridge.js
CHANGED
|
@@ -1,44 +1,16 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* This file contains the class Matterbridge.
|
|
3
|
-
*
|
|
4
|
-
* @file matterbridge.ts
|
|
5
|
-
* @author Luca Liguori
|
|
6
|
-
* @created 2023-12-29
|
|
7
|
-
* @version 1.6.0
|
|
8
|
-
* @license Apache-2.0
|
|
9
|
-
*
|
|
10
|
-
* Copyright 2023, 2024, 2025 Luca Liguori.
|
|
11
|
-
*
|
|
12
|
-
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
13
|
-
* you may not use this file except in compliance with the License.
|
|
14
|
-
* You may obtain a copy of the License at
|
|
15
|
-
*
|
|
16
|
-
* http://www.apache.org/licenses/LICENSE-2.0
|
|
17
|
-
*
|
|
18
|
-
* Unless required by applicable law or agreed to in writing, software
|
|
19
|
-
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
20
|
-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
21
|
-
* See the License for the specific language governing permissions and
|
|
22
|
-
* limitations under the License.
|
|
23
|
-
*/
|
|
24
|
-
// Node.js modules
|
|
25
1
|
import os from 'node:os';
|
|
26
2
|
import path from 'node:path';
|
|
27
3
|
import { promises as fs } from 'node:fs';
|
|
28
4
|
import EventEmitter from 'node:events';
|
|
29
5
|
import { inspect } from 'node:util';
|
|
30
|
-
// AnsiLogger module
|
|
31
6
|
import { AnsiLogger, UNDERLINE, UNDERLINEOFF, db, debugStringify, BRIGHT, RESET, er, nf, rs, wr, RED, GREEN, zb, CYAN, nt, BLUE } from 'node-ansi-logger';
|
|
32
|
-
// NodeStorage module
|
|
33
7
|
import { NodeStorageManager } from 'node-persist-manager';
|
|
34
|
-
// @matter
|
|
35
8
|
import { DeviceTypeId, Endpoint, Logger, LogLevel as MatterLogLevel, LogFormat as MatterLogFormat, VendorId, StorageService, Environment, ServerNode, UINT32_MAX, UINT16_MAX, Crypto, } from '@matter/main';
|
|
36
9
|
import { DeviceCommissioner, FabricAction, MdnsService, PaseClient } from '@matter/main/protocol';
|
|
37
10
|
import { AggregatorEndpoint } from '@matter/main/endpoints';
|
|
38
11
|
import { BasicInformationServer } from '@matter/main/behaviors/basic-information';
|
|
39
12
|
import { BridgedDeviceBasicInformationServer } from '@matter/main/behaviors/bridged-device-basic-information';
|
|
40
|
-
|
|
41
|
-
import { getParameter, getIntParameter, hasParameter, copyDirectory, withTimeout, waiter, isValidString, parseVersionString, isValidNumber, createDirectory } from './utils/export.js';
|
|
13
|
+
import { getParameter, getIntParameter, hasParameter, copyDirectory, withTimeout, waiter, isValidString, parseVersionString, isValidNumber, createDirectory, wait } from './utils/export.js';
|
|
42
14
|
import { dev, plg, typ } from './matterbridgeTypes.js';
|
|
43
15
|
import { PluginManager } from './pluginManager.js';
|
|
44
16
|
import { DeviceManager } from './deviceManager.js';
|
|
@@ -46,9 +18,6 @@ import { MatterbridgeEndpoint } from './matterbridgeEndpoint.js';
|
|
|
46
18
|
import { bridge } from './matterbridgeDeviceTypes.js';
|
|
47
19
|
import { Frontend } from './frontend.js';
|
|
48
20
|
import { addVirtualDevices } from './helpers.js';
|
|
49
|
-
/**
|
|
50
|
-
* Represents the Matterbridge application.
|
|
51
|
-
*/
|
|
52
21
|
export class Matterbridge extends EventEmitter {
|
|
53
22
|
systemInformation = {
|
|
54
23
|
interfaceName: '',
|
|
@@ -97,7 +66,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
97
66
|
shellySysUpdate: false,
|
|
98
67
|
shellyMainUpdate: false,
|
|
99
68
|
profile: getParameter('profile'),
|
|
100
|
-
loggerLevel: "info"
|
|
69
|
+
loggerLevel: "info",
|
|
101
70
|
fileLogger: false,
|
|
102
71
|
matterLoggerLevel: MatterLogLevel.INFO,
|
|
103
72
|
matterFileLogger: false,
|
|
@@ -125,18 +94,15 @@ export class Matterbridge extends EventEmitter {
|
|
|
125
94
|
shutdown = false;
|
|
126
95
|
edge = true;
|
|
127
96
|
failCountLimit = hasParameter('shelly') ? 600 : 120;
|
|
128
|
-
|
|
129
|
-
log = new AnsiLogger({ logName: 'Matterbridge', logTimestampFormat: 4 /* TimestampFormat.TIME_MILLIS */, logLevel: hasParameter('debug') ? "debug" /* LogLevel.DEBUG */ : "info" /* LogLevel.INFO */ });
|
|
97
|
+
log = new AnsiLogger({ logName: 'Matterbridge', logTimestampFormat: 4, logLevel: hasParameter('debug') ? "debug" : "info" });
|
|
130
98
|
matterbridgeLoggerFile = 'matterbridge' + (getParameter('profile') ? '.' + getParameter('profile') : '') + '.log';
|
|
131
99
|
matterLoggerFile = 'matter' + (getParameter('profile') ? '.' + getParameter('profile') : '') + '.log';
|
|
132
|
-
plugins;
|
|
133
|
-
devices;
|
|
100
|
+
plugins = new PluginManager(this);
|
|
101
|
+
devices = new DeviceManager(this);
|
|
134
102
|
frontend = new Frontend(this);
|
|
135
|
-
// Matterbridge storage
|
|
136
103
|
nodeStorageName = 'storage' + (getParameter('profile') ? '.' + getParameter('profile') : '');
|
|
137
104
|
nodeStorage;
|
|
138
105
|
nodeContext;
|
|
139
|
-
// Cleanup
|
|
140
106
|
hasCleanupStarted = false;
|
|
141
107
|
initialized = false;
|
|
142
108
|
execRunningCount = 0;
|
|
@@ -150,23 +116,19 @@ export class Matterbridge extends EventEmitter {
|
|
|
150
116
|
sigtermHandler;
|
|
151
117
|
exceptionHandler;
|
|
152
118
|
rejectionHandler;
|
|
153
|
-
// Matter environment
|
|
154
119
|
environment = Environment.default;
|
|
155
|
-
// Matter storage
|
|
156
120
|
matterStorageName = 'matterstorage' + (getParameter('profile') ? '.' + getParameter('profile') : '');
|
|
157
121
|
matterStorageService;
|
|
158
122
|
matterStorageManager;
|
|
159
123
|
matterbridgeContext;
|
|
160
124
|
controllerContext;
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
certification; // device certification
|
|
169
|
-
// Matter nodes
|
|
125
|
+
mdnsInterface;
|
|
126
|
+
ipv4address;
|
|
127
|
+
ipv6address;
|
|
128
|
+
port;
|
|
129
|
+
passcode;
|
|
130
|
+
discriminator;
|
|
131
|
+
certification;
|
|
170
132
|
serverNode;
|
|
171
133
|
aggregatorNode;
|
|
172
134
|
aggregatorVendorId = VendorId(getIntParameter('vendorId') ?? 0xfff1);
|
|
@@ -174,31 +136,15 @@ export class Matterbridge extends EventEmitter {
|
|
|
174
136
|
aggregatorProductId = getIntParameter('productId') ?? 0x8000;
|
|
175
137
|
aggregatorProductName = getParameter('productName') ?? 'Matterbridge aggregator';
|
|
176
138
|
static instance;
|
|
177
|
-
// We load asyncronously so is private
|
|
178
139
|
constructor() {
|
|
179
140
|
super();
|
|
180
141
|
}
|
|
181
|
-
/**
|
|
182
|
-
* Retrieves the list of Matterbridge devices.
|
|
183
|
-
*
|
|
184
|
-
* @returns {MatterbridgeEndpoint[]} An array of MatterbridgeDevice objects.
|
|
185
|
-
*/
|
|
186
142
|
getDevices() {
|
|
187
143
|
return this.devices.array();
|
|
188
144
|
}
|
|
189
|
-
/**
|
|
190
|
-
* Retrieves the list of registered plugins.
|
|
191
|
-
*
|
|
192
|
-
* @returns {RegisteredPlugin[]} An array of RegisteredPlugin objects.
|
|
193
|
-
*/
|
|
194
145
|
getPlugins() {
|
|
195
146
|
return this.plugins.array();
|
|
196
147
|
}
|
|
197
|
-
/**
|
|
198
|
-
* Set the logger logLevel for the Matterbridge classes and call onChangeLoggerLevel() for each plugin.
|
|
199
|
-
*
|
|
200
|
-
* @param {LogLevel} logLevel The logger logLevel to set.
|
|
201
|
-
*/
|
|
202
148
|
async setLogLevel(logLevel) {
|
|
203
149
|
if (this.log)
|
|
204
150
|
this.log.logLevel = logLevel;
|
|
@@ -212,31 +158,19 @@ export class Matterbridge extends EventEmitter {
|
|
|
212
158
|
for (const plugin of this.plugins) {
|
|
213
159
|
if (!plugin.platform || !plugin.platform.log || !plugin.platform.config)
|
|
214
160
|
continue;
|
|
215
|
-
plugin.platform.log.logLevel = plugin.platform.config.debug === true ? "debug"
|
|
216
|
-
await plugin.platform.onChangeLoggerLevel(plugin.platform.config.debug === true ? "debug"
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
callbackLogLevel = "debug" /* LogLevel.DEBUG */;
|
|
161
|
+
plugin.platform.log.logLevel = plugin.platform.config.debug === true ? "debug" : this.log.logLevel;
|
|
162
|
+
await plugin.platform.onChangeLoggerLevel(plugin.platform.config.debug === true ? "debug" : this.log.logLevel);
|
|
163
|
+
}
|
|
164
|
+
let callbackLogLevel = "notice";
|
|
165
|
+
if (this.matterbridgeInformation.loggerLevel === "info" || this.matterbridgeInformation.matterLoggerLevel === MatterLogLevel.INFO)
|
|
166
|
+
callbackLogLevel = "info";
|
|
167
|
+
if (this.matterbridgeInformation.loggerLevel === "debug" || this.matterbridgeInformation.matterLoggerLevel === MatterLogLevel.DEBUG)
|
|
168
|
+
callbackLogLevel = "debug";
|
|
224
169
|
AnsiLogger.setGlobalCallback(this.frontend.wssSendMessage.bind(this.frontend), callbackLogLevel);
|
|
225
170
|
this.log.debug(`WebSocketServer logger global callback set to ${callbackLogLevel}`);
|
|
226
171
|
}
|
|
227
|
-
//* ************************************************************************************************************************************ */
|
|
228
|
-
// loadInstance() and cleanup() methods */
|
|
229
|
-
//* ************************************************************************************************************************************ */
|
|
230
|
-
/**
|
|
231
|
-
* Loads an instance of the Matterbridge class.
|
|
232
|
-
* If an instance already exists, return that instance.
|
|
233
|
-
*
|
|
234
|
-
* @param {boolean} initialize - Whether to initialize the Matterbridge instance after loading. Defaults to false.
|
|
235
|
-
* @returns {Matterbridge} A promise that resolves to the Matterbridge instance.
|
|
236
|
-
*/
|
|
237
172
|
static async loadInstance(initialize = false) {
|
|
238
173
|
if (!Matterbridge.instance) {
|
|
239
|
-
// eslint-disable-next-line no-console
|
|
240
174
|
if (hasParameter('debug'))
|
|
241
175
|
console.log(GREEN + 'Creating a new instance of Matterbridge.', initialize ? 'Initializing...' : 'Not initializing...', rs);
|
|
242
176
|
Matterbridge.instance = new Matterbridge();
|
|
@@ -245,17 +179,8 @@ export class Matterbridge extends EventEmitter {
|
|
|
245
179
|
}
|
|
246
180
|
return Matterbridge.instance;
|
|
247
181
|
}
|
|
248
|
-
/**
|
|
249
|
-
* Call cleanup() and dispose MdnsService.
|
|
250
|
-
*
|
|
251
|
-
* @param {number} [timeout] - The timeout duration to wait for the cleanup to complete in milliseconds. Default is 1000.
|
|
252
|
-
* @param {number} [pause] - The pause duration after the cleanup in milliseconds. Default is 250.
|
|
253
|
-
*
|
|
254
|
-
* @deprecated This method is deprecated and is ONLY used for jest tests.
|
|
255
|
-
*/
|
|
256
182
|
async destroyInstance(timeout = 1000, pause = 250) {
|
|
257
183
|
this.log.info(`Destroy instance...`);
|
|
258
|
-
// Save server nodes to close
|
|
259
184
|
const servers = [];
|
|
260
185
|
if (this.bridgeMode === 'bridge') {
|
|
261
186
|
if (this.serverNode)
|
|
@@ -273,109 +198,76 @@ export class Matterbridge extends EventEmitter {
|
|
|
273
198
|
servers.push(device.serverNode);
|
|
274
199
|
}
|
|
275
200
|
}
|
|
276
|
-
// Let any already‐queued microtasks run first
|
|
277
201
|
await Promise.resolve();
|
|
278
|
-
// Wait for the cleanup to finish
|
|
279
202
|
await new Promise((resolve) => {
|
|
280
203
|
setTimeout(resolve, pause);
|
|
281
204
|
});
|
|
282
|
-
// Cleanup
|
|
283
205
|
await this.cleanup('destroying instance...', false, timeout);
|
|
284
|
-
// Close servers mdns service
|
|
285
206
|
this.log.info(`Dispose ${servers.length} MdnsService...`);
|
|
286
207
|
for (const server of servers) {
|
|
287
208
|
await server.env.get(MdnsService)[Symbol.asyncDispose]();
|
|
288
209
|
this.log.info(`Closed ${server.id} MdnsService`);
|
|
289
210
|
}
|
|
290
|
-
// Let any already‐queued microtasks run first
|
|
291
211
|
await Promise.resolve();
|
|
292
|
-
// Wait for the cleanup to finish
|
|
293
212
|
await new Promise((resolve) => {
|
|
294
213
|
setTimeout(resolve, pause);
|
|
295
214
|
});
|
|
296
215
|
}
|
|
297
|
-
/**
|
|
298
|
-
* Initializes the Matterbridge application.
|
|
299
|
-
*
|
|
300
|
-
* @remarks
|
|
301
|
-
* This method performs the necessary setup and initialization steps for the Matterbridge application.
|
|
302
|
-
* It displays the help information if the 'help' parameter is provided, sets up the logger, checks the
|
|
303
|
-
* node version, registers signal handlers, initializes storage, and parses the command line.
|
|
304
|
-
*
|
|
305
|
-
* @returns {Promise<void>} A Promise that resolves when the initialization is complete.
|
|
306
|
-
*/
|
|
307
216
|
async initialize() {
|
|
308
|
-
// Emit the initialize_started event
|
|
309
217
|
this.emit('initialize_started');
|
|
310
|
-
// Set the restart mode
|
|
311
218
|
if (hasParameter('service'))
|
|
312
219
|
this.restartMode = 'service';
|
|
313
220
|
if (hasParameter('docker'))
|
|
314
221
|
this.restartMode = 'docker';
|
|
315
|
-
// Set the matterbridge home directory
|
|
316
222
|
this.homeDirectory = getParameter('homedir') ?? os.homedir();
|
|
317
223
|
this.matterbridgeInformation.homeDirectory = this.homeDirectory;
|
|
318
224
|
await createDirectory(this.homeDirectory, 'Matterbridge Home Directory', this.log);
|
|
319
|
-
// Set the matterbridge directory
|
|
320
225
|
this.matterbridgeDirectory = path.join(this.homeDirectory, '.matterbridge');
|
|
321
226
|
this.matterbridgeInformation.matterbridgeDirectory = this.matterbridgeDirectory;
|
|
322
227
|
await createDirectory(this.matterbridgeDirectory, 'Matterbridge Directory', this.log);
|
|
323
228
|
await createDirectory(path.join(this.matterbridgeDirectory, 'certs'), 'Matterbridge Frontend Certificate Directory', this.log);
|
|
324
229
|
await createDirectory(path.join(this.matterbridgeDirectory, 'uploads'), 'Matterbridge Frontend Uploads Directory', this.log);
|
|
325
|
-
// Set the matterbridge plugin directory
|
|
326
230
|
this.matterbridgePluginDirectory = path.join(this.homeDirectory, 'Matterbridge');
|
|
327
231
|
this.matterbridgeInformation.matterbridgePluginDirectory = this.matterbridgePluginDirectory;
|
|
328
232
|
await createDirectory(this.matterbridgePluginDirectory, 'Matterbridge Plugin Directory', this.log);
|
|
329
|
-
// Set the matterbridge cert directory
|
|
330
233
|
this.matterbridgeCertDirectory = path.join(this.homeDirectory, '.mattercert');
|
|
331
234
|
this.matterbridgeInformation.matterbridgeCertDirectory = this.matterbridgeCertDirectory;
|
|
332
235
|
await createDirectory(this.matterbridgeCertDirectory, 'Matterbridge Matter Certificate Directory', this.log);
|
|
333
|
-
// Set the matterbridge root directory
|
|
334
236
|
const { fileURLToPath } = await import('node:url');
|
|
335
237
|
const currentFileDirectory = path.dirname(fileURLToPath(import.meta.url));
|
|
336
238
|
this.rootDirectory = path.resolve(currentFileDirectory, '../');
|
|
337
239
|
this.matterbridgeInformation.rootDirectory = this.rootDirectory;
|
|
338
|
-
// Setup the matter environment
|
|
339
240
|
this.environment.vars.set('log.level', MatterLogLevel.INFO);
|
|
340
241
|
this.environment.vars.set('log.format', MatterLogFormat.ANSI);
|
|
341
242
|
this.environment.vars.set('path.root', path.join(this.matterbridgeDirectory, this.matterStorageName));
|
|
342
243
|
this.environment.vars.set('runtime.signals', false);
|
|
343
244
|
this.environment.vars.set('runtime.exitcode', false);
|
|
344
|
-
// Register process handlers
|
|
345
245
|
this.registerProcessHandlers();
|
|
346
|
-
// Initialize nodeStorage and nodeContext
|
|
347
246
|
try {
|
|
348
247
|
this.log.debug(`Creating node storage manager: ${CYAN}${this.nodeStorageName}${db}`);
|
|
349
248
|
this.nodeStorage = new NodeStorageManager({ dir: path.join(this.matterbridgeDirectory, this.nodeStorageName), writeQueue: false, expiredInterval: undefined, logging: false });
|
|
350
249
|
this.log.debug('Creating node storage context for matterbridge');
|
|
351
250
|
this.nodeContext = await this.nodeStorage.createStorage('matterbridge');
|
|
352
|
-
// TODO: Remove this code when node-persist-manager is updated
|
|
353
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
354
251
|
const keys = (await this.nodeStorage?.storage.keys());
|
|
355
252
|
for (const key of keys) {
|
|
356
253
|
this.log.debug(`Checking node storage manager key: ${CYAN}${key}${db}`);
|
|
357
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
358
254
|
await this.nodeStorage?.storage.get(key);
|
|
359
255
|
}
|
|
360
256
|
const storages = await this.nodeStorage.getStorageNames();
|
|
361
257
|
for (const storage of storages) {
|
|
362
258
|
this.log.debug(`Checking storage: ${CYAN}${storage}${db}`);
|
|
363
259
|
const nodeContext = await this.nodeStorage?.createStorage(storage);
|
|
364
|
-
// TODO: Remove this code when node-persist-manager is updated
|
|
365
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
366
260
|
const keys = (await nodeContext?.storage.keys());
|
|
367
261
|
keys.forEach(async (key) => {
|
|
368
262
|
this.log.debug(`Checking key: ${CYAN}${storage}:${key}${db}`);
|
|
369
263
|
await nodeContext?.get(key);
|
|
370
264
|
});
|
|
371
265
|
}
|
|
372
|
-
// Creating a backup of the node storage since it is not corrupted
|
|
373
266
|
this.log.debug('Creating node storage backup...');
|
|
374
267
|
await copyDirectory(path.join(this.matterbridgeDirectory, this.nodeStorageName), path.join(this.matterbridgeDirectory, this.nodeStorageName + '.backup'));
|
|
375
268
|
this.log.debug('Created node storage backup');
|
|
376
269
|
}
|
|
377
270
|
catch (error) {
|
|
378
|
-
// Restoring the backup of the node storage since it is corrupted
|
|
379
271
|
this.log.error(`Error creating node storage manager and context: ${error instanceof Error ? error.message : error}`);
|
|
380
272
|
if (hasParameter('norestore')) {
|
|
381
273
|
this.log.fatal(`The matterbridge storage is corrupted. Found -norestore parameter: exiting...`);
|
|
@@ -389,20 +281,14 @@ export class Matterbridge extends EventEmitter {
|
|
|
389
281
|
if (!this.nodeStorage || !this.nodeContext) {
|
|
390
282
|
throw new Error('Fatal error creating node storage manager and context for matterbridge');
|
|
391
283
|
}
|
|
392
|
-
// Set the first port to use for the commissioning server (will be incremented in childbridge mode)
|
|
393
284
|
this.port = getIntParameter('port') ?? (await this.nodeContext.get('matterport', 5540)) ?? 5540;
|
|
394
|
-
// Set the first passcode to use for the commissioning server (will be incremented in childbridge mode)
|
|
395
285
|
this.passcode = getIntParameter('passcode') ?? (await this.nodeContext.get('matterpasscode')) ?? PaseClient.generateRandomPasscode(this.environment.get(Crypto));
|
|
396
|
-
// Set the first discriminator to use for the commissioning server (will be incremented in childbridge mode)
|
|
397
286
|
this.discriminator = getIntParameter('discriminator') ?? (await this.nodeContext.get('matterdiscriminator')) ?? PaseClient.generateRandomDiscriminator(this.environment.get(Crypto));
|
|
398
|
-
// Certificate management
|
|
399
287
|
const pairingFilePath = path.join(this.matterbridgeCertDirectory, 'pairing.json');
|
|
400
288
|
try {
|
|
401
|
-
// eslint-disable-next-line n/no-unsupported-features/node-builtins
|
|
402
289
|
await fs.access(pairingFilePath, fs.constants.R_OK);
|
|
403
290
|
const pairingFileContent = await fs.readFile(pairingFilePath, 'utf8');
|
|
404
291
|
const pairingFileJson = JSON.parse(pairingFileContent);
|
|
405
|
-
// Set the vendorId, vendorName, productId and productName if they are present in the pairing file
|
|
406
292
|
if (isValidNumber(pairingFileJson.vendorId))
|
|
407
293
|
this.aggregatorVendorId = VendorId(pairingFileJson.vendorId);
|
|
408
294
|
if (isValidString(pairingFileJson.vendorName, 3))
|
|
@@ -411,14 +297,11 @@ export class Matterbridge extends EventEmitter {
|
|
|
411
297
|
this.aggregatorProductId = pairingFileJson.productId;
|
|
412
298
|
if (isValidString(pairingFileJson.productName, 3))
|
|
413
299
|
this.aggregatorProductName = pairingFileJson.productName;
|
|
414
|
-
// Override the passcode and discriminator if they are present in the pairing file
|
|
415
300
|
if (isValidNumber(pairingFileJson.passcode) && isValidNumber(pairingFileJson.discriminator)) {
|
|
416
301
|
this.passcode = pairingFileJson.passcode;
|
|
417
302
|
this.discriminator = pairingFileJson.discriminator;
|
|
418
303
|
this.log.info(`Pairing file ${CYAN}${pairingFilePath}${nf} found. Using passcode ${CYAN}${this.passcode}${nf} and discriminator ${CYAN}${this.discriminator}${nf} from pairing file.`);
|
|
419
304
|
}
|
|
420
|
-
// Set the certification if it is present in the pairing file
|
|
421
|
-
/* istanbul ignore next if */
|
|
422
305
|
if (pairingFileJson.privateKey && pairingFileJson.certificate && pairingFileJson.intermediateCertificate && pairingFileJson.declaration) {
|
|
423
306
|
const hexStringToUint8Array = (hexString) => {
|
|
424
307
|
const matches = hexString.match(/.{1,2}/g);
|
|
@@ -436,44 +319,41 @@ export class Matterbridge extends EventEmitter {
|
|
|
436
319
|
catch (error) {
|
|
437
320
|
this.log.debug(`Pairing file ${CYAN}${pairingFilePath}${db} not found: ${error instanceof Error ? error.message : error}`);
|
|
438
321
|
}
|
|
439
|
-
// Store the passcode, discriminator and port in the node context
|
|
440
322
|
await this.nodeContext.set('matterport', this.port);
|
|
441
323
|
await this.nodeContext.set('matterpasscode', this.passcode);
|
|
442
324
|
await this.nodeContext.set('matterdiscriminator', this.discriminator);
|
|
443
325
|
this.log.debug(`Initializing server node for Matterbridge on port ${this.port} with passcode ${this.passcode} and discriminator ${this.discriminator}`);
|
|
444
|
-
// Set matterbridge logger level (context: matterbridgeLogLevel)
|
|
445
326
|
if (hasParameter('logger')) {
|
|
446
327
|
const level = getParameter('logger');
|
|
447
328
|
if (level === 'debug') {
|
|
448
|
-
this.log.logLevel = "debug"
|
|
329
|
+
this.log.logLevel = "debug";
|
|
449
330
|
}
|
|
450
331
|
else if (level === 'info') {
|
|
451
|
-
this.log.logLevel = "info"
|
|
332
|
+
this.log.logLevel = "info";
|
|
452
333
|
}
|
|
453
334
|
else if (level === 'notice') {
|
|
454
|
-
this.log.logLevel = "notice"
|
|
335
|
+
this.log.logLevel = "notice";
|
|
455
336
|
}
|
|
456
337
|
else if (level === 'warn') {
|
|
457
|
-
this.log.logLevel = "warn"
|
|
338
|
+
this.log.logLevel = "warn";
|
|
458
339
|
}
|
|
459
340
|
else if (level === 'error') {
|
|
460
|
-
this.log.logLevel = "error"
|
|
341
|
+
this.log.logLevel = "error";
|
|
461
342
|
}
|
|
462
343
|
else if (level === 'fatal') {
|
|
463
|
-
this.log.logLevel = "fatal"
|
|
344
|
+
this.log.logLevel = "fatal";
|
|
464
345
|
}
|
|
465
346
|
else {
|
|
466
347
|
this.log.warn(`Invalid matterbridge logger level: ${level}. Using default level "info".`);
|
|
467
|
-
this.log.logLevel = "info"
|
|
348
|
+
this.log.logLevel = "info";
|
|
468
349
|
}
|
|
469
350
|
}
|
|
470
351
|
else {
|
|
471
|
-
this.log.logLevel = await this.nodeContext.get('matterbridgeLogLevel', this.matterbridgeInformation.shellyBoard ? "notice"
|
|
352
|
+
this.log.logLevel = await this.nodeContext.get('matterbridgeLogLevel', this.matterbridgeInformation.shellyBoard ? "notice" : "info");
|
|
472
353
|
}
|
|
473
354
|
this.frontend.logLevel = this.log.logLevel;
|
|
474
355
|
MatterbridgeEndpoint.logLevel = this.log.logLevel;
|
|
475
356
|
this.matterbridgeInformation.loggerLevel = this.log.logLevel;
|
|
476
|
-
// Create the file logger for matterbridge (context: matterbridgeFileLog)
|
|
477
357
|
if (hasParameter('filelogger') || (await this.nodeContext.get('matterbridgeFileLog', false))) {
|
|
478
358
|
AnsiLogger.setGlobalLogfile(path.join(this.matterbridgeDirectory, this.matterbridgeLoggerFile), this.log.logLevel, true);
|
|
479
359
|
this.matterbridgeInformation.fileLogger = true;
|
|
@@ -482,7 +362,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
482
362
|
this.log.debug(`Matterbridge logLevel: ${this.log.logLevel} fileLoger: ${this.matterbridgeInformation.fileLogger}.`);
|
|
483
363
|
if (this.profile !== undefined)
|
|
484
364
|
this.log.debug(`Matterbridge profile: ${this.profile}.`);
|
|
485
|
-
// Set matter.js logger level, format and logger (context: matterLogLevel)
|
|
486
365
|
if (hasParameter('matterlogger')) {
|
|
487
366
|
const level = getParameter('matterlogger');
|
|
488
367
|
if (level === 'debug') {
|
|
@@ -513,9 +392,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
513
392
|
}
|
|
514
393
|
Logger.format = MatterLogFormat.ANSI;
|
|
515
394
|
Logger.setLogger('default', this.createMatterLogger());
|
|
516
|
-
// Logger.destinations.default.write = this.createMatterLogger();
|
|
517
395
|
this.matterbridgeInformation.matterLoggerLevel = Logger.level;
|
|
518
|
-
// Create the file logger for matter.js (context: matterFileLog)
|
|
519
396
|
if (hasParameter('matterfilelogger') || (await this.nodeContext.get('matterFileLog', false))) {
|
|
520
397
|
this.matterbridgeInformation.matterFileLogger = true;
|
|
521
398
|
Logger.addLogger('matterfilelogger', await this.createMatterFileLogger(path.join(this.matterbridgeDirectory, this.matterLoggerFile), true), {
|
|
@@ -524,9 +401,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
524
401
|
});
|
|
525
402
|
}
|
|
526
403
|
this.log.debug(`Matter logLevel: ${Logger.level} fileLoger: ${this.matterbridgeInformation.matterFileLogger}.`);
|
|
527
|
-
// Log network interfaces
|
|
528
404
|
const networkInterfaces = os.networkInterfaces();
|
|
529
|
-
// console.log(`Network interfaces:`, networkInterfaces);
|
|
530
405
|
const availableAddresses = Object.entries(networkInterfaces);
|
|
531
406
|
const availableInterfaces = Object.keys(networkInterfaces);
|
|
532
407
|
for (const [ifaceName, ifaces] of availableAddresses) {
|
|
@@ -538,7 +413,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
538
413
|
});
|
|
539
414
|
}
|
|
540
415
|
}
|
|
541
|
-
// Set the interface to use for matter server node mdnsInterface
|
|
542
416
|
if (hasParameter('mdnsinterface')) {
|
|
543
417
|
this.mdnsInterface = getParameter('mdnsinterface');
|
|
544
418
|
}
|
|
@@ -547,7 +421,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
547
421
|
if (this.mdnsInterface === '')
|
|
548
422
|
this.mdnsInterface = undefined;
|
|
549
423
|
}
|
|
550
|
-
// Validate mdnsInterface
|
|
551
424
|
if (this.mdnsInterface) {
|
|
552
425
|
if (!availableInterfaces.includes(this.mdnsInterface)) {
|
|
553
426
|
this.log.error(`Invalid mdnsinterface: ${this.mdnsInterface}. Available interfaces are: ${availableInterfaces.join(', ')}. Using all available interfaces.`);
|
|
@@ -560,7 +433,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
560
433
|
}
|
|
561
434
|
if (this.mdnsInterface)
|
|
562
435
|
this.environment.vars.set('mdns.networkInterface', this.mdnsInterface);
|
|
563
|
-
// Set the listeningAddressIpv4 for the matter commissioning server
|
|
564
436
|
if (hasParameter('ipv4address')) {
|
|
565
437
|
this.ipv4address = getParameter('ipv4address');
|
|
566
438
|
}
|
|
@@ -569,7 +441,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
569
441
|
if (this.ipv4address === '')
|
|
570
442
|
this.ipv4address = undefined;
|
|
571
443
|
}
|
|
572
|
-
// Validate ipv4address
|
|
573
444
|
if (this.ipv4address) {
|
|
574
445
|
let isValid = false;
|
|
575
446
|
for (const [ifaceName, ifaces] of availableAddresses) {
|
|
@@ -585,7 +456,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
585
456
|
await this.nodeContext.remove('matteripv4address');
|
|
586
457
|
}
|
|
587
458
|
}
|
|
588
|
-
// Set the listeningAddressIpv6 for the matter commissioning server
|
|
589
459
|
if (hasParameter('ipv6address')) {
|
|
590
460
|
this.ipv6address = getParameter('ipv6address');
|
|
591
461
|
}
|
|
@@ -594,7 +464,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
594
464
|
if (this.ipv6address === '')
|
|
595
465
|
this.ipv6address = undefined;
|
|
596
466
|
}
|
|
597
|
-
// Validate ipv6address
|
|
598
467
|
if (this.ipv6address) {
|
|
599
468
|
let isValid = false;
|
|
600
469
|
for (const [ifaceName, ifaces] of availableAddresses) {
|
|
@@ -603,7 +472,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
603
472
|
isValid = true;
|
|
604
473
|
break;
|
|
605
474
|
}
|
|
606
|
-
/* istanbul ignore next */
|
|
607
475
|
if (ifaces && ifaces.find((iface) => iface.scopeid && iface.scopeid > 0 && iface.address + '%' + (process.platform === 'win32' ? iface.scopeid : ifaceName) === this.ipv6address)) {
|
|
608
476
|
this.log.info(`Using ipv6address ${CYAN}${this.ipv6address}${nf} on interface ${CYAN}${ifaceName}${nf} for the Matter server node.`);
|
|
609
477
|
isValid = true;
|
|
@@ -616,7 +484,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
616
484
|
await this.nodeContext.remove('matteripv6address');
|
|
617
485
|
}
|
|
618
486
|
}
|
|
619
|
-
// Initialize the virtual mode
|
|
620
487
|
if (hasParameter('novirtual')) {
|
|
621
488
|
this.matterbridgeInformation.virtualMode = 'disabled';
|
|
622
489
|
await this.nodeContext.set('virtualmode', 'disabled');
|
|
@@ -625,19 +492,12 @@ export class Matterbridge extends EventEmitter {
|
|
|
625
492
|
this.matterbridgeInformation.virtualMode = (await this.nodeContext.get('virtualmode', 'outlet'));
|
|
626
493
|
}
|
|
627
494
|
this.log.debug(`Virtual mode ${this.matterbridgeInformation.virtualMode}.`);
|
|
628
|
-
// Initialize PluginManager
|
|
629
|
-
this.plugins = new PluginManager(this);
|
|
630
|
-
await this.plugins.loadFromStorage();
|
|
631
495
|
this.plugins.logLevel = this.log.logLevel;
|
|
632
|
-
|
|
633
|
-
this.devices = new DeviceManager(this);
|
|
496
|
+
await this.plugins.loadFromStorage();
|
|
634
497
|
this.devices.logLevel = this.log.logLevel;
|
|
635
|
-
// Get the plugins from node storage and create the plugins node storage contexts
|
|
636
498
|
for (const plugin of this.plugins) {
|
|
637
499
|
const packageJson = await this.plugins.parse(plugin);
|
|
638
500
|
if (packageJson === null && !hasParameter('add') && !hasParameter('remove') && !hasParameter('enable') && !hasParameter('disable') && !hasParameter('reset') && !hasParameter('factoryreset')) {
|
|
639
|
-
// Try to reinstall the plugin from npm (for Docker pull and external plugins)
|
|
640
|
-
// We don't do this when the add and other parameters are set because we shut down the process after adding the plugin
|
|
641
501
|
this.log.info(`Error parsing plugin ${plg}${plugin.name}${nf}. Trying to reinstall it from npm.`);
|
|
642
502
|
try {
|
|
643
503
|
const { spawnCommand } = await import('./utils/spawn.js');
|
|
@@ -660,7 +520,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
660
520
|
await plugin.nodeContext.set('description', plugin.description);
|
|
661
521
|
await plugin.nodeContext.set('author', plugin.author);
|
|
662
522
|
}
|
|
663
|
-
// Log system info and create .matterbridge directory
|
|
664
523
|
await this.logNodeAndSystemInfo();
|
|
665
524
|
this.log.notice(`Matterbridge version ${this.matterbridgeVersion} ` +
|
|
666
525
|
`${hasParameter('bridge') || (!hasParameter('childbridge') && (await this.nodeContext?.get('bridgeMode', '')) === 'bridge') ? 'mode bridge ' : ''}` +
|
|
@@ -668,7 +527,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
668
527
|
`${hasParameter('controller') ? 'mode controller ' : ''}` +
|
|
669
528
|
`${this.restartMode !== '' ? 'restart mode ' + this.restartMode + ' ' : ''}` +
|
|
670
529
|
`running on ${this.systemInformation.osType} (v.${this.systemInformation.osRelease}) platform ${this.systemInformation.osPlatform} arch ${this.systemInformation.osArch}`);
|
|
671
|
-
// Check node version and throw error
|
|
672
530
|
const minNodeVersion = 18;
|
|
673
531
|
const nodeVersion = process.versions.node;
|
|
674
532
|
const versionMajor = parseInt(nodeVersion.split('.')[0]);
|
|
@@ -676,18 +534,10 @@ export class Matterbridge extends EventEmitter {
|
|
|
676
534
|
this.log.error(`Node version ${versionMajor} is not supported. Please upgrade to ${minNodeVersion} or above.`);
|
|
677
535
|
throw new Error(`Node version ${versionMajor} is not supported. Please upgrade to ${minNodeVersion} or above.`);
|
|
678
536
|
}
|
|
679
|
-
// Parse command line
|
|
680
537
|
await this.parseCommandLine();
|
|
681
|
-
// Emit the initialize_completed event
|
|
682
538
|
this.emit('initialize_completed');
|
|
683
539
|
this.initialized = true;
|
|
684
540
|
}
|
|
685
|
-
/**
|
|
686
|
-
* Parses the command line arguments and performs the corresponding actions.
|
|
687
|
-
*
|
|
688
|
-
* @private
|
|
689
|
-
* @returns {Promise<void>} A promise that resolves when the command line arguments have been processed, or the process exits.
|
|
690
|
-
*/
|
|
691
541
|
async parseCommandLine() {
|
|
692
542
|
if (hasParameter('help')) {
|
|
693
543
|
this.log.info(`\nUsage: matterbridge [options]\n
|
|
@@ -713,7 +563,8 @@ export class Matterbridge extends EventEmitter {
|
|
|
713
563
|
- nosudo: force not to use sudo to install or update packages if the internal logic fails
|
|
714
564
|
- norestore: force not to automatically restore the matterbridge node storage and the matter storage from backup if it is corrupted
|
|
715
565
|
- novirtual: disable the creation of the virtual devices Restart, Update and Reboot Matterbridge
|
|
716
|
-
- ssl: enable SSL for the frontend and
|
|
566
|
+
- ssl: enable SSL for the frontend and the WebSocketServer (the server will use the certificates and switch to https)
|
|
567
|
+
- mtls: enable mTLS for the frontend and the WebSocketServer (both server and client will use and require the certificates and switch to https)
|
|
717
568
|
- vendorId: override the default vendorId 0xfff1
|
|
718
569
|
- vendorName: override the default vendorName "Matterbridge"
|
|
719
570
|
- productId: override the default productId 0x8000
|
|
@@ -748,19 +599,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
748
599
|
}
|
|
749
600
|
index++;
|
|
750
601
|
}
|
|
751
|
-
/*
|
|
752
|
-
const serializedRegisteredDevices = await this.nodeContext?.get<SerializedMatterbridgeEndpoint[]>('devices', []);
|
|
753
|
-
this.log.info(`│ Registered devices (${serializedRegisteredDevices?.length})`);
|
|
754
|
-
serializedRegisteredDevices?.forEach((device, index) => {
|
|
755
|
-
if (index !== serializedRegisteredDevices.length - 1) {
|
|
756
|
-
this.log.info(`├─┬─ plugin ${plg}${device.pluginName}${nf} device: ${dev}${device.deviceName}${nf} uniqueId: ${YELLOW}${device.uniqueId}${nf}`);
|
|
757
|
-
this.log.info(`│ └─ endpoint ${RED}${device.endpoint}${nf} ${typ}${device.endpointName}${nf} ${debugStringify(device.clusterServersId)}`);
|
|
758
|
-
} else {
|
|
759
|
-
this.log.info(`└─┬─ plugin ${plg}${device.pluginName}${nf} device: ${dev}${device.deviceName}${nf} uniqueId: ${YELLOW}${device.uniqueId}${nf}`);
|
|
760
|
-
this.log.info(` └─ endpoint ${RED}${device.endpoint}${nf} ${typ}${device.endpointName}${nf} ${debugStringify(device.clusterServersId)}`);
|
|
761
|
-
}
|
|
762
|
-
});
|
|
763
|
-
*/
|
|
764
602
|
this.shutdown = true;
|
|
765
603
|
return;
|
|
766
604
|
}
|
|
@@ -776,7 +614,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
776
614
|
}
|
|
777
615
|
if (hasParameter('loginterfaces')) {
|
|
778
616
|
const { logInterfaces } = await import('./utils/network.js');
|
|
779
|
-
// this.log.info(`${plg}Matterbridge${nf} network interfaces log`);
|
|
780
617
|
logInterfaces();
|
|
781
618
|
this.shutdown = true;
|
|
782
619
|
return;
|
|
@@ -811,7 +648,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
811
648
|
this.shutdown = true;
|
|
812
649
|
return;
|
|
813
650
|
}
|
|
814
|
-
// Start the matter storage and create the matterbridge context
|
|
815
651
|
try {
|
|
816
652
|
await this.startMatterStorage();
|
|
817
653
|
}
|
|
@@ -819,21 +655,18 @@ export class Matterbridge extends EventEmitter {
|
|
|
819
655
|
this.log.fatal(`Fatal error creating matter storage: ${error instanceof Error ? error.message : error}`);
|
|
820
656
|
throw new Error(`Fatal error creating matter storage: ${error instanceof Error ? error.message : error}`);
|
|
821
657
|
}
|
|
822
|
-
// Clear the matterbridge context if the reset parameter is set
|
|
823
658
|
if (hasParameter('reset') && getParameter('reset') === undefined) {
|
|
824
659
|
this.initialized = true;
|
|
825
660
|
await this.shutdownProcessAndReset();
|
|
826
661
|
this.shutdown = true;
|
|
827
662
|
return;
|
|
828
663
|
}
|
|
829
|
-
// Clear matterbridge plugin context if the reset parameter is set
|
|
830
664
|
if (hasParameter('reset') && getParameter('reset') !== undefined) {
|
|
831
665
|
this.log.debug(`Reset plugin ${getParameter('reset')}`);
|
|
832
666
|
const plugin = this.plugins.get(getParameter('reset'));
|
|
833
667
|
if (plugin) {
|
|
834
668
|
const matterStorageManager = await this.matterStorageService?.open(plugin.name);
|
|
835
669
|
if (!matterStorageManager) {
|
|
836
|
-
/* istanbul ignore next */
|
|
837
670
|
this.log.error(`Plugin ${plg}${plugin.name}${er} storageManager not found`);
|
|
838
671
|
}
|
|
839
672
|
else {
|
|
@@ -852,39 +685,32 @@ export class Matterbridge extends EventEmitter {
|
|
|
852
685
|
this.shutdown = true;
|
|
853
686
|
return;
|
|
854
687
|
}
|
|
855
|
-
// Initialize frontend
|
|
856
688
|
if (getIntParameter('frontend') !== 0 || getIntParameter('frontend') === undefined)
|
|
857
689
|
await this.frontend.start(getIntParameter('frontend'));
|
|
858
|
-
// Check in 30 seconds the latest and dev versions of matterbridge and the plugins
|
|
859
690
|
clearTimeout(this.checkUpdateTimeout);
|
|
860
691
|
this.checkUpdateTimeout = setTimeout(async () => {
|
|
861
692
|
const { checkUpdates } = await import('./update.js');
|
|
862
693
|
checkUpdates(this);
|
|
863
694
|
}, 30 * 1000).unref();
|
|
864
|
-
// Check each 12 hours the latest and dev versions of matterbridge and the plugins
|
|
865
695
|
clearInterval(this.checkUpdateInterval);
|
|
866
696
|
this.checkUpdateInterval = setInterval(async () => {
|
|
867
697
|
const { checkUpdates } = await import('./update.js');
|
|
868
698
|
checkUpdates(this);
|
|
869
699
|
}, 12 * 60 * 60 * 1000).unref();
|
|
870
|
-
// Start the matterbridge in mode test
|
|
871
700
|
if (hasParameter('test')) {
|
|
872
701
|
this.bridgeMode = 'bridge';
|
|
873
702
|
MatterbridgeEndpoint.bridgeMode = 'bridge';
|
|
874
703
|
return;
|
|
875
704
|
}
|
|
876
|
-
// Start the matterbridge in mode controller
|
|
877
705
|
if (hasParameter('controller')) {
|
|
878
706
|
this.bridgeMode = 'controller';
|
|
879
707
|
await this.startController();
|
|
880
708
|
return;
|
|
881
709
|
}
|
|
882
|
-
// Check if the bridge mode is set and start matterbridge in bridge mode if not set
|
|
883
710
|
if (!hasParameter('bridge') && !hasParameter('childbridge') && (await this.nodeContext?.get('bridgeMode', '')) === '') {
|
|
884
711
|
this.log.info('Setting default matterbridge start mode to bridge');
|
|
885
712
|
await this.nodeContext?.set('bridgeMode', 'bridge');
|
|
886
713
|
}
|
|
887
|
-
// Start matterbridge in bridge mode
|
|
888
714
|
if (hasParameter('bridge') || (!hasParameter('childbridge') && (await this.nodeContext?.get('bridgeMode', '')) === 'bridge')) {
|
|
889
715
|
this.bridgeMode = 'bridge';
|
|
890
716
|
MatterbridgeEndpoint.bridgeMode = 'bridge';
|
|
@@ -892,7 +718,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
892
718
|
await this.startBridge();
|
|
893
719
|
return;
|
|
894
720
|
}
|
|
895
|
-
// Start matterbridge in childbridge mode
|
|
896
721
|
if (hasParameter('childbridge') || (!hasParameter('bridge') && (await this.nodeContext?.get('bridgeMode', '')) === 'childbridge')) {
|
|
897
722
|
this.bridgeMode = 'childbridge';
|
|
898
723
|
MatterbridgeEndpoint.bridgeMode = 'childbridge';
|
|
@@ -901,20 +726,10 @@ export class Matterbridge extends EventEmitter {
|
|
|
901
726
|
return;
|
|
902
727
|
}
|
|
903
728
|
}
|
|
904
|
-
/**
|
|
905
|
-
* Asynchronously loads and starts the registered plugins.
|
|
906
|
-
*
|
|
907
|
-
* This method is responsible for initializing and starting all enabled plugins.
|
|
908
|
-
* It ensures that each plugin is properly loaded and started before the bridge starts.
|
|
909
|
-
*
|
|
910
|
-
* @returns {Promise<void>} A promise that resolves when all plugins have been loaded and started.
|
|
911
|
-
*/
|
|
912
729
|
async startPlugins() {
|
|
913
|
-
// Check, load and start the plugins
|
|
914
730
|
for (const plugin of this.plugins) {
|
|
915
731
|
plugin.configJson = await this.plugins.loadConfig(plugin);
|
|
916
732
|
plugin.schemaJson = await this.plugins.loadSchema(plugin);
|
|
917
|
-
// Check if the plugin is available
|
|
918
733
|
if (!(await this.plugins.resolve(plugin.path))) {
|
|
919
734
|
this.log.error(`Plugin ${plg}${plugin.name}${er} not found or not validated. Disabling it.`);
|
|
920
735
|
plugin.enabled = false;
|
|
@@ -932,14 +747,10 @@ export class Matterbridge extends EventEmitter {
|
|
|
932
747
|
plugin.configured = false;
|
|
933
748
|
plugin.registeredDevices = undefined;
|
|
934
749
|
plugin.addedDevices = undefined;
|
|
935
|
-
this.plugins.load(plugin, true, 'Matterbridge is starting');
|
|
750
|
+
this.plugins.load(plugin, true, 'Matterbridge is starting');
|
|
936
751
|
}
|
|
937
752
|
this.frontend.wssSendRefreshRequired('plugins');
|
|
938
753
|
}
|
|
939
|
-
/**
|
|
940
|
-
* Registers the process handlers for uncaughtException, unhandledRejection, SIGINT and SIGTERM.
|
|
941
|
-
* When either of these signals are received, the cleanup method is called with an appropriate message.
|
|
942
|
-
*/
|
|
943
754
|
registerProcessHandlers() {
|
|
944
755
|
this.log.debug(`Registering uncaughtException and unhandledRejection handlers...`);
|
|
945
756
|
process.removeAllListeners('uncaughtException');
|
|
@@ -966,9 +777,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
966
777
|
};
|
|
967
778
|
process.on('SIGTERM', this.sigtermHandler);
|
|
968
779
|
}
|
|
969
|
-
/**
|
|
970
|
-
* Deregisters the process uncaughtException, unhandledRejection, SIGINT and SIGTERM signal handlers.
|
|
971
|
-
*/
|
|
972
780
|
deregisterProcessHandlers() {
|
|
973
781
|
this.log.debug(`Deregistering uncaughtException and unhandledRejection handlers...`);
|
|
974
782
|
if (this.exceptionHandler)
|
|
@@ -985,17 +793,12 @@ export class Matterbridge extends EventEmitter {
|
|
|
985
793
|
process.off('SIGTERM', this.sigtermHandler);
|
|
986
794
|
this.sigtermHandler = undefined;
|
|
987
795
|
}
|
|
988
|
-
/**
|
|
989
|
-
* Logs the node and system information.
|
|
990
|
-
*/
|
|
991
796
|
async logNodeAndSystemInfo() {
|
|
992
|
-
// IP address information
|
|
993
797
|
const networkInterfaces = os.networkInterfaces();
|
|
994
798
|
this.systemInformation.interfaceName = '';
|
|
995
799
|
this.systemInformation.ipv4Address = '';
|
|
996
800
|
this.systemInformation.ipv6Address = '';
|
|
997
801
|
for (const [interfaceName, interfaceDetails] of Object.entries(networkInterfaces)) {
|
|
998
|
-
// this.log.debug(`Checking interface: '${interfaceName}' for '${this.mdnsInterface}'`);
|
|
999
802
|
if (this.mdnsInterface && interfaceName !== this.mdnsInterface)
|
|
1000
803
|
continue;
|
|
1001
804
|
if (!interfaceDetails) {
|
|
@@ -1021,22 +824,19 @@ export class Matterbridge extends EventEmitter {
|
|
|
1021
824
|
break;
|
|
1022
825
|
}
|
|
1023
826
|
}
|
|
1024
|
-
// Node information
|
|
1025
827
|
this.systemInformation.nodeVersion = process.versions.node;
|
|
1026
828
|
const versionMajor = parseInt(this.systemInformation.nodeVersion.split('.')[0]);
|
|
1027
829
|
const versionMinor = parseInt(this.systemInformation.nodeVersion.split('.')[1]);
|
|
1028
830
|
const versionPatch = parseInt(this.systemInformation.nodeVersion.split('.')[2]);
|
|
1029
|
-
// Host system information
|
|
1030
831
|
this.systemInformation.hostname = os.hostname();
|
|
1031
832
|
this.systemInformation.user = os.userInfo().username;
|
|
1032
|
-
this.systemInformation.osType = os.type();
|
|
1033
|
-
this.systemInformation.osRelease = os.release();
|
|
1034
|
-
this.systemInformation.osPlatform = os.platform();
|
|
1035
|
-
this.systemInformation.osArch = os.arch();
|
|
1036
|
-
this.systemInformation.totalMemory = (os.totalmem() / 1024 / 1024 / 1024).toFixed(2) + ' GB';
|
|
1037
|
-
this.systemInformation.freeMemory = (os.freemem() / 1024 / 1024 / 1024).toFixed(2) + ' GB';
|
|
1038
|
-
this.systemInformation.systemUptime = (os.uptime() / 60 / 60).toFixed(2) + ' hours';
|
|
1039
|
-
// Log the system information
|
|
833
|
+
this.systemInformation.osType = os.type();
|
|
834
|
+
this.systemInformation.osRelease = os.release();
|
|
835
|
+
this.systemInformation.osPlatform = os.platform();
|
|
836
|
+
this.systemInformation.osArch = os.arch();
|
|
837
|
+
this.systemInformation.totalMemory = (os.totalmem() / 1024 / 1024 / 1024).toFixed(2) + ' GB';
|
|
838
|
+
this.systemInformation.freeMemory = (os.freemem() / 1024 / 1024 / 1024).toFixed(2) + ' GB';
|
|
839
|
+
this.systemInformation.systemUptime = (os.uptime() / 60 / 60).toFixed(2) + ' hours';
|
|
1040
840
|
this.log.debug('Host System Information:');
|
|
1041
841
|
this.log.debug(`- Hostname: ${this.systemInformation.hostname}`);
|
|
1042
842
|
this.log.debug(`- User: ${this.systemInformation.user}`);
|
|
@@ -1052,17 +852,14 @@ export class Matterbridge extends EventEmitter {
|
|
|
1052
852
|
this.log.debug(`- Total Memory: ${this.systemInformation.totalMemory}`);
|
|
1053
853
|
this.log.debug(`- Free Memory: ${this.systemInformation.freeMemory}`);
|
|
1054
854
|
this.log.debug(`- System Uptime: ${this.systemInformation.systemUptime}`);
|
|
1055
|
-
// Log directories
|
|
1056
855
|
this.log.debug(`Root Directory: ${this.rootDirectory}`);
|
|
1057
856
|
this.log.debug(`Home Directory: ${this.homeDirectory}`);
|
|
1058
857
|
this.log.debug(`Matterbridge Directory: ${this.matterbridgeDirectory}`);
|
|
1059
858
|
this.log.debug(`Matterbridge Plugin Directory: ${this.matterbridgePluginDirectory}`);
|
|
1060
859
|
this.log.debug(`Matterbridge Matter Certificate Directory: ${this.matterbridgeCertDirectory}`);
|
|
1061
|
-
// Global node_modules directory
|
|
1062
860
|
if (this.nodeContext)
|
|
1063
861
|
this.globalModulesDirectory = this.matterbridgeInformation.globalModulesDirectory = await this.nodeContext.get('globalModulesDirectory', '');
|
|
1064
862
|
if (this.globalModulesDirectory === '') {
|
|
1065
|
-
// First run of Matterbridge so the node storage is empty
|
|
1066
863
|
try {
|
|
1067
864
|
const { getGlobalNodeModules } = await import('./utils/network.js');
|
|
1068
865
|
this.execRunningCount++;
|
|
@@ -1077,81 +874,50 @@ export class Matterbridge extends EventEmitter {
|
|
|
1077
874
|
}
|
|
1078
875
|
else
|
|
1079
876
|
this.log.debug(`Global node_modules Directory: ${this.globalModulesDirectory}`);
|
|
1080
|
-
/* removed cause is too expensive for the shelly board and not really needed. Why should the globalModulesDirectory change?
|
|
1081
|
-
else {
|
|
1082
|
-
this.getGlobalNodeModules()
|
|
1083
|
-
.then(async (globalModulesDirectory) => {
|
|
1084
|
-
this.globalModulesDirectory = globalModulesDirectory;
|
|
1085
|
-
this.matterbridgeInformation.globalModulesDirectory = this.globalModulesDirectory;
|
|
1086
|
-
this.log.debug(`Global node_modules Directory: ${this.globalModulesDirectory}`);
|
|
1087
|
-
await this.nodeContext?.set<string>('globalModulesDirectory', this.globalModulesDirectory);
|
|
1088
|
-
})
|
|
1089
|
-
.catch((error) => {
|
|
1090
|
-
this.log.error(`Error getting global node_modules directory: ${error}`);
|
|
1091
|
-
});
|
|
1092
|
-
}*/
|
|
1093
|
-
// Matterbridge version
|
|
1094
877
|
const packageJson = JSON.parse(await fs.readFile(path.join(this.rootDirectory, 'package.json'), 'utf-8'));
|
|
1095
878
|
this.matterbridgeVersion = this.matterbridgeLatestVersion = this.matterbridgeDevVersion = packageJson.version;
|
|
1096
879
|
this.matterbridgeInformation.matterbridgeVersion = this.matterbridgeInformation.matterbridgeLatestVersion = this.matterbridgeInformation.matterbridgeDevVersion = packageJson.version;
|
|
1097
880
|
this.log.debug(`Matterbridge Version: ${this.matterbridgeVersion}`);
|
|
1098
|
-
// Matterbridge latest version (will be set in the checkUpdate function)
|
|
1099
881
|
if (this.nodeContext)
|
|
1100
882
|
this.matterbridgeLatestVersion = this.matterbridgeInformation.matterbridgeLatestVersion = await this.nodeContext.get('matterbridgeLatestVersion', this.matterbridgeVersion);
|
|
1101
883
|
this.log.debug(`Matterbridge Latest Version: ${this.matterbridgeLatestVersion}`);
|
|
1102
|
-
// Matterbridge dev version (will be set in the checkUpdate function)
|
|
1103
884
|
if (this.nodeContext)
|
|
1104
885
|
this.matterbridgeDevVersion = this.matterbridgeInformation.matterbridgeDevVersion = await this.nodeContext.get('matterbridgeDevVersion', this.matterbridgeVersion);
|
|
1105
886
|
this.log.debug(`Matterbridge Dev Version: ${this.matterbridgeDevVersion}`);
|
|
1106
|
-
// Current working directory
|
|
1107
887
|
const currentDir = process.cwd();
|
|
1108
888
|
this.log.debug(`Current Working Directory: ${currentDir}`);
|
|
1109
|
-
// Command line arguments (excluding 'node' and the script name)
|
|
1110
889
|
const cmdArgs = process.argv.slice(2).join(' ');
|
|
1111
890
|
this.log.debug(`Command Line Arguments: ${cmdArgs}`);
|
|
1112
891
|
}
|
|
1113
|
-
/**
|
|
1114
|
-
* Creates a MatterLogger function to show the matter.js log messages in AnsiLogger (for the frontend).
|
|
1115
|
-
*
|
|
1116
|
-
* @returns {Function} The MatterLogger function.
|
|
1117
|
-
*/
|
|
1118
892
|
createMatterLogger() {
|
|
1119
|
-
const matterLogger = new AnsiLogger({ logName: 'Matter', logTimestampFormat: 4
|
|
893
|
+
const matterLogger = new AnsiLogger({ logName: 'Matter', logTimestampFormat: 4, logLevel: "debug" });
|
|
1120
894
|
return (level, formattedLog) => {
|
|
1121
895
|
const logger = formattedLog.slice(44, 44 + 20).trim();
|
|
1122
896
|
const message = formattedLog.slice(65);
|
|
1123
897
|
matterLogger.logName = logger;
|
|
1124
898
|
switch (level) {
|
|
1125
899
|
case MatterLogLevel.DEBUG:
|
|
1126
|
-
matterLogger.log("debug"
|
|
900
|
+
matterLogger.log("debug", message);
|
|
1127
901
|
break;
|
|
1128
902
|
case MatterLogLevel.INFO:
|
|
1129
|
-
matterLogger.log("info"
|
|
903
|
+
matterLogger.log("info", message);
|
|
1130
904
|
break;
|
|
1131
905
|
case MatterLogLevel.NOTICE:
|
|
1132
|
-
matterLogger.log("notice"
|
|
906
|
+
matterLogger.log("notice", message);
|
|
1133
907
|
break;
|
|
1134
908
|
case MatterLogLevel.WARN:
|
|
1135
|
-
matterLogger.log("warn"
|
|
909
|
+
matterLogger.log("warn", message);
|
|
1136
910
|
break;
|
|
1137
911
|
case MatterLogLevel.ERROR:
|
|
1138
|
-
matterLogger.log("error"
|
|
912
|
+
matterLogger.log("error", message);
|
|
1139
913
|
break;
|
|
1140
914
|
case MatterLogLevel.FATAL:
|
|
1141
|
-
matterLogger.log("fatal"
|
|
915
|
+
matterLogger.log("fatal", message);
|
|
1142
916
|
break;
|
|
1143
917
|
}
|
|
1144
918
|
};
|
|
1145
919
|
}
|
|
1146
|
-
/**
|
|
1147
|
-
* Creates a Matter File Logger.
|
|
1148
|
-
*
|
|
1149
|
-
* @param {string} filePath - The path to the log file.
|
|
1150
|
-
* @param {boolean} [unlink] - Whether to unlink the log file before creating a new one.
|
|
1151
|
-
* @returns {Function} - A function that logs formatted messages to the log file.
|
|
1152
|
-
*/
|
|
1153
920
|
async createMatterFileLogger(filePath, unlink = false) {
|
|
1154
|
-
// 2024-08-21 08:55:19.488 DEBUG InteractionMessenger Sending DataReport chunk with 28 attributes and 0 events: 1004 bytes
|
|
1155
921
|
let fileSize = 0;
|
|
1156
922
|
if (unlink) {
|
|
1157
923
|
try {
|
|
@@ -1162,12 +928,10 @@ export class Matterbridge extends EventEmitter {
|
|
|
1162
928
|
}
|
|
1163
929
|
}
|
|
1164
930
|
return async (level, formattedLog) => {
|
|
1165
|
-
/* istanbul ignore if */
|
|
1166
931
|
if (fileSize > 100000000) {
|
|
1167
|
-
return;
|
|
932
|
+
return;
|
|
1168
933
|
}
|
|
1169
934
|
fileSize += formattedLog.length;
|
|
1170
|
-
/* istanbul ignore if */
|
|
1171
935
|
if (fileSize > 100000000) {
|
|
1172
936
|
await fs.appendFile(filePath, `Logging on file has been stopped because the file size is greater than 100MB.` + os.EOL);
|
|
1173
937
|
return;
|
|
@@ -1200,21 +964,12 @@ export class Matterbridge extends EventEmitter {
|
|
|
1200
964
|
}
|
|
1201
965
|
};
|
|
1202
966
|
}
|
|
1203
|
-
/**
|
|
1204
|
-
* Restarts the process by exiting the current instance and loading a new instance.
|
|
1205
|
-
*/
|
|
1206
967
|
async restartProcess() {
|
|
1207
968
|
await this.cleanup('restarting...', true);
|
|
1208
969
|
}
|
|
1209
|
-
/**
|
|
1210
|
-
* Shut down the process.
|
|
1211
|
-
*/
|
|
1212
970
|
async shutdownProcess() {
|
|
1213
971
|
await this.cleanup('shutting down...', false);
|
|
1214
972
|
}
|
|
1215
|
-
/**
|
|
1216
|
-
* Update matterbridge and shut down the process.
|
|
1217
|
-
*/
|
|
1218
973
|
async updateProcess() {
|
|
1219
974
|
this.log.info('Updating matterbridge...');
|
|
1220
975
|
try {
|
|
@@ -1228,75 +983,52 @@ export class Matterbridge extends EventEmitter {
|
|
|
1228
983
|
this.frontend.wssSendRestartRequired();
|
|
1229
984
|
await this.cleanup('updating...', false);
|
|
1230
985
|
}
|
|
1231
|
-
|
|
1232
|
-
* Unregister all devices and shut down the process.
|
|
1233
|
-
*/
|
|
1234
|
-
async unregisterAndShutdownProcess() {
|
|
986
|
+
async unregisterAndShutdownProcess(timeout = 1000) {
|
|
1235
987
|
this.log.info('Unregistering all devices and shutting down...');
|
|
1236
988
|
for (const plugin of this.plugins) {
|
|
1237
989
|
await this.removeAllBridgedEndpoints(plugin.name, 250);
|
|
1238
990
|
}
|
|
1239
991
|
this.log.debug('Waiting for the MessageExchange to finish...');
|
|
1240
|
-
await new Promise((resolve) => setTimeout(resolve,
|
|
992
|
+
await new Promise((resolve) => setTimeout(resolve, timeout));
|
|
1241
993
|
this.log.debug('Cleaning up and shutting down...');
|
|
1242
|
-
await this.cleanup('unregistered all devices and shutting down...', false);
|
|
994
|
+
await this.cleanup('unregistered all devices and shutting down...', false, timeout);
|
|
1243
995
|
}
|
|
1244
|
-
/**
|
|
1245
|
-
* Reset commissioning and shut down the process.
|
|
1246
|
-
*/
|
|
1247
996
|
async shutdownProcessAndReset() {
|
|
1248
997
|
await this.cleanup('shutting down with reset...', false);
|
|
1249
998
|
}
|
|
1250
|
-
/**
|
|
1251
|
-
* Factory reset and shut down the process.
|
|
1252
|
-
*/
|
|
1253
999
|
async shutdownProcessAndFactoryReset() {
|
|
1254
1000
|
await this.cleanup('shutting down with factory reset...', false);
|
|
1255
1001
|
}
|
|
1256
|
-
/**
|
|
1257
|
-
* Cleans up the Matterbridge instance.
|
|
1258
|
-
*
|
|
1259
|
-
* @param {string} message - The cleanup message.
|
|
1260
|
-
* @param {boolean} [restart] - Indicates whether to restart the instance after cleanup. Default is `false`.
|
|
1261
|
-
* @param {number} [timeout] - The timeout duration to wait for the message exchange to complete in milliseconds. Default is 1000.
|
|
1262
|
-
* @returns {Promise<void>} A promise that resolves when the cleanup is completed.
|
|
1263
|
-
*/
|
|
1264
1002
|
async cleanup(message, restart = false, timeout = 1000) {
|
|
1265
1003
|
if (this.initialized && !this.hasCleanupStarted) {
|
|
1266
1004
|
this.emit('cleanup_started');
|
|
1267
1005
|
this.hasCleanupStarted = true;
|
|
1268
1006
|
this.log.info(message);
|
|
1269
|
-
// Clear the start matter interval
|
|
1270
1007
|
if (this.startMatterInterval) {
|
|
1271
1008
|
clearInterval(this.startMatterInterval);
|
|
1272
1009
|
this.startMatterInterval = undefined;
|
|
1273
1010
|
this.log.debug('Start matter interval cleared');
|
|
1274
1011
|
}
|
|
1275
|
-
// Clear the check update timeout
|
|
1276
1012
|
if (this.checkUpdateTimeout) {
|
|
1277
1013
|
clearTimeout(this.checkUpdateTimeout);
|
|
1278
1014
|
this.checkUpdateTimeout = undefined;
|
|
1279
1015
|
this.log.debug('Check update timeout cleared');
|
|
1280
1016
|
}
|
|
1281
|
-
// Clear the check update interval
|
|
1282
1017
|
if (this.checkUpdateInterval) {
|
|
1283
1018
|
clearInterval(this.checkUpdateInterval);
|
|
1284
1019
|
this.checkUpdateInterval = undefined;
|
|
1285
1020
|
this.log.debug('Check update interval cleared');
|
|
1286
1021
|
}
|
|
1287
|
-
// Clear the configure timeout
|
|
1288
1022
|
if (this.configureTimeout) {
|
|
1289
1023
|
clearTimeout(this.configureTimeout);
|
|
1290
1024
|
this.configureTimeout = undefined;
|
|
1291
1025
|
this.log.debug('Matterbridge configure timeout cleared');
|
|
1292
1026
|
}
|
|
1293
|
-
// Clear the reachability timeout
|
|
1294
1027
|
if (this.reachabilityTimeout) {
|
|
1295
1028
|
clearTimeout(this.reachabilityTimeout);
|
|
1296
1029
|
this.reachabilityTimeout = undefined;
|
|
1297
1030
|
this.log.debug('Matterbridge reachability timeout cleared');
|
|
1298
1031
|
}
|
|
1299
|
-
// Call the shutdown method of each plugin and clear the plugins reachability timeout
|
|
1300
1032
|
for (const plugin of this.plugins) {
|
|
1301
1033
|
if (!plugin.enabled || plugin.error)
|
|
1302
1034
|
continue;
|
|
@@ -1307,10 +1039,9 @@ export class Matterbridge extends EventEmitter {
|
|
|
1307
1039
|
this.log.debug(`Plugin ${plg}${plugin.name}${db} reachability timeout cleared`);
|
|
1308
1040
|
}
|
|
1309
1041
|
}
|
|
1310
|
-
// Stop matter server nodes
|
|
1311
1042
|
this.log.notice(`Stopping matter server nodes in ${this.bridgeMode} mode...`);
|
|
1312
1043
|
this.log.debug('Waiting for the MessageExchange to finish...');
|
|
1313
|
-
await new Promise((resolve) => setTimeout(resolve, timeout));
|
|
1044
|
+
await new Promise((resolve) => setTimeout(resolve, timeout));
|
|
1314
1045
|
if (this.bridgeMode === 'bridge') {
|
|
1315
1046
|
if (this.serverNode) {
|
|
1316
1047
|
await this.stopServerNode(this.serverNode);
|
|
@@ -1332,7 +1063,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1332
1063
|
}
|
|
1333
1064
|
}
|
|
1334
1065
|
this.log.notice('Stopped matter server nodes');
|
|
1335
|
-
// Matter commisioning reset
|
|
1336
1066
|
if (message === 'shutting down with reset...') {
|
|
1337
1067
|
this.log.info('Resetting Matterbridge commissioning information...');
|
|
1338
1068
|
await this.matterStorageManager?.createContext('events')?.clearAll();
|
|
@@ -1342,36 +1072,18 @@ export class Matterbridge extends EventEmitter {
|
|
|
1342
1072
|
await this.matterbridgeContext?.clearAll();
|
|
1343
1073
|
this.log.info('Matter storage reset done! Remove the bridge from the controller.');
|
|
1344
1074
|
}
|
|
1345
|
-
// Stop matter storage
|
|
1346
1075
|
await this.stopMatterStorage();
|
|
1347
|
-
// Stop the frontend
|
|
1348
1076
|
await this.frontend.stop();
|
|
1349
|
-
// Remove the matterfilelogger
|
|
1350
1077
|
try {
|
|
1351
1078
|
Logger.removeLogger('matterfilelogger');
|
|
1352
1079
|
}
|
|
1353
1080
|
catch (error) {
|
|
1354
1081
|
this.log.debug(`Error removing the matterfilelogger for file ${CYAN}${path.join(this.matterbridgeDirectory, this.matterLoggerFile)}${db}: ${error instanceof Error ? error.message : String(error)}`);
|
|
1355
1082
|
}
|
|
1356
|
-
// Close the matterbridge node storage and context
|
|
1357
1083
|
if (this.nodeStorage && this.nodeContext) {
|
|
1358
|
-
/*
|
|
1359
|
-
TODO: Implement serialization of registered devices in edge mode
|
|
1360
|
-
this.log.info('Saving registered devices...');
|
|
1361
|
-
const serializedRegisteredDevices: SerializedMatterbridgeEndpoint[] = [];
|
|
1362
|
-
this.devices.forEach(async (device) => {
|
|
1363
|
-
const serializedMatterbridgeDevice = MatterbridgeEndpoint.serialize(device);
|
|
1364
|
-
// this.log.info(`- ${serializedMatterbridgeDevice.deviceName}${rs}\n`, serializedMatterbridgeDevice);
|
|
1365
|
-
if (serializedMatterbridgeDevice) serializedRegisteredDevices.push(serializedMatterbridgeDevice);
|
|
1366
|
-
});
|
|
1367
|
-
await this.nodeContext.set<SerializedMatterbridgeEndpoint[]>('devices', serializedRegisteredDevices);
|
|
1368
|
-
this.log.info(`Saved registered devices (${serializedRegisteredDevices?.length})`);
|
|
1369
|
-
*/
|
|
1370
|
-
// Clear nodeContext and nodeStorage (they just need 1000ms to write the data to disk)
|
|
1371
1084
|
this.log.debug(`Closing node storage context for ${plg}Matterbridge${db}...`);
|
|
1372
1085
|
await this.nodeContext.close();
|
|
1373
1086
|
this.nodeContext = undefined;
|
|
1374
|
-
// Clear nodeContext for each plugin (they just need 1000ms to write the data to disk)
|
|
1375
1087
|
for (const plugin of this.plugins) {
|
|
1376
1088
|
if (plugin.nodeContext) {
|
|
1377
1089
|
this.log.debug(`Closing node storage context for plugin ${plg}${plugin.name}${db}...`);
|
|
@@ -1388,10 +1100,8 @@ export class Matterbridge extends EventEmitter {
|
|
|
1388
1100
|
}
|
|
1389
1101
|
this.plugins.clear();
|
|
1390
1102
|
this.devices.clear();
|
|
1391
|
-
// Factory reset
|
|
1392
1103
|
if (message === 'shutting down with factory reset...') {
|
|
1393
1104
|
try {
|
|
1394
|
-
// Delete matter storage directory with its subdirectories and backup
|
|
1395
1105
|
const dir = path.join(this.matterbridgeDirectory, this.matterStorageName);
|
|
1396
1106
|
this.log.info(`Removing matter storage directory: ${dir}`);
|
|
1397
1107
|
await fs.rm(dir, { recursive: true });
|
|
@@ -1405,7 +1115,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1405
1115
|
}
|
|
1406
1116
|
}
|
|
1407
1117
|
try {
|
|
1408
|
-
// Delete matterbridge storage directory with its subdirectories and backup
|
|
1409
1118
|
const dir = path.join(this.matterbridgeDirectory, this.nodeStorageName);
|
|
1410
1119
|
this.log.info(`Removing matterbridge storage directory: ${dir}`);
|
|
1411
1120
|
await fs.rm(dir, { recursive: true });
|
|
@@ -1420,13 +1129,12 @@ export class Matterbridge extends EventEmitter {
|
|
|
1420
1129
|
}
|
|
1421
1130
|
this.log.info('Factory reset done! Remove all paired fabrics from the controllers.');
|
|
1422
1131
|
}
|
|
1423
|
-
// Deregisters the process handlers
|
|
1424
1132
|
this.deregisterProcessHandlers();
|
|
1425
1133
|
if (restart) {
|
|
1426
1134
|
if (message === 'updating...') {
|
|
1427
1135
|
this.log.info('Cleanup completed. Updating...');
|
|
1428
1136
|
Matterbridge.instance = undefined;
|
|
1429
|
-
this.emit('update');
|
|
1137
|
+
this.emit('update');
|
|
1430
1138
|
}
|
|
1431
1139
|
else if (message === 'restarting...') {
|
|
1432
1140
|
this.log.info('Cleanup completed. Restarting...');
|
|
@@ -1447,13 +1155,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1447
1155
|
this.log.debug('Cleanup already started...');
|
|
1448
1156
|
}
|
|
1449
1157
|
}
|
|
1450
|
-
/**
|
|
1451
|
-
* Creates and configures the server node for a single not bridged device.
|
|
1452
|
-
*
|
|
1453
|
-
* @param {RegisteredPlugin} plugin - The plugin to configure.
|
|
1454
|
-
* @param {MatterbridgeEndpoint} device - The device to associate with the plugin.
|
|
1455
|
-
* @returns {Promise<void>} A promise that resolves when the server node for the accessory plugin is created and configured.
|
|
1456
|
-
*/
|
|
1457
1158
|
async createDeviceServerNode(plugin, device) {
|
|
1458
1159
|
if (device.mode === 'server' && !device.serverNode && device.deviceType && device.deviceName && device.vendorId && device.vendorName && device.productId && device.productName) {
|
|
1459
1160
|
this.log.debug(`Creating device ${plg}${plugin.name}${db}:${dev}${device.deviceName}${db} server node...`);
|
|
@@ -1464,15 +1165,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
1464
1165
|
this.log.debug(`Added ${plg}${plugin.name}${db}:${dev}${device.deviceName}${db} to server node`);
|
|
1465
1166
|
}
|
|
1466
1167
|
}
|
|
1467
|
-
|
|
1468
|
-
* Creates and configures the server node for an accessory plugin for a given device.
|
|
1469
|
-
*
|
|
1470
|
-
* @param {RegisteredPlugin} plugin - The plugin to configure.
|
|
1471
|
-
* @param {MatterbridgeEndpoint} device - The device to associate with the plugin.
|
|
1472
|
-
* @param {boolean} [start] - Whether to start the server node after adding the device. Default is `false`.
|
|
1473
|
-
* @returns {Promise<void>} A promise that resolves when the server node for the accessory plugin is created and configured.
|
|
1474
|
-
*/
|
|
1475
|
-
async createAccessoryPlugin(plugin, device, start = false) {
|
|
1168
|
+
async createAccessoryPlugin(plugin, device) {
|
|
1476
1169
|
if (!plugin.locked && device.deviceType && device.deviceName && device.vendorId && device.productId && device.vendorName && device.productName) {
|
|
1477
1170
|
plugin.locked = true;
|
|
1478
1171
|
plugin.device = device;
|
|
@@ -1481,16 +1174,8 @@ export class Matterbridge extends EventEmitter {
|
|
|
1481
1174
|
plugin.serialNumber = await plugin.storageContext.get('serialNumber', '');
|
|
1482
1175
|
this.log.debug(`Adding ${plg}${plugin.name}${db}:${dev}${device.deviceName}${db} to ${plg}${plugin.name}${db} server node`);
|
|
1483
1176
|
await plugin.serverNode.add(device);
|
|
1484
|
-
if (start)
|
|
1485
|
-
await this.startServerNode(plugin.serverNode);
|
|
1486
1177
|
}
|
|
1487
1178
|
}
|
|
1488
|
-
/**
|
|
1489
|
-
* Creates and configures the server node and the aggregator node for a dynamic plugin.
|
|
1490
|
-
*
|
|
1491
|
-
* @param {RegisteredPlugin} plugin - The plugin to configure.
|
|
1492
|
-
* @returns {Promise<void>} A promise that resolves when the server node and the aggregator node for the dynamic plugin is created and configured.
|
|
1493
|
-
*/
|
|
1494
1179
|
async createDynamicPlugin(plugin) {
|
|
1495
1180
|
if (!plugin.locked) {
|
|
1496
1181
|
plugin.locked = true;
|
|
@@ -1501,14 +1186,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
1501
1186
|
await plugin.serverNode.add(plugin.aggregatorNode);
|
|
1502
1187
|
}
|
|
1503
1188
|
}
|
|
1504
|
-
/**
|
|
1505
|
-
* Starts the Matterbridge in bridge mode.
|
|
1506
|
-
*
|
|
1507
|
-
* @private
|
|
1508
|
-
* @returns {Promise<void>} A promise that resolves when the Matterbridge is started.
|
|
1509
|
-
*/
|
|
1510
1189
|
async startBridge() {
|
|
1511
|
-
// Plugins are configured by a timer when matter server is started and plugin.configured is set to true
|
|
1512
1190
|
if (!this.matterStorageManager)
|
|
1513
1191
|
throw new Error('No storage manager initialized');
|
|
1514
1192
|
if (!this.matterbridgeContext)
|
|
@@ -1547,18 +1225,15 @@ export class Matterbridge extends EventEmitter {
|
|
|
1547
1225
|
clearInterval(this.startMatterInterval);
|
|
1548
1226
|
this.startMatterInterval = undefined;
|
|
1549
1227
|
this.log.debug('Cleared startMatterInterval interval for Matterbridge');
|
|
1550
|
-
|
|
1551
|
-
this.startServerNode(this.serverNode); // We don't await this, because the server node is started in the background
|
|
1552
|
-
// Start the Matter server node of single devices in mode 'server'
|
|
1228
|
+
this.startServerNode(this.serverNode);
|
|
1553
1229
|
for (const device of this.devices.array()) {
|
|
1554
1230
|
if (device.mode === 'server' && device.serverNode) {
|
|
1555
1231
|
this.log.debug(`Starting server node for device ${dev}${device.deviceName}${db} in server mode...`);
|
|
1556
|
-
this.startServerNode(device.serverNode);
|
|
1232
|
+
this.startServerNode(device.serverNode);
|
|
1557
1233
|
}
|
|
1558
1234
|
}
|
|
1559
|
-
// Configure the plugins
|
|
1560
1235
|
this.configureTimeout = setTimeout(async () => {
|
|
1561
|
-
for (const plugin of this.plugins) {
|
|
1236
|
+
for (const plugin of this.plugins.array()) {
|
|
1562
1237
|
if (!plugin.enabled || !plugin.loaded || !plugin.started || plugin.error)
|
|
1563
1238
|
continue;
|
|
1564
1239
|
try {
|
|
@@ -1574,25 +1249,17 @@ export class Matterbridge extends EventEmitter {
|
|
|
1574
1249
|
}
|
|
1575
1250
|
this.frontend.wssSendRefreshRequired('plugins');
|
|
1576
1251
|
}, 30 * 1000).unref();
|
|
1577
|
-
// Setting reachability to true
|
|
1578
1252
|
this.reachabilityTimeout = setTimeout(() => {
|
|
1579
1253
|
this.log.info(`Setting reachability to true for ${plg}Matterbridge${db}`);
|
|
1580
1254
|
if (this.aggregatorNode)
|
|
1581
1255
|
this.setAggregatorReachability(this.aggregatorNode, true);
|
|
1582
1256
|
this.frontend.wssSendRefreshRequired('reachability');
|
|
1583
1257
|
}, 60 * 1000).unref();
|
|
1584
|
-
// Logger.get('LogServerNode').info(this.serverNode);
|
|
1585
1258
|
this.emit('bridge_started');
|
|
1586
1259
|
this.log.notice('Matterbridge bridge started successfully');
|
|
1587
1260
|
}, 1000);
|
|
1588
1261
|
}
|
|
1589
|
-
|
|
1590
|
-
* Starts the Matterbridge in childbridge mode.
|
|
1591
|
-
*
|
|
1592
|
-
* @private
|
|
1593
|
-
* @returns {Promise<void>} A promise that resolves when the Matterbridge is started.
|
|
1594
|
-
*/
|
|
1595
|
-
async startChildbridge() {
|
|
1262
|
+
async startChildbridge(delay = 1000) {
|
|
1596
1263
|
if (!this.matterStorageManager)
|
|
1597
1264
|
throw new Error('No storage manager initialized');
|
|
1598
1265
|
await this.startPlugins();
|
|
@@ -1600,7 +1267,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
1600
1267
|
let failCount = 0;
|
|
1601
1268
|
this.startMatterInterval = setInterval(async () => {
|
|
1602
1269
|
let allStarted = true;
|
|
1603
|
-
for (const plugin of this.plugins) {
|
|
1270
|
+
for (const plugin of this.plugins.array()) {
|
|
1604
1271
|
if (!plugin.enabled)
|
|
1605
1272
|
continue;
|
|
1606
1273
|
if (plugin.error) {
|
|
@@ -1619,7 +1286,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
1619
1286
|
this.log.debug(`Waiting (failSafeCount=${failCount}/${this.failCountLimit}) for plugin ${plg}${plugin.name}${db} to load (${plugin.loaded}) and start (${plugin.started}) ...`);
|
|
1620
1287
|
failCount++;
|
|
1621
1288
|
if (failCount > this.failCountLimit) {
|
|
1622
|
-
this.log.error(`Error waiting for plugin ${plg}${plugin.name}${er} to load and start. Plugin is in error
|
|
1289
|
+
this.log.error(`Error waiting for plugin ${plg}${plugin.name}${er} to load and start. Plugin is in error state.`);
|
|
1623
1290
|
plugin.error = true;
|
|
1624
1291
|
}
|
|
1625
1292
|
}
|
|
@@ -1628,11 +1295,11 @@ export class Matterbridge extends EventEmitter {
|
|
|
1628
1295
|
return;
|
|
1629
1296
|
clearInterval(this.startMatterInterval);
|
|
1630
1297
|
this.startMatterInterval = undefined;
|
|
1631
|
-
|
|
1298
|
+
if (delay > 0)
|
|
1299
|
+
await wait(delay);
|
|
1632
1300
|
this.log.debug('Cleared startMatterInterval interval in childbridge mode');
|
|
1633
|
-
// Configure the plugins
|
|
1634
1301
|
this.configureTimeout = setTimeout(async () => {
|
|
1635
|
-
for (const plugin of this.plugins) {
|
|
1302
|
+
for (const plugin of this.plugins.array()) {
|
|
1636
1303
|
if (!plugin.enabled || !plugin.loaded || !plugin.started || plugin.error)
|
|
1637
1304
|
continue;
|
|
1638
1305
|
try {
|
|
@@ -1667,9 +1334,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
1667
1334
|
this.log.error(`Node storage context not found for plugin ${plg}${plugin.name}${er}`);
|
|
1668
1335
|
continue;
|
|
1669
1336
|
}
|
|
1670
|
-
|
|
1671
|
-
this.startServerNode(plugin.serverNode); // We don't await this, because the server node is started in the background
|
|
1672
|
-
// Setting reachability to true
|
|
1337
|
+
this.startServerNode(plugin.serverNode);
|
|
1673
1338
|
plugin.reachabilityTimeout = setTimeout(() => {
|
|
1674
1339
|
this.log.info(`Setting reachability to true for ${plg}${plugin.name}${nf} type ${plugin.type} server node ${plugin.serverNode !== undefined} aggregator node ${plugin.aggregatorNode !== undefined} device ${plugin.device !== undefined}`);
|
|
1675
1340
|
if (plugin.type === 'DynamicPlatform' && plugin.aggregatorNode)
|
|
@@ -1677,241 +1342,19 @@ export class Matterbridge extends EventEmitter {
|
|
|
1677
1342
|
this.frontend.wssSendRefreshRequired('reachability');
|
|
1678
1343
|
}, 60 * 1000).unref();
|
|
1679
1344
|
}
|
|
1680
|
-
// Start the Matter server node of single devices in mode 'server'
|
|
1681
1345
|
for (const device of this.devices.array()) {
|
|
1682
1346
|
if (device.mode === 'server' && device.serverNode) {
|
|
1683
1347
|
this.log.debug(`Starting server node for device ${dev}${device.deviceName}${db} in server mode...`);
|
|
1684
|
-
this.startServerNode(device.serverNode);
|
|
1348
|
+
this.startServerNode(device.serverNode);
|
|
1685
1349
|
}
|
|
1686
1350
|
}
|
|
1687
|
-
// Logger.get('LogServerNode').info(this.serverNode);
|
|
1688
1351
|
this.emit('childbridge_started');
|
|
1689
1352
|
this.log.notice('Matterbridge childbridge started successfully');
|
|
1690
1353
|
}, 1000);
|
|
1691
1354
|
}
|
|
1692
|
-
/**
|
|
1693
|
-
* Starts the Matterbridge controller.
|
|
1694
|
-
*
|
|
1695
|
-
* @private
|
|
1696
|
-
* @returns {Promise<void>} A promise that resolves when the Matterbridge is started.
|
|
1697
|
-
*/
|
|
1698
1355
|
async startController() {
|
|
1699
|
-
/*
|
|
1700
|
-
if (!this.matterStorageManager) {
|
|
1701
|
-
this.log.error('No storage manager initialized');
|
|
1702
|
-
await this.cleanup('No storage manager initialized');
|
|
1703
|
-
return;
|
|
1704
|
-
}
|
|
1705
|
-
this.log.info('Creating context: mattercontrollerContext');
|
|
1706
|
-
this.controllerContext = this.matterStorageManager.createContext('mattercontrollerContext');
|
|
1707
|
-
if (!this.controllerContext) {
|
|
1708
|
-
this.log.error('No storage context mattercontrollerContext initialized');
|
|
1709
|
-
await this.cleanup('No storage context mattercontrollerContext initialized');
|
|
1710
|
-
return;
|
|
1711
|
-
}
|
|
1712
|
-
|
|
1713
|
-
this.log.debug('Starting matterbridge in mode', this.bridgeMode);
|
|
1714
|
-
this.matterServer = await this.createMatterServer(this.storageManager);
|
|
1715
|
-
this.log.info('Creating matter commissioning controller');
|
|
1716
|
-
this.commissioningController = new CommissioningController({
|
|
1717
|
-
autoConnect: false,
|
|
1718
|
-
});
|
|
1719
|
-
this.log.info('Adding matter commissioning controller to matter server');
|
|
1720
|
-
await this.matterServer.addCommissioningController(this.commissioningController);
|
|
1721
|
-
|
|
1722
|
-
this.log.info('Starting matter server');
|
|
1723
|
-
await this.matterServer.start();
|
|
1724
|
-
this.log.info('Matter server started');
|
|
1725
|
-
const commissioningOptions: ControllerCommissioningFlowOptions = {
|
|
1726
|
-
regulatoryLocation: GeneralCommissioning.RegulatoryLocationType.IndoorOutdoor,
|
|
1727
|
-
regulatoryCountryCode: 'XX',
|
|
1728
|
-
};
|
|
1729
|
-
const commissioningController = new CommissioningController({
|
|
1730
|
-
environment: {
|
|
1731
|
-
environment,
|
|
1732
|
-
id: uniqueId,
|
|
1733
|
-
},
|
|
1734
|
-
autoConnect: false, // Do not auto connect to the commissioned nodes
|
|
1735
|
-
adminFabricLabel,
|
|
1736
|
-
});
|
|
1737
|
-
|
|
1738
|
-
if (hasParameter('pairingcode')) {
|
|
1739
|
-
this.log.info('Pairing device with pairingcode:', getParameter('pairingcode'));
|
|
1740
|
-
const pairingCode = getParameter('pairingcode');
|
|
1741
|
-
const ip = this.controllerContext.has('ip') ? this.controllerContext.get<string>('ip') : undefined;
|
|
1742
|
-
const port = this.controllerContext.has('port') ? this.controllerContext.get<number>('port') : undefined;
|
|
1743
|
-
|
|
1744
|
-
let longDiscriminator, setupPin, shortDiscriminator;
|
|
1745
|
-
if (pairingCode !== undefined) {
|
|
1746
|
-
const pairingCodeCodec = ManualPairingCodeCodec.decode(pairingCode);
|
|
1747
|
-
shortDiscriminator = pairingCodeCodec.shortDiscriminator;
|
|
1748
|
-
longDiscriminator = undefined;
|
|
1749
|
-
setupPin = pairingCodeCodec.passcode;
|
|
1750
|
-
this.log.info(`Data extracted from pairing code: ${Logger.toJSON(pairingCodeCodec)}`);
|
|
1751
|
-
} else {
|
|
1752
|
-
longDiscriminator = await this.controllerContext.get('longDiscriminator', 3840);
|
|
1753
|
-
if (longDiscriminator > 4095) throw new Error('Discriminator value must be less than 4096');
|
|
1754
|
-
setupPin = this.controllerContext.get('pin', 20202021);
|
|
1755
|
-
}
|
|
1756
|
-
if ((shortDiscriminator === undefined && longDiscriminator === undefined) || setupPin === undefined) {
|
|
1757
|
-
throw new Error('Please specify the longDiscriminator of the device to commission with -longDiscriminator or provide a valid passcode with -passcode');
|
|
1758
|
-
}
|
|
1759
|
-
|
|
1760
|
-
const options = {
|
|
1761
|
-
commissioning: commissioningOptions,
|
|
1762
|
-
discovery: {
|
|
1763
|
-
knownAddress: ip !== undefined && port !== undefined ? { ip, port, type: 'udp' } : undefined,
|
|
1764
|
-
identifierData: longDiscriminator !== undefined ? { longDiscriminator } : shortDiscriminator !== undefined ? { shortDiscriminator } : {},
|
|
1765
|
-
},
|
|
1766
|
-
passcode: setupPin,
|
|
1767
|
-
} as NodeCommissioningOptions;
|
|
1768
|
-
this.log.info('Commissioning with options:', options);
|
|
1769
|
-
const nodeId = await this.commissioningController.commissionNode(options);
|
|
1770
|
-
this.log.info(`Commissioning successfully done with nodeId: ${nodeId}`);
|
|
1771
|
-
this.log.info('ActiveSessionInformation:', this.commissioningController.getActiveSessionInformation());
|
|
1772
|
-
} // (hasParameter('pairingcode'))
|
|
1773
|
-
|
|
1774
|
-
if (hasParameter('unpairall')) {
|
|
1775
|
-
this.log.info('***Commissioning controller unpairing all nodes...');
|
|
1776
|
-
const nodeIds = this.commissioningController.getCommissionedNodes();
|
|
1777
|
-
for (const nodeId of nodeIds) {
|
|
1778
|
-
this.log.info('***Commissioning controller unpairing node:', nodeId);
|
|
1779
|
-
await this.commissioningController.removeNode(nodeId);
|
|
1780
|
-
}
|
|
1781
|
-
return;
|
|
1782
|
-
}
|
|
1783
|
-
|
|
1784
|
-
if (hasParameter('discover')) {
|
|
1785
|
-
// const discover = await this.commissioningController.discoverCommissionableDevices({ productId: 0x8000, deviceType: 0xfff1 });
|
|
1786
|
-
// console.log(discover);
|
|
1787
|
-
}
|
|
1788
|
-
|
|
1789
|
-
if (!this.commissioningController.isCommissioned()) {
|
|
1790
|
-
this.log.info('***Commissioning controller is not commissioned: use matterbridge -controller -pairingcode [pairingcode] to commission a device');
|
|
1791
|
-
return;
|
|
1792
|
-
}
|
|
1793
|
-
|
|
1794
|
-
const nodeIds = this.commissioningController.getCommissionedNodes();
|
|
1795
|
-
this.log.info(`***Commissioning controller is commissioned ${this.commissioningController.isCommissioned()} and has ${nodeIds.length} nodes commisioned: `);
|
|
1796
|
-
for (const nodeId of nodeIds) {
|
|
1797
|
-
this.log.info(`***Connecting to commissioned node: ${nodeId}`);
|
|
1798
|
-
|
|
1799
|
-
const node = await this.commissioningController.connectNode(nodeId, {
|
|
1800
|
-
autoSubscribe: false,
|
|
1801
|
-
attributeChangedCallback: (peerNodeId, { path: { nodeId, clusterId, endpointId, attributeName }, value }) =>
|
|
1802
|
-
this.log.info(`***Commissioning controller attributeChangedCallback ${peerNodeId}: attribute ${nodeId}/${endpointId}/${clusterId}/${attributeName} changed to ${Logger.toJSON(value)}`),
|
|
1803
|
-
eventTriggeredCallback: (peerNodeId, { path: { nodeId, clusterId, endpointId, eventName }, events }) =>
|
|
1804
|
-
this.log.info(`***Commissioning controller eventTriggeredCallback ${peerNodeId}: Event ${nodeId}/${endpointId}/${clusterId}/${eventName} triggered with ${Logger.toJSON(events)}`),
|
|
1805
|
-
stateInformationCallback: (peerNodeId, info) => {
|
|
1806
|
-
switch (info) {
|
|
1807
|
-
case NodeStateInformation.Connected:
|
|
1808
|
-
this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} connected`);
|
|
1809
|
-
break;
|
|
1810
|
-
case NodeStateInformation.Disconnected:
|
|
1811
|
-
this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} disconnected`);
|
|
1812
|
-
break;
|
|
1813
|
-
case NodeStateInformation.Reconnecting:
|
|
1814
|
-
this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} reconnecting`);
|
|
1815
|
-
break;
|
|
1816
|
-
case NodeStateInformation.WaitingForDeviceDiscovery:
|
|
1817
|
-
this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} waiting for device discovery`);
|
|
1818
|
-
break;
|
|
1819
|
-
case NodeStateInformation.StructureChanged:
|
|
1820
|
-
this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} structure changed`);
|
|
1821
|
-
break;
|
|
1822
|
-
case NodeStateInformation.Decommissioned:
|
|
1823
|
-
this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} decommissioned`);
|
|
1824
|
-
break;
|
|
1825
|
-
default:
|
|
1826
|
-
this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} NodeStateInformation.${info}`);
|
|
1827
|
-
break;
|
|
1828
|
-
}
|
|
1829
|
-
},
|
|
1830
|
-
});
|
|
1831
|
-
|
|
1832
|
-
node.logStructure();
|
|
1833
|
-
|
|
1834
|
-
// Get the interaction client
|
|
1835
|
-
this.log.info('Getting the interaction client');
|
|
1836
|
-
const interactionClient = await node.getInteractionClient();
|
|
1837
|
-
let cluster;
|
|
1838
|
-
let attributes;
|
|
1839
|
-
|
|
1840
|
-
// Log BasicInformationCluster
|
|
1841
|
-
cluster = BasicInformationCluster;
|
|
1842
|
-
attributes = await interactionClient.getMultipleAttributes({
|
|
1843
|
-
attributes: [{ clusterId: cluster.id }],
|
|
1844
|
-
});
|
|
1845
|
-
if (attributes.length > 0) this.log.info(`Cluster: ${idn}${cluster.name}${rs}${nf} attributes:`);
|
|
1846
|
-
attributes.forEach((attribute) => {
|
|
1847
|
-
this.log.info(
|
|
1848
|
-
`- 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}`,
|
|
1849
|
-
);
|
|
1850
|
-
});
|
|
1851
|
-
|
|
1852
|
-
// Log PowerSourceCluster
|
|
1853
|
-
cluster = PowerSourceCluster;
|
|
1854
|
-
attributes = await interactionClient.getMultipleAttributes({
|
|
1855
|
-
attributes: [{ clusterId: cluster.id }],
|
|
1856
|
-
});
|
|
1857
|
-
if (attributes.length > 0) this.log.info(`Cluster: ${idn}${cluster.name}${rs}${nf} attributes:`);
|
|
1858
|
-
attributes.forEach((attribute) => {
|
|
1859
|
-
this.log.info(
|
|
1860
|
-
`- 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}`,
|
|
1861
|
-
);
|
|
1862
|
-
});
|
|
1863
|
-
|
|
1864
|
-
// Log ThreadNetworkDiagnostics
|
|
1865
|
-
cluster = ThreadNetworkDiagnosticsCluster;
|
|
1866
|
-
attributes = await interactionClient.getMultipleAttributes({
|
|
1867
|
-
attributes: [{ clusterId: cluster.id }],
|
|
1868
|
-
});
|
|
1869
|
-
if (attributes.length > 0) this.log.info(`Cluster: ${idn}${cluster.name}${rs}${nf} attributes:`);
|
|
1870
|
-
attributes.forEach((attribute) => {
|
|
1871
|
-
this.log.info(
|
|
1872
|
-
`- 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}`,
|
|
1873
|
-
);
|
|
1874
|
-
});
|
|
1875
|
-
|
|
1876
|
-
// Log SwitchCluster
|
|
1877
|
-
cluster = SwitchCluster;
|
|
1878
|
-
attributes = await interactionClient.getMultipleAttributes({
|
|
1879
|
-
attributes: [{ clusterId: cluster.id }],
|
|
1880
|
-
});
|
|
1881
|
-
if (attributes.length > 0) this.log.info(`Cluster: ${idn}${cluster.name}${rs}${nf} attributes:`);
|
|
1882
|
-
attributes.forEach((attribute) => {
|
|
1883
|
-
this.log.info(
|
|
1884
|
-
`- 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}`,
|
|
1885
|
-
);
|
|
1886
|
-
});
|
|
1887
|
-
|
|
1888
|
-
this.log.info('Subscribing to all attributes and events');
|
|
1889
|
-
await node.subscribeAllAttributesAndEvents({
|
|
1890
|
-
ignoreInitialTriggers: false,
|
|
1891
|
-
attributeChangedCallback: ({ path: { nodeId, clusterId, endpointId, attributeName }, version, value }) =>
|
|
1892
|
-
this.log.info(
|
|
1893
|
-
`***${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}`,
|
|
1894
|
-
),
|
|
1895
|
-
eventTriggeredCallback: ({ path: { nodeId, clusterId, endpointId, eventName }, events }) => {
|
|
1896
|
-
this.log.info(
|
|
1897
|
-
`***${db}Commissioning controller eventTriggeredCallback: event ${BLUE}${nodeId}${db}/${or}${endpointId}${db}/${hk}${getClusterNameById(clusterId)}${db}/${zb}${eventName}${db} triggered with ${debugStringify(events ?? { none: true })}`,
|
|
1898
|
-
);
|
|
1899
|
-
},
|
|
1900
|
-
});
|
|
1901
|
-
this.log.info('Subscribed to all attributes and events');
|
|
1902
|
-
}
|
|
1903
|
-
*/
|
|
1904
1356
|
}
|
|
1905
|
-
/** */
|
|
1906
|
-
/** Matter.js methods */
|
|
1907
|
-
/** */
|
|
1908
|
-
/**
|
|
1909
|
-
* Starts the matter storage with name Matterbridge, create the matterbridge context and performs a backup.
|
|
1910
|
-
*
|
|
1911
|
-
* @returns {Promise<void>} - A promise that resolves when the storage is started.
|
|
1912
|
-
*/
|
|
1913
1357
|
async startMatterStorage() {
|
|
1914
|
-
// Setup Matter storage
|
|
1915
1358
|
this.log.info(`Starting matter node storage...`);
|
|
1916
1359
|
this.matterStorageService = this.environment.get(StorageService);
|
|
1917
1360
|
this.log.info(`Matter node storage service created: ${this.matterStorageService.location}`);
|
|
@@ -1920,17 +1363,8 @@ export class Matterbridge extends EventEmitter {
|
|
|
1920
1363
|
this.matterbridgeContext = await this.createServerNodeContext('Matterbridge', 'Matterbridge', bridge.code, this.aggregatorVendorId, this.aggregatorVendorName, this.aggregatorProductId, this.aggregatorProductName);
|
|
1921
1364
|
this.matterbridgeInformation.matterbridgeSerialNumber = await this.matterbridgeContext.get('serialNumber', '');
|
|
1922
1365
|
this.log.info('Matter node storage started');
|
|
1923
|
-
// Backup matter storage since it is created/opened correctly
|
|
1924
1366
|
await this.backupMatterStorage(path.join(this.matterbridgeDirectory, this.matterStorageName), path.join(this.matterbridgeDirectory, this.matterStorageName + '.backup'));
|
|
1925
1367
|
}
|
|
1926
|
-
/**
|
|
1927
|
-
* Makes a backup copy of the specified matter storage directory.
|
|
1928
|
-
*
|
|
1929
|
-
* @param {string} storageName - The name of the storage directory to be backed up.
|
|
1930
|
-
* @param {string} backupName - The name of the backup directory to be created.
|
|
1931
|
-
* @private
|
|
1932
|
-
* @returns {Promise<void>} A promise that resolves when the has been done.
|
|
1933
|
-
*/
|
|
1934
1368
|
async backupMatterStorage(storageName, backupName) {
|
|
1935
1369
|
this.log.info('Creating matter node storage backup...');
|
|
1936
1370
|
try {
|
|
@@ -1941,11 +1375,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1941
1375
|
this.log.error(`Error creating matter node storage backup from ${storageName} to ${backupName}:`, error);
|
|
1942
1376
|
}
|
|
1943
1377
|
}
|
|
1944
|
-
/**
|
|
1945
|
-
* Stops the matter storage.
|
|
1946
|
-
*
|
|
1947
|
-
* @returns {Promise<void>} A promise that resolves when the storage is stopped.
|
|
1948
|
-
*/
|
|
1949
1378
|
async stopMatterStorage() {
|
|
1950
1379
|
this.log.info('Closing matter node storage...');
|
|
1951
1380
|
await this.matterStorageManager?.close();
|
|
@@ -1954,19 +1383,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1954
1383
|
this.matterbridgeContext = undefined;
|
|
1955
1384
|
this.log.info('Matter node storage closed');
|
|
1956
1385
|
}
|
|
1957
|
-
/**
|
|
1958
|
-
* Creates a server node storage context.
|
|
1959
|
-
*
|
|
1960
|
-
* @param {string} pluginName - The name of the plugin.
|
|
1961
|
-
* @param {string} deviceName - The name of the device.
|
|
1962
|
-
* @param {DeviceTypeId} deviceType - The device type of the device.
|
|
1963
|
-
* @param {number} vendorId - The vendor ID.
|
|
1964
|
-
* @param {string} vendorName - The vendor name.
|
|
1965
|
-
* @param {number} productId - The product ID.
|
|
1966
|
-
* @param {string} productName - The product name.
|
|
1967
|
-
* @param {string} [serialNumber] - The serial number of the device (optional).
|
|
1968
|
-
* @returns {Promise<StorageContext>} The storage context for the commissioning server.
|
|
1969
|
-
*/
|
|
1970
1386
|
async createServerNodeContext(pluginName, deviceName, deviceType, vendorId, vendorName, productId, productName, serialNumber) {
|
|
1971
1387
|
const { randomBytes } = await import('node:crypto');
|
|
1972
1388
|
if (!this.matterStorageService)
|
|
@@ -2000,15 +1416,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2000
1416
|
this.log.debug(`- hardwareVersion: ${await storageContext.get('hardwareVersion')} hardwareVersionString: ${await storageContext.get('hardwareVersionString')}`);
|
|
2001
1417
|
return storageContext;
|
|
2002
1418
|
}
|
|
2003
|
-
/**
|
|
2004
|
-
* Creates a server node.
|
|
2005
|
-
*
|
|
2006
|
-
* @param {StorageContext} storageContext - The storage context for the server node.
|
|
2007
|
-
* @param {number} [port] - The port number for the server node. Defaults to 5540.
|
|
2008
|
-
* @param {number} [passcode] - The passcode for the server node. Defaults to 20242025.
|
|
2009
|
-
* @param {number} [discriminator] - The discriminator for the server node. Defaults to 3850.
|
|
2010
|
-
* @returns {Promise<ServerNode<ServerNode.RootEndpoint>>} A promise that resolves to the created server node.
|
|
2011
|
-
*/
|
|
2012
1419
|
async createServerNode(storageContext, port = 5540, passcode = 20242025, discriminator = 3850) {
|
|
2013
1420
|
const storeId = await storageContext.get('storeId');
|
|
2014
1421
|
this.log.notice(`Creating server node for ${storeId} on port ${port} with passcode ${passcode} and discriminator ${discriminator}...`);
|
|
@@ -2018,37 +1425,24 @@ export class Matterbridge extends EventEmitter {
|
|
|
2018
1425
|
this.log.debug(`- uniqueId: ${await storageContext.get('uniqueId')}`);
|
|
2019
1426
|
this.log.debug(`- softwareVersion: ${await storageContext.get('softwareVersion')} softwareVersionString: ${await storageContext.get('softwareVersionString')}`);
|
|
2020
1427
|
this.log.debug(`- hardwareVersion: ${await storageContext.get('hardwareVersion')} hardwareVersionString: ${await storageContext.get('hardwareVersionString')}`);
|
|
2021
|
-
/**
|
|
2022
|
-
* Create a Matter ServerNode, which contains the Root Endpoint and all relevant data and configuration
|
|
2023
|
-
*/
|
|
2024
1428
|
const serverNode = await ServerNode.create({
|
|
2025
|
-
// Required: Give the Node a unique ID which is used to store the state of this node
|
|
2026
1429
|
id: storeId,
|
|
2027
|
-
// Provide Network relevant configuration like the port
|
|
2028
|
-
// Optional when operating only one device on a host, Default port is 5540
|
|
2029
1430
|
network: {
|
|
2030
1431
|
listeningAddressIpv4: this.ipv4address,
|
|
2031
1432
|
listeningAddressIpv6: this.ipv6address,
|
|
2032
1433
|
port,
|
|
2033
1434
|
},
|
|
2034
|
-
// Provide the certificate for the device
|
|
2035
1435
|
operationalCredentials: {
|
|
2036
1436
|
certification: this.certification,
|
|
2037
1437
|
},
|
|
2038
|
-
// Provide Commissioning relevant settings
|
|
2039
|
-
// Optional for development/testing purposes
|
|
2040
1438
|
commissioning: {
|
|
2041
1439
|
passcode,
|
|
2042
1440
|
discriminator,
|
|
2043
1441
|
},
|
|
2044
|
-
// Provide Node announcement settings
|
|
2045
|
-
// Optional: If Ommitted some development defaults are used
|
|
2046
1442
|
productDescription: {
|
|
2047
1443
|
name: await storageContext.get('deviceName'),
|
|
2048
1444
|
deviceType: DeviceTypeId(await storageContext.get('deviceType')),
|
|
2049
1445
|
},
|
|
2050
|
-
// Provide defaults for the BasicInformation cluster on the Root endpoint
|
|
2051
|
-
// Optional: If Omitted some development defaults are used
|
|
2052
1446
|
basicInformation: {
|
|
2053
1447
|
vendorId: VendorId(await storageContext.get('vendorId')),
|
|
2054
1448
|
vendorName: await storageContext.get('vendorName'),
|
|
@@ -2065,20 +1459,14 @@ export class Matterbridge extends EventEmitter {
|
|
|
2065
1459
|
reachable: true,
|
|
2066
1460
|
},
|
|
2067
1461
|
});
|
|
2068
|
-
/**
|
|
2069
|
-
* This event is triggered when the device is initially commissioned successfully.
|
|
2070
|
-
* This means: It is added to the first fabric.
|
|
2071
|
-
*/
|
|
2072
1462
|
serverNode.lifecycle.commissioned.on(() => {
|
|
2073
1463
|
this.log.notice(`Server node for ${storeId} was initially commissioned successfully!`);
|
|
2074
1464
|
clearTimeout(this.endAdvertiseTimeout);
|
|
2075
1465
|
});
|
|
2076
|
-
/** This event is triggered when all fabrics are removed from the device, usually it also does a factory reset then. */
|
|
2077
1466
|
serverNode.lifecycle.decommissioned.on(() => {
|
|
2078
1467
|
this.log.notice(`Server node for ${storeId} was fully decommissioned successfully!`);
|
|
2079
1468
|
clearTimeout(this.endAdvertiseTimeout);
|
|
2080
1469
|
});
|
|
2081
|
-
/** This event is triggered when the device went online. This means that it is discoverable in the network. */
|
|
2082
1470
|
serverNode.lifecycle.online.on(async () => {
|
|
2083
1471
|
this.log.notice(`Server node for ${storeId} is online`);
|
|
2084
1472
|
if (!serverNode.lifecycle.isCommissioned) {
|
|
@@ -2086,7 +1474,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2086
1474
|
const { qrPairingCode, manualPairingCode } = serverNode.state.commissioning.pairingCodes;
|
|
2087
1475
|
this.log.notice(`QR Code URL: https://project-chip.github.io/connectedhomeip/qrcode.html?data=${qrPairingCode}`);
|
|
2088
1476
|
this.log.notice(`Manual pairing code: ${manualPairingCode}`);
|
|
2089
|
-
// Set a timeout to show that advertising stops after 15 minutes if not commissioned
|
|
2090
1477
|
this.startEndAdvertiseTimer(serverNode);
|
|
2091
1478
|
}
|
|
2092
1479
|
else {
|
|
@@ -2097,19 +1484,14 @@ export class Matterbridge extends EventEmitter {
|
|
|
2097
1484
|
this.frontend.wssSendSnackbarMessage(`${storeId} is online`, 5, 'success');
|
|
2098
1485
|
this.emit('online', storeId);
|
|
2099
1486
|
});
|
|
2100
|
-
/** This event is triggered when the device went offline. it is not longer discoverable or connectable in the network. */
|
|
2101
1487
|
serverNode.lifecycle.offline.on(() => {
|
|
2102
1488
|
this.log.notice(`Server node for ${storeId} is offline`);
|
|
2103
|
-
this.matterbridgeInformation.matterbridgeEndAdvertise = true;
|
|
1489
|
+
this.matterbridgeInformation.matterbridgeEndAdvertise = true;
|
|
2104
1490
|
this.frontend.wssSendRefreshRequired('plugins');
|
|
2105
1491
|
this.frontend.wssSendRefreshRequired('settings');
|
|
2106
1492
|
this.frontend.wssSendSnackbarMessage(`${storeId} is offline`, 5, 'warning');
|
|
2107
1493
|
this.emit('offline', storeId);
|
|
2108
1494
|
});
|
|
2109
|
-
/**
|
|
2110
|
-
* This event is triggered when a fabric is added, removed or updated on the device. Use this if more granular
|
|
2111
|
-
* information is needed.
|
|
2112
|
-
*/
|
|
2113
1495
|
serverNode.events.commissioning.fabricsChanged.on((fabricIndex, fabricAction) => {
|
|
2114
1496
|
let action = '';
|
|
2115
1497
|
switch (fabricAction) {
|
|
@@ -2126,22 +1508,14 @@ export class Matterbridge extends EventEmitter {
|
|
|
2126
1508
|
this.log.notice(`Commissioned fabric index ${fabricIndex} ${action} on server node for ${storeId}: ${debugStringify(serverNode.state.commissioning.fabrics[fabricIndex])}`);
|
|
2127
1509
|
this.frontend.wssSendRefreshRequired('fabrics');
|
|
2128
1510
|
});
|
|
2129
|
-
/**
|
|
2130
|
-
* This event is triggered when an operative new session was opened by a Controller.
|
|
2131
|
-
* It is not triggered for the initial commissioning process, just afterwards for real connections.
|
|
2132
|
-
*/
|
|
2133
1511
|
serverNode.events.sessions.opened.on((session) => {
|
|
2134
1512
|
this.log.notice(`Session opened on server node for ${storeId}: ${debugStringify(session)}`);
|
|
2135
1513
|
this.frontend.wssSendRefreshRequired('sessions');
|
|
2136
1514
|
});
|
|
2137
|
-
/**
|
|
2138
|
-
* This event is triggered when an operative session is closed by a Controller or because the Device goes offline.
|
|
2139
|
-
*/
|
|
2140
1515
|
serverNode.events.sessions.closed.on((session) => {
|
|
2141
1516
|
this.log.notice(`Session closed on server node for ${storeId}: ${debugStringify(session)}`);
|
|
2142
1517
|
this.frontend.wssSendRefreshRequired('sessions');
|
|
2143
1518
|
});
|
|
2144
|
-
/** This event is triggered when a subscription gets added or removed on an operative session. */
|
|
2145
1519
|
serverNode.events.sessions.subscriptionsChanged.on((session) => {
|
|
2146
1520
|
this.log.notice(`Session subscriptions changed on server node for ${storeId}: ${debugStringify(session)}`);
|
|
2147
1521
|
this.frontend.wssSendRefreshRequired('sessions');
|
|
@@ -2149,11 +1523,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2149
1523
|
this.log.info(`Created server node for ${storeId}`);
|
|
2150
1524
|
return serverNode;
|
|
2151
1525
|
}
|
|
2152
|
-
/**
|
|
2153
|
-
* Starts the 15 minutes timer to advice that advertising for the specified server node is ended.
|
|
2154
|
-
*
|
|
2155
|
-
* @param {ServerNode} [matterServerNode] - The server node to start.
|
|
2156
|
-
*/
|
|
2157
1526
|
startEndAdvertiseTimer(matterServerNode) {
|
|
2158
1527
|
if (this.endAdvertiseTimeout) {
|
|
2159
1528
|
this.log.debug(`Clear ${matterServerNode.id} server node end advertise timer`);
|
|
@@ -2172,25 +1541,12 @@ export class Matterbridge extends EventEmitter {
|
|
|
2172
1541
|
this.log.notice(`Advertising on server node for ${matterServerNode.id} stopped. Restart to commission.`);
|
|
2173
1542
|
}, 15 * 60 * 1000).unref();
|
|
2174
1543
|
}
|
|
2175
|
-
/**
|
|
2176
|
-
* Starts the specified server node.
|
|
2177
|
-
*
|
|
2178
|
-
* @param {ServerNode} [matterServerNode] - The server node to start.
|
|
2179
|
-
* @returns {Promise<void>} A promise that resolves when the server node has started.
|
|
2180
|
-
*/
|
|
2181
1544
|
async startServerNode(matterServerNode) {
|
|
2182
1545
|
if (!matterServerNode)
|
|
2183
1546
|
return;
|
|
2184
1547
|
this.log.notice(`Starting ${matterServerNode.id} server node`);
|
|
2185
1548
|
await matterServerNode.start();
|
|
2186
1549
|
}
|
|
2187
|
-
/**
|
|
2188
|
-
* Stops the specified server node.
|
|
2189
|
-
*
|
|
2190
|
-
* @param {ServerNode} matterServerNode - The server node to stop.
|
|
2191
|
-
* @param {number} [timeout] - The timeout in milliseconds for stopping the server node. Defaults to 30 seconds.
|
|
2192
|
-
* @returns {Promise<void>} A promise that resolves when the server node has stopped.
|
|
2193
|
-
*/
|
|
2194
1550
|
async stopServerNode(matterServerNode, timeout = 30000) {
|
|
2195
1551
|
if (!matterServerNode)
|
|
2196
1552
|
return;
|
|
@@ -2203,12 +1559,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2203
1559
|
this.log.error(`Failed to close ${matterServerNode.id} server node: ${error instanceof Error ? error.message : error}`);
|
|
2204
1560
|
}
|
|
2205
1561
|
}
|
|
2206
|
-
/**
|
|
2207
|
-
* Advertises the specified server node.
|
|
2208
|
-
*
|
|
2209
|
-
* @param {ServerNode} [matterServerNode] - The server node to advertise.
|
|
2210
|
-
* @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.
|
|
2211
|
-
*/
|
|
2212
1562
|
async advertiseServerNode(matterServerNode) {
|
|
2213
1563
|
if (matterServerNode) {
|
|
2214
1564
|
await matterServerNode.env.get(DeviceCommissioner)?.allowBasicCommissioning();
|
|
@@ -2217,39 +1567,19 @@ export class Matterbridge extends EventEmitter {
|
|
|
2217
1567
|
return { qrPairingCode, manualPairingCode };
|
|
2218
1568
|
}
|
|
2219
1569
|
}
|
|
2220
|
-
/**
|
|
2221
|
-
* Stop advertise the specified server node.
|
|
2222
|
-
*
|
|
2223
|
-
* @param {ServerNode} [matterServerNode] - The server node to advertise.
|
|
2224
|
-
* @returns {Promise<void>} A promise that resolves when the server node has stopped advertising.
|
|
2225
|
-
*/
|
|
2226
1570
|
async stopAdvertiseServerNode(matterServerNode) {
|
|
2227
1571
|
if (matterServerNode && matterServerNode.lifecycle.isOnline) {
|
|
2228
1572
|
await matterServerNode.env.get(DeviceCommissioner)?.endCommissioning();
|
|
2229
1573
|
this.log.notice(`Stopped advertising for ${matterServerNode.id}`);
|
|
2230
1574
|
}
|
|
2231
1575
|
}
|
|
2232
|
-
/**
|
|
2233
|
-
* Creates an aggregator node with the specified storage context.
|
|
2234
|
-
*
|
|
2235
|
-
* @param {StorageContext} storageContext - The storage context for the aggregator node.
|
|
2236
|
-
* @returns {Promise<Endpoint<AggregatorEndpoint>>} A promise that resolves to the created aggregator node.
|
|
2237
|
-
*/
|
|
2238
1576
|
async createAggregatorNode(storageContext) {
|
|
2239
1577
|
this.log.notice(`Creating ${await storageContext.get('storeId')} aggregator...`);
|
|
2240
1578
|
const aggregatorNode = new Endpoint(AggregatorEndpoint, { id: `${await storageContext.get('storeId')}` });
|
|
2241
1579
|
this.log.info(`Created ${await storageContext.get('storeId')} aggregator`);
|
|
2242
1580
|
return aggregatorNode;
|
|
2243
1581
|
}
|
|
2244
|
-
/**
|
|
2245
|
-
* Adds a MatterbridgeEndpoint to the specified plugin.
|
|
2246
|
-
*
|
|
2247
|
-
* @param {string} pluginName - The name of the plugin.
|
|
2248
|
-
* @param {MatterbridgeEndpoint} device - The device to add as a bridged endpoint.
|
|
2249
|
-
* @returns {Promise<void>} A promise that resolves when the bridged endpoint has been added.
|
|
2250
|
-
*/
|
|
2251
1582
|
async addBridgedEndpoint(pluginName, device) {
|
|
2252
|
-
// Check if the plugin is registered
|
|
2253
1583
|
const plugin = this.plugins.get(pluginName);
|
|
2254
1584
|
if (!plugin) {
|
|
2255
1585
|
this.log.error(`Error adding bridged endpoint ${dev}${device.deviceName}${er} (${zb}${device.id}${er}) plugin ${plg}${pluginName}${er} not found`);
|
|
@@ -2269,7 +1599,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2269
1599
|
}
|
|
2270
1600
|
else if (this.bridgeMode === 'bridge') {
|
|
2271
1601
|
if (device.mode === 'matter') {
|
|
2272
|
-
// Register and add the device to the matterbridge server node
|
|
2273
1602
|
this.log.debug(`Adding matter endpoint ${plg}${pluginName}${db}:${dev}${device.deviceName}${db} to Matterbridge server node...`);
|
|
2274
1603
|
if (!this.serverNode) {
|
|
2275
1604
|
this.log.error('Server node not found for Matterbridge');
|
|
@@ -2286,7 +1615,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2286
1615
|
}
|
|
2287
1616
|
}
|
|
2288
1617
|
else {
|
|
2289
|
-
// Register and add the device to the matterbridge aggregator node
|
|
2290
1618
|
this.log.debug(`Adding bridged endpoint ${plg}${pluginName}${db}:${dev}${device.deviceName}${db} to Matterbridge aggregator node`);
|
|
2291
1619
|
if (!this.aggregatorNode) {
|
|
2292
1620
|
this.log.error('Aggregator node not found for Matterbridge');
|
|
@@ -2304,7 +1632,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2304
1632
|
}
|
|
2305
1633
|
}
|
|
2306
1634
|
else if (this.bridgeMode === 'childbridge') {
|
|
2307
|
-
// Register and add the device to the plugin server node
|
|
2308
1635
|
if (plugin.type === 'AccessoryPlatform') {
|
|
2309
1636
|
try {
|
|
2310
1637
|
this.log.debug(`Creating endpoint ${dev}${device.deviceName}${db} for AccessoryPlatform plugin ${plg}${plugin.name}${db} server node`);
|
|
@@ -2328,12 +1655,10 @@ export class Matterbridge extends EventEmitter {
|
|
|
2328
1655
|
return;
|
|
2329
1656
|
}
|
|
2330
1657
|
}
|
|
2331
|
-
// Register and add the device to the plugin aggregator node
|
|
2332
1658
|
if (plugin.type === 'DynamicPlatform') {
|
|
2333
1659
|
try {
|
|
2334
1660
|
this.log.debug(`Adding bridged endpoint ${dev}${device.deviceName}${db} for DynamicPlatform plugin ${plg}${plugin.name}${db} aggregator node`);
|
|
2335
1661
|
await this.createDynamicPlugin(plugin);
|
|
2336
|
-
// Fast plugins can add another device before the server node is ready, so we wait for the server node to be ready
|
|
2337
1662
|
await waiter(`createDynamicPlugin(${plugin.name})`, () => plugin.serverNode?.hasParts === true);
|
|
2338
1663
|
if (!plugin.aggregatorNode) {
|
|
2339
1664
|
this.log.error(`Aggregator node not found for plugin ${plg}${plugin.name}${er}`);
|
|
@@ -2356,28 +1681,17 @@ export class Matterbridge extends EventEmitter {
|
|
|
2356
1681
|
plugin.registeredDevices++;
|
|
2357
1682
|
if (plugin.addedDevices !== undefined)
|
|
2358
1683
|
plugin.addedDevices++;
|
|
2359
|
-
// Add the device to the DeviceManager
|
|
2360
1684
|
this.devices.set(device);
|
|
2361
|
-
// Subscribe to the reachable$Changed event
|
|
2362
1685
|
await this.subscribeAttributeChanged(plugin, device);
|
|
2363
1686
|
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}`);
|
|
2364
1687
|
}
|
|
2365
|
-
/**
|
|
2366
|
-
* Removes a MatterbridgeEndpoint from the specified plugin.
|
|
2367
|
-
*
|
|
2368
|
-
* @param {string} pluginName - The name of the plugin.
|
|
2369
|
-
* @param {MatterbridgeEndpoint} device - The device to remove as a bridged endpoint.
|
|
2370
|
-
* @returns {Promise<void>} A promise that resolves when the bridged endpoint has been removed.
|
|
2371
|
-
*/
|
|
2372
1688
|
async removeBridgedEndpoint(pluginName, device) {
|
|
2373
1689
|
this.log.debug(`Removing bridged endpoint ${plg}${pluginName}${db}:${dev}${device.deviceName}${db} (${zb}${device.name}${db}) for plugin ${plg}${pluginName}${db}`);
|
|
2374
|
-
// Check if the plugin is registered
|
|
2375
1690
|
const plugin = this.plugins.get(pluginName);
|
|
2376
1691
|
if (!plugin) {
|
|
2377
1692
|
this.log.error(`Error removing bridged endpoint ${dev}${device.deviceName}${er} (${zb}${device.name}${er}) for plugin ${plg}${pluginName}${er}: plugin not found`);
|
|
2378
1693
|
return;
|
|
2379
1694
|
}
|
|
2380
|
-
// Register and add the device to the matterbridge aggregator node
|
|
2381
1695
|
if (this.bridgeMode === 'bridge') {
|
|
2382
1696
|
if (!this.aggregatorNode) {
|
|
2383
1697
|
this.log.error(`Error removing bridged endpoint ${dev}${device.deviceName}${er} (${zb}${device.name}${er}) for plugin ${plg}${pluginName}${er}: aggregator node not found`);
|
|
@@ -2392,7 +1706,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2392
1706
|
}
|
|
2393
1707
|
else if (this.bridgeMode === 'childbridge') {
|
|
2394
1708
|
if (plugin.type === 'AccessoryPlatform') {
|
|
2395
|
-
// Nothing to do here since the server node has no aggregator node but only the device itself
|
|
2396
1709
|
}
|
|
2397
1710
|
else if (plugin.type === 'DynamicPlatform') {
|
|
2398
1711
|
if (!plugin.aggregatorNode) {
|
|
@@ -2407,21 +1720,8 @@ export class Matterbridge extends EventEmitter {
|
|
|
2407
1720
|
if (plugin.addedDevices !== undefined)
|
|
2408
1721
|
plugin.addedDevices--;
|
|
2409
1722
|
}
|
|
2410
|
-
// Remove the device from the DeviceManager
|
|
2411
1723
|
this.devices.remove(device);
|
|
2412
1724
|
}
|
|
2413
|
-
/**
|
|
2414
|
-
* Removes all bridged endpoints from the specified plugin.
|
|
2415
|
-
*
|
|
2416
|
-
* @param {string} pluginName - The name of the plugin.
|
|
2417
|
-
* @param {number} [delay] - The delay in milliseconds between removing each bridged endpoint (default: 0).
|
|
2418
|
-
* @returns {Promise<void>} A promise that resolves when all bridged endpoints have been removed.
|
|
2419
|
-
*
|
|
2420
|
-
* @remarks
|
|
2421
|
-
* This method iterates through all devices in the DeviceManager and removes each bridged endpoint associated with the specified plugin.
|
|
2422
|
-
* It also applies a delay between each removal if specified.
|
|
2423
|
-
* The delay is useful to allow the controllers to receive a single subscription for each device removed.
|
|
2424
|
-
*/
|
|
2425
1725
|
async removeAllBridgedEndpoints(pluginName, delay = 0) {
|
|
2426
1726
|
this.log.debug(`Removing all bridged endpoints for plugin ${plg}${pluginName}${db}${delay > 0 ? ` with delay ${delay} ms` : ''}`);
|
|
2427
1727
|
for (const device of this.devices.array().filter((device) => device.plugin === pluginName)) {
|
|
@@ -2432,15 +1732,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2432
1732
|
if (delay > 0)
|
|
2433
1733
|
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
2434
1734
|
}
|
|
2435
|
-
/**
|
|
2436
|
-
* Subscribes to the attribute change event for the given device and plugin.
|
|
2437
|
-
* Specifically, it listens for changes in the 'reachable' attribute of the
|
|
2438
|
-
* BridgedDeviceBasicInformationServer cluster server of the bridged device or BasicInformationServer cluster server of server node.
|
|
2439
|
-
*
|
|
2440
|
-
* @param {RegisteredPlugin} plugin - The plugin associated with the device.
|
|
2441
|
-
* @param {MatterbridgeEndpoint} device - The device to subscribe to attribute changes for.
|
|
2442
|
-
* @returns {Promise<void>} A promise that resolves when the subscription is set up.
|
|
2443
|
-
*/
|
|
2444
1735
|
async subscribeAttributeChanged(plugin, device) {
|
|
2445
1736
|
this.log.info(`Subscribing attributes for endpoint ${dev}${device.deviceName}${nf} (${dev}${device.id}${nf}) plugin ${plg}${plugin.name}${nf}`);
|
|
2446
1737
|
if (this.bridgeMode === 'childbridge' && plugin.type === 'AccessoryPlatform' && plugin.serverNode) {
|
|
@@ -2456,12 +1747,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2456
1747
|
});
|
|
2457
1748
|
}
|
|
2458
1749
|
}
|
|
2459
|
-
/**
|
|
2460
|
-
* Sanitizes the fabric information by converting bigint properties to strings because `res.json` doesn't support bigint.
|
|
2461
|
-
*
|
|
2462
|
-
* @param {ExposedFabricInformation[]} fabricInfo - The array of exposed fabric information objects.
|
|
2463
|
-
* @returns {SanitizedExposedFabricInformation[]} An array of sanitized exposed fabric information objects.
|
|
2464
|
-
*/
|
|
2465
1750
|
sanitizeFabricInformations(fabricInfo) {
|
|
2466
1751
|
return fabricInfo.map((info) => {
|
|
2467
1752
|
return {
|
|
@@ -2475,12 +1760,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2475
1760
|
};
|
|
2476
1761
|
});
|
|
2477
1762
|
}
|
|
2478
|
-
/**
|
|
2479
|
-
* Sanitizes the session information by converting bigint properties to strings because `res.json` doesn't support bigint.
|
|
2480
|
-
*
|
|
2481
|
-
* @param {SessionsBehavior.Session[]} sessions - The array of session information objects.
|
|
2482
|
-
* @returns {SanitizedSession[]} An array of sanitized session information objects.
|
|
2483
|
-
*/
|
|
2484
1763
|
sanitizeSessionInformation(sessions) {
|
|
2485
1764
|
return sessions
|
|
2486
1765
|
.filter((session) => session.isPeerActive)
|
|
@@ -2507,21 +1786,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
2507
1786
|
};
|
|
2508
1787
|
});
|
|
2509
1788
|
}
|
|
2510
|
-
/**
|
|
2511
|
-
* Sets the reachability of the specified aggregator node bridged devices and trigger.
|
|
2512
|
-
*
|
|
2513
|
-
* @param {Endpoint<AggregatorEndpoint>} aggregatorNode - The aggregator node to set the reachability for.
|
|
2514
|
-
* @param {boolean} reachable - A boolean indicating the reachability status to set.
|
|
2515
|
-
*/
|
|
2516
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
2517
1789
|
async setAggregatorReachability(aggregatorNode, reachable) {
|
|
2518
|
-
/*
|
|
2519
|
-
for (const child of aggregatorNode.parts) {
|
|
2520
|
-
this.log.debug(`Setting reachability of ${(child as unknown as MatterbridgeEndpoint)?.deviceName} to ${reachable}`);
|
|
2521
|
-
await child.setStateOf(BridgedDeviceBasicInformationServer, { reachable });
|
|
2522
|
-
child.act((agent) => child.eventsOf(BridgedDeviceBasicInformationServer).reachableChanged.emit({ reachableNewValue: true }, agent.context));
|
|
2523
|
-
}
|
|
2524
|
-
*/
|
|
2525
1790
|
}
|
|
2526
1791
|
getVendorIdName = (vendorId) => {
|
|
2527
1792
|
if (!vendorId)
|
|
@@ -2561,11 +1826,10 @@ export class Matterbridge extends EventEmitter {
|
|
|
2561
1826
|
case 0x1488:
|
|
2562
1827
|
vendorName = '(ShortcutLabsFlic)';
|
|
2563
1828
|
break;
|
|
2564
|
-
case 65521:
|
|
1829
|
+
case 65521:
|
|
2565
1830
|
vendorName = '(MatterTest)';
|
|
2566
1831
|
break;
|
|
2567
1832
|
}
|
|
2568
1833
|
return vendorName;
|
|
2569
1834
|
};
|
|
2570
1835
|
}
|
|
2571
|
-
//# sourceMappingURL=matterbridge.js.map
|