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