matterbridge 2.1.5 → 2.1.6-dev.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +17 -0
- package/README-DOCKER.md +17 -7
- package/dist/cli.js +0 -26
- package/dist/cluster/export.js +0 -2
- package/dist/defaultConfigSchema.js +0 -23
- package/dist/deviceManager.js +1 -94
- package/dist/frontend.js +55 -280
- package/dist/index.js +0 -28
- package/dist/logger/export.js +0 -1
- package/dist/matter/behaviors.js +0 -2
- package/dist/matter/clusters.js +0 -2
- package/dist/matter/devices.js +0 -2
- package/dist/matter/endpoints.js +0 -2
- package/dist/matter/export.js +0 -2
- package/dist/matter/types.js +0 -2
- package/dist/matterbridge.js +68 -765
- package/dist/matterbridgeAccessoryPlatform.js +0 -33
- package/dist/matterbridgeBehaviors.js +1 -32
- package/dist/matterbridgeDeviceTypes.js +11 -112
- package/dist/matterbridgeDynamicPlatform.js +0 -33
- package/dist/matterbridgeEndpoint.js +6 -690
- package/dist/matterbridgeEndpointHelpers.js +9 -117
- package/dist/matterbridgePlatform.js +56 -131
- package/dist/matterbridgeTypes.js +0 -24
- package/dist/pluginManager.js +3 -230
- package/dist/storage/export.js +0 -1
- package/dist/utils/colorUtils.js +2 -205
- package/dist/utils/export.js +0 -1
- package/dist/utils/utils.js +10 -264
- package/frontend/build/asset-manifest.json +3 -3
- package/frontend/build/index.html +1 -1
- package/frontend/build/static/js/{main.cd192588.js → main.a241d4f0.js} +9 -9
- package/frontend/build/static/js/main.a241d4f0.js.map +1 -0
- package/npm-shrinkwrap.json +50 -50
- package/package.json +2 -3
- package/dist/cli.d.ts +0 -25
- package/dist/cli.d.ts.map +0 -1
- package/dist/cli.js.map +0 -1
- package/dist/cluster/export.d.ts +0 -2
- package/dist/cluster/export.d.ts.map +0 -1
- package/dist/cluster/export.js.map +0 -1
- package/dist/defaultConfigSchema.d.ts +0 -27
- package/dist/defaultConfigSchema.d.ts.map +0 -1
- package/dist/defaultConfigSchema.js.map +0 -1
- package/dist/deviceManager.d.ts +0 -114
- package/dist/deviceManager.d.ts.map +0 -1
- package/dist/deviceManager.js.map +0 -1
- package/dist/frontend.d.ts +0 -143
- package/dist/frontend.d.ts.map +0 -1
- package/dist/frontend.js.map +0 -1
- package/dist/index.d.ts +0 -35
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/logger/export.d.ts +0 -2
- package/dist/logger/export.d.ts.map +0 -1
- package/dist/logger/export.js.map +0 -1
- package/dist/matter/behaviors.d.ts +0 -2
- package/dist/matter/behaviors.d.ts.map +0 -1
- package/dist/matter/behaviors.js.map +0 -1
- package/dist/matter/clusters.d.ts +0 -2
- package/dist/matter/clusters.d.ts.map +0 -1
- package/dist/matter/clusters.js.map +0 -1
- package/dist/matter/devices.d.ts +0 -2
- package/dist/matter/devices.d.ts.map +0 -1
- package/dist/matter/devices.js.map +0 -1
- package/dist/matter/endpoints.d.ts +0 -2
- package/dist/matter/endpoints.d.ts.map +0 -1
- package/dist/matter/endpoints.js.map +0 -1
- package/dist/matter/export.d.ts +0 -5
- package/dist/matter/export.d.ts.map +0 -1
- package/dist/matter/export.js.map +0 -1
- package/dist/matter/types.d.ts +0 -3
- package/dist/matter/types.d.ts.map +0 -1
- package/dist/matter/types.js.map +0 -1
- package/dist/matterbridge.d.ts +0 -409
- package/dist/matterbridge.d.ts.map +0 -1
- package/dist/matterbridge.js.map +0 -1
- package/dist/matterbridgeAccessoryPlatform.d.ts +0 -39
- package/dist/matterbridgeAccessoryPlatform.d.ts.map +0 -1
- package/dist/matterbridgeAccessoryPlatform.js.map +0 -1
- package/dist/matterbridgeBehaviors.d.ts +0 -1056
- package/dist/matterbridgeBehaviors.d.ts.map +0 -1
- package/dist/matterbridgeBehaviors.js.map +0 -1
- package/dist/matterbridgeDeviceTypes.d.ts +0 -177
- package/dist/matterbridgeDeviceTypes.d.ts.map +0 -1
- package/dist/matterbridgeDeviceTypes.js.map +0 -1
- package/dist/matterbridgeDynamicPlatform.d.ts +0 -39
- package/dist/matterbridgeDynamicPlatform.d.ts.map +0 -1
- package/dist/matterbridgeDynamicPlatform.js.map +0 -1
- package/dist/matterbridgeEndpoint.d.ts +0 -835
- package/dist/matterbridgeEndpoint.d.ts.map +0 -1
- package/dist/matterbridgeEndpoint.js.map +0 -1
- package/dist/matterbridgeEndpointHelpers.d.ts +0 -2275
- package/dist/matterbridgeEndpointHelpers.d.ts.map +0 -1
- package/dist/matterbridgeEndpointHelpers.js.map +0 -1
- package/dist/matterbridgePlatform.d.ts +0 -159
- package/dist/matterbridgePlatform.d.ts.map +0 -1
- package/dist/matterbridgePlatform.js.map +0 -1
- package/dist/matterbridgeTypes.d.ts +0 -169
- package/dist/matterbridgeTypes.d.ts.map +0 -1
- package/dist/matterbridgeTypes.js.map +0 -1
- package/dist/pluginManager.d.ts +0 -236
- package/dist/pluginManager.d.ts.map +0 -1
- package/dist/pluginManager.js.map +0 -1
- package/dist/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/utils/colorUtils.d.ts +0 -61
- package/dist/utils/colorUtils.d.ts.map +0 -1
- package/dist/utils/colorUtils.js.map +0 -1
- package/dist/utils/export.d.ts +0 -3
- package/dist/utils/export.d.ts.map +0 -1
- package/dist/utils/export.js.map +0 -1
- package/dist/utils/utils.d.ts +0 -231
- package/dist/utils/utils.d.ts.map +0 -1
- package/dist/utils/utils.js.map +0 -1
- package/frontend/build/static/js/main.cd192588.js.map +0 -1
- /package/frontend/build/static/js/{main.cd192588.js.LICENSE.txt → main.a241d4f0.js.LICENSE.txt} +0 -0
package/dist/matterbridge.js
CHANGED
|
@@ -1,26 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* This file contains the class Matterbridge.
|
|
3
|
-
*
|
|
4
|
-
* @file matterbridge.ts
|
|
5
|
-
* @author Luca Liguori
|
|
6
|
-
* @date 2023-12-29
|
|
7
|
-
* @version 1.5.2
|
|
8
|
-
*
|
|
9
|
-
* Copyright 2023, 2024, 2025 Luca Liguori.
|
|
10
|
-
*
|
|
11
|
-
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
12
|
-
* you may not use this file except in compliance with the License.
|
|
13
|
-
* You may obtain a copy of the License at
|
|
14
|
-
*
|
|
15
|
-
* http://www.apache.org/licenses/LICENSE-2.0
|
|
16
|
-
*
|
|
17
|
-
* Unless required by applicable law or agreed to in writing, software
|
|
18
|
-
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
19
|
-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
20
|
-
* See the License for the specific language governing permissions and
|
|
21
|
-
* limitations under the License. *
|
|
22
|
-
*/
|
|
23
|
-
// Node.js modules
|
|
24
1
|
import { fileURLToPath } from 'url';
|
|
25
2
|
import { promises as fs } from 'fs';
|
|
26
3
|
import { exec, spawn } from 'child_process';
|
|
@@ -28,27 +5,20 @@ import EventEmitter from 'events';
|
|
|
28
5
|
import os from 'os';
|
|
29
6
|
import path from 'path';
|
|
30
7
|
import { randomBytes } from 'crypto';
|
|
31
|
-
// NodeStorage and AnsiLogger modules
|
|
32
8
|
import { NodeStorageManager } from './storage/export.js';
|
|
33
9
|
import { AnsiLogger, UNDERLINE, UNDERLINEOFF, YELLOW, db, debugStringify, BRIGHT, RESET, er, nf, rs, wr, RED, GREEN, zb, CYAN, nt } from './logger/export.js';
|
|
34
|
-
// Matterbridge
|
|
35
10
|
import { logInterfaces, copyDirectory, getParameter, getIntParameter, hasParameter, getNpmPackageVersion } from './utils/utils.js';
|
|
36
11
|
import { PluginManager } from './pluginManager.js';
|
|
37
12
|
import { DeviceManager } from './deviceManager.js';
|
|
38
13
|
import { MatterbridgeEndpoint } from './matterbridgeEndpoint.js';
|
|
39
14
|
import { bridge } from './matterbridgeDeviceTypes.js';
|
|
40
15
|
import { Frontend } from './frontend.js';
|
|
41
|
-
// @matter
|
|
42
16
|
import { DeviceTypeId, Endpoint as EndpointNode, Logger, LogLevel as MatterLogLevel, LogFormat as MatterLogFormat, VendorId, StorageService, Environment, ServerNode } from '@matter/main';
|
|
43
17
|
import { DeviceCommissioner, FabricAction, MdnsService, PaseClient } from '@matter/main/protocol';
|
|
44
18
|
import { AggregatorEndpoint } from '@matter/main/endpoints';
|
|
45
|
-
// Default colors
|
|
46
19
|
const plg = '\u001B[38;5;33m';
|
|
47
20
|
const dev = '\u001B[38;5;79m';
|
|
48
21
|
const typ = '\u001B[38;5;207m';
|
|
49
|
-
/**
|
|
50
|
-
* Represents the Matterbridge application.
|
|
51
|
-
*/
|
|
52
22
|
export class Matterbridge extends EventEmitter {
|
|
53
23
|
systemInformation = {
|
|
54
24
|
interfaceName: '',
|
|
@@ -87,7 +57,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
87
57
|
restartMode: '',
|
|
88
58
|
readOnly: hasParameter('readonly'),
|
|
89
59
|
profile: getParameter('profile'),
|
|
90
|
-
loggerLevel: "info"
|
|
60
|
+
loggerLevel: "info",
|
|
91
61
|
fileLogger: false,
|
|
92
62
|
matterLoggerLevel: MatterLogLevel.INFO,
|
|
93
63
|
matterFileLogger: false,
|
|
@@ -122,11 +92,9 @@ export class Matterbridge extends EventEmitter {
|
|
|
122
92
|
plugins;
|
|
123
93
|
devices;
|
|
124
94
|
frontend = new Frontend(this);
|
|
125
|
-
// Matterbridge storage
|
|
126
95
|
nodeStorage;
|
|
127
96
|
nodeContext;
|
|
128
97
|
nodeStorageName = 'storage' + (getParameter('profile') ? '.' + getParameter('profile') : '');
|
|
129
|
-
// Cleanup
|
|
130
98
|
hasCleanupStarted = false;
|
|
131
99
|
initialized = false;
|
|
132
100
|
execRunningCount = 0;
|
|
@@ -138,57 +106,58 @@ export class Matterbridge extends EventEmitter {
|
|
|
138
106
|
sigtermHandler;
|
|
139
107
|
exceptionHandler;
|
|
140
108
|
rejectionHandler;
|
|
141
|
-
// Matter environment
|
|
142
109
|
environment = Environment.default;
|
|
143
|
-
// Matter storage
|
|
144
110
|
matterStorageName = 'matterstorage' + (getParameter('profile') ? '.' + getParameter('profile') : '');
|
|
145
111
|
matterStorageService;
|
|
146
112
|
matterStorageManager;
|
|
147
113
|
matterbridgeContext;
|
|
148
114
|
mattercontrollerContext;
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
discriminator; // first server node discriminator
|
|
115
|
+
mdnsInterface;
|
|
116
|
+
ipv4address;
|
|
117
|
+
ipv6address;
|
|
118
|
+
port;
|
|
119
|
+
passcode;
|
|
120
|
+
discriminator;
|
|
156
121
|
serverNode;
|
|
157
122
|
aggregatorNode;
|
|
158
123
|
aggregatorVendorId = VendorId(getIntParameter('vendorId') ?? 0xfff1);
|
|
159
124
|
aggregatorProductId = getIntParameter('productId') ?? 0x8000;
|
|
160
125
|
static instance;
|
|
161
|
-
// We load asyncronously so is private
|
|
162
126
|
constructor() {
|
|
163
127
|
super();
|
|
164
128
|
}
|
|
165
|
-
/**
|
|
166
|
-
* Retrieves the list of Matterbridge devices.
|
|
167
|
-
* @returns {MatterbridgeEndpoint[]} An array of MatterbridgeDevice objects.
|
|
168
|
-
*/
|
|
169
129
|
getDevices() {
|
|
170
130
|
return this.devices.array();
|
|
171
131
|
}
|
|
172
|
-
/**
|
|
173
|
-
* Retrieves the list of registered plugins.
|
|
174
|
-
* @returns {RegisteredPlugin[]} An array of RegisteredPlugin objects.
|
|
175
|
-
*/
|
|
176
132
|
getPlugins() {
|
|
177
133
|
return this.plugins.array();
|
|
178
134
|
}
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
135
|
+
async setLogLevel(logLevel) {
|
|
136
|
+
if (this.log)
|
|
137
|
+
this.log.logLevel = logLevel;
|
|
138
|
+
this.matterbridgeInformation.loggerLevel = logLevel;
|
|
139
|
+
this.frontend.logLevel = logLevel;
|
|
140
|
+
MatterbridgeEndpoint.logLevel = logLevel;
|
|
141
|
+
if (this.devices)
|
|
142
|
+
this.devices.logLevel = logLevel;
|
|
143
|
+
if (this.plugins)
|
|
144
|
+
this.plugins.logLevel = logLevel;
|
|
145
|
+
for (const plugin of this.plugins) {
|
|
146
|
+
if (!plugin.platform || !plugin.platform.log || !plugin.platform.config)
|
|
147
|
+
continue;
|
|
148
|
+
plugin.platform.log.logLevel = plugin.platform.config.debug === true ? "debug" : this.log.logLevel;
|
|
149
|
+
await plugin.platform.onChangeLoggerLevel(plugin.platform.config.debug === true ? "debug" : this.log.logLevel);
|
|
150
|
+
}
|
|
151
|
+
let callbackLogLevel = "notice";
|
|
152
|
+
if (this.matterbridgeInformation.loggerLevel === "info" || this.matterbridgeInformation.matterLoggerLevel === MatterLogLevel.INFO)
|
|
153
|
+
callbackLogLevel = "info";
|
|
154
|
+
if (this.matterbridgeInformation.loggerLevel === "debug" || this.matterbridgeInformation.matterLoggerLevel === MatterLogLevel.DEBUG)
|
|
155
|
+
callbackLogLevel = "debug";
|
|
156
|
+
AnsiLogger.setGlobalCallback(this.frontend.wssSendMessage.bind(this.frontend), callbackLogLevel);
|
|
157
|
+
this.log.debug(`WebSocketServer logger global callback set to ${callbackLogLevel}`);
|
|
158
|
+
}
|
|
189
159
|
static async loadInstance(initialize = false) {
|
|
190
160
|
if (!Matterbridge.instance) {
|
|
191
|
-
// eslint-disable-next-line no-console
|
|
192
161
|
if (hasParameter('debug'))
|
|
193
162
|
console.log(GREEN + 'Creating a new instance of Matterbridge.', initialize ? 'Initializing...' : 'Not initializing...', rs);
|
|
194
163
|
Matterbridge.instance = new Matterbridge();
|
|
@@ -197,13 +166,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
197
166
|
}
|
|
198
167
|
return Matterbridge.instance;
|
|
199
168
|
}
|
|
200
|
-
/**
|
|
201
|
-
* Call cleanup().
|
|
202
|
-
* @deprecated This method is deprecated and is only used for jest tests.
|
|
203
|
-
*
|
|
204
|
-
*/
|
|
205
169
|
async destroyInstance() {
|
|
206
|
-
// Save server nodes to close
|
|
207
170
|
const servers = [];
|
|
208
171
|
if (this.bridgeMode === 'bridge') {
|
|
209
172
|
if (this.serverNode)
|
|
@@ -215,80 +178,54 @@ export class Matterbridge extends EventEmitter {
|
|
|
215
178
|
servers.push(plugin.serverNode);
|
|
216
179
|
}
|
|
217
180
|
}
|
|
218
|
-
// Cleanup
|
|
219
181
|
await this.cleanup('destroying instance...', false);
|
|
220
|
-
// Close servers mdns service
|
|
221
182
|
for (const server of servers) {
|
|
222
183
|
await server.env.get(MdnsService)[Symbol.asyncDispose]();
|
|
223
184
|
this.log.info(`Closed ${server.id} MdnsService`);
|
|
224
185
|
}
|
|
225
|
-
// Wait for the cleanup to finish
|
|
226
186
|
await new Promise((resolve) => {
|
|
227
187
|
setTimeout(resolve, 1000);
|
|
228
188
|
});
|
|
229
189
|
}
|
|
230
|
-
/**
|
|
231
|
-
* Initializes the Matterbridge application.
|
|
232
|
-
*
|
|
233
|
-
* @remarks
|
|
234
|
-
* This method performs the necessary setup and initialization steps for the Matterbridge application.
|
|
235
|
-
* It displays the help information if the 'help' parameter is provided, sets up the logger, checks the
|
|
236
|
-
* node version, registers signal handlers, initializes storage, and parses the command line.
|
|
237
|
-
*
|
|
238
|
-
* @returns A Promise that resolves when the initialization is complete.
|
|
239
|
-
*/
|
|
240
190
|
async initialize() {
|
|
241
|
-
// Set the restart mode
|
|
242
191
|
if (hasParameter('service'))
|
|
243
192
|
this.restartMode = 'service';
|
|
244
193
|
if (hasParameter('docker'))
|
|
245
194
|
this.restartMode = 'docker';
|
|
246
|
-
// Set the matterbridge directory
|
|
247
195
|
this.homeDirectory = getParameter('homedir') ?? os.homedir();
|
|
248
196
|
this.matterbridgeDirectory = path.join(this.homeDirectory, '.matterbridge');
|
|
249
|
-
// Setup the matter environment
|
|
250
197
|
this.environment.vars.set('log.level', MatterLogLevel.INFO);
|
|
251
198
|
this.environment.vars.set('log.format', MatterLogFormat.ANSI);
|
|
252
199
|
this.environment.vars.set('path.root', path.join(this.matterbridgeDirectory, this.matterStorageName));
|
|
253
200
|
this.environment.vars.set('runtime.signals', false);
|
|
254
201
|
this.environment.vars.set('runtime.exitcode', false);
|
|
255
|
-
|
|
256
|
-
this.log = new AnsiLogger({ logName: 'Matterbridge', logTimestampFormat: 4 /* TimestampFormat.TIME_MILLIS */, logLevel: hasParameter('debug') ? "debug" /* LogLevel.DEBUG */ : "info" /* LogLevel.INFO */ });
|
|
257
|
-
// Register process handlers
|
|
202
|
+
this.log = new AnsiLogger({ logName: 'Matterbridge', logTimestampFormat: 4, logLevel: hasParameter('debug') ? "debug" : "info" });
|
|
258
203
|
this.registerProcessHandlers();
|
|
259
|
-
// Initialize nodeStorage and nodeContext
|
|
260
204
|
try {
|
|
261
205
|
this.log.debug(`Creating node storage manager: ${CYAN}${this.nodeStorageName}${db}`);
|
|
262
206
|
this.nodeStorage = new NodeStorageManager({ dir: path.join(this.matterbridgeDirectory, this.nodeStorageName), writeQueue: false, expiredInterval: undefined, logging: false });
|
|
263
207
|
this.log.debug('Creating node storage context for matterbridge');
|
|
264
208
|
this.nodeContext = await this.nodeStorage.createStorage('matterbridge');
|
|
265
|
-
// TODO: Remove this code when node-persist-manager is updated
|
|
266
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
267
209
|
const keys = (await this.nodeStorage?.storage.keys());
|
|
268
210
|
for (const key of keys) {
|
|
269
211
|
this.log.debug(`Checking node storage manager key: ${CYAN}${key}${db}`);
|
|
270
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
271
212
|
await this.nodeStorage?.storage.get(key);
|
|
272
213
|
}
|
|
273
214
|
const storages = await this.nodeStorage.getStorageNames();
|
|
274
215
|
for (const storage of storages) {
|
|
275
216
|
this.log.debug(`Checking storage: ${CYAN}${storage}${db}`);
|
|
276
217
|
const nodeContext = await this.nodeStorage?.createStorage(storage);
|
|
277
|
-
// TODO: Remove this code when node-persist-manager is updated
|
|
278
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
279
218
|
const keys = (await nodeContext?.storage.keys());
|
|
280
219
|
keys.forEach(async (key) => {
|
|
281
220
|
this.log.debug(`Checking key: ${CYAN}${storage}:${key}${db}`);
|
|
282
221
|
await nodeContext?.get(key);
|
|
283
222
|
});
|
|
284
223
|
}
|
|
285
|
-
// Creating a backup of the node storage since it is not corrupted
|
|
286
224
|
this.log.debug('Creating node storage backup...');
|
|
287
225
|
await copyDirectory(path.join(this.matterbridgeDirectory, this.nodeStorageName), path.join(this.matterbridgeDirectory, this.nodeStorageName + '.backup'));
|
|
288
226
|
this.log.debug('Created node storage backup');
|
|
289
227
|
}
|
|
290
228
|
catch (error) {
|
|
291
|
-
// Restoring the backup of the node storage since it is corrupted
|
|
292
229
|
this.log.error(`Error creating node storage manager and context: ${error instanceof Error ? error.message : error}`);
|
|
293
230
|
if (hasParameter('norestore')) {
|
|
294
231
|
this.log.fatal(`The matterbridge node storage is corrupted. Parameter -norestore found: exiting...`);
|
|
@@ -303,52 +240,47 @@ export class Matterbridge extends EventEmitter {
|
|
|
303
240
|
this.log.fatal('Fatal error creating node storage manager and context for matterbridge');
|
|
304
241
|
throw new Error('Fatal error creating node storage manager and context for matterbridge');
|
|
305
242
|
}
|
|
306
|
-
// Set the first port to use for the commissioning server (will be incremented in childbridge mode)
|
|
307
243
|
this.port = getIntParameter('port') ?? (await this.nodeContext.get('matterport', 5540)) ?? 5540;
|
|
308
|
-
// Set the first passcode to use for the commissioning server (will be incremented in childbridge mode)
|
|
309
244
|
this.passcode = getIntParameter('passcode') ?? (await this.nodeContext.get('matterpasscode')) ?? PaseClient.generateRandomPasscode();
|
|
310
|
-
// Set the first discriminator to use for the commissioning server (will be incremented in childbridge mode)
|
|
311
245
|
this.discriminator = getIntParameter('discriminator') ?? (await this.nodeContext.get('matterdiscriminator')) ?? PaseClient.generateRandomDiscriminator();
|
|
312
246
|
this.log.debug(`Initializing commissioning server for Matterbridge... on port ${this.port} with passcode ${this.passcode} and discriminator ${this.discriminator}`);
|
|
313
|
-
// Set matterbridge logger level (context: matterbridgeLogLevel)
|
|
314
247
|
if (hasParameter('logger')) {
|
|
315
248
|
const level = getParameter('logger');
|
|
316
249
|
if (level === 'debug') {
|
|
317
|
-
this.log.logLevel = "debug"
|
|
250
|
+
this.log.logLevel = "debug";
|
|
318
251
|
}
|
|
319
252
|
else if (level === 'info') {
|
|
320
|
-
this.log.logLevel = "info"
|
|
253
|
+
this.log.logLevel = "info";
|
|
321
254
|
}
|
|
322
255
|
else if (level === 'notice') {
|
|
323
|
-
this.log.logLevel = "notice"
|
|
256
|
+
this.log.logLevel = "notice";
|
|
324
257
|
}
|
|
325
258
|
else if (level === 'warn') {
|
|
326
|
-
this.log.logLevel = "warn"
|
|
259
|
+
this.log.logLevel = "warn";
|
|
327
260
|
}
|
|
328
261
|
else if (level === 'error') {
|
|
329
|
-
this.log.logLevel = "error"
|
|
262
|
+
this.log.logLevel = "error";
|
|
330
263
|
}
|
|
331
264
|
else if (level === 'fatal') {
|
|
332
|
-
this.log.logLevel = "fatal"
|
|
265
|
+
this.log.logLevel = "fatal";
|
|
333
266
|
}
|
|
334
267
|
else {
|
|
335
268
|
this.log.warn(`Invalid matterbridge logger level: ${level}. Using default level "info".`);
|
|
336
|
-
this.log.logLevel = "info"
|
|
269
|
+
this.log.logLevel = "info";
|
|
337
270
|
}
|
|
338
271
|
}
|
|
339
272
|
else {
|
|
340
|
-
this.log.logLevel = await this.nodeContext.get('matterbridgeLogLevel', "info"
|
|
273
|
+
this.log.logLevel = await this.nodeContext.get('matterbridgeLogLevel', "info");
|
|
341
274
|
}
|
|
275
|
+
this.frontend.logLevel = this.log.logLevel;
|
|
342
276
|
MatterbridgeEndpoint.logLevel = this.log.logLevel;
|
|
343
277
|
this.matterbridgeInformation.loggerLevel = this.log.logLevel;
|
|
344
|
-
// Create the file logger for matterbridge (context: matterbridgeFileLog)
|
|
345
278
|
if (hasParameter('filelogger') || (await this.nodeContext.get('matterbridgeFileLog', false))) {
|
|
346
279
|
AnsiLogger.setGlobalLogfile(path.join(this.matterbridgeDirectory, this.matterbrideLoggerFile), this.log.logLevel, true);
|
|
347
280
|
this.matterbridgeInformation.fileLogger = true;
|
|
348
281
|
}
|
|
349
282
|
this.log.notice('Matterbridge is starting...');
|
|
350
283
|
this.log.debug(`Matterbridge logLevel: ${this.log.logLevel} fileLoger: ${this.matterbridgeInformation.fileLogger}.`);
|
|
351
|
-
// Set matter.js logger level, format and logger (context: matterLogLevel)
|
|
352
284
|
if (hasParameter('matterlogger')) {
|
|
353
285
|
const level = getParameter('matterlogger');
|
|
354
286
|
if (level === 'debug') {
|
|
@@ -380,7 +312,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
380
312
|
Logger.format = MatterLogFormat.ANSI;
|
|
381
313
|
Logger.setLogger('default', this.createMatterLogger());
|
|
382
314
|
this.matterbridgeInformation.matterLoggerLevel = Logger.defaultLogLevel;
|
|
383
|
-
// Create the file logger for matter.js (context: matterFileLog)
|
|
384
315
|
if (hasParameter('matterfilelogger') || (await this.nodeContext.get('matterFileLog', false))) {
|
|
385
316
|
this.matterbridgeInformation.matterFileLogger = true;
|
|
386
317
|
Logger.addLogger('matterfilelogger', await this.createMatterFileLogger(path.join(this.matterbridgeDirectory, this.matterLoggerFile), true), {
|
|
@@ -389,7 +320,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
389
320
|
});
|
|
390
321
|
}
|
|
391
322
|
this.log.debug(`Matter logLevel: ${Logger.defaultLogLevel} fileLoger: ${this.matterbridgeInformation.matterFileLogger}.`);
|
|
392
|
-
// Set the interface to use for matter server node mdnsInterface
|
|
393
323
|
if (hasParameter('mdnsinterface')) {
|
|
394
324
|
this.mdnsInterface = getParameter('mdnsinterface');
|
|
395
325
|
}
|
|
@@ -398,7 +328,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
398
328
|
if (this.mdnsInterface === '')
|
|
399
329
|
this.mdnsInterface = undefined;
|
|
400
330
|
}
|
|
401
|
-
// Validate mdnsInterface
|
|
402
331
|
if (this.mdnsInterface) {
|
|
403
332
|
const networkInterfaces = os.networkInterfaces();
|
|
404
333
|
const availableInterfaces = Object.keys(networkInterfaces);
|
|
@@ -412,7 +341,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
412
341
|
}
|
|
413
342
|
if (this.mdnsInterface)
|
|
414
343
|
this.environment.vars.set('mdns.networkInterface', this.mdnsInterface);
|
|
415
|
-
// Set the listeningAddressIpv4 for the matter commissioning server
|
|
416
344
|
if (hasParameter('ipv4address')) {
|
|
417
345
|
this.ipv4address = getParameter('ipv4address');
|
|
418
346
|
}
|
|
@@ -421,7 +349,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
421
349
|
if (this.ipv4address === '')
|
|
422
350
|
this.ipv4address = undefined;
|
|
423
351
|
}
|
|
424
|
-
// Set the listeningAddressIpv6 for the matter commissioning server
|
|
425
352
|
if (hasParameter('ipv6address')) {
|
|
426
353
|
this.ipv6address = getParameter('ipv6address');
|
|
427
354
|
}
|
|
@@ -430,19 +357,14 @@ export class Matterbridge extends EventEmitter {
|
|
|
430
357
|
if (this.ipv6address === '')
|
|
431
358
|
this.ipv6address = undefined;
|
|
432
359
|
}
|
|
433
|
-
// Initialize PluginManager
|
|
434
360
|
this.plugins = new PluginManager(this);
|
|
435
361
|
await this.plugins.loadFromStorage();
|
|
436
362
|
this.plugins.logLevel = this.log.logLevel;
|
|
437
|
-
// Initialize DeviceManager
|
|
438
363
|
this.devices = new DeviceManager(this, this.nodeContext);
|
|
439
364
|
this.devices.logLevel = this.log.logLevel;
|
|
440
|
-
// Get the plugins from node storage and create the plugins node storage contexts
|
|
441
365
|
for (const plugin of this.plugins) {
|
|
442
366
|
const packageJson = await this.plugins.parse(plugin);
|
|
443
367
|
if (packageJson === null && !hasParameter('add') && !hasParameter('remove') && !hasParameter('enable') && !hasParameter('disable') && !hasParameter('reset') && !hasParameter('factoryreset')) {
|
|
444
|
-
// Try to reinstall the plugin from npm (for Docker pull and external plugins)
|
|
445
|
-
// We don't do this when the add and other parameters are set because we shut down the process after adding the plugin
|
|
446
368
|
this.log.info(`Error parsing plugin ${plg}${plugin.name}${nf}. Trying to reinstall it from npm.`);
|
|
447
369
|
try {
|
|
448
370
|
await this.spawnCommand('npm', ['install', '-g', plugin.name, '--omit=dev', '--verbose']);
|
|
@@ -464,7 +386,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
464
386
|
await plugin.nodeContext.set('description', plugin.description);
|
|
465
387
|
await plugin.nodeContext.set('author', plugin.author);
|
|
466
388
|
}
|
|
467
|
-
// Log system info and create .matterbridge directory
|
|
468
389
|
await this.logNodeAndSystemInfo();
|
|
469
390
|
this.log.notice(`Matterbridge version ${this.matterbridgeVersion} ` +
|
|
470
391
|
`${hasParameter('bridge') || (!hasParameter('childbridge') && (await this.nodeContext?.get('bridgeMode', '')) === 'bridge') ? 'mode bridge ' : ''}` +
|
|
@@ -472,7 +393,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
472
393
|
`${hasParameter('controller') ? 'mode controller ' : ''}` +
|
|
473
394
|
`${this.restartMode !== '' ? 'restart mode ' + this.restartMode + ' ' : ''}` +
|
|
474
395
|
`running on ${this.systemInformation.osType} (v.${this.systemInformation.osRelease}) platform ${this.systemInformation.osPlatform} arch ${this.systemInformation.osArch}`);
|
|
475
|
-
// Check node version and throw error
|
|
476
396
|
const minNodeVersion = 18;
|
|
477
397
|
const nodeVersion = process.versions.node;
|
|
478
398
|
const versionMajor = parseInt(nodeVersion.split('.')[0]);
|
|
@@ -480,15 +400,9 @@ export class Matterbridge extends EventEmitter {
|
|
|
480
400
|
this.log.error(`Node version ${versionMajor} is not supported. Please upgrade to ${minNodeVersion} or above.`);
|
|
481
401
|
throw new Error(`Node version ${versionMajor} is not supported. Please upgrade to ${minNodeVersion} or above.`);
|
|
482
402
|
}
|
|
483
|
-
// Parse command line
|
|
484
403
|
await this.parseCommandLine();
|
|
485
404
|
this.initialized = true;
|
|
486
405
|
}
|
|
487
|
-
/**
|
|
488
|
-
* Parses the command line arguments and performs the corresponding actions.
|
|
489
|
-
* @private
|
|
490
|
-
* @returns {Promise<void>} A promise that resolves when the command line arguments have been processed, or the process exits.
|
|
491
|
-
*/
|
|
492
406
|
async parseCommandLine() {
|
|
493
407
|
if (hasParameter('help')) {
|
|
494
408
|
this.log.info(`\nUsage: matterbridge [options]\n
|
|
@@ -598,7 +512,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
598
512
|
await this.shutdownProcessAndFactoryReset();
|
|
599
513
|
return;
|
|
600
514
|
}
|
|
601
|
-
// Start the matter storage and create the matterbridge context
|
|
602
515
|
try {
|
|
603
516
|
await this.startMatterStorage();
|
|
604
517
|
}
|
|
@@ -606,12 +519,10 @@ export class Matterbridge extends EventEmitter {
|
|
|
606
519
|
this.log.fatal(`Fatal error creating matter storage: ${error instanceof Error ? error.message : error}`);
|
|
607
520
|
throw new Error(`Fatal error creating matter storage: ${error instanceof Error ? error.message : error}`);
|
|
608
521
|
}
|
|
609
|
-
// Clear the matterbridge context if the reset parameter is set
|
|
610
522
|
if (hasParameter('reset') && getParameter('reset') === undefined) {
|
|
611
523
|
await this.shutdownProcessAndReset();
|
|
612
524
|
return;
|
|
613
525
|
}
|
|
614
|
-
// Clear matterbridge plugin context if the reset parameter is set
|
|
615
526
|
if (hasParameter('reset') && getParameter('reset') !== undefined) {
|
|
616
527
|
this.log.debug(`Reset plugin ${getParameter('reset')}`);
|
|
617
528
|
const plugin = this.plugins.get(getParameter('reset'));
|
|
@@ -633,16 +544,12 @@ export class Matterbridge extends EventEmitter {
|
|
|
633
544
|
this.emit('shutdown');
|
|
634
545
|
return;
|
|
635
546
|
}
|
|
636
|
-
// Initialize frontend
|
|
637
547
|
if (getIntParameter('frontend') !== 0 || getIntParameter('frontend') === undefined)
|
|
638
548
|
await this.frontend.start(getIntParameter('frontend'));
|
|
639
|
-
this.frontend.logLevel = this.log.logLevel;
|
|
640
|
-
// Check now the latest versions of matterbridge and plugins
|
|
641
549
|
this.getMatterbridgeLatestVersion();
|
|
642
550
|
for (const plugin of this.plugins) {
|
|
643
551
|
this.getPluginLatestVersion(plugin);
|
|
644
552
|
}
|
|
645
|
-
// Check each 60 minutes the latest versions
|
|
646
553
|
this.checkUpdateInterval = setInterval(() => {
|
|
647
554
|
this.getMatterbridgeLatestVersion();
|
|
648
555
|
for (const plugin of this.plugins) {
|
|
@@ -650,24 +557,20 @@ export class Matterbridge extends EventEmitter {
|
|
|
650
557
|
}
|
|
651
558
|
this.frontend.wssSendRefreshRequired();
|
|
652
559
|
}, 60 * 60 * 1000);
|
|
653
|
-
// Start the matterbridge in mode test
|
|
654
560
|
if (hasParameter('test')) {
|
|
655
561
|
this.bridgeMode = 'bridge';
|
|
656
562
|
MatterbridgeEndpoint.bridgeMode = 'bridge';
|
|
657
563
|
return;
|
|
658
564
|
}
|
|
659
|
-
// Start the matterbridge in mode controller
|
|
660
565
|
if (hasParameter('controller')) {
|
|
661
566
|
this.bridgeMode = 'controller';
|
|
662
567
|
await this.startController();
|
|
663
568
|
return;
|
|
664
569
|
}
|
|
665
|
-
// Check if the bridge mode is set and start matterbridge in bridge mode if not set
|
|
666
570
|
if (!hasParameter('bridge') && !hasParameter('childbridge') && (await this.nodeContext?.get('bridgeMode', '')) === '') {
|
|
667
571
|
this.log.info('Setting default matterbridge start mode to bridge');
|
|
668
572
|
await this.nodeContext?.set('bridgeMode', 'bridge');
|
|
669
573
|
}
|
|
670
|
-
// Start matterbridge in bridge mode
|
|
671
574
|
if (hasParameter('bridge') || (!hasParameter('childbridge') && (await this.nodeContext?.get('bridgeMode', '')) === 'bridge')) {
|
|
672
575
|
this.bridgeMode = 'bridge';
|
|
673
576
|
MatterbridgeEndpoint.bridgeMode = 'bridge';
|
|
@@ -675,7 +578,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
675
578
|
await this.startBridge();
|
|
676
579
|
return;
|
|
677
580
|
}
|
|
678
|
-
// Start matterbridge in childbridge mode
|
|
679
581
|
if (hasParameter('childbridge') || (!hasParameter('bridge') && (await this.nodeContext?.get('bridgeMode', '')) === 'childbridge')) {
|
|
680
582
|
this.bridgeMode = 'childbridge';
|
|
681
583
|
MatterbridgeEndpoint.bridgeMode = 'childbridge';
|
|
@@ -684,28 +586,16 @@ export class Matterbridge extends EventEmitter {
|
|
|
684
586
|
return;
|
|
685
587
|
}
|
|
686
588
|
}
|
|
687
|
-
/**
|
|
688
|
-
* Asynchronously loads and starts the registered plugins.
|
|
689
|
-
*
|
|
690
|
-
* This method is responsible for initializing and staarting all enabled plugins.
|
|
691
|
-
* It ensures that each plugin is properly loaded and started before the bridge starts.
|
|
692
|
-
*
|
|
693
|
-
* @returns {Promise<void>} A promise that resolves when all plugins have been loaded and started.
|
|
694
|
-
*/
|
|
695
589
|
async startPlugins() {
|
|
696
|
-
// Check, load and start the plugins
|
|
697
590
|
for (const plugin of this.plugins) {
|
|
698
591
|
plugin.configJson = await this.plugins.loadConfig(plugin);
|
|
699
592
|
plugin.schemaJson = await this.plugins.loadSchema(plugin);
|
|
700
|
-
// Check if the plugin is available
|
|
701
593
|
if (!(await this.plugins.resolve(plugin.path))) {
|
|
702
594
|
this.log.error(`Plugin ${plg}${plugin.name}${er} not found or not validated. Disabling it.`);
|
|
703
595
|
plugin.enabled = false;
|
|
704
596
|
plugin.error = true;
|
|
705
597
|
continue;
|
|
706
598
|
}
|
|
707
|
-
// Check if the plugin has a new version
|
|
708
|
-
// this.getPluginLatestVersion(plugin); // No await do it asyncronously
|
|
709
599
|
if (!plugin.enabled) {
|
|
710
600
|
this.log.info(`Plugin ${plg}${plugin.name}${nf} not enabled`);
|
|
711
601
|
continue;
|
|
@@ -719,26 +609,20 @@ export class Matterbridge extends EventEmitter {
|
|
|
719
609
|
plugin.addedDevices = undefined;
|
|
720
610
|
plugin.qrPairingCode = undefined;
|
|
721
611
|
plugin.manualPairingCode = undefined;
|
|
722
|
-
this.plugins.load(plugin, true, 'Matterbridge is starting');
|
|
612
|
+
this.plugins.load(plugin, true, 'Matterbridge is starting');
|
|
723
613
|
}
|
|
724
614
|
this.frontend.wssSendRefreshRequired();
|
|
725
615
|
}
|
|
726
|
-
/**
|
|
727
|
-
* Registers the process handlers for uncaughtException, unhandledRejection, SIGINT and SIGTERM.
|
|
728
|
-
* When either of these signals are received, the cleanup method is called with an appropriate message.
|
|
729
|
-
*/
|
|
730
616
|
registerProcessHandlers() {
|
|
731
617
|
this.log.debug(`Registering uncaughtException and unhandledRejection handlers...`);
|
|
732
618
|
process.removeAllListeners('uncaughtException');
|
|
733
619
|
process.removeAllListeners('unhandledRejection');
|
|
734
620
|
this.exceptionHandler = async (error) => {
|
|
735
621
|
this.log.error('Unhandled Exception detected at:', error.stack || error, rs);
|
|
736
|
-
// await this.cleanup('Unhandled Exception detected, cleaning up...');
|
|
737
622
|
};
|
|
738
623
|
process.on('uncaughtException', this.exceptionHandler);
|
|
739
624
|
this.rejectionHandler = async (reason, promise) => {
|
|
740
625
|
this.log.error('Unhandled Rejection detected at:', promise, 'reason:', reason instanceof Error ? reason.stack : reason, rs);
|
|
741
|
-
// await this.cleanup('Unhandled Rejection detected, cleaning up...');
|
|
742
626
|
};
|
|
743
627
|
process.on('unhandledRejection', this.rejectionHandler);
|
|
744
628
|
this.log.debug(`Registering SIGINT and SIGTERM signal handlers...`);
|
|
@@ -751,9 +635,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
751
635
|
};
|
|
752
636
|
process.on('SIGTERM', this.sigtermHandler);
|
|
753
637
|
}
|
|
754
|
-
/**
|
|
755
|
-
* Deregisters the process uncaughtException, unhandledRejection, SIGINT and SIGTERM signal handlers.
|
|
756
|
-
*/
|
|
757
638
|
deregisterProcesslHandlers() {
|
|
758
639
|
this.log.debug(`Deregistering uncaughtException and unhandledRejection handlers...`);
|
|
759
640
|
if (this.exceptionHandler)
|
|
@@ -770,17 +651,12 @@ export class Matterbridge extends EventEmitter {
|
|
|
770
651
|
process.off('SIGTERM', this.sigtermHandler);
|
|
771
652
|
this.sigtermHandler = undefined;
|
|
772
653
|
}
|
|
773
|
-
/**
|
|
774
|
-
* Logs the node and system information.
|
|
775
|
-
*/
|
|
776
654
|
async logNodeAndSystemInfo() {
|
|
777
|
-
// IP address information
|
|
778
655
|
const networkInterfaces = os.networkInterfaces();
|
|
779
656
|
this.systemInformation.interfaceName = '';
|
|
780
657
|
this.systemInformation.ipv4Address = '';
|
|
781
658
|
this.systemInformation.ipv6Address = '';
|
|
782
659
|
for (const [interfaceName, interfaceDetails] of Object.entries(networkInterfaces)) {
|
|
783
|
-
// this.log.debug(`Checking interface: '${interfaceName}' for '${this.mdnsInterface}'`);
|
|
784
660
|
if (this.mdnsInterface && interfaceName !== this.mdnsInterface)
|
|
785
661
|
continue;
|
|
786
662
|
if (!interfaceDetails) {
|
|
@@ -806,22 +682,19 @@ export class Matterbridge extends EventEmitter {
|
|
|
806
682
|
break;
|
|
807
683
|
}
|
|
808
684
|
}
|
|
809
|
-
// Node information
|
|
810
685
|
this.systemInformation.nodeVersion = process.versions.node;
|
|
811
686
|
const versionMajor = parseInt(this.systemInformation.nodeVersion.split('.')[0]);
|
|
812
687
|
const versionMinor = parseInt(this.systemInformation.nodeVersion.split('.')[1]);
|
|
813
688
|
const versionPatch = parseInt(this.systemInformation.nodeVersion.split('.')[2]);
|
|
814
|
-
// Host system information
|
|
815
689
|
this.systemInformation.hostname = os.hostname();
|
|
816
690
|
this.systemInformation.user = os.userInfo().username;
|
|
817
|
-
this.systemInformation.osType = os.type();
|
|
818
|
-
this.systemInformation.osRelease = os.release();
|
|
819
|
-
this.systemInformation.osPlatform = os.platform();
|
|
820
|
-
this.systemInformation.osArch = os.arch();
|
|
821
|
-
this.systemInformation.totalMemory = (os.totalmem() / 1024 / 1024 / 1024).toFixed(2) + ' GB';
|
|
822
|
-
this.systemInformation.freeMemory = (os.freemem() / 1024 / 1024 / 1024).toFixed(2) + ' GB';
|
|
823
|
-
this.systemInformation.systemUptime = (os.uptime() / 60 / 60).toFixed(2) + ' hours';
|
|
824
|
-
// Log the system information
|
|
691
|
+
this.systemInformation.osType = os.type();
|
|
692
|
+
this.systemInformation.osRelease = os.release();
|
|
693
|
+
this.systemInformation.osPlatform = os.platform();
|
|
694
|
+
this.systemInformation.osArch = os.arch();
|
|
695
|
+
this.systemInformation.totalMemory = (os.totalmem() / 1024 / 1024 / 1024).toFixed(2) + ' GB';
|
|
696
|
+
this.systemInformation.freeMemory = (os.freemem() / 1024 / 1024 / 1024).toFixed(2) + ' GB';
|
|
697
|
+
this.systemInformation.systemUptime = (os.uptime() / 60 / 60).toFixed(2) + ' hours';
|
|
825
698
|
this.log.debug('Host System Information:');
|
|
826
699
|
this.log.debug(`- Hostname: ${this.systemInformation.hostname}`);
|
|
827
700
|
this.log.debug(`- User: ${this.systemInformation.user}`);
|
|
@@ -837,19 +710,15 @@ export class Matterbridge extends EventEmitter {
|
|
|
837
710
|
this.log.debug(`- Total Memory: ${this.systemInformation.totalMemory}`);
|
|
838
711
|
this.log.debug(`- Free Memory: ${this.systemInformation.freeMemory}`);
|
|
839
712
|
this.log.debug(`- System Uptime: ${this.systemInformation.systemUptime}`);
|
|
840
|
-
// Home directory
|
|
841
713
|
this.homeDirectory = getParameter('homedir') ?? os.homedir();
|
|
842
714
|
this.matterbridgeInformation.homeDirectory = this.homeDirectory;
|
|
843
715
|
this.log.debug(`Home Directory: ${this.homeDirectory}`);
|
|
844
|
-
// Package root directory
|
|
845
716
|
const currentFileDirectory = path.dirname(fileURLToPath(import.meta.url));
|
|
846
717
|
this.rootDirectory = path.resolve(currentFileDirectory, '../');
|
|
847
718
|
this.matterbridgeInformation.rootDirectory = this.rootDirectory;
|
|
848
719
|
this.log.debug(`Root Directory: ${this.rootDirectory}`);
|
|
849
|
-
// Global node_modules directory
|
|
850
720
|
if (this.nodeContext)
|
|
851
721
|
this.globalModulesDirectory = this.matterbridgeInformation.globalModulesDirectory = await this.nodeContext.get('globalModulesDirectory', '');
|
|
852
|
-
// First run of Matterbridge so the node storage is empty
|
|
853
722
|
if (this.globalModulesDirectory === '') {
|
|
854
723
|
try {
|
|
855
724
|
this.globalModulesDirectory = await this.getGlobalNodeModules();
|
|
@@ -863,20 +732,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
863
732
|
}
|
|
864
733
|
else
|
|
865
734
|
this.log.debug(`Global node_modules Directory: ${this.globalModulesDirectory}`);
|
|
866
|
-
/* removed cause is too expensive for the shelly board and not really needed. Why should it change the globalModulesDirectory?
|
|
867
|
-
else {
|
|
868
|
-
this.getGlobalNodeModules()
|
|
869
|
-
.then(async (globalModulesDirectory) => {
|
|
870
|
-
this.globalModulesDirectory = globalModulesDirectory;
|
|
871
|
-
this.matterbridgeInformation.globalModulesDirectory = this.globalModulesDirectory;
|
|
872
|
-
this.log.debug(`Global node_modules Directory: ${this.globalModulesDirectory}`);
|
|
873
|
-
await this.nodeContext?.set<string>('globalModulesDirectory', this.globalModulesDirectory);
|
|
874
|
-
})
|
|
875
|
-
.catch((error) => {
|
|
876
|
-
this.log.error(`Error getting global node_modules directory: ${error}`);
|
|
877
|
-
});
|
|
878
|
-
}*/
|
|
879
|
-
// Create the data directory .matterbridge in the home directory
|
|
880
735
|
this.matterbridgeDirectory = path.join(this.homeDirectory, '.matterbridge');
|
|
881
736
|
this.matterbridgeInformation.matterbridgeDirectory = this.matterbridgeDirectory;
|
|
882
737
|
try {
|
|
@@ -900,7 +755,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
900
755
|
}
|
|
901
756
|
}
|
|
902
757
|
this.log.debug(`Matterbridge Directory: ${this.matterbridgeDirectory}`);
|
|
903
|
-
// Create the plugin directory Matterbridge in the home directory
|
|
904
758
|
this.matterbridgePluginDirectory = path.join(this.homeDirectory, 'Matterbridge');
|
|
905
759
|
this.matterbridgeInformation.matterbridgePluginDirectory = this.matterbridgePluginDirectory;
|
|
906
760
|
try {
|
|
@@ -924,46 +778,18 @@ export class Matterbridge extends EventEmitter {
|
|
|
924
778
|
}
|
|
925
779
|
}
|
|
926
780
|
this.log.debug(`Matterbridge Plugin Directory: ${this.matterbridgePluginDirectory}`);
|
|
927
|
-
// Matterbridge version
|
|
928
781
|
const packageJson = JSON.parse(await fs.readFile(path.join(this.rootDirectory, 'package.json'), 'utf-8'));
|
|
929
782
|
this.matterbridgeVersion = this.matterbridgeLatestVersion = packageJson.version;
|
|
930
783
|
this.matterbridgeInformation.matterbridgeVersion = this.matterbridgeInformation.matterbridgeLatestVersion = this.matterbridgeVersion;
|
|
931
784
|
this.log.debug(`Matterbridge Version: ${this.matterbridgeVersion}`);
|
|
932
|
-
// Matterbridge latest version
|
|
933
785
|
if (this.nodeContext)
|
|
934
786
|
this.matterbridgeLatestVersion = await this.nodeContext.get('matterbridgeLatestVersion', this.matterbridgeVersion);
|
|
935
787
|
this.log.debug(`Matterbridge Latest Version: ${this.matterbridgeLatestVersion}`);
|
|
936
|
-
// this.getMatterbridgeLatestVersion();
|
|
937
|
-
// Current working directory
|
|
938
788
|
const currentDir = process.cwd();
|
|
939
789
|
this.log.debug(`Current Working Directory: ${currentDir}`);
|
|
940
|
-
// Command line arguments (excluding 'node' and the script name)
|
|
941
790
|
const cmdArgs = process.argv.slice(2).join(' ');
|
|
942
791
|
this.log.debug(`Command Line Arguments: ${cmdArgs}`);
|
|
943
792
|
}
|
|
944
|
-
/**
|
|
945
|
-
* Retrieves the latest version of a package from the npm registry.
|
|
946
|
-
* @param packageName - The name of the package.
|
|
947
|
-
* @returns A Promise that resolves to the latest version of the package.
|
|
948
|
-
*/
|
|
949
|
-
async getLatestVersion(packageName) {
|
|
950
|
-
return new Promise((resolve, reject) => {
|
|
951
|
-
this.execRunningCount++;
|
|
952
|
-
exec(`npm view ${packageName} version`, (error, stdout) => {
|
|
953
|
-
this.execRunningCount--;
|
|
954
|
-
if (error) {
|
|
955
|
-
reject(error);
|
|
956
|
-
}
|
|
957
|
-
else {
|
|
958
|
-
resolve(stdout.trim());
|
|
959
|
-
}
|
|
960
|
-
});
|
|
961
|
-
});
|
|
962
|
-
}
|
|
963
|
-
/**
|
|
964
|
-
* Retrieves the path to the global Node.js modules directory.
|
|
965
|
-
* @returns A promise that resolves to the path of the global Node.js modules directory.
|
|
966
|
-
*/
|
|
967
793
|
async getGlobalNodeModules() {
|
|
968
794
|
return new Promise((resolve, reject) => {
|
|
969
795
|
this.execRunningCount++;
|
|
@@ -978,13 +804,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
978
804
|
});
|
|
979
805
|
});
|
|
980
806
|
}
|
|
981
|
-
/**
|
|
982
|
-
* Retrieves the latest version of Matterbridge and updates the matterbridgeLatestVersion property.
|
|
983
|
-
* If there is an error retrieving the latest version, logs an error message.
|
|
984
|
-
*
|
|
985
|
-
* @private
|
|
986
|
-
* @returns {Promise<void>} A promise that resolves when the latest version is retrieved.
|
|
987
|
-
*/
|
|
988
807
|
async getMatterbridgeLatestVersion() {
|
|
989
808
|
getNpmPackageVersion('matterbridge')
|
|
990
809
|
.then(async (version) => {
|
|
@@ -1000,17 +819,9 @@ export class Matterbridge extends EventEmitter {
|
|
|
1000
819
|
this.frontend.wssSendRefreshRequired();
|
|
1001
820
|
})
|
|
1002
821
|
.catch((error) => {
|
|
1003
|
-
this.log.
|
|
822
|
+
this.log.warn(`Error getting Matterbridge latest version: ${error.message}`);
|
|
1004
823
|
});
|
|
1005
824
|
}
|
|
1006
|
-
/**
|
|
1007
|
-
* Retrieves the latest version of a plugin and updates the plugin's latestVersion property.
|
|
1008
|
-
* If there is an error retrieving the latest version, logs an error message.
|
|
1009
|
-
*
|
|
1010
|
-
* @private
|
|
1011
|
-
* @param {RegisteredPlugin} plugin - The plugin for which to retrieve the latest version.
|
|
1012
|
-
* @returns {Promise<void>} A promise that resolves when the latest version is retrieved.
|
|
1013
|
-
*/
|
|
1014
825
|
async getPluginLatestVersion(plugin) {
|
|
1015
826
|
getNpmPackageVersion(plugin.name)
|
|
1016
827
|
.then((version) => {
|
|
@@ -1021,54 +832,41 @@ export class Matterbridge extends EventEmitter {
|
|
|
1021
832
|
this.log.debug(`The plugin ${plg}${plugin.name}${db} is up to date. Current version: ${plugin.version}. Latest version: ${plugin.latestVersion}.`);
|
|
1022
833
|
})
|
|
1023
834
|
.catch((error) => {
|
|
1024
|
-
this.log.
|
|
835
|
+
this.log.warn(`Error getting ${plg}${plugin.name}${er} latest version: ${error.message}`);
|
|
1025
836
|
});
|
|
1026
837
|
}
|
|
1027
|
-
/**
|
|
1028
|
-
* Creates a MatterLogger function to show the matter.js log messages in AnsiLogger (for the frontend).
|
|
1029
|
-
*
|
|
1030
|
-
* @returns {Function} The MatterLogger function.
|
|
1031
|
-
*/
|
|
1032
838
|
createMatterLogger() {
|
|
1033
|
-
const matterLogger = new AnsiLogger({ logName: 'Matter', logTimestampFormat: 4
|
|
839
|
+
const matterLogger = new AnsiLogger({ logName: 'Matter', logTimestampFormat: 4, logLevel: "debug" });
|
|
1034
840
|
return (_level, formattedLog) => {
|
|
1035
841
|
const logger = formattedLog.slice(44, 44 + 20).trim();
|
|
1036
842
|
const message = formattedLog.slice(65);
|
|
1037
843
|
matterLogger.logName = logger;
|
|
1038
844
|
switch (_level) {
|
|
1039
845
|
case MatterLogLevel.DEBUG:
|
|
1040
|
-
matterLogger.log("debug"
|
|
846
|
+
matterLogger.log("debug", message);
|
|
1041
847
|
break;
|
|
1042
848
|
case MatterLogLevel.INFO:
|
|
1043
|
-
matterLogger.log("info"
|
|
849
|
+
matterLogger.log("info", message);
|
|
1044
850
|
break;
|
|
1045
851
|
case MatterLogLevel.NOTICE:
|
|
1046
|
-
matterLogger.log("notice"
|
|
852
|
+
matterLogger.log("notice", message);
|
|
1047
853
|
break;
|
|
1048
854
|
case MatterLogLevel.WARN:
|
|
1049
|
-
matterLogger.log("warn"
|
|
855
|
+
matterLogger.log("warn", message);
|
|
1050
856
|
break;
|
|
1051
857
|
case MatterLogLevel.ERROR:
|
|
1052
|
-
matterLogger.log("error"
|
|
858
|
+
matterLogger.log("error", message);
|
|
1053
859
|
break;
|
|
1054
860
|
case MatterLogLevel.FATAL:
|
|
1055
|
-
matterLogger.log("fatal"
|
|
861
|
+
matterLogger.log("fatal", message);
|
|
1056
862
|
break;
|
|
1057
863
|
default:
|
|
1058
|
-
matterLogger.log("debug"
|
|
864
|
+
matterLogger.log("debug", message);
|
|
1059
865
|
break;
|
|
1060
866
|
}
|
|
1061
867
|
};
|
|
1062
868
|
}
|
|
1063
|
-
/**
|
|
1064
|
-
* Creates a Matter File Logger.
|
|
1065
|
-
*
|
|
1066
|
-
* @param {string} filePath - The path to the log file.
|
|
1067
|
-
* @param {boolean} [unlink=false] - Whether to unlink the log file before creating a new one.
|
|
1068
|
-
* @returns {Function} - A function that logs formatted messages to the log file.
|
|
1069
|
-
*/
|
|
1070
869
|
async createMatterFileLogger(filePath, unlink = false) {
|
|
1071
|
-
// 2024-08-21 08:55:19.488 DEBUG InteractionMessenger Sending DataReport chunk with 28 attributes and 0 events: 1004 bytes
|
|
1072
870
|
let fileSize = 0;
|
|
1073
871
|
if (unlink) {
|
|
1074
872
|
try {
|
|
@@ -1117,21 +915,12 @@ export class Matterbridge extends EventEmitter {
|
|
|
1117
915
|
}
|
|
1118
916
|
};
|
|
1119
917
|
}
|
|
1120
|
-
/**
|
|
1121
|
-
* Restarts the process by exiting the current instance and loading a new instance.
|
|
1122
|
-
*/
|
|
1123
918
|
async restartProcess() {
|
|
1124
919
|
await this.cleanup('restarting...', true);
|
|
1125
920
|
}
|
|
1126
|
-
/**
|
|
1127
|
-
* Shut down the process by exiting the current process.
|
|
1128
|
-
*/
|
|
1129
921
|
async shutdownProcess() {
|
|
1130
922
|
await this.cleanup('shutting down...', false);
|
|
1131
923
|
}
|
|
1132
|
-
/**
|
|
1133
|
-
* Update matterbridge and and shut down the process.
|
|
1134
|
-
*/
|
|
1135
924
|
async updateProcess() {
|
|
1136
925
|
this.log.info('Updating matterbridge...');
|
|
1137
926
|
try {
|
|
@@ -1144,66 +933,46 @@ export class Matterbridge extends EventEmitter {
|
|
|
1144
933
|
this.frontend.wssSendRestartRequired();
|
|
1145
934
|
await this.cleanup('updating...', false);
|
|
1146
935
|
}
|
|
1147
|
-
/**
|
|
1148
|
-
* Unregister all devices and shut down the process.
|
|
1149
|
-
*/
|
|
1150
936
|
async unregisterAndShutdownProcess() {
|
|
1151
937
|
this.log.info('Unregistering all devices and shutting down...');
|
|
1152
938
|
for (const plugin of this.plugins) {
|
|
1153
939
|
await this.removeAllBridgedEndpoints(plugin.name);
|
|
1154
940
|
}
|
|
1155
941
|
this.log.debug('Waiting for the MessageExchange to finish...');
|
|
1156
|
-
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
942
|
+
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
1157
943
|
this.log.debug('Cleaning up and shutting down...');
|
|
1158
944
|
await this.cleanup('unregistered all devices and shutting down...', false);
|
|
1159
945
|
}
|
|
1160
|
-
/**
|
|
1161
|
-
* Reset commissioning and shut down the process.
|
|
1162
|
-
*/
|
|
1163
946
|
async shutdownProcessAndReset() {
|
|
1164
947
|
await this.cleanup('shutting down with reset...', false);
|
|
1165
948
|
}
|
|
1166
|
-
/**
|
|
1167
|
-
* Factory reset and shut down the process.
|
|
1168
|
-
*/
|
|
1169
949
|
async shutdownProcessAndFactoryReset() {
|
|
1170
950
|
await this.cleanup('shutting down with factory reset...', false);
|
|
1171
951
|
}
|
|
1172
|
-
/**
|
|
1173
|
-
* Cleans up the Matterbridge instance.
|
|
1174
|
-
* @param message - The cleanup message.
|
|
1175
|
-
* @param restart - Indicates whether to restart the instance after cleanup. Default is `false`.
|
|
1176
|
-
* @returns A promise that resolves when the cleanup is completed.
|
|
1177
|
-
*/
|
|
1178
952
|
async cleanup(message, restart = false) {
|
|
1179
953
|
if (this.initialized && !this.hasCleanupStarted) {
|
|
1180
954
|
this.hasCleanupStarted = true;
|
|
1181
955
|
this.log.info(message);
|
|
1182
|
-
// Clear the start matter interval
|
|
1183
956
|
if (this.startMatterInterval) {
|
|
1184
957
|
clearInterval(this.startMatterInterval);
|
|
1185
958
|
this.startMatterInterval = undefined;
|
|
1186
959
|
this.log.debug('Start matter interval cleared');
|
|
1187
960
|
}
|
|
1188
|
-
// Clear the check update interval
|
|
1189
961
|
if (this.checkUpdateInterval) {
|
|
1190
962
|
clearInterval(this.checkUpdateInterval);
|
|
1191
963
|
this.checkUpdateInterval = undefined;
|
|
1192
964
|
this.log.debug('Check update interval cleared');
|
|
1193
965
|
}
|
|
1194
|
-
// Clear the configure timeout
|
|
1195
966
|
if (this.configureTimeout) {
|
|
1196
967
|
clearTimeout(this.configureTimeout);
|
|
1197
968
|
this.configureTimeout = undefined;
|
|
1198
969
|
this.log.debug('Matterbridge configure timeout cleared');
|
|
1199
970
|
}
|
|
1200
|
-
// Clear the reachability timeout
|
|
1201
971
|
if (this.reachabilityTimeout) {
|
|
1202
972
|
clearTimeout(this.reachabilityTimeout);
|
|
1203
973
|
this.reachabilityTimeout = undefined;
|
|
1204
974
|
this.log.debug('Matterbridge reachability timeout cleared');
|
|
1205
975
|
}
|
|
1206
|
-
// Calling the shutdown method of each plugin and clear the plugins reachability timeout
|
|
1207
976
|
for (const plugin of this.plugins) {
|
|
1208
977
|
if (!plugin.enabled || plugin.error)
|
|
1209
978
|
continue;
|
|
@@ -1214,10 +983,9 @@ export class Matterbridge extends EventEmitter {
|
|
|
1214
983
|
this.log.debug(`Plugin ${plg}${plugin.name}${db} reachability timeout cleared`);
|
|
1215
984
|
}
|
|
1216
985
|
}
|
|
1217
|
-
// Stopping matter server nodes
|
|
1218
986
|
this.log.notice(`Stopping matter server nodes in ${this.bridgeMode} mode...`);
|
|
1219
987
|
this.log.debug('Waiting for the MessageExchange to finish...');
|
|
1220
|
-
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
988
|
+
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
1221
989
|
if (this.bridgeMode === 'bridge') {
|
|
1222
990
|
if (this.serverNode) {
|
|
1223
991
|
await this.stopServerNode(this.serverNode);
|
|
@@ -1233,7 +1001,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1233
1001
|
}
|
|
1234
1002
|
}
|
|
1235
1003
|
this.log.notice('Stopped matter server nodes');
|
|
1236
|
-
// Matter commisioning reset
|
|
1237
1004
|
if (message === 'shutting down with reset...') {
|
|
1238
1005
|
this.log.info('Resetting Matterbridge commissioning information...');
|
|
1239
1006
|
await this.matterStorageManager?.createContext('events')?.clearAll();
|
|
@@ -1243,37 +1010,17 @@ export class Matterbridge extends EventEmitter {
|
|
|
1243
1010
|
await this.matterbridgeContext?.clearAll();
|
|
1244
1011
|
this.log.info('Matter storage reset done! Remove the bridge from the controller.');
|
|
1245
1012
|
}
|
|
1246
|
-
// Stop matter storage
|
|
1247
1013
|
await this.stopMatterStorage();
|
|
1248
|
-
// Stop the frontend
|
|
1249
1014
|
await this.frontend.stop();
|
|
1250
|
-
// Remove the matterfilelogger
|
|
1251
1015
|
try {
|
|
1252
1016
|
Logger.removeLogger('matterfilelogger');
|
|
1253
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
1254
1017
|
}
|
|
1255
1018
|
catch (error) {
|
|
1256
|
-
// this.log.debug(`Error removing the matterfilelogger for file ${CYAN}${path.join(this.matterbridgeDirectory, this.matterLoggerFile)}${er}: ${error instanceof Error ? error.message : error}`);
|
|
1257
1019
|
}
|
|
1258
|
-
// Serialize registeredDevices
|
|
1259
1020
|
if (this.nodeStorage && this.nodeContext) {
|
|
1260
|
-
/*
|
|
1261
|
-
TODO: Implement serialization of registered devices in edge mode
|
|
1262
|
-
this.log.info('Saving registered devices...');
|
|
1263
|
-
const serializedRegisteredDevices: SerializedMatterbridgeEndpoint[] = [];
|
|
1264
|
-
this.devices.forEach(async (device) => {
|
|
1265
|
-
const serializedMatterbridgeDevice = MatterbridgeEndpoint.serialize(device);
|
|
1266
|
-
// this.log.info(`- ${serializedMatterbridgeDevice.deviceName}${rs}\n`, serializedMatterbridgeDevice);
|
|
1267
|
-
if (serializedMatterbridgeDevice) serializedRegisteredDevices.push(serializedMatterbridgeDevice);
|
|
1268
|
-
});
|
|
1269
|
-
await this.nodeContext.set<SerializedMatterbridgeEndpoint[]>('devices', serializedRegisteredDevices);
|
|
1270
|
-
this.log.info(`Saved registered devices (${serializedRegisteredDevices?.length})`);
|
|
1271
|
-
*/
|
|
1272
|
-
// Clear nodeContext and nodeStorage (they just need 1000ms to write the data to disk)
|
|
1273
1021
|
this.log.debug(`Closing node storage context for ${plg}Matterbridge${db}...`);
|
|
1274
1022
|
await this.nodeContext.close();
|
|
1275
1023
|
this.nodeContext = undefined;
|
|
1276
|
-
// Clear nodeContext for each plugin (they just need 1000ms to write the data to disk)
|
|
1277
1024
|
for (const plugin of this.plugins) {
|
|
1278
1025
|
if (plugin.nodeContext) {
|
|
1279
1026
|
this.log.debug(`Closing node storage context for plugin ${plg}${plugin.name}${db}...`);
|
|
@@ -1290,10 +1037,8 @@ export class Matterbridge extends EventEmitter {
|
|
|
1290
1037
|
}
|
|
1291
1038
|
this.plugins.clear();
|
|
1292
1039
|
this.devices.clear();
|
|
1293
|
-
// Factory reset
|
|
1294
1040
|
if (message === 'shutting down with factory reset...') {
|
|
1295
1041
|
try {
|
|
1296
|
-
// Delete old matter storage file and backup
|
|
1297
1042
|
const file = path.join(this.matterbridgeDirectory, 'matterbridge' + (getParameter('profile') ? '.' + getParameter('profile') : '') + '.json');
|
|
1298
1043
|
this.log.info(`Unlinking old matter storage file: ${file}`);
|
|
1299
1044
|
await fs.unlink(file);
|
|
@@ -1307,7 +1052,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1307
1052
|
}
|
|
1308
1053
|
}
|
|
1309
1054
|
try {
|
|
1310
|
-
// Delete matter node storage directory with its subdirectories and backup
|
|
1311
1055
|
const dir = path.join(this.matterbridgeDirectory, 'matterstorage' + (getParameter('profile') ? '.' + getParameter('profile') : ''));
|
|
1312
1056
|
this.log.info(`Removing matter node storage directory: ${dir}`);
|
|
1313
1057
|
await fs.rm(dir, { recursive: true });
|
|
@@ -1321,7 +1065,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1321
1065
|
}
|
|
1322
1066
|
}
|
|
1323
1067
|
try {
|
|
1324
|
-
// Delete node storage directory with its subdirectories and backup
|
|
1325
1068
|
const dir = path.join(this.matterbridgeDirectory, 'storage' + (getParameter('profile') ? '.' + getParameter('profile') : ''));
|
|
1326
1069
|
this.log.info(`Removing storage directory: ${dir}`);
|
|
1327
1070
|
await fs.rm(dir, { recursive: true });
|
|
@@ -1336,13 +1079,12 @@ export class Matterbridge extends EventEmitter {
|
|
|
1336
1079
|
}
|
|
1337
1080
|
this.log.info('Factory reset done! Remove all paired fabrics from the controllers.');
|
|
1338
1081
|
}
|
|
1339
|
-
// Deregisters the process handlers
|
|
1340
1082
|
this.deregisterProcesslHandlers();
|
|
1341
1083
|
if (restart) {
|
|
1342
1084
|
if (message === 'updating...') {
|
|
1343
1085
|
this.log.info('Cleanup completed. Updating...');
|
|
1344
1086
|
Matterbridge.instance = undefined;
|
|
1345
|
-
this.emit('update');
|
|
1087
|
+
this.emit('update');
|
|
1346
1088
|
}
|
|
1347
1089
|
else if (message === 'restarting...') {
|
|
1348
1090
|
this.log.info('Cleanup completed. Restarting...');
|
|
@@ -1362,14 +1104,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1362
1104
|
this.log.debug('Cleanup already started...');
|
|
1363
1105
|
}
|
|
1364
1106
|
}
|
|
1365
|
-
/**
|
|
1366
|
-
* Creates and configures the server node for an accessory plugin for a given device.
|
|
1367
|
-
*
|
|
1368
|
-
* @param {RegisteredPlugin} plugin - The plugin to configure.
|
|
1369
|
-
* @param {MatterbridgeEndpoint} device - The device to associate with the plugin.
|
|
1370
|
-
* @param {boolean} [start=false] - Whether to start the server node after adding the device.
|
|
1371
|
-
* @returns {Promise<void>} A promise that resolves when the server node for the accessory plugin is created and configured.
|
|
1372
|
-
*/
|
|
1373
1107
|
async createAccessoryPlugin(plugin, device, start = false) {
|
|
1374
1108
|
if (!plugin.locked && device.deviceName && device.vendorId && device.productId && device.vendorName && device.productName) {
|
|
1375
1109
|
plugin.locked = true;
|
|
@@ -1381,13 +1115,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1381
1115
|
await this.startServerNode(plugin.serverNode);
|
|
1382
1116
|
}
|
|
1383
1117
|
}
|
|
1384
|
-
/**
|
|
1385
|
-
* Creates and configures the server node for a dynamic plugin.
|
|
1386
|
-
*
|
|
1387
|
-
* @param {RegisteredPlugin} plugin - The plugin to configure.
|
|
1388
|
-
* @param {boolean} [start=false] - Whether to start the server node after adding the aggregator node.
|
|
1389
|
-
* @returns {Promise<void>} A promise that resolves when the server node for the dynamic plugin is created and configured.
|
|
1390
|
-
*/
|
|
1391
1118
|
async createDynamicPlugin(plugin, start = false) {
|
|
1392
1119
|
if (!plugin.locked) {
|
|
1393
1120
|
plugin.locked = true;
|
|
@@ -1399,13 +1126,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
1399
1126
|
await this.startServerNode(plugin.serverNode);
|
|
1400
1127
|
}
|
|
1401
1128
|
}
|
|
1402
|
-
/**
|
|
1403
|
-
* Starts the Matterbridge in bridge mode.
|
|
1404
|
-
* @private
|
|
1405
|
-
* @returns {Promise<void>} A promise that resolves when the Matterbridge is started.
|
|
1406
|
-
*/
|
|
1407
1129
|
async startBridge() {
|
|
1408
|
-
// Plugins are configured by a timer when matter server is started and plugin.configured is set to true
|
|
1409
1130
|
if (!this.matterStorageManager)
|
|
1410
1131
|
throw new Error('No storage manager initialized');
|
|
1411
1132
|
if (!this.matterbridgeContext)
|
|
@@ -1442,9 +1163,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
1442
1163
|
clearInterval(this.startMatterInterval);
|
|
1443
1164
|
this.startMatterInterval = undefined;
|
|
1444
1165
|
this.log.debug('Cleared startMatterInterval interval for Matterbridge');
|
|
1445
|
-
// Start the Matter server node
|
|
1446
1166
|
this.startServerNode(this.serverNode);
|
|
1447
|
-
// Configure the plugins
|
|
1448
1167
|
this.configureTimeout = setTimeout(async () => {
|
|
1449
1168
|
for (const plugin of this.plugins) {
|
|
1450
1169
|
if (!plugin.enabled || !plugin.loaded || !plugin.started || plugin.error)
|
|
@@ -1459,7 +1178,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1459
1178
|
}
|
|
1460
1179
|
this.frontend.wssSendRefreshRequired();
|
|
1461
1180
|
}, 30 * 1000);
|
|
1462
|
-
// Setting reachability to true
|
|
1463
1181
|
this.reachabilityTimeout = setTimeout(() => {
|
|
1464
1182
|
this.log.info(`Setting reachability to true for ${plg}Matterbridge${db}`);
|
|
1465
1183
|
if (this.serverNode)
|
|
@@ -1470,14 +1188,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
1470
1188
|
}, 60 * 1000);
|
|
1471
1189
|
}, 1000);
|
|
1472
1190
|
}
|
|
1473
|
-
/**
|
|
1474
|
-
* Starts the Matterbridge in childbridge mode.
|
|
1475
|
-
* @private
|
|
1476
|
-
* @returns {Promise<void>} A promise that resolves when the Matterbridge is started.
|
|
1477
|
-
*/
|
|
1478
1191
|
async startChildbridge() {
|
|
1479
|
-
// Matterbridge.addBridgedDevice creates the commissionig servers and add the devices to the the commissioning server or to the aggregator
|
|
1480
|
-
// Plugins are configured by a timer when matter server is started and plugin.configured is set to true
|
|
1481
1192
|
if (!this.matterStorageManager)
|
|
1482
1193
|
throw new Error('No storage manager initialized');
|
|
1483
1194
|
for (const plugin of this.plugins) {
|
|
@@ -1524,13 +1235,12 @@ export class Matterbridge extends EventEmitter {
|
|
|
1524
1235
|
clearInterval(this.startMatterInterval);
|
|
1525
1236
|
this.startMatterInterval = undefined;
|
|
1526
1237
|
this.log.debug('Cleared startMatterInterval interval in childbridge mode');
|
|
1527
|
-
// Configure the plugins
|
|
1528
1238
|
this.configureTimeout = setTimeout(async () => {
|
|
1529
1239
|
for (const plugin of this.plugins) {
|
|
1530
1240
|
if (!plugin.enabled || !plugin.loaded || !plugin.started || plugin.error)
|
|
1531
1241
|
continue;
|
|
1532
1242
|
try {
|
|
1533
|
-
await this.plugins.configure(plugin);
|
|
1243
|
+
await this.plugins.configure(plugin);
|
|
1534
1244
|
}
|
|
1535
1245
|
catch (error) {
|
|
1536
1246
|
plugin.error = true;
|
|
@@ -1558,9 +1268,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
1558
1268
|
this.log.error(`Node storage context not found for plugin ${plg}${plugin.name}${er}`);
|
|
1559
1269
|
continue;
|
|
1560
1270
|
}
|
|
1561
|
-
// Start the Matter server node
|
|
1562
1271
|
this.startServerNode(plugin.serverNode);
|
|
1563
|
-
// Setting reachability to true
|
|
1564
1272
|
plugin.reachabilityTimeout = setTimeout(() => {
|
|
1565
1273
|
this.log.info(`Setting reachability to true for ${plg}${plugin.name}${db}`);
|
|
1566
1274
|
if (plugin.serverNode)
|
|
@@ -1574,219 +1282,9 @@ export class Matterbridge extends EventEmitter {
|
|
|
1574
1282
|
}
|
|
1575
1283
|
}, 1000);
|
|
1576
1284
|
}
|
|
1577
|
-
/**
|
|
1578
|
-
* Starts the Matterbridge controller.
|
|
1579
|
-
* @private
|
|
1580
|
-
* @returns {Promise<void>} A promise that resolves when the Matterbridge is started.
|
|
1581
|
-
*/
|
|
1582
1285
|
async startController() {
|
|
1583
|
-
/*
|
|
1584
|
-
if (!this.storageManager) {
|
|
1585
|
-
this.log.error('No storage manager initialized');
|
|
1586
|
-
await this.cleanup('No storage manager initialized');
|
|
1587
|
-
return;
|
|
1588
|
-
}
|
|
1589
|
-
this.log.info('Creating context: mattercontrollerContext');
|
|
1590
|
-
this.mattercontrollerContext = this.storageManager.createContext('mattercontrollerContext');
|
|
1591
|
-
if (!this.mattercontrollerContext) {
|
|
1592
|
-
this.log.error('No storage context mattercontrollerContext initialized');
|
|
1593
|
-
await this.cleanup('No storage context mattercontrollerContext initialized');
|
|
1594
|
-
return;
|
|
1595
|
-
}
|
|
1596
|
-
|
|
1597
|
-
this.log.debug('Starting matterbridge in mode', this.bridgeMode);
|
|
1598
|
-
this.matterServer = await this.createMatterServer(this.storageManager);
|
|
1599
|
-
this.log.info('Creating matter commissioning controller');
|
|
1600
|
-
this.commissioningController = new CommissioningController({
|
|
1601
|
-
autoConnect: false,
|
|
1602
|
-
});
|
|
1603
|
-
this.log.info('Adding matter commissioning controller to matter server');
|
|
1604
|
-
await this.matterServer.addCommissioningController(this.commissioningController);
|
|
1605
|
-
|
|
1606
|
-
this.log.info('Starting matter server');
|
|
1607
|
-
await this.matterServer.start();
|
|
1608
|
-
this.log.info('Matter server started');
|
|
1609
|
-
|
|
1610
|
-
if (hasParameter('pairingcode')) {
|
|
1611
|
-
this.log.info('Pairing device with pairingcode:', getParameter('pairingcode'));
|
|
1612
|
-
const pairingCode = getParameter('pairingcode');
|
|
1613
|
-
const ip = this.mattercontrollerContext.has('ip') ? this.mattercontrollerContext.get<string>('ip') : undefined;
|
|
1614
|
-
const port = this.mattercontrollerContext.has('port') ? this.mattercontrollerContext.get<number>('port') : undefined;
|
|
1615
|
-
|
|
1616
|
-
let longDiscriminator, setupPin, shortDiscriminator;
|
|
1617
|
-
if (pairingCode !== undefined) {
|
|
1618
|
-
const pairingCodeCodec = ManualPairingCodeCodec.decode(pairingCode);
|
|
1619
|
-
shortDiscriminator = pairingCodeCodec.shortDiscriminator;
|
|
1620
|
-
longDiscriminator = undefined;
|
|
1621
|
-
setupPin = pairingCodeCodec.passcode;
|
|
1622
|
-
this.log.info(`Data extracted from pairing code: ${Logger.toJSON(pairingCodeCodec)}`);
|
|
1623
|
-
} else {
|
|
1624
|
-
longDiscriminator = await this.mattercontrollerContext.get('longDiscriminator', 3840);
|
|
1625
|
-
if (longDiscriminator > 4095) throw new Error('Discriminator value must be less than 4096');
|
|
1626
|
-
setupPin = this.mattercontrollerContext.get('pin', 20202021);
|
|
1627
|
-
}
|
|
1628
|
-
if ((shortDiscriminator === undefined && longDiscriminator === undefined) || setupPin === undefined) {
|
|
1629
|
-
throw new Error('Please specify the longDiscriminator of the device to commission with -longDiscriminator or provide a valid passcode with -passcode');
|
|
1630
|
-
}
|
|
1631
|
-
|
|
1632
|
-
const commissioningOptions: ControllerCommissioningFlowOptions = {
|
|
1633
|
-
regulatoryLocation: GeneralCommissioning.RegulatoryLocationType.IndoorOutdoor,
|
|
1634
|
-
regulatoryCountryCode: 'XX',
|
|
1635
|
-
};
|
|
1636
|
-
const options = {
|
|
1637
|
-
commissioning: commissioningOptions,
|
|
1638
|
-
discovery: {
|
|
1639
|
-
knownAddress: ip !== undefined && port !== undefined ? { ip, port, type: 'udp' } : undefined,
|
|
1640
|
-
identifierData: longDiscriminator !== undefined ? { longDiscriminator } : shortDiscriminator !== undefined ? { shortDiscriminator } : {},
|
|
1641
|
-
},
|
|
1642
|
-
passcode: setupPin,
|
|
1643
|
-
} as NodeCommissioningOptions;
|
|
1644
|
-
this.log.info('Commissioning with options:', options);
|
|
1645
|
-
const nodeId = await this.commissioningController.commissionNode(options);
|
|
1646
|
-
this.log.info(`Commissioning successfully done with nodeId: ${nodeId}`);
|
|
1647
|
-
this.log.info('ActiveSessionInformation:', this.commissioningController.getActiveSessionInformation());
|
|
1648
|
-
} // (hasParameter('pairingcode'))
|
|
1649
|
-
|
|
1650
|
-
if (hasParameter('unpairall')) {
|
|
1651
|
-
this.log.info('***Commissioning controller unpairing all nodes...');
|
|
1652
|
-
const nodeIds = this.commissioningController.getCommissionedNodes();
|
|
1653
|
-
for (const nodeId of nodeIds) {
|
|
1654
|
-
this.log.info('***Commissioning controller unpairing node:', nodeId);
|
|
1655
|
-
await this.commissioningController.removeNode(nodeId);
|
|
1656
|
-
}
|
|
1657
|
-
return;
|
|
1658
|
-
}
|
|
1659
|
-
|
|
1660
|
-
if (hasParameter('discover')) {
|
|
1661
|
-
// const discover = await this.commissioningController.discoverCommissionableDevices({ productId: 0x8000, deviceType: 0xfff1 });
|
|
1662
|
-
// console.log(discover);
|
|
1663
|
-
}
|
|
1664
|
-
|
|
1665
|
-
if (!this.commissioningController.isCommissioned()) {
|
|
1666
|
-
this.log.info('***Commissioning controller is not commissioned: use matterbridge -controller -pairingcode [pairingcode] to commission a device');
|
|
1667
|
-
return;
|
|
1668
|
-
}
|
|
1669
|
-
|
|
1670
|
-
const nodeIds = this.commissioningController.getCommissionedNodes();
|
|
1671
|
-
this.log.info(`***Commissioning controller is commissioned ${this.commissioningController.isCommissioned()} and has ${nodeIds.length} nodes commisioned: `);
|
|
1672
|
-
for (const nodeId of nodeIds) {
|
|
1673
|
-
this.log.info(`***Connecting to commissioned node: ${nodeId}`);
|
|
1674
|
-
|
|
1675
|
-
const node = await this.commissioningController.connectNode(nodeId, {
|
|
1676
|
-
autoSubscribe: false,
|
|
1677
|
-
attributeChangedCallback: (peerNodeId, { path: { nodeId, clusterId, endpointId, attributeName }, value }) =>
|
|
1678
|
-
this.log.info(`***Commissioning controller attributeChangedCallback ${peerNodeId}: attribute ${nodeId}/${endpointId}/${clusterId}/${attributeName} changed to ${Logger.toJSON(value)}`),
|
|
1679
|
-
eventTriggeredCallback: (peerNodeId, { path: { nodeId, clusterId, endpointId, eventName }, events }) =>
|
|
1680
|
-
this.log.info(`***Commissioning controller eventTriggeredCallback ${peerNodeId}: Event ${nodeId}/${endpointId}/${clusterId}/${eventName} triggered with ${Logger.toJSON(events)}`),
|
|
1681
|
-
stateInformationCallback: (peerNodeId, info) => {
|
|
1682
|
-
switch (info) {
|
|
1683
|
-
case NodeStateInformation.Connected:
|
|
1684
|
-
this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} connected`);
|
|
1685
|
-
break;
|
|
1686
|
-
case NodeStateInformation.Disconnected:
|
|
1687
|
-
this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} disconnected`);
|
|
1688
|
-
break;
|
|
1689
|
-
case NodeStateInformation.Reconnecting:
|
|
1690
|
-
this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} reconnecting`);
|
|
1691
|
-
break;
|
|
1692
|
-
case NodeStateInformation.WaitingForDeviceDiscovery:
|
|
1693
|
-
this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} waiting for device discovery`);
|
|
1694
|
-
break;
|
|
1695
|
-
case NodeStateInformation.StructureChanged:
|
|
1696
|
-
this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} structure changed`);
|
|
1697
|
-
break;
|
|
1698
|
-
case NodeStateInformation.Decommissioned:
|
|
1699
|
-
this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} decommissioned`);
|
|
1700
|
-
break;
|
|
1701
|
-
default:
|
|
1702
|
-
this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} NodeStateInformation.${info}`);
|
|
1703
|
-
break;
|
|
1704
|
-
}
|
|
1705
|
-
},
|
|
1706
|
-
});
|
|
1707
|
-
|
|
1708
|
-
node.logStructure();
|
|
1709
|
-
|
|
1710
|
-
// Get the interaction client
|
|
1711
|
-
this.log.info('Getting the interaction client');
|
|
1712
|
-
const interactionClient = await node.getInteractionClient();
|
|
1713
|
-
let cluster;
|
|
1714
|
-
let attributes;
|
|
1715
|
-
|
|
1716
|
-
// Log BasicInformationCluster
|
|
1717
|
-
cluster = BasicInformationCluster;
|
|
1718
|
-
attributes = await interactionClient.getMultipleAttributes({
|
|
1719
|
-
attributes: [{ clusterId: cluster.id }],
|
|
1720
|
-
});
|
|
1721
|
-
if (attributes.length > 0) this.log.info(`Cluster: ${idn}${cluster.name}${rs}${nf} attributes:`);
|
|
1722
|
-
attributes.forEach((attribute) => {
|
|
1723
|
-
this.log.info(
|
|
1724
|
-
`- endpoint ${or}${attribute.path.endpointId}${nf} cluster ${hk}${getClusterNameById(attribute.path.clusterId)}${nf} (${hk}0x${attribute.path.clusterId.toString(16)}${nf}) attribute ${zb}${attribute.path.attributeName}${nf} (${zb}0x${attribute.path.attributeId.toString(16)}${nf}): ${typeof attribute.value === 'object' ? stringify(attribute.value) : attribute.value}`,
|
|
1725
|
-
);
|
|
1726
|
-
});
|
|
1727
|
-
|
|
1728
|
-
// Log PowerSourceCluster
|
|
1729
|
-
cluster = PowerSourceCluster;
|
|
1730
|
-
attributes = await interactionClient.getMultipleAttributes({
|
|
1731
|
-
attributes: [{ clusterId: cluster.id }],
|
|
1732
|
-
});
|
|
1733
|
-
if (attributes.length > 0) this.log.info(`Cluster: ${idn}${cluster.name}${rs}${nf} attributes:`);
|
|
1734
|
-
attributes.forEach((attribute) => {
|
|
1735
|
-
this.log.info(
|
|
1736
|
-
`- endpoint ${or}${attribute.path.endpointId}${nf} cluster ${hk}${getClusterNameById(attribute.path.clusterId)}${nf} (${hk}0x${attribute.path.clusterId.toString(16)}${nf}) attribute ${zb}${attribute.path.attributeName}${nf} (${zb}0x${attribute.path.attributeId.toString(16)}${nf}): ${typeof attribute.value === 'object' ? stringify(attribute.value) : attribute.value}`,
|
|
1737
|
-
);
|
|
1738
|
-
});
|
|
1739
|
-
|
|
1740
|
-
// Log ThreadNetworkDiagnostics
|
|
1741
|
-
cluster = ThreadNetworkDiagnosticsCluster;
|
|
1742
|
-
attributes = await interactionClient.getMultipleAttributes({
|
|
1743
|
-
attributes: [{ clusterId: cluster.id }],
|
|
1744
|
-
});
|
|
1745
|
-
if (attributes.length > 0) this.log.info(`Cluster: ${idn}${cluster.name}${rs}${nf} attributes:`);
|
|
1746
|
-
attributes.forEach((attribute) => {
|
|
1747
|
-
this.log.info(
|
|
1748
|
-
`- endpoint ${or}${attribute.path.endpointId}${nf} cluster ${hk}${getClusterNameById(attribute.path.clusterId)}${nf} (${hk}0x${attribute.path.clusterId.toString(16)}${nf}) attribute ${zb}${attribute.path.attributeName}${nf} (${zb}0x${attribute.path.attributeId.toString(16)}${nf}): ${typeof attribute.value === 'object' ? stringify(attribute.value) : attribute.value}`,
|
|
1749
|
-
);
|
|
1750
|
-
});
|
|
1751
|
-
|
|
1752
|
-
// Log SwitchCluster
|
|
1753
|
-
cluster = SwitchCluster;
|
|
1754
|
-
attributes = await interactionClient.getMultipleAttributes({
|
|
1755
|
-
attributes: [{ clusterId: cluster.id }],
|
|
1756
|
-
});
|
|
1757
|
-
if (attributes.length > 0) this.log.info(`Cluster: ${idn}${cluster.name}${rs}${nf} attributes:`);
|
|
1758
|
-
attributes.forEach((attribute) => {
|
|
1759
|
-
this.log.info(
|
|
1760
|
-
`- 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}`,
|
|
1761
|
-
);
|
|
1762
|
-
});
|
|
1763
|
-
|
|
1764
|
-
this.log.info('Subscribing to all attributes and events');
|
|
1765
|
-
await node.subscribeAllAttributesAndEvents({
|
|
1766
|
-
ignoreInitialTriggers: false,
|
|
1767
|
-
attributeChangedCallback: ({ path: { nodeId, clusterId, endpointId, attributeName }, version, value }) =>
|
|
1768
|
-
this.log.info(
|
|
1769
|
-
`***${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}`,
|
|
1770
|
-
),
|
|
1771
|
-
eventTriggeredCallback: ({ path: { nodeId, clusterId, endpointId, eventName }, events }) => {
|
|
1772
|
-
this.log.info(
|
|
1773
|
-
`***${db}Commissioning controller eventTriggeredCallback: event ${BLUE}${nodeId}${db}/${or}${endpointId}${db}/${hk}${getClusterNameById(clusterId)}${db}/${zb}${eventName}${db} triggered with ${debugStringify(events ?? { none: true })}`,
|
|
1774
|
-
);
|
|
1775
|
-
},
|
|
1776
|
-
});
|
|
1777
|
-
this.log.info('Subscribed to all attributes and events');
|
|
1778
|
-
}
|
|
1779
|
-
*/
|
|
1780
1286
|
}
|
|
1781
|
-
/** ***********************************************************************************************************************************/
|
|
1782
|
-
/** Matter.js methods */
|
|
1783
|
-
/** ***********************************************************************************************************************************/
|
|
1784
|
-
/**
|
|
1785
|
-
* Starts the matter storage process with name Matterbridge.
|
|
1786
|
-
* @returns {Promise<void>} - A promise that resolves when the storage process is started.
|
|
1787
|
-
*/
|
|
1788
1287
|
async startMatterStorage() {
|
|
1789
|
-
// Setup Matter storage
|
|
1790
1288
|
this.log.info(`Starting matter node storage...`);
|
|
1791
1289
|
this.matterStorageService = this.environment.get(StorageService);
|
|
1792
1290
|
this.log.info(`Matter node storage service created: ${this.matterStorageService.location}`);
|
|
@@ -1794,25 +1292,13 @@ export class Matterbridge extends EventEmitter {
|
|
|
1794
1292
|
this.log.info('Matter node storage manager "Matterbridge" created');
|
|
1795
1293
|
this.matterbridgeContext = await this.createServerNodeContext('Matterbridge', 'Matterbridge', bridge.code, this.aggregatorVendorId, 'Matterbridge', this.aggregatorProductId, 'Matterbridge aggregator');
|
|
1796
1294
|
this.log.info('Matter node storage started');
|
|
1797
|
-
// Backup matter storage since it is created/opened correctly
|
|
1798
1295
|
await this.backupMatterStorage(path.join(this.matterbridgeDirectory, this.matterStorageName), path.join(this.matterbridgeDirectory, this.matterStorageName + '.backup'));
|
|
1799
1296
|
}
|
|
1800
|
-
/**
|
|
1801
|
-
* Makes a backup copy of the specified matter storage directory.
|
|
1802
|
-
*
|
|
1803
|
-
* @param storageName - The name of the storage directory to be backed up.
|
|
1804
|
-
* @param backupName - The name of the backup directory to be created.
|
|
1805
|
-
* @returns {Promise<void>} A promise that resolves when the has been done.
|
|
1806
|
-
*/
|
|
1807
1297
|
async backupMatterStorage(storageName, backupName) {
|
|
1808
1298
|
this.log.info('Creating matter node storage backup...');
|
|
1809
1299
|
await copyDirectory(storageName, backupName);
|
|
1810
1300
|
this.log.info('Created matter node storage backup');
|
|
1811
1301
|
}
|
|
1812
|
-
/**
|
|
1813
|
-
* Stops the matter storage.
|
|
1814
|
-
* @returns {Promise<void>} A promise that resolves when the storage is stopped.
|
|
1815
|
-
*/
|
|
1816
1302
|
async stopMatterStorage() {
|
|
1817
1303
|
this.log.info('Closing matter node storage...');
|
|
1818
1304
|
this.matterStorageManager?.close();
|
|
@@ -1821,19 +1307,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1821
1307
|
this.matterbridgeContext = undefined;
|
|
1822
1308
|
this.log.info('Matter node storage closed');
|
|
1823
1309
|
}
|
|
1824
|
-
/**
|
|
1825
|
-
* Creates a server node storage context.
|
|
1826
|
-
*
|
|
1827
|
-
* @param {string} pluginName - The name of the plugin.
|
|
1828
|
-
* @param {string} deviceName - The name of the device.
|
|
1829
|
-
* @param {DeviceTypeId} deviceType - The device type of the device.
|
|
1830
|
-
* @param {number} vendorId - The vendor ID.
|
|
1831
|
-
* @param {string} vendorName - The vendor name.
|
|
1832
|
-
* @param {number} productId - The product ID.
|
|
1833
|
-
* @param {string} productName - The product name.
|
|
1834
|
-
* @param {string} [serialNumber] - The serial number of the device (optional).
|
|
1835
|
-
* @returns {Promise<StorageContext>} The storage context for the commissioning server.
|
|
1836
|
-
*/
|
|
1837
1310
|
async createServerNodeContext(pluginName, deviceName, deviceType, vendorId, vendorName, productId, productName, serialNumber) {
|
|
1838
1311
|
if (!this.matterStorageService)
|
|
1839
1312
|
throw new Error('No storage service initialized');
|
|
@@ -1866,15 +1339,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1866
1339
|
this.log.debug(`- hardwareVersion: ${await storageContext.get('hardwareVersion')} hardwareVersionString: ${await storageContext.get('hardwareVersionString')}`);
|
|
1867
1340
|
return storageContext;
|
|
1868
1341
|
}
|
|
1869
|
-
/**
|
|
1870
|
-
* Creates a server node.
|
|
1871
|
-
*
|
|
1872
|
-
* @param {StorageContext} storageContext - The storage context for the server node.
|
|
1873
|
-
* @param {number} [port=5540] - The port number for the server node. Defaults to 5540.
|
|
1874
|
-
* @param {number} [passcode=20242025] - The passcode for the server node. Defaults to 20242025.
|
|
1875
|
-
* @param {number} [discriminator=3850] - The discriminator for the server node. Defaults to 3850.
|
|
1876
|
-
* @returns {Promise<ServerNode<ServerNode.RootEndpoint>>} A promise that resolves to the created server node.
|
|
1877
|
-
*/
|
|
1878
1342
|
async createServerNode(storageContext, port = 5540, passcode = 20242025, discriminator = 3850) {
|
|
1879
1343
|
const storeId = await storageContext.get('storeId');
|
|
1880
1344
|
this.log.notice(`Creating server node for ${storeId} on port ${port} with passcode ${passcode} and discriminator ${discriminator}...`);
|
|
@@ -1884,33 +1348,21 @@ export class Matterbridge extends EventEmitter {
|
|
|
1884
1348
|
this.log.debug(`- uniqueId: ${await storageContext.get('uniqueId')}`);
|
|
1885
1349
|
this.log.debug(`- softwareVersion: ${await storageContext.get('softwareVersion')} softwareVersionString: ${await storageContext.get('softwareVersionString')}`);
|
|
1886
1350
|
this.log.debug(`- hardwareVersion: ${await storageContext.get('hardwareVersion')} hardwareVersionString: ${await storageContext.get('hardwareVersionString')}`);
|
|
1887
|
-
/**
|
|
1888
|
-
* Create a Matter ServerNode, which contains the Root Endpoint and all relevant data and configuration
|
|
1889
|
-
*/
|
|
1890
1351
|
const serverNode = await ServerNode.create({
|
|
1891
|
-
// Required: Give the Node a unique ID which is used to store the state of this node
|
|
1892
1352
|
id: storeId,
|
|
1893
|
-
// Provide Network relevant configuration like the port
|
|
1894
|
-
// Optional when operating only one device on a host, Default port is 5540
|
|
1895
1353
|
network: {
|
|
1896
1354
|
listeningAddressIpv4: this.ipv4address,
|
|
1897
1355
|
listeningAddressIpv6: this.ipv6address,
|
|
1898
1356
|
port,
|
|
1899
1357
|
},
|
|
1900
|
-
// Provide Commissioning relevant settings
|
|
1901
|
-
// Optional for development/testing purposes
|
|
1902
1358
|
commissioning: {
|
|
1903
1359
|
passcode,
|
|
1904
1360
|
discriminator,
|
|
1905
1361
|
},
|
|
1906
|
-
// Provide Node announcement settings
|
|
1907
|
-
// Optional: If Ommitted some development defaults are used
|
|
1908
1362
|
productDescription: {
|
|
1909
1363
|
name: await storageContext.get('deviceName'),
|
|
1910
1364
|
deviceType: DeviceTypeId(await storageContext.get('deviceType')),
|
|
1911
1365
|
},
|
|
1912
|
-
// Provide defaults for the BasicInformation cluster on the Root endpoint
|
|
1913
|
-
// Optional: If Omitted some development defaults are used
|
|
1914
1366
|
basicInformation: {
|
|
1915
1367
|
vendorId: VendorId(await storageContext.get('vendorId')),
|
|
1916
1368
|
vendorName: await storageContext.get('vendorName'),
|
|
@@ -1927,13 +1379,12 @@ export class Matterbridge extends EventEmitter {
|
|
|
1927
1379
|
},
|
|
1928
1380
|
});
|
|
1929
1381
|
const sanitizeFabrics = (fabrics, resetSessions = false) => {
|
|
1930
|
-
// New type of fabric information: Record<FabricIndex, ExposedFabricInformation>
|
|
1931
1382
|
const sanitizedFabrics = this.sanitizeFabricInformations(Array.from(Object.values(fabrics)));
|
|
1932
1383
|
this.log.info(`Fabrics: ${debugStringify(sanitizedFabrics)}`);
|
|
1933
1384
|
if (this.bridgeMode === 'bridge') {
|
|
1934
1385
|
this.matterbridgeFabricInformations = sanitizedFabrics;
|
|
1935
1386
|
if (resetSessions)
|
|
1936
|
-
this.matterbridgeSessionInformations = undefined;
|
|
1387
|
+
this.matterbridgeSessionInformations = undefined;
|
|
1937
1388
|
this.matterbridgePaired = true;
|
|
1938
1389
|
}
|
|
1939
1390
|
if (this.bridgeMode === 'childbridge') {
|
|
@@ -1941,19 +1392,13 @@ export class Matterbridge extends EventEmitter {
|
|
|
1941
1392
|
if (plugin) {
|
|
1942
1393
|
plugin.fabricInformations = sanitizedFabrics;
|
|
1943
1394
|
if (resetSessions)
|
|
1944
|
-
plugin.sessionInformations = undefined;
|
|
1395
|
+
plugin.sessionInformations = undefined;
|
|
1945
1396
|
plugin.paired = true;
|
|
1946
1397
|
}
|
|
1947
1398
|
}
|
|
1948
1399
|
};
|
|
1949
|
-
/**
|
|
1950
|
-
* This event is triggered when the device is initially commissioned successfully.
|
|
1951
|
-
* This means: It is added to the first fabric.
|
|
1952
|
-
*/
|
|
1953
1400
|
serverNode.lifecycle.commissioned.on(() => this.log.notice(`Server node for ${storeId} was initially commissioned successfully!`));
|
|
1954
|
-
/** This event is triggered when all fabrics are removed from the device, usually it also does a factory reset then. */
|
|
1955
1401
|
serverNode.lifecycle.decommissioned.on(() => this.log.notice(`Server node for ${storeId} was fully decommissioned successfully!`));
|
|
1956
|
-
/** This event is triggered when the device went online. This means that it is discoverable in the network. */
|
|
1957
1402
|
serverNode.lifecycle.online.on(async () => {
|
|
1958
1403
|
this.log.notice(`Server node for ${storeId} is online`);
|
|
1959
1404
|
if (!serverNode.lifecycle.isCommissioned) {
|
|
@@ -1999,7 +1444,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1999
1444
|
}
|
|
2000
1445
|
this.frontend.wssSendRefreshRequired();
|
|
2001
1446
|
});
|
|
2002
|
-
/** This event is triggered when the device went offline. it is not longer discoverable or connectable in the network. */
|
|
2003
1447
|
serverNode.lifecycle.offline.on(() => {
|
|
2004
1448
|
this.log.notice(`Server node for ${storeId} is offline`);
|
|
2005
1449
|
if (this.bridgeMode === 'bridge') {
|
|
@@ -2021,10 +1465,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2021
1465
|
}
|
|
2022
1466
|
this.frontend.wssSendRefreshRequired();
|
|
2023
1467
|
});
|
|
2024
|
-
/**
|
|
2025
|
-
* This event is triggered when a fabric is added, removed or updated on the device. Use this if more granular
|
|
2026
|
-
* information is needed.
|
|
2027
|
-
*/
|
|
2028
1468
|
serverNode.events.commissioning.fabricsChanged.on((fabricIndex, fabricAction) => {
|
|
2029
1469
|
let action = '';
|
|
2030
1470
|
switch (fabricAction) {
|
|
@@ -2058,24 +1498,16 @@ export class Matterbridge extends EventEmitter {
|
|
|
2058
1498
|
}
|
|
2059
1499
|
}
|
|
2060
1500
|
};
|
|
2061
|
-
/**
|
|
2062
|
-
* This event is triggered when an operative new session was opened by a Controller.
|
|
2063
|
-
* It is not triggered for the initial commissioning process, just afterwards for real connections.
|
|
2064
|
-
*/
|
|
2065
1501
|
serverNode.events.sessions.opened.on((session) => {
|
|
2066
1502
|
this.log.notice(`Session opened on server node for ${storeId}: ${debugStringify(session)}`);
|
|
2067
1503
|
sanitizeSessions(Object.values(serverNode.state.sessions.sessions));
|
|
2068
1504
|
this.frontend.wssSendRefreshRequired();
|
|
2069
1505
|
});
|
|
2070
|
-
/**
|
|
2071
|
-
* This event is triggered when an operative session is closed by a Controller or because the Device goes offline.
|
|
2072
|
-
*/
|
|
2073
1506
|
serverNode.events.sessions.closed.on((session) => {
|
|
2074
1507
|
this.log.notice(`Session closed on server node for ${storeId}: ${debugStringify(session)}`);
|
|
2075
1508
|
sanitizeSessions(Object.values(serverNode.state.sessions.sessions));
|
|
2076
1509
|
this.frontend.wssSendRefreshRequired();
|
|
2077
1510
|
});
|
|
2078
|
-
/** This event is triggered when a subscription gets added or removed on an operative session. */
|
|
2079
1511
|
serverNode.events.sessions.subscriptionsChanged.on((session) => {
|
|
2080
1512
|
this.log.notice(`Session subscriptions changed on server node for ${storeId}: ${debugStringify(session)}`);
|
|
2081
1513
|
sanitizeSessions(Object.values(serverNode.state.sessions.sessions));
|
|
@@ -2084,57 +1516,38 @@ export class Matterbridge extends EventEmitter {
|
|
|
2084
1516
|
this.log.info(`Created server node for ${storeId}`);
|
|
2085
1517
|
return serverNode;
|
|
2086
1518
|
}
|
|
2087
|
-
/**
|
|
2088
|
-
* Starts the specified server node.
|
|
2089
|
-
*
|
|
2090
|
-
* @param {ServerNode} [matterServerNode] - The server node to start.
|
|
2091
|
-
* @returns {Promise<void>} A promise that resolves when the server node has started.
|
|
2092
|
-
*/
|
|
2093
1519
|
async startServerNode(matterServerNode) {
|
|
2094
1520
|
if (!matterServerNode)
|
|
2095
1521
|
return;
|
|
2096
1522
|
this.log.notice(`Starting ${matterServerNode.id} server node`);
|
|
2097
1523
|
await matterServerNode.start();
|
|
2098
1524
|
}
|
|
2099
|
-
/**
|
|
2100
|
-
* Stops the specified server node.
|
|
2101
|
-
*
|
|
2102
|
-
* @param {ServerNode} matterServerNode - The server node to stop.
|
|
2103
|
-
* @returns {Promise<void>} A promise that resolves when the server node has stopped.
|
|
2104
|
-
*/
|
|
2105
1525
|
async stopServerNode(matterServerNode) {
|
|
2106
1526
|
if (!matterServerNode)
|
|
2107
1527
|
return;
|
|
2108
1528
|
this.log.notice(`Closing ${matterServerNode.id} server node`);
|
|
2109
|
-
// Helper function to add a timeout to a promise
|
|
2110
1529
|
const withTimeout = (promise, ms) => {
|
|
2111
1530
|
return new Promise((resolve, reject) => {
|
|
2112
1531
|
const timer = setTimeout(() => reject(new Error('Operation timed out')), ms);
|
|
2113
1532
|
promise
|
|
2114
1533
|
.then((result) => {
|
|
2115
|
-
clearTimeout(timer);
|
|
1534
|
+
clearTimeout(timer);
|
|
2116
1535
|
resolve(result);
|
|
2117
1536
|
})
|
|
2118
1537
|
.catch((error) => {
|
|
2119
|
-
clearTimeout(timer);
|
|
1538
|
+
clearTimeout(timer);
|
|
2120
1539
|
reject(error);
|
|
2121
1540
|
});
|
|
2122
1541
|
});
|
|
2123
1542
|
};
|
|
2124
1543
|
try {
|
|
2125
|
-
await withTimeout(matterServerNode.close(), 30000);
|
|
1544
|
+
await withTimeout(matterServerNode.close(), 30000);
|
|
2126
1545
|
this.log.info(`Closed ${matterServerNode.id} server node`);
|
|
2127
1546
|
}
|
|
2128
1547
|
catch (error) {
|
|
2129
1548
|
this.log.error(`Failed to close ${matterServerNode.id} server node: ${error instanceof Error ? error.message : error}`);
|
|
2130
1549
|
}
|
|
2131
1550
|
}
|
|
2132
|
-
/**
|
|
2133
|
-
* Advertises the specified server node if it is commissioned.
|
|
2134
|
-
*
|
|
2135
|
-
* @param {ServerNode} [matterServerNode] - The server node to advertise.
|
|
2136
|
-
* @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.
|
|
2137
|
-
*/
|
|
2138
1551
|
async advertiseServerNode(matterServerNode) {
|
|
2139
1552
|
if (matterServerNode && matterServerNode.lifecycle.isCommissioned) {
|
|
2140
1553
|
await matterServerNode.env.get(DeviceCommissioner)?.allowBasicCommissioning();
|
|
@@ -2144,32 +1557,17 @@ export class Matterbridge extends EventEmitter {
|
|
|
2144
1557
|
}
|
|
2145
1558
|
return undefined;
|
|
2146
1559
|
}
|
|
2147
|
-
/**
|
|
2148
|
-
* Creates an aggregator node with the specified storage context.
|
|
2149
|
-
*
|
|
2150
|
-
* @param {StorageContext} storageContext - The storage context for the aggregator node.
|
|
2151
|
-
* @returns {Promise<EndpointNode<AggregatorEndpoint>>} A promise that resolves to the created aggregator node.
|
|
2152
|
-
*/
|
|
2153
1560
|
async createAggregatorNode(storageContext) {
|
|
2154
1561
|
this.log.notice(`Creating ${await storageContext.get('storeId')} aggregator `);
|
|
2155
1562
|
const aggregatorNode = new EndpointNode(AggregatorEndpoint, { id: `${await storageContext.get('storeId')}` });
|
|
2156
1563
|
return aggregatorNode;
|
|
2157
1564
|
}
|
|
2158
|
-
/**
|
|
2159
|
-
* Adds a MatterbridgeEndpoint to the specified plugin.
|
|
2160
|
-
*
|
|
2161
|
-
* @param {string} pluginName - The name of the plugin.
|
|
2162
|
-
* @param {MatterbridgeEndpoint} device - The device to add as a bridged endpoint.
|
|
2163
|
-
* @returns {Promise<void>} A promise that resolves when the bridged endpoint has been added.
|
|
2164
|
-
*/
|
|
2165
1565
|
async addBridgedEndpoint(pluginName, device) {
|
|
2166
|
-
// Check if the plugin is registered
|
|
2167
1566
|
const plugin = this.plugins.get(pluginName);
|
|
2168
1567
|
if (!plugin) {
|
|
2169
1568
|
this.log.error(`Error adding bridged endpoint ${dev}${device.deviceName}${er} (${zb}${device.id}${er}) plugin ${plg}${pluginName}${er} not found`);
|
|
2170
1569
|
return;
|
|
2171
1570
|
}
|
|
2172
|
-
// Register and add the device to the matterbridge aggregator node
|
|
2173
1571
|
if (this.bridgeMode === 'bridge') {
|
|
2174
1572
|
this.log.debug(`Adding bridged endpoint ${plg}${pluginName}${db}:${dev}${device.deviceName}${db} to Matterbridge aggregator node`);
|
|
2175
1573
|
if (!this.aggregatorNode)
|
|
@@ -2192,26 +1590,16 @@ export class Matterbridge extends EventEmitter {
|
|
|
2192
1590
|
plugin.registeredDevices++;
|
|
2193
1591
|
if (plugin.addedDevices !== undefined)
|
|
2194
1592
|
plugin.addedDevices++;
|
|
2195
|
-
// Add the device to the DeviceManager
|
|
2196
1593
|
this.devices.set(device);
|
|
2197
1594
|
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}`);
|
|
2198
1595
|
}
|
|
2199
|
-
/**
|
|
2200
|
-
* Removes a MatterbridgeEndpoint from the specified plugin.
|
|
2201
|
-
*
|
|
2202
|
-
* @param {string} pluginName - The name of the plugin.
|
|
2203
|
-
* @param {MatterbridgeEndpoint} device - The device to remove as a bridged endpoint.
|
|
2204
|
-
* @returns {Promise<void>} A promise that resolves when the bridged endpoint has been removed.
|
|
2205
|
-
*/
|
|
2206
1596
|
async removeBridgedEndpoint(pluginName, device) {
|
|
2207
1597
|
this.log.debug(`Removing bridged endpoint ${plg}${pluginName}${db}:${dev}${device.deviceName}${db} (${zb}${device.name}${db}) for plugin ${plg}${pluginName}${db}`);
|
|
2208
|
-
// Check if the plugin is registered
|
|
2209
1598
|
const plugin = this.plugins.get(pluginName);
|
|
2210
1599
|
if (!plugin) {
|
|
2211
1600
|
this.log.error(`Error removing bridged endpoint ${dev}${device.deviceName}${er} (${zb}${device.name}${er}) for plugin ${plg}${pluginName}${er}: plugin not found`);
|
|
2212
1601
|
return;
|
|
2213
1602
|
}
|
|
2214
|
-
// Register and add the device to the matterbridge aggregator node
|
|
2215
1603
|
if (this.bridgeMode === 'bridge') {
|
|
2216
1604
|
if (!this.aggregatorNode) {
|
|
2217
1605
|
this.log.error(`Error removing bridged endpoint ${dev}${device.deviceName}${er} (${zb}${device.name}${er}) for plugin ${plg}${pluginName}${er}: aggregator node not found`);
|
|
@@ -2226,7 +1614,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2226
1614
|
}
|
|
2227
1615
|
else if (this.bridgeMode === 'childbridge') {
|
|
2228
1616
|
if (plugin.type === 'AccessoryPlatform') {
|
|
2229
|
-
// Nothing to do here since the server node has no aggregator node but only the device itself
|
|
2230
1617
|
}
|
|
2231
1618
|
else if (plugin.type === 'DynamicPlatform') {
|
|
2232
1619
|
if (!plugin.aggregatorNode) {
|
|
@@ -2240,7 +1627,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2240
1627
|
plugin.registeredDevices--;
|
|
2241
1628
|
if (plugin.addedDevices !== undefined)
|
|
2242
1629
|
plugin.addedDevices--;
|
|
2243
|
-
// Close the server node TODO check if this is correct
|
|
2244
1630
|
if (plugin.registeredDevices === 0 && plugin.addedDevices === 0) {
|
|
2245
1631
|
if (plugin.serverNode) {
|
|
2246
1632
|
await this.stopServerNode(plugin.serverNode);
|
|
@@ -2251,27 +1637,14 @@ export class Matterbridge extends EventEmitter {
|
|
|
2251
1637
|
}
|
|
2252
1638
|
}
|
|
2253
1639
|
}
|
|
2254
|
-
// Remove the device from the DeviceManager
|
|
2255
1640
|
this.devices.remove(device);
|
|
2256
1641
|
}
|
|
2257
|
-
/**
|
|
2258
|
-
* Removes all bridged endpoints from the specified plugin.
|
|
2259
|
-
*
|
|
2260
|
-
* @param {string} pluginName - The name of the plugin.
|
|
2261
|
-
* @returns {Promise<void>} A promise that resolves when all bridged endpoints have been removed.
|
|
2262
|
-
*/
|
|
2263
1642
|
async removeAllBridgedEndpoints(pluginName) {
|
|
2264
1643
|
this.log.debug(`Removing all bridged endpoints for plugin ${plg}${pluginName}${db}`);
|
|
2265
1644
|
for (const device of this.devices.array().filter((device) => device.plugin === pluginName)) {
|
|
2266
1645
|
await this.removeBridgedEndpoint(pluginName, device);
|
|
2267
1646
|
}
|
|
2268
1647
|
}
|
|
2269
|
-
/**
|
|
2270
|
-
* Sanitizes the fabric information by converting bigint properties to strings because `res.json` doesn't support bigint.
|
|
2271
|
-
*
|
|
2272
|
-
* @param {ExposedFabricInformation[]} fabricInfo - The array of exposed fabric information objects.
|
|
2273
|
-
* @returns {SanitizedExposedFabricInformation[]} An array of sanitized exposed fabric information objects.
|
|
2274
|
-
*/
|
|
2275
1648
|
sanitizeFabricInformations(fabricInfo) {
|
|
2276
1649
|
return fabricInfo.map((info) => {
|
|
2277
1650
|
return {
|
|
@@ -2285,12 +1658,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2285
1658
|
};
|
|
2286
1659
|
});
|
|
2287
1660
|
}
|
|
2288
|
-
/**
|
|
2289
|
-
* Sanitizes the session information by converting bigint properties to strings because `res.json` doesn't support bigint.
|
|
2290
|
-
*
|
|
2291
|
-
* @param {SessionInformation[]} sessionInfo - The array of session information objects.
|
|
2292
|
-
* @returns {SanitizedSessionInformation[]} An array of sanitized session information objects.
|
|
2293
|
-
*/
|
|
2294
1661
|
sanitizeSessionInformation(sessionInfo) {
|
|
2295
1662
|
return sessionInfo
|
|
2296
1663
|
.filter((session) => session.isPeerActive)
|
|
@@ -2318,51 +1685,11 @@ export class Matterbridge extends EventEmitter {
|
|
|
2318
1685
|
};
|
|
2319
1686
|
});
|
|
2320
1687
|
}
|
|
2321
|
-
/**
|
|
2322
|
-
* Sets the reachability of a matter server node and trigger ReachableChanged event.
|
|
2323
|
-
*
|
|
2324
|
-
* @param {ServerNode<ServerNode.RootEndpoint>} serverNode - The commissioning server to set the reachability for.
|
|
2325
|
-
* @param {boolean} reachable - The new reachability status.
|
|
2326
|
-
*/
|
|
2327
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
2328
1688
|
setServerNodeReachability(serverNode, reachable) {
|
|
2329
|
-
/*
|
|
2330
|
-
const basicInformationCluster = commissioningServer?.getRootClusterServer(BasicInformationCluster);
|
|
2331
|
-
if (basicInformationCluster && basicInformationCluster.attributes.reachable !== undefined) basicInformationCluster.setReachableAttribute(reachable);
|
|
2332
|
-
if (basicInformationCluster && basicInformationCluster.triggerReachableChangedEvent) basicInformationCluster.triggerReachableChangedEvent({ reachableNewValue: reachable });
|
|
2333
|
-
*/
|
|
2334
1689
|
}
|
|
2335
|
-
/**
|
|
2336
|
-
* Sets the reachability of the specified matter aggregator and its bridged devices and trigger.
|
|
2337
|
-
* @param {EndpointNode<AggregatorEndpoint>} aggregatorNode - The matter aggregator to set the reachability for.
|
|
2338
|
-
* @param {boolean} reachable - A boolean indicating the reachability status to set.
|
|
2339
|
-
*/
|
|
2340
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
2341
1690
|
setAggregatorReachability(aggregatorNode, reachable) {
|
|
2342
|
-
/*
|
|
2343
|
-
const basicInformationCluster = matterAggregator.getClusterServer(BasicInformationCluster);
|
|
2344
|
-
if (basicInformationCluster && basicInformationCluster.attributes.reachable !== undefined) basicInformationCluster.setReachableAttribute(reachable);
|
|
2345
|
-
if (basicInformationCluster && basicInformationCluster.triggerReachableChangedEvent) basicInformationCluster.triggerReachableChangedEvent({ reachableNewValue: reachable });
|
|
2346
|
-
matterAggregator.getBridgedDevices().forEach((device) => {
|
|
2347
|
-
this.log.debug(`Setting reachability to true for bridged device: ${dev}${device.name}${nf}`);
|
|
2348
|
-
device.getClusterServer(BridgedDeviceBasicInformationCluster)?.setReachableAttribute(reachable);
|
|
2349
|
-
device.getClusterServer(BridgedDeviceBasicInformationCluster)?.triggerReachableChangedEvent({ reachableNewValue: reachable });
|
|
2350
|
-
});
|
|
2351
|
-
*/
|
|
2352
1691
|
}
|
|
2353
|
-
/**
|
|
2354
|
-
* Sets the reachability of a device and trigger.
|
|
2355
|
-
*
|
|
2356
|
-
* @param {MatterbridgeEndpoint} device - The device to set the reachability for.
|
|
2357
|
-
* @param {boolean} reachable - The new reachability status of the device.
|
|
2358
|
-
*/
|
|
2359
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
2360
1692
|
setDeviceReachability(device, reachable) {
|
|
2361
|
-
/*
|
|
2362
|
-
const basicInformationCluster = device.getClusterServer(BasicInformationCluster);
|
|
2363
|
-
if (basicInformationCluster && basicInformationCluster.attributes.reachable !== undefined) basicInformationCluster.setReachableAttribute(reachable);
|
|
2364
|
-
if (basicInformationCluster && basicInformationCluster.triggerReachableChangedEvent) basicInformationCluster.triggerReachableChangedEvent({ reachableNewValue: reachable });
|
|
2365
|
-
*/
|
|
2366
1693
|
}
|
|
2367
1694
|
getVendorIdName = (vendorId) => {
|
|
2368
1695
|
if (!vendorId)
|
|
@@ -2405,36 +1732,13 @@ export class Matterbridge extends EventEmitter {
|
|
|
2405
1732
|
}
|
|
2406
1733
|
return vendorName;
|
|
2407
1734
|
};
|
|
2408
|
-
/**
|
|
2409
|
-
* Spawns a child process with the given command and arguments.
|
|
2410
|
-
* @param {string} command - The command to execute.
|
|
2411
|
-
* @param {string[]} args - The arguments to pass to the command (default: []).
|
|
2412
|
-
* @returns {Promise<boolean>} A promise that resolves when the child process exits successfully, or rejects if there is an error.
|
|
2413
|
-
*/
|
|
2414
1735
|
async spawnCommand(command, args = []) {
|
|
2415
|
-
/*
|
|
2416
|
-
npm > npm.cmd on windows
|
|
2417
|
-
cmd.exe ['dir'] on windows
|
|
2418
|
-
await this.spawnCommand('npm', ['install', '-g', 'matterbridge']);
|
|
2419
|
-
process.on('unhandledRejection', (reason, promise) => {
|
|
2420
|
-
this.log.error('Unhandled Rejection at:', promise, 'reason:', reason);
|
|
2421
|
-
});
|
|
2422
|
-
|
|
2423
|
-
spawn - [14:27:21.125] [Matterbridge:spawn]: changed 38 packages in 4s
|
|
2424
|
-
spawn - [14:27:21.125] [Matterbridge:spawn]: 10 packages are looking for funding run `npm fund` for details
|
|
2425
|
-
debug - [14:27:21.131] [Matterbridge]: Child process exited with code 0 and signal null
|
|
2426
|
-
debug - [14:27:21.131] [Matterbridge]: Child process stdio streams have closed with code 0
|
|
2427
|
-
*/
|
|
2428
1736
|
const cmdLine = command + ' ' + args.join(' ');
|
|
2429
1737
|
if (process.platform === 'win32' && command === 'npm') {
|
|
2430
|
-
// Must be spawn('cmd.exe', ['/c', 'npm -g install <package>']);
|
|
2431
1738
|
const argstring = 'npm ' + args.join(' ');
|
|
2432
1739
|
args.splice(0, args.length, '/c', argstring);
|
|
2433
1740
|
command = 'cmd.exe';
|
|
2434
1741
|
}
|
|
2435
|
-
// Decide when using sudo on linux
|
|
2436
|
-
// When you need sudo: Spawn stderr: npm error Error: EACCES: permission denied
|
|
2437
|
-
// When you don't need sudo: Failed to start child process "npm install -g matterbridge-eve-door": spawn sudo ENOENT
|
|
2438
1742
|
if (hasParameter('sudo') || (process.platform === 'linux' && command === 'npm' && !hasParameter('docker') && !hasParameter('nosudo'))) {
|
|
2439
1743
|
args.unshift(command);
|
|
2440
1744
|
command = 'sudo';
|
|
@@ -2493,4 +1797,3 @@ export class Matterbridge extends EventEmitter {
|
|
|
2493
1797
|
});
|
|
2494
1798
|
}
|
|
2495
1799
|
}
|
|
2496
|
-
//# sourceMappingURL=matterbridge.js.map
|