matterbridge 3.1.7 → 3.1.8-dev-20250725-d6e57e3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +15 -0
- package/dist/cli.js +2 -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/dishwasher.js +3 -78
- package/dist/devices/evse.js +10 -74
- package/dist/devices/export.js +0 -2
- package/dist/devices/extractorHood.js +0 -42
- 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 +7 -93
- package/dist/devices/solarPower.js +0 -38
- package/dist/devices/waterHeater.js +2 -82
- package/dist/dgram/coap.js +13 -126
- package/dist/dgram/dgram.js +2 -113
- package/dist/dgram/mb_coap.js +3 -41
- package/dist/dgram/mb_mdns.js +13 -51
- package/dist/dgram/mdns.js +137 -298
- package/dist/dgram/multicast.js +1 -60
- package/dist/dgram/unicast.js +0 -54
- package/dist/frontend.js +21 -435
- 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 +55 -811
- package/dist/matterbridgeAccessoryPlatform.js +0 -36
- package/dist/matterbridgeBehaviors.js +5 -65
- package/dist/matterbridgeDeviceTypes.js +15 -579
- package/dist/matterbridgeDynamicPlatform.js +0 -36
- package/dist/matterbridgeEndpoint.js +54 -1214
- package/dist/matterbridgeEndpointHelpers.js +12 -344
- package/dist/matterbridgePlatform.js +0 -233
- package/dist/matterbridgeTypes.js +0 -25
- package/dist/pluginManager.js +3 -249
- 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 +0 -41
- 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 -40
- package/dist/utils/wait.js +9 -62
- package/npm-shrinkwrap.json +2 -2
- 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/dishwasher.d.ts +0 -91
- package/dist/devices/dishwasher.d.ts.map +0 -1
- package/dist/devices/dishwasher.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 -11
- package/dist/devices/export.d.ts.map +0 -1
- package/dist/devices/export.js.map +0 -1
- package/dist/devices/extractorHood.d.ts +0 -46
- package/dist/devices/extractorHood.d.ts.map +0 -1
- package/dist/devices/extractorHood.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 -112
- 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/dgram/coap.d.ts +0 -205
- package/dist/dgram/coap.d.ts.map +0 -1
- package/dist/dgram/coap.js.map +0 -1
- package/dist/dgram/dgram.d.ts +0 -140
- package/dist/dgram/dgram.d.ts.map +0 -1
- package/dist/dgram/dgram.js.map +0 -1
- package/dist/dgram/mb_coap.d.ts +0 -24
- package/dist/dgram/mb_coap.d.ts.map +0 -1
- package/dist/dgram/mb_coap.js.map +0 -1
- package/dist/dgram/mb_mdns.d.ts +0 -24
- package/dist/dgram/mb_mdns.d.ts.map +0 -1
- package/dist/dgram/mb_mdns.js.map +0 -1
- package/dist/dgram/mdns.d.ts +0 -288
- package/dist/dgram/mdns.d.ts.map +0 -1
- package/dist/dgram/mdns.js.map +0 -1
- package/dist/dgram/multicast.d.ts +0 -65
- package/dist/dgram/multicast.d.ts.map +0 -1
- package/dist/dgram/multicast.js.map +0 -1
- package/dist/dgram/unicast.d.ts +0 -56
- package/dist/dgram/unicast.d.ts.map +0 -1
- package/dist/dgram/unicast.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 -463
- 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 -1351
- 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 -1348
- package/dist/matterbridgeEndpoint.d.ts.map +0 -1
- package/dist/matterbridgeEndpoint.js.map +0 -1
- package/dist/matterbridgeEndpointHelpers.d.ts +0 -406
- 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 -270
- 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/error.d.ts +0 -44
- package/dist/utils/error.d.ts.map +0 -1
- package/dist/utils/error.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 -33
- 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,43 +1,15 @@
|
|
|
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
|
-
// Matterbridge
|
|
41
13
|
import { getParameter, getIntParameter, hasParameter, copyDirectory, isValidString, parseVersionString, isValidNumber, createDirectory } from './utils/export.js';
|
|
42
14
|
import { withTimeout, waiter, wait } from './utils/wait.js';
|
|
43
15
|
import { dev, plg, typ } from './matterbridgeTypes.js';
|
|
@@ -47,9 +19,6 @@ import { MatterbridgeEndpoint } from './matterbridgeEndpoint.js';
|
|
|
47
19
|
import { bridge } from './matterbridgeDeviceTypes.js';
|
|
48
20
|
import { Frontend } from './frontend.js';
|
|
49
21
|
import { addVirtualDevices } from './helpers.js';
|
|
50
|
-
/**
|
|
51
|
-
* Represents the Matterbridge application.
|
|
52
|
-
*/
|
|
53
22
|
export class Matterbridge extends EventEmitter {
|
|
54
23
|
systemInformation = {
|
|
55
24
|
interfaceName: '',
|
|
@@ -98,7 +67,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
98
67
|
shellySysUpdate: false,
|
|
99
68
|
shellyMainUpdate: false,
|
|
100
69
|
profile: getParameter('profile'),
|
|
101
|
-
loggerLevel: "info"
|
|
70
|
+
loggerLevel: "info",
|
|
102
71
|
fileLogger: false,
|
|
103
72
|
matterLoggerLevel: MatterLogLevel.INFO,
|
|
104
73
|
matterFileLogger: false,
|
|
@@ -125,18 +94,15 @@ export class Matterbridge extends EventEmitter {
|
|
|
125
94
|
profile = getParameter('profile');
|
|
126
95
|
shutdown = false;
|
|
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
100
|
plugins = new PluginManager(this);
|
|
133
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;
|
|
@@ -151,23 +117,19 @@ export class Matterbridge extends EventEmitter {
|
|
|
151
117
|
sigtermHandler;
|
|
152
118
|
exceptionHandler;
|
|
153
119
|
rejectionHandler;
|
|
154
|
-
// Matter environment
|
|
155
120
|
environment = Environment.default;
|
|
156
|
-
// Matter storage
|
|
157
121
|
matterStorageName = 'matterstorage' + (getParameter('profile') ? '.' + getParameter('profile') : '');
|
|
158
122
|
matterStorageService;
|
|
159
123
|
matterStorageManager;
|
|
160
124
|
matterbridgeContext;
|
|
161
125
|
controllerContext;
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
certification; // device certification
|
|
170
|
-
// Matter nodes
|
|
126
|
+
mdnsInterface;
|
|
127
|
+
ipv4address;
|
|
128
|
+
ipv6address;
|
|
129
|
+
port;
|
|
130
|
+
passcode;
|
|
131
|
+
discriminator;
|
|
132
|
+
certification;
|
|
171
133
|
serverNode;
|
|
172
134
|
aggregatorNode;
|
|
173
135
|
aggregatorVendorId = VendorId(getIntParameter('vendorId') ?? 0xfff1);
|
|
@@ -178,31 +140,15 @@ export class Matterbridge extends EventEmitter {
|
|
|
178
140
|
aggregatorSerialNumber = getParameter('serialNumber');
|
|
179
141
|
aggregatorUniqueId = getParameter('uniqueId');
|
|
180
142
|
static instance;
|
|
181
|
-
// We load asyncronously so is private
|
|
182
143
|
constructor() {
|
|
183
144
|
super();
|
|
184
145
|
}
|
|
185
|
-
/**
|
|
186
|
-
* Retrieves the list of Matterbridge devices.
|
|
187
|
-
*
|
|
188
|
-
* @returns {MatterbridgeEndpoint[]} An array of MatterbridgeDevice objects.
|
|
189
|
-
*/
|
|
190
146
|
getDevices() {
|
|
191
147
|
return this.devices.array();
|
|
192
148
|
}
|
|
193
|
-
/**
|
|
194
|
-
* Retrieves the list of registered plugins.
|
|
195
|
-
*
|
|
196
|
-
* @returns {RegisteredPlugin[]} An array of RegisteredPlugin objects.
|
|
197
|
-
*/
|
|
198
149
|
getPlugins() {
|
|
199
150
|
return this.plugins.array();
|
|
200
151
|
}
|
|
201
|
-
/**
|
|
202
|
-
* Set the logger logLevel for the Matterbridge classes and call onChangeLoggerLevel() for each plugin.
|
|
203
|
-
*
|
|
204
|
-
* @param {LogLevel} logLevel The logger logLevel to set.
|
|
205
|
-
*/
|
|
206
152
|
async setLogLevel(logLevel) {
|
|
207
153
|
if (this.log)
|
|
208
154
|
this.log.logLevel = logLevel;
|
|
@@ -216,31 +162,19 @@ export class Matterbridge extends EventEmitter {
|
|
|
216
162
|
for (const plugin of this.plugins) {
|
|
217
163
|
if (!plugin.platform || !plugin.platform.log || !plugin.platform.config)
|
|
218
164
|
continue;
|
|
219
|
-
plugin.platform.log.logLevel = plugin.platform.config.debug === true ? "debug"
|
|
220
|
-
await plugin.platform.onChangeLoggerLevel(plugin.platform.config.debug === true ? "debug"
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
callbackLogLevel = "debug" /* LogLevel.DEBUG */;
|
|
165
|
+
plugin.platform.log.logLevel = plugin.platform.config.debug === true ? "debug" : this.log.logLevel;
|
|
166
|
+
await plugin.platform.onChangeLoggerLevel(plugin.platform.config.debug === true ? "debug" : this.log.logLevel);
|
|
167
|
+
}
|
|
168
|
+
let callbackLogLevel = "notice";
|
|
169
|
+
if (this.matterbridgeInformation.loggerLevel === "info" || this.matterbridgeInformation.matterLoggerLevel === MatterLogLevel.INFO)
|
|
170
|
+
callbackLogLevel = "info";
|
|
171
|
+
if (this.matterbridgeInformation.loggerLevel === "debug" || this.matterbridgeInformation.matterLoggerLevel === MatterLogLevel.DEBUG)
|
|
172
|
+
callbackLogLevel = "debug";
|
|
228
173
|
AnsiLogger.setGlobalCallback(this.frontend.wssSendMessage.bind(this.frontend), callbackLogLevel);
|
|
229
174
|
this.log.debug(`WebSocketServer logger global callback set to ${callbackLogLevel}`);
|
|
230
175
|
}
|
|
231
|
-
//* ************************************************************************************************************************************ */
|
|
232
|
-
// loadInstance() and cleanup() methods */
|
|
233
|
-
//* ************************************************************************************************************************************ */
|
|
234
|
-
/**
|
|
235
|
-
* Loads an instance of the Matterbridge class.
|
|
236
|
-
* If an instance already exists, return that instance.
|
|
237
|
-
*
|
|
238
|
-
* @param {boolean} initialize - Whether to initialize the Matterbridge instance after loading. Defaults to false.
|
|
239
|
-
* @returns {Matterbridge} A promise that resolves to the Matterbridge instance.
|
|
240
|
-
*/
|
|
241
176
|
static async loadInstance(initialize = false) {
|
|
242
177
|
if (!Matterbridge.instance) {
|
|
243
|
-
// eslint-disable-next-line no-console
|
|
244
178
|
if (hasParameter('debug'))
|
|
245
179
|
console.log(GREEN + 'Creating a new instance of Matterbridge.', initialize ? 'Initializing...' : 'Not initializing...', rs);
|
|
246
180
|
Matterbridge.instance = new Matterbridge();
|
|
@@ -249,17 +183,8 @@ export class Matterbridge extends EventEmitter {
|
|
|
249
183
|
}
|
|
250
184
|
return Matterbridge.instance;
|
|
251
185
|
}
|
|
252
|
-
/**
|
|
253
|
-
* Call cleanup() and dispose MdnsService.
|
|
254
|
-
*
|
|
255
|
-
* @param {number} [timeout] - The timeout duration to wait for the cleanup to complete in milliseconds. Default is 1000.
|
|
256
|
-
* @param {number} [pause] - The pause duration after the cleanup in milliseconds. Default is 250.
|
|
257
|
-
*
|
|
258
|
-
* @deprecated This method is deprecated and is ONLY used for jest tests.
|
|
259
|
-
*/
|
|
260
186
|
async destroyInstance(timeout = 1000, pause = 250) {
|
|
261
187
|
this.log.info(`Destroy instance...`);
|
|
262
|
-
// Save server nodes to close
|
|
263
188
|
const servers = [];
|
|
264
189
|
if (this.bridgeMode === 'bridge') {
|
|
265
190
|
if (this.serverNode)
|
|
@@ -277,105 +202,72 @@ export class Matterbridge extends EventEmitter {
|
|
|
277
202
|
servers.push(device.serverNode);
|
|
278
203
|
}
|
|
279
204
|
}
|
|
280
|
-
// Let any already‐queued microtasks run first
|
|
281
205
|
await Promise.resolve();
|
|
282
|
-
// Wait for the cleanup to finish
|
|
283
206
|
await wait(pause, 'destroyInstance start', true);
|
|
284
|
-
// Cleanup
|
|
285
207
|
await this.cleanup('destroying instance...', false, timeout);
|
|
286
|
-
// Close servers mdns service
|
|
287
208
|
this.log.info(`Dispose ${servers.length} MdnsService...`);
|
|
288
209
|
for (const server of servers) {
|
|
289
210
|
await server.env.get(MdnsService)[Symbol.asyncDispose]();
|
|
290
211
|
this.log.info(`Closed ${server.id} MdnsService`);
|
|
291
212
|
}
|
|
292
|
-
// Let any already‐queued microtasks run first
|
|
293
213
|
await Promise.resolve();
|
|
294
|
-
// Wait for the cleanup to finish
|
|
295
214
|
await wait(pause, 'destroyInstance stop', true);
|
|
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, productName, deviceType, serialNumber, uniqueId if they are present in the pairing file
|
|
406
292
|
if (isValidNumber(pairingFileJson.vendorId)) {
|
|
407
293
|
this.aggregatorVendorId = VendorId(pairingFileJson.vendorId);
|
|
408
294
|
this.log.info(`Pairing file ${CYAN}${pairingFilePath}${nf} found. Using vendorId ${CYAN}${this.aggregatorVendorId}${nf} from pairing file.`);
|
|
@@ -431,23 +317,18 @@ export class Matterbridge extends EventEmitter {
|
|
|
431
317
|
this.aggregatorUniqueId = pairingFileJson.uniqueId;
|
|
432
318
|
this.log.info(`Pairing file ${CYAN}${pairingFilePath}${nf} found. Using uniqueId ${CYAN}${this.aggregatorUniqueId}${nf} from pairing file.`);
|
|
433
319
|
}
|
|
434
|
-
// Override the passcode and discriminator if they are present in the pairing file
|
|
435
320
|
if (isValidNumber(pairingFileJson.passcode) && isValidNumber(pairingFileJson.discriminator)) {
|
|
436
321
|
this.passcode = pairingFileJson.passcode;
|
|
437
322
|
this.discriminator = pairingFileJson.discriminator;
|
|
438
323
|
this.log.info(`Pairing file ${CYAN}${pairingFilePath}${nf} found. Using passcode ${CYAN}${this.passcode}${nf} and discriminator ${CYAN}${this.discriminator}${nf} from pairing file.`);
|
|
439
324
|
}
|
|
440
|
-
// Set the certification for matter.js if it is present in the pairing file
|
|
441
325
|
if (pairingFileJson.privateKey && pairingFileJson.certificate && pairingFileJson.intermediateCertificate && pairingFileJson.declaration) {
|
|
442
|
-
const
|
|
443
|
-
const matches = hexString.match(/.{1,2}/g);
|
|
444
|
-
return matches ? new Uint8Array(matches.map((byte) => parseInt(byte, 16))) : new Uint8Array();
|
|
445
|
-
};
|
|
326
|
+
const { hexToBuffer } = await import('./utils/hex.js');
|
|
446
327
|
this.certification = {
|
|
447
|
-
privateKey:
|
|
448
|
-
certificate:
|
|
449
|
-
intermediateCertificate:
|
|
450
|
-
declaration:
|
|
328
|
+
privateKey: hexToBuffer(pairingFileJson.privateKey),
|
|
329
|
+
certificate: hexToBuffer(pairingFileJson.certificate),
|
|
330
|
+
intermediateCertificate: hexToBuffer(pairingFileJson.intermediateCertificate),
|
|
331
|
+
declaration: hexToBuffer(pairingFileJson.declaration),
|
|
451
332
|
};
|
|
452
333
|
this.log.info(`Pairing file ${CYAN}${pairingFilePath}${nf} found. Using privateKey, certificate, intermediateCertificate and declaration from pairing file.`);
|
|
453
334
|
}
|
|
@@ -455,44 +336,41 @@ export class Matterbridge extends EventEmitter {
|
|
|
455
336
|
catch (error) {
|
|
456
337
|
this.log.debug(`Pairing file ${CYAN}${pairingFilePath}${db} not found: ${error instanceof Error ? error.message : error}`);
|
|
457
338
|
}
|
|
458
|
-
// Store the passcode, discriminator and port in the node context
|
|
459
339
|
await this.nodeContext.set('matterport', this.port);
|
|
460
340
|
await this.nodeContext.set('matterpasscode', this.passcode);
|
|
461
341
|
await this.nodeContext.set('matterdiscriminator', this.discriminator);
|
|
462
342
|
this.log.debug(`Initializing server node for Matterbridge on port ${this.port} with passcode ${this.passcode} and discriminator ${this.discriminator}`);
|
|
463
|
-
// Set matterbridge logger level (context: matterbridgeLogLevel)
|
|
464
343
|
if (hasParameter('logger')) {
|
|
465
344
|
const level = getParameter('logger');
|
|
466
345
|
if (level === 'debug') {
|
|
467
|
-
this.log.logLevel = "debug"
|
|
346
|
+
this.log.logLevel = "debug";
|
|
468
347
|
}
|
|
469
348
|
else if (level === 'info') {
|
|
470
|
-
this.log.logLevel = "info"
|
|
349
|
+
this.log.logLevel = "info";
|
|
471
350
|
}
|
|
472
351
|
else if (level === 'notice') {
|
|
473
|
-
this.log.logLevel = "notice"
|
|
352
|
+
this.log.logLevel = "notice";
|
|
474
353
|
}
|
|
475
354
|
else if (level === 'warn') {
|
|
476
|
-
this.log.logLevel = "warn"
|
|
355
|
+
this.log.logLevel = "warn";
|
|
477
356
|
}
|
|
478
357
|
else if (level === 'error') {
|
|
479
|
-
this.log.logLevel = "error"
|
|
358
|
+
this.log.logLevel = "error";
|
|
480
359
|
}
|
|
481
360
|
else if (level === 'fatal') {
|
|
482
|
-
this.log.logLevel = "fatal"
|
|
361
|
+
this.log.logLevel = "fatal";
|
|
483
362
|
}
|
|
484
363
|
else {
|
|
485
364
|
this.log.warn(`Invalid matterbridge logger level: ${level}. Using default level "info".`);
|
|
486
|
-
this.log.logLevel = "info"
|
|
365
|
+
this.log.logLevel = "info";
|
|
487
366
|
}
|
|
488
367
|
}
|
|
489
368
|
else {
|
|
490
|
-
this.log.logLevel = await this.nodeContext.get('matterbridgeLogLevel', this.matterbridgeInformation.shellyBoard ? "notice"
|
|
369
|
+
this.log.logLevel = await this.nodeContext.get('matterbridgeLogLevel', this.matterbridgeInformation.shellyBoard ? "notice" : "info");
|
|
491
370
|
}
|
|
492
371
|
this.frontend.logLevel = this.log.logLevel;
|
|
493
372
|
MatterbridgeEndpoint.logLevel = this.log.logLevel;
|
|
494
373
|
this.matterbridgeInformation.loggerLevel = this.log.logLevel;
|
|
495
|
-
// Create the file logger for matterbridge (context: matterbridgeFileLog)
|
|
496
374
|
if (hasParameter('filelogger') || (await this.nodeContext.get('matterbridgeFileLog', false))) {
|
|
497
375
|
AnsiLogger.setGlobalLogfile(path.join(this.matterbridgeDirectory, this.matterbridgeLoggerFile), this.log.logLevel, true);
|
|
498
376
|
this.matterbridgeInformation.fileLogger = true;
|
|
@@ -501,7 +379,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
501
379
|
this.log.debug(`Matterbridge logLevel: ${this.log.logLevel} fileLoger: ${this.matterbridgeInformation.fileLogger}.`);
|
|
502
380
|
if (this.profile !== undefined)
|
|
503
381
|
this.log.debug(`Matterbridge profile: ${this.profile}.`);
|
|
504
|
-
// Set matter.js logger level, format and logger (context: matterLogLevel)
|
|
505
382
|
if (hasParameter('matterlogger')) {
|
|
506
383
|
const level = getParameter('matterlogger');
|
|
507
384
|
if (level === 'debug') {
|
|
@@ -532,9 +409,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
532
409
|
}
|
|
533
410
|
Logger.format = MatterLogFormat.ANSI;
|
|
534
411
|
Logger.setLogger('default', this.createMatterLogger());
|
|
535
|
-
// Logger.destinations.default.write = this.createMatterLogger();
|
|
536
412
|
this.matterbridgeInformation.matterLoggerLevel = Logger.level;
|
|
537
|
-
// Create the file logger for matter.js (context: matterFileLog)
|
|
538
413
|
if (hasParameter('matterfilelogger') || (await this.nodeContext.get('matterFileLog', false))) {
|
|
539
414
|
this.matterbridgeInformation.matterFileLogger = true;
|
|
540
415
|
Logger.addLogger('matterfilelogger', await this.createMatterFileLogger(path.join(this.matterbridgeDirectory, this.matterLoggerFile), true), {
|
|
@@ -543,9 +418,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
543
418
|
});
|
|
544
419
|
}
|
|
545
420
|
this.log.debug(`Matter logLevel: ${Logger.level} fileLoger: ${this.matterbridgeInformation.matterFileLogger}.`);
|
|
546
|
-
// Log network interfaces
|
|
547
421
|
const networkInterfaces = os.networkInterfaces();
|
|
548
|
-
// console.log(`Network interfaces:`, networkInterfaces);
|
|
549
422
|
const availableAddresses = Object.entries(networkInterfaces);
|
|
550
423
|
const availableInterfaces = Object.keys(networkInterfaces);
|
|
551
424
|
for (const [ifaceName, ifaces] of availableAddresses) {
|
|
@@ -557,7 +430,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
557
430
|
});
|
|
558
431
|
}
|
|
559
432
|
}
|
|
560
|
-
// Set the interface to use for matter server node mdnsInterface
|
|
561
433
|
if (hasParameter('mdnsinterface')) {
|
|
562
434
|
this.mdnsInterface = getParameter('mdnsinterface');
|
|
563
435
|
}
|
|
@@ -566,7 +438,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
566
438
|
if (this.mdnsInterface === '')
|
|
567
439
|
this.mdnsInterface = undefined;
|
|
568
440
|
}
|
|
569
|
-
// Validate mdnsInterface
|
|
570
441
|
if (this.mdnsInterface) {
|
|
571
442
|
if (!availableInterfaces.includes(this.mdnsInterface)) {
|
|
572
443
|
this.log.error(`Invalid mdnsinterface: ${this.mdnsInterface}. Available interfaces are: ${availableInterfaces.join(', ')}. Using all available interfaces.`);
|
|
@@ -579,7 +450,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
579
450
|
}
|
|
580
451
|
if (this.mdnsInterface)
|
|
581
452
|
this.environment.vars.set('mdns.networkInterface', this.mdnsInterface);
|
|
582
|
-
// Set the listeningAddressIpv4 for the matter commissioning server
|
|
583
453
|
if (hasParameter('ipv4address')) {
|
|
584
454
|
this.ipv4address = getParameter('ipv4address');
|
|
585
455
|
}
|
|
@@ -588,7 +458,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
588
458
|
if (this.ipv4address === '')
|
|
589
459
|
this.ipv4address = undefined;
|
|
590
460
|
}
|
|
591
|
-
// Validate ipv4address
|
|
592
461
|
if (this.ipv4address) {
|
|
593
462
|
let isValid = false;
|
|
594
463
|
for (const [ifaceName, ifaces] of availableAddresses) {
|
|
@@ -604,7 +473,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
604
473
|
await this.nodeContext.remove('matteripv4address');
|
|
605
474
|
}
|
|
606
475
|
}
|
|
607
|
-
// Set the listeningAddressIpv6 for the matter commissioning server
|
|
608
476
|
if (hasParameter('ipv6address')) {
|
|
609
477
|
this.ipv6address = getParameter('ipv6address');
|
|
610
478
|
}
|
|
@@ -613,7 +481,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
613
481
|
if (this.ipv6address === '')
|
|
614
482
|
this.ipv6address = undefined;
|
|
615
483
|
}
|
|
616
|
-
// Validate ipv6address
|
|
617
484
|
if (this.ipv6address) {
|
|
618
485
|
let isValid = false;
|
|
619
486
|
for (const [ifaceName, ifaces] of availableAddresses) {
|
|
@@ -622,7 +489,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
622
489
|
isValid = true;
|
|
623
490
|
break;
|
|
624
491
|
}
|
|
625
|
-
/* istanbul ignore next */
|
|
626
492
|
if (ifaces && ifaces.find((iface) => iface.scopeid && iface.scopeid > 0 && iface.address + '%' + (process.platform === 'win32' ? iface.scopeid : ifaceName) === this.ipv6address)) {
|
|
627
493
|
this.log.info(`Using ipv6address ${CYAN}${this.ipv6address}${nf} on interface ${CYAN}${ifaceName}${nf} for the Matter server node.`);
|
|
628
494
|
isValid = true;
|
|
@@ -635,7 +501,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
635
501
|
await this.nodeContext.remove('matteripv6address');
|
|
636
502
|
}
|
|
637
503
|
}
|
|
638
|
-
// Initialize the virtual mode
|
|
639
504
|
if (hasParameter('novirtual')) {
|
|
640
505
|
this.matterbridgeInformation.virtualMode = 'disabled';
|
|
641
506
|
await this.nodeContext.set('virtualmode', 'disabled');
|
|
@@ -644,17 +509,12 @@ export class Matterbridge extends EventEmitter {
|
|
|
644
509
|
this.matterbridgeInformation.virtualMode = (await this.nodeContext.get('virtualmode', 'outlet'));
|
|
645
510
|
}
|
|
646
511
|
this.log.debug(`Virtual mode ${this.matterbridgeInformation.virtualMode}.`);
|
|
647
|
-
// Initialize PluginManager
|
|
648
512
|
this.plugins.logLevel = this.log.logLevel;
|
|
649
513
|
await this.plugins.loadFromStorage();
|
|
650
|
-
// Initialize DeviceManager
|
|
651
514
|
this.devices.logLevel = this.log.logLevel;
|
|
652
|
-
// Get the plugins from node storage and create the plugins node storage contexts
|
|
653
515
|
for (const plugin of this.plugins) {
|
|
654
516
|
const packageJson = await this.plugins.parse(plugin);
|
|
655
517
|
if (packageJson === null && !hasParameter('add') && !hasParameter('remove') && !hasParameter('enable') && !hasParameter('disable') && !hasParameter('reset') && !hasParameter('factoryreset')) {
|
|
656
|
-
// Try to reinstall the plugin from npm (for Docker pull and external plugins)
|
|
657
|
-
// We don't do this when the add and other parameters are set because we shut down the process after adding the plugin
|
|
658
518
|
this.log.info(`Error parsing plugin ${plg}${plugin.name}${nf}. Trying to reinstall it from npm.`);
|
|
659
519
|
try {
|
|
660
520
|
const { spawnCommand } = await import('./utils/spawn.js');
|
|
@@ -677,7 +537,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
677
537
|
await plugin.nodeContext.set('description', plugin.description);
|
|
678
538
|
await plugin.nodeContext.set('author', plugin.author);
|
|
679
539
|
}
|
|
680
|
-
// Log system info and create .matterbridge directory
|
|
681
540
|
await this.logNodeAndSystemInfo();
|
|
682
541
|
this.log.notice(`Matterbridge version ${this.matterbridgeVersion} ` +
|
|
683
542
|
`${hasParameter('bridge') || (!hasParameter('childbridge') && (await this.nodeContext?.get('bridgeMode', '')) === 'bridge') ? 'mode bridge ' : ''}` +
|
|
@@ -685,7 +544,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
685
544
|
`${hasParameter('controller') ? 'mode controller ' : ''}` +
|
|
686
545
|
`${this.restartMode !== '' ? 'restart mode ' + this.restartMode + ' ' : ''}` +
|
|
687
546
|
`running on ${this.systemInformation.osType} (v.${this.systemInformation.osRelease}) platform ${this.systemInformation.osPlatform} arch ${this.systemInformation.osArch}`);
|
|
688
|
-
// Check node version and throw error
|
|
689
547
|
const minNodeVersion = 18;
|
|
690
548
|
const nodeVersion = process.versions.node;
|
|
691
549
|
const versionMajor = parseInt(nodeVersion.split('.')[0]);
|
|
@@ -693,18 +551,10 @@ export class Matterbridge extends EventEmitter {
|
|
|
693
551
|
this.log.error(`Node version ${versionMajor} is not supported. Please upgrade to ${minNodeVersion} or above.`);
|
|
694
552
|
throw new Error(`Node version ${versionMajor} is not supported. Please upgrade to ${minNodeVersion} or above.`);
|
|
695
553
|
}
|
|
696
|
-
// Parse command line
|
|
697
554
|
await this.parseCommandLine();
|
|
698
|
-
// Emit the initialize_completed event
|
|
699
555
|
this.emit('initialize_completed');
|
|
700
556
|
this.initialized = true;
|
|
701
557
|
}
|
|
702
|
-
/**
|
|
703
|
-
* Parses the command line arguments and performs the corresponding actions.
|
|
704
|
-
*
|
|
705
|
-
* @private
|
|
706
|
-
* @returns {Promise<void>} A promise that resolves when the command line arguments have been processed, or the process exits.
|
|
707
|
-
*/
|
|
708
558
|
async parseCommandLine() {
|
|
709
559
|
if (hasParameter('help')) {
|
|
710
560
|
this.log.info(`\nUsage: matterbridge [options]\n
|
|
@@ -766,19 +616,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
766
616
|
}
|
|
767
617
|
index++;
|
|
768
618
|
}
|
|
769
|
-
/*
|
|
770
|
-
const serializedRegisteredDevices = await this.nodeContext?.get<SerializedMatterbridgeEndpoint[]>('devices', []);
|
|
771
|
-
this.log.info(`│ Registered devices (${serializedRegisteredDevices?.length})`);
|
|
772
|
-
serializedRegisteredDevices?.forEach((device, index) => {
|
|
773
|
-
if (index !== serializedRegisteredDevices.length - 1) {
|
|
774
|
-
this.log.info(`├─┬─ plugin ${plg}${device.pluginName}${nf} device: ${dev}${device.deviceName}${nf} uniqueId: ${YELLOW}${device.uniqueId}${nf}`);
|
|
775
|
-
this.log.info(`│ └─ endpoint ${RED}${device.endpoint}${nf} ${typ}${device.endpointName}${nf} ${debugStringify(device.clusterServersId)}`);
|
|
776
|
-
} else {
|
|
777
|
-
this.log.info(`└─┬─ plugin ${plg}${device.pluginName}${nf} device: ${dev}${device.deviceName}${nf} uniqueId: ${YELLOW}${device.uniqueId}${nf}`);
|
|
778
|
-
this.log.info(` └─ endpoint ${RED}${device.endpoint}${nf} ${typ}${device.endpointName}${nf} ${debugStringify(device.clusterServersId)}`);
|
|
779
|
-
}
|
|
780
|
-
});
|
|
781
|
-
*/
|
|
782
619
|
this.shutdown = true;
|
|
783
620
|
return;
|
|
784
621
|
}
|
|
@@ -828,7 +665,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
828
665
|
this.shutdown = true;
|
|
829
666
|
return;
|
|
830
667
|
}
|
|
831
|
-
// Start the matter storage and create the matterbridge context
|
|
832
668
|
try {
|
|
833
669
|
await this.startMatterStorage();
|
|
834
670
|
if (this.aggregatorSerialNumber && this.aggregatorUniqueId && this.matterStorageService) {
|
|
@@ -845,21 +681,18 @@ export class Matterbridge extends EventEmitter {
|
|
|
845
681
|
this.log.fatal(`Fatal error creating matter storage: ${error instanceof Error ? error.message : error}`);
|
|
846
682
|
throw new Error(`Fatal error creating matter storage: ${error instanceof Error ? error.message : error}`);
|
|
847
683
|
}
|
|
848
|
-
// Clear the matterbridge context if the reset parameter is set
|
|
849
684
|
if (hasParameter('reset') && getParameter('reset') === undefined) {
|
|
850
685
|
this.initialized = true;
|
|
851
686
|
await this.shutdownProcessAndReset();
|
|
852
687
|
this.shutdown = true;
|
|
853
688
|
return;
|
|
854
689
|
}
|
|
855
|
-
// Clear matterbridge plugin context if the reset parameter is set
|
|
856
690
|
if (hasParameter('reset') && getParameter('reset') !== undefined) {
|
|
857
691
|
this.log.debug(`Reset plugin ${getParameter('reset')}`);
|
|
858
692
|
const plugin = this.plugins.get(getParameter('reset'));
|
|
859
693
|
if (plugin) {
|
|
860
694
|
const matterStorageManager = await this.matterStorageService?.open(plugin.name);
|
|
861
695
|
if (!matterStorageManager) {
|
|
862
|
-
/* istanbul ignore next */
|
|
863
696
|
this.log.error(`Plugin ${plg}${plugin.name}${er} storageManager not found`);
|
|
864
697
|
}
|
|
865
698
|
else {
|
|
@@ -878,39 +711,32 @@ export class Matterbridge extends EventEmitter {
|
|
|
878
711
|
this.shutdown = true;
|
|
879
712
|
return;
|
|
880
713
|
}
|
|
881
|
-
// Initialize frontend
|
|
882
714
|
if (getIntParameter('frontend') !== 0 || getIntParameter('frontend') === undefined)
|
|
883
715
|
await this.frontend.start(getIntParameter('frontend'));
|
|
884
|
-
// Check in 30 seconds the latest and dev versions of matterbridge and the plugins
|
|
885
716
|
clearTimeout(this.checkUpdateTimeout);
|
|
886
717
|
this.checkUpdateTimeout = setTimeout(async () => {
|
|
887
718
|
const { checkUpdates } = await import('./update.js');
|
|
888
719
|
checkUpdates(this);
|
|
889
720
|
}, 30 * 1000).unref();
|
|
890
|
-
// Check each 12 hours the latest and dev versions of matterbridge and the plugins
|
|
891
721
|
clearInterval(this.checkUpdateInterval);
|
|
892
722
|
this.checkUpdateInterval = setInterval(async () => {
|
|
893
723
|
const { checkUpdates } = await import('./update.js');
|
|
894
724
|
checkUpdates(this);
|
|
895
725
|
}, 12 * 60 * 60 * 1000).unref();
|
|
896
|
-
// Start the matterbridge in mode test
|
|
897
726
|
if (hasParameter('test')) {
|
|
898
727
|
this.bridgeMode = 'bridge';
|
|
899
728
|
MatterbridgeEndpoint.bridgeMode = 'bridge';
|
|
900
729
|
return;
|
|
901
730
|
}
|
|
902
|
-
// Start the matterbridge in mode controller
|
|
903
731
|
if (hasParameter('controller')) {
|
|
904
732
|
this.bridgeMode = 'controller';
|
|
905
733
|
await this.startController();
|
|
906
734
|
return;
|
|
907
735
|
}
|
|
908
|
-
// Check if the bridge mode is set and start matterbridge in bridge mode if not set
|
|
909
736
|
if (!hasParameter('bridge') && !hasParameter('childbridge') && (await this.nodeContext?.get('bridgeMode', '')) === '') {
|
|
910
737
|
this.log.info('Setting default matterbridge start mode to bridge');
|
|
911
738
|
await this.nodeContext?.set('bridgeMode', 'bridge');
|
|
912
739
|
}
|
|
913
|
-
// Start matterbridge in bridge mode
|
|
914
740
|
if (hasParameter('bridge') || (!hasParameter('childbridge') && (await this.nodeContext?.get('bridgeMode', '')) === 'bridge')) {
|
|
915
741
|
this.bridgeMode = 'bridge';
|
|
916
742
|
MatterbridgeEndpoint.bridgeMode = 'bridge';
|
|
@@ -918,7 +744,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
918
744
|
await this.startBridge();
|
|
919
745
|
return;
|
|
920
746
|
}
|
|
921
|
-
// Start matterbridge in childbridge mode
|
|
922
747
|
if (hasParameter('childbridge') || (!hasParameter('bridge') && (await this.nodeContext?.get('bridgeMode', '')) === 'childbridge')) {
|
|
923
748
|
this.bridgeMode = 'childbridge';
|
|
924
749
|
MatterbridgeEndpoint.bridgeMode = 'childbridge';
|
|
@@ -927,20 +752,10 @@ export class Matterbridge extends EventEmitter {
|
|
|
927
752
|
return;
|
|
928
753
|
}
|
|
929
754
|
}
|
|
930
|
-
/**
|
|
931
|
-
* Asynchronously loads and starts the registered plugins.
|
|
932
|
-
*
|
|
933
|
-
* This method is responsible for initializing and starting all enabled plugins.
|
|
934
|
-
* It ensures that each plugin is properly loaded and started before the bridge starts.
|
|
935
|
-
*
|
|
936
|
-
* @returns {Promise<void>} A promise that resolves when all plugins have been loaded and started.
|
|
937
|
-
*/
|
|
938
755
|
async startPlugins() {
|
|
939
|
-
// Check, load and start the plugins
|
|
940
756
|
for (const plugin of this.plugins) {
|
|
941
757
|
plugin.configJson = await this.plugins.loadConfig(plugin);
|
|
942
758
|
plugin.schemaJson = await this.plugins.loadSchema(plugin);
|
|
943
|
-
// Check if the plugin is available
|
|
944
759
|
if (!(await this.plugins.resolve(plugin.path))) {
|
|
945
760
|
this.log.error(`Plugin ${plg}${plugin.name}${er} not found or not validated. Disabling it.`);
|
|
946
761
|
plugin.enabled = false;
|
|
@@ -958,14 +773,10 @@ export class Matterbridge extends EventEmitter {
|
|
|
958
773
|
plugin.configured = false;
|
|
959
774
|
plugin.registeredDevices = undefined;
|
|
960
775
|
plugin.addedDevices = undefined;
|
|
961
|
-
this.plugins.load(plugin, true, 'Matterbridge is starting');
|
|
776
|
+
this.plugins.load(plugin, true, 'Matterbridge is starting');
|
|
962
777
|
}
|
|
963
778
|
this.frontend.wssSendRefreshRequired('plugins');
|
|
964
779
|
}
|
|
965
|
-
/**
|
|
966
|
-
* Registers the process handlers for uncaughtException, unhandledRejection, SIGINT and SIGTERM.
|
|
967
|
-
* When either of these signals are received, the cleanup method is called with an appropriate message.
|
|
968
|
-
*/
|
|
969
780
|
registerProcessHandlers() {
|
|
970
781
|
this.log.debug(`Registering uncaughtException and unhandledRejection handlers...`);
|
|
971
782
|
process.removeAllListeners('uncaughtException');
|
|
@@ -992,9 +803,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
992
803
|
};
|
|
993
804
|
process.on('SIGTERM', this.sigtermHandler);
|
|
994
805
|
}
|
|
995
|
-
/**
|
|
996
|
-
* Deregisters the process uncaughtException, unhandledRejection, SIGINT and SIGTERM signal handlers.
|
|
997
|
-
*/
|
|
998
806
|
deregisterProcessHandlers() {
|
|
999
807
|
this.log.debug(`Deregistering uncaughtException and unhandledRejection handlers...`);
|
|
1000
808
|
if (this.exceptionHandler)
|
|
@@ -1011,17 +819,12 @@ export class Matterbridge extends EventEmitter {
|
|
|
1011
819
|
process.off('SIGTERM', this.sigtermHandler);
|
|
1012
820
|
this.sigtermHandler = undefined;
|
|
1013
821
|
}
|
|
1014
|
-
/**
|
|
1015
|
-
* Logs the node and system information.
|
|
1016
|
-
*/
|
|
1017
822
|
async logNodeAndSystemInfo() {
|
|
1018
|
-
// IP address information
|
|
1019
823
|
const networkInterfaces = os.networkInterfaces();
|
|
1020
824
|
this.systemInformation.interfaceName = '';
|
|
1021
825
|
this.systemInformation.ipv4Address = '';
|
|
1022
826
|
this.systemInformation.ipv6Address = '';
|
|
1023
827
|
for (const [interfaceName, interfaceDetails] of Object.entries(networkInterfaces)) {
|
|
1024
|
-
// this.log.debug(`Checking interface: '${interfaceName}' for '${this.mdnsInterface}'`);
|
|
1025
828
|
if (this.mdnsInterface && interfaceName !== this.mdnsInterface)
|
|
1026
829
|
continue;
|
|
1027
830
|
if (!interfaceDetails) {
|
|
@@ -1047,22 +850,19 @@ export class Matterbridge extends EventEmitter {
|
|
|
1047
850
|
break;
|
|
1048
851
|
}
|
|
1049
852
|
}
|
|
1050
|
-
// Node information
|
|
1051
853
|
this.systemInformation.nodeVersion = process.versions.node;
|
|
1052
854
|
const versionMajor = parseInt(this.systemInformation.nodeVersion.split('.')[0]);
|
|
1053
855
|
const versionMinor = parseInt(this.systemInformation.nodeVersion.split('.')[1]);
|
|
1054
856
|
const versionPatch = parseInt(this.systemInformation.nodeVersion.split('.')[2]);
|
|
1055
|
-
// Host system information
|
|
1056
857
|
this.systemInformation.hostname = os.hostname();
|
|
1057
858
|
this.systemInformation.user = os.userInfo().username;
|
|
1058
|
-
this.systemInformation.osType = os.type();
|
|
1059
|
-
this.systemInformation.osRelease = os.release();
|
|
1060
|
-
this.systemInformation.osPlatform = os.platform();
|
|
1061
|
-
this.systemInformation.osArch = os.arch();
|
|
1062
|
-
this.systemInformation.totalMemory = (os.totalmem() / 1024 / 1024 / 1024).toFixed(2) + ' GB';
|
|
1063
|
-
this.systemInformation.freeMemory = (os.freemem() / 1024 / 1024 / 1024).toFixed(2) + ' GB';
|
|
1064
|
-
this.systemInformation.systemUptime = (os.uptime() / 60 / 60).toFixed(2) + ' hours';
|
|
1065
|
-
// Log the system information
|
|
859
|
+
this.systemInformation.osType = os.type();
|
|
860
|
+
this.systemInformation.osRelease = os.release();
|
|
861
|
+
this.systemInformation.osPlatform = os.platform();
|
|
862
|
+
this.systemInformation.osArch = os.arch();
|
|
863
|
+
this.systemInformation.totalMemory = (os.totalmem() / 1024 / 1024 / 1024).toFixed(2) + ' GB';
|
|
864
|
+
this.systemInformation.freeMemory = (os.freemem() / 1024 / 1024 / 1024).toFixed(2) + ' GB';
|
|
865
|
+
this.systemInformation.systemUptime = (os.uptime() / 60 / 60).toFixed(2) + ' hours';
|
|
1066
866
|
this.log.debug('Host System Information:');
|
|
1067
867
|
this.log.debug(`- Hostname: ${this.systemInformation.hostname}`);
|
|
1068
868
|
this.log.debug(`- User: ${this.systemInformation.user}`);
|
|
@@ -1078,17 +878,14 @@ export class Matterbridge extends EventEmitter {
|
|
|
1078
878
|
this.log.debug(`- Total Memory: ${this.systemInformation.totalMemory}`);
|
|
1079
879
|
this.log.debug(`- Free Memory: ${this.systemInformation.freeMemory}`);
|
|
1080
880
|
this.log.debug(`- System Uptime: ${this.systemInformation.systemUptime}`);
|
|
1081
|
-
// Log directories
|
|
1082
881
|
this.log.debug(`Root Directory: ${this.rootDirectory}`);
|
|
1083
882
|
this.log.debug(`Home Directory: ${this.homeDirectory}`);
|
|
1084
883
|
this.log.debug(`Matterbridge Directory: ${this.matterbridgeDirectory}`);
|
|
1085
884
|
this.log.debug(`Matterbridge Plugin Directory: ${this.matterbridgePluginDirectory}`);
|
|
1086
885
|
this.log.debug(`Matterbridge Matter Certificate Directory: ${this.matterbridgeCertDirectory}`);
|
|
1087
|
-
// Global node_modules directory
|
|
1088
886
|
if (this.nodeContext)
|
|
1089
887
|
this.globalModulesDirectory = this.matterbridgeInformation.globalModulesDirectory = await this.nodeContext.get('globalModulesDirectory', '');
|
|
1090
888
|
if (this.globalModulesDirectory === '') {
|
|
1091
|
-
// First run of Matterbridge so the node storage is empty
|
|
1092
889
|
try {
|
|
1093
890
|
const { getGlobalNodeModules } = await import('./utils/network.js');
|
|
1094
891
|
this.execRunningCount++;
|
|
@@ -1098,87 +895,55 @@ export class Matterbridge extends EventEmitter {
|
|
|
1098
895
|
await this.nodeContext?.set('globalModulesDirectory', this.globalModulesDirectory);
|
|
1099
896
|
}
|
|
1100
897
|
catch (error) {
|
|
1101
|
-
// istanbul ignore next
|
|
1102
898
|
this.log.error(`Error getting global node_modules directory: ${error}`);
|
|
1103
899
|
}
|
|
1104
900
|
}
|
|
1105
901
|
else
|
|
1106
902
|
this.log.debug(`Global node_modules Directory: ${this.globalModulesDirectory}`);
|
|
1107
|
-
/* removed cause is too expensive for the shelly board and not really needed. Why should the globalModulesDirectory change?
|
|
1108
|
-
else {
|
|
1109
|
-
this.getGlobalNodeModules()
|
|
1110
|
-
.then(async (globalModulesDirectory) => {
|
|
1111
|
-
this.globalModulesDirectory = globalModulesDirectory;
|
|
1112
|
-
this.matterbridgeInformation.globalModulesDirectory = this.globalModulesDirectory;
|
|
1113
|
-
this.log.debug(`Global node_modules Directory: ${this.globalModulesDirectory}`);
|
|
1114
|
-
await this.nodeContext?.set<string>('globalModulesDirectory', this.globalModulesDirectory);
|
|
1115
|
-
})
|
|
1116
|
-
.catch((error) => {
|
|
1117
|
-
this.log.error(`Error getting global node_modules directory: ${error}`);
|
|
1118
|
-
});
|
|
1119
|
-
}*/
|
|
1120
|
-
// Matterbridge version
|
|
1121
903
|
const packageJson = JSON.parse(await fs.readFile(path.join(this.rootDirectory, 'package.json'), 'utf-8'));
|
|
1122
904
|
this.matterbridgeVersion = this.matterbridgeLatestVersion = this.matterbridgeDevVersion = packageJson.version;
|
|
1123
905
|
this.matterbridgeInformation.matterbridgeVersion = this.matterbridgeInformation.matterbridgeLatestVersion = this.matterbridgeInformation.matterbridgeDevVersion = packageJson.version;
|
|
1124
906
|
this.log.debug(`Matterbridge Version: ${this.matterbridgeVersion}`);
|
|
1125
|
-
// Matterbridge latest version (will be set in the checkUpdate function)
|
|
1126
907
|
if (this.nodeContext)
|
|
1127
908
|
this.matterbridgeLatestVersion = this.matterbridgeInformation.matterbridgeLatestVersion = await this.nodeContext.get('matterbridgeLatestVersion', this.matterbridgeVersion);
|
|
1128
909
|
this.log.debug(`Matterbridge Latest Version: ${this.matterbridgeLatestVersion}`);
|
|
1129
|
-
// Matterbridge dev version (will be set in the checkUpdate function)
|
|
1130
910
|
if (this.nodeContext)
|
|
1131
911
|
this.matterbridgeDevVersion = this.matterbridgeInformation.matterbridgeDevVersion = await this.nodeContext.get('matterbridgeDevVersion', this.matterbridgeVersion);
|
|
1132
912
|
this.log.debug(`Matterbridge Dev Version: ${this.matterbridgeDevVersion}`);
|
|
1133
|
-
// Current working directory
|
|
1134
913
|
const currentDir = process.cwd();
|
|
1135
914
|
this.log.debug(`Current Working Directory: ${currentDir}`);
|
|
1136
|
-
// Command line arguments (excluding 'node' and the script name)
|
|
1137
915
|
const cmdArgs = process.argv.slice(2).join(' ');
|
|
1138
916
|
this.log.debug(`Command Line Arguments: ${cmdArgs}`);
|
|
1139
917
|
}
|
|
1140
|
-
/**
|
|
1141
|
-
* Creates a MatterLogger function to show the matter.js log messages in AnsiLogger (for the frontend).
|
|
1142
|
-
*
|
|
1143
|
-
* @returns {Function} The MatterLogger function.
|
|
1144
|
-
*/
|
|
1145
918
|
createMatterLogger() {
|
|
1146
|
-
const matterLogger = new AnsiLogger({ logName: 'Matter', logTimestampFormat: 4
|
|
919
|
+
const matterLogger = new AnsiLogger({ logName: 'Matter', logTimestampFormat: 4, logLevel: "debug" });
|
|
1147
920
|
return (level, formattedLog) => {
|
|
1148
921
|
const logger = formattedLog.slice(44, 44 + 20).trim();
|
|
1149
922
|
const message = formattedLog.slice(65);
|
|
1150
923
|
matterLogger.logName = logger;
|
|
1151
924
|
switch (level) {
|
|
1152
925
|
case MatterLogLevel.DEBUG:
|
|
1153
|
-
matterLogger.log("debug"
|
|
926
|
+
matterLogger.log("debug", message);
|
|
1154
927
|
break;
|
|
1155
928
|
case MatterLogLevel.INFO:
|
|
1156
|
-
matterLogger.log("info"
|
|
929
|
+
matterLogger.log("info", message);
|
|
1157
930
|
break;
|
|
1158
931
|
case MatterLogLevel.NOTICE:
|
|
1159
|
-
matterLogger.log("notice"
|
|
932
|
+
matterLogger.log("notice", message);
|
|
1160
933
|
break;
|
|
1161
934
|
case MatterLogLevel.WARN:
|
|
1162
|
-
matterLogger.log("warn"
|
|
935
|
+
matterLogger.log("warn", message);
|
|
1163
936
|
break;
|
|
1164
937
|
case MatterLogLevel.ERROR:
|
|
1165
|
-
matterLogger.log("error"
|
|
938
|
+
matterLogger.log("error", message);
|
|
1166
939
|
break;
|
|
1167
940
|
case MatterLogLevel.FATAL:
|
|
1168
|
-
matterLogger.log("fatal"
|
|
941
|
+
matterLogger.log("fatal", message);
|
|
1169
942
|
break;
|
|
1170
943
|
}
|
|
1171
944
|
};
|
|
1172
945
|
}
|
|
1173
|
-
/**
|
|
1174
|
-
* Creates a Matter File Logger.
|
|
1175
|
-
*
|
|
1176
|
-
* @param {string} filePath - The path to the log file.
|
|
1177
|
-
* @param {boolean} [unlink] - Whether to unlink the log file before creating a new one.
|
|
1178
|
-
* @returns {Function} - A function that logs formatted messages to the log file.
|
|
1179
|
-
*/
|
|
1180
946
|
async createMatterFileLogger(filePath, unlink = false) {
|
|
1181
|
-
// 2024-08-21 08:55:19.488 DEBUG InteractionMessenger Sending DataReport chunk with 28 attributes and 0 events: 1004 bytes
|
|
1182
947
|
let fileSize = 0;
|
|
1183
948
|
if (unlink) {
|
|
1184
949
|
try {
|
|
@@ -1189,12 +954,10 @@ export class Matterbridge extends EventEmitter {
|
|
|
1189
954
|
}
|
|
1190
955
|
}
|
|
1191
956
|
return async (level, formattedLog) => {
|
|
1192
|
-
/* istanbul ignore if */
|
|
1193
957
|
if (fileSize > 100000000) {
|
|
1194
|
-
return;
|
|
958
|
+
return;
|
|
1195
959
|
}
|
|
1196
960
|
fileSize += formattedLog.length;
|
|
1197
|
-
/* istanbul ignore if */
|
|
1198
961
|
if (fileSize > 100000000) {
|
|
1199
962
|
await fs.appendFile(filePath, `Logging on file has been stopped because the file size is greater than 100MB.` + os.EOL);
|
|
1200
963
|
return;
|
|
@@ -1227,27 +990,12 @@ export class Matterbridge extends EventEmitter {
|
|
|
1227
990
|
}
|
|
1228
991
|
};
|
|
1229
992
|
}
|
|
1230
|
-
/**
|
|
1231
|
-
* Restarts the process by exiting the current instance and loading a new instance (/api/restart).
|
|
1232
|
-
*
|
|
1233
|
-
* @returns {Promise<void>} A promise that resolves when the restart is completed.
|
|
1234
|
-
*/
|
|
1235
993
|
async restartProcess() {
|
|
1236
994
|
await this.cleanup('restarting...', true);
|
|
1237
995
|
}
|
|
1238
|
-
/**
|
|
1239
|
-
* Shut down the process (/api/shutdown).
|
|
1240
|
-
*
|
|
1241
|
-
* @returns {Promise<void>} A promise that resolves when the shutdown is completed.
|
|
1242
|
-
*/
|
|
1243
996
|
async shutdownProcess() {
|
|
1244
997
|
await this.cleanup('shutting down...', false);
|
|
1245
998
|
}
|
|
1246
|
-
/**
|
|
1247
|
-
* Update matterbridge and shut down the process (virtual device 'Update Matterbridge').
|
|
1248
|
-
*
|
|
1249
|
-
* @returns {Promise<void>} A promise that resolves when the update is completed.
|
|
1250
|
-
*/
|
|
1251
999
|
async updateProcess() {
|
|
1252
1000
|
this.log.info('Updating matterbridge...');
|
|
1253
1001
|
try {
|
|
@@ -1261,13 +1009,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1261
1009
|
this.frontend.wssSendRestartRequired();
|
|
1262
1010
|
await this.cleanup('updating...', false);
|
|
1263
1011
|
}
|
|
1264
|
-
/**
|
|
1265
|
-
* Unregister all devices and shut down the process (/api/unregister).
|
|
1266
|
-
*
|
|
1267
|
-
* @param {number} [timeout] - The timeout duration to wait for the message exchange to complete in milliseconds. Default is 1000.
|
|
1268
|
-
*
|
|
1269
|
-
* @returns {Promise<void>} A promise that resolves when the cleanup is completed.
|
|
1270
|
-
*/
|
|
1271
1012
|
async unregisterAndShutdownProcess(timeout = 1000) {
|
|
1272
1013
|
this.log.info('Unregistering all devices and shutting down...');
|
|
1273
1014
|
for (const plugin of this.plugins.array()) {
|
|
@@ -1281,71 +1022,46 @@ export class Matterbridge extends EventEmitter {
|
|
|
1281
1022
|
await this.removeAllBridgedEndpoints(plugin.name, 100);
|
|
1282
1023
|
}
|
|
1283
1024
|
this.log.debug('Waiting for the MessageExchange to finish...');
|
|
1284
|
-
await wait(timeout);
|
|
1025
|
+
await wait(timeout);
|
|
1285
1026
|
this.log.debug('Cleaning up and shutting down...');
|
|
1286
1027
|
await this.cleanup('unregistered all devices and shutting down...', false, timeout);
|
|
1287
1028
|
}
|
|
1288
|
-
/**
|
|
1289
|
-
* Reset commissioning and shut down the process (/api/reset).
|
|
1290
|
-
*
|
|
1291
|
-
* @returns {Promise<void>} A promise that resolves when the cleanup is completed.
|
|
1292
|
-
*/
|
|
1293
1029
|
async shutdownProcessAndReset() {
|
|
1294
1030
|
await this.cleanup('shutting down with reset...', false);
|
|
1295
1031
|
}
|
|
1296
|
-
/**
|
|
1297
|
-
* Factory reset and shut down the process (/api/factory-reset).
|
|
1298
|
-
*
|
|
1299
|
-
* @returns {Promise<void>} A promise that resolves when the cleanup is completed.
|
|
1300
|
-
*/
|
|
1301
1032
|
async shutdownProcessAndFactoryReset() {
|
|
1302
1033
|
await this.cleanup('shutting down with factory reset...', false);
|
|
1303
1034
|
}
|
|
1304
|
-
/**
|
|
1305
|
-
* Cleans up the Matterbridge instance.
|
|
1306
|
-
*
|
|
1307
|
-
* @param {string} message - The cleanup message.
|
|
1308
|
-
* @param {boolean} [restart] - Indicates whether to restart the instance after cleanup. Default is `false`.
|
|
1309
|
-
* @param {number} [timeout] - The timeout duration to wait for the message exchange to complete in milliseconds. Default is 1000.
|
|
1310
|
-
*
|
|
1311
|
-
* @returns {Promise<void>} A promise that resolves when the cleanup is completed.
|
|
1312
|
-
*/
|
|
1313
1035
|
async cleanup(message, restart = false, timeout = 1000) {
|
|
1314
1036
|
if (this.initialized && !this.hasCleanupStarted) {
|
|
1315
1037
|
this.emit('cleanup_started');
|
|
1316
1038
|
this.hasCleanupStarted = true;
|
|
1317
1039
|
this.log.info(message);
|
|
1318
|
-
// Clear the start matter interval
|
|
1319
1040
|
if (this.startMatterInterval) {
|
|
1320
1041
|
clearInterval(this.startMatterInterval);
|
|
1321
1042
|
this.startMatterInterval = undefined;
|
|
1322
1043
|
this.log.debug('Start matter interval cleared');
|
|
1323
1044
|
}
|
|
1324
|
-
// Clear the check update timeout
|
|
1325
1045
|
if (this.checkUpdateTimeout) {
|
|
1326
1046
|
clearTimeout(this.checkUpdateTimeout);
|
|
1327
1047
|
this.checkUpdateTimeout = undefined;
|
|
1328
1048
|
this.log.debug('Check update timeout cleared');
|
|
1329
1049
|
}
|
|
1330
|
-
// Clear the check update interval
|
|
1331
1050
|
if (this.checkUpdateInterval) {
|
|
1332
1051
|
clearInterval(this.checkUpdateInterval);
|
|
1333
1052
|
this.checkUpdateInterval = undefined;
|
|
1334
1053
|
this.log.debug('Check update interval cleared');
|
|
1335
1054
|
}
|
|
1336
|
-
// Clear the configure timeout
|
|
1337
1055
|
if (this.configureTimeout) {
|
|
1338
1056
|
clearTimeout(this.configureTimeout);
|
|
1339
1057
|
this.configureTimeout = undefined;
|
|
1340
1058
|
this.log.debug('Matterbridge configure timeout cleared');
|
|
1341
1059
|
}
|
|
1342
|
-
// Clear the reachability timeout
|
|
1343
1060
|
if (this.reachabilityTimeout) {
|
|
1344
1061
|
clearTimeout(this.reachabilityTimeout);
|
|
1345
1062
|
this.reachabilityTimeout = undefined;
|
|
1346
1063
|
this.log.debug('Matterbridge reachability timeout cleared');
|
|
1347
1064
|
}
|
|
1348
|
-
// Call the shutdown method of each plugin and clear the plugins reachability timeout
|
|
1349
1065
|
for (const plugin of this.plugins) {
|
|
1350
1066
|
if (!plugin.enabled || plugin.error)
|
|
1351
1067
|
continue;
|
|
@@ -1356,7 +1072,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1356
1072
|
this.log.debug(`Plugin ${plg}${plugin.name}${db} reachability timeout cleared`);
|
|
1357
1073
|
}
|
|
1358
1074
|
}
|
|
1359
|
-
// Stop matter server nodes
|
|
1360
1075
|
this.log.notice(`Stopping matter server nodes in ${this.bridgeMode} mode...`);
|
|
1361
1076
|
this.log.debug('Waiting for the MessageExchange to finish...');
|
|
1362
1077
|
await wait(timeout, 'Waiting for the MessageExchange to finish...', true);
|
|
@@ -1381,7 +1096,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1381
1096
|
}
|
|
1382
1097
|
}
|
|
1383
1098
|
this.log.notice('Stopped matter server nodes');
|
|
1384
|
-
// Matter commisioning reset
|
|
1385
1099
|
if (message === 'shutting down with reset...') {
|
|
1386
1100
|
this.log.info('Resetting Matterbridge commissioning information...');
|
|
1387
1101
|
await this.matterStorageManager?.createContext('events')?.clearAll();
|
|
@@ -1391,7 +1105,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1391
1105
|
await this.matterbridgeContext?.clearAll();
|
|
1392
1106
|
this.log.info('Matter storage reset done! Remove the bridge from the controller.');
|
|
1393
1107
|
}
|
|
1394
|
-
// Unregister all devices
|
|
1395
1108
|
if (message === 'unregistered all devices and shutting down...') {
|
|
1396
1109
|
if (this.bridgeMode === 'bridge') {
|
|
1397
1110
|
await this.matterStorageManager?.createContext('root')?.createContext('parts')?.createContext('Matterbridge')?.createContext('parts')?.clearAll();
|
|
@@ -1409,36 +1122,18 @@ export class Matterbridge extends EventEmitter {
|
|
|
1409
1122
|
}
|
|
1410
1123
|
this.log.info('Matter storage reset done!');
|
|
1411
1124
|
}
|
|
1412
|
-
// Stop matter storage
|
|
1413
1125
|
await this.stopMatterStorage();
|
|
1414
|
-
// Stop the frontend
|
|
1415
1126
|
await this.frontend.stop();
|
|
1416
|
-
// Remove the matterfilelogger
|
|
1417
1127
|
try {
|
|
1418
1128
|
Logger.removeLogger('matterfilelogger');
|
|
1419
1129
|
}
|
|
1420
1130
|
catch (error) {
|
|
1421
1131
|
this.log.debug(`Error removing the matterfilelogger for file ${CYAN}${path.join(this.matterbridgeDirectory, this.matterLoggerFile)}${db}: ${error instanceof Error ? error.message : String(error)}`);
|
|
1422
1132
|
}
|
|
1423
|
-
// Close the matterbridge node storage and context
|
|
1424
1133
|
if (this.nodeStorage && this.nodeContext) {
|
|
1425
|
-
/*
|
|
1426
|
-
TODO: Implement serialization of registered devices in edge mode
|
|
1427
|
-
this.log.info('Saving registered devices...');
|
|
1428
|
-
const serializedRegisteredDevices: SerializedMatterbridgeEndpoint[] = [];
|
|
1429
|
-
this.devices.forEach(async (device) => {
|
|
1430
|
-
const serializedMatterbridgeDevice = MatterbridgeEndpoint.serialize(device);
|
|
1431
|
-
// this.log.info(`- ${serializedMatterbridgeDevice.deviceName}${rs}\n`, serializedMatterbridgeDevice);
|
|
1432
|
-
if (serializedMatterbridgeDevice) serializedRegisteredDevices.push(serializedMatterbridgeDevice);
|
|
1433
|
-
});
|
|
1434
|
-
await this.nodeContext.set<SerializedMatterbridgeEndpoint[]>('devices', serializedRegisteredDevices);
|
|
1435
|
-
this.log.info(`Saved registered devices (${serializedRegisteredDevices?.length})`);
|
|
1436
|
-
*/
|
|
1437
|
-
// Clear nodeContext and nodeStorage (they just need 1000ms to write the data to disk)
|
|
1438
1134
|
this.log.debug(`Closing node storage context for ${plg}Matterbridge${db}...`);
|
|
1439
1135
|
await this.nodeContext.close();
|
|
1440
1136
|
this.nodeContext = undefined;
|
|
1441
|
-
// Clear nodeContext for each plugin (they just need 1000ms to write the data to disk)
|
|
1442
1137
|
for (const plugin of this.plugins) {
|
|
1443
1138
|
if (plugin.nodeContext) {
|
|
1444
1139
|
this.log.debug(`Closing node storage context for plugin ${plg}${plugin.name}${db}...`);
|
|
@@ -1455,10 +1150,8 @@ export class Matterbridge extends EventEmitter {
|
|
|
1455
1150
|
}
|
|
1456
1151
|
this.plugins.clear();
|
|
1457
1152
|
this.devices.clear();
|
|
1458
|
-
// Factory reset
|
|
1459
1153
|
if (message === 'shutting down with factory reset...') {
|
|
1460
1154
|
try {
|
|
1461
|
-
// Delete matter storage directory with its subdirectories and backup
|
|
1462
1155
|
const dir = path.join(this.matterbridgeDirectory, this.matterStorageName);
|
|
1463
1156
|
this.log.info(`Removing matter storage directory: ${dir}`);
|
|
1464
1157
|
await fs.rm(dir, { recursive: true });
|
|
@@ -1467,13 +1160,11 @@ export class Matterbridge extends EventEmitter {
|
|
|
1467
1160
|
await fs.rm(backup, { recursive: true });
|
|
1468
1161
|
}
|
|
1469
1162
|
catch (error) {
|
|
1470
|
-
// istanbul ignore next if
|
|
1471
1163
|
if (error instanceof Error && error.code !== 'ENOENT') {
|
|
1472
1164
|
this.log.error(`Error removing matter storage directory: ${error}`);
|
|
1473
1165
|
}
|
|
1474
1166
|
}
|
|
1475
1167
|
try {
|
|
1476
|
-
// Delete matterbridge storage directory with its subdirectories and backup
|
|
1477
1168
|
const dir = path.join(this.matterbridgeDirectory, this.nodeStorageName);
|
|
1478
1169
|
this.log.info(`Removing matterbridge storage directory: ${dir}`);
|
|
1479
1170
|
await fs.rm(dir, { recursive: true });
|
|
@@ -1482,20 +1173,18 @@ export class Matterbridge extends EventEmitter {
|
|
|
1482
1173
|
await fs.rm(backup, { recursive: true });
|
|
1483
1174
|
}
|
|
1484
1175
|
catch (error) {
|
|
1485
|
-
// istanbul ignore next if
|
|
1486
1176
|
if (error instanceof Error && error.code !== 'ENOENT') {
|
|
1487
1177
|
this.log.error(`Error removing matterbridge storage directory: ${error}`);
|
|
1488
1178
|
}
|
|
1489
1179
|
}
|
|
1490
1180
|
this.log.info('Factory reset done! Remove all paired fabrics from the controllers.');
|
|
1491
1181
|
}
|
|
1492
|
-
// Deregisters the process handlers
|
|
1493
1182
|
this.deregisterProcessHandlers();
|
|
1494
1183
|
if (restart) {
|
|
1495
1184
|
if (message === 'updating...') {
|
|
1496
1185
|
this.log.info('Cleanup completed. Updating...');
|
|
1497
1186
|
Matterbridge.instance = undefined;
|
|
1498
|
-
this.emit('update');
|
|
1187
|
+
this.emit('update');
|
|
1499
1188
|
}
|
|
1500
1189
|
else if (message === 'restarting...') {
|
|
1501
1190
|
this.log.info('Cleanup completed. Restarting...');
|
|
@@ -1516,13 +1205,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1516
1205
|
this.log.debug('Cleanup already started...');
|
|
1517
1206
|
}
|
|
1518
1207
|
}
|
|
1519
|
-
/**
|
|
1520
|
-
* Creates and configures the server node for a single not bridged device.
|
|
1521
|
-
*
|
|
1522
|
-
* @param {RegisteredPlugin} plugin - The plugin to configure.
|
|
1523
|
-
* @param {MatterbridgeEndpoint} device - The device to associate with the plugin.
|
|
1524
|
-
* @returns {Promise<void>} A promise that resolves when the server node for the accessory plugin is created and configured.
|
|
1525
|
-
*/
|
|
1526
1208
|
async createDeviceServerNode(plugin, device) {
|
|
1527
1209
|
if (device.mode === 'server' && !device.serverNode && device.deviceType && device.deviceName && device.vendorId && device.vendorName && device.productId && device.productName) {
|
|
1528
1210
|
this.log.debug(`Creating device ${plg}${plugin.name}${db}:${dev}${device.deviceName}${db} server node...`);
|
|
@@ -1533,13 +1215,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1533
1215
|
this.log.debug(`Added ${plg}${plugin.name}${db}:${dev}${device.deviceName}${db} to server node`);
|
|
1534
1216
|
}
|
|
1535
1217
|
}
|
|
1536
|
-
/**
|
|
1537
|
-
* Creates and configures the server node for an accessory plugin for a given device.
|
|
1538
|
-
*
|
|
1539
|
-
* @param {RegisteredPlugin} plugin - The plugin to configure.
|
|
1540
|
-
* @param {MatterbridgeEndpoint} device - The device to associate with the plugin.
|
|
1541
|
-
* @returns {Promise<void>} A promise that resolves when the server node for the accessory plugin is created and configured.
|
|
1542
|
-
*/
|
|
1543
1218
|
async createAccessoryPlugin(plugin, device) {
|
|
1544
1219
|
if (!plugin.locked && device.deviceType && device.deviceName && device.vendorId && device.productId && device.vendorName && device.productName) {
|
|
1545
1220
|
plugin.locked = true;
|
|
@@ -1551,12 +1226,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1551
1226
|
await plugin.serverNode.add(device);
|
|
1552
1227
|
}
|
|
1553
1228
|
}
|
|
1554
|
-
/**
|
|
1555
|
-
* Creates and configures the server node and the aggregator node for a dynamic plugin.
|
|
1556
|
-
*
|
|
1557
|
-
* @param {RegisteredPlugin} plugin - The plugin to configure.
|
|
1558
|
-
* @returns {Promise<void>} A promise that resolves when the server node and the aggregator node for the dynamic plugin is created and configured.
|
|
1559
|
-
*/
|
|
1560
1229
|
async createDynamicPlugin(plugin) {
|
|
1561
1230
|
if (!plugin.locked) {
|
|
1562
1231
|
plugin.locked = true;
|
|
@@ -1567,14 +1236,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
1567
1236
|
await plugin.serverNode.add(plugin.aggregatorNode);
|
|
1568
1237
|
}
|
|
1569
1238
|
}
|
|
1570
|
-
/**
|
|
1571
|
-
* Starts the Matterbridge in bridge mode.
|
|
1572
|
-
*
|
|
1573
|
-
* @private
|
|
1574
|
-
* @returns {Promise<void>} A promise that resolves when the Matterbridge is started.
|
|
1575
|
-
*/
|
|
1576
1239
|
async startBridge() {
|
|
1577
|
-
// Plugins are configured by a timer when matter server is started and plugin.configured is set to true
|
|
1578
1240
|
if (!this.matterStorageManager)
|
|
1579
1241
|
throw new Error('No storage manager initialized');
|
|
1580
1242
|
if (!this.matterbridgeContext)
|
|
@@ -1613,16 +1275,13 @@ export class Matterbridge extends EventEmitter {
|
|
|
1613
1275
|
clearInterval(this.startMatterInterval);
|
|
1614
1276
|
this.startMatterInterval = undefined;
|
|
1615
1277
|
this.log.debug('Cleared startMatterInterval interval for Matterbridge');
|
|
1616
|
-
|
|
1617
|
-
this.startServerNode(this.serverNode); // We don't await this, because the server node is started in the background
|
|
1618
|
-
// Start the Matter server node of single devices in mode 'server'
|
|
1278
|
+
this.startServerNode(this.serverNode);
|
|
1619
1279
|
for (const device of this.devices.array()) {
|
|
1620
1280
|
if (device.mode === 'server' && device.serverNode) {
|
|
1621
1281
|
this.log.debug(`Starting server node for device ${dev}${device.deviceName}${db} in server mode...`);
|
|
1622
|
-
this.startServerNode(device.serverNode);
|
|
1282
|
+
this.startServerNode(device.serverNode);
|
|
1623
1283
|
}
|
|
1624
1284
|
}
|
|
1625
|
-
// Configure the plugins
|
|
1626
1285
|
this.configureTimeout = setTimeout(async () => {
|
|
1627
1286
|
for (const plugin of this.plugins.array()) {
|
|
1628
1287
|
if (!plugin.enabled || !plugin.loaded || !plugin.started || plugin.error)
|
|
@@ -1640,25 +1299,16 @@ export class Matterbridge extends EventEmitter {
|
|
|
1640
1299
|
}
|
|
1641
1300
|
this.frontend.wssSendRefreshRequired('plugins');
|
|
1642
1301
|
}, 30 * 1000).unref();
|
|
1643
|
-
// Setting reachability to true
|
|
1644
1302
|
this.reachabilityTimeout = setTimeout(() => {
|
|
1645
1303
|
this.log.info(`Setting reachability to true for ${plg}Matterbridge${db}`);
|
|
1646
1304
|
if (this.aggregatorNode)
|
|
1647
1305
|
this.setAggregatorReachability(this.aggregatorNode, true);
|
|
1648
1306
|
this.frontend.wssSendRefreshRequired('reachability');
|
|
1649
1307
|
}, 60 * 1000).unref();
|
|
1650
|
-
// Logger.get('LogServerNode').info(this.serverNode);
|
|
1651
1308
|
this.emit('bridge_started');
|
|
1652
1309
|
this.log.notice('Matterbridge bridge started successfully');
|
|
1653
1310
|
}, this.startMatterIntervalMs);
|
|
1654
1311
|
}
|
|
1655
|
-
/**
|
|
1656
|
-
* Starts the Matterbridge in childbridge mode.
|
|
1657
|
-
*
|
|
1658
|
-
* @param {number} [delay] - The delay before starting the childbridge. Default is 1000 milliseconds.
|
|
1659
|
-
*
|
|
1660
|
-
* @returns {Promise<void>} A promise that resolves when the Matterbridge is started.
|
|
1661
|
-
*/
|
|
1662
1312
|
async startChildbridge(delay = 1000) {
|
|
1663
1313
|
if (!this.matterStorageManager)
|
|
1664
1314
|
throw new Error('No storage manager initialized');
|
|
@@ -1696,9 +1346,8 @@ export class Matterbridge extends EventEmitter {
|
|
|
1696
1346
|
clearInterval(this.startMatterInterval);
|
|
1697
1347
|
this.startMatterInterval = undefined;
|
|
1698
1348
|
if (delay > 0)
|
|
1699
|
-
await wait(delay);
|
|
1349
|
+
await wait(delay);
|
|
1700
1350
|
this.log.debug('Cleared startMatterInterval interval in childbridge mode');
|
|
1701
|
-
// Configure the plugins
|
|
1702
1351
|
this.configureTimeout = setTimeout(async () => {
|
|
1703
1352
|
for (const plugin of this.plugins.array()) {
|
|
1704
1353
|
if (!plugin.enabled || !plugin.loaded || !plugin.started || plugin.error)
|
|
@@ -1735,9 +1384,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
1735
1384
|
this.log.error(`Node storage context not found for plugin ${plg}${plugin.name}${er}`);
|
|
1736
1385
|
continue;
|
|
1737
1386
|
}
|
|
1738
|
-
|
|
1739
|
-
this.startServerNode(plugin.serverNode); // We don't await this, because the server node is started in the background
|
|
1740
|
-
// Setting reachability to true
|
|
1387
|
+
this.startServerNode(plugin.serverNode);
|
|
1741
1388
|
plugin.reachabilityTimeout = setTimeout(() => {
|
|
1742
1389
|
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}`);
|
|
1743
1390
|
if (plugin.type === 'DynamicPlatform' && plugin.aggregatorNode)
|
|
@@ -1745,241 +1392,19 @@ export class Matterbridge extends EventEmitter {
|
|
|
1745
1392
|
this.frontend.wssSendRefreshRequired('reachability');
|
|
1746
1393
|
}, 60 * 1000).unref();
|
|
1747
1394
|
}
|
|
1748
|
-
// Start the Matter server node of single devices in mode 'server'
|
|
1749
1395
|
for (const device of this.devices.array()) {
|
|
1750
1396
|
if (device.mode === 'server' && device.serverNode) {
|
|
1751
1397
|
this.log.debug(`Starting server node for device ${dev}${device.deviceName}${db} in server mode...`);
|
|
1752
|
-
this.startServerNode(device.serverNode);
|
|
1398
|
+
this.startServerNode(device.serverNode);
|
|
1753
1399
|
}
|
|
1754
1400
|
}
|
|
1755
|
-
// Logger.get('LogServerNode').info(this.serverNode);
|
|
1756
1401
|
this.emit('childbridge_started');
|
|
1757
1402
|
this.log.notice('Matterbridge childbridge started successfully');
|
|
1758
1403
|
}, this.startMatterIntervalMs);
|
|
1759
1404
|
}
|
|
1760
|
-
/**
|
|
1761
|
-
* Starts the Matterbridge controller.
|
|
1762
|
-
*
|
|
1763
|
-
* @private
|
|
1764
|
-
* @returns {Promise<void>} A promise that resolves when the Matterbridge is started.
|
|
1765
|
-
*/
|
|
1766
1405
|
async startController() {
|
|
1767
|
-
/*
|
|
1768
|
-
if (!this.matterStorageManager) {
|
|
1769
|
-
this.log.error('No storage manager initialized');
|
|
1770
|
-
await this.cleanup('No storage manager initialized');
|
|
1771
|
-
return;
|
|
1772
|
-
}
|
|
1773
|
-
this.log.info('Creating context: mattercontrollerContext');
|
|
1774
|
-
this.controllerContext = this.matterStorageManager.createContext('mattercontrollerContext');
|
|
1775
|
-
if (!this.controllerContext) {
|
|
1776
|
-
this.log.error('No storage context mattercontrollerContext initialized');
|
|
1777
|
-
await this.cleanup('No storage context mattercontrollerContext initialized');
|
|
1778
|
-
return;
|
|
1779
|
-
}
|
|
1780
|
-
|
|
1781
|
-
this.log.debug('Starting matterbridge in mode', this.bridgeMode);
|
|
1782
|
-
this.matterServer = await this.createMatterServer(this.storageManager);
|
|
1783
|
-
this.log.info('Creating matter commissioning controller');
|
|
1784
|
-
this.commissioningController = new CommissioningController({
|
|
1785
|
-
autoConnect: false,
|
|
1786
|
-
});
|
|
1787
|
-
this.log.info('Adding matter commissioning controller to matter server');
|
|
1788
|
-
await this.matterServer.addCommissioningController(this.commissioningController);
|
|
1789
|
-
|
|
1790
|
-
this.log.info('Starting matter server');
|
|
1791
|
-
await this.matterServer.start();
|
|
1792
|
-
this.log.info('Matter server started');
|
|
1793
|
-
const commissioningOptions: ControllerCommissioningFlowOptions = {
|
|
1794
|
-
regulatoryLocation: GeneralCommissioning.RegulatoryLocationType.IndoorOutdoor,
|
|
1795
|
-
regulatoryCountryCode: 'XX',
|
|
1796
|
-
};
|
|
1797
|
-
const commissioningController = new CommissioningController({
|
|
1798
|
-
environment: {
|
|
1799
|
-
environment,
|
|
1800
|
-
id: uniqueId,
|
|
1801
|
-
},
|
|
1802
|
-
autoConnect: false, // Do not auto connect to the commissioned nodes
|
|
1803
|
-
adminFabricLabel,
|
|
1804
|
-
});
|
|
1805
|
-
|
|
1806
|
-
if (hasParameter('pairingcode')) {
|
|
1807
|
-
this.log.info('Pairing device with pairingcode:', getParameter('pairingcode'));
|
|
1808
|
-
const pairingCode = getParameter('pairingcode');
|
|
1809
|
-
const ip = this.controllerContext.has('ip') ? this.controllerContext.get<string>('ip') : undefined;
|
|
1810
|
-
const port = this.controllerContext.has('port') ? this.controllerContext.get<number>('port') : undefined;
|
|
1811
|
-
|
|
1812
|
-
let longDiscriminator, setupPin, shortDiscriminator;
|
|
1813
|
-
if (pairingCode !== undefined) {
|
|
1814
|
-
const pairingCodeCodec = ManualPairingCodeCodec.decode(pairingCode);
|
|
1815
|
-
shortDiscriminator = pairingCodeCodec.shortDiscriminator;
|
|
1816
|
-
longDiscriminator = undefined;
|
|
1817
|
-
setupPin = pairingCodeCodec.passcode;
|
|
1818
|
-
this.log.info(`Data extracted from pairing code: ${Logger.toJSON(pairingCodeCodec)}`);
|
|
1819
|
-
} else {
|
|
1820
|
-
longDiscriminator = await this.controllerContext.get('longDiscriminator', 3840);
|
|
1821
|
-
if (longDiscriminator > 4095) throw new Error('Discriminator value must be less than 4096');
|
|
1822
|
-
setupPin = this.controllerContext.get('pin', 20202021);
|
|
1823
|
-
}
|
|
1824
|
-
if ((shortDiscriminator === undefined && longDiscriminator === undefined) || setupPin === undefined) {
|
|
1825
|
-
throw new Error('Please specify the longDiscriminator of the device to commission with -longDiscriminator or provide a valid passcode with -passcode');
|
|
1826
|
-
}
|
|
1827
|
-
|
|
1828
|
-
const options = {
|
|
1829
|
-
commissioning: commissioningOptions,
|
|
1830
|
-
discovery: {
|
|
1831
|
-
knownAddress: ip !== undefined && port !== undefined ? { ip, port, type: 'udp' } : undefined,
|
|
1832
|
-
identifierData: longDiscriminator !== undefined ? { longDiscriminator } : shortDiscriminator !== undefined ? { shortDiscriminator } : {},
|
|
1833
|
-
},
|
|
1834
|
-
passcode: setupPin,
|
|
1835
|
-
} as NodeCommissioningOptions;
|
|
1836
|
-
this.log.info('Commissioning with options:', options);
|
|
1837
|
-
const nodeId = await this.commissioningController.commissionNode(options);
|
|
1838
|
-
this.log.info(`Commissioning successfully done with nodeId: ${nodeId}`);
|
|
1839
|
-
this.log.info('ActiveSessionInformation:', this.commissioningController.getActiveSessionInformation());
|
|
1840
|
-
} // (hasParameter('pairingcode'))
|
|
1841
|
-
|
|
1842
|
-
if (hasParameter('unpairall')) {
|
|
1843
|
-
this.log.info('***Commissioning controller unpairing all nodes...');
|
|
1844
|
-
const nodeIds = this.commissioningController.getCommissionedNodes();
|
|
1845
|
-
for (const nodeId of nodeIds) {
|
|
1846
|
-
this.log.info('***Commissioning controller unpairing node:', nodeId);
|
|
1847
|
-
await this.commissioningController.removeNode(nodeId);
|
|
1848
|
-
}
|
|
1849
|
-
return;
|
|
1850
|
-
}
|
|
1851
|
-
|
|
1852
|
-
if (hasParameter('discover')) {
|
|
1853
|
-
// const discover = await this.commissioningController.discoverCommissionableDevices({ productId: 0x8000, deviceType: 0xfff1 });
|
|
1854
|
-
// console.log(discover);
|
|
1855
|
-
}
|
|
1856
|
-
|
|
1857
|
-
if (!this.commissioningController.isCommissioned()) {
|
|
1858
|
-
this.log.info('***Commissioning controller is not commissioned: use matterbridge -controller -pairingcode [pairingcode] to commission a device');
|
|
1859
|
-
return;
|
|
1860
|
-
}
|
|
1861
|
-
|
|
1862
|
-
const nodeIds = this.commissioningController.getCommissionedNodes();
|
|
1863
|
-
this.log.info(`***Commissioning controller is commissioned ${this.commissioningController.isCommissioned()} and has ${nodeIds.length} nodes commisioned: `);
|
|
1864
|
-
for (const nodeId of nodeIds) {
|
|
1865
|
-
this.log.info(`***Connecting to commissioned node: ${nodeId}`);
|
|
1866
|
-
|
|
1867
|
-
const node = await this.commissioningController.connectNode(nodeId, {
|
|
1868
|
-
autoSubscribe: false,
|
|
1869
|
-
attributeChangedCallback: (peerNodeId, { path: { nodeId, clusterId, endpointId, attributeName }, value }) =>
|
|
1870
|
-
this.log.info(`***Commissioning controller attributeChangedCallback ${peerNodeId}: attribute ${nodeId}/${endpointId}/${clusterId}/${attributeName} changed to ${Logger.toJSON(value)}`),
|
|
1871
|
-
eventTriggeredCallback: (peerNodeId, { path: { nodeId, clusterId, endpointId, eventName }, events }) =>
|
|
1872
|
-
this.log.info(`***Commissioning controller eventTriggeredCallback ${peerNodeId}: Event ${nodeId}/${endpointId}/${clusterId}/${eventName} triggered with ${Logger.toJSON(events)}`),
|
|
1873
|
-
stateInformationCallback: (peerNodeId, info) => {
|
|
1874
|
-
switch (info) {
|
|
1875
|
-
case NodeStateInformation.Connected:
|
|
1876
|
-
this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} connected`);
|
|
1877
|
-
break;
|
|
1878
|
-
case NodeStateInformation.Disconnected:
|
|
1879
|
-
this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} disconnected`);
|
|
1880
|
-
break;
|
|
1881
|
-
case NodeStateInformation.Reconnecting:
|
|
1882
|
-
this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} reconnecting`);
|
|
1883
|
-
break;
|
|
1884
|
-
case NodeStateInformation.WaitingForDeviceDiscovery:
|
|
1885
|
-
this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} waiting for device discovery`);
|
|
1886
|
-
break;
|
|
1887
|
-
case NodeStateInformation.StructureChanged:
|
|
1888
|
-
this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} structure changed`);
|
|
1889
|
-
break;
|
|
1890
|
-
case NodeStateInformation.Decommissioned:
|
|
1891
|
-
this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} decommissioned`);
|
|
1892
|
-
break;
|
|
1893
|
-
default:
|
|
1894
|
-
this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} NodeStateInformation.${info}`);
|
|
1895
|
-
break;
|
|
1896
|
-
}
|
|
1897
|
-
},
|
|
1898
|
-
});
|
|
1899
|
-
|
|
1900
|
-
node.logStructure();
|
|
1901
|
-
|
|
1902
|
-
// Get the interaction client
|
|
1903
|
-
this.log.info('Getting the interaction client');
|
|
1904
|
-
const interactionClient = await node.getInteractionClient();
|
|
1905
|
-
let cluster;
|
|
1906
|
-
let attributes;
|
|
1907
|
-
|
|
1908
|
-
// Log BasicInformationCluster
|
|
1909
|
-
cluster = BasicInformationCluster;
|
|
1910
|
-
attributes = await interactionClient.getMultipleAttributes({
|
|
1911
|
-
attributes: [{ clusterId: cluster.id }],
|
|
1912
|
-
});
|
|
1913
|
-
if (attributes.length > 0) this.log.info(`Cluster: ${idn}${cluster.name}${rs}${nf} attributes:`);
|
|
1914
|
-
attributes.forEach((attribute) => {
|
|
1915
|
-
this.log.info(
|
|
1916
|
-
`- 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}`,
|
|
1917
|
-
);
|
|
1918
|
-
});
|
|
1919
|
-
|
|
1920
|
-
// Log PowerSourceCluster
|
|
1921
|
-
cluster = PowerSourceCluster;
|
|
1922
|
-
attributes = await interactionClient.getMultipleAttributes({
|
|
1923
|
-
attributes: [{ clusterId: cluster.id }],
|
|
1924
|
-
});
|
|
1925
|
-
if (attributes.length > 0) this.log.info(`Cluster: ${idn}${cluster.name}${rs}${nf} attributes:`);
|
|
1926
|
-
attributes.forEach((attribute) => {
|
|
1927
|
-
this.log.info(
|
|
1928
|
-
`- 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}`,
|
|
1929
|
-
);
|
|
1930
|
-
});
|
|
1931
|
-
|
|
1932
|
-
// Log ThreadNetworkDiagnostics
|
|
1933
|
-
cluster = ThreadNetworkDiagnosticsCluster;
|
|
1934
|
-
attributes = await interactionClient.getMultipleAttributes({
|
|
1935
|
-
attributes: [{ clusterId: cluster.id }],
|
|
1936
|
-
});
|
|
1937
|
-
if (attributes.length > 0) this.log.info(`Cluster: ${idn}${cluster.name}${rs}${nf} attributes:`);
|
|
1938
|
-
attributes.forEach((attribute) => {
|
|
1939
|
-
this.log.info(
|
|
1940
|
-
`- 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}`,
|
|
1941
|
-
);
|
|
1942
|
-
});
|
|
1943
|
-
|
|
1944
|
-
// Log SwitchCluster
|
|
1945
|
-
cluster = SwitchCluster;
|
|
1946
|
-
attributes = await interactionClient.getMultipleAttributes({
|
|
1947
|
-
attributes: [{ clusterId: cluster.id }],
|
|
1948
|
-
});
|
|
1949
|
-
if (attributes.length > 0) this.log.info(`Cluster: ${idn}${cluster.name}${rs}${nf} attributes:`);
|
|
1950
|
-
attributes.forEach((attribute) => {
|
|
1951
|
-
this.log.info(
|
|
1952
|
-
`- 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}`,
|
|
1953
|
-
);
|
|
1954
|
-
});
|
|
1955
|
-
|
|
1956
|
-
this.log.info('Subscribing to all attributes and events');
|
|
1957
|
-
await node.subscribeAllAttributesAndEvents({
|
|
1958
|
-
ignoreInitialTriggers: false,
|
|
1959
|
-
attributeChangedCallback: ({ path: { nodeId, clusterId, endpointId, attributeName }, version, value }) =>
|
|
1960
|
-
this.log.info(
|
|
1961
|
-
`***${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}`,
|
|
1962
|
-
),
|
|
1963
|
-
eventTriggeredCallback: ({ path: { nodeId, clusterId, endpointId, eventName }, events }) => {
|
|
1964
|
-
this.log.info(
|
|
1965
|
-
`***${db}Commissioning controller eventTriggeredCallback: event ${BLUE}${nodeId}${db}/${or}${endpointId}${db}/${hk}${getClusterNameById(clusterId)}${db}/${zb}${eventName}${db} triggered with ${debugStringify(events ?? { none: true })}`,
|
|
1966
|
-
);
|
|
1967
|
-
},
|
|
1968
|
-
});
|
|
1969
|
-
this.log.info('Subscribed to all attributes and events');
|
|
1970
|
-
}
|
|
1971
|
-
*/
|
|
1972
1406
|
}
|
|
1973
|
-
/** */
|
|
1974
|
-
/** Matter.js methods */
|
|
1975
|
-
/** */
|
|
1976
|
-
/**
|
|
1977
|
-
* Starts the matter storage with name Matterbridge, create the matterbridge context and performs a backup.
|
|
1978
|
-
*
|
|
1979
|
-
* @returns {Promise<void>} - A promise that resolves when the storage is started.
|
|
1980
|
-
*/
|
|
1981
1407
|
async startMatterStorage() {
|
|
1982
|
-
// Setup Matter storage
|
|
1983
1408
|
this.log.info(`Starting matter node storage...`);
|
|
1984
1409
|
this.matterStorageService = this.environment.get(StorageService);
|
|
1985
1410
|
this.log.info(`Matter node storage service created: ${this.matterStorageService.location}`);
|
|
@@ -1988,17 +1413,8 @@ export class Matterbridge extends EventEmitter {
|
|
|
1988
1413
|
this.matterbridgeContext = await this.createServerNodeContext('Matterbridge', 'Matterbridge', this.aggregatorDeviceType, this.aggregatorVendorId, this.aggregatorVendorName, this.aggregatorProductId, this.aggregatorProductName, this.aggregatorSerialNumber, this.aggregatorUniqueId);
|
|
1989
1414
|
this.matterbridgeInformation.matterbridgeSerialNumber = await this.matterbridgeContext.get('serialNumber', '');
|
|
1990
1415
|
this.log.info('Matter node storage started');
|
|
1991
|
-
// Backup matter storage since it is created/opened correctly
|
|
1992
1416
|
await this.backupMatterStorage(path.join(this.matterbridgeDirectory, this.matterStorageName), path.join(this.matterbridgeDirectory, this.matterStorageName + '.backup'));
|
|
1993
1417
|
}
|
|
1994
|
-
/**
|
|
1995
|
-
* Makes a backup copy of the specified matter storage directory.
|
|
1996
|
-
*
|
|
1997
|
-
* @param {string} storageName - The name of the storage directory to be backed up.
|
|
1998
|
-
* @param {string} backupName - The name of the backup directory to be created.
|
|
1999
|
-
* @private
|
|
2000
|
-
* @returns {Promise<void>} A promise that resolves when the has been done.
|
|
2001
|
-
*/
|
|
2002
1418
|
async backupMatterStorage(storageName, backupName) {
|
|
2003
1419
|
this.log.info('Creating matter node storage backup...');
|
|
2004
1420
|
try {
|
|
@@ -2009,11 +1425,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2009
1425
|
this.log.error(`Error creating matter node storage backup from ${storageName} to ${backupName}:`, error);
|
|
2010
1426
|
}
|
|
2011
1427
|
}
|
|
2012
|
-
/**
|
|
2013
|
-
* Stops the matter storage.
|
|
2014
|
-
*
|
|
2015
|
-
* @returns {Promise<void>} A promise that resolves when the storage is stopped.
|
|
2016
|
-
*/
|
|
2017
1428
|
async stopMatterStorage() {
|
|
2018
1429
|
this.log.info('Closing matter node storage...');
|
|
2019
1430
|
await this.matterStorageManager?.close();
|
|
@@ -2022,20 +1433,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2022
1433
|
this.matterbridgeContext = undefined;
|
|
2023
1434
|
this.log.info('Matter node storage closed');
|
|
2024
1435
|
}
|
|
2025
|
-
/**
|
|
2026
|
-
* Creates a server node storage context.
|
|
2027
|
-
*
|
|
2028
|
-
* @param {string} pluginName - The name of the plugin.
|
|
2029
|
-
* @param {string} deviceName - The name of the device.
|
|
2030
|
-
* @param {DeviceTypeId} deviceType - The device type of the device.
|
|
2031
|
-
* @param {number} vendorId - The vendor ID.
|
|
2032
|
-
* @param {string} vendorName - The vendor name.
|
|
2033
|
-
* @param {number} productId - The product ID.
|
|
2034
|
-
* @param {string} productName - The product name.
|
|
2035
|
-
* @param {string} [serialNumber] - The serial number of the device (optional).
|
|
2036
|
-
* @param {string} [uniqueId] - The unique ID of the device (optional).
|
|
2037
|
-
* @returns {Promise<StorageContext>} The storage context for the commissioning server.
|
|
2038
|
-
*/
|
|
2039
1436
|
async createServerNodeContext(pluginName, deviceName, deviceType, vendorId, vendorName, productId, productName, serialNumber, uniqueId) {
|
|
2040
1437
|
const { randomBytes } = await import('node:crypto');
|
|
2041
1438
|
if (!this.matterStorageService)
|
|
@@ -2069,15 +1466,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2069
1466
|
this.log.debug(`- hardwareVersion: ${await storageContext.get('hardwareVersion')} hardwareVersionString: ${await storageContext.get('hardwareVersionString')}`);
|
|
2070
1467
|
return storageContext;
|
|
2071
1468
|
}
|
|
2072
|
-
/**
|
|
2073
|
-
* Creates a server node.
|
|
2074
|
-
*
|
|
2075
|
-
* @param {StorageContext} storageContext - The storage context for the server node.
|
|
2076
|
-
* @param {number} [port] - The port number for the server node. Defaults to 5540.
|
|
2077
|
-
* @param {number} [passcode] - The passcode for the server node. Defaults to 20242025.
|
|
2078
|
-
* @param {number} [discriminator] - The discriminator for the server node. Defaults to 3850.
|
|
2079
|
-
* @returns {Promise<ServerNode<ServerNode.RootEndpoint>>} A promise that resolves to the created server node.
|
|
2080
|
-
*/
|
|
2081
1469
|
async createServerNode(storageContext, port = 5540, passcode = 20242025, discriminator = 3850) {
|
|
2082
1470
|
const storeId = await storageContext.get('storeId');
|
|
2083
1471
|
this.log.notice(`Creating server node for ${storeId} on port ${port} with passcode ${passcode} and discriminator ${discriminator}...`);
|
|
@@ -2087,37 +1475,24 @@ export class Matterbridge extends EventEmitter {
|
|
|
2087
1475
|
this.log.debug(`- uniqueId: ${await storageContext.get('uniqueId')}`);
|
|
2088
1476
|
this.log.debug(`- softwareVersion: ${await storageContext.get('softwareVersion')} softwareVersionString: ${await storageContext.get('softwareVersionString')}`);
|
|
2089
1477
|
this.log.debug(`- hardwareVersion: ${await storageContext.get('hardwareVersion')} hardwareVersionString: ${await storageContext.get('hardwareVersionString')}`);
|
|
2090
|
-
/**
|
|
2091
|
-
* Create a Matter ServerNode, which contains the Root Endpoint and all relevant data and configuration
|
|
2092
|
-
*/
|
|
2093
1478
|
const serverNode = await ServerNode.create({
|
|
2094
|
-
// Required: Give the Node a unique ID which is used to store the state of this node
|
|
2095
1479
|
id: storeId,
|
|
2096
|
-
// Provide Network relevant configuration like the port
|
|
2097
|
-
// Optional when operating only one device on a host, Default port is 5540
|
|
2098
1480
|
network: {
|
|
2099
1481
|
listeningAddressIpv4: this.ipv4address,
|
|
2100
1482
|
listeningAddressIpv6: this.ipv6address,
|
|
2101
1483
|
port,
|
|
2102
1484
|
},
|
|
2103
|
-
// Provide the certificate for the device
|
|
2104
1485
|
operationalCredentials: {
|
|
2105
1486
|
certification: this.certification,
|
|
2106
1487
|
},
|
|
2107
|
-
// Provide Commissioning relevant settings
|
|
2108
|
-
// Optional for development/testing purposes
|
|
2109
1488
|
commissioning: {
|
|
2110
1489
|
passcode,
|
|
2111
1490
|
discriminator,
|
|
2112
1491
|
},
|
|
2113
|
-
// Provide Node announcement settings
|
|
2114
|
-
// Optional: If Ommitted some development defaults are used
|
|
2115
1492
|
productDescription: {
|
|
2116
1493
|
name: await storageContext.get('deviceName'),
|
|
2117
1494
|
deviceType: DeviceTypeId(await storageContext.get('deviceType')),
|
|
2118
1495
|
},
|
|
2119
|
-
// Provide defaults for the BasicInformation cluster on the Root endpoint
|
|
2120
|
-
// Optional: If Omitted some development defaults are used
|
|
2121
1496
|
basicInformation: {
|
|
2122
1497
|
vendorId: VendorId(await storageContext.get('vendorId')),
|
|
2123
1498
|
vendorName: await storageContext.get('vendorName'),
|
|
@@ -2134,20 +1509,14 @@ export class Matterbridge extends EventEmitter {
|
|
|
2134
1509
|
reachable: true,
|
|
2135
1510
|
},
|
|
2136
1511
|
});
|
|
2137
|
-
/**
|
|
2138
|
-
* This event is triggered when the device is initially commissioned successfully.
|
|
2139
|
-
* This means: It is added to the first fabric.
|
|
2140
|
-
*/
|
|
2141
1512
|
serverNode.lifecycle.commissioned.on(() => {
|
|
2142
1513
|
this.log.notice(`Server node for ${storeId} was initially commissioned successfully!`);
|
|
2143
1514
|
clearTimeout(this.endAdvertiseTimeout);
|
|
2144
1515
|
});
|
|
2145
|
-
/** This event is triggered when all fabrics are removed from the device, usually it also does a factory reset then. */
|
|
2146
1516
|
serverNode.lifecycle.decommissioned.on(() => {
|
|
2147
1517
|
this.log.notice(`Server node for ${storeId} was fully decommissioned successfully!`);
|
|
2148
1518
|
clearTimeout(this.endAdvertiseTimeout);
|
|
2149
1519
|
});
|
|
2150
|
-
/** This event is triggered when the device went online. This means that it is discoverable in the network. */
|
|
2151
1520
|
serverNode.lifecycle.online.on(async () => {
|
|
2152
1521
|
this.log.notice(`Server node for ${storeId} is online`);
|
|
2153
1522
|
if (!serverNode.lifecycle.isCommissioned) {
|
|
@@ -2155,11 +1524,9 @@ export class Matterbridge extends EventEmitter {
|
|
|
2155
1524
|
const { qrPairingCode, manualPairingCode } = serverNode.state.commissioning.pairingCodes;
|
|
2156
1525
|
this.log.notice(`QR Code URL: https://project-chip.github.io/connectedhomeip/qrcode.html?data=${qrPairingCode}`);
|
|
2157
1526
|
this.log.notice(`Manual pairing code: ${manualPairingCode}`);
|
|
2158
|
-
// Set a timeout to show that advertising stops after 15 minutes if not commissioned
|
|
2159
1527
|
this.startEndAdvertiseTimer(serverNode);
|
|
2160
1528
|
}
|
|
2161
1529
|
else {
|
|
2162
|
-
// istanbul ignore next
|
|
2163
1530
|
this.log.notice(`Server node for ${storeId} is already commissioned. Waiting for controllers to connect ...`);
|
|
2164
1531
|
}
|
|
2165
1532
|
this.frontend.wssSendRefreshRequired('plugins');
|
|
@@ -2167,19 +1534,14 @@ export class Matterbridge extends EventEmitter {
|
|
|
2167
1534
|
this.frontend.wssSendSnackbarMessage(`${storeId} is online`, 5, 'success');
|
|
2168
1535
|
this.emit('online', storeId);
|
|
2169
1536
|
});
|
|
2170
|
-
/** This event is triggered when the device went offline. it is not longer discoverable or connectable in the network. */
|
|
2171
1537
|
serverNode.lifecycle.offline.on(() => {
|
|
2172
1538
|
this.log.notice(`Server node for ${storeId} is offline`);
|
|
2173
|
-
this.matterbridgeInformation.matterbridgeEndAdvertise = true;
|
|
1539
|
+
this.matterbridgeInformation.matterbridgeEndAdvertise = true;
|
|
2174
1540
|
this.frontend.wssSendRefreshRequired('plugins');
|
|
2175
1541
|
this.frontend.wssSendRefreshRequired('settings');
|
|
2176
1542
|
this.frontend.wssSendSnackbarMessage(`${storeId} is offline`, 5, 'warning');
|
|
2177
1543
|
this.emit('offline', storeId);
|
|
2178
1544
|
});
|
|
2179
|
-
/**
|
|
2180
|
-
* This event is triggered when a fabric is added, removed or updated on the device. Use this if more granular
|
|
2181
|
-
* information is needed.
|
|
2182
|
-
*/
|
|
2183
1545
|
serverNode.events.commissioning.fabricsChanged.on((fabricIndex, fabricAction) => {
|
|
2184
1546
|
let action = '';
|
|
2185
1547
|
switch (fabricAction) {
|
|
@@ -2196,22 +1558,14 @@ export class Matterbridge extends EventEmitter {
|
|
|
2196
1558
|
this.log.notice(`Commissioned fabric index ${fabricIndex} ${action} on server node for ${storeId}: ${debugStringify(serverNode.state.commissioning.fabrics[fabricIndex])}`);
|
|
2197
1559
|
this.frontend.wssSendRefreshRequired('fabrics');
|
|
2198
1560
|
});
|
|
2199
|
-
/**
|
|
2200
|
-
* This event is triggered when an operative new session was opened by a Controller.
|
|
2201
|
-
* It is not triggered for the initial commissioning process, just afterwards for real connections.
|
|
2202
|
-
*/
|
|
2203
1561
|
serverNode.events.sessions.opened.on((session) => {
|
|
2204
1562
|
this.log.notice(`Session opened on server node for ${storeId}: ${debugStringify(session)}`);
|
|
2205
1563
|
this.frontend.wssSendRefreshRequired('sessions');
|
|
2206
1564
|
});
|
|
2207
|
-
/**
|
|
2208
|
-
* This event is triggered when an operative session is closed by a Controller or because the Device goes offline.
|
|
2209
|
-
*/
|
|
2210
1565
|
serverNode.events.sessions.closed.on((session) => {
|
|
2211
1566
|
this.log.notice(`Session closed on server node for ${storeId}: ${debugStringify(session)}`);
|
|
2212
1567
|
this.frontend.wssSendRefreshRequired('sessions');
|
|
2213
1568
|
});
|
|
2214
|
-
/** This event is triggered when a subscription gets added or removed on an operative session. */
|
|
2215
1569
|
serverNode.events.sessions.subscriptionsChanged.on((session) => {
|
|
2216
1570
|
this.log.notice(`Session subscriptions changed on server node for ${storeId}: ${debugStringify(session)}`);
|
|
2217
1571
|
this.frontend.wssSendRefreshRequired('sessions');
|
|
@@ -2219,11 +1573,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2219
1573
|
this.log.info(`Created server node for ${storeId}`);
|
|
2220
1574
|
return serverNode;
|
|
2221
1575
|
}
|
|
2222
|
-
/**
|
|
2223
|
-
* Starts the 15 minutes timer to advice that advertising for the specified server node is ended.
|
|
2224
|
-
*
|
|
2225
|
-
* @param {ServerNode} [matterServerNode] - The server node to start.
|
|
2226
|
-
*/
|
|
2227
1576
|
startEndAdvertiseTimer(matterServerNode) {
|
|
2228
1577
|
if (this.endAdvertiseTimeout) {
|
|
2229
1578
|
this.log.debug(`Clear ${matterServerNode.id} server node end advertise timer`);
|
|
@@ -2242,25 +1591,12 @@ export class Matterbridge extends EventEmitter {
|
|
|
2242
1591
|
this.log.notice(`Advertising on server node for ${matterServerNode.id} stopped. Restart to commission.`);
|
|
2243
1592
|
}, 15 * 60 * 1000).unref();
|
|
2244
1593
|
}
|
|
2245
|
-
/**
|
|
2246
|
-
* Starts the specified server node.
|
|
2247
|
-
*
|
|
2248
|
-
* @param {ServerNode} [matterServerNode] - The server node to start.
|
|
2249
|
-
* @returns {Promise<void>} A promise that resolves when the server node has started.
|
|
2250
|
-
*/
|
|
2251
1594
|
async startServerNode(matterServerNode) {
|
|
2252
1595
|
if (!matterServerNode)
|
|
2253
1596
|
return;
|
|
2254
1597
|
this.log.notice(`Starting ${matterServerNode.id} server node`);
|
|
2255
1598
|
await matterServerNode.start();
|
|
2256
1599
|
}
|
|
2257
|
-
/**
|
|
2258
|
-
* Stops the specified server node.
|
|
2259
|
-
*
|
|
2260
|
-
* @param {ServerNode} matterServerNode - The server node to stop.
|
|
2261
|
-
* @param {number} [timeout] - The timeout in milliseconds for stopping the server node. Defaults to 30 seconds.
|
|
2262
|
-
* @returns {Promise<void>} A promise that resolves when the server node has stopped.
|
|
2263
|
-
*/
|
|
2264
1600
|
async stopServerNode(matterServerNode, timeout = 30000) {
|
|
2265
1601
|
if (!matterServerNode)
|
|
2266
1602
|
return;
|
|
@@ -2273,12 +1609,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2273
1609
|
this.log.error(`Failed to close ${matterServerNode.id} server node: ${error instanceof Error ? error.message : error}`);
|
|
2274
1610
|
}
|
|
2275
1611
|
}
|
|
2276
|
-
/**
|
|
2277
|
-
* Advertises the specified server node.
|
|
2278
|
-
*
|
|
2279
|
-
* @param {ServerNode} [matterServerNode] - The server node to advertise.
|
|
2280
|
-
* @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.
|
|
2281
|
-
*/
|
|
2282
1612
|
async advertiseServerNode(matterServerNode) {
|
|
2283
1613
|
if (matterServerNode) {
|
|
2284
1614
|
await matterServerNode.env.get(DeviceCommissioner)?.allowBasicCommissioning();
|
|
@@ -2287,39 +1617,19 @@ export class Matterbridge extends EventEmitter {
|
|
|
2287
1617
|
return { qrPairingCode, manualPairingCode };
|
|
2288
1618
|
}
|
|
2289
1619
|
}
|
|
2290
|
-
/**
|
|
2291
|
-
* Stop advertise the specified server node.
|
|
2292
|
-
*
|
|
2293
|
-
* @param {ServerNode} [matterServerNode] - The server node to advertise.
|
|
2294
|
-
* @returns {Promise<void>} A promise that resolves when the server node has stopped advertising.
|
|
2295
|
-
*/
|
|
2296
1620
|
async stopAdvertiseServerNode(matterServerNode) {
|
|
2297
1621
|
if (matterServerNode && matterServerNode.lifecycle.isOnline) {
|
|
2298
1622
|
await matterServerNode.env.get(DeviceCommissioner)?.endCommissioning();
|
|
2299
1623
|
this.log.notice(`Stopped advertising for ${matterServerNode.id}`);
|
|
2300
1624
|
}
|
|
2301
1625
|
}
|
|
2302
|
-
/**
|
|
2303
|
-
* Creates an aggregator node with the specified storage context.
|
|
2304
|
-
*
|
|
2305
|
-
* @param {StorageContext} storageContext - The storage context for the aggregator node.
|
|
2306
|
-
* @returns {Promise<Endpoint<AggregatorEndpoint>>} A promise that resolves to the created aggregator node.
|
|
2307
|
-
*/
|
|
2308
1626
|
async createAggregatorNode(storageContext) {
|
|
2309
1627
|
this.log.notice(`Creating ${await storageContext.get('storeId')} aggregator...`);
|
|
2310
1628
|
const aggregatorNode = new Endpoint(AggregatorEndpoint, { id: `${await storageContext.get('storeId')}` });
|
|
2311
1629
|
this.log.info(`Created ${await storageContext.get('storeId')} aggregator`);
|
|
2312
1630
|
return aggregatorNode;
|
|
2313
1631
|
}
|
|
2314
|
-
/**
|
|
2315
|
-
* Adds a MatterbridgeEndpoint to the specified plugin.
|
|
2316
|
-
*
|
|
2317
|
-
* @param {string} pluginName - The name of the plugin.
|
|
2318
|
-
* @param {MatterbridgeEndpoint} device - The device to add as a bridged endpoint.
|
|
2319
|
-
* @returns {Promise<void>} A promise that resolves when the bridged endpoint has been added.
|
|
2320
|
-
*/
|
|
2321
1632
|
async addBridgedEndpoint(pluginName, device) {
|
|
2322
|
-
// Check if the plugin is registered
|
|
2323
1633
|
const plugin = this.plugins.get(pluginName);
|
|
2324
1634
|
if (!plugin) {
|
|
2325
1635
|
this.log.error(`Error adding bridged endpoint ${dev}${device.deviceName}${er} (${zb}${device.id}${er}) plugin ${plg}${pluginName}${er} not found`);
|
|
@@ -2339,7 +1649,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2339
1649
|
}
|
|
2340
1650
|
else if (this.bridgeMode === 'bridge') {
|
|
2341
1651
|
if (device.mode === 'matter') {
|
|
2342
|
-
// Register and add the device to the matterbridge server node
|
|
2343
1652
|
this.log.debug(`Adding matter endpoint ${plg}${pluginName}${db}:${dev}${device.deviceName}${db} to Matterbridge server node...`);
|
|
2344
1653
|
if (!this.serverNode) {
|
|
2345
1654
|
this.log.error('Server node not found for Matterbridge');
|
|
@@ -2356,7 +1665,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2356
1665
|
}
|
|
2357
1666
|
}
|
|
2358
1667
|
else {
|
|
2359
|
-
// Register and add the device to the matterbridge aggregator node
|
|
2360
1668
|
this.log.debug(`Adding bridged endpoint ${plg}${pluginName}${db}:${dev}${device.deviceName}${db} to Matterbridge aggregator node`);
|
|
2361
1669
|
if (!this.aggregatorNode) {
|
|
2362
1670
|
this.log.error('Aggregator node not found for Matterbridge');
|
|
@@ -2374,7 +1682,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2374
1682
|
}
|
|
2375
1683
|
}
|
|
2376
1684
|
else if (this.bridgeMode === 'childbridge') {
|
|
2377
|
-
// Register and add the device to the plugin server node
|
|
2378
1685
|
if (plugin.type === 'AccessoryPlatform') {
|
|
2379
1686
|
try {
|
|
2380
1687
|
this.log.debug(`Creating endpoint ${dev}${device.deviceName}${db} for AccessoryPlatform plugin ${plg}${plugin.name}${db} server node`);
|
|
@@ -2398,12 +1705,10 @@ export class Matterbridge extends EventEmitter {
|
|
|
2398
1705
|
return;
|
|
2399
1706
|
}
|
|
2400
1707
|
}
|
|
2401
|
-
// Register and add the device to the plugin aggregator node
|
|
2402
1708
|
if (plugin.type === 'DynamicPlatform') {
|
|
2403
1709
|
try {
|
|
2404
1710
|
this.log.debug(`Adding bridged endpoint ${dev}${device.deviceName}${db} for DynamicPlatform plugin ${plg}${plugin.name}${db} aggregator node`);
|
|
2405
1711
|
await this.createDynamicPlugin(plugin);
|
|
2406
|
-
// Fast plugins can add another device before the server node is ready, so we wait for the server node to be ready
|
|
2407
1712
|
await waiter(`createDynamicPlugin(${plugin.name})`, () => plugin.serverNode?.hasParts === true);
|
|
2408
1713
|
if (!plugin.aggregatorNode) {
|
|
2409
1714
|
this.log.error(`Aggregator node not found for plugin ${plg}${plugin.name}${er}`);
|
|
@@ -2426,28 +1731,17 @@ export class Matterbridge extends EventEmitter {
|
|
|
2426
1731
|
plugin.registeredDevices++;
|
|
2427
1732
|
if (plugin.addedDevices !== undefined)
|
|
2428
1733
|
plugin.addedDevices++;
|
|
2429
|
-
// Add the device to the DeviceManager
|
|
2430
1734
|
this.devices.set(device);
|
|
2431
|
-
// Subscribe to the reachable$Changed event
|
|
2432
1735
|
await this.subscribeAttributeChanged(plugin, device);
|
|
2433
1736
|
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}`);
|
|
2434
1737
|
}
|
|
2435
|
-
/**
|
|
2436
|
-
* Removes a MatterbridgeEndpoint from the specified plugin.
|
|
2437
|
-
*
|
|
2438
|
-
* @param {string} pluginName - The name of the plugin.
|
|
2439
|
-
* @param {MatterbridgeEndpoint} device - The device to remove as a bridged endpoint.
|
|
2440
|
-
* @returns {Promise<void>} A promise that resolves when the bridged endpoint has been removed.
|
|
2441
|
-
*/
|
|
2442
1738
|
async removeBridgedEndpoint(pluginName, device) {
|
|
2443
1739
|
this.log.debug(`Removing bridged endpoint ${plg}${pluginName}${db}:${dev}${device.deviceName}${db} (${zb}${device.name}${db}) for plugin ${plg}${pluginName}${db}`);
|
|
2444
|
-
// Check if the plugin is registered
|
|
2445
1740
|
const plugin = this.plugins.get(pluginName);
|
|
2446
1741
|
if (!plugin) {
|
|
2447
1742
|
this.log.error(`Error removing bridged endpoint ${dev}${device.deviceName}${er} (${zb}${device.name}${er}) for plugin ${plg}${pluginName}${er}: plugin not found`);
|
|
2448
1743
|
return;
|
|
2449
1744
|
}
|
|
2450
|
-
// Register and add the device to the matterbridge aggregator node
|
|
2451
1745
|
if (this.bridgeMode === 'bridge') {
|
|
2452
1746
|
if (!this.aggregatorNode) {
|
|
2453
1747
|
this.log.error(`Error removing bridged endpoint ${dev}${device.deviceName}${er} (${zb}${device.name}${er}) for plugin ${plg}${pluginName}${er}: aggregator node not found`);
|
|
@@ -2462,7 +1756,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2462
1756
|
}
|
|
2463
1757
|
else if (this.bridgeMode === 'childbridge') {
|
|
2464
1758
|
if (plugin.type === 'AccessoryPlatform') {
|
|
2465
|
-
// Nothing to do here since the server node has no aggregator node but only the device itself
|
|
2466
1759
|
}
|
|
2467
1760
|
else if (plugin.type === 'DynamicPlatform') {
|
|
2468
1761
|
if (!plugin.aggregatorNode) {
|
|
@@ -2477,21 +1770,8 @@ export class Matterbridge extends EventEmitter {
|
|
|
2477
1770
|
if (plugin.addedDevices !== undefined)
|
|
2478
1771
|
plugin.addedDevices--;
|
|
2479
1772
|
}
|
|
2480
|
-
// Remove the device from the DeviceManager
|
|
2481
1773
|
this.devices.remove(device);
|
|
2482
1774
|
}
|
|
2483
|
-
/**
|
|
2484
|
-
* Removes all bridged endpoints from the specified plugin.
|
|
2485
|
-
*
|
|
2486
|
-
* @param {string} pluginName - The name of the plugin.
|
|
2487
|
-
* @param {number} [delay] - The delay in milliseconds between removing each bridged endpoint (default: 0).
|
|
2488
|
-
* @returns {Promise<void>} A promise that resolves when all bridged endpoints have been removed.
|
|
2489
|
-
*
|
|
2490
|
-
* @remarks
|
|
2491
|
-
* This method iterates through all devices in the DeviceManager and removes each bridged endpoint associated with the specified plugin.
|
|
2492
|
-
* It also applies a delay between each removal if specified.
|
|
2493
|
-
* The delay is useful to allow the controllers to receive a single subscription for each device removed.
|
|
2494
|
-
*/
|
|
2495
1775
|
async removeAllBridgedEndpoints(pluginName, delay = 0) {
|
|
2496
1776
|
this.log.debug(`Removing all bridged endpoints for plugin ${plg}${pluginName}${db}${delay > 0 ? ` with delay ${delay} ms` : ''}`);
|
|
2497
1777
|
for (const device of this.devices.array().filter((device) => device.plugin === pluginName)) {
|
|
@@ -2502,15 +1782,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2502
1782
|
if (delay > 0)
|
|
2503
1783
|
await wait(2000);
|
|
2504
1784
|
}
|
|
2505
|
-
/**
|
|
2506
|
-
* Subscribes to the attribute change event for the given device and plugin.
|
|
2507
|
-
* Specifically, it listens for changes in the 'reachable' attribute of the
|
|
2508
|
-
* BridgedDeviceBasicInformationServer cluster server of the bridged device or BasicInformationServer cluster server of server node.
|
|
2509
|
-
*
|
|
2510
|
-
* @param {RegisteredPlugin} plugin - The plugin associated with the device.
|
|
2511
|
-
* @param {MatterbridgeEndpoint} device - The device to subscribe to attribute changes for.
|
|
2512
|
-
* @returns {Promise<void>} A promise that resolves when the subscription is set up.
|
|
2513
|
-
*/
|
|
2514
1785
|
async subscribeAttributeChanged(plugin, device) {
|
|
2515
1786
|
this.log.info(`Subscribing attributes for endpoint ${dev}${device.deviceName}${nf} (${dev}${device.id}${nf}) plugin ${plg}${plugin.name}${nf}`);
|
|
2516
1787
|
if (this.bridgeMode === 'childbridge' && plugin.type === 'AccessoryPlatform' && plugin.serverNode) {
|
|
@@ -2526,12 +1797,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2526
1797
|
});
|
|
2527
1798
|
}
|
|
2528
1799
|
}
|
|
2529
|
-
/**
|
|
2530
|
-
* Sanitizes the fabric information by converting bigint properties to strings because `res.json` doesn't support bigint.
|
|
2531
|
-
*
|
|
2532
|
-
* @param {ExposedFabricInformation[]} fabricInfo - The array of exposed fabric information objects.
|
|
2533
|
-
* @returns {SanitizedExposedFabricInformation[]} An array of sanitized exposed fabric information objects.
|
|
2534
|
-
*/
|
|
2535
1800
|
sanitizeFabricInformations(fabricInfo) {
|
|
2536
1801
|
return fabricInfo.map((info) => {
|
|
2537
1802
|
return {
|
|
@@ -2545,12 +1810,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2545
1810
|
};
|
|
2546
1811
|
});
|
|
2547
1812
|
}
|
|
2548
|
-
/**
|
|
2549
|
-
* Sanitizes the session information by converting bigint properties to strings because `res.json` doesn't support bigint.
|
|
2550
|
-
*
|
|
2551
|
-
* @param {SessionsBehavior.Session[]} sessions - The array of session information objects.
|
|
2552
|
-
* @returns {SanitizedSession[]} An array of sanitized session information objects.
|
|
2553
|
-
*/
|
|
2554
1813
|
sanitizeSessionInformation(sessions) {
|
|
2555
1814
|
return sessions
|
|
2556
1815
|
.filter((session) => session.isPeerActive)
|
|
@@ -2577,21 +1836,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
2577
1836
|
};
|
|
2578
1837
|
});
|
|
2579
1838
|
}
|
|
2580
|
-
/**
|
|
2581
|
-
* Sets the reachability of the specified aggregator node bridged devices and trigger.
|
|
2582
|
-
*
|
|
2583
|
-
* @param {Endpoint<AggregatorEndpoint>} aggregatorNode - The aggregator node to set the reachability for.
|
|
2584
|
-
* @param {boolean} reachable - A boolean indicating the reachability status to set.
|
|
2585
|
-
*/
|
|
2586
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
2587
1839
|
async setAggregatorReachability(aggregatorNode, reachable) {
|
|
2588
|
-
/*
|
|
2589
|
-
for (const child of aggregatorNode.parts) {
|
|
2590
|
-
this.log.debug(`Setting reachability of ${(child as unknown as MatterbridgeEndpoint)?.deviceName} to ${reachable}`);
|
|
2591
|
-
await child.setStateOf(BridgedDeviceBasicInformationServer, { reachable });
|
|
2592
|
-
child.act((agent) => child.eventsOf(BridgedDeviceBasicInformationServer).reachableChanged.emit({ reachableNewValue: true }, agent.context));
|
|
2593
|
-
}
|
|
2594
|
-
*/
|
|
2595
1840
|
}
|
|
2596
1841
|
getVendorIdName = (vendorId) => {
|
|
2597
1842
|
if (!vendorId)
|
|
@@ -2631,11 +1876,10 @@ export class Matterbridge extends EventEmitter {
|
|
|
2631
1876
|
case 0x1488:
|
|
2632
1877
|
vendorName = '(ShortcutLabsFlic)';
|
|
2633
1878
|
break;
|
|
2634
|
-
case 65521:
|
|
1879
|
+
case 65521:
|
|
2635
1880
|
vendorName = '(MatterTest)';
|
|
2636
1881
|
break;
|
|
2637
1882
|
}
|
|
2638
1883
|
return vendorName;
|
|
2639
1884
|
};
|
|
2640
1885
|
}
|
|
2641
|
-
//# sourceMappingURL=matterbridge.js.map
|