matterbridge 2.1.4 → 2.1.5-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 +14 -0
- 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 +23 -232
- 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 +55 -761
- 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 +10 -710
- package/dist/matterbridgeEndpointHelpers.js +29 -105
- package/dist/matterbridgePlatform.js +5 -121
- 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 +13 -267
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -2
- 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 -110
- 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 -834
- package/dist/matterbridgeEndpoint.d.ts.map +0 -1
- package/dist/matterbridgeEndpoint.js.map +0 -1
- package/dist/matterbridgeEndpointHelpers.d.ts +0 -2264
- 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 -167
- 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/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
|
-
|
|
35
|
-
import { logInterfaces, copyDirectory, getParameter, getIntParameter, hasParameter } from './utils/utils.js';
|
|
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: '',
|
|
@@ -85,7 +55,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
85
55
|
restartMode: '',
|
|
86
56
|
readOnly: hasParameter('readonly'),
|
|
87
57
|
profile: getParameter('profile'),
|
|
88
|
-
loggerLevel: "info"
|
|
58
|
+
loggerLevel: "info",
|
|
89
59
|
fileLogger: false,
|
|
90
60
|
matterLoggerLevel: MatterLogLevel.INFO,
|
|
91
61
|
matterFileLogger: false,
|
|
@@ -120,11 +90,9 @@ export class Matterbridge extends EventEmitter {
|
|
|
120
90
|
plugins;
|
|
121
91
|
devices;
|
|
122
92
|
frontend = new Frontend(this);
|
|
123
|
-
// Matterbridge storage
|
|
124
93
|
nodeStorage;
|
|
125
94
|
nodeContext;
|
|
126
95
|
nodeStorageName = 'storage' + (getParameter('profile') ? '.' + getParameter('profile') : '');
|
|
127
|
-
// Cleanup
|
|
128
96
|
hasCleanupStarted = false;
|
|
129
97
|
initialized = false;
|
|
130
98
|
execRunningCount = 0;
|
|
@@ -136,57 +104,34 @@ export class Matterbridge extends EventEmitter {
|
|
|
136
104
|
sigtermHandler;
|
|
137
105
|
exceptionHandler;
|
|
138
106
|
rejectionHandler;
|
|
139
|
-
// Matter environment
|
|
140
107
|
environment = Environment.default;
|
|
141
|
-
// Matter storage
|
|
142
108
|
matterStorageName = 'matterstorage' + (getParameter('profile') ? '.' + getParameter('profile') : '');
|
|
143
109
|
matterStorageService;
|
|
144
110
|
matterStorageManager;
|
|
145
111
|
matterbridgeContext;
|
|
146
112
|
mattercontrollerContext;
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
discriminator; // first server node discriminator
|
|
113
|
+
mdnsInterface;
|
|
114
|
+
ipv4address;
|
|
115
|
+
ipv6address;
|
|
116
|
+
port;
|
|
117
|
+
passcode;
|
|
118
|
+
discriminator;
|
|
154
119
|
serverNode;
|
|
155
120
|
aggregatorNode;
|
|
156
121
|
aggregatorVendorId = VendorId(getIntParameter('vendorId') ?? 0xfff1);
|
|
157
122
|
aggregatorProductId = getIntParameter('productId') ?? 0x8000;
|
|
158
123
|
static instance;
|
|
159
|
-
// We load asyncronously so is private
|
|
160
124
|
constructor() {
|
|
161
125
|
super();
|
|
162
126
|
}
|
|
163
|
-
/**
|
|
164
|
-
* Retrieves the list of Matterbridge devices.
|
|
165
|
-
* @returns {MatterbridgeEndpoint[]} An array of MatterbridgeDevice objects.
|
|
166
|
-
*/
|
|
167
127
|
getDevices() {
|
|
168
128
|
return this.devices.array();
|
|
169
129
|
}
|
|
170
|
-
/**
|
|
171
|
-
* Retrieves the list of registered plugins.
|
|
172
|
-
* @returns {RegisteredPlugin[]} An array of RegisteredPlugin objects.
|
|
173
|
-
*/
|
|
174
130
|
getPlugins() {
|
|
175
131
|
return this.plugins.array();
|
|
176
132
|
}
|
|
177
|
-
/** ***********************************************************************************************************************************/
|
|
178
|
-
/** loadInstance() and cleanup() methods */
|
|
179
|
-
/** ***********************************************************************************************************************************/
|
|
180
|
-
/**
|
|
181
|
-
* Loads an instance of the Matterbridge class.
|
|
182
|
-
* If an instance already exists, return that instance.
|
|
183
|
-
*
|
|
184
|
-
* @param initialize - Whether to initialize the Matterbridge instance after loading.
|
|
185
|
-
* @returns The loaded Matterbridge instance.
|
|
186
|
-
*/
|
|
187
133
|
static async loadInstance(initialize = false) {
|
|
188
134
|
if (!Matterbridge.instance) {
|
|
189
|
-
// eslint-disable-next-line no-console
|
|
190
135
|
if (hasParameter('debug'))
|
|
191
136
|
console.log(GREEN + 'Creating a new instance of Matterbridge.', initialize ? 'Initializing...' : 'Not initializing...', rs);
|
|
192
137
|
Matterbridge.instance = new Matterbridge();
|
|
@@ -195,13 +140,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
195
140
|
}
|
|
196
141
|
return Matterbridge.instance;
|
|
197
142
|
}
|
|
198
|
-
/**
|
|
199
|
-
* Call cleanup().
|
|
200
|
-
* @deprecated This method is deprecated and is only used for jest tests.
|
|
201
|
-
*
|
|
202
|
-
*/
|
|
203
143
|
async destroyInstance() {
|
|
204
|
-
// Save server nodes to close
|
|
205
144
|
const servers = [];
|
|
206
145
|
if (this.bridgeMode === 'bridge') {
|
|
207
146
|
if (this.serverNode)
|
|
@@ -213,80 +152,54 @@ export class Matterbridge extends EventEmitter {
|
|
|
213
152
|
servers.push(plugin.serverNode);
|
|
214
153
|
}
|
|
215
154
|
}
|
|
216
|
-
// Cleanup
|
|
217
155
|
await this.cleanup('destroying instance...', false);
|
|
218
|
-
// Close servers mdns service
|
|
219
156
|
for (const server of servers) {
|
|
220
157
|
await server.env.get(MdnsService)[Symbol.asyncDispose]();
|
|
221
158
|
this.log.info(`Closed ${server.id} MdnsService`);
|
|
222
159
|
}
|
|
223
|
-
// Wait for the cleanup to finish
|
|
224
160
|
await new Promise((resolve) => {
|
|
225
161
|
setTimeout(resolve, 1000);
|
|
226
162
|
});
|
|
227
163
|
}
|
|
228
|
-
/**
|
|
229
|
-
* Initializes the Matterbridge application.
|
|
230
|
-
*
|
|
231
|
-
* @remarks
|
|
232
|
-
* This method performs the necessary setup and initialization steps for the Matterbridge application.
|
|
233
|
-
* It displays the help information if the 'help' parameter is provided, sets up the logger, checks the
|
|
234
|
-
* node version, registers signal handlers, initializes storage, and parses the command line.
|
|
235
|
-
*
|
|
236
|
-
* @returns A Promise that resolves when the initialization is complete.
|
|
237
|
-
*/
|
|
238
164
|
async initialize() {
|
|
239
|
-
// Set the restart mode
|
|
240
165
|
if (hasParameter('service'))
|
|
241
166
|
this.restartMode = 'service';
|
|
242
167
|
if (hasParameter('docker'))
|
|
243
168
|
this.restartMode = 'docker';
|
|
244
|
-
// Set the matterbridge directory
|
|
245
169
|
this.homeDirectory = getParameter('homedir') ?? os.homedir();
|
|
246
170
|
this.matterbridgeDirectory = path.join(this.homeDirectory, '.matterbridge');
|
|
247
|
-
// Setup the matter environment
|
|
248
171
|
this.environment.vars.set('log.level', MatterLogLevel.INFO);
|
|
249
172
|
this.environment.vars.set('log.format', MatterLogFormat.ANSI);
|
|
250
173
|
this.environment.vars.set('path.root', path.join(this.matterbridgeDirectory, this.matterStorageName));
|
|
251
174
|
this.environment.vars.set('runtime.signals', false);
|
|
252
175
|
this.environment.vars.set('runtime.exitcode', false);
|
|
253
|
-
|
|
254
|
-
this.log = new AnsiLogger({ logName: 'Matterbridge', logTimestampFormat: 4 /* TimestampFormat.TIME_MILLIS */, logLevel: hasParameter('debug') ? "debug" /* LogLevel.DEBUG */ : "info" /* LogLevel.INFO */ });
|
|
255
|
-
// Register process handlers
|
|
176
|
+
this.log = new AnsiLogger({ logName: 'Matterbridge', logTimestampFormat: 4, logLevel: hasParameter('debug') ? "debug" : "info" });
|
|
256
177
|
this.registerProcessHandlers();
|
|
257
|
-
// Initialize nodeStorage and nodeContext
|
|
258
178
|
try {
|
|
259
179
|
this.log.debug(`Creating node storage manager: ${CYAN}${this.nodeStorageName}${db}`);
|
|
260
180
|
this.nodeStorage = new NodeStorageManager({ dir: path.join(this.matterbridgeDirectory, this.nodeStorageName), writeQueue: false, expiredInterval: undefined, logging: false });
|
|
261
181
|
this.log.debug('Creating node storage context for matterbridge');
|
|
262
182
|
this.nodeContext = await this.nodeStorage.createStorage('matterbridge');
|
|
263
|
-
// TODO: Remove this code when node-persist-manager is updated
|
|
264
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
265
183
|
const keys = (await this.nodeStorage?.storage.keys());
|
|
266
184
|
for (const key of keys) {
|
|
267
185
|
this.log.debug(`Checking node storage manager key: ${CYAN}${key}${db}`);
|
|
268
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
269
186
|
await this.nodeStorage?.storage.get(key);
|
|
270
187
|
}
|
|
271
188
|
const storages = await this.nodeStorage.getStorageNames();
|
|
272
189
|
for (const storage of storages) {
|
|
273
190
|
this.log.debug(`Checking storage: ${CYAN}${storage}${db}`);
|
|
274
191
|
const nodeContext = await this.nodeStorage?.createStorage(storage);
|
|
275
|
-
// TODO: Remove this code when node-persist-manager is updated
|
|
276
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
277
192
|
const keys = (await nodeContext?.storage.keys());
|
|
278
193
|
keys.forEach(async (key) => {
|
|
279
194
|
this.log.debug(`Checking key: ${CYAN}${storage}:${key}${db}`);
|
|
280
195
|
await nodeContext?.get(key);
|
|
281
196
|
});
|
|
282
197
|
}
|
|
283
|
-
// Creating a backup of the node storage since it is not corrupted
|
|
284
198
|
this.log.debug('Creating node storage backup...');
|
|
285
199
|
await copyDirectory(path.join(this.matterbridgeDirectory, this.nodeStorageName), path.join(this.matterbridgeDirectory, this.nodeStorageName + '.backup'));
|
|
286
200
|
this.log.debug('Created node storage backup');
|
|
287
201
|
}
|
|
288
202
|
catch (error) {
|
|
289
|
-
// Restoring the backup of the node storage since it is corrupted
|
|
290
203
|
this.log.error(`Error creating node storage manager and context: ${error instanceof Error ? error.message : error}`);
|
|
291
204
|
if (hasParameter('norestore')) {
|
|
292
205
|
this.log.fatal(`The matterbridge node storage is corrupted. Parameter -norestore found: exiting...`);
|
|
@@ -301,51 +214,45 @@ export class Matterbridge extends EventEmitter {
|
|
|
301
214
|
this.log.fatal('Fatal error creating node storage manager and context for matterbridge');
|
|
302
215
|
throw new Error('Fatal error creating node storage manager and context for matterbridge');
|
|
303
216
|
}
|
|
304
|
-
// Set the first port to use for the commissioning server (will be incremented in childbridge mode)
|
|
305
217
|
this.port = getIntParameter('port') ?? (await this.nodeContext.get('matterport', 5540)) ?? 5540;
|
|
306
|
-
// Set the first passcode to use for the commissioning server (will be incremented in childbridge mode)
|
|
307
218
|
this.passcode = getIntParameter('passcode') ?? (await this.nodeContext.get('matterpasscode')) ?? PaseClient.generateRandomPasscode();
|
|
308
|
-
// Set the first discriminator to use for the commissioning server (will be incremented in childbridge mode)
|
|
309
219
|
this.discriminator = getIntParameter('discriminator') ?? (await this.nodeContext.get('matterdiscriminator')) ?? PaseClient.generateRandomDiscriminator();
|
|
310
220
|
this.log.debug(`Initializing commissioning server for Matterbridge... on port ${this.port} with passcode ${this.passcode} and discriminator ${this.discriminator}`);
|
|
311
|
-
// Set matterbridge logger level (context: matterbridgeLogLevel)
|
|
312
221
|
if (hasParameter('logger')) {
|
|
313
222
|
const level = getParameter('logger');
|
|
314
223
|
if (level === 'debug') {
|
|
315
|
-
this.log.logLevel = "debug"
|
|
224
|
+
this.log.logLevel = "debug";
|
|
316
225
|
}
|
|
317
226
|
else if (level === 'info') {
|
|
318
|
-
this.log.logLevel = "info"
|
|
227
|
+
this.log.logLevel = "info";
|
|
319
228
|
}
|
|
320
229
|
else if (level === 'notice') {
|
|
321
|
-
this.log.logLevel = "notice"
|
|
230
|
+
this.log.logLevel = "notice";
|
|
322
231
|
}
|
|
323
232
|
else if (level === 'warn') {
|
|
324
|
-
this.log.logLevel = "warn"
|
|
233
|
+
this.log.logLevel = "warn";
|
|
325
234
|
}
|
|
326
235
|
else if (level === 'error') {
|
|
327
|
-
this.log.logLevel = "error"
|
|
236
|
+
this.log.logLevel = "error";
|
|
328
237
|
}
|
|
329
238
|
else if (level === 'fatal') {
|
|
330
|
-
this.log.logLevel = "fatal"
|
|
239
|
+
this.log.logLevel = "fatal";
|
|
331
240
|
}
|
|
332
241
|
else {
|
|
333
242
|
this.log.warn(`Invalid matterbridge logger level: ${level}. Using default level "info".`);
|
|
334
|
-
this.log.logLevel = "info"
|
|
243
|
+
this.log.logLevel = "info";
|
|
335
244
|
}
|
|
336
245
|
}
|
|
337
246
|
else {
|
|
338
|
-
this.log.logLevel = await this.nodeContext.get('matterbridgeLogLevel', "info"
|
|
247
|
+
this.log.logLevel = await this.nodeContext.get('matterbridgeLogLevel', "info");
|
|
339
248
|
}
|
|
340
249
|
MatterbridgeEndpoint.logLevel = this.log.logLevel;
|
|
341
|
-
// Create the file logger for matterbridge (context: matterbridgeFileLog)
|
|
342
250
|
if (hasParameter('filelogger') || (await this.nodeContext.get('matterbridgeFileLog', false))) {
|
|
343
251
|
AnsiLogger.setGlobalLogfile(path.join(this.matterbridgeDirectory, this.matterbrideLoggerFile), this.log.logLevel, true);
|
|
344
252
|
this.matterbridgeInformation.fileLogger = true;
|
|
345
253
|
}
|
|
346
254
|
this.log.notice('Matterbridge is starting...');
|
|
347
255
|
this.log.debug(`Matterbridge logLevel: ${this.log.logLevel} fileLoger: ${this.matterbridgeInformation.fileLogger}.`);
|
|
348
|
-
// Set matter.js logger level, format and logger (context: matterLogLevel)
|
|
349
256
|
if (hasParameter('matterlogger')) {
|
|
350
257
|
const level = getParameter('matterlogger');
|
|
351
258
|
if (level === 'debug') {
|
|
@@ -376,7 +283,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
376
283
|
}
|
|
377
284
|
Logger.format = MatterLogFormat.ANSI;
|
|
378
285
|
Logger.setLogger('default', this.createMatterLogger());
|
|
379
|
-
// Create the file logger for matter.js (context: matterFileLog)
|
|
380
286
|
if (hasParameter('matterfilelogger') || (await this.nodeContext.get('matterFileLog', false))) {
|
|
381
287
|
this.matterbridgeInformation.matterFileLogger = true;
|
|
382
288
|
Logger.addLogger('matterfilelogger', await this.createMatterFileLogger(path.join(this.matterbridgeDirectory, this.matterLoggerFile), true), {
|
|
@@ -385,7 +291,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
385
291
|
});
|
|
386
292
|
}
|
|
387
293
|
this.log.debug(`Matter logLevel: ${Logger.defaultLogLevel} fileLoger: ${this.matterbridgeInformation.matterFileLogger}.`);
|
|
388
|
-
// Set the interface to use for matter server node mdnsInterface
|
|
389
294
|
if (hasParameter('mdnsinterface')) {
|
|
390
295
|
this.mdnsInterface = getParameter('mdnsinterface');
|
|
391
296
|
}
|
|
@@ -394,7 +299,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
394
299
|
if (this.mdnsInterface === '')
|
|
395
300
|
this.mdnsInterface = undefined;
|
|
396
301
|
}
|
|
397
|
-
// Validate mdnsInterface
|
|
398
302
|
if (this.mdnsInterface) {
|
|
399
303
|
const networkInterfaces = os.networkInterfaces();
|
|
400
304
|
const availableInterfaces = Object.keys(networkInterfaces);
|
|
@@ -408,7 +312,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
408
312
|
}
|
|
409
313
|
if (this.mdnsInterface)
|
|
410
314
|
this.environment.vars.set('mdns.networkInterface', this.mdnsInterface);
|
|
411
|
-
// Set the listeningAddressIpv4 for the matter commissioning server
|
|
412
315
|
if (hasParameter('ipv4address')) {
|
|
413
316
|
this.ipv4address = getParameter('ipv4address');
|
|
414
317
|
}
|
|
@@ -417,7 +320,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
417
320
|
if (this.ipv4address === '')
|
|
418
321
|
this.ipv4address = undefined;
|
|
419
322
|
}
|
|
420
|
-
// Set the listeningAddressIpv6 for the matter commissioning server
|
|
421
323
|
if (hasParameter('ipv6address')) {
|
|
422
324
|
this.ipv6address = getParameter('ipv6address');
|
|
423
325
|
}
|
|
@@ -426,19 +328,14 @@ export class Matterbridge extends EventEmitter {
|
|
|
426
328
|
if (this.ipv6address === '')
|
|
427
329
|
this.ipv6address = undefined;
|
|
428
330
|
}
|
|
429
|
-
// Initialize PluginManager
|
|
430
331
|
this.plugins = new PluginManager(this);
|
|
431
332
|
await this.plugins.loadFromStorage();
|
|
432
333
|
this.plugins.logLevel = this.log.logLevel;
|
|
433
|
-
// Initialize DeviceManager
|
|
434
334
|
this.devices = new DeviceManager(this, this.nodeContext);
|
|
435
335
|
this.devices.logLevel = this.log.logLevel;
|
|
436
|
-
// Get the plugins from node storage and create the plugins node storage contexts
|
|
437
336
|
for (const plugin of this.plugins) {
|
|
438
337
|
const packageJson = await this.plugins.parse(plugin);
|
|
439
338
|
if (packageJson === null && !hasParameter('add') && !hasParameter('remove') && !hasParameter('enable') && !hasParameter('disable') && !hasParameter('reset') && !hasParameter('factoryreset')) {
|
|
440
|
-
// Try to reinstall the plugin from npm (for Docker pull and external plugins)
|
|
441
|
-
// We don't do this when the add and other parameters are set because we shut down the process after adding the plugin
|
|
442
339
|
this.log.info(`Error parsing plugin ${plg}${plugin.name}${nf}. Trying to reinstall it from npm.`);
|
|
443
340
|
try {
|
|
444
341
|
await this.spawnCommand('npm', ['install', '-g', plugin.name, '--omit=dev', '--verbose']);
|
|
@@ -460,7 +357,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
460
357
|
await plugin.nodeContext.set('description', plugin.description);
|
|
461
358
|
await plugin.nodeContext.set('author', plugin.author);
|
|
462
359
|
}
|
|
463
|
-
// Log system info and create .matterbridge directory
|
|
464
360
|
await this.logNodeAndSystemInfo();
|
|
465
361
|
this.log.notice(`Matterbridge version ${this.matterbridgeVersion} ` +
|
|
466
362
|
`${hasParameter('bridge') || (!hasParameter('childbridge') && (await this.nodeContext?.get('bridgeMode', '')) === 'bridge') ? 'mode bridge ' : ''}` +
|
|
@@ -468,7 +364,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
468
364
|
`${hasParameter('controller') ? 'mode controller ' : ''}` +
|
|
469
365
|
`${this.restartMode !== '' ? 'restart mode ' + this.restartMode + ' ' : ''}` +
|
|
470
366
|
`running on ${this.systemInformation.osType} (v.${this.systemInformation.osRelease}) platform ${this.systemInformation.osPlatform} arch ${this.systemInformation.osArch}`);
|
|
471
|
-
// Check node version and throw error
|
|
472
367
|
const minNodeVersion = 18;
|
|
473
368
|
const nodeVersion = process.versions.node;
|
|
474
369
|
const versionMajor = parseInt(nodeVersion.split('.')[0]);
|
|
@@ -476,15 +371,9 @@ export class Matterbridge extends EventEmitter {
|
|
|
476
371
|
this.log.error(`Node version ${versionMajor} is not supported. Please upgrade to ${minNodeVersion} or above.`);
|
|
477
372
|
throw new Error(`Node version ${versionMajor} is not supported. Please upgrade to ${minNodeVersion} or above.`);
|
|
478
373
|
}
|
|
479
|
-
// Parse command line
|
|
480
374
|
await this.parseCommandLine();
|
|
481
375
|
this.initialized = true;
|
|
482
376
|
}
|
|
483
|
-
/**
|
|
484
|
-
* Parses the command line arguments and performs the corresponding actions.
|
|
485
|
-
* @private
|
|
486
|
-
* @returns {Promise<void>} A promise that resolves when the command line arguments have been processed, or the process exits.
|
|
487
|
-
*/
|
|
488
377
|
async parseCommandLine() {
|
|
489
378
|
if (hasParameter('help')) {
|
|
490
379
|
this.log.info(`\nUsage: matterbridge [options]\n
|
|
@@ -594,7 +483,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
594
483
|
await this.shutdownProcessAndFactoryReset();
|
|
595
484
|
return;
|
|
596
485
|
}
|
|
597
|
-
// Start the matter storage and create the matterbridge context
|
|
598
486
|
try {
|
|
599
487
|
await this.startMatterStorage();
|
|
600
488
|
}
|
|
@@ -602,12 +490,10 @@ export class Matterbridge extends EventEmitter {
|
|
|
602
490
|
this.log.fatal(`Fatal error creating matter storage: ${error instanceof Error ? error.message : error}`);
|
|
603
491
|
throw new Error(`Fatal error creating matter storage: ${error instanceof Error ? error.message : error}`);
|
|
604
492
|
}
|
|
605
|
-
// Clear the matterbridge context if the reset parameter is set
|
|
606
493
|
if (hasParameter('reset') && getParameter('reset') === undefined) {
|
|
607
494
|
await this.shutdownProcessAndReset();
|
|
608
495
|
return;
|
|
609
496
|
}
|
|
610
|
-
// Clear matterbridge plugin context if the reset parameter is set
|
|
611
497
|
if (hasParameter('reset') && getParameter('reset') !== undefined) {
|
|
612
498
|
this.log.debug(`Reset plugin ${getParameter('reset')}`);
|
|
613
499
|
const plugin = this.plugins.get(getParameter('reset'));
|
|
@@ -629,11 +515,13 @@ export class Matterbridge extends EventEmitter {
|
|
|
629
515
|
this.emit('shutdown');
|
|
630
516
|
return;
|
|
631
517
|
}
|
|
632
|
-
// Initialize frontend
|
|
633
518
|
if (getIntParameter('frontend') !== 0 || getIntParameter('frontend') === undefined)
|
|
634
519
|
await this.frontend.start(getIntParameter('frontend'));
|
|
635
520
|
this.frontend.logLevel = this.log.logLevel;
|
|
636
|
-
|
|
521
|
+
this.getMatterbridgeLatestVersion();
|
|
522
|
+
for (const plugin of this.plugins) {
|
|
523
|
+
this.getPluginLatestVersion(plugin);
|
|
524
|
+
}
|
|
637
525
|
this.checkUpdateInterval = setInterval(() => {
|
|
638
526
|
this.getMatterbridgeLatestVersion();
|
|
639
527
|
for (const plugin of this.plugins) {
|
|
@@ -641,24 +529,20 @@ export class Matterbridge extends EventEmitter {
|
|
|
641
529
|
}
|
|
642
530
|
this.frontend.wssSendRefreshRequired();
|
|
643
531
|
}, 60 * 60 * 1000);
|
|
644
|
-
// Start the matterbridge in mode test
|
|
645
532
|
if (hasParameter('test')) {
|
|
646
533
|
this.bridgeMode = 'bridge';
|
|
647
534
|
MatterbridgeEndpoint.bridgeMode = 'bridge';
|
|
648
535
|
return;
|
|
649
536
|
}
|
|
650
|
-
// Start the matterbridge in mode controller
|
|
651
537
|
if (hasParameter('controller')) {
|
|
652
538
|
this.bridgeMode = 'controller';
|
|
653
539
|
await this.startController();
|
|
654
540
|
return;
|
|
655
541
|
}
|
|
656
|
-
// Check if the bridge mode is set and start matterbridge in bridge mode if not set
|
|
657
542
|
if (!hasParameter('bridge') && !hasParameter('childbridge') && (await this.nodeContext?.get('bridgeMode', '')) === '') {
|
|
658
543
|
this.log.info('Setting default matterbridge start mode to bridge');
|
|
659
544
|
await this.nodeContext?.set('bridgeMode', 'bridge');
|
|
660
545
|
}
|
|
661
|
-
// Start matterbridge in bridge mode
|
|
662
546
|
if (hasParameter('bridge') || (!hasParameter('childbridge') && (await this.nodeContext?.get('bridgeMode', '')) === 'bridge')) {
|
|
663
547
|
this.bridgeMode = 'bridge';
|
|
664
548
|
MatterbridgeEndpoint.bridgeMode = 'bridge';
|
|
@@ -666,7 +550,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
666
550
|
await this.startBridge();
|
|
667
551
|
return;
|
|
668
552
|
}
|
|
669
|
-
// Start matterbridge in childbridge mode
|
|
670
553
|
if (hasParameter('childbridge') || (!hasParameter('bridge') && (await this.nodeContext?.get('bridgeMode', '')) === 'childbridge')) {
|
|
671
554
|
this.bridgeMode = 'childbridge';
|
|
672
555
|
MatterbridgeEndpoint.bridgeMode = 'childbridge';
|
|
@@ -675,28 +558,16 @@ export class Matterbridge extends EventEmitter {
|
|
|
675
558
|
return;
|
|
676
559
|
}
|
|
677
560
|
}
|
|
678
|
-
/**
|
|
679
|
-
* Asynchronously loads and starts the registered plugins.
|
|
680
|
-
*
|
|
681
|
-
* This method is responsible for initializing and staarting all enabled plugins.
|
|
682
|
-
* It ensures that each plugin is properly loaded and started before the bridge starts.
|
|
683
|
-
*
|
|
684
|
-
* @returns {Promise<void>} A promise that resolves when all plugins have been loaded and started.
|
|
685
|
-
*/
|
|
686
561
|
async startPlugins() {
|
|
687
|
-
// Check, load and start the plugins
|
|
688
562
|
for (const plugin of this.plugins) {
|
|
689
563
|
plugin.configJson = await this.plugins.loadConfig(plugin);
|
|
690
564
|
plugin.schemaJson = await this.plugins.loadSchema(plugin);
|
|
691
|
-
// Check if the plugin is available
|
|
692
565
|
if (!(await this.plugins.resolve(plugin.path))) {
|
|
693
566
|
this.log.error(`Plugin ${plg}${plugin.name}${er} not found or not validated. Disabling it.`);
|
|
694
567
|
plugin.enabled = false;
|
|
695
568
|
plugin.error = true;
|
|
696
569
|
continue;
|
|
697
570
|
}
|
|
698
|
-
// Check if the plugin has a new version
|
|
699
|
-
// this.getPluginLatestVersion(plugin); // No await do it asyncronously
|
|
700
571
|
if (!plugin.enabled) {
|
|
701
572
|
this.log.info(`Plugin ${plg}${plugin.name}${nf} not enabled`);
|
|
702
573
|
continue;
|
|
@@ -710,26 +581,20 @@ export class Matterbridge extends EventEmitter {
|
|
|
710
581
|
plugin.addedDevices = undefined;
|
|
711
582
|
plugin.qrPairingCode = undefined;
|
|
712
583
|
plugin.manualPairingCode = undefined;
|
|
713
|
-
this.plugins.load(plugin, true, 'Matterbridge is starting');
|
|
584
|
+
this.plugins.load(plugin, true, 'Matterbridge is starting');
|
|
714
585
|
}
|
|
715
586
|
this.frontend.wssSendRefreshRequired();
|
|
716
587
|
}
|
|
717
|
-
/**
|
|
718
|
-
* Registers the process handlers for uncaughtException, unhandledRejection, SIGINT and SIGTERM.
|
|
719
|
-
* When either of these signals are received, the cleanup method is called with an appropriate message.
|
|
720
|
-
*/
|
|
721
588
|
registerProcessHandlers() {
|
|
722
589
|
this.log.debug(`Registering uncaughtException and unhandledRejection handlers...`);
|
|
723
590
|
process.removeAllListeners('uncaughtException');
|
|
724
591
|
process.removeAllListeners('unhandledRejection');
|
|
725
592
|
this.exceptionHandler = async (error) => {
|
|
726
593
|
this.log.error('Unhandled Exception detected at:', error.stack || error, rs);
|
|
727
|
-
// await this.cleanup('Unhandled Exception detected, cleaning up...');
|
|
728
594
|
};
|
|
729
595
|
process.on('uncaughtException', this.exceptionHandler);
|
|
730
596
|
this.rejectionHandler = async (reason, promise) => {
|
|
731
597
|
this.log.error('Unhandled Rejection detected at:', promise, 'reason:', reason instanceof Error ? reason.stack : reason, rs);
|
|
732
|
-
// await this.cleanup('Unhandled Rejection detected, cleaning up...');
|
|
733
598
|
};
|
|
734
599
|
process.on('unhandledRejection', this.rejectionHandler);
|
|
735
600
|
this.log.debug(`Registering SIGINT and SIGTERM signal handlers...`);
|
|
@@ -742,9 +607,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
742
607
|
};
|
|
743
608
|
process.on('SIGTERM', this.sigtermHandler);
|
|
744
609
|
}
|
|
745
|
-
/**
|
|
746
|
-
* Deregisters the process uncaughtException, unhandledRejection, SIGINT and SIGTERM signal handlers.
|
|
747
|
-
*/
|
|
748
610
|
deregisterProcesslHandlers() {
|
|
749
611
|
this.log.debug(`Deregistering uncaughtException and unhandledRejection handlers...`);
|
|
750
612
|
if (this.exceptionHandler)
|
|
@@ -761,17 +623,12 @@ export class Matterbridge extends EventEmitter {
|
|
|
761
623
|
process.off('SIGTERM', this.sigtermHandler);
|
|
762
624
|
this.sigtermHandler = undefined;
|
|
763
625
|
}
|
|
764
|
-
/**
|
|
765
|
-
* Logs the node and system information.
|
|
766
|
-
*/
|
|
767
626
|
async logNodeAndSystemInfo() {
|
|
768
|
-
// IP address information
|
|
769
627
|
const networkInterfaces = os.networkInterfaces();
|
|
770
628
|
this.systemInformation.interfaceName = '';
|
|
771
629
|
this.systemInformation.ipv4Address = '';
|
|
772
630
|
this.systemInformation.ipv6Address = '';
|
|
773
631
|
for (const [interfaceName, interfaceDetails] of Object.entries(networkInterfaces)) {
|
|
774
|
-
// this.log.debug(`Checking interface: '${interfaceName}' for '${this.mdnsInterface}'`);
|
|
775
632
|
if (this.mdnsInterface && interfaceName !== this.mdnsInterface)
|
|
776
633
|
continue;
|
|
777
634
|
if (!interfaceDetails) {
|
|
@@ -797,22 +654,19 @@ export class Matterbridge extends EventEmitter {
|
|
|
797
654
|
break;
|
|
798
655
|
}
|
|
799
656
|
}
|
|
800
|
-
// Node information
|
|
801
657
|
this.systemInformation.nodeVersion = process.versions.node;
|
|
802
658
|
const versionMajor = parseInt(this.systemInformation.nodeVersion.split('.')[0]);
|
|
803
659
|
const versionMinor = parseInt(this.systemInformation.nodeVersion.split('.')[1]);
|
|
804
660
|
const versionPatch = parseInt(this.systemInformation.nodeVersion.split('.')[2]);
|
|
805
|
-
// Host system information
|
|
806
661
|
this.systemInformation.hostname = os.hostname();
|
|
807
662
|
this.systemInformation.user = os.userInfo().username;
|
|
808
|
-
this.systemInformation.osType = os.type();
|
|
809
|
-
this.systemInformation.osRelease = os.release();
|
|
810
|
-
this.systemInformation.osPlatform = os.platform();
|
|
811
|
-
this.systemInformation.osArch = os.arch();
|
|
812
|
-
this.systemInformation.totalMemory = (os.totalmem() / 1024 / 1024 / 1024).toFixed(2) + ' GB';
|
|
813
|
-
this.systemInformation.freeMemory = (os.freemem() / 1024 / 1024 / 1024).toFixed(2) + ' GB';
|
|
814
|
-
this.systemInformation.systemUptime = (os.uptime() / 60 / 60).toFixed(2) + ' hours';
|
|
815
|
-
// Log the system information
|
|
663
|
+
this.systemInformation.osType = os.type();
|
|
664
|
+
this.systemInformation.osRelease = os.release();
|
|
665
|
+
this.systemInformation.osPlatform = os.platform();
|
|
666
|
+
this.systemInformation.osArch = os.arch();
|
|
667
|
+
this.systemInformation.totalMemory = (os.totalmem() / 1024 / 1024 / 1024).toFixed(2) + ' GB';
|
|
668
|
+
this.systemInformation.freeMemory = (os.freemem() / 1024 / 1024 / 1024).toFixed(2) + ' GB';
|
|
669
|
+
this.systemInformation.systemUptime = (os.uptime() / 60 / 60).toFixed(2) + ' hours';
|
|
816
670
|
this.log.debug('Host System Information:');
|
|
817
671
|
this.log.debug(`- Hostname: ${this.systemInformation.hostname}`);
|
|
818
672
|
this.log.debug(`- User: ${this.systemInformation.user}`);
|
|
@@ -828,19 +682,15 @@ export class Matterbridge extends EventEmitter {
|
|
|
828
682
|
this.log.debug(`- Total Memory: ${this.systemInformation.totalMemory}`);
|
|
829
683
|
this.log.debug(`- Free Memory: ${this.systemInformation.freeMemory}`);
|
|
830
684
|
this.log.debug(`- System Uptime: ${this.systemInformation.systemUptime}`);
|
|
831
|
-
// Home directory
|
|
832
685
|
this.homeDirectory = getParameter('homedir') ?? os.homedir();
|
|
833
686
|
this.matterbridgeInformation.homeDirectory = this.homeDirectory;
|
|
834
687
|
this.log.debug(`Home Directory: ${this.homeDirectory}`);
|
|
835
|
-
// Package root directory
|
|
836
688
|
const currentFileDirectory = path.dirname(fileURLToPath(import.meta.url));
|
|
837
689
|
this.rootDirectory = path.resolve(currentFileDirectory, '../');
|
|
838
690
|
this.matterbridgeInformation.rootDirectory = this.rootDirectory;
|
|
839
691
|
this.log.debug(`Root Directory: ${this.rootDirectory}`);
|
|
840
|
-
// Global node_modules directory
|
|
841
692
|
if (this.nodeContext)
|
|
842
693
|
this.globalModulesDirectory = this.matterbridgeInformation.globalModulesDirectory = await this.nodeContext.get('globalModulesDirectory', '');
|
|
843
|
-
// First run of Matterbridge so the node storage is empty
|
|
844
694
|
if (this.globalModulesDirectory === '') {
|
|
845
695
|
try {
|
|
846
696
|
this.globalModulesDirectory = await this.getGlobalNodeModules();
|
|
@@ -854,20 +704,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
854
704
|
}
|
|
855
705
|
else
|
|
856
706
|
this.log.debug(`Global node_modules Directory: ${this.globalModulesDirectory}`);
|
|
857
|
-
/* removed cause is too expensive for the shelly board and not really needed. Why should it change the globalModulesDirectory?
|
|
858
|
-
else {
|
|
859
|
-
this.getGlobalNodeModules()
|
|
860
|
-
.then(async (globalModulesDirectory) => {
|
|
861
|
-
this.globalModulesDirectory = globalModulesDirectory;
|
|
862
|
-
this.matterbridgeInformation.globalModulesDirectory = this.globalModulesDirectory;
|
|
863
|
-
this.log.debug(`Global node_modules Directory: ${this.globalModulesDirectory}`);
|
|
864
|
-
await this.nodeContext?.set<string>('globalModulesDirectory', this.globalModulesDirectory);
|
|
865
|
-
})
|
|
866
|
-
.catch((error) => {
|
|
867
|
-
this.log.error(`Error getting global node_modules directory: ${error}`);
|
|
868
|
-
});
|
|
869
|
-
}*/
|
|
870
|
-
// Create the data directory .matterbridge in the home directory
|
|
871
707
|
this.matterbridgeDirectory = path.join(this.homeDirectory, '.matterbridge');
|
|
872
708
|
this.matterbridgeInformation.matterbridgeDirectory = this.matterbridgeDirectory;
|
|
873
709
|
try {
|
|
@@ -891,7 +727,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
891
727
|
}
|
|
892
728
|
}
|
|
893
729
|
this.log.debug(`Matterbridge Directory: ${this.matterbridgeDirectory}`);
|
|
894
|
-
// Create the plugin directory Matterbridge in the home directory
|
|
895
730
|
this.matterbridgePluginDirectory = path.join(this.homeDirectory, 'Matterbridge');
|
|
896
731
|
this.matterbridgeInformation.matterbridgePluginDirectory = this.matterbridgePluginDirectory;
|
|
897
732
|
try {
|
|
@@ -915,28 +750,18 @@ export class Matterbridge extends EventEmitter {
|
|
|
915
750
|
}
|
|
916
751
|
}
|
|
917
752
|
this.log.debug(`Matterbridge Plugin Directory: ${this.matterbridgePluginDirectory}`);
|
|
918
|
-
// Matterbridge version
|
|
919
753
|
const packageJson = JSON.parse(await fs.readFile(path.join(this.rootDirectory, 'package.json'), 'utf-8'));
|
|
920
754
|
this.matterbridgeVersion = this.matterbridgeLatestVersion = packageJson.version;
|
|
921
755
|
this.matterbridgeInformation.matterbridgeVersion = this.matterbridgeInformation.matterbridgeLatestVersion = this.matterbridgeVersion;
|
|
922
756
|
this.log.debug(`Matterbridge Version: ${this.matterbridgeVersion}`);
|
|
923
|
-
// Matterbridge latest version
|
|
924
757
|
if (this.nodeContext)
|
|
925
758
|
this.matterbridgeLatestVersion = await this.nodeContext.get('matterbridgeLatestVersion', this.matterbridgeVersion);
|
|
926
759
|
this.log.debug(`Matterbridge Latest Version: ${this.matterbridgeLatestVersion}`);
|
|
927
|
-
// this.getMatterbridgeLatestVersion();
|
|
928
|
-
// Current working directory
|
|
929
760
|
const currentDir = process.cwd();
|
|
930
761
|
this.log.debug(`Current Working Directory: ${currentDir}`);
|
|
931
|
-
// Command line arguments (excluding 'node' and the script name)
|
|
932
762
|
const cmdArgs = process.argv.slice(2).join(' ');
|
|
933
763
|
this.log.debug(`Command Line Arguments: ${cmdArgs}`);
|
|
934
764
|
}
|
|
935
|
-
/**
|
|
936
|
-
* Retrieves the latest version of a package from the npm registry.
|
|
937
|
-
* @param packageName - The name of the package.
|
|
938
|
-
* @returns A Promise that resolves to the latest version of the package.
|
|
939
|
-
*/
|
|
940
765
|
async getLatestVersion(packageName) {
|
|
941
766
|
return new Promise((resolve, reject) => {
|
|
942
767
|
this.execRunningCount++;
|
|
@@ -951,10 +776,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
951
776
|
});
|
|
952
777
|
});
|
|
953
778
|
}
|
|
954
|
-
/**
|
|
955
|
-
* Retrieves the path to the global Node.js modules directory.
|
|
956
|
-
* @returns A promise that resolves to the path of the global Node.js modules directory.
|
|
957
|
-
*/
|
|
958
779
|
async getGlobalNodeModules() {
|
|
959
780
|
return new Promise((resolve, reject) => {
|
|
960
781
|
this.execRunningCount++;
|
|
@@ -969,17 +790,11 @@ export class Matterbridge extends EventEmitter {
|
|
|
969
790
|
});
|
|
970
791
|
});
|
|
971
792
|
}
|
|
972
|
-
/**
|
|
973
|
-
* Retrieves the latest version of Matterbridge and performs necessary actions based on the version comparison.
|
|
974
|
-
* @private
|
|
975
|
-
* @returns {Promise<void>} A promise that resolves when the latest version is retrieved and actions are performed.
|
|
976
|
-
*/
|
|
977
793
|
async getMatterbridgeLatestVersion() {
|
|
978
|
-
|
|
979
|
-
.then(async (
|
|
980
|
-
this.matterbridgeLatestVersion =
|
|
981
|
-
this.matterbridgeInformation.matterbridgeLatestVersion =
|
|
982
|
-
this.log.debug(`Matterbridge Latest Version: ${this.matterbridgeLatestVersion}`);
|
|
794
|
+
getNpmPackageVersion('matterbridge')
|
|
795
|
+
.then(async (version) => {
|
|
796
|
+
this.matterbridgeLatestVersion = version;
|
|
797
|
+
this.matterbridgeInformation.matterbridgeLatestVersion = version;
|
|
983
798
|
await this.nodeContext?.set('matterbridgeLatestVersion', this.matterbridgeLatestVersion);
|
|
984
799
|
if (this.matterbridgeVersion !== this.matterbridgeLatestVersion) {
|
|
985
800
|
this.log.notice(`Matterbridge is out of date. Current version: ${this.matterbridgeVersion}. Latest version: ${this.matterbridgeLatestVersion}.`);
|
|
@@ -987,81 +802,57 @@ export class Matterbridge extends EventEmitter {
|
|
|
987
802
|
else {
|
|
988
803
|
this.log.debug(`Matterbridge is up to date. Current version: ${this.matterbridgeVersion}. Latest version: ${this.matterbridgeLatestVersion}.`);
|
|
989
804
|
}
|
|
805
|
+
this.frontend.wssSendRefreshRequired();
|
|
990
806
|
})
|
|
991
807
|
.catch((error) => {
|
|
992
808
|
this.log.error(`Error getting Matterbridge latest version: ${error.message}`);
|
|
993
|
-
// error.stack && this.log.debug(error.stack);
|
|
994
809
|
});
|
|
995
810
|
}
|
|
996
|
-
/**
|
|
997
|
-
* Retrieves the latest version of a plugin and updates the plugin's latestVersion property.
|
|
998
|
-
* If the plugin's version is different from the latest version, logs a warning message.
|
|
999
|
-
* If the plugin's version is the same as the latest version, logs an info message.
|
|
1000
|
-
* If there is an error retrieving the latest version, logs an error message.
|
|
1001
|
-
*
|
|
1002
|
-
* @private
|
|
1003
|
-
* @param {RegisteredPlugin} plugin - The plugin for which to retrieve the latest version.
|
|
1004
|
-
* @returns {Promise<void>} A promise that resolves when the latest version is retrieved and actions are performed.
|
|
1005
|
-
*/
|
|
1006
811
|
async getPluginLatestVersion(plugin) {
|
|
1007
|
-
|
|
1008
|
-
.then(
|
|
1009
|
-
plugin.latestVersion =
|
|
1010
|
-
if (plugin.version !== latestVersion)
|
|
1011
|
-
this.log.notice(`The plugin ${plg}${plugin.name}${nt} is out of date. Current version: ${plugin.version}. Latest version: ${latestVersion}.`);
|
|
812
|
+
getNpmPackageVersion(plugin.name)
|
|
813
|
+
.then((version) => {
|
|
814
|
+
plugin.latestVersion = version;
|
|
815
|
+
if (plugin.version !== plugin.latestVersion)
|
|
816
|
+
this.log.notice(`The plugin ${plg}${plugin.name}${nt} is out of date. Current version: ${plugin.version}. Latest version: ${plugin.latestVersion}.`);
|
|
1012
817
|
else
|
|
1013
|
-
this.log.debug(`The plugin ${plg}${plugin.name}${db} is up to date. Current version: ${plugin.version}. Latest version: ${latestVersion}.`);
|
|
818
|
+
this.log.debug(`The plugin ${plg}${plugin.name}${db} is up to date. Current version: ${plugin.version}. Latest version: ${plugin.latestVersion}.`);
|
|
1014
819
|
})
|
|
1015
820
|
.catch((error) => {
|
|
1016
821
|
this.log.error(`Error getting ${plg}${plugin.name}${er} latest version: ${error.message}`);
|
|
1017
|
-
// error.stack && this.log.debug(error.stack);
|
|
1018
822
|
});
|
|
1019
823
|
}
|
|
1020
|
-
/**
|
|
1021
|
-
* Creates a MatterLogger function to show the matter.js log messages in AnsiLogger (for the frontend).
|
|
1022
|
-
*
|
|
1023
|
-
* @returns {Function} The MatterLogger function.
|
|
1024
|
-
*/
|
|
1025
824
|
createMatterLogger() {
|
|
1026
|
-
const matterLogger = new AnsiLogger({ logName: 'Matter', logTimestampFormat: 4
|
|
825
|
+
const matterLogger = new AnsiLogger({ logName: 'Matter', logTimestampFormat: 4, logLevel: "debug" });
|
|
1027
826
|
return (_level, formattedLog) => {
|
|
1028
827
|
const logger = formattedLog.slice(44, 44 + 20).trim();
|
|
1029
828
|
const message = formattedLog.slice(65);
|
|
1030
829
|
matterLogger.logName = logger;
|
|
1031
830
|
switch (_level) {
|
|
1032
831
|
case MatterLogLevel.DEBUG:
|
|
1033
|
-
matterLogger.log("debug"
|
|
832
|
+
matterLogger.log("debug", message);
|
|
1034
833
|
break;
|
|
1035
834
|
case MatterLogLevel.INFO:
|
|
1036
|
-
matterLogger.log("info"
|
|
835
|
+
matterLogger.log("info", message);
|
|
1037
836
|
break;
|
|
1038
837
|
case MatterLogLevel.NOTICE:
|
|
1039
|
-
matterLogger.log("notice"
|
|
838
|
+
matterLogger.log("notice", message);
|
|
1040
839
|
break;
|
|
1041
840
|
case MatterLogLevel.WARN:
|
|
1042
|
-
matterLogger.log("warn"
|
|
841
|
+
matterLogger.log("warn", message);
|
|
1043
842
|
break;
|
|
1044
843
|
case MatterLogLevel.ERROR:
|
|
1045
|
-
matterLogger.log("error"
|
|
844
|
+
matterLogger.log("error", message);
|
|
1046
845
|
break;
|
|
1047
846
|
case MatterLogLevel.FATAL:
|
|
1048
|
-
matterLogger.log("fatal"
|
|
847
|
+
matterLogger.log("fatal", message);
|
|
1049
848
|
break;
|
|
1050
849
|
default:
|
|
1051
|
-
matterLogger.log("debug"
|
|
850
|
+
matterLogger.log("debug", message);
|
|
1052
851
|
break;
|
|
1053
852
|
}
|
|
1054
853
|
};
|
|
1055
854
|
}
|
|
1056
|
-
/**
|
|
1057
|
-
* Creates a Matter File Logger.
|
|
1058
|
-
*
|
|
1059
|
-
* @param {string} filePath - The path to the log file.
|
|
1060
|
-
* @param {boolean} [unlink=false] - Whether to unlink the log file before creating a new one.
|
|
1061
|
-
* @returns {Function} - A function that logs formatted messages to the log file.
|
|
1062
|
-
*/
|
|
1063
855
|
async createMatterFileLogger(filePath, unlink = false) {
|
|
1064
|
-
// 2024-08-21 08:55:19.488 DEBUG InteractionMessenger Sending DataReport chunk with 28 attributes and 0 events: 1004 bytes
|
|
1065
856
|
let fileSize = 0;
|
|
1066
857
|
if (unlink) {
|
|
1067
858
|
try {
|
|
@@ -1110,21 +901,12 @@ export class Matterbridge extends EventEmitter {
|
|
|
1110
901
|
}
|
|
1111
902
|
};
|
|
1112
903
|
}
|
|
1113
|
-
/**
|
|
1114
|
-
* Restarts the process by exiting the current instance and loading a new instance.
|
|
1115
|
-
*/
|
|
1116
904
|
async restartProcess() {
|
|
1117
905
|
await this.cleanup('restarting...', true);
|
|
1118
906
|
}
|
|
1119
|
-
/**
|
|
1120
|
-
* Shut down the process by exiting the current process.
|
|
1121
|
-
*/
|
|
1122
907
|
async shutdownProcess() {
|
|
1123
908
|
await this.cleanup('shutting down...', false);
|
|
1124
909
|
}
|
|
1125
|
-
/**
|
|
1126
|
-
* Update matterbridge and and shut down the process.
|
|
1127
|
-
*/
|
|
1128
910
|
async updateProcess() {
|
|
1129
911
|
this.log.info('Updating matterbridge...');
|
|
1130
912
|
try {
|
|
@@ -1137,9 +919,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1137
919
|
this.frontend.wssSendRestartRequired();
|
|
1138
920
|
await this.cleanup('updating...', false);
|
|
1139
921
|
}
|
|
1140
|
-
/**
|
|
1141
|
-
* Unregister all devices and shut down the process.
|
|
1142
|
-
*/
|
|
1143
922
|
async unregisterAndShutdownProcess() {
|
|
1144
923
|
this.log.info('Unregistering all devices and shutting down...');
|
|
1145
924
|
for (const plugin of this.plugins) {
|
|
@@ -1147,9 +926,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1147
926
|
}
|
|
1148
927
|
await this.cleanup('unregistered all devices and shutting down...', false);
|
|
1149
928
|
}
|
|
1150
|
-
/**
|
|
1151
|
-
* Reset commissioning and shut down the process.
|
|
1152
|
-
*/
|
|
1153
929
|
async shutdownProcessAndReset() {
|
|
1154
930
|
this.log.info('Resetting Matterbridge commissioning information...');
|
|
1155
931
|
await this.matterStorageManager?.createContext('events')?.clearAll();
|
|
@@ -1161,12 +937,8 @@ export class Matterbridge extends EventEmitter {
|
|
|
1161
937
|
this.log.info('Matter storage reset done! Remove the bridge from the controller.');
|
|
1162
938
|
await this.cleanup('shutting down with reset...', false);
|
|
1163
939
|
}
|
|
1164
|
-
/**
|
|
1165
|
-
* Factory reset and shut down the process.
|
|
1166
|
-
*/
|
|
1167
940
|
async shutdownProcessAndFactoryReset() {
|
|
1168
941
|
try {
|
|
1169
|
-
// Delete old matter storage file and backup
|
|
1170
942
|
const file = path.join(this.matterbridgeDirectory, 'matterbridge' + (getParameter('profile') ? '.' + getParameter('profile') : '') + '.json');
|
|
1171
943
|
this.log.info(`Unlinking old matter storage file: ${file}`);
|
|
1172
944
|
await fs.unlink(file);
|
|
@@ -1180,7 +952,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1180
952
|
}
|
|
1181
953
|
}
|
|
1182
954
|
try {
|
|
1183
|
-
// Delete matter node storage directory with its subdirectories and backup
|
|
1184
955
|
const dir = path.join(this.matterbridgeDirectory, 'matterstorage' + (getParameter('profile') ? '.' + getParameter('profile') : ''));
|
|
1185
956
|
this.log.info(`Removing matter node storage directory: ${dir}`);
|
|
1186
957
|
await fs.rm(dir, { recursive: true });
|
|
@@ -1194,7 +965,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1194
965
|
}
|
|
1195
966
|
}
|
|
1196
967
|
try {
|
|
1197
|
-
// Delete node storage directory with its subdirectories and backup
|
|
1198
968
|
const dir = path.join(this.matterbridgeDirectory, 'storage' + (getParameter('profile') ? '.' + getParameter('profile') : ''));
|
|
1199
969
|
this.log.info(`Removing storage directory: ${dir}`);
|
|
1200
970
|
await fs.rm(dir, { recursive: true });
|
|
@@ -1214,41 +984,30 @@ export class Matterbridge extends EventEmitter {
|
|
|
1214
984
|
this.devices.clear();
|
|
1215
985
|
await this.cleanup('shutting down with factory reset...', false);
|
|
1216
986
|
}
|
|
1217
|
-
/**
|
|
1218
|
-
* Cleans up the Matterbridge instance.
|
|
1219
|
-
* @param message - The cleanup message.
|
|
1220
|
-
* @param restart - Indicates whether to restart the instance after cleanup. Default is `false`.
|
|
1221
|
-
* @returns A promise that resolves when the cleanup is completed.
|
|
1222
|
-
*/
|
|
1223
987
|
async cleanup(message, restart = false) {
|
|
1224
988
|
if (this.initialized && !this.hasCleanupStarted) {
|
|
1225
989
|
this.hasCleanupStarted = true;
|
|
1226
990
|
this.log.info(message);
|
|
1227
|
-
// Clear the start matter interval
|
|
1228
991
|
if (this.startMatterInterval) {
|
|
1229
992
|
clearInterval(this.startMatterInterval);
|
|
1230
993
|
this.startMatterInterval = undefined;
|
|
1231
994
|
this.log.debug('Start matter interval cleared');
|
|
1232
995
|
}
|
|
1233
|
-
// Clear the check update interval
|
|
1234
996
|
if (this.checkUpdateInterval) {
|
|
1235
997
|
clearInterval(this.checkUpdateInterval);
|
|
1236
998
|
this.checkUpdateInterval = undefined;
|
|
1237
999
|
this.log.debug('Check update interval cleared');
|
|
1238
1000
|
}
|
|
1239
|
-
// Clear the configure timeout
|
|
1240
1001
|
if (this.configureTimeout) {
|
|
1241
1002
|
clearTimeout(this.configureTimeout);
|
|
1242
1003
|
this.configureTimeout = undefined;
|
|
1243
1004
|
this.log.debug('Matterbridge configure timeout cleared');
|
|
1244
1005
|
}
|
|
1245
|
-
// Clear the reachability timeout
|
|
1246
1006
|
if (this.reachabilityTimeout) {
|
|
1247
1007
|
clearTimeout(this.reachabilityTimeout);
|
|
1248
1008
|
this.reachabilityTimeout = undefined;
|
|
1249
1009
|
this.log.debug('Matterbridge reachability timeout cleared');
|
|
1250
1010
|
}
|
|
1251
|
-
// Calling the shutdown method of each plugin and clear the plugins reachability timeout
|
|
1252
1011
|
for (const plugin of this.plugins) {
|
|
1253
1012
|
if (!plugin.enabled || plugin.error)
|
|
1254
1013
|
continue;
|
|
@@ -1259,7 +1018,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1259
1018
|
this.log.debug(`Plugin ${plg}${plugin.name}${db} reachability timeout cleared`);
|
|
1260
1019
|
}
|
|
1261
1020
|
}
|
|
1262
|
-
// Stopping matter server nodes
|
|
1263
1021
|
this.log.notice(`Stopping matter server nodes in ${this.bridgeMode} mode...`);
|
|
1264
1022
|
if (this.bridgeMode === 'bridge') {
|
|
1265
1023
|
if (this.serverNode) {
|
|
@@ -1276,37 +1034,17 @@ export class Matterbridge extends EventEmitter {
|
|
|
1276
1034
|
}
|
|
1277
1035
|
}
|
|
1278
1036
|
this.log.notice('Stopped matter server nodes');
|
|
1279
|
-
// Stop matter storage
|
|
1280
1037
|
await this.stopMatterStorage();
|
|
1281
|
-
// Stop the frontend
|
|
1282
1038
|
await this.frontend.stop();
|
|
1283
|
-
// Remove the matterfilelogger
|
|
1284
1039
|
try {
|
|
1285
1040
|
Logger.removeLogger('matterfilelogger');
|
|
1286
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
1287
1041
|
}
|
|
1288
1042
|
catch (error) {
|
|
1289
|
-
// this.log.debug(`Error removing the matterfilelogger for file ${CYAN}${path.join(this.matterbridgeDirectory, this.matterLoggerFile)}${er}: ${error instanceof Error ? error.message : error}`);
|
|
1290
1043
|
}
|
|
1291
|
-
// Serialize registeredDevices
|
|
1292
1044
|
if (this.nodeStorage && this.nodeContext) {
|
|
1293
|
-
/*
|
|
1294
|
-
TODO: Implement serialization of registered devices in edge mode
|
|
1295
|
-
this.log.info('Saving registered devices...');
|
|
1296
|
-
const serializedRegisteredDevices: SerializedMatterbridgeEndpoint[] = [];
|
|
1297
|
-
this.devices.forEach(async (device) => {
|
|
1298
|
-
const serializedMatterbridgeDevice = MatterbridgeEndpoint.serialize(device);
|
|
1299
|
-
// this.log.info(`- ${serializedMatterbridgeDevice.deviceName}${rs}\n`, serializedMatterbridgeDevice);
|
|
1300
|
-
if (serializedMatterbridgeDevice) serializedRegisteredDevices.push(serializedMatterbridgeDevice);
|
|
1301
|
-
});
|
|
1302
|
-
await this.nodeContext.set<SerializedMatterbridgeEndpoint[]>('devices', serializedRegisteredDevices);
|
|
1303
|
-
this.log.info(`Saved registered devices (${serializedRegisteredDevices?.length})`);
|
|
1304
|
-
*/
|
|
1305
|
-
// Clear nodeContext and nodeStorage (they just need 1000ms to write the data to disk)
|
|
1306
1045
|
this.log.debug(`Closing node storage context for ${plg}Matterbridge${db}...`);
|
|
1307
1046
|
await this.nodeContext.close();
|
|
1308
1047
|
this.nodeContext = undefined;
|
|
1309
|
-
// Clear nodeContext for each plugin (they just need 1000ms to write the data to disk)
|
|
1310
1048
|
for (const plugin of this.plugins) {
|
|
1311
1049
|
if (plugin.nodeContext) {
|
|
1312
1050
|
this.log.debug(`Closing node storage context for plugin ${plg}${plugin.name}${db}...`);
|
|
@@ -1323,13 +1061,12 @@ export class Matterbridge extends EventEmitter {
|
|
|
1323
1061
|
}
|
|
1324
1062
|
this.plugins.clear();
|
|
1325
1063
|
this.devices.clear();
|
|
1326
|
-
// Deregisters the process handlers
|
|
1327
1064
|
this.deregisterProcesslHandlers();
|
|
1328
1065
|
if (restart) {
|
|
1329
1066
|
if (message === 'updating...') {
|
|
1330
1067
|
this.log.info('Cleanup completed. Updating...');
|
|
1331
1068
|
Matterbridge.instance = undefined;
|
|
1332
|
-
this.emit('update');
|
|
1069
|
+
this.emit('update');
|
|
1333
1070
|
}
|
|
1334
1071
|
else if (message === 'restarting...') {
|
|
1335
1072
|
this.log.info('Cleanup completed. Restarting...');
|
|
@@ -1349,14 +1086,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1349
1086
|
this.log.debug('Cleanup already started...');
|
|
1350
1087
|
}
|
|
1351
1088
|
}
|
|
1352
|
-
/**
|
|
1353
|
-
* Creates and configures the server node for an accessory plugin for a given device.
|
|
1354
|
-
*
|
|
1355
|
-
* @param {RegisteredPlugin} plugin - The plugin to configure.
|
|
1356
|
-
* @param {MatterbridgeEndpoint} device - The device to associate with the plugin.
|
|
1357
|
-
* @param {boolean} [start=false] - Whether to start the server node after adding the device.
|
|
1358
|
-
* @returns {Promise<void>} A promise that resolves when the server node for the accessory plugin is created and configured.
|
|
1359
|
-
*/
|
|
1360
1089
|
async createAccessoryPlugin(plugin, device, start = false) {
|
|
1361
1090
|
if (!plugin.locked && device.deviceName && device.vendorId && device.productId && device.vendorName && device.productName) {
|
|
1362
1091
|
plugin.locked = true;
|
|
@@ -1368,13 +1097,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1368
1097
|
await this.startServerNode(plugin.serverNode);
|
|
1369
1098
|
}
|
|
1370
1099
|
}
|
|
1371
|
-
/**
|
|
1372
|
-
* Creates and configures the server node for a dynamic plugin.
|
|
1373
|
-
*
|
|
1374
|
-
* @param {RegisteredPlugin} plugin - The plugin to configure.
|
|
1375
|
-
* @param {boolean} [start=false] - Whether to start the server node after adding the aggregator node.
|
|
1376
|
-
* @returns {Promise<void>} A promise that resolves when the server node for the dynamic plugin is created and configured.
|
|
1377
|
-
*/
|
|
1378
1100
|
async createDynamicPlugin(plugin, start = false) {
|
|
1379
1101
|
if (!plugin.locked) {
|
|
1380
1102
|
plugin.locked = true;
|
|
@@ -1386,13 +1108,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
1386
1108
|
await this.startServerNode(plugin.serverNode);
|
|
1387
1109
|
}
|
|
1388
1110
|
}
|
|
1389
|
-
/**
|
|
1390
|
-
* Starts the Matterbridge in bridge mode.
|
|
1391
|
-
* @private
|
|
1392
|
-
* @returns {Promise<void>} A promise that resolves when the Matterbridge is started.
|
|
1393
|
-
*/
|
|
1394
1111
|
async startBridge() {
|
|
1395
|
-
// Plugins are configured by a timer when matter server is started and plugin.configured is set to true
|
|
1396
1112
|
if (!this.matterStorageManager)
|
|
1397
1113
|
throw new Error('No storage manager initialized');
|
|
1398
1114
|
if (!this.matterbridgeContext)
|
|
@@ -1429,9 +1145,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
1429
1145
|
clearInterval(this.startMatterInterval);
|
|
1430
1146
|
this.startMatterInterval = undefined;
|
|
1431
1147
|
this.log.debug('Cleared startMatterInterval interval for Matterbridge');
|
|
1432
|
-
// Start the Matter server node
|
|
1433
1148
|
this.startServerNode(this.serverNode);
|
|
1434
|
-
// Configure the plugins
|
|
1435
1149
|
this.configureTimeout = setTimeout(async () => {
|
|
1436
1150
|
for (const plugin of this.plugins) {
|
|
1437
1151
|
if (!plugin.enabled || !plugin.loaded || !plugin.started || plugin.error)
|
|
@@ -1446,7 +1160,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1446
1160
|
}
|
|
1447
1161
|
this.frontend.wssSendRefreshRequired();
|
|
1448
1162
|
}, 30 * 1000);
|
|
1449
|
-
// Setting reachability to true
|
|
1450
1163
|
this.reachabilityTimeout = setTimeout(() => {
|
|
1451
1164
|
this.log.info(`Setting reachability to true for ${plg}Matterbridge${db}`);
|
|
1452
1165
|
if (this.serverNode)
|
|
@@ -1457,14 +1170,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
1457
1170
|
}, 60 * 1000);
|
|
1458
1171
|
}, 1000);
|
|
1459
1172
|
}
|
|
1460
|
-
/**
|
|
1461
|
-
* Starts the Matterbridge in childbridge mode.
|
|
1462
|
-
* @private
|
|
1463
|
-
* @returns {Promise<void>} A promise that resolves when the Matterbridge is started.
|
|
1464
|
-
*/
|
|
1465
1173
|
async startChildbridge() {
|
|
1466
|
-
// Matterbridge.addBridgedDevice creates the commissionig servers and add the devices to the the commissioning server or to the aggregator
|
|
1467
|
-
// Plugins are configured by a timer when matter server is started and plugin.configured is set to true
|
|
1468
1174
|
if (!this.matterStorageManager)
|
|
1469
1175
|
throw new Error('No storage manager initialized');
|
|
1470
1176
|
for (const plugin of this.plugins) {
|
|
@@ -1511,13 +1217,12 @@ export class Matterbridge extends EventEmitter {
|
|
|
1511
1217
|
clearInterval(this.startMatterInterval);
|
|
1512
1218
|
this.startMatterInterval = undefined;
|
|
1513
1219
|
this.log.debug('Cleared startMatterInterval interval in childbridge mode');
|
|
1514
|
-
// Configure the plugins
|
|
1515
1220
|
this.configureTimeout = setTimeout(async () => {
|
|
1516
1221
|
for (const plugin of this.plugins) {
|
|
1517
1222
|
if (!plugin.enabled || !plugin.loaded || !plugin.started || plugin.error)
|
|
1518
1223
|
continue;
|
|
1519
1224
|
try {
|
|
1520
|
-
await this.plugins.configure(plugin);
|
|
1225
|
+
await this.plugins.configure(plugin);
|
|
1521
1226
|
}
|
|
1522
1227
|
catch (error) {
|
|
1523
1228
|
plugin.error = true;
|
|
@@ -1545,9 +1250,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
1545
1250
|
this.log.error(`Node storage context not found for plugin ${plg}${plugin.name}${er}`);
|
|
1546
1251
|
continue;
|
|
1547
1252
|
}
|
|
1548
|
-
// Start the Matter server node
|
|
1549
1253
|
this.startServerNode(plugin.serverNode);
|
|
1550
|
-
// Setting reachability to true
|
|
1551
1254
|
plugin.reachabilityTimeout = setTimeout(() => {
|
|
1552
1255
|
this.log.info(`Setting reachability to true for ${plg}${plugin.name}${db}`);
|
|
1553
1256
|
if (plugin.serverNode)
|
|
@@ -1561,219 +1264,9 @@ export class Matterbridge extends EventEmitter {
|
|
|
1561
1264
|
}
|
|
1562
1265
|
}, 1000);
|
|
1563
1266
|
}
|
|
1564
|
-
/**
|
|
1565
|
-
* Starts the Matterbridge controller.
|
|
1566
|
-
* @private
|
|
1567
|
-
* @returns {Promise<void>} A promise that resolves when the Matterbridge is started.
|
|
1568
|
-
*/
|
|
1569
1267
|
async startController() {
|
|
1570
|
-
/*
|
|
1571
|
-
if (!this.storageManager) {
|
|
1572
|
-
this.log.error('No storage manager initialized');
|
|
1573
|
-
await this.cleanup('No storage manager initialized');
|
|
1574
|
-
return;
|
|
1575
|
-
}
|
|
1576
|
-
this.log.info('Creating context: mattercontrollerContext');
|
|
1577
|
-
this.mattercontrollerContext = this.storageManager.createContext('mattercontrollerContext');
|
|
1578
|
-
if (!this.mattercontrollerContext) {
|
|
1579
|
-
this.log.error('No storage context mattercontrollerContext initialized');
|
|
1580
|
-
await this.cleanup('No storage context mattercontrollerContext initialized');
|
|
1581
|
-
return;
|
|
1582
|
-
}
|
|
1583
|
-
|
|
1584
|
-
this.log.debug('Starting matterbridge in mode', this.bridgeMode);
|
|
1585
|
-
this.matterServer = await this.createMatterServer(this.storageManager);
|
|
1586
|
-
this.log.info('Creating matter commissioning controller');
|
|
1587
|
-
this.commissioningController = new CommissioningController({
|
|
1588
|
-
autoConnect: false,
|
|
1589
|
-
});
|
|
1590
|
-
this.log.info('Adding matter commissioning controller to matter server');
|
|
1591
|
-
await this.matterServer.addCommissioningController(this.commissioningController);
|
|
1592
|
-
|
|
1593
|
-
this.log.info('Starting matter server');
|
|
1594
|
-
await this.matterServer.start();
|
|
1595
|
-
this.log.info('Matter server started');
|
|
1596
|
-
|
|
1597
|
-
if (hasParameter('pairingcode')) {
|
|
1598
|
-
this.log.info('Pairing device with pairingcode:', getParameter('pairingcode'));
|
|
1599
|
-
const pairingCode = getParameter('pairingcode');
|
|
1600
|
-
const ip = this.mattercontrollerContext.has('ip') ? this.mattercontrollerContext.get<string>('ip') : undefined;
|
|
1601
|
-
const port = this.mattercontrollerContext.has('port') ? this.mattercontrollerContext.get<number>('port') : undefined;
|
|
1602
|
-
|
|
1603
|
-
let longDiscriminator, setupPin, shortDiscriminator;
|
|
1604
|
-
if (pairingCode !== undefined) {
|
|
1605
|
-
const pairingCodeCodec = ManualPairingCodeCodec.decode(pairingCode);
|
|
1606
|
-
shortDiscriminator = pairingCodeCodec.shortDiscriminator;
|
|
1607
|
-
longDiscriminator = undefined;
|
|
1608
|
-
setupPin = pairingCodeCodec.passcode;
|
|
1609
|
-
this.log.info(`Data extracted from pairing code: ${Logger.toJSON(pairingCodeCodec)}`);
|
|
1610
|
-
} else {
|
|
1611
|
-
longDiscriminator = await this.mattercontrollerContext.get('longDiscriminator', 3840);
|
|
1612
|
-
if (longDiscriminator > 4095) throw new Error('Discriminator value must be less than 4096');
|
|
1613
|
-
setupPin = this.mattercontrollerContext.get('pin', 20202021);
|
|
1614
|
-
}
|
|
1615
|
-
if ((shortDiscriminator === undefined && longDiscriminator === undefined) || setupPin === undefined) {
|
|
1616
|
-
throw new Error('Please specify the longDiscriminator of the device to commission with -longDiscriminator or provide a valid passcode with -passcode');
|
|
1617
|
-
}
|
|
1618
|
-
|
|
1619
|
-
const commissioningOptions: ControllerCommissioningFlowOptions = {
|
|
1620
|
-
regulatoryLocation: GeneralCommissioning.RegulatoryLocationType.IndoorOutdoor,
|
|
1621
|
-
regulatoryCountryCode: 'XX',
|
|
1622
|
-
};
|
|
1623
|
-
const options = {
|
|
1624
|
-
commissioning: commissioningOptions,
|
|
1625
|
-
discovery: {
|
|
1626
|
-
knownAddress: ip !== undefined && port !== undefined ? { ip, port, type: 'udp' } : undefined,
|
|
1627
|
-
identifierData: longDiscriminator !== undefined ? { longDiscriminator } : shortDiscriminator !== undefined ? { shortDiscriminator } : {},
|
|
1628
|
-
},
|
|
1629
|
-
passcode: setupPin,
|
|
1630
|
-
} as NodeCommissioningOptions;
|
|
1631
|
-
this.log.info('Commissioning with options:', options);
|
|
1632
|
-
const nodeId = await this.commissioningController.commissionNode(options);
|
|
1633
|
-
this.log.info(`Commissioning successfully done with nodeId: ${nodeId}`);
|
|
1634
|
-
this.log.info('ActiveSessionInformation:', this.commissioningController.getActiveSessionInformation());
|
|
1635
|
-
} // (hasParameter('pairingcode'))
|
|
1636
|
-
|
|
1637
|
-
if (hasParameter('unpairall')) {
|
|
1638
|
-
this.log.info('***Commissioning controller unpairing all nodes...');
|
|
1639
|
-
const nodeIds = this.commissioningController.getCommissionedNodes();
|
|
1640
|
-
for (const nodeId of nodeIds) {
|
|
1641
|
-
this.log.info('***Commissioning controller unpairing node:', nodeId);
|
|
1642
|
-
await this.commissioningController.removeNode(nodeId);
|
|
1643
|
-
}
|
|
1644
|
-
return;
|
|
1645
|
-
}
|
|
1646
|
-
|
|
1647
|
-
if (hasParameter('discover')) {
|
|
1648
|
-
// const discover = await this.commissioningController.discoverCommissionableDevices({ productId: 0x8000, deviceType: 0xfff1 });
|
|
1649
|
-
// console.log(discover);
|
|
1650
|
-
}
|
|
1651
|
-
|
|
1652
|
-
if (!this.commissioningController.isCommissioned()) {
|
|
1653
|
-
this.log.info('***Commissioning controller is not commissioned: use matterbridge -controller -pairingcode [pairingcode] to commission a device');
|
|
1654
|
-
return;
|
|
1655
|
-
}
|
|
1656
|
-
|
|
1657
|
-
const nodeIds = this.commissioningController.getCommissionedNodes();
|
|
1658
|
-
this.log.info(`***Commissioning controller is commissioned ${this.commissioningController.isCommissioned()} and has ${nodeIds.length} nodes commisioned: `);
|
|
1659
|
-
for (const nodeId of nodeIds) {
|
|
1660
|
-
this.log.info(`***Connecting to commissioned node: ${nodeId}`);
|
|
1661
|
-
|
|
1662
|
-
const node = await this.commissioningController.connectNode(nodeId, {
|
|
1663
|
-
autoSubscribe: false,
|
|
1664
|
-
attributeChangedCallback: (peerNodeId, { path: { nodeId, clusterId, endpointId, attributeName }, value }) =>
|
|
1665
|
-
this.log.info(`***Commissioning controller attributeChangedCallback ${peerNodeId}: attribute ${nodeId}/${endpointId}/${clusterId}/${attributeName} changed to ${Logger.toJSON(value)}`),
|
|
1666
|
-
eventTriggeredCallback: (peerNodeId, { path: { nodeId, clusterId, endpointId, eventName }, events }) =>
|
|
1667
|
-
this.log.info(`***Commissioning controller eventTriggeredCallback ${peerNodeId}: Event ${nodeId}/${endpointId}/${clusterId}/${eventName} triggered with ${Logger.toJSON(events)}`),
|
|
1668
|
-
stateInformationCallback: (peerNodeId, info) => {
|
|
1669
|
-
switch (info) {
|
|
1670
|
-
case NodeStateInformation.Connected:
|
|
1671
|
-
this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} connected`);
|
|
1672
|
-
break;
|
|
1673
|
-
case NodeStateInformation.Disconnected:
|
|
1674
|
-
this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} disconnected`);
|
|
1675
|
-
break;
|
|
1676
|
-
case NodeStateInformation.Reconnecting:
|
|
1677
|
-
this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} reconnecting`);
|
|
1678
|
-
break;
|
|
1679
|
-
case NodeStateInformation.WaitingForDeviceDiscovery:
|
|
1680
|
-
this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} waiting for device discovery`);
|
|
1681
|
-
break;
|
|
1682
|
-
case NodeStateInformation.StructureChanged:
|
|
1683
|
-
this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} structure changed`);
|
|
1684
|
-
break;
|
|
1685
|
-
case NodeStateInformation.Decommissioned:
|
|
1686
|
-
this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} decommissioned`);
|
|
1687
|
-
break;
|
|
1688
|
-
default:
|
|
1689
|
-
this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} NodeStateInformation.${info}`);
|
|
1690
|
-
break;
|
|
1691
|
-
}
|
|
1692
|
-
},
|
|
1693
|
-
});
|
|
1694
|
-
|
|
1695
|
-
node.logStructure();
|
|
1696
|
-
|
|
1697
|
-
// Get the interaction client
|
|
1698
|
-
this.log.info('Getting the interaction client');
|
|
1699
|
-
const interactionClient = await node.getInteractionClient();
|
|
1700
|
-
let cluster;
|
|
1701
|
-
let attributes;
|
|
1702
|
-
|
|
1703
|
-
// Log BasicInformationCluster
|
|
1704
|
-
cluster = BasicInformationCluster;
|
|
1705
|
-
attributes = await interactionClient.getMultipleAttributes({
|
|
1706
|
-
attributes: [{ clusterId: cluster.id }],
|
|
1707
|
-
});
|
|
1708
|
-
if (attributes.length > 0) this.log.info(`Cluster: ${idn}${cluster.name}${rs}${nf} attributes:`);
|
|
1709
|
-
attributes.forEach((attribute) => {
|
|
1710
|
-
this.log.info(
|
|
1711
|
-
`- 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}`,
|
|
1712
|
-
);
|
|
1713
|
-
});
|
|
1714
|
-
|
|
1715
|
-
// Log PowerSourceCluster
|
|
1716
|
-
cluster = PowerSourceCluster;
|
|
1717
|
-
attributes = await interactionClient.getMultipleAttributes({
|
|
1718
|
-
attributes: [{ clusterId: cluster.id }],
|
|
1719
|
-
});
|
|
1720
|
-
if (attributes.length > 0) this.log.info(`Cluster: ${idn}${cluster.name}${rs}${nf} attributes:`);
|
|
1721
|
-
attributes.forEach((attribute) => {
|
|
1722
|
-
this.log.info(
|
|
1723
|
-
`- 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}`,
|
|
1724
|
-
);
|
|
1725
|
-
});
|
|
1726
|
-
|
|
1727
|
-
// Log ThreadNetworkDiagnostics
|
|
1728
|
-
cluster = ThreadNetworkDiagnosticsCluster;
|
|
1729
|
-
attributes = await interactionClient.getMultipleAttributes({
|
|
1730
|
-
attributes: [{ clusterId: cluster.id }],
|
|
1731
|
-
});
|
|
1732
|
-
if (attributes.length > 0) this.log.info(`Cluster: ${idn}${cluster.name}${rs}${nf} attributes:`);
|
|
1733
|
-
attributes.forEach((attribute) => {
|
|
1734
|
-
this.log.info(
|
|
1735
|
-
`- 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}`,
|
|
1736
|
-
);
|
|
1737
|
-
});
|
|
1738
|
-
|
|
1739
|
-
// Log SwitchCluster
|
|
1740
|
-
cluster = SwitchCluster;
|
|
1741
|
-
attributes = await interactionClient.getMultipleAttributes({
|
|
1742
|
-
attributes: [{ clusterId: cluster.id }],
|
|
1743
|
-
});
|
|
1744
|
-
if (attributes.length > 0) this.log.info(`Cluster: ${idn}${cluster.name}${rs}${nf} attributes:`);
|
|
1745
|
-
attributes.forEach((attribute) => {
|
|
1746
|
-
this.log.info(
|
|
1747
|
-
`- 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}`,
|
|
1748
|
-
);
|
|
1749
|
-
});
|
|
1750
|
-
|
|
1751
|
-
this.log.info('Subscribing to all attributes and events');
|
|
1752
|
-
await node.subscribeAllAttributesAndEvents({
|
|
1753
|
-
ignoreInitialTriggers: false,
|
|
1754
|
-
attributeChangedCallback: ({ path: { nodeId, clusterId, endpointId, attributeName }, version, value }) =>
|
|
1755
|
-
this.log.info(
|
|
1756
|
-
`***${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}`,
|
|
1757
|
-
),
|
|
1758
|
-
eventTriggeredCallback: ({ path: { nodeId, clusterId, endpointId, eventName }, events }) => {
|
|
1759
|
-
this.log.info(
|
|
1760
|
-
`***${db}Commissioning controller eventTriggeredCallback: event ${BLUE}${nodeId}${db}/${or}${endpointId}${db}/${hk}${getClusterNameById(clusterId)}${db}/${zb}${eventName}${db} triggered with ${debugStringify(events ?? { none: true })}`,
|
|
1761
|
-
);
|
|
1762
|
-
},
|
|
1763
|
-
});
|
|
1764
|
-
this.log.info('Subscribed to all attributes and events');
|
|
1765
|
-
}
|
|
1766
|
-
*/
|
|
1767
1268
|
}
|
|
1768
|
-
/** ***********************************************************************************************************************************/
|
|
1769
|
-
/** Matter.js methods */
|
|
1770
|
-
/** ***********************************************************************************************************************************/
|
|
1771
|
-
/**
|
|
1772
|
-
* Starts the matter storage process with name Matterbridge.
|
|
1773
|
-
* @returns {Promise<void>} - A promise that resolves when the storage process is started.
|
|
1774
|
-
*/
|
|
1775
1269
|
async startMatterStorage() {
|
|
1776
|
-
// Setup Matter storage
|
|
1777
1270
|
this.log.info(`Starting matter node storage...`);
|
|
1778
1271
|
this.matterStorageService = this.environment.get(StorageService);
|
|
1779
1272
|
this.log.info(`Matter node storage service created: ${this.matterStorageService.location}`);
|
|
@@ -1781,25 +1274,13 @@ export class Matterbridge extends EventEmitter {
|
|
|
1781
1274
|
this.log.info('Matter node storage manager "Matterbridge" created');
|
|
1782
1275
|
this.matterbridgeContext = await this.createServerNodeContext('Matterbridge', 'Matterbridge', bridge.code, this.aggregatorVendorId, 'Matterbridge', this.aggregatorProductId, 'Matterbridge aggregator');
|
|
1783
1276
|
this.log.info('Matter node storage started');
|
|
1784
|
-
// Backup matter storage since it is created/opened correctly
|
|
1785
1277
|
await this.backupMatterStorage(path.join(this.matterbridgeDirectory, this.matterStorageName), path.join(this.matterbridgeDirectory, this.matterStorageName + '.backup'));
|
|
1786
1278
|
}
|
|
1787
|
-
/**
|
|
1788
|
-
* Makes a backup copy of the specified matter storage directory.
|
|
1789
|
-
*
|
|
1790
|
-
* @param storageName - The name of the storage directory to be backed up.
|
|
1791
|
-
* @param backupName - The name of the backup directory to be created.
|
|
1792
|
-
* @returns {Promise<void>} A promise that resolves when the has been done.
|
|
1793
|
-
*/
|
|
1794
1279
|
async backupMatterStorage(storageName, backupName) {
|
|
1795
1280
|
this.log.info('Creating matter node storage backup...');
|
|
1796
1281
|
await copyDirectory(storageName, backupName);
|
|
1797
1282
|
this.log.info('Created matter node storage backup');
|
|
1798
1283
|
}
|
|
1799
|
-
/**
|
|
1800
|
-
* Stops the matter storage.
|
|
1801
|
-
* @returns {Promise<void>} A promise that resolves when the storage is stopped.
|
|
1802
|
-
*/
|
|
1803
1284
|
async stopMatterStorage() {
|
|
1804
1285
|
this.log.info('Closing matter node storage...');
|
|
1805
1286
|
this.matterStorageManager?.close();
|
|
@@ -1808,19 +1289,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1808
1289
|
this.matterbridgeContext = undefined;
|
|
1809
1290
|
this.log.info('Matter node storage closed');
|
|
1810
1291
|
}
|
|
1811
|
-
/**
|
|
1812
|
-
* Creates a server node storage context.
|
|
1813
|
-
*
|
|
1814
|
-
* @param {string} pluginName - The name of the plugin.
|
|
1815
|
-
* @param {string} deviceName - The name of the device.
|
|
1816
|
-
* @param {DeviceTypeId} deviceType - The device type of the device.
|
|
1817
|
-
* @param {number} vendorId - The vendor ID.
|
|
1818
|
-
* @param {string} vendorName - The vendor name.
|
|
1819
|
-
* @param {number} productId - The product ID.
|
|
1820
|
-
* @param {string} productName - The product name.
|
|
1821
|
-
* @param {string} [serialNumber] - The serial number of the device (optional).
|
|
1822
|
-
* @returns {Promise<StorageContext>} The storage context for the commissioning server.
|
|
1823
|
-
*/
|
|
1824
1292
|
async createServerNodeContext(pluginName, deviceName, deviceType, vendorId, vendorName, productId, productName, serialNumber) {
|
|
1825
1293
|
if (!this.matterStorageService)
|
|
1826
1294
|
throw new Error('No storage service initialized');
|
|
@@ -1853,15 +1321,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1853
1321
|
this.log.debug(`- hardwareVersion: ${await storageContext.get('hardwareVersion')} hardwareVersionString: ${await storageContext.get('hardwareVersionString')}`);
|
|
1854
1322
|
return storageContext;
|
|
1855
1323
|
}
|
|
1856
|
-
/**
|
|
1857
|
-
* Creates a server node.
|
|
1858
|
-
*
|
|
1859
|
-
* @param {StorageContext} storageContext - The storage context for the server node.
|
|
1860
|
-
* @param {number} [port=5540] - The port number for the server node. Defaults to 5540.
|
|
1861
|
-
* @param {number} [passcode=20242025] - The passcode for the server node. Defaults to 20242025.
|
|
1862
|
-
* @param {number} [discriminator=3850] - The discriminator for the server node. Defaults to 3850.
|
|
1863
|
-
* @returns {Promise<ServerNode<ServerNode.RootEndpoint>>} A promise that resolves to the created server node.
|
|
1864
|
-
*/
|
|
1865
1324
|
async createServerNode(storageContext, port = 5540, passcode = 20242025, discriminator = 3850) {
|
|
1866
1325
|
const storeId = await storageContext.get('storeId');
|
|
1867
1326
|
this.log.notice(`Creating server node for ${storeId} on port ${port} with passcode ${passcode} and discriminator ${discriminator}...`);
|
|
@@ -1871,33 +1330,21 @@ export class Matterbridge extends EventEmitter {
|
|
|
1871
1330
|
this.log.debug(`- uniqueId: ${await storageContext.get('uniqueId')}`);
|
|
1872
1331
|
this.log.debug(`- softwareVersion: ${await storageContext.get('softwareVersion')} softwareVersionString: ${await storageContext.get('softwareVersionString')}`);
|
|
1873
1332
|
this.log.debug(`- hardwareVersion: ${await storageContext.get('hardwareVersion')} hardwareVersionString: ${await storageContext.get('hardwareVersionString')}`);
|
|
1874
|
-
/**
|
|
1875
|
-
* Create a Matter ServerNode, which contains the Root Endpoint and all relevant data and configuration
|
|
1876
|
-
*/
|
|
1877
1333
|
const serverNode = await ServerNode.create({
|
|
1878
|
-
// Required: Give the Node a unique ID which is used to store the state of this node
|
|
1879
1334
|
id: storeId,
|
|
1880
|
-
// Provide Network relevant configuration like the port
|
|
1881
|
-
// Optional when operating only one device on a host, Default port is 5540
|
|
1882
1335
|
network: {
|
|
1883
1336
|
listeningAddressIpv4: this.ipv4address,
|
|
1884
1337
|
listeningAddressIpv6: this.ipv6address,
|
|
1885
1338
|
port,
|
|
1886
1339
|
},
|
|
1887
|
-
// Provide Commissioning relevant settings
|
|
1888
|
-
// Optional for development/testing purposes
|
|
1889
1340
|
commissioning: {
|
|
1890
1341
|
passcode,
|
|
1891
1342
|
discriminator,
|
|
1892
1343
|
},
|
|
1893
|
-
// Provide Node announcement settings
|
|
1894
|
-
// Optional: If Ommitted some development defaults are used
|
|
1895
1344
|
productDescription: {
|
|
1896
1345
|
name: await storageContext.get('deviceName'),
|
|
1897
1346
|
deviceType: DeviceTypeId(await storageContext.get('deviceType')),
|
|
1898
1347
|
},
|
|
1899
|
-
// Provide defaults for the BasicInformation cluster on the Root endpoint
|
|
1900
|
-
// Optional: If Omitted some development defaults are used
|
|
1901
1348
|
basicInformation: {
|
|
1902
1349
|
vendorId: VendorId(await storageContext.get('vendorId')),
|
|
1903
1350
|
vendorName: await storageContext.get('vendorName'),
|
|
@@ -1914,13 +1361,12 @@ export class Matterbridge extends EventEmitter {
|
|
|
1914
1361
|
},
|
|
1915
1362
|
});
|
|
1916
1363
|
const sanitizeFabrics = (fabrics, resetSessions = false) => {
|
|
1917
|
-
// New type of fabric information: Record<FabricIndex, ExposedFabricInformation>
|
|
1918
1364
|
const sanitizedFabrics = this.sanitizeFabricInformations(Array.from(Object.values(fabrics)));
|
|
1919
1365
|
this.log.info(`Fabrics: ${debugStringify(sanitizedFabrics)}`);
|
|
1920
1366
|
if (this.bridgeMode === 'bridge') {
|
|
1921
1367
|
this.matterbridgeFabricInformations = sanitizedFabrics;
|
|
1922
1368
|
if (resetSessions)
|
|
1923
|
-
this.matterbridgeSessionInformations = undefined;
|
|
1369
|
+
this.matterbridgeSessionInformations = undefined;
|
|
1924
1370
|
this.matterbridgePaired = true;
|
|
1925
1371
|
}
|
|
1926
1372
|
if (this.bridgeMode === 'childbridge') {
|
|
@@ -1928,19 +1374,13 @@ export class Matterbridge extends EventEmitter {
|
|
|
1928
1374
|
if (plugin) {
|
|
1929
1375
|
plugin.fabricInformations = sanitizedFabrics;
|
|
1930
1376
|
if (resetSessions)
|
|
1931
|
-
plugin.sessionInformations = undefined;
|
|
1377
|
+
plugin.sessionInformations = undefined;
|
|
1932
1378
|
plugin.paired = true;
|
|
1933
1379
|
}
|
|
1934
1380
|
}
|
|
1935
1381
|
};
|
|
1936
|
-
/**
|
|
1937
|
-
* This event is triggered when the device is initially commissioned successfully.
|
|
1938
|
-
* This means: It is added to the first fabric.
|
|
1939
|
-
*/
|
|
1940
1382
|
serverNode.lifecycle.commissioned.on(() => this.log.notice(`Server node for ${storeId} was initially commissioned successfully!`));
|
|
1941
|
-
/** This event is triggered when all fabrics are removed from the device, usually it also does a factory reset then. */
|
|
1942
1383
|
serverNode.lifecycle.decommissioned.on(() => this.log.notice(`Server node for ${storeId} was fully decommissioned successfully!`));
|
|
1943
|
-
/** This event is triggered when the device went online. This means that it is discoverable in the network. */
|
|
1944
1384
|
serverNode.lifecycle.online.on(async () => {
|
|
1945
1385
|
this.log.notice(`Server node for ${storeId} is online`);
|
|
1946
1386
|
if (!serverNode.lifecycle.isCommissioned) {
|
|
@@ -1986,7 +1426,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1986
1426
|
}
|
|
1987
1427
|
this.frontend.wssSendRefreshRequired();
|
|
1988
1428
|
});
|
|
1989
|
-
/** This event is triggered when the device went offline. it is not longer discoverable or connectable in the network. */
|
|
1990
1429
|
serverNode.lifecycle.offline.on(() => {
|
|
1991
1430
|
this.log.notice(`Server node for ${storeId} is offline`);
|
|
1992
1431
|
if (this.bridgeMode === 'bridge') {
|
|
@@ -2008,10 +1447,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2008
1447
|
}
|
|
2009
1448
|
this.frontend.wssSendRefreshRequired();
|
|
2010
1449
|
});
|
|
2011
|
-
/**
|
|
2012
|
-
* This event is triggered when a fabric is added, removed or updated on the device. Use this if more granular
|
|
2013
|
-
* information is needed.
|
|
2014
|
-
*/
|
|
2015
1450
|
serverNode.events.commissioning.fabricsChanged.on((fabricIndex, fabricAction) => {
|
|
2016
1451
|
let action = '';
|
|
2017
1452
|
switch (fabricAction) {
|
|
@@ -2045,24 +1480,16 @@ export class Matterbridge extends EventEmitter {
|
|
|
2045
1480
|
}
|
|
2046
1481
|
}
|
|
2047
1482
|
};
|
|
2048
|
-
/**
|
|
2049
|
-
* This event is triggered when an operative new session was opened by a Controller.
|
|
2050
|
-
* It is not triggered for the initial commissioning process, just afterwards for real connections.
|
|
2051
|
-
*/
|
|
2052
1483
|
serverNode.events.sessions.opened.on((session) => {
|
|
2053
1484
|
this.log.notice(`Session opened on server node for ${storeId}: ${debugStringify(session)}`);
|
|
2054
1485
|
sanitizeSessions(Object.values(serverNode.state.sessions.sessions));
|
|
2055
1486
|
this.frontend.wssSendRefreshRequired();
|
|
2056
1487
|
});
|
|
2057
|
-
/**
|
|
2058
|
-
* This event is triggered when an operative session is closed by a Controller or because the Device goes offline.
|
|
2059
|
-
*/
|
|
2060
1488
|
serverNode.events.sessions.closed.on((session) => {
|
|
2061
1489
|
this.log.notice(`Session closed on server node for ${storeId}: ${debugStringify(session)}`);
|
|
2062
1490
|
sanitizeSessions(Object.values(serverNode.state.sessions.sessions));
|
|
2063
1491
|
this.frontend.wssSendRefreshRequired();
|
|
2064
1492
|
});
|
|
2065
|
-
/** This event is triggered when a subscription gets added or removed on an operative session. */
|
|
2066
1493
|
serverNode.events.sessions.subscriptionsChanged.on((session) => {
|
|
2067
1494
|
this.log.notice(`Session subscriptions changed on server node for ${storeId}: ${debugStringify(session)}`);
|
|
2068
1495
|
sanitizeSessions(Object.values(serverNode.state.sessions.sessions));
|
|
@@ -2071,61 +1498,38 @@ export class Matterbridge extends EventEmitter {
|
|
|
2071
1498
|
this.log.info(`Created server node for ${storeId}`);
|
|
2072
1499
|
return serverNode;
|
|
2073
1500
|
}
|
|
2074
|
-
/**
|
|
2075
|
-
* Starts the specified server node.
|
|
2076
|
-
*
|
|
2077
|
-
* @param {ServerNode} [matterServerNode] - The server node to start.
|
|
2078
|
-
* @returns {Promise<void>} A promise that resolves when the server node has started.
|
|
2079
|
-
*/
|
|
2080
1501
|
async startServerNode(matterServerNode) {
|
|
2081
1502
|
if (!matterServerNode)
|
|
2082
1503
|
return;
|
|
2083
1504
|
this.log.notice(`Starting ${matterServerNode.id} server node`);
|
|
2084
1505
|
await matterServerNode.start();
|
|
2085
1506
|
}
|
|
2086
|
-
/**
|
|
2087
|
-
* Stops the specified server node.
|
|
2088
|
-
*
|
|
2089
|
-
* @param {ServerNode} matterServerNode - The server node to stop.
|
|
2090
|
-
* @returns {Promise<void>} A promise that resolves when the server node has stopped.
|
|
2091
|
-
*/
|
|
2092
1507
|
async stopServerNode(matterServerNode) {
|
|
2093
1508
|
if (!matterServerNode)
|
|
2094
1509
|
return;
|
|
2095
1510
|
this.log.notice(`Closing ${matterServerNode.id} server node`);
|
|
2096
|
-
/*
|
|
2097
|
-
await matterServerNode.close();
|
|
2098
|
-
this.log.info(`Closed ${matterServerNode.id} server node`);
|
|
2099
|
-
*/
|
|
2100
|
-
// Helper function to add a timeout to a promise
|
|
2101
1511
|
const withTimeout = (promise, ms) => {
|
|
2102
1512
|
return new Promise((resolve, reject) => {
|
|
2103
1513
|
const timer = setTimeout(() => reject(new Error('Operation timed out')), ms);
|
|
2104
1514
|
promise
|
|
2105
1515
|
.then((result) => {
|
|
2106
|
-
clearTimeout(timer);
|
|
1516
|
+
clearTimeout(timer);
|
|
2107
1517
|
resolve(result);
|
|
2108
1518
|
})
|
|
2109
1519
|
.catch((error) => {
|
|
2110
|
-
clearTimeout(timer);
|
|
1520
|
+
clearTimeout(timer);
|
|
2111
1521
|
reject(error);
|
|
2112
1522
|
});
|
|
2113
1523
|
});
|
|
2114
1524
|
};
|
|
2115
1525
|
try {
|
|
2116
|
-
await withTimeout(matterServerNode.close(), 5000);
|
|
1526
|
+
await withTimeout(matterServerNode.close(), 5000);
|
|
2117
1527
|
this.log.info(`Closed ${matterServerNode.id} server node`);
|
|
2118
1528
|
}
|
|
2119
1529
|
catch (error) {
|
|
2120
1530
|
this.log.error(`Failed to close ${matterServerNode.id} server node: ${error instanceof Error ? error.message : error}`);
|
|
2121
1531
|
}
|
|
2122
1532
|
}
|
|
2123
|
-
/**
|
|
2124
|
-
* Advertises the specified server node if it is commissioned.
|
|
2125
|
-
*
|
|
2126
|
-
* @param {ServerNode} [matterServerNode] - The server node to advertise.
|
|
2127
|
-
* @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.
|
|
2128
|
-
*/
|
|
2129
1533
|
async advertiseServerNode(matterServerNode) {
|
|
2130
1534
|
if (matterServerNode && matterServerNode.lifecycle.isCommissioned) {
|
|
2131
1535
|
await matterServerNode.env.get(DeviceCommissioner)?.allowBasicCommissioning();
|
|
@@ -2135,32 +1539,17 @@ export class Matterbridge extends EventEmitter {
|
|
|
2135
1539
|
}
|
|
2136
1540
|
return undefined;
|
|
2137
1541
|
}
|
|
2138
|
-
/**
|
|
2139
|
-
* Creates an aggregator node with the specified storage context.
|
|
2140
|
-
*
|
|
2141
|
-
* @param {StorageContext} storageContext - The storage context for the aggregator node.
|
|
2142
|
-
* @returns {Promise<EndpointNode<AggregatorEndpoint>>} A promise that resolves to the created aggregator node.
|
|
2143
|
-
*/
|
|
2144
1542
|
async createAggregatorNode(storageContext) {
|
|
2145
1543
|
this.log.notice(`Creating ${await storageContext.get('storeId')} aggregator `);
|
|
2146
1544
|
const aggregatorNode = new EndpointNode(AggregatorEndpoint, { id: `${await storageContext.get('storeId')}` });
|
|
2147
1545
|
return aggregatorNode;
|
|
2148
1546
|
}
|
|
2149
|
-
/**
|
|
2150
|
-
* Adds a MatterbridgeEndpoint to the specified plugin.
|
|
2151
|
-
*
|
|
2152
|
-
* @param {string} pluginName - The name of the plugin.
|
|
2153
|
-
* @param {MatterbridgeEndpoint} device - The device to add as a bridged endpoint.
|
|
2154
|
-
* @returns {Promise<void>} A promise that resolves when the bridged endpoint has been added.
|
|
2155
|
-
*/
|
|
2156
1547
|
async addBridgedEndpoint(pluginName, device) {
|
|
2157
|
-
// Check if the plugin is registered
|
|
2158
1548
|
const plugin = this.plugins.get(pluginName);
|
|
2159
1549
|
if (!plugin) {
|
|
2160
1550
|
this.log.error(`Error adding bridged endpoint ${dev}${device.deviceName}${er} (${zb}${device.id}${er}) plugin ${plg}${pluginName}${er} not found`);
|
|
2161
1551
|
return;
|
|
2162
1552
|
}
|
|
2163
|
-
// Register and add the device to the matterbridge aggregator node
|
|
2164
1553
|
if (this.bridgeMode === 'bridge') {
|
|
2165
1554
|
this.log.debug(`Adding bridged endpoint ${plg}${pluginName}${db}:${dev}${device.deviceName}${db} to Matterbridge aggregator node`);
|
|
2166
1555
|
if (!this.aggregatorNode)
|
|
@@ -2183,26 +1572,16 @@ export class Matterbridge extends EventEmitter {
|
|
|
2183
1572
|
plugin.registeredDevices++;
|
|
2184
1573
|
if (plugin.addedDevices !== undefined)
|
|
2185
1574
|
plugin.addedDevices++;
|
|
2186
|
-
// Add the device to the DeviceManager
|
|
2187
1575
|
this.devices.set(device);
|
|
2188
1576
|
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}`);
|
|
2189
1577
|
}
|
|
2190
|
-
/**
|
|
2191
|
-
* Removes a MatterbridgeEndpoint from the specified plugin.
|
|
2192
|
-
*
|
|
2193
|
-
* @param {string} pluginName - The name of the plugin.
|
|
2194
|
-
* @param {MatterbridgeEndpoint} device - The device to remove as a bridged endpoint.
|
|
2195
|
-
* @returns {Promise<void>} A promise that resolves when the bridged endpoint has been removed.
|
|
2196
|
-
*/
|
|
2197
1578
|
async removeBridgedEndpoint(pluginName, device) {
|
|
2198
1579
|
this.log.debug(`Removing bridged endpoint ${plg}${pluginName}${db}:${dev}${device.deviceName}${db} (${zb}${device.name}${db}) for plugin ${plg}${pluginName}${db}`);
|
|
2199
|
-
// Check if the plugin is registered
|
|
2200
1580
|
const plugin = this.plugins.get(pluginName);
|
|
2201
1581
|
if (!plugin) {
|
|
2202
1582
|
this.log.error(`Error removing bridged endpoint ${dev}${device.deviceName}${er} (${zb}${device.name}${er}) for plugin ${plg}${pluginName}${er}: plugin not found`);
|
|
2203
1583
|
return;
|
|
2204
1584
|
}
|
|
2205
|
-
// Register and add the device to the matterbridge aggregator node
|
|
2206
1585
|
if (this.bridgeMode === 'bridge') {
|
|
2207
1586
|
if (!this.aggregatorNode) {
|
|
2208
1587
|
this.log.error(`Error removing bridged endpoint ${dev}${device.deviceName}${er} (${zb}${device.name}${er}) for plugin ${plg}${pluginName}${er}: aggregator node not found`);
|
|
@@ -2217,7 +1596,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2217
1596
|
}
|
|
2218
1597
|
else if (this.bridgeMode === 'childbridge') {
|
|
2219
1598
|
if (plugin.type === 'AccessoryPlatform') {
|
|
2220
|
-
// Nothing to do here since the server node has no aggregator node but only the device itself
|
|
2221
1599
|
}
|
|
2222
1600
|
else if (plugin.type === 'DynamicPlatform') {
|
|
2223
1601
|
if (!plugin.aggregatorNode) {
|
|
@@ -2231,7 +1609,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2231
1609
|
plugin.registeredDevices--;
|
|
2232
1610
|
if (plugin.addedDevices !== undefined)
|
|
2233
1611
|
plugin.addedDevices--;
|
|
2234
|
-
// Close the server node TODO check if this is correct
|
|
2235
1612
|
if (plugin.registeredDevices === 0 && plugin.addedDevices === 0) {
|
|
2236
1613
|
if (plugin.serverNode) {
|
|
2237
1614
|
await this.stopServerNode(plugin.serverNode);
|
|
@@ -2242,27 +1619,14 @@ export class Matterbridge extends EventEmitter {
|
|
|
2242
1619
|
}
|
|
2243
1620
|
}
|
|
2244
1621
|
}
|
|
2245
|
-
// Remove the device from the DeviceManager
|
|
2246
1622
|
this.devices.remove(device);
|
|
2247
1623
|
}
|
|
2248
|
-
/**
|
|
2249
|
-
* Removes all bridged endpoints from the specified plugin.
|
|
2250
|
-
*
|
|
2251
|
-
* @param {string} pluginName - The name of the plugin.
|
|
2252
|
-
* @returns {Promise<void>} A promise that resolves when all bridged endpoints have been removed.
|
|
2253
|
-
*/
|
|
2254
1624
|
async removeAllBridgedEndpoints(pluginName) {
|
|
2255
1625
|
this.log.debug(`Removing all bridged endpoints for plugin ${plg}${pluginName}${db}`);
|
|
2256
1626
|
for (const device of this.devices.array().filter((device) => device.plugin === pluginName)) {
|
|
2257
1627
|
await this.removeBridgedEndpoint(pluginName, device);
|
|
2258
1628
|
}
|
|
2259
1629
|
}
|
|
2260
|
-
/**
|
|
2261
|
-
* Sanitizes the fabric information by converting bigint properties to strings because `res.json` doesn't support bigint.
|
|
2262
|
-
*
|
|
2263
|
-
* @param {ExposedFabricInformation[]} fabricInfo - The array of exposed fabric information objects.
|
|
2264
|
-
* @returns {SanitizedExposedFabricInformation[]} An array of sanitized exposed fabric information objects.
|
|
2265
|
-
*/
|
|
2266
1630
|
sanitizeFabricInformations(fabricInfo) {
|
|
2267
1631
|
return fabricInfo.map((info) => {
|
|
2268
1632
|
return {
|
|
@@ -2276,12 +1640,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2276
1640
|
};
|
|
2277
1641
|
});
|
|
2278
1642
|
}
|
|
2279
|
-
/**
|
|
2280
|
-
* Sanitizes the session information by converting bigint properties to strings because `res.json` doesn't support bigint.
|
|
2281
|
-
*
|
|
2282
|
-
* @param {SessionInformation[]} sessionInfo - The array of session information objects.
|
|
2283
|
-
* @returns {SanitizedSessionInformation[]} An array of sanitized session information objects.
|
|
2284
|
-
*/
|
|
2285
1643
|
sanitizeSessionInformation(sessionInfo) {
|
|
2286
1644
|
return sessionInfo
|
|
2287
1645
|
.filter((session) => session.isPeerActive)
|
|
@@ -2309,51 +1667,11 @@ export class Matterbridge extends EventEmitter {
|
|
|
2309
1667
|
};
|
|
2310
1668
|
});
|
|
2311
1669
|
}
|
|
2312
|
-
/**
|
|
2313
|
-
* Sets the reachability of a matter server node and trigger ReachableChanged event.
|
|
2314
|
-
*
|
|
2315
|
-
* @param {ServerNode<ServerNode.RootEndpoint>} serverNode - The commissioning server to set the reachability for.
|
|
2316
|
-
* @param {boolean} reachable - The new reachability status.
|
|
2317
|
-
*/
|
|
2318
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
2319
1670
|
setServerNodeReachability(serverNode, reachable) {
|
|
2320
|
-
/*
|
|
2321
|
-
const basicInformationCluster = commissioningServer?.getRootClusterServer(BasicInformationCluster);
|
|
2322
|
-
if (basicInformationCluster && basicInformationCluster.attributes.reachable !== undefined) basicInformationCluster.setReachableAttribute(reachable);
|
|
2323
|
-
if (basicInformationCluster && basicInformationCluster.triggerReachableChangedEvent) basicInformationCluster.triggerReachableChangedEvent({ reachableNewValue: reachable });
|
|
2324
|
-
*/
|
|
2325
1671
|
}
|
|
2326
|
-
/**
|
|
2327
|
-
* Sets the reachability of the specified matter aggregator and its bridged devices and trigger.
|
|
2328
|
-
* @param {EndpointNode<AggregatorEndpoint>} aggregatorNode - The matter aggregator to set the reachability for.
|
|
2329
|
-
* @param {boolean} reachable - A boolean indicating the reachability status to set.
|
|
2330
|
-
*/
|
|
2331
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
2332
1672
|
setAggregatorReachability(aggregatorNode, reachable) {
|
|
2333
|
-
/*
|
|
2334
|
-
const basicInformationCluster = matterAggregator.getClusterServer(BasicInformationCluster);
|
|
2335
|
-
if (basicInformationCluster && basicInformationCluster.attributes.reachable !== undefined) basicInformationCluster.setReachableAttribute(reachable);
|
|
2336
|
-
if (basicInformationCluster && basicInformationCluster.triggerReachableChangedEvent) basicInformationCluster.triggerReachableChangedEvent({ reachableNewValue: reachable });
|
|
2337
|
-
matterAggregator.getBridgedDevices().forEach((device) => {
|
|
2338
|
-
this.log.debug(`Setting reachability to true for bridged device: ${dev}${device.name}${nf}`);
|
|
2339
|
-
device.getClusterServer(BridgedDeviceBasicInformationCluster)?.setReachableAttribute(reachable);
|
|
2340
|
-
device.getClusterServer(BridgedDeviceBasicInformationCluster)?.triggerReachableChangedEvent({ reachableNewValue: reachable });
|
|
2341
|
-
});
|
|
2342
|
-
*/
|
|
2343
1673
|
}
|
|
2344
|
-
/**
|
|
2345
|
-
* Sets the reachability of a device and trigger.
|
|
2346
|
-
*
|
|
2347
|
-
* @param {MatterbridgeEndpoint} device - The device to set the reachability for.
|
|
2348
|
-
* @param {boolean} reachable - The new reachability status of the device.
|
|
2349
|
-
*/
|
|
2350
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
2351
1674
|
setDeviceReachability(device, reachable) {
|
|
2352
|
-
/*
|
|
2353
|
-
const basicInformationCluster = device.getClusterServer(BasicInformationCluster);
|
|
2354
|
-
if (basicInformationCluster && basicInformationCluster.attributes.reachable !== undefined) basicInformationCluster.setReachableAttribute(reachable);
|
|
2355
|
-
if (basicInformationCluster && basicInformationCluster.triggerReachableChangedEvent) basicInformationCluster.triggerReachableChangedEvent({ reachableNewValue: reachable });
|
|
2356
|
-
*/
|
|
2357
1675
|
}
|
|
2358
1676
|
getVendorIdName = (vendorId) => {
|
|
2359
1677
|
if (!vendorId)
|
|
@@ -2396,36 +1714,13 @@ export class Matterbridge extends EventEmitter {
|
|
|
2396
1714
|
}
|
|
2397
1715
|
return vendorName;
|
|
2398
1716
|
};
|
|
2399
|
-
/**
|
|
2400
|
-
* Spawns a child process with the given command and arguments.
|
|
2401
|
-
* @param {string} command - The command to execute.
|
|
2402
|
-
* @param {string[]} args - The arguments to pass to the command (default: []).
|
|
2403
|
-
* @returns {Promise<boolean>} A promise that resolves when the child process exits successfully, or rejects if there is an error.
|
|
2404
|
-
*/
|
|
2405
1717
|
async spawnCommand(command, args = []) {
|
|
2406
|
-
/*
|
|
2407
|
-
npm > npm.cmd on windows
|
|
2408
|
-
cmd.exe ['dir'] on windows
|
|
2409
|
-
await this.spawnCommand('npm', ['install', '-g', 'matterbridge']);
|
|
2410
|
-
process.on('unhandledRejection', (reason, promise) => {
|
|
2411
|
-
this.log.error('Unhandled Rejection at:', promise, 'reason:', reason);
|
|
2412
|
-
});
|
|
2413
|
-
|
|
2414
|
-
spawn - [14:27:21.125] [Matterbridge:spawn]: changed 38 packages in 4s
|
|
2415
|
-
spawn - [14:27:21.125] [Matterbridge:spawn]: 10 packages are looking for funding run `npm fund` for details
|
|
2416
|
-
debug - [14:27:21.131] [Matterbridge]: Child process exited with code 0 and signal null
|
|
2417
|
-
debug - [14:27:21.131] [Matterbridge]: Child process stdio streams have closed with code 0
|
|
2418
|
-
*/
|
|
2419
1718
|
const cmdLine = command + ' ' + args.join(' ');
|
|
2420
1719
|
if (process.platform === 'win32' && command === 'npm') {
|
|
2421
|
-
// Must be spawn('cmd.exe', ['/c', 'npm -g install <package>']);
|
|
2422
1720
|
const argstring = 'npm ' + args.join(' ');
|
|
2423
1721
|
args.splice(0, args.length, '/c', argstring);
|
|
2424
1722
|
command = 'cmd.exe';
|
|
2425
1723
|
}
|
|
2426
|
-
// Decide when using sudo on linux
|
|
2427
|
-
// When you need sudo: Spawn stderr: npm error Error: EACCES: permission denied
|
|
2428
|
-
// When you don't need sudo: Failed to start child process "npm install -g matterbridge-eve-door": spawn sudo ENOENT
|
|
2429
1724
|
if (hasParameter('sudo') || (process.platform === 'linux' && command === 'npm' && !hasParameter('docker') && !hasParameter('nosudo'))) {
|
|
2430
1725
|
args.unshift(command);
|
|
2431
1726
|
command = 'sudo';
|
|
@@ -2484,4 +1779,3 @@ export class Matterbridge extends EventEmitter {
|
|
|
2484
1779
|
});
|
|
2485
1780
|
}
|
|
2486
1781
|
}
|
|
2487
|
-
//# sourceMappingURL=matterbridge.js.map
|