matterbridge 3.3.2 → 3.3.3-dev-20251012-1af2e85
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 +21 -7
- package/dist/broadcastServer.js +1 -86
- package/dist/broadcastServerTypes.js +0 -24
- package/dist/cli.js +5 -135
- package/dist/cliEmitter.js +0 -37
- package/dist/cliHistory.js +0 -44
- package/dist/clusters/export.js +0 -2
- package/dist/defaultConfigSchema.js +0 -24
- package/dist/deviceManager.js +1 -124
- package/dist/devices/airConditioner.js +0 -57
- package/dist/devices/batteryStorage.js +1 -48
- package/dist/devices/cooktop.js +0 -55
- package/dist/devices/dishwasher.js +0 -57
- package/dist/devices/evse.js +10 -74
- package/dist/devices/export.js +0 -5
- package/dist/devices/extractorHood.js +0 -42
- package/dist/devices/heatPump.js +2 -50
- package/dist/devices/laundryDryer.js +3 -62
- package/dist/devices/laundryWasher.js +4 -70
- package/dist/devices/microwaveOven.js +5 -88
- package/dist/devices/oven.js +0 -85
- package/dist/devices/refrigerator.js +0 -102
- package/dist/devices/roboticVacuumCleaner.js +9 -100
- package/dist/devices/solarPower.js +0 -38
- package/dist/devices/speaker.js +0 -84
- package/dist/devices/temperatureControl.js +3 -25
- package/dist/devices/waterHeater.js +2 -82
- package/dist/dgram/coap.js +13 -126
- package/dist/dgram/dgram.js +2 -114
- package/dist/dgram/mb_coap.js +3 -41
- package/dist/dgram/mb_mdns.js +15 -80
- package/dist/dgram/mdns.js +137 -299
- package/dist/dgram/multicast.js +1 -62
- package/dist/dgram/unicast.js +0 -54
- package/dist/frontend.js +29 -402
- package/dist/frontendTypes.js +0 -45
- package/dist/helpers.js +0 -53
- package/dist/index.js +0 -25
- package/dist/logger/export.js +0 -1
- package/dist/matter/behaviors.js +0 -2
- package/dist/matter/clusters.js +0 -2
- package/dist/matter/devices.js +0 -2
- package/dist/matter/endpoints.js +0 -2
- package/dist/matter/export.js +0 -3
- package/dist/matter/types.js +0 -3
- package/dist/matterbridge.js +45 -795
- package/dist/matterbridgeAccessoryPlatform.js +0 -36
- package/dist/matterbridgeBehaviors.js +5 -65
- package/dist/matterbridgeDeviceTypes.js +17 -630
- package/dist/matterbridgeDynamicPlatform.js +0 -36
- package/dist/matterbridgeEndpoint.js +58 -1398
- package/dist/matterbridgeEndpointHelpers.js +12 -345
- package/dist/matterbridgePlatform.js +1 -341
- package/dist/matterbridgeTypes.js +0 -26
- package/dist/pluginManager.js +3 -325
- package/dist/shelly.js +7 -168
- package/dist/storage/export.js +0 -1
- package/dist/update.js +0 -69
- package/dist/utils/colorUtils.js +2 -97
- package/dist/utils/commandLine.js +0 -54
- package/dist/utils/copyDirectory.js +1 -38
- package/dist/utils/createDirectory.js +0 -33
- package/dist/utils/createZip.js +2 -47
- package/dist/utils/deepCopy.js +0 -39
- package/dist/utils/deepEqual.js +1 -72
- package/dist/utils/error.js +0 -41
- package/dist/utils/export.js +0 -1
- package/dist/utils/hex.js +0 -124
- package/dist/utils/isvalid.js +0 -101
- package/dist/utils/jestHelpers.js +3 -153
- package/dist/utils/network.js +5 -108
- package/dist/utils/spawn.js +0 -71
- package/dist/utils/wait.js +8 -60
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -2
- package/dist/broadcastServer.d.ts +0 -105
- package/dist/broadcastServer.d.ts.map +0 -1
- package/dist/broadcastServer.js.map +0 -1
- package/dist/broadcastServerTypes.d.ts +0 -717
- package/dist/broadcastServerTypes.d.ts.map +0 -1
- package/dist/broadcastServerTypes.js.map +0 -1
- package/dist/cli.d.ts +0 -26
- package/dist/cli.d.ts.map +0 -1
- package/dist/cli.js.map +0 -1
- package/dist/cliEmitter.d.ts +0 -50
- package/dist/cliEmitter.d.ts.map +0 -1
- package/dist/cliEmitter.js.map +0 -1
- package/dist/cliHistory.d.ts +0 -70
- package/dist/cliHistory.d.ts.map +0 -1
- package/dist/cliHistory.js.map +0 -1
- package/dist/clusters/export.d.ts +0 -2
- package/dist/clusters/export.d.ts.map +0 -1
- package/dist/clusters/export.js.map +0 -1
- package/dist/defaultConfigSchema.d.ts +0 -28
- package/dist/defaultConfigSchema.d.ts.map +0 -1
- package/dist/defaultConfigSchema.js.map +0 -1
- package/dist/deviceManager.d.ts +0 -117
- package/dist/deviceManager.d.ts.map +0 -1
- package/dist/deviceManager.js.map +0 -1
- package/dist/devices/airConditioner.d.ts +0 -98
- package/dist/devices/airConditioner.d.ts.map +0 -1
- package/dist/devices/airConditioner.js.map +0 -1
- package/dist/devices/batteryStorage.d.ts +0 -48
- package/dist/devices/batteryStorage.d.ts.map +0 -1
- package/dist/devices/batteryStorage.js.map +0 -1
- package/dist/devices/cooktop.d.ts +0 -60
- package/dist/devices/cooktop.d.ts.map +0 -1
- package/dist/devices/cooktop.js.map +0 -1
- package/dist/devices/dishwasher.d.ts +0 -71
- package/dist/devices/dishwasher.d.ts.map +0 -1
- package/dist/devices/dishwasher.js.map +0 -1
- package/dist/devices/evse.d.ts +0 -75
- package/dist/devices/evse.d.ts.map +0 -1
- package/dist/devices/evse.js.map +0 -1
- package/dist/devices/export.d.ts +0 -17
- package/dist/devices/export.d.ts.map +0 -1
- package/dist/devices/export.js.map +0 -1
- package/dist/devices/extractorHood.d.ts +0 -46
- package/dist/devices/extractorHood.d.ts.map +0 -1
- package/dist/devices/extractorHood.js.map +0 -1
- package/dist/devices/heatPump.d.ts +0 -47
- package/dist/devices/heatPump.d.ts.map +0 -1
- package/dist/devices/heatPump.js.map +0 -1
- package/dist/devices/laundryDryer.d.ts +0 -67
- package/dist/devices/laundryDryer.d.ts.map +0 -1
- package/dist/devices/laundryDryer.js.map +0 -1
- package/dist/devices/laundryWasher.d.ts +0 -81
- package/dist/devices/laundryWasher.d.ts.map +0 -1
- package/dist/devices/laundryWasher.js.map +0 -1
- package/dist/devices/microwaveOven.d.ts +0 -168
- package/dist/devices/microwaveOven.d.ts.map +0 -1
- package/dist/devices/microwaveOven.js.map +0 -1
- package/dist/devices/oven.d.ts +0 -105
- package/dist/devices/oven.d.ts.map +0 -1
- package/dist/devices/oven.js.map +0 -1
- package/dist/devices/refrigerator.d.ts +0 -118
- package/dist/devices/refrigerator.d.ts.map +0 -1
- package/dist/devices/refrigerator.js.map +0 -1
- package/dist/devices/roboticVacuumCleaner.d.ts +0 -112
- package/dist/devices/roboticVacuumCleaner.d.ts.map +0 -1
- package/dist/devices/roboticVacuumCleaner.js.map +0 -1
- package/dist/devices/solarPower.d.ts +0 -40
- package/dist/devices/solarPower.d.ts.map +0 -1
- package/dist/devices/solarPower.js.map +0 -1
- package/dist/devices/speaker.d.ts +0 -87
- package/dist/devices/speaker.d.ts.map +0 -1
- package/dist/devices/speaker.js.map +0 -1
- package/dist/devices/temperatureControl.d.ts +0 -166
- package/dist/devices/temperatureControl.d.ts.map +0 -1
- package/dist/devices/temperatureControl.js.map +0 -1
- package/dist/devices/waterHeater.d.ts +0 -111
- package/dist/devices/waterHeater.d.ts.map +0 -1
- package/dist/devices/waterHeater.js.map +0 -1
- package/dist/dgram/coap.d.ts +0 -205
- package/dist/dgram/coap.d.ts.map +0 -1
- package/dist/dgram/coap.js.map +0 -1
- package/dist/dgram/dgram.d.ts +0 -141
- package/dist/dgram/dgram.d.ts.map +0 -1
- package/dist/dgram/dgram.js.map +0 -1
- package/dist/dgram/mb_coap.d.ts +0 -24
- package/dist/dgram/mb_coap.d.ts.map +0 -1
- package/dist/dgram/mb_coap.js.map +0 -1
- package/dist/dgram/mb_mdns.d.ts +0 -24
- package/dist/dgram/mb_mdns.d.ts.map +0 -1
- package/dist/dgram/mb_mdns.js.map +0 -1
- package/dist/dgram/mdns.d.ts +0 -290
- package/dist/dgram/mdns.d.ts.map +0 -1
- package/dist/dgram/mdns.js.map +0 -1
- package/dist/dgram/multicast.d.ts +0 -67
- package/dist/dgram/multicast.d.ts.map +0 -1
- package/dist/dgram/multicast.js.map +0 -1
- package/dist/dgram/unicast.d.ts +0 -56
- package/dist/dgram/unicast.d.ts.map +0 -1
- package/dist/dgram/unicast.js.map +0 -1
- package/dist/frontend.d.ts +0 -234
- package/dist/frontend.d.ts.map +0 -1
- package/dist/frontend.js.map +0 -1
- package/dist/frontendTypes.d.ts +0 -522
- package/dist/frontendTypes.d.ts.map +0 -1
- package/dist/frontendTypes.js.map +0 -1
- package/dist/helpers.d.ts +0 -48
- package/dist/helpers.d.ts.map +0 -1
- package/dist/helpers.js.map +0 -1
- package/dist/index.d.ts +0 -33
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/logger/export.d.ts +0 -2
- package/dist/logger/export.d.ts.map +0 -1
- package/dist/logger/export.js.map +0 -1
- package/dist/matter/behaviors.d.ts +0 -2
- package/dist/matter/behaviors.d.ts.map +0 -1
- package/dist/matter/behaviors.js.map +0 -1
- package/dist/matter/clusters.d.ts +0 -2
- package/dist/matter/clusters.d.ts.map +0 -1
- package/dist/matter/clusters.js.map +0 -1
- package/dist/matter/devices.d.ts +0 -2
- package/dist/matter/devices.d.ts.map +0 -1
- package/dist/matter/devices.js.map +0 -1
- package/dist/matter/endpoints.d.ts +0 -2
- package/dist/matter/endpoints.d.ts.map +0 -1
- package/dist/matter/endpoints.js.map +0 -1
- package/dist/matter/export.d.ts +0 -5
- package/dist/matter/export.d.ts.map +0 -1
- package/dist/matter/export.js.map +0 -1
- package/dist/matter/types.d.ts +0 -3
- package/dist/matter/types.d.ts.map +0 -1
- package/dist/matter/types.js.map +0 -1
- package/dist/matterbridge.d.ts +0 -469
- package/dist/matterbridge.d.ts.map +0 -1
- package/dist/matterbridge.js.map +0 -1
- package/dist/matterbridgeAccessoryPlatform.d.ts +0 -42
- package/dist/matterbridgeAccessoryPlatform.d.ts.map +0 -1
- package/dist/matterbridgeAccessoryPlatform.js.map +0 -1
- package/dist/matterbridgeBehaviors.d.ts +0 -1747
- package/dist/matterbridgeBehaviors.d.ts.map +0 -1
- package/dist/matterbridgeBehaviors.js.map +0 -1
- package/dist/matterbridgeDeviceTypes.d.ts +0 -761
- package/dist/matterbridgeDeviceTypes.d.ts.map +0 -1
- package/dist/matterbridgeDeviceTypes.js.map +0 -1
- package/dist/matterbridgeDynamicPlatform.d.ts +0 -42
- package/dist/matterbridgeDynamicPlatform.d.ts.map +0 -1
- package/dist/matterbridgeDynamicPlatform.js.map +0 -1
- package/dist/matterbridgeEndpoint.d.ts +0 -1534
- package/dist/matterbridgeEndpoint.d.ts.map +0 -1
- package/dist/matterbridgeEndpoint.js.map +0 -1
- package/dist/matterbridgeEndpointHelpers.d.ts +0 -407
- package/dist/matterbridgeEndpointHelpers.d.ts.map +0 -1
- package/dist/matterbridgeEndpointHelpers.js.map +0 -1
- package/dist/matterbridgePlatform.d.ts +0 -402
- package/dist/matterbridgePlatform.d.ts.map +0 -1
- package/dist/matterbridgePlatform.js.map +0 -1
- package/dist/matterbridgeTypes.d.ts +0 -209
- package/dist/matterbridgeTypes.d.ts.map +0 -1
- package/dist/matterbridgeTypes.js.map +0 -1
- package/dist/pluginManager.d.ts +0 -353
- package/dist/pluginManager.d.ts.map +0 -1
- package/dist/pluginManager.js.map +0 -1
- package/dist/shelly.d.ts +0 -174
- package/dist/shelly.d.ts.map +0 -1
- package/dist/shelly.js.map +0 -1
- package/dist/storage/export.d.ts +0 -2
- package/dist/storage/export.d.ts.map +0 -1
- package/dist/storage/export.js.map +0 -1
- package/dist/update.d.ts +0 -75
- package/dist/update.d.ts.map +0 -1
- package/dist/update.js.map +0 -1
- package/dist/utils/colorUtils.d.ts +0 -99
- package/dist/utils/colorUtils.d.ts.map +0 -1
- package/dist/utils/colorUtils.js.map +0 -1
- package/dist/utils/commandLine.d.ts +0 -59
- package/dist/utils/commandLine.d.ts.map +0 -1
- package/dist/utils/commandLine.js.map +0 -1
- package/dist/utils/copyDirectory.d.ts +0 -33
- package/dist/utils/copyDirectory.d.ts.map +0 -1
- package/dist/utils/copyDirectory.js.map +0 -1
- package/dist/utils/createDirectory.d.ts +0 -34
- package/dist/utils/createDirectory.d.ts.map +0 -1
- package/dist/utils/createDirectory.js.map +0 -1
- package/dist/utils/createZip.d.ts +0 -39
- package/dist/utils/createZip.d.ts.map +0 -1
- package/dist/utils/createZip.js.map +0 -1
- package/dist/utils/deepCopy.d.ts +0 -32
- package/dist/utils/deepCopy.d.ts.map +0 -1
- package/dist/utils/deepCopy.js.map +0 -1
- package/dist/utils/deepEqual.d.ts +0 -54
- package/dist/utils/deepEqual.d.ts.map +0 -1
- package/dist/utils/deepEqual.js.map +0 -1
- package/dist/utils/error.d.ts +0 -44
- package/dist/utils/error.d.ts.map +0 -1
- package/dist/utils/error.js.map +0 -1
- package/dist/utils/export.d.ts +0 -13
- package/dist/utils/export.d.ts.map +0 -1
- package/dist/utils/export.js.map +0 -1
- package/dist/utils/hex.d.ts +0 -89
- package/dist/utils/hex.d.ts.map +0 -1
- package/dist/utils/hex.js.map +0 -1
- package/dist/utils/isvalid.d.ts +0 -103
- package/dist/utils/isvalid.d.ts.map +0 -1
- package/dist/utils/isvalid.js.map +0 -1
- package/dist/utils/jestHelpers.d.ts +0 -137
- package/dist/utils/jestHelpers.d.ts.map +0 -1
- package/dist/utils/jestHelpers.js.map +0 -1
- package/dist/utils/network.d.ts +0 -115
- package/dist/utils/network.d.ts.map +0 -1
- package/dist/utils/network.js.map +0 -1
- package/dist/utils/spawn.d.ts +0 -35
- package/dist/utils/spawn.d.ts.map +0 -1
- package/dist/utils/spawn.js.map +0 -1
- package/dist/utils/wait.d.ts +0 -54
- package/dist/utils/wait.d.ts.map +0 -1
- package/dist/utils/wait.js.map +0 -1
package/dist/matterbridge.js
CHANGED
|
@@ -1,45 +1,16 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* This file contains the class Matterbridge.
|
|
3
|
-
*
|
|
4
|
-
* @file matterbridge.ts
|
|
5
|
-
* @author Luca Liguori
|
|
6
|
-
* @created 2023-12-29
|
|
7
|
-
* @version 1.6.0
|
|
8
|
-
* @license Apache-2.0
|
|
9
|
-
*
|
|
10
|
-
* Copyright 2023, 2024, 2025 Luca Liguori.
|
|
11
|
-
*
|
|
12
|
-
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
13
|
-
* you may not use this file except in compliance with the License.
|
|
14
|
-
* You may obtain a copy of the License at
|
|
15
|
-
*
|
|
16
|
-
* http://www.apache.org/licenses/LICENSE-2.0
|
|
17
|
-
*
|
|
18
|
-
* Unless required by applicable law or agreed to in writing, software
|
|
19
|
-
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
20
|
-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
21
|
-
* See the License for the specific language governing permissions and
|
|
22
|
-
* limitations under the License.
|
|
23
|
-
*/
|
|
24
|
-
// eslint-disable-next-line no-console
|
|
25
1
|
if (process.argv.includes('--loader') || process.argv.includes('-loader'))
|
|
26
2
|
console.log('\u001B[32mMatterbridge loaded.\u001B[40;0m');
|
|
27
|
-
// Node.js modules
|
|
28
3
|
import os from 'node:os';
|
|
29
4
|
import path from 'node:path';
|
|
30
5
|
import { promises as fs } from 'node:fs';
|
|
31
6
|
import EventEmitter from 'node:events';
|
|
32
7
|
import { inspect } from 'node:util';
|
|
33
|
-
// AnsiLogger module
|
|
34
8
|
import { AnsiLogger, UNDERLINE, UNDERLINEOFF, db, debugStringify, BRIGHT, RESET, er, nf, rs, wr, RED, GREEN, zb, CYAN, nt, BLUE, or } from 'node-ansi-logger';
|
|
35
|
-
// NodeStorage module
|
|
36
9
|
import { NodeStorageManager } from 'node-persist-manager';
|
|
37
|
-
// @matter
|
|
38
10
|
import { DeviceTypeId, Endpoint, Logger, LogLevel as MatterLogLevel, LogFormat as MatterLogFormat, VendorId, StorageService, Environment, ServerNode, UINT32_MAX, UINT16_MAX, Crypto, } from '@matter/main';
|
|
39
11
|
import { FabricAction, MdnsService, PaseClient } from '@matter/main/protocol';
|
|
40
12
|
import { AggregatorEndpoint } from '@matter/main/endpoints';
|
|
41
13
|
import { BasicInformationServer } from '@matter/main/behaviors/basic-information';
|
|
42
|
-
// Matterbridge
|
|
43
14
|
import { getParameter, getIntParameter, hasParameter } from './utils/commandLine.js';
|
|
44
15
|
import { copyDirectory } from './utils/copyDirectory.js';
|
|
45
16
|
import { createDirectory } from './utils/createDirectory.js';
|
|
@@ -53,11 +24,7 @@ import { MatterbridgeEndpoint } from './matterbridgeEndpoint.js';
|
|
|
53
24
|
import { bridge } from './matterbridgeDeviceTypes.js';
|
|
54
25
|
import { Frontend } from './frontend.js';
|
|
55
26
|
import { addVirtualDevices } from './helpers.js';
|
|
56
|
-
/**
|
|
57
|
-
* Represents the Matterbridge application.
|
|
58
|
-
*/
|
|
59
27
|
export class Matterbridge extends EventEmitter {
|
|
60
|
-
/** Matterbridge system information */
|
|
61
28
|
systemInformation = {
|
|
62
29
|
interfaceName: '',
|
|
63
30
|
macAddress: '',
|
|
@@ -80,7 +47,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
80
47
|
heapTotal: '',
|
|
81
48
|
heapUsed: '',
|
|
82
49
|
};
|
|
83
|
-
// Matterbridge settings
|
|
84
50
|
homeDirectory = '';
|
|
85
51
|
rootDirectory = '';
|
|
86
52
|
matterbridgeDirectory = '';
|
|
@@ -95,15 +61,10 @@ export class Matterbridge extends EventEmitter {
|
|
|
95
61
|
restartMode = '';
|
|
96
62
|
virtualMode = 'outlet';
|
|
97
63
|
profile = getParameter('profile');
|
|
98
|
-
|
|
99
|
-
log = new AnsiLogger({ logName: 'Matterbridge', logTimestampFormat: 4 /* TimestampFormat.TIME_MILLIS */, logLevel: hasParameter('debug') ? "debug" /* LogLevel.DEBUG */ : "info" /* LogLevel.INFO */ });
|
|
100
|
-
/** Whether to log to a file */
|
|
64
|
+
log = new AnsiLogger({ logName: 'Matterbridge', logTimestampFormat: 4, logLevel: hasParameter('debug') ? "debug" : "info" });
|
|
101
65
|
fileLogger = false;
|
|
102
|
-
|
|
103
|
-
matterLog = new AnsiLogger({ logName: 'Matter', logTimestampFormat: 4 /* TimestampFormat.TIME_MILLIS */, logLevel: "debug" /* LogLevel.DEBUG */ });
|
|
104
|
-
/** Whether to log Matter to a file */
|
|
66
|
+
matterLog = new AnsiLogger({ logName: 'Matter', logTimestampFormat: 4, logLevel: "debug" });
|
|
105
67
|
matterFileLogger = false;
|
|
106
|
-
// Frontend settings
|
|
107
68
|
readOnly = hasParameter('readonly') || hasParameter('shelly');
|
|
108
69
|
shellyBoard = hasParameter('shelly');
|
|
109
70
|
shellySysUpdate = false;
|
|
@@ -111,18 +72,12 @@ export class Matterbridge extends EventEmitter {
|
|
|
111
72
|
restartRequired = false;
|
|
112
73
|
fixedRestartRequired = false;
|
|
113
74
|
updateRequired = false;
|
|
114
|
-
// Managers
|
|
115
75
|
plugins = new PluginManager(this);
|
|
116
76
|
devices = new DeviceManager();
|
|
117
|
-
// Frontend
|
|
118
77
|
frontend = new Frontend(this);
|
|
119
|
-
/** Matterbridge node storage manager created in matterbridgeDirectory with name 'storage' */
|
|
120
78
|
nodeStorage;
|
|
121
|
-
/** Matterbridge node context created with name 'matterbridge' */
|
|
122
79
|
nodeContext;
|
|
123
|
-
/** The main instance of the Matterbridge class (singleton) */
|
|
124
80
|
static instance;
|
|
125
|
-
// Instance properties
|
|
126
81
|
shutdown = false;
|
|
127
82
|
failCountLimit = hasParameter('shelly') ? 600 : 120;
|
|
128
83
|
hasCleanupStarted = false;
|
|
@@ -137,32 +92,19 @@ export class Matterbridge extends EventEmitter {
|
|
|
137
92
|
sigtermHandler;
|
|
138
93
|
exceptionHandler;
|
|
139
94
|
rejectionHandler;
|
|
140
|
-
/** Matter environment default */
|
|
141
95
|
environment = Environment.default;
|
|
142
|
-
/** Matter storage service from environment default */
|
|
143
96
|
matterStorageService;
|
|
144
|
-
/** Matter storage manager created with name 'Matterbridge' */
|
|
145
97
|
matterStorageManager;
|
|
146
|
-
/** Matter matterbridge storage context created in the storage manager with name 'persist' */
|
|
147
98
|
matterbridgeContext;
|
|
148
99
|
controllerContext;
|
|
149
|
-
/** Matter mdns interface e.g. 'eth0' or 'wlan0' or 'Wi-Fi' */
|
|
150
100
|
mdnsInterface;
|
|
151
|
-
/** Matter listeningAddressIpv4 address */
|
|
152
101
|
ipv4Address;
|
|
153
|
-
/** Matter listeningAddressIpv6 address */
|
|
154
102
|
ipv6Address;
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
/** Matter commissioning discriminator */
|
|
160
|
-
discriminator; // first server node discriminator
|
|
161
|
-
/** Matter device certification */
|
|
162
|
-
certification; // device certification
|
|
163
|
-
/** Matter server node in bridge mode */
|
|
103
|
+
port;
|
|
104
|
+
passcode;
|
|
105
|
+
discriminator;
|
|
106
|
+
certification;
|
|
164
107
|
serverNode;
|
|
165
|
-
/** Matter aggregator node in bridge mode */
|
|
166
108
|
aggregatorNode;
|
|
167
109
|
aggregatorVendorId = VendorId(getIntParameter('vendorId') ?? 0xfff1);
|
|
168
110
|
aggregatorVendorName = getParameter('vendorName') ?? 'Matterbridge';
|
|
@@ -171,26 +113,13 @@ export class Matterbridge extends EventEmitter {
|
|
|
171
113
|
aggregatorDeviceType = DeviceTypeId(getIntParameter('deviceType') ?? bridge.code);
|
|
172
114
|
aggregatorSerialNumber = getParameter('serialNumber');
|
|
173
115
|
aggregatorUniqueId = getParameter('uniqueId');
|
|
174
|
-
/** Advertising nodes map: time advertising started keyed by storeId */
|
|
175
116
|
advertisingNodes = new Map();
|
|
176
|
-
/** We load asyncronously so is private */
|
|
177
117
|
constructor() {
|
|
178
118
|
super();
|
|
179
119
|
this.log.logNameColor = '\x1b[38;5;115m';
|
|
180
120
|
}
|
|
181
|
-
//* ************************************************************************************************************************************ */
|
|
182
|
-
// loadInstance() and cleanup() methods */
|
|
183
|
-
//* ************************************************************************************************************************************ */
|
|
184
|
-
/**
|
|
185
|
-
* Loads an instance of the Matterbridge class.
|
|
186
|
-
* If an instance already exists, return that instance.
|
|
187
|
-
*
|
|
188
|
-
* @param {boolean} initialize - Whether to initialize the Matterbridge instance after loading. Defaults to false.
|
|
189
|
-
* @returns {Matterbridge} A promise that resolves to the Matterbridge instance.
|
|
190
|
-
*/
|
|
191
121
|
static async loadInstance(initialize = false) {
|
|
192
122
|
if (!Matterbridge.instance) {
|
|
193
|
-
// eslint-disable-next-line no-console
|
|
194
123
|
if (hasParameter('debug'))
|
|
195
124
|
console.log(GREEN + 'Creating a new instance of Matterbridge.', initialize ? 'Initializing...' : 'Not initializing...', rs);
|
|
196
125
|
Matterbridge.instance = new Matterbridge();
|
|
@@ -199,17 +128,8 @@ export class Matterbridge extends EventEmitter {
|
|
|
199
128
|
}
|
|
200
129
|
return Matterbridge.instance;
|
|
201
130
|
}
|
|
202
|
-
/**
|
|
203
|
-
* Call cleanup() and dispose MdnsService.
|
|
204
|
-
*
|
|
205
|
-
* @param {number} [timeout] - The timeout duration to wait for the cleanup to complete in milliseconds. Default is 1000.
|
|
206
|
-
* @param {number} [pause] - The pause duration after the cleanup in milliseconds. Default is 250.
|
|
207
|
-
*
|
|
208
|
-
* @deprecated This method is deprecated and is ONLY used for jest tests.
|
|
209
|
-
*/
|
|
210
131
|
async destroyInstance(timeout = 1000, pause = 250) {
|
|
211
132
|
this.log.info(`Destroy instance...`);
|
|
212
|
-
// Save server nodes to close
|
|
213
133
|
const servers = [];
|
|
214
134
|
if (this.bridgeMode === 'bridge') {
|
|
215
135
|
if (this.serverNode)
|
|
@@ -227,101 +147,67 @@ export class Matterbridge extends EventEmitter {
|
|
|
227
147
|
servers.push(device.serverNode);
|
|
228
148
|
}
|
|
229
149
|
}
|
|
230
|
-
// Let any already‐queued microtasks run first
|
|
231
150
|
await Promise.resolve();
|
|
232
|
-
// Wait for the cleanup to finish
|
|
233
151
|
await wait(pause, 'destroyInstance start', true);
|
|
234
|
-
// Cleanup
|
|
235
152
|
await this.cleanup('destroying instance...', false, timeout);
|
|
236
|
-
// Close servers mdns service
|
|
237
153
|
this.log.info(`Dispose ${servers.length} MdnsService...`);
|
|
238
154
|
for (const server of servers) {
|
|
239
155
|
await server.env.get(MdnsService)[Symbol.asyncDispose]();
|
|
240
156
|
this.log.info(`Closed ${server.id} MdnsService`);
|
|
241
157
|
}
|
|
242
|
-
// Let any already‐queued microtasks run first
|
|
243
158
|
await Promise.resolve();
|
|
244
|
-
// Wait for the cleanup to finish
|
|
245
159
|
await wait(pause, 'destroyInstance stop', true);
|
|
246
160
|
}
|
|
247
|
-
/**
|
|
248
|
-
* Initializes the Matterbridge application.
|
|
249
|
-
*
|
|
250
|
-
* @remarks
|
|
251
|
-
* This method performs the necessary setup and initialization steps for the Matterbridge application.
|
|
252
|
-
* It displays the help information if the 'help' parameter is provided, sets up the logger, checks the
|
|
253
|
-
* node version, registers signal handlers, initializes storage, and parses the command line.
|
|
254
|
-
*
|
|
255
|
-
* @returns {Promise<void>} A Promise that resolves when the initialization is complete.
|
|
256
|
-
*/
|
|
257
161
|
async initialize() {
|
|
258
|
-
// for (let i = 1; i <= 255; i++) console.log(`\x1b[38;5;${i}mColor: ${i}`);
|
|
259
|
-
// Emit the initialize_started event
|
|
260
162
|
this.emit('initialize_started');
|
|
261
|
-
// Set the restart mode
|
|
262
163
|
if (hasParameter('service'))
|
|
263
164
|
this.restartMode = 'service';
|
|
264
165
|
if (hasParameter('docker'))
|
|
265
166
|
this.restartMode = 'docker';
|
|
266
|
-
// Set the matterbridge home directory
|
|
267
167
|
this.homeDirectory = getParameter('homedir') ?? os.homedir();
|
|
268
168
|
await createDirectory(this.homeDirectory, 'Matterbridge Home Directory', this.log);
|
|
269
|
-
// Set the matterbridge directory
|
|
270
169
|
this.matterbridgeDirectory = this.profile ? path.join(this.homeDirectory, '.matterbridge', 'profiles', this.profile) : path.join(this.homeDirectory, '.matterbridge');
|
|
271
170
|
await createDirectory(this.matterbridgeDirectory, 'Matterbridge Directory', this.log);
|
|
272
171
|
await createDirectory(path.join(this.matterbridgeDirectory, 'certs'), 'Matterbridge Frontend Certificate Directory', this.log);
|
|
273
172
|
await createDirectory(path.join(this.matterbridgeDirectory, 'uploads'), 'Matterbridge Frontend Uploads Directory', this.log);
|
|
274
|
-
// Set the matterbridge plugin directory
|
|
275
173
|
this.matterbridgePluginDirectory = this.profile ? path.join(this.homeDirectory, 'Matterbridge', 'profiles', this.profile) : path.join(this.homeDirectory, 'Matterbridge');
|
|
276
174
|
await createDirectory(this.matterbridgePluginDirectory, 'Matterbridge Plugin Directory', this.log);
|
|
277
|
-
// Set the matterbridge cert directory
|
|
278
175
|
this.matterbridgeCertDirectory = this.profile ? path.join(this.homeDirectory, '.mattercert', 'profiles', this.profile) : path.join(this.homeDirectory, '.mattercert');
|
|
279
176
|
await createDirectory(this.matterbridgeCertDirectory, 'Matterbridge Matter Certificate Directory', this.log);
|
|
280
|
-
// Set the matterbridge root directory
|
|
281
177
|
const { fileURLToPath } = await import('node:url');
|
|
282
178
|
const currentFileDirectory = path.dirname(fileURLToPath(import.meta.url));
|
|
283
|
-
this.rootDirectory = path.resolve(currentFileDirectory, '../');
|
|
284
|
-
// Setup the matter environment with default values
|
|
179
|
+
this.rootDirectory = path.resolve(currentFileDirectory, '../');
|
|
285
180
|
this.environment.vars.set('log.level', MatterLogLevel.INFO);
|
|
286
181
|
this.environment.vars.set('log.format', MatterLogFormat.ANSI);
|
|
287
182
|
this.environment.vars.set('path.root', path.join(this.matterbridgeDirectory, MATTER_STORAGE_NAME));
|
|
288
183
|
this.environment.vars.set('runtime.signals', false);
|
|
289
184
|
this.environment.vars.set('runtime.exitcode', false);
|
|
290
|
-
// Register process handlers
|
|
291
185
|
this.registerProcessHandlers();
|
|
292
|
-
// Initialize nodeStorage and nodeContext
|
|
293
186
|
try {
|
|
294
187
|
this.log.debug(`Creating node storage manager: ${CYAN}${NODE_STORAGE_DIR}${db}`);
|
|
295
188
|
this.nodeStorage = new NodeStorageManager({ dir: path.join(this.matterbridgeDirectory, NODE_STORAGE_DIR), writeQueue: false, expiredInterval: undefined, logging: false });
|
|
296
189
|
this.log.debug('Creating node storage context for matterbridge');
|
|
297
190
|
this.nodeContext = await this.nodeStorage.createStorage('matterbridge');
|
|
298
|
-
// TODO: Remove this code when node-persist-manager is updated
|
|
299
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
300
191
|
const keys = (await this.nodeStorage?.storage.keys());
|
|
301
192
|
for (const key of keys) {
|
|
302
193
|
this.log.debug(`Checking node storage manager key: ${CYAN}${key}${db}`);
|
|
303
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
304
194
|
await this.nodeStorage?.storage.get(key);
|
|
305
195
|
}
|
|
306
196
|
const storages = await this.nodeStorage.getStorageNames();
|
|
307
197
|
for (const storage of storages) {
|
|
308
198
|
this.log.debug(`Checking storage: ${CYAN}${storage}${db}`);
|
|
309
199
|
const nodeContext = await this.nodeStorage?.createStorage(storage);
|
|
310
|
-
// TODO: Remove this code when node-persist-manager is updated
|
|
311
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
312
200
|
const keys = (await nodeContext?.storage.keys());
|
|
313
201
|
keys.forEach(async (key) => {
|
|
314
202
|
this.log.debug(`Checking key: ${CYAN}${storage}:${key}${db}`);
|
|
315
203
|
await nodeContext?.get(key);
|
|
316
204
|
});
|
|
317
205
|
}
|
|
318
|
-
// Creating a backup of the node storage since it is not corrupted
|
|
319
206
|
this.log.debug('Creating node storage backup...');
|
|
320
207
|
await copyDirectory(path.join(this.matterbridgeDirectory, NODE_STORAGE_DIR), path.join(this.matterbridgeDirectory, NODE_STORAGE_DIR + '.backup'));
|
|
321
208
|
this.log.debug('Created node storage backup');
|
|
322
209
|
}
|
|
323
210
|
catch (error) {
|
|
324
|
-
// Restoring the backup of the node storage since it is corrupted
|
|
325
211
|
this.log.error(`Error creating node storage manager and context: ${error instanceof Error ? error.message : error}`);
|
|
326
212
|
if (hasParameter('norestore')) {
|
|
327
213
|
this.log.fatal(`The matterbridge storage is corrupted. Found -norestore parameter: exiting...`);
|
|
@@ -335,19 +221,14 @@ export class Matterbridge extends EventEmitter {
|
|
|
335
221
|
if (!this.nodeStorage || !this.nodeContext) {
|
|
336
222
|
throw new Error('Fatal error creating node storage manager and context for matterbridge');
|
|
337
223
|
}
|
|
338
|
-
// Set the first port to use for the commissioning server (will be incremented in childbridge mode)
|
|
339
224
|
this.port = getIntParameter('port') ?? (await this.nodeContext.get('matterport', 5540)) ?? 5540;
|
|
340
|
-
// Set the first passcode to use for the commissioning server (will be incremented in childbridge mode)
|
|
341
225
|
this.passcode = getIntParameter('passcode') ?? (await this.nodeContext.get('matterpasscode')) ?? PaseClient.generateRandomPasscode(this.environment.get(Crypto));
|
|
342
|
-
// Set the first discriminator to use for the commissioning server (will be incremented in childbridge mode)
|
|
343
226
|
this.discriminator = getIntParameter('discriminator') ?? (await this.nodeContext.get('matterdiscriminator')) ?? PaseClient.generateRandomDiscriminator(this.environment.get(Crypto));
|
|
344
|
-
// Certificate management
|
|
345
227
|
const pairingFilePath = path.join(this.matterbridgeCertDirectory, 'pairing.json');
|
|
346
228
|
try {
|
|
347
229
|
await fs.access(pairingFilePath, fs.constants.R_OK);
|
|
348
230
|
const pairingFileContent = await fs.readFile(pairingFilePath, 'utf8');
|
|
349
231
|
const pairingFileJson = JSON.parse(pairingFileContent);
|
|
350
|
-
// Set the vendorId, vendorName, productId, productName, deviceType, serialNumber, uniqueId if they are present in the pairing file
|
|
351
232
|
if (isValidNumber(pairingFileJson.vendorId)) {
|
|
352
233
|
this.aggregatorVendorId = VendorId(pairingFileJson.vendorId);
|
|
353
234
|
this.log.info(`Pairing file ${CYAN}${pairingFilePath}${nf} found. Using vendorId ${CYAN}${this.aggregatorVendorId}${nf} from pairing file.`);
|
|
@@ -376,13 +257,11 @@ export class Matterbridge extends EventEmitter {
|
|
|
376
257
|
this.aggregatorUniqueId = pairingFileJson.uniqueId;
|
|
377
258
|
this.log.info(`Pairing file ${CYAN}${pairingFilePath}${nf} found. Using uniqueId ${CYAN}${this.aggregatorUniqueId}${nf} from pairing file.`);
|
|
378
259
|
}
|
|
379
|
-
// Override the passcode and discriminator if they are present in the pairing file
|
|
380
260
|
if (isValidNumber(pairingFileJson.passcode) && isValidNumber(pairingFileJson.discriminator)) {
|
|
381
261
|
this.passcode = pairingFileJson.passcode;
|
|
382
262
|
this.discriminator = pairingFileJson.discriminator;
|
|
383
263
|
this.log.info(`Pairing file ${CYAN}${pairingFilePath}${nf} found. Using passcode ${CYAN}${this.passcode}${nf} and discriminator ${CYAN}${this.discriminator}${nf} from pairing file.`);
|
|
384
264
|
}
|
|
385
|
-
// Set the certification for matter.js if it is present in the pairing file
|
|
386
265
|
if (pairingFileJson.privateKey && pairingFileJson.certificate && pairingFileJson.intermediateCertificate && pairingFileJson.declaration) {
|
|
387
266
|
const { hexToBuffer } = await import('./utils/hex.js');
|
|
388
267
|
this.certification = {
|
|
@@ -397,43 +276,40 @@ export class Matterbridge extends EventEmitter {
|
|
|
397
276
|
catch (error) {
|
|
398
277
|
this.log.debug(`Pairing file ${CYAN}${pairingFilePath}${db} not found: ${error instanceof Error ? error.message : error}`);
|
|
399
278
|
}
|
|
400
|
-
// Store the passcode, discriminator and port in the node context
|
|
401
279
|
await this.nodeContext.set('matterport', this.port);
|
|
402
280
|
await this.nodeContext.set('matterpasscode', this.passcode);
|
|
403
281
|
await this.nodeContext.set('matterdiscriminator', this.discriminator);
|
|
404
282
|
this.log.debug(`Initializing server node for Matterbridge on port ${this.port} with passcode ${this.passcode} and discriminator ${this.discriminator}`);
|
|
405
|
-
// Set matterbridge logger level (context: matterbridgeLogLevel)
|
|
406
283
|
if (hasParameter('logger')) {
|
|
407
284
|
const level = getParameter('logger');
|
|
408
285
|
if (level === 'debug') {
|
|
409
|
-
this.log.logLevel = "debug"
|
|
286
|
+
this.log.logLevel = "debug";
|
|
410
287
|
}
|
|
411
288
|
else if (level === 'info') {
|
|
412
|
-
this.log.logLevel = "info"
|
|
289
|
+
this.log.logLevel = "info";
|
|
413
290
|
}
|
|
414
291
|
else if (level === 'notice') {
|
|
415
|
-
this.log.logLevel = "notice"
|
|
292
|
+
this.log.logLevel = "notice";
|
|
416
293
|
}
|
|
417
294
|
else if (level === 'warn') {
|
|
418
|
-
this.log.logLevel = "warn"
|
|
295
|
+
this.log.logLevel = "warn";
|
|
419
296
|
}
|
|
420
297
|
else if (level === 'error') {
|
|
421
|
-
this.log.logLevel = "error"
|
|
298
|
+
this.log.logLevel = "error";
|
|
422
299
|
}
|
|
423
300
|
else if (level === 'fatal') {
|
|
424
|
-
this.log.logLevel = "fatal"
|
|
301
|
+
this.log.logLevel = "fatal";
|
|
425
302
|
}
|
|
426
303
|
else {
|
|
427
304
|
this.log.warn(`Invalid matterbridge logger level: ${level}. Using default level "info".`);
|
|
428
|
-
this.log.logLevel = "info"
|
|
305
|
+
this.log.logLevel = "info";
|
|
429
306
|
}
|
|
430
307
|
}
|
|
431
308
|
else {
|
|
432
|
-
this.log.logLevel = await this.nodeContext.get('matterbridgeLogLevel', this.shellyBoard ? "notice"
|
|
309
|
+
this.log.logLevel = await this.nodeContext.get('matterbridgeLogLevel', this.shellyBoard ? "notice" : "info");
|
|
433
310
|
}
|
|
434
311
|
this.frontend.logLevel = this.log.logLevel;
|
|
435
312
|
MatterbridgeEndpoint.logLevel = this.log.logLevel;
|
|
436
|
-
// Create the file logger for matterbridge (context: matterbridgeFileLog)
|
|
437
313
|
if (hasParameter('filelogger') || (await this.nodeContext.get('matterbridgeFileLog', false))) {
|
|
438
314
|
AnsiLogger.setGlobalLogfile(path.join(this.matterbridgeDirectory, MATTERBRIDGE_LOGGER_FILE), this.log.logLevel, true);
|
|
439
315
|
this.fileLogger = true;
|
|
@@ -442,7 +318,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
442
318
|
this.log.debug(`Matterbridge logLevel: ${this.log.logLevel} fileLoger: ${this.fileLogger}.`);
|
|
443
319
|
if (this.profile !== undefined)
|
|
444
320
|
this.log.debug(`Matterbridge profile: ${this.profile}.`);
|
|
445
|
-
// Set matter.js logger level, format and logger (context: matterLogLevel)
|
|
446
321
|
if (hasParameter('matterlogger')) {
|
|
447
322
|
const level = getParameter('matterlogger');
|
|
448
323
|
if (level === 'debug') {
|
|
@@ -472,13 +347,11 @@ export class Matterbridge extends EventEmitter {
|
|
|
472
347
|
Logger.level = (await this.nodeContext.get('matterLogLevel', this.shellyBoard ? MatterLogLevel.NOTICE : MatterLogLevel.INFO));
|
|
473
348
|
}
|
|
474
349
|
Logger.format = MatterLogFormat.ANSI;
|
|
475
|
-
// Create the logger for matter.js with file logging (context: matterFileLog)
|
|
476
350
|
if (hasParameter('matterfilelogger') || (await this.nodeContext.get('matterFileLog', false))) {
|
|
477
351
|
this.matterFileLogger = true;
|
|
478
352
|
}
|
|
479
353
|
Logger.destinations.default.write = this.createDestinationMatterLogger(this.matterFileLogger);
|
|
480
354
|
this.log.debug(`Matter logLevel: ${Logger.level} fileLoger: ${this.matterFileLogger}.`);
|
|
481
|
-
// Log network interfaces
|
|
482
355
|
const networkInterfaces = os.networkInterfaces();
|
|
483
356
|
const availableAddresses = Object.entries(networkInterfaces);
|
|
484
357
|
const availableInterfaceNames = Object.keys(networkInterfaces);
|
|
@@ -491,7 +364,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
491
364
|
});
|
|
492
365
|
}
|
|
493
366
|
}
|
|
494
|
-
// Set the interface to use for matter server node mdnsInterface
|
|
495
367
|
if (hasParameter('mdnsinterface')) {
|
|
496
368
|
this.mdnsInterface = getParameter('mdnsinterface');
|
|
497
369
|
}
|
|
@@ -500,7 +372,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
500
372
|
if (this.mdnsInterface === '')
|
|
501
373
|
this.mdnsInterface = undefined;
|
|
502
374
|
}
|
|
503
|
-
// Validate mdnsInterface
|
|
504
375
|
if (this.mdnsInterface) {
|
|
505
376
|
if (!availableInterfaceNames.includes(this.mdnsInterface)) {
|
|
506
377
|
this.log.error(`Invalid mdnsinterface: ${this.mdnsInterface}. Available interfaces are: ${availableInterfaceNames.join(', ')}. Using all available interfaces.`);
|
|
@@ -513,7 +384,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
513
384
|
}
|
|
514
385
|
if (this.mdnsInterface)
|
|
515
386
|
this.environment.vars.set('mdns.networkInterface', this.mdnsInterface);
|
|
516
|
-
// Set the listeningAddressIpv4 for the matter commissioning server
|
|
517
387
|
if (hasParameter('ipv4address')) {
|
|
518
388
|
this.ipv4Address = getParameter('ipv4address');
|
|
519
389
|
}
|
|
@@ -522,7 +392,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
522
392
|
if (this.ipv4Address === '')
|
|
523
393
|
this.ipv4Address = undefined;
|
|
524
394
|
}
|
|
525
|
-
// Validate ipv4address
|
|
526
395
|
if (this.ipv4Address) {
|
|
527
396
|
let isValid = false;
|
|
528
397
|
for (const [ifaceName, ifaces] of availableAddresses) {
|
|
@@ -538,7 +407,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
538
407
|
await this.nodeContext.remove('matteripv4address');
|
|
539
408
|
}
|
|
540
409
|
}
|
|
541
|
-
// Set the listeningAddressIpv6 for the matter commissioning server
|
|
542
410
|
if (hasParameter('ipv6address')) {
|
|
543
411
|
this.ipv6Address = getParameter('ipv6address');
|
|
544
412
|
}
|
|
@@ -547,7 +415,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
547
415
|
if (this.ipv6Address === '')
|
|
548
416
|
this.ipv6Address = undefined;
|
|
549
417
|
}
|
|
550
|
-
// Validate ipv6address
|
|
551
418
|
if (this.ipv6Address) {
|
|
552
419
|
let isValid = false;
|
|
553
420
|
for (const [ifaceName, ifaces] of availableAddresses) {
|
|
@@ -556,7 +423,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
556
423
|
isValid = true;
|
|
557
424
|
break;
|
|
558
425
|
}
|
|
559
|
-
/* istanbul ignore next */
|
|
560
426
|
if (ifaces && ifaces.find((iface) => iface.scopeid && iface.scopeid > 0 && iface.address + '%' + (process.platform === 'win32' ? iface.scopeid : ifaceName) === this.ipv6Address)) {
|
|
561
427
|
this.log.info(`Using ipv6address ${CYAN}${this.ipv6Address}${nf} on interface ${CYAN}${ifaceName}${nf} for the Matter server node.`);
|
|
562
428
|
isValid = true;
|
|
@@ -569,7 +435,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
569
435
|
await this.nodeContext.remove('matteripv6address');
|
|
570
436
|
}
|
|
571
437
|
}
|
|
572
|
-
// Initialize the virtual mode
|
|
573
438
|
if (hasParameter('novirtual')) {
|
|
574
439
|
this.virtualMode = 'disabled';
|
|
575
440
|
await this.nodeContext.set('virtualmode', 'disabled');
|
|
@@ -578,17 +443,12 @@ export class Matterbridge extends EventEmitter {
|
|
|
578
443
|
this.virtualMode = (await this.nodeContext.get('virtualmode', 'outlet'));
|
|
579
444
|
}
|
|
580
445
|
this.log.debug(`Virtual mode ${this.virtualMode}.`);
|
|
581
|
-
// Initialize PluginManager
|
|
582
446
|
this.plugins.logLevel = this.log.logLevel;
|
|
583
447
|
await this.plugins.loadFromStorage();
|
|
584
|
-
// Initialize DeviceManager
|
|
585
448
|
this.devices.logLevel = this.log.logLevel;
|
|
586
|
-
// Get the plugins from node storage and create the plugins node storage contexts
|
|
587
449
|
for (const plugin of this.plugins) {
|
|
588
450
|
const packageJson = await this.plugins.parse(plugin);
|
|
589
451
|
if (packageJson === null && !hasParameter('add') && !hasParameter('remove') && !hasParameter('enable') && !hasParameter('disable') && !hasParameter('reset') && !hasParameter('factoryreset')) {
|
|
590
|
-
// Try to reinstall the plugin from npm (for Docker pull and external plugins)
|
|
591
|
-
// We don't do this when the add and other parameters are set because we shut down the process after adding the plugin
|
|
592
452
|
this.log.info(`Error parsing plugin ${plg}${plugin.name}${nf}. Trying to reinstall it from npm.`);
|
|
593
453
|
try {
|
|
594
454
|
const { spawnCommand } = await import('./utils/spawn.js');
|
|
@@ -611,7 +471,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
611
471
|
await plugin.nodeContext.set('description', plugin.description);
|
|
612
472
|
await plugin.nodeContext.set('author', plugin.author);
|
|
613
473
|
}
|
|
614
|
-
// Log system info and create .matterbridge directory
|
|
615
474
|
await this.logNodeAndSystemInfo();
|
|
616
475
|
this.log.notice(`Matterbridge version ${this.matterbridgeVersion} ` +
|
|
617
476
|
`${hasParameter('bridge') || (!hasParameter('childbridge') && (await this.nodeContext?.get('bridgeMode', '')) === 'bridge') ? 'mode bridge ' : ''}` +
|
|
@@ -619,7 +478,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
619
478
|
`${hasParameter('controller') ? 'mode controller ' : ''}` +
|
|
620
479
|
`${this.restartMode !== '' ? 'restart mode ' + this.restartMode + ' ' : ''}` +
|
|
621
480
|
`running on ${this.systemInformation.osType} (v.${this.systemInformation.osRelease}) platform ${this.systemInformation.osPlatform} arch ${this.systemInformation.osArch}`);
|
|
622
|
-
// Check node version and throw error
|
|
623
481
|
const minNodeVersion = 18;
|
|
624
482
|
const nodeVersion = process.versions.node;
|
|
625
483
|
const versionMajor = parseInt(nodeVersion.split('.')[0]);
|
|
@@ -627,18 +485,10 @@ export class Matterbridge extends EventEmitter {
|
|
|
627
485
|
this.log.error(`Node version ${versionMajor} is not supported. Please upgrade to ${minNodeVersion} or above.`);
|
|
628
486
|
throw new Error(`Node version ${versionMajor} is not supported. Please upgrade to ${minNodeVersion} or above.`);
|
|
629
487
|
}
|
|
630
|
-
// Parse command line
|
|
631
488
|
await this.parseCommandLine();
|
|
632
|
-
// Emit the initialize_completed event
|
|
633
489
|
this.emit('initialize_completed');
|
|
634
490
|
this.initialized = true;
|
|
635
491
|
}
|
|
636
|
-
/**
|
|
637
|
-
* Parses the command line arguments and performs the corresponding actions.
|
|
638
|
-
*
|
|
639
|
-
* @private
|
|
640
|
-
* @returns {Promise<void>} A promise that resolves when the command line arguments have been processed, or the process exits.
|
|
641
|
-
*/
|
|
642
492
|
async parseCommandLine() {
|
|
643
493
|
if (hasParameter('help')) {
|
|
644
494
|
this.log.info(`\nUsage: matterbridge [options]\n
|
|
@@ -700,19 +550,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
700
550
|
}
|
|
701
551
|
index++;
|
|
702
552
|
}
|
|
703
|
-
/*
|
|
704
|
-
const serializedRegisteredDevices = await this.nodeContext?.get<SerializedMatterbridgeEndpoint[]>('devices', []);
|
|
705
|
-
this.log.info(`│ Registered devices (${serializedRegisteredDevices?.length})`);
|
|
706
|
-
serializedRegisteredDevices?.forEach((device, index) => {
|
|
707
|
-
if (index !== serializedRegisteredDevices.length - 1) {
|
|
708
|
-
this.log.info(`├─┬─ plugin ${plg}${device.pluginName}${nf} device: ${dev}${device.deviceName}${nf} uniqueId: ${YELLOW}${device.uniqueId}${nf}`);
|
|
709
|
-
this.log.info(`│ └─ endpoint ${RED}${device.endpoint}${nf} ${typ}${device.endpointName}${nf} ${debugStringify(device.clusterServersId)}`);
|
|
710
|
-
} else {
|
|
711
|
-
this.log.info(`└─┬─ plugin ${plg}${device.pluginName}${nf} device: ${dev}${device.deviceName}${nf} uniqueId: ${YELLOW}${device.uniqueId}${nf}`);
|
|
712
|
-
this.log.info(` └─ endpoint ${RED}${device.endpoint}${nf} ${typ}${device.endpointName}${nf} ${debugStringify(device.clusterServersId)}`);
|
|
713
|
-
}
|
|
714
|
-
});
|
|
715
|
-
*/
|
|
716
553
|
this.shutdown = true;
|
|
717
554
|
return;
|
|
718
555
|
}
|
|
@@ -762,10 +599,8 @@ export class Matterbridge extends EventEmitter {
|
|
|
762
599
|
this.shutdown = true;
|
|
763
600
|
return;
|
|
764
601
|
}
|
|
765
|
-
// Initialize frontend
|
|
766
602
|
if (getIntParameter('frontend') !== 0 || getIntParameter('frontend') === undefined)
|
|
767
603
|
await this.frontend.start(getIntParameter('frontend'));
|
|
768
|
-
// Start the matter storage and create the matterbridge context
|
|
769
604
|
try {
|
|
770
605
|
await this.startMatterStorage();
|
|
771
606
|
if (this.aggregatorSerialNumber && this.aggregatorUniqueId && this.matterStorageService) {
|
|
@@ -781,21 +616,18 @@ export class Matterbridge extends EventEmitter {
|
|
|
781
616
|
this.log.fatal(`Fatal error creating matter storage: ${error instanceof Error ? error.message : error}`);
|
|
782
617
|
throw new Error(`Fatal error creating matter storage: ${error instanceof Error ? error.message : error}`);
|
|
783
618
|
}
|
|
784
|
-
// Clear the matterbridge context if the reset parameter is set
|
|
785
619
|
if (hasParameter('reset') && getParameter('reset') === undefined) {
|
|
786
620
|
this.initialized = true;
|
|
787
621
|
await this.shutdownProcessAndReset();
|
|
788
622
|
this.shutdown = true;
|
|
789
623
|
return;
|
|
790
624
|
}
|
|
791
|
-
// Clear matterbridge plugin context if the reset parameter is set
|
|
792
625
|
if (hasParameter('reset') && getParameter('reset') !== undefined) {
|
|
793
626
|
this.log.debug(`Reset plugin ${getParameter('reset')}`);
|
|
794
627
|
const plugin = this.plugins.get(getParameter('reset'));
|
|
795
628
|
if (plugin) {
|
|
796
629
|
const matterStorageManager = await this.matterStorageService?.open(plugin.name);
|
|
797
630
|
if (!matterStorageManager) {
|
|
798
|
-
/* istanbul ignore next */
|
|
799
631
|
this.log.error(`Plugin ${plg}${plugin.name}${er} storageManager not found`);
|
|
800
632
|
}
|
|
801
633
|
else {
|
|
@@ -814,42 +646,35 @@ export class Matterbridge extends EventEmitter {
|
|
|
814
646
|
this.shutdown = true;
|
|
815
647
|
return;
|
|
816
648
|
}
|
|
817
|
-
// Check in 30 seconds the latest and dev versions of matterbridge and the plugins
|
|
818
649
|
clearTimeout(this.checkUpdateTimeout);
|
|
819
650
|
this.checkUpdateTimeout = setTimeout(async () => {
|
|
820
651
|
const { checkUpdates } = await import('./update.js');
|
|
821
652
|
checkUpdates(this);
|
|
822
653
|
}, 30 * 1000).unref();
|
|
823
|
-
// Check each 12 hours the latest and dev versions of matterbridge and the plugins
|
|
824
654
|
clearInterval(this.checkUpdateInterval);
|
|
825
655
|
this.checkUpdateInterval = setInterval(async () => {
|
|
826
656
|
const { checkUpdates } = await import('./update.js');
|
|
827
657
|
checkUpdates(this);
|
|
828
658
|
}, 12 * 60 * 60 * 1000).unref();
|
|
829
|
-
// Start the matterbridge in mode test
|
|
830
659
|
if (hasParameter('test')) {
|
|
831
660
|
this.bridgeMode = 'bridge';
|
|
832
661
|
return;
|
|
833
662
|
}
|
|
834
|
-
// Start the matterbridge in mode controller
|
|
835
663
|
if (hasParameter('controller')) {
|
|
836
664
|
this.bridgeMode = 'controller';
|
|
837
665
|
await this.startController();
|
|
838
666
|
return;
|
|
839
667
|
}
|
|
840
|
-
// Check if the bridge mode is set and start matterbridge in bridge mode if not set
|
|
841
668
|
if (!hasParameter('bridge') && !hasParameter('childbridge') && (await this.nodeContext?.get('bridgeMode', '')) === '') {
|
|
842
669
|
this.log.info('Setting default matterbridge start mode to bridge');
|
|
843
670
|
await this.nodeContext?.set('bridgeMode', 'bridge');
|
|
844
671
|
}
|
|
845
|
-
// Start matterbridge in bridge mode
|
|
846
672
|
if (hasParameter('bridge') || (!hasParameter('childbridge') && (await this.nodeContext?.get('bridgeMode', '')) === 'bridge')) {
|
|
847
673
|
this.bridgeMode = 'bridge';
|
|
848
674
|
this.log.debug(`Starting matterbridge in mode ${this.bridgeMode}`);
|
|
849
675
|
await this.startBridge();
|
|
850
676
|
return;
|
|
851
677
|
}
|
|
852
|
-
// Start matterbridge in childbridge mode
|
|
853
678
|
if (hasParameter('childbridge') || (!hasParameter('bridge') && (await this.nodeContext?.get('bridgeMode', '')) === 'childbridge')) {
|
|
854
679
|
this.bridgeMode = 'childbridge';
|
|
855
680
|
this.log.debug(`Starting matterbridge in mode ${this.bridgeMode}`);
|
|
@@ -857,22 +682,10 @@ export class Matterbridge extends EventEmitter {
|
|
|
857
682
|
return;
|
|
858
683
|
}
|
|
859
684
|
}
|
|
860
|
-
/**
|
|
861
|
-
* Asynchronously loads and starts the registered plugins.
|
|
862
|
-
*
|
|
863
|
-
* This method is responsible for initializing and starting all enabled plugins.
|
|
864
|
-
* It ensures that each plugin is properly loaded and started before the bridge starts.
|
|
865
|
-
*
|
|
866
|
-
* @param {boolean} [wait] - If true, the method will wait for all plugins to be fully loaded and started before resolving.
|
|
867
|
-
* @param {boolean} [start] - If true, the method will start the plugins after loading them.
|
|
868
|
-
* @returns {Promise<void>} A promise that resolves when all plugins have been loaded and started.
|
|
869
|
-
*/
|
|
870
685
|
async startPlugins(wait = false, start = true) {
|
|
871
|
-
// Check, load and start the plugins
|
|
872
686
|
for (const plugin of this.plugins) {
|
|
873
687
|
plugin.configJson = await this.plugins.loadConfig(plugin);
|
|
874
688
|
plugin.schemaJson = await this.plugins.loadSchema(plugin);
|
|
875
|
-
// Check if the plugin is available
|
|
876
689
|
if (!(await this.plugins.resolve(plugin.path))) {
|
|
877
690
|
this.log.error(`Plugin ${plg}${plugin.name}${er} not found or not validated. Disabling it.`);
|
|
878
691
|
plugin.enabled = false;
|
|
@@ -892,16 +705,10 @@ export class Matterbridge extends EventEmitter {
|
|
|
892
705
|
if (wait)
|
|
893
706
|
await this.plugins.load(plugin, start, 'Matterbridge is starting');
|
|
894
707
|
else
|
|
895
|
-
this.plugins.load(plugin, start, 'Matterbridge is starting');
|
|
708
|
+
this.plugins.load(plugin, start, 'Matterbridge is starting');
|
|
896
709
|
}
|
|
897
710
|
this.frontend.wssSendRefreshRequired('plugins');
|
|
898
711
|
}
|
|
899
|
-
/**
|
|
900
|
-
* Registers the process handlers for uncaughtException, unhandledRejection, SIGINT and SIGTERM.
|
|
901
|
-
* - When an uncaught exception occurs, the exceptionHandler logs the error message and stack trace.
|
|
902
|
-
* - When an unhandled promise rejection occurs, the rejectionHandler logs the reason and stack trace.
|
|
903
|
-
* - When either of SIGINT and SIGTERM signals are received, the cleanup method is called with an appropriate message.
|
|
904
|
-
*/
|
|
905
712
|
registerProcessHandlers() {
|
|
906
713
|
this.log.debug(`Registering uncaughtException and unhandledRejection handlers...`);
|
|
907
714
|
process.removeAllListeners('uncaughtException');
|
|
@@ -928,9 +735,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
928
735
|
};
|
|
929
736
|
process.on('SIGTERM', this.sigtermHandler);
|
|
930
737
|
}
|
|
931
|
-
/**
|
|
932
|
-
* Deregisters the process uncaughtException, unhandledRejection, SIGINT and SIGTERM signal handlers.
|
|
933
|
-
*/
|
|
934
738
|
deregisterProcessHandlers() {
|
|
935
739
|
this.log.debug(`Deregistering uncaughtException and unhandledRejection handlers...`);
|
|
936
740
|
if (this.exceptionHandler)
|
|
@@ -947,18 +751,13 @@ export class Matterbridge extends EventEmitter {
|
|
|
947
751
|
process.off('SIGTERM', this.sigtermHandler);
|
|
948
752
|
this.sigtermHandler = undefined;
|
|
949
753
|
}
|
|
950
|
-
/**
|
|
951
|
-
* Logs the node and system information.
|
|
952
|
-
*/
|
|
953
754
|
async logNodeAndSystemInfo() {
|
|
954
|
-
// IP address information
|
|
955
755
|
const networkInterfaces = os.networkInterfaces();
|
|
956
756
|
this.systemInformation.interfaceName = '';
|
|
957
757
|
this.systemInformation.ipv4Address = '';
|
|
958
758
|
this.systemInformation.ipv6Address = '';
|
|
959
759
|
this.systemInformation.macAddress = '';
|
|
960
760
|
for (const [interfaceName, interfaceDetails] of Object.entries(networkInterfaces)) {
|
|
961
|
-
// this.log.debug(`Checking interface: '${interfaceName}' for '${this.mdnsInterface}'`);
|
|
962
761
|
if (this.mdnsInterface && interfaceName !== this.mdnsInterface)
|
|
963
762
|
continue;
|
|
964
763
|
if (!interfaceDetails) {
|
|
@@ -984,18 +783,16 @@ export class Matterbridge extends EventEmitter {
|
|
|
984
783
|
break;
|
|
985
784
|
}
|
|
986
785
|
}
|
|
987
|
-
// Node information
|
|
988
786
|
this.systemInformation.nodeVersion = process.versions.node;
|
|
989
787
|
const versionMajor = parseInt(this.systemInformation.nodeVersion.split('.')[0]);
|
|
990
788
|
const versionMinor = parseInt(this.systemInformation.nodeVersion.split('.')[1]);
|
|
991
789
|
const versionPatch = parseInt(this.systemInformation.nodeVersion.split('.')[2]);
|
|
992
|
-
// Host system information
|
|
993
790
|
this.systemInformation.hostname = os.hostname();
|
|
994
791
|
this.systemInformation.user = os.userInfo().username;
|
|
995
|
-
this.systemInformation.osType = os.type();
|
|
996
|
-
this.systemInformation.osRelease = os.release();
|
|
997
|
-
this.systemInformation.osPlatform = os.platform();
|
|
998
|
-
this.systemInformation.osArch = os.arch();
|
|
792
|
+
this.systemInformation.osType = os.type();
|
|
793
|
+
this.systemInformation.osRelease = os.release();
|
|
794
|
+
this.systemInformation.osPlatform = os.platform();
|
|
795
|
+
this.systemInformation.osArch = os.arch();
|
|
999
796
|
this.systemInformation.totalMemory = formatMemoryUsage(os.totalmem());
|
|
1000
797
|
this.systemInformation.freeMemory = formatMemoryUsage(os.freemem());
|
|
1001
798
|
this.systemInformation.systemUptime = formatOsUpTime(os.uptime());
|
|
@@ -1004,7 +801,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1004
801
|
this.systemInformation.rss = formatMemoryUsage(process.memoryUsage().rss);
|
|
1005
802
|
this.systemInformation.heapTotal = formatMemoryUsage(process.memoryUsage().heapTotal);
|
|
1006
803
|
this.systemInformation.heapUsed = formatMemoryUsage(process.memoryUsage().heapUsed);
|
|
1007
|
-
// Log the system information
|
|
1008
804
|
this.log.debug('Host System Information:');
|
|
1009
805
|
this.log.debug(`- Hostname: ${this.systemInformation.hostname}`);
|
|
1010
806
|
this.log.debug(`- User: ${this.systemInformation.user}`);
|
|
@@ -1024,17 +820,14 @@ export class Matterbridge extends EventEmitter {
|
|
|
1024
820
|
this.log.debug(`- RSS: ${this.systemInformation.rss}`);
|
|
1025
821
|
this.log.debug(`- Heap Total: ${this.systemInformation.heapTotal}`);
|
|
1026
822
|
this.log.debug(`- Heap Used: ${this.systemInformation.heapUsed}`);
|
|
1027
|
-
// Log directories
|
|
1028
823
|
this.log.debug(`Root Directory: ${this.rootDirectory}`);
|
|
1029
824
|
this.log.debug(`Home Directory: ${this.homeDirectory}`);
|
|
1030
825
|
this.log.debug(`Matterbridge Directory: ${this.matterbridgeDirectory}`);
|
|
1031
826
|
this.log.debug(`Matterbridge Plugin Directory: ${this.matterbridgePluginDirectory}`);
|
|
1032
827
|
this.log.debug(`Matterbridge Matter Certificate Directory: ${this.matterbridgeCertDirectory}`);
|
|
1033
|
-
// Global node_modules directory
|
|
1034
828
|
if (this.nodeContext)
|
|
1035
829
|
this.globalModulesDirectory = await this.nodeContext.get('globalModulesDirectory', '');
|
|
1036
830
|
if (this.globalModulesDirectory === '') {
|
|
1037
|
-
// First run of Matterbridge so the node storage is empty
|
|
1038
831
|
this.log.debug(`Getting global node_modules directory...`);
|
|
1039
832
|
try {
|
|
1040
833
|
const { getGlobalNodeModules } = await import('./utils/network.js');
|
|
@@ -1047,7 +840,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1047
840
|
}
|
|
1048
841
|
}
|
|
1049
842
|
else {
|
|
1050
|
-
// The global node_modules directory is already set in the node storage and we check if it is still valid
|
|
1051
843
|
this.log.debug(`Checking global node_modules directory: ${this.globalModulesDirectory}`);
|
|
1052
844
|
try {
|
|
1053
845
|
const { getGlobalNodeModules } = await import('./utils/network.js');
|
|
@@ -1059,37 +851,25 @@ export class Matterbridge extends EventEmitter {
|
|
|
1059
851
|
this.log.error(`Error checking global node_modules directory: ${error}`);
|
|
1060
852
|
}
|
|
1061
853
|
}
|
|
1062
|
-
// Matterbridge version
|
|
1063
854
|
this.log.debug(`Reading matterbridge package.json...`);
|
|
1064
855
|
const packageJson = JSON.parse(await fs.readFile(path.join(this.rootDirectory, 'package.json'), 'utf-8'));
|
|
1065
856
|
this.matterbridgeVersion = this.matterbridgeLatestVersion = this.matterbridgeDevVersion = packageJson.version;
|
|
1066
857
|
this.log.debug(`Matterbridge Version: ${this.matterbridgeVersion}`);
|
|
1067
|
-
// Matterbridge latest version (will be set in the checkUpdate function)
|
|
1068
858
|
if (this.nodeContext)
|
|
1069
859
|
this.matterbridgeLatestVersion = await this.nodeContext.get('matterbridgeLatestVersion', this.matterbridgeVersion);
|
|
1070
860
|
this.log.debug(`Matterbridge Latest Version: ${this.matterbridgeLatestVersion}`);
|
|
1071
|
-
// Matterbridge dev version (will be set in the checkUpdate function)
|
|
1072
861
|
if (this.nodeContext)
|
|
1073
862
|
this.matterbridgeDevVersion = await this.nodeContext.get('matterbridgeDevVersion', this.matterbridgeVersion);
|
|
1074
863
|
this.log.debug(`Matterbridge Dev Version: ${this.matterbridgeDevVersion}`);
|
|
1075
|
-
// Frontend version
|
|
1076
864
|
this.log.debug(`Reading frontend package.json...`);
|
|
1077
865
|
const frontendPackageJson = JSON.parse(await fs.readFile(path.join(this.rootDirectory, 'frontend/package.json'), 'utf8'));
|
|
1078
866
|
this.frontendVersion = frontendPackageJson.version;
|
|
1079
867
|
this.log.debug(`Frontend version ${CYAN}${this.frontendVersion}${db}`);
|
|
1080
|
-
// Current working directory
|
|
1081
868
|
const currentDir = process.cwd();
|
|
1082
869
|
this.log.debug(`Current Working Directory: ${currentDir}`);
|
|
1083
|
-
// Command line arguments (excluding 'node' and the script name)
|
|
1084
870
|
const cmdArgs = process.argv.slice(2).join(' ');
|
|
1085
871
|
this.log.debug(`Command Line Arguments: ${cmdArgs}`);
|
|
1086
872
|
}
|
|
1087
|
-
/**
|
|
1088
|
-
* Set the logger logLevel for the Matterbridge classes and call onChangeLoggerLevel() for each plugin.
|
|
1089
|
-
*
|
|
1090
|
-
* @param {LogLevel} logLevel The logger logLevel to set.
|
|
1091
|
-
* @returns {Promise<void>} A promise that resolves when the logLevel has been set.
|
|
1092
|
-
*/
|
|
1093
873
|
async setLogLevel(logLevel) {
|
|
1094
874
|
this.log.logLevel = logLevel;
|
|
1095
875
|
this.frontend.logLevel = logLevel;
|
|
@@ -1099,86 +879,57 @@ export class Matterbridge extends EventEmitter {
|
|
|
1099
879
|
for (const plugin of this.plugins) {
|
|
1100
880
|
if (!plugin.platform || !plugin.platform.log || !plugin.platform.config)
|
|
1101
881
|
continue;
|
|
1102
|
-
plugin.platform.log.logLevel = plugin.platform.config.debug === true ? "debug"
|
|
1103
|
-
await plugin.platform.onChangeLoggerLevel(plugin.platform.config.debug === true ? "debug"
|
|
1104
|
-
}
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
callbackLogLevel = "debug" /* LogLevel.DEBUG */;
|
|
882
|
+
plugin.platform.log.logLevel = plugin.platform.config.debug === true ? "debug" : logLevel;
|
|
883
|
+
await plugin.platform.onChangeLoggerLevel(plugin.platform.config.debug === true ? "debug" : logLevel);
|
|
884
|
+
}
|
|
885
|
+
let callbackLogLevel = "notice";
|
|
886
|
+
if (logLevel === "info" || Logger.level === MatterLogLevel.INFO)
|
|
887
|
+
callbackLogLevel = "info";
|
|
888
|
+
if (logLevel === "debug" || Logger.level === MatterLogLevel.DEBUG)
|
|
889
|
+
callbackLogLevel = "debug";
|
|
1111
890
|
AnsiLogger.setGlobalCallbackLevel(callbackLogLevel);
|
|
1112
891
|
this.log.debug(`WebSocketServer logger global callback set to ${callbackLogLevel}`);
|
|
1113
892
|
}
|
|
1114
|
-
/**
|
|
1115
|
-
* Get the current logger logLevel.
|
|
1116
|
-
*
|
|
1117
|
-
* @returns {LogLevel} The current logger logLevel.
|
|
1118
|
-
*/
|
|
1119
893
|
getLogLevel() {
|
|
1120
894
|
return this.log.logLevel;
|
|
1121
895
|
}
|
|
1122
|
-
/**
|
|
1123
|
-
* Creates a MatterLogger function to show the matter.js log messages in AnsiLogger (for the frontend).
|
|
1124
|
-
* It also logs to file (matter.log) if fileLogger is true.
|
|
1125
|
-
*
|
|
1126
|
-
* @param {boolean} fileLogger - Whether to log to file or not.
|
|
1127
|
-
* @returns {Function} The MatterLogger function. \x1b[35m for violet \x1b[34m is blue
|
|
1128
|
-
*/
|
|
1129
896
|
createDestinationMatterLogger(fileLogger) {
|
|
1130
|
-
this.matterLog.logNameColor = '\x1b[34m';
|
|
897
|
+
this.matterLog.logNameColor = '\x1b[34m';
|
|
1131
898
|
if (fileLogger) {
|
|
1132
899
|
this.matterLog.logFilePath = path.join(this.matterbridgeDirectory, MATTER_LOGGER_FILE);
|
|
1133
900
|
}
|
|
1134
901
|
return (text, message) => {
|
|
1135
|
-
// 2024-08-21 08:55:19.488 DEBUG InteractionMessenger Sending DataReport chunk with 28 attributes and 0 events: 1004 bytes
|
|
1136
902
|
const logger = text.slice(44, 44 + 20).trim();
|
|
1137
903
|
const msg = text.slice(65);
|
|
1138
904
|
this.matterLog.logName = logger;
|
|
1139
905
|
switch (message.level) {
|
|
1140
906
|
case MatterLogLevel.DEBUG:
|
|
1141
|
-
this.matterLog.log("debug"
|
|
907
|
+
this.matterLog.log("debug", msg);
|
|
1142
908
|
break;
|
|
1143
909
|
case MatterLogLevel.INFO:
|
|
1144
|
-
this.matterLog.log("info"
|
|
910
|
+
this.matterLog.log("info", msg);
|
|
1145
911
|
break;
|
|
1146
912
|
case MatterLogLevel.NOTICE:
|
|
1147
|
-
this.matterLog.log("notice"
|
|
913
|
+
this.matterLog.log("notice", msg);
|
|
1148
914
|
break;
|
|
1149
915
|
case MatterLogLevel.WARN:
|
|
1150
|
-
this.matterLog.log("warn"
|
|
916
|
+
this.matterLog.log("warn", msg);
|
|
1151
917
|
break;
|
|
1152
918
|
case MatterLogLevel.ERROR:
|
|
1153
|
-
this.matterLog.log("error"
|
|
919
|
+
this.matterLog.log("error", msg);
|
|
1154
920
|
break;
|
|
1155
921
|
case MatterLogLevel.FATAL:
|
|
1156
|
-
this.matterLog.log("fatal"
|
|
922
|
+
this.matterLog.log("fatal", msg);
|
|
1157
923
|
break;
|
|
1158
924
|
}
|
|
1159
925
|
};
|
|
1160
926
|
}
|
|
1161
|
-
/**
|
|
1162
|
-
* Restarts the process by exiting the current instance and loading a new instance (/api/restart).
|
|
1163
|
-
*
|
|
1164
|
-
* @returns {Promise<void>} A promise that resolves when the restart is completed.
|
|
1165
|
-
*/
|
|
1166
927
|
async restartProcess() {
|
|
1167
928
|
await this.cleanup('restarting...', true);
|
|
1168
929
|
}
|
|
1169
|
-
/**
|
|
1170
|
-
* Shut down the process (/api/shutdown).
|
|
1171
|
-
*
|
|
1172
|
-
* @returns {Promise<void>} A promise that resolves when the shutdown is completed.
|
|
1173
|
-
*/
|
|
1174
930
|
async shutdownProcess() {
|
|
1175
931
|
await this.cleanup('shutting down...', false);
|
|
1176
932
|
}
|
|
1177
|
-
/**
|
|
1178
|
-
* Update matterbridge and shut down the process (virtual device 'Update Matterbridge').
|
|
1179
|
-
*
|
|
1180
|
-
* @returns {Promise<void>} A promise that resolves when the update is completed.
|
|
1181
|
-
*/
|
|
1182
933
|
async updateProcess() {
|
|
1183
934
|
this.log.info('Updating matterbridge...');
|
|
1184
935
|
try {
|
|
@@ -1192,13 +943,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1192
943
|
this.frontend.wssSendRestartRequired();
|
|
1193
944
|
await this.cleanup('updating...', false);
|
|
1194
945
|
}
|
|
1195
|
-
/**
|
|
1196
|
-
* Unregister all devices and shut down the process (/api/unregister).
|
|
1197
|
-
*
|
|
1198
|
-
* @param {number} [timeout] - The timeout duration to wait for the message exchange to complete in milliseconds. Default is 1000.
|
|
1199
|
-
*
|
|
1200
|
-
* @returns {Promise<void>} A promise that resolves when the cleanup is completed.
|
|
1201
|
-
*/
|
|
1202
946
|
async unregisterAndShutdownProcess(timeout = 1000) {
|
|
1203
947
|
this.log.info('Unregistering all devices and shutting down...');
|
|
1204
948
|
for (const plugin of this.plugins.array()) {
|
|
@@ -1210,71 +954,46 @@ export class Matterbridge extends EventEmitter {
|
|
|
1210
954
|
await this.removeAllBridgedEndpoints(plugin.name, 100);
|
|
1211
955
|
}
|
|
1212
956
|
this.log.debug('Waiting for the MessageExchange to finish...');
|
|
1213
|
-
await wait(timeout);
|
|
957
|
+
await wait(timeout);
|
|
1214
958
|
this.log.debug('Cleaning up and shutting down...');
|
|
1215
959
|
await this.cleanup('unregistered all devices and shutting down...', false, timeout);
|
|
1216
960
|
}
|
|
1217
|
-
/**
|
|
1218
|
-
* Reset commissioning and shut down the process (/api/reset).
|
|
1219
|
-
*
|
|
1220
|
-
* @returns {Promise<void>} A promise that resolves when the cleanup is completed.
|
|
1221
|
-
*/
|
|
1222
961
|
async shutdownProcessAndReset() {
|
|
1223
962
|
await this.cleanup('shutting down with reset...', false);
|
|
1224
963
|
}
|
|
1225
|
-
/**
|
|
1226
|
-
* Factory reset and shut down the process (/api/factory-reset).
|
|
1227
|
-
*
|
|
1228
|
-
* @returns {Promise<void>} A promise that resolves when the cleanup is completed.
|
|
1229
|
-
*/
|
|
1230
964
|
async shutdownProcessAndFactoryReset() {
|
|
1231
965
|
await this.cleanup('shutting down with factory reset...', false);
|
|
1232
966
|
}
|
|
1233
|
-
/**
|
|
1234
|
-
* Cleans up the Matterbridge instance.
|
|
1235
|
-
*
|
|
1236
|
-
* @param {string} message - The cleanup message.
|
|
1237
|
-
* @param {boolean} [restart] - Indicates whether to restart the instance after cleanup. Default is `false`.
|
|
1238
|
-
* @param {number} [timeout] - The timeout duration to wait for the message exchange to complete in milliseconds. Default is 1000.
|
|
1239
|
-
*
|
|
1240
|
-
* @returns {Promise<void>} A promise that resolves when the cleanup is completed.
|
|
1241
|
-
*/
|
|
1242
967
|
async cleanup(message, restart = false, timeout = 1000) {
|
|
1243
968
|
if (this.initialized && !this.hasCleanupStarted) {
|
|
1244
969
|
this.emit('cleanup_started');
|
|
1245
970
|
this.hasCleanupStarted = true;
|
|
1246
971
|
this.log.info(message);
|
|
1247
|
-
// Clear the start matter interval
|
|
1248
972
|
if (this.startMatterInterval) {
|
|
1249
973
|
clearInterval(this.startMatterInterval);
|
|
1250
974
|
this.startMatterInterval = undefined;
|
|
1251
975
|
this.log.debug('Start matter interval cleared');
|
|
1252
976
|
}
|
|
1253
|
-
// Clear the check update timeout
|
|
1254
977
|
if (this.checkUpdateTimeout) {
|
|
1255
978
|
clearTimeout(this.checkUpdateTimeout);
|
|
1256
979
|
this.checkUpdateTimeout = undefined;
|
|
1257
980
|
this.log.debug('Check update timeout cleared');
|
|
1258
981
|
}
|
|
1259
|
-
// Clear the check update interval
|
|
1260
982
|
if (this.checkUpdateInterval) {
|
|
1261
983
|
clearInterval(this.checkUpdateInterval);
|
|
1262
984
|
this.checkUpdateInterval = undefined;
|
|
1263
985
|
this.log.debug('Check update interval cleared');
|
|
1264
986
|
}
|
|
1265
|
-
// Clear the configure timeout
|
|
1266
987
|
if (this.configureTimeout) {
|
|
1267
988
|
clearTimeout(this.configureTimeout);
|
|
1268
989
|
this.configureTimeout = undefined;
|
|
1269
990
|
this.log.debug('Matterbridge configure timeout cleared');
|
|
1270
991
|
}
|
|
1271
|
-
// Clear the reachability timeout
|
|
1272
992
|
if (this.reachabilityTimeout) {
|
|
1273
993
|
clearTimeout(this.reachabilityTimeout);
|
|
1274
994
|
this.reachabilityTimeout = undefined;
|
|
1275
995
|
this.log.debug('Matterbridge reachability timeout cleared');
|
|
1276
996
|
}
|
|
1277
|
-
// Call the shutdown method of each plugin and clear the plugins reachability timeout
|
|
1278
997
|
for (const plugin of this.plugins) {
|
|
1279
998
|
if (!plugin.enabled || plugin.error)
|
|
1280
999
|
continue;
|
|
@@ -1285,7 +1004,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1285
1004
|
this.log.debug(`Plugin ${plg}${plugin.name}${db} reachability timeout cleared`);
|
|
1286
1005
|
}
|
|
1287
1006
|
}
|
|
1288
|
-
// Stop matter server nodes
|
|
1289
1007
|
this.log.notice(`Stopping matter server nodes in ${this.bridgeMode} mode...`);
|
|
1290
1008
|
if (timeout > 0) {
|
|
1291
1009
|
this.log.debug(`Waiting ${timeout}ms for the MessageExchange to finish...`);
|
|
@@ -1312,7 +1030,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1312
1030
|
}
|
|
1313
1031
|
}
|
|
1314
1032
|
this.log.notice('Stopped matter server nodes');
|
|
1315
|
-
// Matter commisioning reset
|
|
1316
1033
|
if (message === 'shutting down with reset...') {
|
|
1317
1034
|
this.log.info('Resetting Matterbridge commissioning information...');
|
|
1318
1035
|
await this.matterStorageManager?.createContext('events')?.clearAll();
|
|
@@ -1322,7 +1039,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1322
1039
|
await this.matterbridgeContext?.clearAll();
|
|
1323
1040
|
this.log.info('Matter storage reset done! Remove the bridge from the controller.');
|
|
1324
1041
|
}
|
|
1325
|
-
// Unregister all devices
|
|
1326
1042
|
if (message === 'unregistered all devices and shutting down...') {
|
|
1327
1043
|
if (this.bridgeMode === 'bridge') {
|
|
1328
1044
|
await this.matterStorageManager?.createContext('root')?.createContext('parts')?.createContext('Matterbridge')?.createContext('parts')?.clearAll();
|
|
@@ -1340,33 +1056,15 @@ export class Matterbridge extends EventEmitter {
|
|
|
1340
1056
|
}
|
|
1341
1057
|
this.log.info('Matter storage reset done!');
|
|
1342
1058
|
}
|
|
1343
|
-
// Stop matter storage
|
|
1344
1059
|
await this.stopMatterStorage();
|
|
1345
|
-
// Stop the frontend
|
|
1346
1060
|
await this.frontend.stop();
|
|
1347
1061
|
this.frontend.destroy();
|
|
1348
|
-
// Close PluginManager and DeviceManager
|
|
1349
1062
|
this.plugins.destroy();
|
|
1350
1063
|
this.devices.destroy();
|
|
1351
|
-
// Close the matterbridge node storage and context
|
|
1352
1064
|
if (this.nodeStorage && this.nodeContext) {
|
|
1353
|
-
/*
|
|
1354
|
-
TODO: Implement serialization of registered devices in edge mode
|
|
1355
|
-
this.log.info('Saving registered devices...');
|
|
1356
|
-
const serializedRegisteredDevices: SerializedMatterbridgeEndpoint[] = [];
|
|
1357
|
-
this.devices.forEach(async (device) => {
|
|
1358
|
-
const serializedMatterbridgeDevice = MatterbridgeEndpoint.serialize(device);
|
|
1359
|
-
// this.log.info(`- ${serializedMatterbridgeDevice.deviceName}${rs}\n`, serializedMatterbridgeDevice);
|
|
1360
|
-
if (serializedMatterbridgeDevice) serializedRegisteredDevices.push(serializedMatterbridgeDevice);
|
|
1361
|
-
});
|
|
1362
|
-
await this.nodeContext.set<SerializedMatterbridgeEndpoint[]>('devices', serializedRegisteredDevices);
|
|
1363
|
-
this.log.info(`Saved registered devices (${serializedRegisteredDevices?.length})`);
|
|
1364
|
-
*/
|
|
1365
|
-
// Clear nodeContext and nodeStorage (they just need 1000ms to write the data to disk)
|
|
1366
1065
|
this.log.debug(`Closing node storage context for ${plg}Matterbridge${db}...`);
|
|
1367
1066
|
await this.nodeContext.close();
|
|
1368
1067
|
this.nodeContext = undefined;
|
|
1369
|
-
// Clear nodeContext for each plugin (they just need 1000ms to write the data to disk)
|
|
1370
1068
|
for (const plugin of this.plugins) {
|
|
1371
1069
|
if (plugin.nodeContext) {
|
|
1372
1070
|
this.log.debug(`Closing node storage context for plugin ${plg}${plugin.name}${db}...`);
|
|
@@ -1383,10 +1081,8 @@ export class Matterbridge extends EventEmitter {
|
|
|
1383
1081
|
}
|
|
1384
1082
|
this.plugins.clear();
|
|
1385
1083
|
this.devices.clear();
|
|
1386
|
-
// Factory reset
|
|
1387
1084
|
if (message === 'shutting down with factory reset...') {
|
|
1388
1085
|
try {
|
|
1389
|
-
// Delete matter storage directory with its subdirectories and backup
|
|
1390
1086
|
const dir = path.join(this.matterbridgeDirectory, MATTER_STORAGE_NAME);
|
|
1391
1087
|
this.log.info(`Removing matter storage directory: ${dir}`);
|
|
1392
1088
|
await fs.rm(dir, { recursive: true });
|
|
@@ -1395,13 +1091,11 @@ export class Matterbridge extends EventEmitter {
|
|
|
1395
1091
|
await fs.rm(backup, { recursive: true });
|
|
1396
1092
|
}
|
|
1397
1093
|
catch (error) {
|
|
1398
|
-
// istanbul ignore next if
|
|
1399
1094
|
if (error instanceof Error && error.code !== 'ENOENT') {
|
|
1400
1095
|
this.log.error(`Error removing matter storage directory: ${error}`);
|
|
1401
1096
|
}
|
|
1402
1097
|
}
|
|
1403
1098
|
try {
|
|
1404
|
-
// Delete matterbridge storage directory with its subdirectories and backup
|
|
1405
1099
|
const dir = path.join(this.matterbridgeDirectory, NODE_STORAGE_DIR);
|
|
1406
1100
|
this.log.info(`Removing matterbridge storage directory: ${dir}`);
|
|
1407
1101
|
await fs.rm(dir, { recursive: true });
|
|
@@ -1410,20 +1104,18 @@ export class Matterbridge extends EventEmitter {
|
|
|
1410
1104
|
await fs.rm(backup, { recursive: true });
|
|
1411
1105
|
}
|
|
1412
1106
|
catch (error) {
|
|
1413
|
-
// istanbul ignore next if
|
|
1414
1107
|
if (error instanceof Error && error.code !== 'ENOENT') {
|
|
1415
1108
|
this.log.error(`Error removing matterbridge storage directory: ${error}`);
|
|
1416
1109
|
}
|
|
1417
1110
|
}
|
|
1418
1111
|
this.log.info('Factory reset done! Remove all paired fabrics from the controllers.');
|
|
1419
1112
|
}
|
|
1420
|
-
// Deregisters the process handlers
|
|
1421
1113
|
this.deregisterProcessHandlers();
|
|
1422
1114
|
if (restart) {
|
|
1423
1115
|
if (message === 'updating...') {
|
|
1424
1116
|
this.log.info('Cleanup completed. Updating...');
|
|
1425
1117
|
Matterbridge.instance = undefined;
|
|
1426
|
-
this.emit('update');
|
|
1118
|
+
this.emit('update');
|
|
1427
1119
|
}
|
|
1428
1120
|
else if (message === 'restarting...') {
|
|
1429
1121
|
this.log.info('Cleanup completed. Restarting...');
|
|
@@ -1447,14 +1139,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
1447
1139
|
this.log.debug('Cleanup already started...');
|
|
1448
1140
|
}
|
|
1449
1141
|
}
|
|
1450
|
-
/**
|
|
1451
|
-
* Starts the Matterbridge in bridge mode.
|
|
1452
|
-
*
|
|
1453
|
-
* @private
|
|
1454
|
-
* @returns {Promise<void>} A promise that resolves when the Matterbridge is started.
|
|
1455
|
-
*/
|
|
1456
1142
|
async startBridge() {
|
|
1457
|
-
// Plugins are configured by a timer when matter server is started and plugin.configured is set to true
|
|
1458
1143
|
if (!this.matterStorageManager)
|
|
1459
1144
|
throw new Error('No storage manager initialized');
|
|
1460
1145
|
if (!this.matterbridgeContext)
|
|
@@ -1493,16 +1178,13 @@ export class Matterbridge extends EventEmitter {
|
|
|
1493
1178
|
clearInterval(this.startMatterInterval);
|
|
1494
1179
|
this.startMatterInterval = undefined;
|
|
1495
1180
|
this.log.debug('Cleared startMatterInterval interval for Matterbridge');
|
|
1496
|
-
|
|
1497
|
-
this.startServerNode(this.serverNode); // We don't await this, because the server node is started in the background
|
|
1498
|
-
// Start the Matter server node of single devices in mode 'server'
|
|
1181
|
+
this.startServerNode(this.serverNode);
|
|
1499
1182
|
for (const device of this.devices.array()) {
|
|
1500
1183
|
if (device.mode === 'server' && device.serverNode) {
|
|
1501
1184
|
this.log.debug(`Starting server node for device ${dev}${device.deviceName}${db} in server mode...`);
|
|
1502
|
-
this.startServerNode(device.serverNode);
|
|
1185
|
+
this.startServerNode(device.serverNode);
|
|
1503
1186
|
}
|
|
1504
1187
|
}
|
|
1505
|
-
// Configure the plugins
|
|
1506
1188
|
this.configureTimeout = setTimeout(async () => {
|
|
1507
1189
|
for (const plugin of this.plugins.array()) {
|
|
1508
1190
|
if (!plugin.enabled || !plugin.loaded || !plugin.started || plugin.error)
|
|
@@ -1520,40 +1202,28 @@ export class Matterbridge extends EventEmitter {
|
|
|
1520
1202
|
}
|
|
1521
1203
|
this.frontend.wssSendRefreshRequired('plugins');
|
|
1522
1204
|
}, 30 * 1000).unref();
|
|
1523
|
-
// Setting reachability to true
|
|
1524
1205
|
this.reachabilityTimeout = setTimeout(() => {
|
|
1525
1206
|
this.log.info(`Setting reachability to true for ${plg}Matterbridge${db}`);
|
|
1526
1207
|
if (this.aggregatorNode)
|
|
1527
1208
|
this.setAggregatorReachability(this.aggregatorNode, true);
|
|
1528
1209
|
}, 60 * 1000).unref();
|
|
1529
|
-
// Logger.get('LogServerNode').info(this.serverNode);
|
|
1530
1210
|
this.emit('bridge_started');
|
|
1531
1211
|
this.log.notice('Matterbridge bridge started successfully');
|
|
1532
1212
|
this.frontend.wssSendRefreshRequired('settings');
|
|
1533
1213
|
this.frontend.wssSendRefreshRequired('plugins');
|
|
1534
1214
|
}, this.startMatterIntervalMs);
|
|
1535
1215
|
}
|
|
1536
|
-
/**
|
|
1537
|
-
* Starts the Matterbridge in childbridge mode.
|
|
1538
|
-
*
|
|
1539
|
-
* @param {number} [delay] - The delay before starting the childbridge. Default is 1000 milliseconds.
|
|
1540
|
-
*
|
|
1541
|
-
* @returns {Promise<void>} A promise that resolves when the Matterbridge is started.
|
|
1542
|
-
*/
|
|
1543
1216
|
async startChildbridge(delay = 1000) {
|
|
1544
1217
|
if (!this.matterStorageManager)
|
|
1545
1218
|
throw new Error('No storage manager initialized');
|
|
1546
|
-
// Load with await all plugins but don't start them. We get the platform.type to pre-create server nodes for DynamicPlatform plugins
|
|
1547
1219
|
this.log.debug('Loading all plugins in childbridge mode...');
|
|
1548
1220
|
await this.startPlugins(true, false);
|
|
1549
|
-
// Create server nodes for DynamicPlatform plugins and start all plugins in the background
|
|
1550
1221
|
this.log.debug('Creating server nodes for DynamicPlatform plugins and starting all plugins in childbridge mode...');
|
|
1551
1222
|
for (const plugin of this.plugins.array().filter((p) => p.enabled && !p.error)) {
|
|
1552
1223
|
if (plugin.type === 'DynamicPlatform')
|
|
1553
1224
|
await this.createDynamicPlugin(plugin);
|
|
1554
|
-
this.plugins.start(plugin, 'Matterbridge is starting');
|
|
1225
|
+
this.plugins.start(plugin, 'Matterbridge is starting');
|
|
1555
1226
|
}
|
|
1556
|
-
// Start the Matterbridge in childbridge mode when all plugins are loaded and started
|
|
1557
1227
|
this.log.debug('Starting start matter interval in childbridge mode...');
|
|
1558
1228
|
let failCount = 0;
|
|
1559
1229
|
this.startMatterInterval = setInterval(async () => {
|
|
@@ -1587,9 +1257,8 @@ export class Matterbridge extends EventEmitter {
|
|
|
1587
1257
|
clearInterval(this.startMatterInterval);
|
|
1588
1258
|
this.startMatterInterval = undefined;
|
|
1589
1259
|
if (delay > 0)
|
|
1590
|
-
await wait(delay);
|
|
1260
|
+
await wait(delay);
|
|
1591
1261
|
this.log.debug('Cleared startMatterInterval interval in childbridge mode');
|
|
1592
|
-
// Configure the plugins
|
|
1593
1262
|
this.configureTimeout = setTimeout(async () => {
|
|
1594
1263
|
for (const plugin of this.plugins.array()) {
|
|
1595
1264
|
if (!plugin.enabled || !plugin.loaded || !plugin.started || plugin.error)
|
|
@@ -1614,7 +1283,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1614
1283
|
this.log.error(`Plugin ${plg}${plugin.name}${er} didn't register any devices to Matterbridge. Verify the plugin configuration.`);
|
|
1615
1284
|
continue;
|
|
1616
1285
|
}
|
|
1617
|
-
// istanbul ignore next if cause is just a safety check
|
|
1618
1286
|
if (!plugin.serverNode) {
|
|
1619
1287
|
this.log.error(`Server node not found for plugin ${plg}${plugin.name}${er}`);
|
|
1620
1288
|
continue;
|
|
@@ -1627,252 +1295,28 @@ export class Matterbridge extends EventEmitter {
|
|
|
1627
1295
|
this.log.error(`Node storage context not found for plugin ${plg}${plugin.name}${er}`);
|
|
1628
1296
|
continue;
|
|
1629
1297
|
}
|
|
1630
|
-
|
|
1631
|
-
this.startServerNode(plugin.serverNode); // We don't await this, because the server node is started in the background
|
|
1632
|
-
// Setting reachability to true
|
|
1298
|
+
this.startServerNode(plugin.serverNode);
|
|
1633
1299
|
plugin.reachabilityTimeout = setTimeout(() => {
|
|
1634
1300
|
this.log.info(`Setting reachability to true for ${plg}${plugin.name}${nf}`);
|
|
1635
1301
|
if (plugin.type === 'DynamicPlatform' && plugin.aggregatorNode)
|
|
1636
1302
|
this.setAggregatorReachability(plugin.aggregatorNode, true);
|
|
1637
1303
|
}, 60 * 1000).unref();
|
|
1638
1304
|
}
|
|
1639
|
-
// Start the Matter server node of single devices in mode 'server'
|
|
1640
1305
|
for (const device of this.devices.array()) {
|
|
1641
1306
|
if (device.mode === 'server' && device.serverNode) {
|
|
1642
1307
|
this.log.debug(`Starting server node for device ${dev}${device.deviceName}${db} in server mode...`);
|
|
1643
|
-
this.startServerNode(device.serverNode);
|
|
1308
|
+
this.startServerNode(device.serverNode);
|
|
1644
1309
|
}
|
|
1645
1310
|
}
|
|
1646
|
-
// Logger.get('LogServerNode').info(this.serverNode);
|
|
1647
1311
|
this.emit('childbridge_started');
|
|
1648
1312
|
this.log.notice('Matterbridge childbridge started successfully');
|
|
1649
1313
|
this.frontend.wssSendRefreshRequired('settings');
|
|
1650
1314
|
this.frontend.wssSendRefreshRequired('plugins');
|
|
1651
1315
|
}, this.startMatterIntervalMs);
|
|
1652
1316
|
}
|
|
1653
|
-
/**
|
|
1654
|
-
* Starts the Matterbridge controller.
|
|
1655
|
-
*
|
|
1656
|
-
* @private
|
|
1657
|
-
* @returns {Promise<void>} A promise that resolves when the Matterbridge is started.
|
|
1658
|
-
*/
|
|
1659
1317
|
async startController() {
|
|
1660
|
-
/*
|
|
1661
|
-
if (!this.matterStorageManager) {
|
|
1662
|
-
this.log.error('No storage manager initialized');
|
|
1663
|
-
await this.cleanup('No storage manager initialized');
|
|
1664
|
-
return;
|
|
1665
|
-
}
|
|
1666
|
-
this.log.info('Creating context: mattercontrollerContext');
|
|
1667
|
-
this.controllerContext = this.matterStorageManager.createContext('mattercontrollerContext');
|
|
1668
|
-
if (!this.controllerContext) {
|
|
1669
|
-
this.log.error('No storage context mattercontrollerContext initialized');
|
|
1670
|
-
await this.cleanup('No storage context mattercontrollerContext initialized');
|
|
1671
|
-
return;
|
|
1672
|
-
}
|
|
1673
|
-
|
|
1674
|
-
this.log.debug('Starting matterbridge in mode', this.bridgeMode);
|
|
1675
|
-
this.matterServer = await this.createMatterServer(this.storageManager);
|
|
1676
|
-
this.log.info('Creating matter commissioning controller');
|
|
1677
|
-
this.commissioningController = new CommissioningController({
|
|
1678
|
-
autoConnect: false,
|
|
1679
|
-
});
|
|
1680
|
-
this.log.info('Adding matter commissioning controller to matter server');
|
|
1681
|
-
await this.matterServer.addCommissioningController(this.commissioningController);
|
|
1682
|
-
|
|
1683
|
-
this.log.info('Starting matter server');
|
|
1684
|
-
await this.matterServer.start();
|
|
1685
|
-
this.log.info('Matter server started');
|
|
1686
|
-
const commissioningOptions: ControllerCommissioningFlowOptions = {
|
|
1687
|
-
regulatoryLocation: GeneralCommissioning.RegulatoryLocationType.IndoorOutdoor,
|
|
1688
|
-
regulatoryCountryCode: 'XX',
|
|
1689
|
-
};
|
|
1690
|
-
const commissioningController = new CommissioningController({
|
|
1691
|
-
environment: {
|
|
1692
|
-
environment,
|
|
1693
|
-
id: uniqueId,
|
|
1694
|
-
},
|
|
1695
|
-
autoConnect: false, // Do not auto connect to the commissioned nodes
|
|
1696
|
-
adminFabricLabel,
|
|
1697
|
-
});
|
|
1698
|
-
|
|
1699
|
-
if (hasParameter('pairingcode')) {
|
|
1700
|
-
this.log.info('Pairing device with pairingcode:', getParameter('pairingcode'));
|
|
1701
|
-
const pairingCode = getParameter('pairingcode');
|
|
1702
|
-
const ip = this.controllerContext.has('ip') ? this.controllerContext.get<string>('ip') : undefined;
|
|
1703
|
-
const port = this.controllerContext.has('port') ? this.controllerContext.get<number>('port') : undefined;
|
|
1704
|
-
|
|
1705
|
-
let longDiscriminator, setupPin, shortDiscriminator;
|
|
1706
|
-
if (pairingCode !== undefined) {
|
|
1707
|
-
const pairingCodeCodec = ManualPairingCodeCodec.decode(pairingCode);
|
|
1708
|
-
shortDiscriminator = pairingCodeCodec.shortDiscriminator;
|
|
1709
|
-
longDiscriminator = undefined;
|
|
1710
|
-
setupPin = pairingCodeCodec.passcode;
|
|
1711
|
-
this.log.info(`Data extracted from pairing code: ${Logger.toJSON(pairingCodeCodec)}`);
|
|
1712
|
-
} else {
|
|
1713
|
-
longDiscriminator = await this.controllerContext.get('longDiscriminator', 3840);
|
|
1714
|
-
if (longDiscriminator > 4095) throw new Error('Discriminator value must be less than 4096');
|
|
1715
|
-
setupPin = this.controllerContext.get('pin', 20202021);
|
|
1716
|
-
}
|
|
1717
|
-
if ((shortDiscriminator === undefined && longDiscriminator === undefined) || setupPin === undefined) {
|
|
1718
|
-
throw new Error('Please specify the longDiscriminator of the device to commission with -longDiscriminator or provide a valid passcode with -passcode');
|
|
1719
|
-
}
|
|
1720
|
-
|
|
1721
|
-
const options = {
|
|
1722
|
-
commissioning: commissioningOptions,
|
|
1723
|
-
discovery: {
|
|
1724
|
-
knownAddress: ip !== undefined && port !== undefined ? { ip, port, type: 'udp' } : undefined,
|
|
1725
|
-
identifierData: longDiscriminator !== undefined ? { longDiscriminator } : shortDiscriminator !== undefined ? { shortDiscriminator } : {},
|
|
1726
|
-
},
|
|
1727
|
-
passcode: setupPin,
|
|
1728
|
-
} as NodeCommissioningOptions;
|
|
1729
|
-
this.log.info('Commissioning with options:', options);
|
|
1730
|
-
const nodeId = await this.commissioningController.commissionNode(options);
|
|
1731
|
-
this.log.info(`Commissioning successfully done with nodeId: ${nodeId}`);
|
|
1732
|
-
this.log.info('ActiveSessionInformation:', this.commissioningController.getActiveSessionInformation());
|
|
1733
|
-
} // (hasParameter('pairingcode'))
|
|
1734
|
-
|
|
1735
|
-
if (hasParameter('unpairall')) {
|
|
1736
|
-
this.log.info('***Commissioning controller unpairing all nodes...');
|
|
1737
|
-
const nodeIds = this.commissioningController.getCommissionedNodes();
|
|
1738
|
-
for (const nodeId of nodeIds) {
|
|
1739
|
-
this.log.info('***Commissioning controller unpairing node:', nodeId);
|
|
1740
|
-
await this.commissioningController.removeNode(nodeId);
|
|
1741
|
-
}
|
|
1742
|
-
return;
|
|
1743
|
-
}
|
|
1744
|
-
|
|
1745
|
-
if (hasParameter('discover')) {
|
|
1746
|
-
// const discover = await this.commissioningController.discoverCommissionableDevices({ productId: 0x8000, deviceType: 0xfff1 });
|
|
1747
|
-
// console.log(discover);
|
|
1748
|
-
}
|
|
1749
|
-
|
|
1750
|
-
if (!this.commissioningController.isCommissioned()) {
|
|
1751
|
-
this.log.info('***Commissioning controller is not commissioned: use matterbridge -controller -pairingcode [pairingcode] to commission a device');
|
|
1752
|
-
return;
|
|
1753
|
-
}
|
|
1754
|
-
|
|
1755
|
-
const nodeIds = this.commissioningController.getCommissionedNodes();
|
|
1756
|
-
this.log.info(`***Commissioning controller is commissioned ${this.commissioningController.isCommissioned()} and has ${nodeIds.length} nodes commisioned: `);
|
|
1757
|
-
for (const nodeId of nodeIds) {
|
|
1758
|
-
this.log.info(`***Connecting to commissioned node: ${nodeId}`);
|
|
1759
|
-
|
|
1760
|
-
const node = await this.commissioningController.connectNode(nodeId, {
|
|
1761
|
-
autoSubscribe: false,
|
|
1762
|
-
attributeChangedCallback: (peerNodeId, { path: { nodeId, clusterId, endpointId, attributeName }, value }) =>
|
|
1763
|
-
this.log.info(`***Commissioning controller attributeChangedCallback ${peerNodeId}: attribute ${nodeId}/${endpointId}/${clusterId}/${attributeName} changed to ${Logger.toJSON(value)}`),
|
|
1764
|
-
eventTriggeredCallback: (peerNodeId, { path: { nodeId, clusterId, endpointId, eventName }, events }) =>
|
|
1765
|
-
this.log.info(`***Commissioning controller eventTriggeredCallback ${peerNodeId}: Event ${nodeId}/${endpointId}/${clusterId}/${eventName} triggered with ${Logger.toJSON(events)}`),
|
|
1766
|
-
stateInformationCallback: (peerNodeId, info) => {
|
|
1767
|
-
switch (info) {
|
|
1768
|
-
case NodeStateInformation.Connected:
|
|
1769
|
-
this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} connected`);
|
|
1770
|
-
break;
|
|
1771
|
-
case NodeStateInformation.Disconnected:
|
|
1772
|
-
this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} disconnected`);
|
|
1773
|
-
break;
|
|
1774
|
-
case NodeStateInformation.Reconnecting:
|
|
1775
|
-
this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} reconnecting`);
|
|
1776
|
-
break;
|
|
1777
|
-
case NodeStateInformation.WaitingForDeviceDiscovery:
|
|
1778
|
-
this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} waiting for device discovery`);
|
|
1779
|
-
break;
|
|
1780
|
-
case NodeStateInformation.StructureChanged:
|
|
1781
|
-
this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} structure changed`);
|
|
1782
|
-
break;
|
|
1783
|
-
case NodeStateInformation.Decommissioned:
|
|
1784
|
-
this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} decommissioned`);
|
|
1785
|
-
break;
|
|
1786
|
-
default:
|
|
1787
|
-
this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} NodeStateInformation.${info}`);
|
|
1788
|
-
break;
|
|
1789
|
-
}
|
|
1790
|
-
},
|
|
1791
|
-
});
|
|
1792
|
-
|
|
1793
|
-
node.logStructure();
|
|
1794
|
-
|
|
1795
|
-
// Get the interaction client
|
|
1796
|
-
this.log.info('Getting the interaction client');
|
|
1797
|
-
const interactionClient = await node.getInteractionClient();
|
|
1798
|
-
let cluster;
|
|
1799
|
-
let attributes;
|
|
1800
|
-
|
|
1801
|
-
// Log BasicInformationCluster
|
|
1802
|
-
cluster = BasicInformationCluster;
|
|
1803
|
-
attributes = await interactionClient.getMultipleAttributes({
|
|
1804
|
-
attributes: [{ clusterId: cluster.id }],
|
|
1805
|
-
});
|
|
1806
|
-
if (attributes.length > 0) this.log.info(`Cluster: ${idn}${cluster.name}${rs}${nf} attributes:`);
|
|
1807
|
-
attributes.forEach((attribute) => {
|
|
1808
|
-
this.log.info(
|
|
1809
|
-
`- 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}`,
|
|
1810
|
-
);
|
|
1811
|
-
});
|
|
1812
|
-
|
|
1813
|
-
// Log PowerSourceCluster
|
|
1814
|
-
cluster = PowerSourceCluster;
|
|
1815
|
-
attributes = await interactionClient.getMultipleAttributes({
|
|
1816
|
-
attributes: [{ clusterId: cluster.id }],
|
|
1817
|
-
});
|
|
1818
|
-
if (attributes.length > 0) this.log.info(`Cluster: ${idn}${cluster.name}${rs}${nf} attributes:`);
|
|
1819
|
-
attributes.forEach((attribute) => {
|
|
1820
|
-
this.log.info(
|
|
1821
|
-
`- 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}`,
|
|
1822
|
-
);
|
|
1823
|
-
});
|
|
1824
|
-
|
|
1825
|
-
// Log ThreadNetworkDiagnostics
|
|
1826
|
-
cluster = ThreadNetworkDiagnosticsCluster;
|
|
1827
|
-
attributes = await interactionClient.getMultipleAttributes({
|
|
1828
|
-
attributes: [{ clusterId: cluster.id }],
|
|
1829
|
-
});
|
|
1830
|
-
if (attributes.length > 0) this.log.info(`Cluster: ${idn}${cluster.name}${rs}${nf} attributes:`);
|
|
1831
|
-
attributes.forEach((attribute) => {
|
|
1832
|
-
this.log.info(
|
|
1833
|
-
`- 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}`,
|
|
1834
|
-
);
|
|
1835
|
-
});
|
|
1836
|
-
|
|
1837
|
-
// Log SwitchCluster
|
|
1838
|
-
cluster = SwitchCluster;
|
|
1839
|
-
attributes = await interactionClient.getMultipleAttributes({
|
|
1840
|
-
attributes: [{ clusterId: cluster.id }],
|
|
1841
|
-
});
|
|
1842
|
-
if (attributes.length > 0) this.log.info(`Cluster: ${idn}${cluster.name}${rs}${nf} attributes:`);
|
|
1843
|
-
attributes.forEach((attribute) => {
|
|
1844
|
-
this.log.info(
|
|
1845
|
-
`- 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}`,
|
|
1846
|
-
);
|
|
1847
|
-
});
|
|
1848
|
-
|
|
1849
|
-
this.log.info('Subscribing to all attributes and events');
|
|
1850
|
-
await node.subscribeAllAttributesAndEvents({
|
|
1851
|
-
ignoreInitialTriggers: false,
|
|
1852
|
-
attributeChangedCallback: ({ path: { nodeId, clusterId, endpointId, attributeName }, version, value }) =>
|
|
1853
|
-
this.log.info(
|
|
1854
|
-
`***${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}`,
|
|
1855
|
-
),
|
|
1856
|
-
eventTriggeredCallback: ({ path: { nodeId, clusterId, endpointId, eventName }, events }) => {
|
|
1857
|
-
this.log.info(
|
|
1858
|
-
`***${db}Commissioning controller eventTriggeredCallback: event ${BLUE}${nodeId}${db}/${or}${endpointId}${db}/${hk}${getClusterNameById(clusterId)}${db}/${zb}${eventName}${db} triggered with ${debugStringify(events ?? { none: true })}`,
|
|
1859
|
-
);
|
|
1860
|
-
},
|
|
1861
|
-
});
|
|
1862
|
-
this.log.info('Subscribed to all attributes and events');
|
|
1863
|
-
}
|
|
1864
|
-
*/
|
|
1865
1318
|
}
|
|
1866
|
-
/** */
|
|
1867
|
-
/** Matter.js methods */
|
|
1868
|
-
/** */
|
|
1869
|
-
/**
|
|
1870
|
-
* Starts the matter storage with name Matterbridge, create the matterbridge context and performs a backup.
|
|
1871
|
-
*
|
|
1872
|
-
* @returns {Promise<void>} - A promise that resolves when the storage is started.
|
|
1873
|
-
*/
|
|
1874
1319
|
async startMatterStorage() {
|
|
1875
|
-
// Setup Matter storage
|
|
1876
1320
|
this.log.info(`Starting matter node storage...`);
|
|
1877
1321
|
this.matterStorageService = this.environment.get(StorageService);
|
|
1878
1322
|
this.log.info(`Matter node storage service created: ${this.matterStorageService.location}`);
|
|
@@ -1880,17 +1324,8 @@ export class Matterbridge extends EventEmitter {
|
|
|
1880
1324
|
this.log.info('Matter node storage manager "Matterbridge" created');
|
|
1881
1325
|
this.matterbridgeContext = await this.createServerNodeContext('Matterbridge', 'Matterbridge', this.aggregatorDeviceType, this.aggregatorVendorId, this.aggregatorVendorName, this.aggregatorProductId, this.aggregatorProductName, this.aggregatorSerialNumber, this.aggregatorUniqueId);
|
|
1882
1326
|
this.log.info('Matter node storage started');
|
|
1883
|
-
// Backup matter storage since it is created/opened correctly
|
|
1884
1327
|
await this.backupMatterStorage(path.join(this.matterbridgeDirectory, MATTER_STORAGE_NAME), path.join(this.matterbridgeDirectory, MATTER_STORAGE_NAME + '.backup'));
|
|
1885
1328
|
}
|
|
1886
|
-
/**
|
|
1887
|
-
* Makes a backup copy of the specified matter storage directory.
|
|
1888
|
-
*
|
|
1889
|
-
* @param {string} storageName - The name of the storage directory to be backed up.
|
|
1890
|
-
* @param {string} backupName - The name of the backup directory to be created.
|
|
1891
|
-
* @private
|
|
1892
|
-
* @returns {Promise<void>} A promise that resolves when the has been done.
|
|
1893
|
-
*/
|
|
1894
1329
|
async backupMatterStorage(storageName, backupName) {
|
|
1895
1330
|
this.log.info('Creating matter node storage backup...');
|
|
1896
1331
|
try {
|
|
@@ -1901,11 +1336,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1901
1336
|
this.log.error(`Error creating matter node storage backup from ${storageName} to ${backupName}:`, error);
|
|
1902
1337
|
}
|
|
1903
1338
|
}
|
|
1904
|
-
/**
|
|
1905
|
-
* Stops the matter storage.
|
|
1906
|
-
*
|
|
1907
|
-
* @returns {Promise<void>} A promise that resolves when the storage is stopped.
|
|
1908
|
-
*/
|
|
1909
1339
|
async stopMatterStorage() {
|
|
1910
1340
|
this.log.info('Closing matter node storage...');
|
|
1911
1341
|
await this.matterStorageManager?.close();
|
|
@@ -1914,20 +1344,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1914
1344
|
this.matterbridgeContext = undefined;
|
|
1915
1345
|
this.log.info('Matter node storage closed');
|
|
1916
1346
|
}
|
|
1917
|
-
/**
|
|
1918
|
-
* Creates a server node storage context.
|
|
1919
|
-
*
|
|
1920
|
-
* @param {string} storeId - The storeId.
|
|
1921
|
-
* @param {string} deviceName - The name of the device.
|
|
1922
|
-
* @param {DeviceTypeId} deviceType - The device type of the device.
|
|
1923
|
-
* @param {number} vendorId - The vendor ID.
|
|
1924
|
-
* @param {string} vendorName - The vendor name.
|
|
1925
|
-
* @param {number} productId - The product ID.
|
|
1926
|
-
* @param {string} productName - The product name.
|
|
1927
|
-
* @param {string} [serialNumber] - The serial number of the device (optional).
|
|
1928
|
-
* @param {string} [uniqueId] - The unique ID of the device (optional).
|
|
1929
|
-
* @returns {Promise<StorageContext>} The storage context for the commissioning server.
|
|
1930
|
-
*/
|
|
1931
1347
|
async createServerNodeContext(storeId, deviceName, deviceType, vendorId, vendorName, productId, productName, serialNumber, uniqueId) {
|
|
1932
1348
|
const { randomBytes } = await import('node:crypto');
|
|
1933
1349
|
if (!this.matterStorageService)
|
|
@@ -1967,15 +1383,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1967
1383
|
this.log.debug(`- hardwareVersion: ${await storageContext.get('hardwareVersion')} hardwareVersionString: ${await storageContext.get('hardwareVersionString')}`);
|
|
1968
1384
|
return storageContext;
|
|
1969
1385
|
}
|
|
1970
|
-
/**
|
|
1971
|
-
* Creates a server node.
|
|
1972
|
-
*
|
|
1973
|
-
* @param {StorageContext} storageContext - The storage context for the server node.
|
|
1974
|
-
* @param {number} [port] - The port number for the server node. Defaults to 5540.
|
|
1975
|
-
* @param {number} [passcode] - The passcode for the server node. Defaults to 20242025.
|
|
1976
|
-
* @param {number} [discriminator] - The discriminator for the server node. Defaults to 3850.
|
|
1977
|
-
* @returns {Promise<ServerNode<ServerNode.RootEndpoint>>} A promise that resolves to the created server node.
|
|
1978
|
-
*/
|
|
1979
1386
|
async createServerNode(storageContext, port = 5540, passcode = 20242025, discriminator = 3850) {
|
|
1980
1387
|
const storeId = await storageContext.get('storeId');
|
|
1981
1388
|
this.log.notice(`Creating server node for ${storeId} on port ${port} with passcode ${passcode} and discriminator ${discriminator}...`);
|
|
@@ -1985,37 +1392,24 @@ export class Matterbridge extends EventEmitter {
|
|
|
1985
1392
|
this.log.debug(`- uniqueId: ${await storageContext.get('uniqueId')}`);
|
|
1986
1393
|
this.log.debug(`- softwareVersion: ${await storageContext.get('softwareVersion')} softwareVersionString: ${await storageContext.get('softwareVersionString')}`);
|
|
1987
1394
|
this.log.debug(`- hardwareVersion: ${await storageContext.get('hardwareVersion')} hardwareVersionString: ${await storageContext.get('hardwareVersionString')}`);
|
|
1988
|
-
/**
|
|
1989
|
-
* Create a Matter ServerNode, which contains the Root Endpoint and all relevant data and configuration
|
|
1990
|
-
*/
|
|
1991
1395
|
const serverNode = await ServerNode.create({
|
|
1992
|
-
// Required: Give the Node a unique ID which is used to store the state of this node
|
|
1993
1396
|
id: storeId,
|
|
1994
|
-
// Provide Network relevant configuration like the port
|
|
1995
|
-
// Optional when operating only one device on a host, Default port is 5540
|
|
1996
1397
|
network: {
|
|
1997
1398
|
listeningAddressIpv4: this.ipv4Address,
|
|
1998
1399
|
listeningAddressIpv6: this.ipv6Address,
|
|
1999
1400
|
port,
|
|
2000
1401
|
},
|
|
2001
|
-
// Provide the certificate for the device
|
|
2002
1402
|
operationalCredentials: {
|
|
2003
1403
|
certification: this.certification,
|
|
2004
1404
|
},
|
|
2005
|
-
// Provide Commissioning relevant settings
|
|
2006
|
-
// Optional for development/testing purposes
|
|
2007
1405
|
commissioning: {
|
|
2008
1406
|
passcode,
|
|
2009
1407
|
discriminator,
|
|
2010
1408
|
},
|
|
2011
|
-
// Provide Node announcement settings
|
|
2012
|
-
// Optional: If Ommitted some development defaults are used
|
|
2013
1409
|
productDescription: {
|
|
2014
1410
|
name: await storageContext.get('deviceName'),
|
|
2015
1411
|
deviceType: DeviceTypeId(await storageContext.get('deviceType')),
|
|
2016
1412
|
},
|
|
2017
|
-
// Provide defaults for the BasicInformation cluster on the Root endpoint
|
|
2018
|
-
// Optional: If Omitted some development defaults are used
|
|
2019
1413
|
basicInformation: {
|
|
2020
1414
|
vendorId: VendorId(await storageContext.get('vendorId')),
|
|
2021
1415
|
vendorName: await storageContext.get('vendorName'),
|
|
@@ -2032,23 +1426,17 @@ export class Matterbridge extends EventEmitter {
|
|
|
2032
1426
|
reachable: true,
|
|
2033
1427
|
},
|
|
2034
1428
|
});
|
|
2035
|
-
/**
|
|
2036
|
-
* This event is triggered when the device is initially commissioned successfully.
|
|
2037
|
-
* This means: It is added to the first fabric.
|
|
2038
|
-
*/
|
|
2039
1429
|
serverNode.lifecycle.commissioned.on(() => {
|
|
2040
1430
|
this.log.notice(`Server node for ${storeId} was initially commissioned successfully!`);
|
|
2041
1431
|
this.advertisingNodes.delete(storeId);
|
|
2042
1432
|
this.frontend.wssSendRefreshRequired('matter', { matter: { ...this.getServerNodeData(serverNode) } });
|
|
2043
1433
|
});
|
|
2044
|
-
/** This event is triggered when all fabrics are removed from the device, usually it also does a factory reset then. */
|
|
2045
1434
|
serverNode.lifecycle.decommissioned.on(() => {
|
|
2046
1435
|
this.log.notice(`Server node for ${storeId} was fully decommissioned successfully!`);
|
|
2047
1436
|
this.advertisingNodes.delete(storeId);
|
|
2048
1437
|
this.frontend.wssSendRefreshRequired('matter', { matter: { ...this.getServerNodeData(serverNode) } });
|
|
2049
1438
|
this.frontend.wssSendSnackbarMessage(`${storeId} is offline`, 5, 'warning');
|
|
2050
1439
|
});
|
|
2051
|
-
/** This event is triggered when the device went online. This means that it is discoverable in the network. */
|
|
2052
1440
|
serverNode.lifecycle.online.on(async () => {
|
|
2053
1441
|
this.log.notice(`Server node for ${storeId} is online`);
|
|
2054
1442
|
if (!serverNode.lifecycle.isCommissioned) {
|
|
@@ -2059,16 +1447,13 @@ export class Matterbridge extends EventEmitter {
|
|
|
2059
1447
|
this.log.notice(`Manual pairing code: ${manualPairingCode}`);
|
|
2060
1448
|
}
|
|
2061
1449
|
else {
|
|
2062
|
-
// istanbul ignore next
|
|
2063
1450
|
this.log.notice(`Server node for ${storeId} is already commissioned. Waiting for controllers to connect ...`);
|
|
2064
|
-
// istanbul ignore next
|
|
2065
1451
|
this.advertisingNodes.delete(storeId);
|
|
2066
1452
|
}
|
|
2067
1453
|
this.frontend.wssSendRefreshRequired('matter', { matter: { ...this.getServerNodeData(serverNode) } });
|
|
2068
1454
|
this.frontend.wssSendSnackbarMessage(`${storeId} is online`, 5, 'success');
|
|
2069
1455
|
this.emit('online', storeId);
|
|
2070
1456
|
});
|
|
2071
|
-
/** This event is triggered when the device went offline. it is not longer discoverable or connectable in the network. */
|
|
2072
1457
|
serverNode.lifecycle.offline.on(() => {
|
|
2073
1458
|
this.log.notice(`Server node for ${storeId} is offline`);
|
|
2074
1459
|
this.advertisingNodes.delete(storeId);
|
|
@@ -2076,15 +1461,11 @@ export class Matterbridge extends EventEmitter {
|
|
|
2076
1461
|
this.frontend.wssSendSnackbarMessage(`${storeId} is offline`, 5, 'warning');
|
|
2077
1462
|
this.emit('offline', storeId);
|
|
2078
1463
|
});
|
|
2079
|
-
/**
|
|
2080
|
-
* This event is triggered when a fabric is added, removed or updated on the device. Use this if more granular
|
|
2081
|
-
* information is needed.
|
|
2082
|
-
*/
|
|
2083
1464
|
serverNode.events.commissioning.fabricsChanged.on((fabricIndex, fabricAction) => {
|
|
2084
1465
|
let action = '';
|
|
2085
1466
|
switch (fabricAction) {
|
|
2086
1467
|
case FabricAction.Added:
|
|
2087
|
-
this.advertisingNodes.delete(storeId);
|
|
1468
|
+
this.advertisingNodes.delete(storeId);
|
|
2088
1469
|
action = 'added';
|
|
2089
1470
|
break;
|
|
2090
1471
|
case FabricAction.Removed:
|
|
@@ -2097,22 +1478,14 @@ export class Matterbridge extends EventEmitter {
|
|
|
2097
1478
|
this.log.notice(`Commissioned fabric index ${fabricIndex} ${action} on server node for ${storeId}: ${debugStringify(serverNode.state.commissioning.fabrics[fabricIndex])}`);
|
|
2098
1479
|
this.frontend.wssSendRefreshRequired('matter', { matter: { ...this.getServerNodeData(serverNode) } });
|
|
2099
1480
|
});
|
|
2100
|
-
/**
|
|
2101
|
-
* This event is triggered when an operative new session was opened by a Controller.
|
|
2102
|
-
* It is not triggered for the initial commissioning process, just afterwards for real connections.
|
|
2103
|
-
*/
|
|
2104
1481
|
serverNode.events.sessions.opened.on((session) => {
|
|
2105
1482
|
this.log.notice(`Session opened on server node for ${storeId}: ${debugStringify(session)}`);
|
|
2106
1483
|
this.frontend.wssSendRefreshRequired('matter', { matter: { ...this.getServerNodeData(serverNode) } });
|
|
2107
1484
|
});
|
|
2108
|
-
/**
|
|
2109
|
-
* This event is triggered when an operative session is closed by a Controller or because the Device goes offline.
|
|
2110
|
-
*/
|
|
2111
1485
|
serverNode.events.sessions.closed.on((session) => {
|
|
2112
1486
|
this.log.notice(`Session closed on server node for ${storeId}: ${debugStringify(session)}`);
|
|
2113
1487
|
this.frontend.wssSendRefreshRequired('matter', { matter: { ...this.getServerNodeData(serverNode) } });
|
|
2114
1488
|
});
|
|
2115
|
-
/** This event is triggered when a subscription gets added or removed on an operative session. */
|
|
2116
1489
|
serverNode.events.sessions.subscriptionsChanged.on((session) => {
|
|
2117
1490
|
this.log.notice(`Session subscriptions changed on server node for ${storeId}: ${debugStringify(session)}`);
|
|
2118
1491
|
this.frontend.wssSendRefreshRequired('matter', { matter: { ...this.getServerNodeData(serverNode) } });
|
|
@@ -2120,12 +1493,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2120
1493
|
this.log.info(`Created server node for ${storeId}`);
|
|
2121
1494
|
return serverNode;
|
|
2122
1495
|
}
|
|
2123
|
-
/**
|
|
2124
|
-
* Gets the matter sanitized data of the specified server node.
|
|
2125
|
-
*
|
|
2126
|
-
* @param {ServerNode} [serverNode] - The server node to start.
|
|
2127
|
-
* @returns {ApiMatter} The sanitized data of the server node.
|
|
2128
|
-
*/
|
|
2129
1496
|
getServerNodeData(serverNode) {
|
|
2130
1497
|
const advertiseTime = this.advertisingNodes.get(serverNode.id) || 0;
|
|
2131
1498
|
return {
|
|
@@ -2142,25 +1509,12 @@ export class Matterbridge extends EventEmitter {
|
|
|
2142
1509
|
serialNumber: serverNode.state.basicInformation.serialNumber,
|
|
2143
1510
|
};
|
|
2144
1511
|
}
|
|
2145
|
-
/**
|
|
2146
|
-
* Starts the specified server node.
|
|
2147
|
-
*
|
|
2148
|
-
* @param {ServerNode} [matterServerNode] - The server node to start.
|
|
2149
|
-
* @returns {Promise<void>} A promise that resolves when the server node has started.
|
|
2150
|
-
*/
|
|
2151
1512
|
async startServerNode(matterServerNode) {
|
|
2152
1513
|
if (!matterServerNode)
|
|
2153
1514
|
return;
|
|
2154
1515
|
this.log.notice(`Starting ${matterServerNode.id} server node`);
|
|
2155
1516
|
await matterServerNode.start();
|
|
2156
1517
|
}
|
|
2157
|
-
/**
|
|
2158
|
-
* Stops the specified server node.
|
|
2159
|
-
*
|
|
2160
|
-
* @param {ServerNode} matterServerNode - The server node to stop.
|
|
2161
|
-
* @param {number} [timeout] - The timeout in milliseconds for stopping the server node. Defaults to 30 seconds.
|
|
2162
|
-
* @returns {Promise<void>} A promise that resolves when the server node has stopped.
|
|
2163
|
-
*/
|
|
2164
1518
|
async stopServerNode(matterServerNode, timeout = 30000) {
|
|
2165
1519
|
if (!matterServerNode)
|
|
2166
1520
|
return;
|
|
@@ -2173,25 +1527,12 @@ export class Matterbridge extends EventEmitter {
|
|
|
2173
1527
|
this.log.error(`Failed to close ${matterServerNode.id} server node: ${error instanceof Error ? error.message : error}`);
|
|
2174
1528
|
}
|
|
2175
1529
|
}
|
|
2176
|
-
/**
|
|
2177
|
-
* Creates an aggregator node with the specified storage context.
|
|
2178
|
-
*
|
|
2179
|
-
* @param {StorageContext} storageContext - The storage context for the aggregator node.
|
|
2180
|
-
* @returns {Promise<Endpoint<AggregatorEndpoint>>} A promise that resolves to the created aggregator node.
|
|
2181
|
-
*/
|
|
2182
1530
|
async createAggregatorNode(storageContext) {
|
|
2183
1531
|
this.log.notice(`Creating ${await storageContext.get('storeId')} aggregator...`);
|
|
2184
1532
|
const aggregatorNode = new Endpoint(AggregatorEndpoint, { id: `${await storageContext.get('storeId')}` });
|
|
2185
1533
|
this.log.info(`Created ${await storageContext.get('storeId')} aggregator`);
|
|
2186
1534
|
return aggregatorNode;
|
|
2187
1535
|
}
|
|
2188
|
-
/**
|
|
2189
|
-
* Creates and configures the server node for an accessory plugin for a given device.
|
|
2190
|
-
*
|
|
2191
|
-
* @param {Plugin} plugin - The plugin to configure.
|
|
2192
|
-
* @param {MatterbridgeEndpoint} device - The device to associate with the plugin.
|
|
2193
|
-
* @returns {Promise<void>} A promise that resolves when the server node for the accessory plugin is created and configured.
|
|
2194
|
-
*/
|
|
2195
1536
|
async createAccessoryPlugin(plugin, device) {
|
|
2196
1537
|
if (!plugin.locked && device.deviceType && device.deviceName && device.vendorId && device.productId && device.vendorName && device.productName) {
|
|
2197
1538
|
plugin.locked = true;
|
|
@@ -2203,12 +1544,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2203
1544
|
await plugin.serverNode.add(device);
|
|
2204
1545
|
}
|
|
2205
1546
|
}
|
|
2206
|
-
/**
|
|
2207
|
-
* Creates and configures the server node and the aggregator node for a dynamic plugin.
|
|
2208
|
-
*
|
|
2209
|
-
* @param {Plugin} plugin - The plugin to configure.
|
|
2210
|
-
* @returns {Promise<void>} A promise that resolves when the server node and the aggregator node for the dynamic plugin is created and configured.
|
|
2211
|
-
*/
|
|
2212
1547
|
async createDynamicPlugin(plugin) {
|
|
2213
1548
|
if (!plugin.locked) {
|
|
2214
1549
|
plugin.locked = true;
|
|
@@ -2221,13 +1556,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2221
1556
|
await plugin.serverNode.add(plugin.aggregatorNode);
|
|
2222
1557
|
}
|
|
2223
1558
|
}
|
|
2224
|
-
/**
|
|
2225
|
-
* Creates and configures the server node for a single not bridged device.
|
|
2226
|
-
*
|
|
2227
|
-
* @param {Plugin} plugin - The plugin to configure.
|
|
2228
|
-
* @param {MatterbridgeEndpoint} device - The device to associate with the plugin.
|
|
2229
|
-
* @returns {Promise<void>} A promise that resolves when the server node for the accessory plugin is created and configured.
|
|
2230
|
-
*/
|
|
2231
1559
|
async createDeviceServerNode(plugin, device) {
|
|
2232
1560
|
if (device.mode === 'server' && !device.serverNode && device.deviceType && device.deviceName && device.vendorId && device.vendorName && device.productId && device.productName) {
|
|
2233
1561
|
this.log.debug(`Creating device ${plg}${plugin.name}${db}:${dev}${device.deviceName}${db} server node...`);
|
|
@@ -2238,15 +1566,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
2238
1566
|
this.log.debug(`Added ${plg}${plugin.name}${db}:${dev}${device.deviceName}${db} to server node`);
|
|
2239
1567
|
}
|
|
2240
1568
|
}
|
|
2241
|
-
/**
|
|
2242
|
-
* Adds a MatterbridgeEndpoint to the specified plugin.
|
|
2243
|
-
*
|
|
2244
|
-
* @param {string} pluginName - The name of the plugin.
|
|
2245
|
-
* @param {MatterbridgeEndpoint} device - The device to add as a bridged endpoint.
|
|
2246
|
-
* @returns {Promise<void>} A promise that resolves when the bridged endpoint has been added.
|
|
2247
|
-
*/
|
|
2248
1569
|
async addBridgedEndpoint(pluginName, device) {
|
|
2249
|
-
// Check if the plugin is registered
|
|
2250
1570
|
const plugin = this.plugins.get(pluginName);
|
|
2251
1571
|
if (!plugin) {
|
|
2252
1572
|
this.log.error(`Error adding bridged endpoint ${dev}${device.deviceName}${er} (${zb}${device.id}${er}) plugin ${plg}${pluginName}${er} not found`);
|
|
@@ -2266,7 +1586,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2266
1586
|
}
|
|
2267
1587
|
else if (this.bridgeMode === 'bridge') {
|
|
2268
1588
|
if (device.mode === 'matter') {
|
|
2269
|
-
// Register and add the device to the matterbridge server node
|
|
2270
1589
|
this.log.debug(`Adding matter endpoint ${plg}${pluginName}${db}:${dev}${device.deviceName}${db} to Matterbridge server node...`);
|
|
2271
1590
|
if (!this.serverNode) {
|
|
2272
1591
|
this.log.error('Server node not found for Matterbridge');
|
|
@@ -2283,7 +1602,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2283
1602
|
}
|
|
2284
1603
|
}
|
|
2285
1604
|
else {
|
|
2286
|
-
// Register and add the device to the matterbridge aggregator node
|
|
2287
1605
|
this.log.debug(`Adding bridged endpoint ${plg}${pluginName}${db}:${dev}${device.deviceName}${db} to Matterbridge aggregator node`);
|
|
2288
1606
|
if (!this.aggregatorNode) {
|
|
2289
1607
|
this.log.error('Aggregator node not found for Matterbridge');
|
|
@@ -2301,7 +1619,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2301
1619
|
}
|
|
2302
1620
|
}
|
|
2303
1621
|
else if (this.bridgeMode === 'childbridge') {
|
|
2304
|
-
// Register and add the device to the plugin server node
|
|
2305
1622
|
if (plugin.type === 'AccessoryPlatform') {
|
|
2306
1623
|
try {
|
|
2307
1624
|
this.log.debug(`Creating endpoint ${dev}${device.deviceName}${db} for AccessoryPlatform plugin ${plg}${plugin.name}${db} server node`);
|
|
@@ -2325,12 +1642,10 @@ export class Matterbridge extends EventEmitter {
|
|
|
2325
1642
|
return;
|
|
2326
1643
|
}
|
|
2327
1644
|
}
|
|
2328
|
-
// Register and add the device to the plugin aggregator node
|
|
2329
1645
|
if (plugin.type === 'DynamicPlatform') {
|
|
2330
1646
|
try {
|
|
2331
1647
|
this.log.debug(`Adding bridged endpoint ${dev}${device.deviceName}${db} for DynamicPlatform plugin ${plg}${plugin.name}${db} aggregator node`);
|
|
2332
1648
|
await this.createDynamicPlugin(plugin);
|
|
2333
|
-
// Fast plugins can add another device before the server node is ready, so we wait for the server node to be ready
|
|
2334
1649
|
await waiter(`createDynamicPlugin(${plugin.name})`, () => plugin.serverNode?.hasParts === true);
|
|
2335
1650
|
if (!plugin.aggregatorNode) {
|
|
2336
1651
|
this.log.error(`Aggregator node not found for plugin ${plg}${plugin.name}${er}`);
|
|
@@ -2351,28 +1666,17 @@ export class Matterbridge extends EventEmitter {
|
|
|
2351
1666
|
}
|
|
2352
1667
|
if (plugin.registeredDevices !== undefined)
|
|
2353
1668
|
plugin.registeredDevices++;
|
|
2354
|
-
// Add the device to the DeviceManager
|
|
2355
1669
|
this.devices.set(device);
|
|
2356
|
-
// Subscribe to the attributes changed event
|
|
2357
1670
|
await this.subscribeAttributeChanged(plugin, device);
|
|
2358
1671
|
this.log.info(`Added and registered bridged endpoint (${plugin.registeredDevices}) ${dev}${device.deviceName}${nf} (${dev}${device.id}${nf}) for plugin ${plg}${pluginName}${nf}`);
|
|
2359
1672
|
}
|
|
2360
|
-
/**
|
|
2361
|
-
* Removes a MatterbridgeEndpoint from the specified plugin.
|
|
2362
|
-
*
|
|
2363
|
-
* @param {string} pluginName - The name of the plugin.
|
|
2364
|
-
* @param {MatterbridgeEndpoint} device - The device to remove as a bridged endpoint.
|
|
2365
|
-
* @returns {Promise<void>} A promise that resolves when the bridged endpoint has been removed.
|
|
2366
|
-
*/
|
|
2367
1673
|
async removeBridgedEndpoint(pluginName, device) {
|
|
2368
1674
|
this.log.debug(`Removing bridged endpoint ${plg}${pluginName}${db}:${dev}${device.deviceName}${db} (${zb}${device.name}${db}) for plugin ${plg}${pluginName}${db}`);
|
|
2369
|
-
// Check if the plugin is registered
|
|
2370
1675
|
const plugin = this.plugins.get(pluginName);
|
|
2371
1676
|
if (!plugin) {
|
|
2372
1677
|
this.log.error(`Error removing bridged endpoint ${dev}${device.deviceName}${er} (${zb}${device.name}${er}) for plugin ${plg}${pluginName}${er}: plugin not found`);
|
|
2373
1678
|
return;
|
|
2374
1679
|
}
|
|
2375
|
-
// Register and add the device to the matterbridge aggregator node
|
|
2376
1680
|
if (this.bridgeMode === 'bridge') {
|
|
2377
1681
|
if (!this.aggregatorNode) {
|
|
2378
1682
|
this.log.error(`Error removing bridged endpoint ${dev}${device.deviceName}${er} (${zb}${device.name}${er}) for plugin ${plg}${pluginName}${er}: aggregator node not found`);
|
|
@@ -2385,7 +1689,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2385
1689
|
}
|
|
2386
1690
|
else if (this.bridgeMode === 'childbridge') {
|
|
2387
1691
|
if (plugin.type === 'AccessoryPlatform') {
|
|
2388
|
-
// Nothing to do here since the server node has no aggregator node but only the device itself
|
|
2389
1692
|
}
|
|
2390
1693
|
else if (plugin.type === 'DynamicPlatform') {
|
|
2391
1694
|
if (!plugin.aggregatorNode) {
|
|
@@ -2398,21 +1701,8 @@ export class Matterbridge extends EventEmitter {
|
|
|
2398
1701
|
if (plugin.registeredDevices !== undefined)
|
|
2399
1702
|
plugin.registeredDevices--;
|
|
2400
1703
|
}
|
|
2401
|
-
// Remove the device from the DeviceManager
|
|
2402
1704
|
this.devices.remove(device);
|
|
2403
1705
|
}
|
|
2404
|
-
/**
|
|
2405
|
-
* Removes all bridged endpoints from the specified plugin.
|
|
2406
|
-
*
|
|
2407
|
-
* @param {string} pluginName - The name of the plugin.
|
|
2408
|
-
* @param {number} [delay] - The delay in milliseconds between removing each bridged endpoint (default: 0).
|
|
2409
|
-
* @returns {Promise<void>} A promise that resolves when all bridged endpoints have been removed.
|
|
2410
|
-
*
|
|
2411
|
-
* @remarks
|
|
2412
|
-
* This method iterates through all devices in the DeviceManager and removes each bridged endpoint associated with the specified plugin.
|
|
2413
|
-
* It also applies a delay between each removal if specified.
|
|
2414
|
-
* The delay is useful to allow the controllers to receive a single subscription for each device removed.
|
|
2415
|
-
*/
|
|
2416
1706
|
async removeAllBridgedEndpoints(pluginName, delay = 0) {
|
|
2417
1707
|
this.log.debug(`Removing all bridged endpoints for plugin ${plg}${pluginName}${db}${delay > 0 ? ` with delay ${delay} ms` : ''}`);
|
|
2418
1708
|
for (const device of this.devices.array().filter((device) => device.plugin === pluginName)) {
|
|
@@ -2423,24 +1713,13 @@ export class Matterbridge extends EventEmitter {
|
|
|
2423
1713
|
if (delay > 0)
|
|
2424
1714
|
await wait(2000);
|
|
2425
1715
|
}
|
|
2426
|
-
/**
|
|
2427
|
-
* Subscribes to the attribute change event for the given device and plugin.
|
|
2428
|
-
* Specifically, it listens for changes in the 'reachable' attribute of the
|
|
2429
|
-
* BridgedDeviceBasicInformationServer cluster server of the bridged device or BasicInformationServer cluster server of server node.
|
|
2430
|
-
*
|
|
2431
|
-
* @param {Plugin} plugin - The plugin associated with the device.
|
|
2432
|
-
* @param {MatterbridgeEndpoint} device - The device to subscribe to attribute changes for.
|
|
2433
|
-
* @returns {Promise<void>} A promise that resolves when the subscription is set up.
|
|
2434
|
-
*/
|
|
2435
1716
|
async subscribeAttributeChanged(plugin, device) {
|
|
2436
1717
|
if (!plugin || !device || !device.plugin || !device.serialNumber || !device.uniqueId || !device.maybeNumber)
|
|
2437
1718
|
return;
|
|
2438
1719
|
this.log.info(`Subscribing attributes for endpoint ${dev}${device.deviceName}${nf} (${dev}${device.id}${nf}) plugin ${plg}${plugin.name}${nf}`);
|
|
2439
|
-
// Subscribe to the reachable$Changed event of the BasicInformationServer cluster server of the server node in childbridge mode
|
|
2440
1720
|
if (this.bridgeMode === 'childbridge' && plugin.type === 'AccessoryPlatform' && plugin.serverNode) {
|
|
2441
1721
|
plugin.serverNode.eventsOf(BasicInformationServer).reachable$Changed?.on((reachable) => {
|
|
2442
1722
|
this.log.info(`Accessory endpoint ${dev}${device.deviceName}${nf} (${dev}${device.id}${nf}) is ${reachable ? 'reachable' : 'unreachable'}`);
|
|
2443
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
2444
1723
|
this.frontend.wssSendAttributeChangedMessage(device.plugin, device.serialNumber, device.uniqueId, device.number, device.id, 'BasicInformation', 'reachable', reachable);
|
|
2445
1724
|
});
|
|
2446
1725
|
}
|
|
@@ -2488,7 +1767,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2488
1767
|
this.log.debug(`Subscribing to endpoint ${or}${device.id}${db}:${or}${device.number}${db} attribute ${dev}${sub.cluster}${db}.${dev}${sub.attribute}${db} changes...`);
|
|
2489
1768
|
await device.subscribeAttribute(sub.cluster, sub.attribute, (value) => {
|
|
2490
1769
|
this.log.debug(`Bridged endpoint ${or}${device.id}${db}:${or}${device.number}${db} attribute ${dev}${sub.cluster}${db}.${dev}${sub.attribute}${db} changed to ${CYAN}${value}${db}`);
|
|
2491
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
2492
1770
|
this.frontend.wssSendAttributeChangedMessage(device.plugin, device.serialNumber, device.uniqueId, device.number, device.id, sub.cluster, sub.attribute, value);
|
|
2493
1771
|
});
|
|
2494
1772
|
}
|
|
@@ -2497,19 +1775,12 @@ export class Matterbridge extends EventEmitter {
|
|
|
2497
1775
|
this.log.debug(`Subscribing to child endpoint ${or}${child.id}${db}:${or}${child.number}${db} attribute ${dev}${sub.cluster}${db}.${dev}${sub.attribute}${db} changes...`);
|
|
2498
1776
|
await child.subscribeAttribute(sub.cluster, sub.attribute, (value) => {
|
|
2499
1777
|
this.log.debug(`Bridged child endpoint ${or}${child.id}${db}:${or}${child.number}${db} attribute ${dev}${sub.cluster}${db}.${dev}${sub.attribute}${db} changed to ${CYAN}${value}${db}`);
|
|
2500
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
2501
1778
|
this.frontend.wssSendAttributeChangedMessage(device.plugin, device.serialNumber, device.uniqueId, child.number, child.id, sub.cluster, sub.attribute, value);
|
|
2502
1779
|
});
|
|
2503
1780
|
}
|
|
2504
1781
|
}
|
|
2505
1782
|
}
|
|
2506
1783
|
}
|
|
2507
|
-
/**
|
|
2508
|
-
* Sanitizes the fabric information by converting bigint properties to strings because `res.json` doesn't support bigint.
|
|
2509
|
-
*
|
|
2510
|
-
* @param {ExposedFabricInformation[]} fabricInfo - The array of exposed fabric information objects.
|
|
2511
|
-
* @returns {SanitizedExposedFabricInformation[]} An array of sanitized exposed fabric information objects.
|
|
2512
|
-
*/
|
|
2513
1784
|
sanitizeFabricInformations(fabricInfo) {
|
|
2514
1785
|
return fabricInfo.map((info) => {
|
|
2515
1786
|
return {
|
|
@@ -2523,12 +1794,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2523
1794
|
};
|
|
2524
1795
|
});
|
|
2525
1796
|
}
|
|
2526
|
-
/**
|
|
2527
|
-
* Sanitizes the session information by converting bigint properties to strings because `res.json` doesn't support bigint.
|
|
2528
|
-
*
|
|
2529
|
-
* @param {SessionsBehavior.Session[]} sessions - The array of session information objects.
|
|
2530
|
-
* @returns {SanitizedSession[]} An array of sanitized session information objects.
|
|
2531
|
-
*/
|
|
2532
1797
|
sanitizeSessionInformation(sessions) {
|
|
2533
1798
|
return sessions
|
|
2534
1799
|
.filter((session) => session.isPeerActive)
|
|
@@ -2555,21 +1820,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
2555
1820
|
};
|
|
2556
1821
|
});
|
|
2557
1822
|
}
|
|
2558
|
-
/**
|
|
2559
|
-
* Sets the reachability of the specified aggregator node bridged devices and trigger.
|
|
2560
|
-
*
|
|
2561
|
-
* @param {Endpoint<AggregatorEndpoint>} aggregatorNode - The aggregator node to set the reachability for.
|
|
2562
|
-
* @param {boolean} reachable - A boolean indicating the reachability status to set.
|
|
2563
|
-
*/
|
|
2564
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
2565
1823
|
async setAggregatorReachability(aggregatorNode, reachable) {
|
|
2566
|
-
/*
|
|
2567
|
-
for (const child of aggregatorNode.parts) {
|
|
2568
|
-
this.log.debug(`Setting reachability of ${(child as unknown as MatterbridgeEndpoint)?.deviceName} to ${reachable}`);
|
|
2569
|
-
await child.setStateOf(BridgedDeviceBasicInformationServer, { reachable });
|
|
2570
|
-
child.act((agent) => child.eventsOf(BridgedDeviceBasicInformationServer).reachableChanged.emit({ reachableNewValue: true }, agent.context));
|
|
2571
|
-
}
|
|
2572
|
-
*/
|
|
2573
1824
|
}
|
|
2574
1825
|
getVendorIdName = (vendorId) => {
|
|
2575
1826
|
if (!vendorId)
|
|
@@ -2609,11 +1860,10 @@ export class Matterbridge extends EventEmitter {
|
|
|
2609
1860
|
case 0x1488:
|
|
2610
1861
|
vendorName = '(ShortcutLabsFlic)';
|
|
2611
1862
|
break;
|
|
2612
|
-
case 65521:
|
|
1863
|
+
case 65521:
|
|
2613
1864
|
vendorName = '(MatterTest)';
|
|
2614
1865
|
break;
|
|
2615
1866
|
}
|
|
2616
1867
|
return vendorName;
|
|
2617
1868
|
};
|
|
2618
1869
|
}
|
|
2619
|
-
//# sourceMappingURL=matterbridge.js.map
|