matterbridge 3.3.7 → 3.3.8-dev-20251114-9b65e59
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 +37 -0
- package/README-SERVICE-OPT.md +4 -4
- package/README.md +4 -0
- package/dist/broadcastServer.js +1 -93
- package/dist/broadcastServerTypes.js +0 -24
- package/dist/cli.js +1 -97
- package/dist/cliEmitter.js +0 -37
- package/dist/cliHistory.js +0 -38
- package/dist/clusters/export.js +0 -2
- package/dist/defaultConfigSchema.js +0 -24
- package/dist/deviceManager.js +1 -105
- package/dist/devices/airConditioner.js +1 -58
- package/dist/devices/batteryStorage.js +2 -49
- package/dist/devices/cooktop.js +1 -56
- package/dist/devices/dishwasher.js +1 -58
- package/dist/devices/evse.js +11 -75
- package/dist/devices/export.js +0 -5
- package/dist/devices/extractorHood.js +1 -43
- package/dist/devices/heatPump.js +3 -51
- package/dist/devices/laundryDryer.js +4 -63
- package/dist/devices/laundryWasher.js +5 -71
- package/dist/devices/microwaveOven.js +6 -89
- package/dist/devices/oven.js +1 -86
- package/dist/devices/refrigerator.js +2 -104
- package/dist/devices/roboticVacuumCleaner.js +10 -101
- package/dist/devices/solarPower.js +1 -39
- package/dist/devices/speaker.js +1 -85
- package/dist/devices/temperatureControl.js +3 -24
- package/dist/devices/waterHeater.js +3 -83
- 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 +40 -452
- package/dist/frontendTypes.js +0 -45
- package/dist/helpers.js +0 -53
- package/dist/index.js +1 -25
- package/dist/jestutils/export.js +1 -0
- package/dist/{utils → jestutils}/jestHelpers.js +167 -175
- 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 +50 -838
- package/dist/matterbridgeAccessoryPlatform.js +0 -37
- package/dist/matterbridgeBehaviors.js +5 -68
- package/dist/matterbridgeDeviceTypes.js +27 -653
- package/dist/matterbridgeDynamicPlatform.js +0 -37
- package/dist/matterbridgeEndpoint.js +73 -1429
- package/dist/matterbridgeEndpointHelpers.js +42 -475
- package/dist/matterbridgeEndpointTypes.js +3 -0
- package/dist/matterbridgePlatform.js +18 -341
- package/dist/matterbridgeTypes.js +0 -26
- package/dist/pluginManager.js +5 -340
- 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 -60
- 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 +2 -43
- package/dist/utils/export.js +0 -1
- package/dist/utils/format.js +0 -49
- package/dist/utils/hex.js +0 -124
- package/dist/utils/inspector.js +1 -69
- package/dist/utils/isvalid.js +0 -101
- package/dist/utils/network.js +5 -96
- package/dist/utils/spawn.js +0 -71
- package/dist/utils/tracker.js +1 -64
- package/dist/utils/wait.js +8 -60
- package/npm-shrinkwrap.json +8 -8
- package/package.json +5 -2
- package/scripts/fetch-chip.mjs +100 -0
- package/dist/broadcastServer.d.ts +0 -115
- package/dist/broadcastServer.d.ts.map +0 -1
- package/dist/broadcastServer.js.map +0 -1
- package/dist/broadcastServerTypes.d.ts +0 -806
- package/dist/broadcastServerTypes.d.ts.map +0 -1
- package/dist/broadcastServerTypes.js.map +0 -1
- package/dist/cli.d.ts +0 -30
- 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 -48
- 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 -128
- 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 -76
- 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 -238
- package/dist/frontend.d.ts.map +0 -1
- package/dist/frontend.js.map +0 -1
- package/dist/frontendTypes.d.ts +0 -529
- 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 -478
- 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 -2404
- package/dist/matterbridgeBehaviors.d.ts.map +0 -1
- package/dist/matterbridgeBehaviors.js.map +0 -1
- package/dist/matterbridgeDeviceTypes.d.ts +0 -770
- 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 -1556
- package/dist/matterbridgeEndpoint.d.ts.map +0 -1
- package/dist/matterbridgeEndpoint.js.map +0 -1
- package/dist/matterbridgeEndpointHelpers.d.ts +0 -758
- 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 -239
- package/dist/matterbridgeTypes.d.ts.map +0 -1
- package/dist/matterbridgeTypes.js.map +0 -1
- package/dist/pluginManager.d.ts +0 -371
- 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 -101
- package/dist/utils/colorUtils.d.ts.map +0 -1
- package/dist/utils/colorUtils.js.map +0 -1
- package/dist/utils/commandLine.d.ts +0 -66
- 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/format.d.ts +0 -53
- package/dist/utils/format.d.ts.map +0 -1
- package/dist/utils/format.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/inspector.d.ts +0 -87
- package/dist/utils/inspector.d.ts.map +0 -1
- package/dist/utils/inspector.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 -139
- package/dist/utils/jestHelpers.d.ts.map +0 -1
- package/dist/utils/jestHelpers.js.map +0 -1
- package/dist/utils/network.d.ts +0 -101
- 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/tracker.d.ts +0 -108
- package/dist/utils/tracker.d.ts.map +0 -1
- package/dist/utils/tracker.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,48 +1,19 @@
|
|
|
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 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
|
-
|
|
38
|
-
import '@matter/nodejs'; // Set up Node.js environment for matter.js
|
|
10
|
+
import '@matter/nodejs';
|
|
39
11
|
import { Logger, LogLevel as MatterLogLevel, LogFormat as MatterLogFormat, UINT32_MAX, UINT16_MAX, Crypto, Environment, StorageService } from '@matter/general';
|
|
40
12
|
import { FabricAction, PaseClient } from '@matter/protocol';
|
|
41
13
|
import { Endpoint, ServerNode } from '@matter/node';
|
|
42
14
|
import { DeviceTypeId, VendorId } from '@matter/types/datatype';
|
|
43
15
|
import { AggregatorEndpoint } from '@matter/node/endpoints';
|
|
44
16
|
import { BasicInformationServer } from '@matter/node/behaviors/basic-information';
|
|
45
|
-
// Matterbridge
|
|
46
17
|
import { getParameter, getIntParameter, hasParameter } from './utils/commandLine.js';
|
|
47
18
|
import { copyDirectory } from './utils/copyDirectory.js';
|
|
48
19
|
import { createDirectory } from './utils/createDirectory.js';
|
|
@@ -58,27 +29,19 @@ import { Frontend } from './frontend.js';
|
|
|
58
29
|
import { addVirtualDevices } from './helpers.js';
|
|
59
30
|
import { BroadcastServer } from './broadcastServer.js';
|
|
60
31
|
import { inspectError } from './utils/error.js';
|
|
61
|
-
/**
|
|
62
|
-
* Represents the Matterbridge application.
|
|
63
|
-
*/
|
|
64
32
|
export class Matterbridge extends EventEmitter {
|
|
65
|
-
/** Matterbridge system information */
|
|
66
33
|
systemInformation = {
|
|
67
|
-
// Network properties
|
|
68
34
|
interfaceName: '',
|
|
69
35
|
macAddress: '',
|
|
70
36
|
ipv4Address: '',
|
|
71
37
|
ipv6Address: '',
|
|
72
|
-
// Node.js properties
|
|
73
38
|
nodeVersion: '',
|
|
74
|
-
// Fixed system properties
|
|
75
39
|
hostname: '',
|
|
76
40
|
user: '',
|
|
77
41
|
osType: '',
|
|
78
42
|
osRelease: '',
|
|
79
43
|
osPlatform: '',
|
|
80
44
|
osArch: '',
|
|
81
|
-
// Cpu and memory properties
|
|
82
45
|
totalMemory: '',
|
|
83
46
|
freeMemory: '',
|
|
84
47
|
systemUptime: '',
|
|
@@ -89,7 +52,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
89
52
|
heapTotal: '',
|
|
90
53
|
heapUsed: '',
|
|
91
54
|
};
|
|
92
|
-
// Matterbridge settings
|
|
93
55
|
homeDirectory = '';
|
|
94
56
|
rootDirectory = '';
|
|
95
57
|
matterbridgeDirectory = '';
|
|
@@ -104,15 +66,10 @@ export class Matterbridge extends EventEmitter {
|
|
|
104
66
|
restartMode = '';
|
|
105
67
|
virtualMode = 'outlet';
|
|
106
68
|
profile = getParameter('profile');
|
|
107
|
-
|
|
108
|
-
log = new AnsiLogger({ logName: 'Matterbridge', logTimestampFormat: 4 /* TimestampFormat.TIME_MILLIS */, logLevel: hasParameter('debug') ? "debug" /* LogLevel.DEBUG */ : "info" /* LogLevel.INFO */ });
|
|
109
|
-
/** Whether to log to a file */
|
|
69
|
+
log = new AnsiLogger({ logName: 'Matterbridge', logTimestampFormat: 4, logLevel: hasParameter('debug') ? "debug" : "info" });
|
|
110
70
|
fileLogger = false;
|
|
111
|
-
|
|
112
|
-
matterLog = new AnsiLogger({ logName: 'Matter', logTimestampFormat: 4 /* TimestampFormat.TIME_MILLIS */, logLevel: "debug" /* LogLevel.DEBUG */ });
|
|
113
|
-
/** Whether to log Matter to a file */
|
|
71
|
+
matterLog = new AnsiLogger({ logName: 'Matter', logTimestampFormat: 4, logLevel: "debug" });
|
|
114
72
|
matterFileLogger = false;
|
|
115
|
-
// Frontend settings
|
|
116
73
|
readOnly = hasParameter('readonly') || hasParameter('shelly');
|
|
117
74
|
shellyBoard = hasParameter('shelly');
|
|
118
75
|
shellySysUpdate = false;
|
|
@@ -120,18 +77,12 @@ export class Matterbridge extends EventEmitter {
|
|
|
120
77
|
restartRequired = false;
|
|
121
78
|
fixedRestartRequired = false;
|
|
122
79
|
updateRequired = false;
|
|
123
|
-
// Managers
|
|
124
80
|
plugins = new PluginManager(this);
|
|
125
81
|
devices = new DeviceManager();
|
|
126
|
-
// Frontend
|
|
127
82
|
frontend = new Frontend(this);
|
|
128
|
-
/** Matterbridge node storage manager created in the directory 'storage' in matterbridgeDirectory */
|
|
129
83
|
nodeStorage;
|
|
130
|
-
/** Matterbridge node context created with name 'matterbridge' */
|
|
131
84
|
nodeContext;
|
|
132
|
-
/** The main instance of the Matterbridge class (singleton) */
|
|
133
85
|
static instance;
|
|
134
|
-
// Instance properties
|
|
135
86
|
shutdown = false;
|
|
136
87
|
failCountLimit = hasParameter('shelly') ? 600 : 120;
|
|
137
88
|
hasCleanupStarted = false;
|
|
@@ -146,32 +97,19 @@ export class Matterbridge extends EventEmitter {
|
|
|
146
97
|
sigtermHandler;
|
|
147
98
|
exceptionHandler;
|
|
148
99
|
rejectionHandler;
|
|
149
|
-
/** Matter environment default */
|
|
150
100
|
environment = Environment.default;
|
|
151
|
-
/** Matter storage service from environment default */
|
|
152
101
|
matterStorageService;
|
|
153
|
-
/** Matter storage manager created with name 'Matterbridge' */
|
|
154
102
|
matterStorageManager;
|
|
155
|
-
/** Matter matterbridge storage context created in the storage manager with name 'persist' */
|
|
156
103
|
matterbridgeContext;
|
|
157
104
|
controllerContext;
|
|
158
|
-
/** Matter mdns interface e.g. 'eth0' or 'wlan0' or 'Wi-Fi' */
|
|
159
105
|
mdnsInterface;
|
|
160
|
-
/** Matter listeningAddressIpv4 address */
|
|
161
106
|
ipv4Address;
|
|
162
|
-
/** Matter listeningAddressIpv6 address */
|
|
163
107
|
ipv6Address;
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
/** Matter commissioning discriminator */
|
|
169
|
-
discriminator; // first server node discriminator
|
|
170
|
-
/** Matter device certification */
|
|
171
|
-
certification; // device certification
|
|
172
|
-
/** Matter server node in bridge mode */
|
|
108
|
+
port;
|
|
109
|
+
passcode;
|
|
110
|
+
discriminator;
|
|
111
|
+
certification;
|
|
173
112
|
serverNode;
|
|
174
|
-
/** Matter aggregator node in bridge mode */
|
|
175
113
|
aggregatorNode;
|
|
176
114
|
aggregatorVendorId = VendorId(getIntParameter('vendorId') ?? 0xfff1);
|
|
177
115
|
aggregatorVendorName = getParameter('vendorName') ?? 'Matterbridge';
|
|
@@ -180,13 +118,10 @@ export class Matterbridge extends EventEmitter {
|
|
|
180
118
|
aggregatorDeviceType = DeviceTypeId(getIntParameter('deviceType') ?? bridge.code);
|
|
181
119
|
aggregatorSerialNumber = getParameter('serialNumber');
|
|
182
120
|
aggregatorUniqueId = getParameter('uniqueId');
|
|
183
|
-
/** Advertising nodes map: time advertising started keyed by storeId */
|
|
184
121
|
advertisingNodes = new Map();
|
|
185
|
-
/** Broadcast server */
|
|
186
122
|
server;
|
|
187
123
|
debug = hasParameter('debug') || hasParameter('verbose');
|
|
188
124
|
verbose = hasParameter('verbose');
|
|
189
|
-
/** We load asyncronously so is private */
|
|
190
125
|
constructor() {
|
|
191
126
|
super();
|
|
192
127
|
this.log.logNameColor = '\x1b[38;5;115m';
|
|
@@ -226,19 +161,8 @@ export class Matterbridge extends EventEmitter {
|
|
|
226
161
|
}
|
|
227
162
|
}
|
|
228
163
|
}
|
|
229
|
-
//* ************************************************************************************************************************************ */
|
|
230
|
-
// loadInstance() and cleanup() methods */
|
|
231
|
-
//* ************************************************************************************************************************************ */
|
|
232
|
-
/**
|
|
233
|
-
* Loads an instance of the Matterbridge class.
|
|
234
|
-
* If an instance already exists, return that instance.
|
|
235
|
-
*
|
|
236
|
-
* @param {boolean} initialize - Whether to initialize the Matterbridge instance after loading. Defaults to false.
|
|
237
|
-
* @returns {Matterbridge} A promise that resolves to the Matterbridge instance.
|
|
238
|
-
*/
|
|
239
164
|
static async loadInstance(initialize = false) {
|
|
240
165
|
if (!Matterbridge.instance) {
|
|
241
|
-
// eslint-disable-next-line no-console
|
|
242
166
|
if (hasParameter('debug'))
|
|
243
167
|
console.log(GREEN + 'Creating a new instance of Matterbridge.', initialize ? 'Initializing...' : 'Not initializing...', rs);
|
|
244
168
|
Matterbridge.instance = new Matterbridge();
|
|
@@ -247,131 +171,56 @@ export class Matterbridge extends EventEmitter {
|
|
|
247
171
|
}
|
|
248
172
|
return Matterbridge.instance;
|
|
249
173
|
}
|
|
250
|
-
/**
|
|
251
|
-
* Call cleanup() and dispose MdnsService. Will be removed since matter.js 0.15.6 dispose MdnsService.
|
|
252
|
-
*
|
|
253
|
-
* @param {number} [timeout] - The timeout duration to wait for the cleanup to complete in milliseconds. Default is 1000.
|
|
254
|
-
* @param {number} [pause] - The pause duration after the cleanup in milliseconds. Default is 250.
|
|
255
|
-
*
|
|
256
|
-
* @deprecated This method is deprecated and is ONLY used for jest tests.
|
|
257
|
-
*/
|
|
258
|
-
async destroyInstance(timeout = 1000, pause = 250) {
|
|
259
|
-
this.log.info(`Destroy instance...`);
|
|
260
|
-
// Save server nodes to close
|
|
261
|
-
/*
|
|
262
|
-
const servers: ServerNode<ServerNode.RootEndpoint>[] = [];
|
|
263
|
-
if (this.bridgeMode === 'bridge') {
|
|
264
|
-
if (this.serverNode) servers.push(this.serverNode);
|
|
265
|
-
}
|
|
266
|
-
if (this.bridgeMode === 'childbridge' && this.plugins !== undefined) {
|
|
267
|
-
for (const plugin of this.plugins.array()) {
|
|
268
|
-
if (plugin.serverNode) servers.push(plugin.serverNode);
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
if (this.devices !== undefined) {
|
|
272
|
-
for (const device of this.devices.array()) {
|
|
273
|
-
if (device.mode === 'server' && device.serverNode) servers.push(device.serverNode);
|
|
274
|
-
}
|
|
275
|
-
}
|
|
276
|
-
*/
|
|
277
|
-
// Let any already‐queued microtasks run first
|
|
278
|
-
// await Promise.resolve();
|
|
279
|
-
// Wait for the cleanup to finish
|
|
280
|
-
// await wait(pause, 'destroyInstance start', true);
|
|
281
|
-
// Cleanup
|
|
282
|
-
await this.cleanup('destroying instance...', false, timeout);
|
|
283
|
-
// Close servers mdns service
|
|
284
|
-
/*
|
|
285
|
-
this.log.info(`Dispose ${servers.length} MdnsService...`);
|
|
286
|
-
for (const server of servers) {
|
|
287
|
-
// await server.env.get(MdnsService)[Symbol.asyncDispose]();
|
|
288
|
-
this.log.info(`Closed ${server.id} MdnsService`);
|
|
289
|
-
}
|
|
290
|
-
*/
|
|
291
|
-
// Let any already‐queued microtasks run first
|
|
292
|
-
// await Promise.resolve();
|
|
293
|
-
// Wait for the cleanup to finish
|
|
294
|
-
if (pause)
|
|
295
|
-
await wait(pause, 'destroyInstance stop', true);
|
|
296
|
-
}
|
|
297
|
-
/**
|
|
298
|
-
* Initializes the Matterbridge application.
|
|
299
|
-
*
|
|
300
|
-
* @remarks
|
|
301
|
-
* This method performs the necessary setup and initialization steps for the Matterbridge application.
|
|
302
|
-
* It displays the help information if the 'help' parameter is provided, sets up the logger, checks the
|
|
303
|
-
* node version, registers signal handlers, initializes storage, and parses the command line.
|
|
304
|
-
*
|
|
305
|
-
* @returns {Promise<void>} A Promise that resolves when the initialization is complete.
|
|
306
|
-
*/
|
|
307
174
|
async initialize() {
|
|
308
|
-
// for (let i = 1; i <= 255; i++) console.log(`\x1b[38;5;${i}mColor: ${i}`);
|
|
309
|
-
// Emit the initialize_started event
|
|
310
175
|
this.emit('initialize_started');
|
|
311
|
-
// Set the restart mode
|
|
312
176
|
if (hasParameter('service'))
|
|
313
177
|
this.restartMode = 'service';
|
|
314
178
|
if (hasParameter('docker'))
|
|
315
179
|
this.restartMode = 'docker';
|
|
316
|
-
// Set the matterbridge home directory
|
|
317
180
|
this.homeDirectory = getParameter('homedir') ?? os.homedir();
|
|
318
181
|
await createDirectory(this.homeDirectory, 'Matterbridge Home Directory', this.log);
|
|
319
|
-
// Set the matterbridge directory
|
|
320
182
|
this.matterbridgeDirectory = this.profile ? path.join(this.homeDirectory, '.matterbridge', 'profiles', this.profile) : path.join(this.homeDirectory, '.matterbridge');
|
|
321
183
|
await createDirectory(this.matterbridgeDirectory, 'Matterbridge Directory', this.log);
|
|
322
184
|
await createDirectory(path.join(this.matterbridgeDirectory, 'certs'), 'Matterbridge Frontend Certificate Directory', this.log);
|
|
323
185
|
await createDirectory(path.join(this.matterbridgeDirectory, 'uploads'), 'Matterbridge Frontend Uploads Directory', this.log);
|
|
324
|
-
// Set the matterbridge plugin directory
|
|
325
186
|
this.matterbridgePluginDirectory = this.profile ? path.join(this.homeDirectory, 'Matterbridge', 'profiles', this.profile) : path.join(this.homeDirectory, 'Matterbridge');
|
|
326
187
|
await createDirectory(this.matterbridgePluginDirectory, 'Matterbridge Plugin Directory', this.log);
|
|
327
|
-
// Set the matterbridge cert directory
|
|
328
188
|
this.matterbridgeCertDirectory = this.profile ? path.join(this.homeDirectory, '.mattercert', 'profiles', this.profile) : path.join(this.homeDirectory, '.mattercert');
|
|
329
189
|
await createDirectory(this.matterbridgeCertDirectory, 'Matterbridge Matter Certificate Directory', this.log);
|
|
330
|
-
// Set the matterbridge root directory
|
|
331
190
|
const { fileURLToPath } = await import('node:url');
|
|
332
191
|
const currentFileDirectory = path.dirname(fileURLToPath(import.meta.url));
|
|
333
|
-
this.rootDirectory = path.resolve(currentFileDirectory, '../');
|
|
334
|
-
// Setup the matter environment with default values
|
|
192
|
+
this.rootDirectory = path.resolve(currentFileDirectory, '../');
|
|
335
193
|
this.environment.vars.set('log.level', MatterLogLevel.INFO);
|
|
336
194
|
this.environment.vars.set('log.format', MatterLogFormat.ANSI);
|
|
337
195
|
this.environment.vars.set('path.root', path.join(this.matterbridgeDirectory, MATTER_STORAGE_NAME));
|
|
338
196
|
this.environment.vars.set('runtime.signals', false);
|
|
339
197
|
this.environment.vars.set('runtime.exitcode', false);
|
|
340
|
-
// Register process handlers
|
|
341
198
|
this.registerProcessHandlers();
|
|
342
|
-
// Initialize nodeStorage and nodeContext
|
|
343
199
|
try {
|
|
344
200
|
this.log.debug(`Creating node storage manager: ${CYAN}${NODE_STORAGE_DIR}${db}`);
|
|
345
201
|
this.nodeStorage = new NodeStorageManager({ dir: path.join(this.matterbridgeDirectory, NODE_STORAGE_DIR), writeQueue: false, expiredInterval: undefined, logging: false });
|
|
346
202
|
this.log.debug('Creating node storage context for matterbridge');
|
|
347
203
|
this.nodeContext = await this.nodeStorage.createStorage('matterbridge');
|
|
348
|
-
// TODO: Remove this code when node-persist-manager is updated
|
|
349
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
350
204
|
const keys = (await this.nodeStorage?.storage.keys());
|
|
351
205
|
for (const key of keys) {
|
|
352
206
|
this.log.debug(`Checking node storage manager key: ${CYAN}${key}${db}`);
|
|
353
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
354
207
|
await this.nodeStorage?.storage.get(key);
|
|
355
208
|
}
|
|
356
209
|
const storages = await this.nodeStorage.getStorageNames();
|
|
357
210
|
for (const storage of storages) {
|
|
358
211
|
this.log.debug(`Checking storage: ${CYAN}${storage}${db}`);
|
|
359
212
|
const nodeContext = await this.nodeStorage?.createStorage(storage);
|
|
360
|
-
// TODO: Remove this code when node-persist-manager is updated
|
|
361
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
362
213
|
const keys = (await nodeContext?.storage.keys());
|
|
363
214
|
keys.forEach(async (key) => {
|
|
364
215
|
this.log.debug(`Checking key: ${CYAN}${storage}:${key}${db}`);
|
|
365
216
|
await nodeContext?.get(key);
|
|
366
217
|
});
|
|
367
218
|
}
|
|
368
|
-
// Creating a backup of the node storage since it is not corrupted
|
|
369
219
|
this.log.debug('Creating node storage backup...');
|
|
370
220
|
await copyDirectory(path.join(this.matterbridgeDirectory, NODE_STORAGE_DIR), path.join(this.matterbridgeDirectory, NODE_STORAGE_DIR + '.backup'));
|
|
371
221
|
this.log.debug('Created node storage backup');
|
|
372
222
|
}
|
|
373
223
|
catch (error) {
|
|
374
|
-
// Restoring the backup of the node storage since it is corrupted
|
|
375
224
|
this.log.error(`Error creating node storage manager and context: ${error instanceof Error ? error.message : error}`);
|
|
376
225
|
if (hasParameter('norestore')) {
|
|
377
226
|
this.log.fatal(`The matterbridge storage is corrupted. Found -norestore parameter: exiting...`);
|
|
@@ -385,19 +234,14 @@ export class Matterbridge extends EventEmitter {
|
|
|
385
234
|
if (!this.nodeStorage || !this.nodeContext) {
|
|
386
235
|
throw new Error('Fatal error creating node storage manager and context for matterbridge');
|
|
387
236
|
}
|
|
388
|
-
// Set the first port to use for the commissioning server (will be incremented in childbridge mode)
|
|
389
237
|
this.port = getIntParameter('port') ?? (await this.nodeContext.get('matterport', 5540)) ?? 5540;
|
|
390
|
-
// Set the first passcode to use for the commissioning server (will be incremented in childbridge mode)
|
|
391
238
|
this.passcode = getIntParameter('passcode') ?? (await this.nodeContext.get('matterpasscode')) ?? PaseClient.generateRandomPasscode(this.environment.get(Crypto));
|
|
392
|
-
// Set the first discriminator to use for the commissioning server (will be incremented in childbridge mode)
|
|
393
239
|
this.discriminator = getIntParameter('discriminator') ?? (await this.nodeContext.get('matterdiscriminator')) ?? PaseClient.generateRandomDiscriminator(this.environment.get(Crypto));
|
|
394
|
-
// Certificate management
|
|
395
240
|
const pairingFilePath = path.join(this.matterbridgeCertDirectory, 'pairing.json');
|
|
396
241
|
try {
|
|
397
242
|
await fs.promises.access(pairingFilePath, fs.constants.R_OK);
|
|
398
243
|
const pairingFileContent = await fs.promises.readFile(pairingFilePath, 'utf8');
|
|
399
244
|
const pairingFileJson = JSON.parse(pairingFileContent);
|
|
400
|
-
// Set the vendorId, vendorName, productId, productName, deviceType, serialNumber, uniqueId if they are present in the pairing file
|
|
401
245
|
if (isValidNumber(pairingFileJson.vendorId)) {
|
|
402
246
|
this.aggregatorVendorId = VendorId(pairingFileJson.vendorId);
|
|
403
247
|
this.log.info(`Pairing file ${CYAN}${pairingFilePath}${nf} found. Using vendorId ${CYAN}${this.aggregatorVendorId}${nf} from pairing file.`);
|
|
@@ -426,13 +270,11 @@ export class Matterbridge extends EventEmitter {
|
|
|
426
270
|
this.aggregatorUniqueId = pairingFileJson.uniqueId;
|
|
427
271
|
this.log.info(`Pairing file ${CYAN}${pairingFilePath}${nf} found. Using uniqueId ${CYAN}${this.aggregatorUniqueId}${nf} from pairing file.`);
|
|
428
272
|
}
|
|
429
|
-
// Override the passcode and discriminator if they are present in the pairing file
|
|
430
273
|
if (isValidNumber(pairingFileJson.passcode) && isValidNumber(pairingFileJson.discriminator)) {
|
|
431
274
|
this.passcode = pairingFileJson.passcode;
|
|
432
275
|
this.discriminator = pairingFileJson.discriminator;
|
|
433
276
|
this.log.info(`Pairing file ${CYAN}${pairingFilePath}${nf} found. Using passcode ${CYAN}${this.passcode}${nf} and discriminator ${CYAN}${this.discriminator}${nf} from pairing file.`);
|
|
434
277
|
}
|
|
435
|
-
// Set the certification for matter.js if it is present in the pairing file
|
|
436
278
|
if (pairingFileJson.privateKey && pairingFileJson.certificate && pairingFileJson.intermediateCertificate && pairingFileJson.declaration) {
|
|
437
279
|
const { hexToBuffer } = await import('./utils/hex.js');
|
|
438
280
|
this.certification = {
|
|
@@ -447,43 +289,40 @@ export class Matterbridge extends EventEmitter {
|
|
|
447
289
|
catch (error) {
|
|
448
290
|
this.log.debug(`Pairing file ${CYAN}${pairingFilePath}${db} not found: ${error instanceof Error ? error.message : error}`);
|
|
449
291
|
}
|
|
450
|
-
// Store the passcode, discriminator and port in the node context
|
|
451
292
|
await this.nodeContext.set('matterport', this.port);
|
|
452
293
|
await this.nodeContext.set('matterpasscode', this.passcode);
|
|
453
294
|
await this.nodeContext.set('matterdiscriminator', this.discriminator);
|
|
454
295
|
this.log.debug(`Initializing server node for Matterbridge on port ${this.port} with passcode ${this.passcode} and discriminator ${this.discriminator}`);
|
|
455
|
-
// Set matterbridge logger level (context: matterbridgeLogLevel)
|
|
456
296
|
if (hasParameter('logger')) {
|
|
457
297
|
const level = getParameter('logger');
|
|
458
298
|
if (level === 'debug') {
|
|
459
|
-
this.log.logLevel = "debug"
|
|
299
|
+
this.log.logLevel = "debug";
|
|
460
300
|
}
|
|
461
301
|
else if (level === 'info') {
|
|
462
|
-
this.log.logLevel = "info"
|
|
302
|
+
this.log.logLevel = "info";
|
|
463
303
|
}
|
|
464
304
|
else if (level === 'notice') {
|
|
465
|
-
this.log.logLevel = "notice"
|
|
305
|
+
this.log.logLevel = "notice";
|
|
466
306
|
}
|
|
467
307
|
else if (level === 'warn') {
|
|
468
|
-
this.log.logLevel = "warn"
|
|
308
|
+
this.log.logLevel = "warn";
|
|
469
309
|
}
|
|
470
310
|
else if (level === 'error') {
|
|
471
|
-
this.log.logLevel = "error"
|
|
311
|
+
this.log.logLevel = "error";
|
|
472
312
|
}
|
|
473
313
|
else if (level === 'fatal') {
|
|
474
|
-
this.log.logLevel = "fatal"
|
|
314
|
+
this.log.logLevel = "fatal";
|
|
475
315
|
}
|
|
476
316
|
else {
|
|
477
317
|
this.log.warn(`Invalid matterbridge logger level: ${level}. Using default level "info".`);
|
|
478
|
-
this.log.logLevel = "info"
|
|
318
|
+
this.log.logLevel = "info";
|
|
479
319
|
}
|
|
480
320
|
}
|
|
481
321
|
else {
|
|
482
|
-
this.log.logLevel = await this.nodeContext.get('matterbridgeLogLevel', this.shellyBoard ? "notice"
|
|
322
|
+
this.log.logLevel = await this.nodeContext.get('matterbridgeLogLevel', this.shellyBoard ? "notice" : "info");
|
|
483
323
|
}
|
|
484
324
|
this.frontend.logLevel = this.log.logLevel;
|
|
485
325
|
MatterbridgeEndpoint.logLevel = this.log.logLevel;
|
|
486
|
-
// Create the file logger for matterbridge (context: matterbridgeFileLog)
|
|
487
326
|
if (hasParameter('filelogger') || (await this.nodeContext.get('matterbridgeFileLog', false))) {
|
|
488
327
|
AnsiLogger.setGlobalLogfile(path.join(this.matterbridgeDirectory, MATTERBRIDGE_LOGGER_FILE), this.log.logLevel, true);
|
|
489
328
|
this.fileLogger = true;
|
|
@@ -492,7 +331,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
492
331
|
this.log.debug(`Matterbridge logLevel: ${this.log.logLevel} fileLoger: ${this.fileLogger}.`);
|
|
493
332
|
if (this.profile !== undefined)
|
|
494
333
|
this.log.debug(`Matterbridge profile: ${this.profile}.`);
|
|
495
|
-
// Set matter.js logger level, format and logger (context: matterLogLevel)
|
|
496
334
|
if (hasParameter('matterlogger')) {
|
|
497
335
|
const level = getParameter('matterlogger');
|
|
498
336
|
if (level === 'debug') {
|
|
@@ -522,13 +360,11 @@ export class Matterbridge extends EventEmitter {
|
|
|
522
360
|
Logger.level = (await this.nodeContext.get('matterLogLevel', this.shellyBoard ? MatterLogLevel.NOTICE : MatterLogLevel.INFO));
|
|
523
361
|
}
|
|
524
362
|
Logger.format = MatterLogFormat.ANSI;
|
|
525
|
-
// Create the logger for matter.js with file logging (context: matterFileLog)
|
|
526
363
|
if (hasParameter('matterfilelogger') || (await this.nodeContext.get('matterFileLog', false))) {
|
|
527
364
|
this.matterFileLogger = true;
|
|
528
365
|
}
|
|
529
366
|
Logger.destinations.default.write = this.createDestinationMatterLogger(this.matterFileLogger);
|
|
530
367
|
this.log.debug(`Matter logLevel: ${Logger.level} fileLoger: ${this.matterFileLogger}.`);
|
|
531
|
-
// Log network interfaces
|
|
532
368
|
const networkInterfaces = os.networkInterfaces();
|
|
533
369
|
const availableAddresses = Object.entries(networkInterfaces);
|
|
534
370
|
const availableInterfaceNames = Object.keys(networkInterfaces);
|
|
@@ -541,7 +377,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
541
377
|
});
|
|
542
378
|
}
|
|
543
379
|
}
|
|
544
|
-
// Set the interface to use for matter server node mdnsInterface
|
|
545
380
|
if (hasParameter('mdnsinterface')) {
|
|
546
381
|
this.mdnsInterface = getParameter('mdnsinterface');
|
|
547
382
|
}
|
|
@@ -550,7 +385,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
550
385
|
if (this.mdnsInterface === '')
|
|
551
386
|
this.mdnsInterface = undefined;
|
|
552
387
|
}
|
|
553
|
-
// Validate mdnsInterface
|
|
554
388
|
if (this.mdnsInterface) {
|
|
555
389
|
if (!availableInterfaceNames.includes(this.mdnsInterface)) {
|
|
556
390
|
this.log.error(`Invalid mdnsinterface: ${this.mdnsInterface}. Available interfaces are: ${availableInterfaceNames.join(', ')}. Using all available interfaces.`);
|
|
@@ -563,7 +397,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
563
397
|
}
|
|
564
398
|
if (this.mdnsInterface)
|
|
565
399
|
this.environment.vars.set('mdns.networkInterface', this.mdnsInterface);
|
|
566
|
-
// Set the listeningAddressIpv4 for the matter commissioning server
|
|
567
400
|
if (hasParameter('ipv4address')) {
|
|
568
401
|
this.ipv4Address = getParameter('ipv4address');
|
|
569
402
|
}
|
|
@@ -572,7 +405,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
572
405
|
if (this.ipv4Address === '')
|
|
573
406
|
this.ipv4Address = undefined;
|
|
574
407
|
}
|
|
575
|
-
// Validate ipv4address
|
|
576
408
|
if (this.ipv4Address) {
|
|
577
409
|
let isValid = false;
|
|
578
410
|
for (const [ifaceName, ifaces] of availableAddresses) {
|
|
@@ -588,7 +420,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
588
420
|
await this.nodeContext.remove('matteripv4address');
|
|
589
421
|
}
|
|
590
422
|
}
|
|
591
|
-
// Set the listeningAddressIpv6 for the matter commissioning server
|
|
592
423
|
if (hasParameter('ipv6address')) {
|
|
593
424
|
this.ipv6Address = getParameter('ipv6address');
|
|
594
425
|
}
|
|
@@ -597,7 +428,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
597
428
|
if (this.ipv6Address === '')
|
|
598
429
|
this.ipv6Address = undefined;
|
|
599
430
|
}
|
|
600
|
-
// Validate ipv6address
|
|
601
431
|
if (this.ipv6Address) {
|
|
602
432
|
let isValid = false;
|
|
603
433
|
for (const [ifaceName, ifaces] of availableAddresses) {
|
|
@@ -606,7 +436,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
606
436
|
isValid = true;
|
|
607
437
|
break;
|
|
608
438
|
}
|
|
609
|
-
/* istanbul ignore next */
|
|
610
439
|
if (ifaces && ifaces.find((iface) => iface.scopeid && iface.scopeid > 0 && iface.address + '%' + (process.platform === 'win32' ? iface.scopeid : ifaceName) === this.ipv6Address)) {
|
|
611
440
|
this.log.info(`Using ipv6address ${CYAN}${this.ipv6Address}${nf} on interface ${CYAN}${ifaceName}${nf} for the Matter server node.`);
|
|
612
441
|
isValid = true;
|
|
@@ -619,7 +448,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
619
448
|
await this.nodeContext.remove('matteripv6address');
|
|
620
449
|
}
|
|
621
450
|
}
|
|
622
|
-
// Initialize the virtual mode
|
|
623
451
|
if (hasParameter('novirtual')) {
|
|
624
452
|
this.virtualMode = 'disabled';
|
|
625
453
|
await this.nodeContext.set('virtualmode', 'disabled');
|
|
@@ -628,15 +456,10 @@ export class Matterbridge extends EventEmitter {
|
|
|
628
456
|
this.virtualMode = (await this.nodeContext.get('virtualmode', 'outlet'));
|
|
629
457
|
}
|
|
630
458
|
this.log.debug(`Virtual mode ${this.virtualMode}.`);
|
|
631
|
-
// Initialize PluginManager
|
|
632
459
|
this.plugins.logLevel = this.log.logLevel;
|
|
633
460
|
await this.plugins.loadFromStorage();
|
|
634
|
-
// Initialize DeviceManager
|
|
635
461
|
this.devices.logLevel = this.log.logLevel;
|
|
636
|
-
// Get the plugins from node storage and create the plugins node storage contexts
|
|
637
462
|
for (const plugin of this.plugins) {
|
|
638
|
-
// Try to reinstall the plugin from npm (for Docker pull and external plugins)
|
|
639
|
-
// We don't do this when the add and other shutdown parameters are set because we shut down the process after adding the plugin
|
|
640
463
|
if (!fs.existsSync(plugin.path) && !hasParameter('add') && !hasParameter('remove') && !hasParameter('enable') && !hasParameter('disable') && !hasParameter('reset') && !hasParameter('factoryreset')) {
|
|
641
464
|
this.log.info(`Error parsing plugin ${plg}${plugin.name}${nf}. Trying to reinstall it from npm...`);
|
|
642
465
|
try {
|
|
@@ -667,7 +490,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
667
490
|
await plugin.nodeContext.set('description', plugin.description);
|
|
668
491
|
await plugin.nodeContext.set('author', plugin.author);
|
|
669
492
|
}
|
|
670
|
-
// Log system info and create .matterbridge directory
|
|
671
493
|
await this.logNodeAndSystemInfo();
|
|
672
494
|
this.log.notice(`Matterbridge version ${this.matterbridgeVersion} ` +
|
|
673
495
|
`${hasParameter('bridge') || (!hasParameter('childbridge') && (await this.nodeContext?.get('bridgeMode', '')) === 'bridge') ? 'mode bridge ' : ''}` +
|
|
@@ -675,7 +497,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
675
497
|
`${hasParameter('controller') ? 'mode controller ' : ''}` +
|
|
676
498
|
`${this.restartMode !== '' ? 'restart mode ' + this.restartMode + ' ' : ''}` +
|
|
677
499
|
`running on ${this.systemInformation.osType} (v.${this.systemInformation.osRelease}) platform ${this.systemInformation.osPlatform} arch ${this.systemInformation.osArch}`);
|
|
678
|
-
// Check node version and throw error
|
|
679
500
|
const minNodeVersion = 20;
|
|
680
501
|
const nodeVersion = process.versions.node;
|
|
681
502
|
const versionMajor = parseInt(nodeVersion.split('.')[0]);
|
|
@@ -683,18 +504,10 @@ export class Matterbridge extends EventEmitter {
|
|
|
683
504
|
this.log.error(`Node version ${versionMajor} is not supported. Please upgrade to ${minNodeVersion} or above.`);
|
|
684
505
|
throw new Error(`Node version ${versionMajor} is not supported. Please upgrade to ${minNodeVersion} or above.`);
|
|
685
506
|
}
|
|
686
|
-
// Parse command line
|
|
687
507
|
await this.parseCommandLine();
|
|
688
|
-
// Emit the initialize_completed event
|
|
689
508
|
this.emit('initialize_completed');
|
|
690
509
|
this.initialized = true;
|
|
691
510
|
}
|
|
692
|
-
/**
|
|
693
|
-
* Parses the command line arguments and performs the corresponding actions.
|
|
694
|
-
*
|
|
695
|
-
* @private
|
|
696
|
-
* @returns {Promise<void>} A promise that resolves when the command line arguments have been processed, or the process exits.
|
|
697
|
-
*/
|
|
698
511
|
async parseCommandLine() {
|
|
699
512
|
if (hasParameter('list')) {
|
|
700
513
|
this.log.info(`│ Registered plugins (${this.plugins.length})`);
|
|
@@ -710,19 +523,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
710
523
|
}
|
|
711
524
|
index++;
|
|
712
525
|
}
|
|
713
|
-
/*
|
|
714
|
-
const serializedRegisteredDevices = await this.nodeContext?.get<SerializedMatterbridgeEndpoint[]>('devices', []);
|
|
715
|
-
this.log.info(`│ Registered devices (${serializedRegisteredDevices?.length})`);
|
|
716
|
-
serializedRegisteredDevices?.forEach((device, index) => {
|
|
717
|
-
if (index !== serializedRegisteredDevices.length - 1) {
|
|
718
|
-
this.log.info(`├─┬─ plugin ${plg}${device.pluginName}${nf} device: ${dev}${device.deviceName}${nf} uniqueId: ${YELLOW}${device.uniqueId}${nf}`);
|
|
719
|
-
this.log.info(`│ └─ endpoint ${RED}${device.endpoint}${nf} ${typ}${device.endpointName}${nf} ${debugStringify(device.clusterServersId)}`);
|
|
720
|
-
} else {
|
|
721
|
-
this.log.info(`└─┬─ plugin ${plg}${device.pluginName}${nf} device: ${dev}${device.deviceName}${nf} uniqueId: ${YELLOW}${device.uniqueId}${nf}`);
|
|
722
|
-
this.log.info(` └─ endpoint ${RED}${device.endpoint}${nf} ${typ}${device.endpointName}${nf} ${debugStringify(device.clusterServersId)}`);
|
|
723
|
-
}
|
|
724
|
-
});
|
|
725
|
-
*/
|
|
726
526
|
this.shutdown = true;
|
|
727
527
|
return;
|
|
728
528
|
}
|
|
@@ -772,10 +572,8 @@ export class Matterbridge extends EventEmitter {
|
|
|
772
572
|
this.shutdown = true;
|
|
773
573
|
return;
|
|
774
574
|
}
|
|
775
|
-
// Initialize frontend
|
|
776
575
|
if (getIntParameter('frontend') !== 0 || getIntParameter('frontend') === undefined)
|
|
777
576
|
await this.frontend.start(getIntParameter('frontend'));
|
|
778
|
-
// Start the matter storage and create the matterbridge context
|
|
779
577
|
try {
|
|
780
578
|
await this.startMatterStorage();
|
|
781
579
|
if (this.aggregatorSerialNumber && this.aggregatorUniqueId && this.matterStorageService) {
|
|
@@ -789,21 +587,18 @@ export class Matterbridge extends EventEmitter {
|
|
|
789
587
|
this.log.fatal(`Fatal error creating matter storage: ${error instanceof Error ? error.message : error}`);
|
|
790
588
|
throw new Error(`Fatal error creating matter storage: ${error instanceof Error ? error.message : error}`);
|
|
791
589
|
}
|
|
792
|
-
// Clear the matterbridge context if the reset parameter is set (bridge mode)
|
|
793
590
|
if (hasParameter('reset') && getParameter('reset') === undefined) {
|
|
794
591
|
this.initialized = true;
|
|
795
592
|
await this.shutdownProcessAndReset();
|
|
796
593
|
this.shutdown = true;
|
|
797
594
|
return;
|
|
798
595
|
}
|
|
799
|
-
// Clear matterbridge plugin context if the reset parameter is set (childbridge mode)
|
|
800
596
|
if (hasParameter('reset') && getParameter('reset') !== undefined) {
|
|
801
597
|
this.log.debug(`Reset plugin ${getParameter('reset')}`);
|
|
802
598
|
const plugin = this.plugins.get(getParameter('reset'));
|
|
803
599
|
if (plugin) {
|
|
804
600
|
const matterStorageManager = await this.matterStorageService?.open(plugin.name);
|
|
805
601
|
if (!matterStorageManager) {
|
|
806
|
-
/* istanbul ignore next */
|
|
807
602
|
this.log.error(`Plugin ${plg}${plugin.name}${er} storageManager not found`);
|
|
808
603
|
}
|
|
809
604
|
else {
|
|
@@ -822,42 +617,35 @@ export class Matterbridge extends EventEmitter {
|
|
|
822
617
|
this.shutdown = true;
|
|
823
618
|
return;
|
|
824
619
|
}
|
|
825
|
-
// Check in 30 seconds the latest and dev versions of matterbridge and the plugins
|
|
826
620
|
clearTimeout(this.checkUpdateTimeout);
|
|
827
621
|
this.checkUpdateTimeout = setTimeout(async () => {
|
|
828
622
|
const { checkUpdates } = await import('./update.js');
|
|
829
623
|
checkUpdates(this);
|
|
830
624
|
}, 30 * 1000).unref();
|
|
831
|
-
// Check each 12 hours the latest and dev versions of matterbridge and the plugins
|
|
832
625
|
clearInterval(this.checkUpdateInterval);
|
|
833
626
|
this.checkUpdateInterval = setInterval(async () => {
|
|
834
627
|
const { checkUpdates } = await import('./update.js');
|
|
835
628
|
checkUpdates(this);
|
|
836
629
|
}, 12 * 60 * 60 * 1000).unref();
|
|
837
|
-
// Start the matterbridge in mode test
|
|
838
630
|
if (hasParameter('test')) {
|
|
839
631
|
this.bridgeMode = 'bridge';
|
|
840
632
|
return;
|
|
841
633
|
}
|
|
842
|
-
// Start the matterbridge in mode controller
|
|
843
634
|
if (hasParameter('controller')) {
|
|
844
635
|
this.bridgeMode = 'controller';
|
|
845
636
|
await this.startController();
|
|
846
637
|
return;
|
|
847
638
|
}
|
|
848
|
-
// Check if the bridge mode is set and start matterbridge in bridge mode if not set
|
|
849
639
|
if (!hasParameter('bridge') && !hasParameter('childbridge') && (await this.nodeContext?.get('bridgeMode', '')) === '') {
|
|
850
640
|
this.log.info('Setting default matterbridge start mode to bridge');
|
|
851
641
|
await this.nodeContext?.set('bridgeMode', 'bridge');
|
|
852
642
|
}
|
|
853
|
-
// Start matterbridge in bridge mode
|
|
854
643
|
if (hasParameter('bridge') || (!hasParameter('childbridge') && (await this.nodeContext?.get('bridgeMode', '')) === 'bridge')) {
|
|
855
644
|
this.bridgeMode = 'bridge';
|
|
856
645
|
this.log.debug(`Starting matterbridge in mode ${this.bridgeMode}`);
|
|
857
646
|
await this.startBridge();
|
|
858
647
|
return;
|
|
859
648
|
}
|
|
860
|
-
// Start matterbridge in childbridge mode
|
|
861
649
|
if (hasParameter('childbridge') || (!hasParameter('bridge') && (await this.nodeContext?.get('bridgeMode', '')) === 'childbridge')) {
|
|
862
650
|
this.bridgeMode = 'childbridge';
|
|
863
651
|
this.log.debug(`Starting matterbridge in mode ${this.bridgeMode}`);
|
|
@@ -865,22 +653,10 @@ export class Matterbridge extends EventEmitter {
|
|
|
865
653
|
return;
|
|
866
654
|
}
|
|
867
655
|
}
|
|
868
|
-
/**
|
|
869
|
-
* Asynchronously loads and starts the registered plugins.
|
|
870
|
-
*
|
|
871
|
-
* This method is responsible for initializing and starting all enabled plugins.
|
|
872
|
-
* It ensures that each plugin is properly loaded and started before the bridge starts.
|
|
873
|
-
*
|
|
874
|
-
* @param {boolean} [wait] - If true, the method will wait for all plugins to be fully loaded and started before resolving.
|
|
875
|
-
* @param {boolean} [start] - If true, the method will start the plugins after loading them.
|
|
876
|
-
* @returns {Promise<void>} A promise that resolves when all plugins have been loaded and started.
|
|
877
|
-
*/
|
|
878
656
|
async startPlugins(wait = false, start = true) {
|
|
879
|
-
// Check, load and start the plugins
|
|
880
657
|
for (const plugin of this.plugins) {
|
|
881
658
|
plugin.configJson = await this.plugins.loadConfig(plugin);
|
|
882
659
|
plugin.schemaJson = await this.plugins.loadSchema(plugin);
|
|
883
|
-
// Check if the plugin is available
|
|
884
660
|
if (!(await this.plugins.resolve(plugin.path))) {
|
|
885
661
|
this.log.error(`Plugin ${plg}${plugin.name}${er} not found or not validated. Disabling it.`);
|
|
886
662
|
plugin.enabled = false;
|
|
@@ -900,16 +676,10 @@ export class Matterbridge extends EventEmitter {
|
|
|
900
676
|
if (wait)
|
|
901
677
|
await this.plugins.load(plugin, start, 'Matterbridge is starting');
|
|
902
678
|
else
|
|
903
|
-
this.plugins.load(plugin, start, 'Matterbridge is starting');
|
|
679
|
+
this.plugins.load(plugin, start, 'Matterbridge is starting');
|
|
904
680
|
}
|
|
905
681
|
this.frontend.wssSendRefreshRequired('plugins');
|
|
906
682
|
}
|
|
907
|
-
/**
|
|
908
|
-
* Registers the process handlers for uncaughtException, unhandledRejection, SIGINT and SIGTERM.
|
|
909
|
-
* - When an uncaught exception occurs, the exceptionHandler logs the error message and stack trace.
|
|
910
|
-
* - When an unhandled promise rejection occurs, the rejectionHandler logs the reason and stack trace.
|
|
911
|
-
* - When either of SIGINT and SIGTERM signals are received, the cleanup method is called with an appropriate message.
|
|
912
|
-
*/
|
|
913
683
|
registerProcessHandlers() {
|
|
914
684
|
this.log.debug(`Registering uncaughtException and unhandledRejection handlers...`);
|
|
915
685
|
process.removeAllListeners('uncaughtException');
|
|
@@ -936,9 +706,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
936
706
|
};
|
|
937
707
|
process.on('SIGTERM', this.sigtermHandler);
|
|
938
708
|
}
|
|
939
|
-
/**
|
|
940
|
-
* Deregisters the process uncaughtException, unhandledRejection, SIGINT and SIGTERM signal handlers.
|
|
941
|
-
*/
|
|
942
709
|
deregisterProcessHandlers() {
|
|
943
710
|
this.log.debug(`Deregistering uncaughtException and unhandledRejection handlers...`);
|
|
944
711
|
if (this.exceptionHandler)
|
|
@@ -955,18 +722,13 @@ export class Matterbridge extends EventEmitter {
|
|
|
955
722
|
process.off('SIGTERM', this.sigtermHandler);
|
|
956
723
|
this.sigtermHandler = undefined;
|
|
957
724
|
}
|
|
958
|
-
/**
|
|
959
|
-
* Logs the node and system information.
|
|
960
|
-
*/
|
|
961
725
|
async logNodeAndSystemInfo() {
|
|
962
|
-
// IP address information
|
|
963
726
|
const networkInterfaces = os.networkInterfaces();
|
|
964
727
|
this.systemInformation.interfaceName = '';
|
|
965
728
|
this.systemInformation.ipv4Address = '';
|
|
966
729
|
this.systemInformation.ipv6Address = '';
|
|
967
730
|
this.systemInformation.macAddress = '';
|
|
968
731
|
for (const [interfaceName, interfaceDetails] of Object.entries(networkInterfaces)) {
|
|
969
|
-
// this.log.debug(`Checking interface: '${interfaceName}' for '${this.mdnsInterface}'`);
|
|
970
732
|
if (this.mdnsInterface && interfaceName !== this.mdnsInterface)
|
|
971
733
|
continue;
|
|
972
734
|
if (!interfaceDetails) {
|
|
@@ -992,18 +754,16 @@ export class Matterbridge extends EventEmitter {
|
|
|
992
754
|
break;
|
|
993
755
|
}
|
|
994
756
|
}
|
|
995
|
-
// Node information
|
|
996
757
|
this.systemInformation.nodeVersion = process.versions.node;
|
|
997
758
|
const versionMajor = parseInt(this.systemInformation.nodeVersion.split('.')[0]);
|
|
998
759
|
const versionMinor = parseInt(this.systemInformation.nodeVersion.split('.')[1]);
|
|
999
760
|
const versionPatch = parseInt(this.systemInformation.nodeVersion.split('.')[2]);
|
|
1000
|
-
// Host system information
|
|
1001
761
|
this.systemInformation.hostname = os.hostname();
|
|
1002
762
|
this.systemInformation.user = os.userInfo().username;
|
|
1003
|
-
this.systemInformation.osType = os.type();
|
|
1004
|
-
this.systemInformation.osRelease = os.release();
|
|
1005
|
-
this.systemInformation.osPlatform = os.platform();
|
|
1006
|
-
this.systemInformation.osArch = os.arch();
|
|
763
|
+
this.systemInformation.osType = os.type();
|
|
764
|
+
this.systemInformation.osRelease = os.release();
|
|
765
|
+
this.systemInformation.osPlatform = os.platform();
|
|
766
|
+
this.systemInformation.osArch = os.arch();
|
|
1007
767
|
this.systemInformation.totalMemory = formatBytes(os.totalmem());
|
|
1008
768
|
this.systemInformation.freeMemory = formatBytes(os.freemem());
|
|
1009
769
|
this.systemInformation.systemUptime = formatUptime(os.uptime());
|
|
@@ -1013,7 +773,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1013
773
|
this.systemInformation.rss = formatBytes(process.memoryUsage().rss);
|
|
1014
774
|
this.systemInformation.heapTotal = formatBytes(process.memoryUsage().heapTotal);
|
|
1015
775
|
this.systemInformation.heapUsed = formatBytes(process.memoryUsage().heapUsed);
|
|
1016
|
-
// Log the system information
|
|
1017
776
|
this.log.debug('Host System Information:');
|
|
1018
777
|
this.log.debug(`- Hostname: ${this.systemInformation.hostname}`);
|
|
1019
778
|
this.log.debug(`- User: ${this.systemInformation.user}`);
|
|
@@ -1033,17 +792,14 @@ export class Matterbridge extends EventEmitter {
|
|
|
1033
792
|
this.log.debug(`- RSS: ${this.systemInformation.rss}`);
|
|
1034
793
|
this.log.debug(`- Heap Total: ${this.systemInformation.heapTotal}`);
|
|
1035
794
|
this.log.debug(`- Heap Used: ${this.systemInformation.heapUsed}`);
|
|
1036
|
-
// Log directories
|
|
1037
795
|
this.log.debug(`Root Directory: ${this.rootDirectory}`);
|
|
1038
796
|
this.log.debug(`Home Directory: ${this.homeDirectory}`);
|
|
1039
797
|
this.log.debug(`Matterbridge Directory: ${this.matterbridgeDirectory}`);
|
|
1040
798
|
this.log.debug(`Matterbridge Plugin Directory: ${this.matterbridgePluginDirectory}`);
|
|
1041
799
|
this.log.debug(`Matterbridge Matter Certificate Directory: ${this.matterbridgeCertDirectory}`);
|
|
1042
|
-
// Global node_modules directory
|
|
1043
800
|
if (this.nodeContext)
|
|
1044
801
|
this.globalModulesDirectory = await this.nodeContext.get('globalModulesDirectory', '');
|
|
1045
802
|
if (this.globalModulesDirectory === '') {
|
|
1046
|
-
// First run of Matterbridge so the node storage is empty
|
|
1047
803
|
this.log.debug(`Getting global node_modules directory...`);
|
|
1048
804
|
try {
|
|
1049
805
|
const { getGlobalNodeModules } = await import('./utils/network.js');
|
|
@@ -1056,7 +812,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1056
812
|
}
|
|
1057
813
|
}
|
|
1058
814
|
else {
|
|
1059
|
-
// The global node_modules directory is already set in the node storage and we check if it is still valid
|
|
1060
815
|
this.log.debug(`Checking global node_modules directory: ${this.globalModulesDirectory}`);
|
|
1061
816
|
try {
|
|
1062
817
|
const { getGlobalNodeModules } = await import('./utils/network.js');
|
|
@@ -1068,37 +823,25 @@ export class Matterbridge extends EventEmitter {
|
|
|
1068
823
|
this.log.error(`Error checking global node_modules directory: ${error}`);
|
|
1069
824
|
}
|
|
1070
825
|
}
|
|
1071
|
-
// Matterbridge version
|
|
1072
826
|
this.log.debug(`Reading matterbridge package.json...`);
|
|
1073
827
|
const packageJson = JSON.parse(await fs.promises.readFile(path.join(this.rootDirectory, 'package.json'), 'utf-8'));
|
|
1074
828
|
this.matterbridgeVersion = this.matterbridgeLatestVersion = this.matterbridgeDevVersion = packageJson.version;
|
|
1075
829
|
this.log.debug(`Matterbridge Version: ${this.matterbridgeVersion}`);
|
|
1076
|
-
// Matterbridge latest version (will be set in the checkUpdate function)
|
|
1077
830
|
if (this.nodeContext)
|
|
1078
831
|
this.matterbridgeLatestVersion = await this.nodeContext.get('matterbridgeLatestVersion', this.matterbridgeVersion);
|
|
1079
832
|
this.log.debug(`Matterbridge Latest Version: ${this.matterbridgeLatestVersion}`);
|
|
1080
|
-
// Matterbridge dev version (will be set in the checkUpdate function)
|
|
1081
833
|
if (this.nodeContext)
|
|
1082
834
|
this.matterbridgeDevVersion = await this.nodeContext.get('matterbridgeDevVersion', this.matterbridgeVersion);
|
|
1083
835
|
this.log.debug(`Matterbridge Dev Version: ${this.matterbridgeDevVersion}`);
|
|
1084
|
-
// Frontend version
|
|
1085
836
|
this.log.debug(`Reading frontend package.json...`);
|
|
1086
837
|
const frontendPackageJson = JSON.parse(await fs.promises.readFile(path.join(this.rootDirectory, 'frontend/package.json'), 'utf8'));
|
|
1087
838
|
this.frontendVersion = frontendPackageJson.version;
|
|
1088
839
|
this.log.debug(`Frontend version ${CYAN}${this.frontendVersion}${db}`);
|
|
1089
|
-
// Current working directory
|
|
1090
840
|
const currentDir = process.cwd();
|
|
1091
841
|
this.log.debug(`Current Working Directory: ${currentDir}`);
|
|
1092
|
-
// Command line arguments (excluding 'node' and the script name)
|
|
1093
842
|
const cmdArgs = process.argv.slice(2).join(' ');
|
|
1094
843
|
this.log.debug(`Command Line Arguments: ${cmdArgs}`);
|
|
1095
844
|
}
|
|
1096
|
-
/**
|
|
1097
|
-
* Set the logger logLevel for the Matterbridge classes and call onChangeLoggerLevel() for each plugin.
|
|
1098
|
-
*
|
|
1099
|
-
* @param {LogLevel} logLevel The logger logLevel to set.
|
|
1100
|
-
* @returns {Promise<LogLevel>} A promise that resolves when the logLevel has been set.
|
|
1101
|
-
*/
|
|
1102
845
|
async setLogLevel(logLevel) {
|
|
1103
846
|
this.log.logLevel = logLevel;
|
|
1104
847
|
this.frontend.logLevel = logLevel;
|
|
@@ -1108,87 +851,58 @@ export class Matterbridge extends EventEmitter {
|
|
|
1108
851
|
for (const plugin of this.plugins) {
|
|
1109
852
|
if (!plugin.platform || !plugin.platform.log || !plugin.platform.config)
|
|
1110
853
|
continue;
|
|
1111
|
-
plugin.platform.log.logLevel = plugin.platform.config.debug === true ? "debug"
|
|
1112
|
-
await plugin.platform.onChangeLoggerLevel(plugin.platform.config.debug === true ? "debug"
|
|
1113
|
-
}
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
callbackLogLevel = "debug" /* LogLevel.DEBUG */;
|
|
854
|
+
plugin.platform.log.logLevel = plugin.platform.config.debug === true ? "debug" : logLevel;
|
|
855
|
+
await plugin.platform.onChangeLoggerLevel(plugin.platform.config.debug === true ? "debug" : logLevel);
|
|
856
|
+
}
|
|
857
|
+
let callbackLogLevel = "notice";
|
|
858
|
+
if (logLevel === "info" || Logger.level === MatterLogLevel.INFO)
|
|
859
|
+
callbackLogLevel = "info";
|
|
860
|
+
if (logLevel === "debug" || Logger.level === MatterLogLevel.DEBUG)
|
|
861
|
+
callbackLogLevel = "debug";
|
|
1120
862
|
AnsiLogger.setGlobalCallbackLevel(callbackLogLevel);
|
|
1121
863
|
this.log.debug(`WebSocketServer logger global callback set to ${callbackLogLevel}`);
|
|
1122
864
|
return logLevel;
|
|
1123
865
|
}
|
|
1124
|
-
/**
|
|
1125
|
-
* Get the current logger logLevel.
|
|
1126
|
-
*
|
|
1127
|
-
* @returns {LogLevel} The current logger logLevel.
|
|
1128
|
-
*/
|
|
1129
866
|
getLogLevel() {
|
|
1130
867
|
return this.log.logLevel;
|
|
1131
868
|
}
|
|
1132
|
-
/**
|
|
1133
|
-
* Creates a MatterLogger function to show the matter.js log messages in AnsiLogger (for the frontend).
|
|
1134
|
-
* It also logs to file (matter.log) if fileLogger is true.
|
|
1135
|
-
*
|
|
1136
|
-
* @param {boolean} fileLogger - Whether to log to file or not.
|
|
1137
|
-
* @returns {Function} The MatterLogger function. \x1b[35m for violet \x1b[34m is blue
|
|
1138
|
-
*/
|
|
1139
869
|
createDestinationMatterLogger(fileLogger) {
|
|
1140
|
-
this.matterLog.logNameColor = '\x1b[34m';
|
|
870
|
+
this.matterLog.logNameColor = '\x1b[34m';
|
|
1141
871
|
if (fileLogger) {
|
|
1142
872
|
this.matterLog.logFilePath = path.join(this.matterbridgeDirectory, MATTER_LOGGER_FILE);
|
|
1143
873
|
}
|
|
1144
874
|
return (text, message) => {
|
|
1145
|
-
// 2024-08-21 08:55:19.488 DEBUG InteractionMessenger Sending DataReport chunk with 28 attributes and 0 events: 1004 bytes
|
|
1146
875
|
const logger = text.slice(44, 44 + 20).trim();
|
|
1147
876
|
const msg = text.slice(65);
|
|
1148
877
|
this.matterLog.logName = logger;
|
|
1149
878
|
switch (message.level) {
|
|
1150
879
|
case MatterLogLevel.DEBUG:
|
|
1151
|
-
this.matterLog.log("debug"
|
|
880
|
+
this.matterLog.log("debug", msg);
|
|
1152
881
|
break;
|
|
1153
882
|
case MatterLogLevel.INFO:
|
|
1154
|
-
this.matterLog.log("info"
|
|
883
|
+
this.matterLog.log("info", msg);
|
|
1155
884
|
break;
|
|
1156
885
|
case MatterLogLevel.NOTICE:
|
|
1157
|
-
this.matterLog.log("notice"
|
|
886
|
+
this.matterLog.log("notice", msg);
|
|
1158
887
|
break;
|
|
1159
888
|
case MatterLogLevel.WARN:
|
|
1160
|
-
this.matterLog.log("warn"
|
|
889
|
+
this.matterLog.log("warn", msg);
|
|
1161
890
|
break;
|
|
1162
891
|
case MatterLogLevel.ERROR:
|
|
1163
|
-
this.matterLog.log("error"
|
|
892
|
+
this.matterLog.log("error", msg);
|
|
1164
893
|
break;
|
|
1165
894
|
case MatterLogLevel.FATAL:
|
|
1166
|
-
this.matterLog.log("fatal"
|
|
895
|
+
this.matterLog.log("fatal", msg);
|
|
1167
896
|
break;
|
|
1168
897
|
}
|
|
1169
898
|
};
|
|
1170
899
|
}
|
|
1171
|
-
/**
|
|
1172
|
-
* Restarts the process by exiting the current instance and loading a new instance (/api/restart).
|
|
1173
|
-
*
|
|
1174
|
-
* @returns {Promise<void>} A promise that resolves when the restart is completed.
|
|
1175
|
-
*/
|
|
1176
900
|
async restartProcess() {
|
|
1177
901
|
await this.cleanup('restarting...', true);
|
|
1178
902
|
}
|
|
1179
|
-
/**
|
|
1180
|
-
* Shut down the process (/api/shutdown).
|
|
1181
|
-
*
|
|
1182
|
-
* @returns {Promise<void>} A promise that resolves when the shutdown is completed.
|
|
1183
|
-
*/
|
|
1184
903
|
async shutdownProcess() {
|
|
1185
904
|
await this.cleanup('shutting down...', false);
|
|
1186
905
|
}
|
|
1187
|
-
/**
|
|
1188
|
-
* Update matterbridge and shut down the process (virtual device 'Update Matterbridge').
|
|
1189
|
-
*
|
|
1190
|
-
* @returns {Promise<void>} A promise that resolves when the update is completed.
|
|
1191
|
-
*/
|
|
1192
906
|
async updateProcess() {
|
|
1193
907
|
this.log.info('Updating matterbridge...');
|
|
1194
908
|
try {
|
|
@@ -1202,13 +916,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1202
916
|
this.frontend.wssSendRestartRequired();
|
|
1203
917
|
await this.cleanup('updating...', false);
|
|
1204
918
|
}
|
|
1205
|
-
/**
|
|
1206
|
-
* Unregister all devices and shut down the process (/api/unregister).
|
|
1207
|
-
*
|
|
1208
|
-
* @param {number} [timeout] - The timeout duration to wait for the message exchange to complete in milliseconds. Default is 1000.
|
|
1209
|
-
*
|
|
1210
|
-
* @returns {Promise<void>} A promise that resolves when the cleanup is completed.
|
|
1211
|
-
*/
|
|
1212
919
|
async unregisterAndShutdownProcess(timeout = 1000) {
|
|
1213
920
|
this.log.info('Unregistering all devices and shutting down...');
|
|
1214
921
|
for (const plugin of this.plugins.array()) {
|
|
@@ -1220,71 +927,46 @@ export class Matterbridge extends EventEmitter {
|
|
|
1220
927
|
await this.removeAllBridgedEndpoints(plugin.name, 100);
|
|
1221
928
|
}
|
|
1222
929
|
this.log.debug('Waiting for the MessageExchange to finish...');
|
|
1223
|
-
await wait(timeout);
|
|
930
|
+
await wait(timeout);
|
|
1224
931
|
this.log.debug('Cleaning up and shutting down...');
|
|
1225
932
|
await this.cleanup('unregistered all devices and shutting down...', false, timeout);
|
|
1226
933
|
}
|
|
1227
|
-
/**
|
|
1228
|
-
* Reset commissioning and shut down the process (/api/reset).
|
|
1229
|
-
*
|
|
1230
|
-
* @returns {Promise<void>} A promise that resolves when the cleanup is completed.
|
|
1231
|
-
*/
|
|
1232
934
|
async shutdownProcessAndReset() {
|
|
1233
935
|
await this.cleanup('shutting down with reset...', false);
|
|
1234
936
|
}
|
|
1235
|
-
/**
|
|
1236
|
-
* Factory reset and shut down the process (/api/factory-reset).
|
|
1237
|
-
*
|
|
1238
|
-
* @returns {Promise<void>} A promise that resolves when the cleanup is completed.
|
|
1239
|
-
*/
|
|
1240
937
|
async shutdownProcessAndFactoryReset() {
|
|
1241
938
|
await this.cleanup('shutting down with factory reset...', false);
|
|
1242
939
|
}
|
|
1243
|
-
|
|
1244
|
-
* Cleans up the Matterbridge instance.
|
|
1245
|
-
*
|
|
1246
|
-
* @param {string} message - The cleanup message.
|
|
1247
|
-
* @param {boolean} [restart] - Indicates whether to restart the instance after cleanup. Default is `false`.
|
|
1248
|
-
* @param {number} [timeout] - The timeout duration to wait for the message exchange to complete in milliseconds. Default is 1000.
|
|
1249
|
-
*
|
|
1250
|
-
* @returns {Promise<void>} A promise that resolves when the cleanup is completed.
|
|
1251
|
-
*/
|
|
1252
|
-
async cleanup(message, restart = false, timeout = 1000) {
|
|
940
|
+
async cleanup(message, restart = false, pause = 1000) {
|
|
1253
941
|
if (this.initialized && !this.hasCleanupStarted) {
|
|
1254
942
|
this.emit('cleanup_started');
|
|
1255
943
|
this.hasCleanupStarted = true;
|
|
1256
944
|
this.log.info(message);
|
|
1257
|
-
// Clear the start matter interval
|
|
1258
945
|
if (this.startMatterInterval) {
|
|
1259
946
|
clearInterval(this.startMatterInterval);
|
|
1260
947
|
this.startMatterInterval = undefined;
|
|
1261
948
|
this.log.debug('Start matter interval cleared');
|
|
1262
949
|
}
|
|
1263
|
-
// Clear the check update timeout
|
|
1264
950
|
if (this.checkUpdateTimeout) {
|
|
1265
951
|
clearTimeout(this.checkUpdateTimeout);
|
|
1266
952
|
this.checkUpdateTimeout = undefined;
|
|
1267
953
|
this.log.debug('Check update timeout cleared');
|
|
1268
954
|
}
|
|
1269
|
-
// Clear the check update interval
|
|
1270
955
|
if (this.checkUpdateInterval) {
|
|
1271
956
|
clearInterval(this.checkUpdateInterval);
|
|
1272
957
|
this.checkUpdateInterval = undefined;
|
|
1273
958
|
this.log.debug('Check update interval cleared');
|
|
1274
959
|
}
|
|
1275
|
-
// Clear the configure timeout
|
|
1276
960
|
if (this.configureTimeout) {
|
|
1277
961
|
clearTimeout(this.configureTimeout);
|
|
1278
962
|
this.configureTimeout = undefined;
|
|
1279
963
|
this.log.debug('Matterbridge configure timeout cleared');
|
|
1280
964
|
}
|
|
1281
|
-
// Clear the reachability timeout
|
|
1282
965
|
if (this.reachabilityTimeout) {
|
|
1283
966
|
clearTimeout(this.reachabilityTimeout);
|
|
1284
967
|
this.reachabilityTimeout = undefined;
|
|
1285
968
|
this.log.debug('Matterbridge reachability timeout cleared');
|
|
1286
969
|
}
|
|
1287
|
-
// Call the shutdown method of each plugin and clear the plugins reachability timeout
|
|
1288
970
|
for (const plugin of this.plugins) {
|
|
1289
971
|
if (!plugin.enabled || plugin.error)
|
|
1290
972
|
continue;
|
|
@@ -1295,11 +977,10 @@ export class Matterbridge extends EventEmitter {
|
|
|
1295
977
|
this.log.debug(`Plugin ${plg}${plugin.name}${db} reachability timeout cleared`);
|
|
1296
978
|
}
|
|
1297
979
|
}
|
|
1298
|
-
// Stop matter server nodes
|
|
1299
980
|
this.log.notice(`Stopping matter server nodes in ${this.bridgeMode} mode...`);
|
|
1300
|
-
if (
|
|
1301
|
-
this.log.debug(`Waiting ${
|
|
1302
|
-
await wait(
|
|
981
|
+
if (pause > 0) {
|
|
982
|
+
this.log.debug(`Waiting ${pause}ms for the MessageExchange to finish...`);
|
|
983
|
+
await wait(pause, `Waiting ${pause}ms for the MessageExchange to finish...`, false);
|
|
1303
984
|
}
|
|
1304
985
|
if (this.bridgeMode === 'bridge') {
|
|
1305
986
|
if (this.serverNode) {
|
|
@@ -1322,7 +1003,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1322
1003
|
}
|
|
1323
1004
|
}
|
|
1324
1005
|
this.log.notice('Stopped matter server nodes');
|
|
1325
|
-
// Matter commisioning reset
|
|
1326
1006
|
if (message === 'shutting down with reset...') {
|
|
1327
1007
|
this.log.info('Resetting Matterbridge commissioning information...');
|
|
1328
1008
|
await this.matterStorageManager?.createContext('events')?.clearAll();
|
|
@@ -1332,7 +1012,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1332
1012
|
await this.matterbridgeContext?.clearAll();
|
|
1333
1013
|
this.log.info('Matter storage reset done! Remove the bridge from the controller.');
|
|
1334
1014
|
}
|
|
1335
|
-
// Unregister all devices
|
|
1336
1015
|
if (message === 'unregistered all devices and shutting down...') {
|
|
1337
1016
|
if (this.bridgeMode === 'bridge') {
|
|
1338
1017
|
await this.matterStorageManager?.createContext('root')?.createContext('parts')?.createContext('Matterbridge')?.createContext('parts')?.clearAll();
|
|
@@ -1350,35 +1029,16 @@ export class Matterbridge extends EventEmitter {
|
|
|
1350
1029
|
}
|
|
1351
1030
|
this.log.info('Matter storage reset done!');
|
|
1352
1031
|
}
|
|
1353
|
-
// Stop matter storage
|
|
1354
1032
|
await this.stopMatterStorage();
|
|
1355
|
-
// Stop the frontend
|
|
1356
1033
|
await this.frontend.stop();
|
|
1357
1034
|
this.frontend.destroy();
|
|
1358
|
-
// Close PluginManager and DeviceManager
|
|
1359
1035
|
this.plugins.destroy();
|
|
1360
1036
|
this.devices.destroy();
|
|
1361
|
-
// Stop thread messaging server
|
|
1362
1037
|
this.server.close();
|
|
1363
|
-
// Close the matterbridge node storage and context
|
|
1364
1038
|
if (this.nodeStorage && this.nodeContext) {
|
|
1365
|
-
/*
|
|
1366
|
-
TODO: Implement serialization of registered devices
|
|
1367
|
-
this.log.info('Saving registered devices...');
|
|
1368
|
-
const serializedRegisteredDevices: SerializedMatterbridgeEndpoint[] = [];
|
|
1369
|
-
this.devices.forEach(async (device) => {
|
|
1370
|
-
const serializedMatterbridgeDevice = MatterbridgeEndpoint.serialize(device);
|
|
1371
|
-
this.log.info(`- ${serializedMatterbridgeDevice.deviceName}${rs}\n`, serializedMatterbridgeDevice);
|
|
1372
|
-
if (serializedMatterbridgeDevice) serializedRegisteredDevices.push(serializedMatterbridgeDevice);
|
|
1373
|
-
});
|
|
1374
|
-
await this.nodeContext.set<SerializedMatterbridgeEndpoint[]>('devices', serializedRegisteredDevices);
|
|
1375
|
-
this.log.info(`Saved registered devices (${serializedRegisteredDevices?.length})`);
|
|
1376
|
-
*/
|
|
1377
|
-
// Clear nodeContext and nodeStorage (they just need 1000ms to write the data to disk)
|
|
1378
1039
|
this.log.debug(`Closing node storage context for ${plg}Matterbridge${db}...`);
|
|
1379
1040
|
await this.nodeContext.close();
|
|
1380
1041
|
this.nodeContext = undefined;
|
|
1381
|
-
// Clear nodeContext for each plugin (they just need 1000ms to write the data to disk)
|
|
1382
1042
|
for (const plugin of this.plugins) {
|
|
1383
1043
|
if (plugin.nodeContext) {
|
|
1384
1044
|
this.log.debug(`Closing node storage context for plugin ${plg}${plugin.name}${db}...`);
|
|
@@ -1395,10 +1055,8 @@ export class Matterbridge extends EventEmitter {
|
|
|
1395
1055
|
}
|
|
1396
1056
|
this.plugins.clear();
|
|
1397
1057
|
this.devices.clear();
|
|
1398
|
-
// Factory reset
|
|
1399
1058
|
if (message === 'shutting down with factory reset...') {
|
|
1400
1059
|
try {
|
|
1401
|
-
// Delete matter storage directory with its subdirectories and backup
|
|
1402
1060
|
const dir = path.join(this.matterbridgeDirectory, MATTER_STORAGE_NAME);
|
|
1403
1061
|
this.log.info(`Removing matter storage directory: ${dir}`);
|
|
1404
1062
|
await fs.promises.rm(dir, { recursive: true });
|
|
@@ -1407,13 +1065,11 @@ export class Matterbridge extends EventEmitter {
|
|
|
1407
1065
|
await fs.promises.rm(backup, { recursive: true });
|
|
1408
1066
|
}
|
|
1409
1067
|
catch (error) {
|
|
1410
|
-
// istanbul ignore next if
|
|
1411
1068
|
if (error instanceof Error && error.code !== 'ENOENT') {
|
|
1412
1069
|
this.log.error(`Error removing matter storage directory: ${error}`);
|
|
1413
1070
|
}
|
|
1414
1071
|
}
|
|
1415
1072
|
try {
|
|
1416
|
-
// Delete matterbridge storage directory with its subdirectories and backup
|
|
1417
1073
|
const dir = path.join(this.matterbridgeDirectory, NODE_STORAGE_DIR);
|
|
1418
1074
|
this.log.info(`Removing matterbridge storage directory: ${dir}`);
|
|
1419
1075
|
await fs.promises.rm(dir, { recursive: true });
|
|
@@ -1422,20 +1078,18 @@ export class Matterbridge extends EventEmitter {
|
|
|
1422
1078
|
await fs.promises.rm(backup, { recursive: true });
|
|
1423
1079
|
}
|
|
1424
1080
|
catch (error) {
|
|
1425
|
-
// istanbul ignore next if
|
|
1426
1081
|
if (error instanceof Error && error.code !== 'ENOENT') {
|
|
1427
1082
|
this.log.error(`Error removing matterbridge storage directory: ${error}`);
|
|
1428
1083
|
}
|
|
1429
1084
|
}
|
|
1430
1085
|
this.log.info('Factory reset done! Remove all paired fabrics from the controllers.');
|
|
1431
1086
|
}
|
|
1432
|
-
// Deregisters the process handlers
|
|
1433
1087
|
this.deregisterProcessHandlers();
|
|
1434
1088
|
if (restart) {
|
|
1435
1089
|
if (message === 'updating...') {
|
|
1436
1090
|
this.log.info('Cleanup completed. Updating...');
|
|
1437
1091
|
Matterbridge.instance = undefined;
|
|
1438
|
-
this.emit('update');
|
|
1092
|
+
this.emit('update');
|
|
1439
1093
|
}
|
|
1440
1094
|
else if (message === 'restarting...') {
|
|
1441
1095
|
this.log.info('Cleanup completed. Restarting...');
|
|
@@ -1464,14 +1118,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
1464
1118
|
this.log.debug('Cleanup already started...');
|
|
1465
1119
|
}
|
|
1466
1120
|
}
|
|
1467
|
-
/**
|
|
1468
|
-
* Starts the Matterbridge in bridge mode.
|
|
1469
|
-
*
|
|
1470
|
-
* @private
|
|
1471
|
-
* @returns {Promise<void>} A promise that resolves when the Matterbridge is started.
|
|
1472
|
-
*/
|
|
1473
1121
|
async startBridge() {
|
|
1474
|
-
// Plugins are configured by a timer when matter server is started and plugin.configured is set to true
|
|
1475
1122
|
if (!this.matterStorageManager)
|
|
1476
1123
|
throw new Error('No storage manager initialized');
|
|
1477
1124
|
if (!this.matterbridgeContext)
|
|
@@ -1510,16 +1157,13 @@ export class Matterbridge extends EventEmitter {
|
|
|
1510
1157
|
clearInterval(this.startMatterInterval);
|
|
1511
1158
|
this.startMatterInterval = undefined;
|
|
1512
1159
|
this.log.debug('Cleared startMatterInterval interval for Matterbridge');
|
|
1513
|
-
|
|
1514
|
-
this.startServerNode(this.serverNode); // We don't await this, because the server node is started in the background
|
|
1515
|
-
// Start the Matter server node of single devices in mode 'server'
|
|
1160
|
+
this.startServerNode(this.serverNode);
|
|
1516
1161
|
for (const device of this.devices.array()) {
|
|
1517
1162
|
if (device.mode === 'server' && device.serverNode) {
|
|
1518
1163
|
this.log.debug(`Starting server node for device ${dev}${device.deviceName}${db} in server mode...`);
|
|
1519
|
-
this.startServerNode(device.serverNode);
|
|
1164
|
+
this.startServerNode(device.serverNode);
|
|
1520
1165
|
}
|
|
1521
1166
|
}
|
|
1522
|
-
// Configure the plugins
|
|
1523
1167
|
this.configureTimeout = setTimeout(async () => {
|
|
1524
1168
|
for (const plugin of this.plugins.array()) {
|
|
1525
1169
|
if (!plugin.enabled || !plugin.loaded || !plugin.started || plugin.error)
|
|
@@ -1537,40 +1181,28 @@ export class Matterbridge extends EventEmitter {
|
|
|
1537
1181
|
}
|
|
1538
1182
|
this.frontend.wssSendRefreshRequired('plugins');
|
|
1539
1183
|
}, 30 * 1000).unref();
|
|
1540
|
-
// Setting reachability to true
|
|
1541
1184
|
this.reachabilityTimeout = setTimeout(() => {
|
|
1542
1185
|
this.log.info(`Setting reachability to true for ${plg}Matterbridge${db}`);
|
|
1543
1186
|
if (this.aggregatorNode)
|
|
1544
1187
|
this.setAggregatorReachability(this.aggregatorNode, true);
|
|
1545
1188
|
}, 60 * 1000).unref();
|
|
1546
|
-
// Logger.get('LogServerNode').info(this.serverNode);
|
|
1547
1189
|
this.emit('bridge_started');
|
|
1548
1190
|
this.log.notice('Matterbridge bridge started successfully');
|
|
1549
1191
|
this.frontend.wssSendRefreshRequired('settings');
|
|
1550
1192
|
this.frontend.wssSendRefreshRequired('plugins');
|
|
1551
1193
|
}, this.startMatterIntervalMs);
|
|
1552
1194
|
}
|
|
1553
|
-
/**
|
|
1554
|
-
* Starts the Matterbridge in childbridge mode.
|
|
1555
|
-
*
|
|
1556
|
-
* @param {number} [delay] - The delay before starting the childbridge. Default is 1000 milliseconds.
|
|
1557
|
-
*
|
|
1558
|
-
* @returns {Promise<void>} A promise that resolves when the Matterbridge is started.
|
|
1559
|
-
*/
|
|
1560
1195
|
async startChildbridge(delay = 1000) {
|
|
1561
1196
|
if (!this.matterStorageManager)
|
|
1562
1197
|
throw new Error('No storage manager initialized');
|
|
1563
|
-
// Load with await all plugins but don't start them. We get the platform.type to pre-create server nodes for DynamicPlatform plugins
|
|
1564
1198
|
this.log.debug('Loading all plugins in childbridge mode...');
|
|
1565
1199
|
await this.startPlugins(true, false);
|
|
1566
|
-
// Create server nodes for DynamicPlatform plugins and start all plugins in the background
|
|
1567
1200
|
this.log.debug('Creating server nodes for DynamicPlatform plugins and starting all plugins in childbridge mode...');
|
|
1568
1201
|
for (const plugin of this.plugins.array().filter((p) => p.enabled && !p.error)) {
|
|
1569
1202
|
if (plugin.type === 'DynamicPlatform')
|
|
1570
1203
|
await this.createDynamicPlugin(plugin);
|
|
1571
|
-
this.plugins.start(plugin, 'Matterbridge is starting');
|
|
1204
|
+
this.plugins.start(plugin, 'Matterbridge is starting');
|
|
1572
1205
|
}
|
|
1573
|
-
// Start the Matterbridge in childbridge mode when all plugins are loaded and started
|
|
1574
1206
|
this.log.debug('Starting start matter interval in childbridge mode...');
|
|
1575
1207
|
let failCount = 0;
|
|
1576
1208
|
this.startMatterInterval = setInterval(async () => {
|
|
@@ -1604,9 +1236,8 @@ export class Matterbridge extends EventEmitter {
|
|
|
1604
1236
|
clearInterval(this.startMatterInterval);
|
|
1605
1237
|
this.startMatterInterval = undefined;
|
|
1606
1238
|
if (delay > 0)
|
|
1607
|
-
await wait(delay);
|
|
1239
|
+
await wait(delay);
|
|
1608
1240
|
this.log.debug('Cleared startMatterInterval interval in childbridge mode');
|
|
1609
|
-
// Configure the plugins
|
|
1610
1241
|
this.configureTimeout = setTimeout(async () => {
|
|
1611
1242
|
for (const plugin of this.plugins.array()) {
|
|
1612
1243
|
if (!plugin.enabled || !plugin.loaded || !plugin.started || plugin.error)
|
|
@@ -1631,7 +1262,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1631
1262
|
this.log.error(`Plugin ${plg}${plugin.name}${er} didn't register any devices to Matterbridge. Verify the plugin configuration.`);
|
|
1632
1263
|
continue;
|
|
1633
1264
|
}
|
|
1634
|
-
// istanbul ignore next if cause is just a safety check
|
|
1635
1265
|
if (!plugin.serverNode) {
|
|
1636
1266
|
this.log.error(`Server node not found for plugin ${plg}${plugin.name}${er}`);
|
|
1637
1267
|
continue;
|
|
@@ -1644,252 +1274,28 @@ export class Matterbridge extends EventEmitter {
|
|
|
1644
1274
|
this.log.error(`Node storage context not found for plugin ${plg}${plugin.name}${er}`);
|
|
1645
1275
|
continue;
|
|
1646
1276
|
}
|
|
1647
|
-
|
|
1648
|
-
this.startServerNode(plugin.serverNode); // We don't await this, because the server node is started in the background
|
|
1649
|
-
// Setting reachability to true
|
|
1277
|
+
this.startServerNode(plugin.serverNode);
|
|
1650
1278
|
plugin.reachabilityTimeout = setTimeout(() => {
|
|
1651
1279
|
this.log.info(`Setting reachability to true for ${plg}${plugin.name}${nf}`);
|
|
1652
1280
|
if (plugin.type === 'DynamicPlatform' && plugin.aggregatorNode)
|
|
1653
1281
|
this.setAggregatorReachability(plugin.aggregatorNode, true);
|
|
1654
1282
|
}, 60 * 1000).unref();
|
|
1655
1283
|
}
|
|
1656
|
-
// Start the Matter server node of single devices in mode 'server'
|
|
1657
1284
|
for (const device of this.devices.array()) {
|
|
1658
1285
|
if (device.mode === 'server' && device.serverNode) {
|
|
1659
1286
|
this.log.debug(`Starting server node for device ${dev}${device.deviceName}${db} in server mode...`);
|
|
1660
|
-
this.startServerNode(device.serverNode);
|
|
1287
|
+
this.startServerNode(device.serverNode);
|
|
1661
1288
|
}
|
|
1662
1289
|
}
|
|
1663
|
-
// Logger.get('LogServerNode').info(this.serverNode);
|
|
1664
1290
|
this.emit('childbridge_started');
|
|
1665
1291
|
this.log.notice('Matterbridge childbridge started successfully');
|
|
1666
1292
|
this.frontend.wssSendRefreshRequired('settings');
|
|
1667
1293
|
this.frontend.wssSendRefreshRequired('plugins');
|
|
1668
1294
|
}, this.startMatterIntervalMs);
|
|
1669
1295
|
}
|
|
1670
|
-
/**
|
|
1671
|
-
* Starts the Matterbridge controller.
|
|
1672
|
-
*
|
|
1673
|
-
* @private
|
|
1674
|
-
* @returns {Promise<void>} A promise that resolves when the Matterbridge is started.
|
|
1675
|
-
*/
|
|
1676
1296
|
async startController() {
|
|
1677
|
-
/*
|
|
1678
|
-
if (!this.matterStorageManager) {
|
|
1679
|
-
this.log.error('No storage manager initialized');
|
|
1680
|
-
await this.cleanup('No storage manager initialized');
|
|
1681
|
-
return;
|
|
1682
|
-
}
|
|
1683
|
-
this.log.info('Creating context: mattercontrollerContext');
|
|
1684
|
-
this.controllerContext = this.matterStorageManager.createContext('mattercontrollerContext');
|
|
1685
|
-
if (!this.controllerContext) {
|
|
1686
|
-
this.log.error('No storage context mattercontrollerContext initialized');
|
|
1687
|
-
await this.cleanup('No storage context mattercontrollerContext initialized');
|
|
1688
|
-
return;
|
|
1689
|
-
}
|
|
1690
|
-
|
|
1691
|
-
this.log.debug('Starting matterbridge in mode', this.bridgeMode);
|
|
1692
|
-
this.matterServer = await this.createMatterServer(this.storageManager);
|
|
1693
|
-
this.log.info('Creating matter commissioning controller');
|
|
1694
|
-
this.commissioningController = new CommissioningController({
|
|
1695
|
-
autoConnect: false,
|
|
1696
|
-
});
|
|
1697
|
-
this.log.info('Adding matter commissioning controller to matter server');
|
|
1698
|
-
await this.matterServer.addCommissioningController(this.commissioningController);
|
|
1699
|
-
|
|
1700
|
-
this.log.info('Starting matter server');
|
|
1701
|
-
await this.matterServer.start();
|
|
1702
|
-
this.log.info('Matter server started');
|
|
1703
|
-
const commissioningOptions: ControllerCommissioningFlowOptions = {
|
|
1704
|
-
regulatoryLocation: GeneralCommissioning.RegulatoryLocationType.IndoorOutdoor,
|
|
1705
|
-
regulatoryCountryCode: 'XX',
|
|
1706
|
-
};
|
|
1707
|
-
const commissioningController = new CommissioningController({
|
|
1708
|
-
environment: {
|
|
1709
|
-
environment,
|
|
1710
|
-
id: uniqueId,
|
|
1711
|
-
},
|
|
1712
|
-
autoConnect: false, // Do not auto connect to the commissioned nodes
|
|
1713
|
-
adminFabricLabel,
|
|
1714
|
-
});
|
|
1715
|
-
|
|
1716
|
-
if (hasParameter('pairingcode')) {
|
|
1717
|
-
this.log.info('Pairing device with pairingcode:', getParameter('pairingcode'));
|
|
1718
|
-
const pairingCode = getParameter('pairingcode');
|
|
1719
|
-
const ip = this.controllerContext.has('ip') ? this.controllerContext.get<string>('ip') : undefined;
|
|
1720
|
-
const port = this.controllerContext.has('port') ? this.controllerContext.get<number>('port') : undefined;
|
|
1721
|
-
|
|
1722
|
-
let longDiscriminator, setupPin, shortDiscriminator;
|
|
1723
|
-
if (pairingCode !== undefined) {
|
|
1724
|
-
const pairingCodeCodec = ManualPairingCodeCodec.decode(pairingCode);
|
|
1725
|
-
shortDiscriminator = pairingCodeCodec.shortDiscriminator;
|
|
1726
|
-
longDiscriminator = undefined;
|
|
1727
|
-
setupPin = pairingCodeCodec.passcode;
|
|
1728
|
-
this.log.info(`Data extracted from pairing code: ${Logger.toJSON(pairingCodeCodec)}`);
|
|
1729
|
-
} else {
|
|
1730
|
-
longDiscriminator = await this.controllerContext.get('longDiscriminator', 3840);
|
|
1731
|
-
if (longDiscriminator > 4095) throw new Error('Discriminator value must be less than 4096');
|
|
1732
|
-
setupPin = this.controllerContext.get('pin', 20202021);
|
|
1733
|
-
}
|
|
1734
|
-
if ((shortDiscriminator === undefined && longDiscriminator === undefined) || setupPin === undefined) {
|
|
1735
|
-
throw new Error('Please specify the longDiscriminator of the device to commission with -longDiscriminator or provide a valid passcode with -passcode');
|
|
1736
|
-
}
|
|
1737
|
-
|
|
1738
|
-
const options = {
|
|
1739
|
-
commissioning: commissioningOptions,
|
|
1740
|
-
discovery: {
|
|
1741
|
-
knownAddress: ip !== undefined && port !== undefined ? { ip, port, type: 'udp' } : undefined,
|
|
1742
|
-
identifierData: longDiscriminator !== undefined ? { longDiscriminator } : shortDiscriminator !== undefined ? { shortDiscriminator } : {},
|
|
1743
|
-
},
|
|
1744
|
-
passcode: setupPin,
|
|
1745
|
-
} as NodeCommissioningOptions;
|
|
1746
|
-
this.log.info('Commissioning with options:', options);
|
|
1747
|
-
const nodeId = await this.commissioningController.commissionNode(options);
|
|
1748
|
-
this.log.info(`Commissioning successfully done with nodeId: ${nodeId}`);
|
|
1749
|
-
this.log.info('ActiveSessionInformation:', this.commissioningController.getActiveSessionInformation());
|
|
1750
|
-
} // (hasParameter('pairingcode'))
|
|
1751
|
-
|
|
1752
|
-
if (hasParameter('unpairall')) {
|
|
1753
|
-
this.log.info('***Commissioning controller unpairing all nodes...');
|
|
1754
|
-
const nodeIds = this.commissioningController.getCommissionedNodes();
|
|
1755
|
-
for (const nodeId of nodeIds) {
|
|
1756
|
-
this.log.info('***Commissioning controller unpairing node:', nodeId);
|
|
1757
|
-
await this.commissioningController.removeNode(nodeId);
|
|
1758
|
-
}
|
|
1759
|
-
return;
|
|
1760
|
-
}
|
|
1761
|
-
|
|
1762
|
-
if (hasParameter('discover')) {
|
|
1763
|
-
// const discover = await this.commissioningController.discoverCommissionableDevices({ productId: 0x8000, deviceType: 0xfff1 });
|
|
1764
|
-
// console.log(discover);
|
|
1765
|
-
}
|
|
1766
|
-
|
|
1767
|
-
if (!this.commissioningController.isCommissioned()) {
|
|
1768
|
-
this.log.info('***Commissioning controller is not commissioned: use matterbridge -controller -pairingcode [pairingcode] to commission a device');
|
|
1769
|
-
return;
|
|
1770
|
-
}
|
|
1771
|
-
|
|
1772
|
-
const nodeIds = this.commissioningController.getCommissionedNodes();
|
|
1773
|
-
this.log.info(`***Commissioning controller is commissioned ${this.commissioningController.isCommissioned()} and has ${nodeIds.length} nodes commisioned: `);
|
|
1774
|
-
for (const nodeId of nodeIds) {
|
|
1775
|
-
this.log.info(`***Connecting to commissioned node: ${nodeId}`);
|
|
1776
|
-
|
|
1777
|
-
const node = await this.commissioningController.connectNode(nodeId, {
|
|
1778
|
-
autoSubscribe: false,
|
|
1779
|
-
attributeChangedCallback: (peerNodeId, { path: { nodeId, clusterId, endpointId, attributeName }, value }) =>
|
|
1780
|
-
this.log.info(`***Commissioning controller attributeChangedCallback ${peerNodeId}: attribute ${nodeId}/${endpointId}/${clusterId}/${attributeName} changed to ${Logger.toJSON(value)}`),
|
|
1781
|
-
eventTriggeredCallback: (peerNodeId, { path: { nodeId, clusterId, endpointId, eventName }, events }) =>
|
|
1782
|
-
this.log.info(`***Commissioning controller eventTriggeredCallback ${peerNodeId}: Event ${nodeId}/${endpointId}/${clusterId}/${eventName} triggered with ${Logger.toJSON(events)}`),
|
|
1783
|
-
stateInformationCallback: (peerNodeId, info) => {
|
|
1784
|
-
switch (info) {
|
|
1785
|
-
case NodeStateInformation.Connected:
|
|
1786
|
-
this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} connected`);
|
|
1787
|
-
break;
|
|
1788
|
-
case NodeStateInformation.Disconnected:
|
|
1789
|
-
this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} disconnected`);
|
|
1790
|
-
break;
|
|
1791
|
-
case NodeStateInformation.Reconnecting:
|
|
1792
|
-
this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} reconnecting`);
|
|
1793
|
-
break;
|
|
1794
|
-
case NodeStateInformation.WaitingForDeviceDiscovery:
|
|
1795
|
-
this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} waiting for device discovery`);
|
|
1796
|
-
break;
|
|
1797
|
-
case NodeStateInformation.StructureChanged:
|
|
1798
|
-
this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} structure changed`);
|
|
1799
|
-
break;
|
|
1800
|
-
case NodeStateInformation.Decommissioned:
|
|
1801
|
-
this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} decommissioned`);
|
|
1802
|
-
break;
|
|
1803
|
-
default:
|
|
1804
|
-
this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} NodeStateInformation.${info}`);
|
|
1805
|
-
break;
|
|
1806
|
-
}
|
|
1807
|
-
},
|
|
1808
|
-
});
|
|
1809
|
-
|
|
1810
|
-
node.logStructure();
|
|
1811
|
-
|
|
1812
|
-
// Get the interaction client
|
|
1813
|
-
this.log.info('Getting the interaction client');
|
|
1814
|
-
const interactionClient = await node.getInteractionClient();
|
|
1815
|
-
let cluster;
|
|
1816
|
-
let attributes;
|
|
1817
|
-
|
|
1818
|
-
// Log BasicInformationCluster
|
|
1819
|
-
cluster = BasicInformationCluster;
|
|
1820
|
-
attributes = await interactionClient.getMultipleAttributes({
|
|
1821
|
-
attributes: [{ clusterId: cluster.id }],
|
|
1822
|
-
});
|
|
1823
|
-
if (attributes.length > 0) this.log.info(`Cluster: ${idn}${cluster.name}${rs}${nf} attributes:`);
|
|
1824
|
-
attributes.forEach((attribute) => {
|
|
1825
|
-
this.log.info(
|
|
1826
|
-
`- 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}`,
|
|
1827
|
-
);
|
|
1828
|
-
});
|
|
1829
|
-
|
|
1830
|
-
// Log PowerSourceCluster
|
|
1831
|
-
cluster = PowerSourceCluster;
|
|
1832
|
-
attributes = await interactionClient.getMultipleAttributes({
|
|
1833
|
-
attributes: [{ clusterId: cluster.id }],
|
|
1834
|
-
});
|
|
1835
|
-
if (attributes.length > 0) this.log.info(`Cluster: ${idn}${cluster.name}${rs}${nf} attributes:`);
|
|
1836
|
-
attributes.forEach((attribute) => {
|
|
1837
|
-
this.log.info(
|
|
1838
|
-
`- 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}`,
|
|
1839
|
-
);
|
|
1840
|
-
});
|
|
1841
|
-
|
|
1842
|
-
// Log ThreadNetworkDiagnostics
|
|
1843
|
-
cluster = ThreadNetworkDiagnosticsCluster;
|
|
1844
|
-
attributes = await interactionClient.getMultipleAttributes({
|
|
1845
|
-
attributes: [{ clusterId: cluster.id }],
|
|
1846
|
-
});
|
|
1847
|
-
if (attributes.length > 0) this.log.info(`Cluster: ${idn}${cluster.name}${rs}${nf} attributes:`);
|
|
1848
|
-
attributes.forEach((attribute) => {
|
|
1849
|
-
this.log.info(
|
|
1850
|
-
`- 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}`,
|
|
1851
|
-
);
|
|
1852
|
-
});
|
|
1853
|
-
|
|
1854
|
-
// Log SwitchCluster
|
|
1855
|
-
cluster = SwitchCluster;
|
|
1856
|
-
attributes = await interactionClient.getMultipleAttributes({
|
|
1857
|
-
attributes: [{ clusterId: cluster.id }],
|
|
1858
|
-
});
|
|
1859
|
-
if (attributes.length > 0) this.log.info(`Cluster: ${idn}${cluster.name}${rs}${nf} attributes:`);
|
|
1860
|
-
attributes.forEach((attribute) => {
|
|
1861
|
-
this.log.info(
|
|
1862
|
-
`- 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}`,
|
|
1863
|
-
);
|
|
1864
|
-
});
|
|
1865
|
-
|
|
1866
|
-
this.log.info('Subscribing to all attributes and events');
|
|
1867
|
-
await node.subscribeAllAttributesAndEvents({
|
|
1868
|
-
ignoreInitialTriggers: false,
|
|
1869
|
-
attributeChangedCallback: ({ path: { nodeId, clusterId, endpointId, attributeName }, version, value }) =>
|
|
1870
|
-
this.log.info(
|
|
1871
|
-
`***${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}`,
|
|
1872
|
-
),
|
|
1873
|
-
eventTriggeredCallback: ({ path: { nodeId, clusterId, endpointId, eventName }, events }) => {
|
|
1874
|
-
this.log.info(
|
|
1875
|
-
`***${db}Commissioning controller eventTriggeredCallback: event ${BLUE}${nodeId}${db}/${or}${endpointId}${db}/${hk}${getClusterNameById(clusterId)}${db}/${zb}${eventName}${db} triggered with ${debugStringify(events ?? { none: true })}`,
|
|
1876
|
-
);
|
|
1877
|
-
},
|
|
1878
|
-
});
|
|
1879
|
-
this.log.info('Subscribed to all attributes and events');
|
|
1880
|
-
}
|
|
1881
|
-
*/
|
|
1882
1297
|
}
|
|
1883
|
-
/** */
|
|
1884
|
-
/** Matter.js methods */
|
|
1885
|
-
/** */
|
|
1886
|
-
/**
|
|
1887
|
-
* Starts the matter storage with name Matterbridge, create the matterbridge context and performs a backup.
|
|
1888
|
-
*
|
|
1889
|
-
* @returns {Promise<void>} - A promise that resolves when the storage is started.
|
|
1890
|
-
*/
|
|
1891
1298
|
async startMatterStorage() {
|
|
1892
|
-
// Setup Matter storage
|
|
1893
1299
|
this.log.info(`Starting matter node storage...`);
|
|
1894
1300
|
this.matterStorageService = this.environment.get(StorageService);
|
|
1895
1301
|
this.log.info(`Matter node storage service created: ${this.matterStorageService.location}`);
|
|
@@ -1897,17 +1303,8 @@ export class Matterbridge extends EventEmitter {
|
|
|
1897
1303
|
this.log.info('Matter node storage manager "Matterbridge" created');
|
|
1898
1304
|
this.matterbridgeContext = await this.createServerNodeContext('Matterbridge', 'Matterbridge', this.aggregatorDeviceType, this.aggregatorVendorId, this.aggregatorVendorName, this.aggregatorProductId, this.aggregatorProductName, this.aggregatorSerialNumber, this.aggregatorUniqueId);
|
|
1899
1305
|
this.log.info('Matter node storage started');
|
|
1900
|
-
// Backup matter storage since it is created/opened correctly
|
|
1901
1306
|
await this.backupMatterStorage(path.join(this.matterbridgeDirectory, MATTER_STORAGE_NAME), path.join(this.matterbridgeDirectory, MATTER_STORAGE_NAME + '.backup'));
|
|
1902
1307
|
}
|
|
1903
|
-
/**
|
|
1904
|
-
* Makes a backup copy of the specified matter storage directory.
|
|
1905
|
-
*
|
|
1906
|
-
* @param {string} storageName - The name of the storage directory to be backed up.
|
|
1907
|
-
* @param {string} backupName - The name of the backup directory to be created.
|
|
1908
|
-
* @private
|
|
1909
|
-
* @returns {Promise<void>} A promise that resolves when the has been done.
|
|
1910
|
-
*/
|
|
1911
1308
|
async backupMatterStorage(storageName, backupName) {
|
|
1912
1309
|
this.log.info('Creating matter node storage backup...');
|
|
1913
1310
|
try {
|
|
@@ -1918,11 +1315,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1918
1315
|
this.log.error(`Error creating matter node storage backup from ${storageName} to ${backupName}:`, error);
|
|
1919
1316
|
}
|
|
1920
1317
|
}
|
|
1921
|
-
/**
|
|
1922
|
-
* Stops the matter storage.
|
|
1923
|
-
*
|
|
1924
|
-
* @returns {Promise<void>} A promise that resolves when the storage is stopped.
|
|
1925
|
-
*/
|
|
1926
1318
|
async stopMatterStorage() {
|
|
1927
1319
|
this.log.info('Closing matter node storage...');
|
|
1928
1320
|
await this.matterStorageManager?.close();
|
|
@@ -1931,20 +1323,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1931
1323
|
this.matterbridgeContext = undefined;
|
|
1932
1324
|
this.log.info('Matter node storage closed');
|
|
1933
1325
|
}
|
|
1934
|
-
/**
|
|
1935
|
-
* Creates a server node storage context.
|
|
1936
|
-
*
|
|
1937
|
-
* @param {string} storeId - The storeId.
|
|
1938
|
-
* @param {string} deviceName - The name of the device.
|
|
1939
|
-
* @param {DeviceTypeId} deviceType - The device type of the device.
|
|
1940
|
-
* @param {number} vendorId - The vendor ID.
|
|
1941
|
-
* @param {string} vendorName - The vendor name.
|
|
1942
|
-
* @param {number} productId - The product ID.
|
|
1943
|
-
* @param {string} productName - The product name.
|
|
1944
|
-
* @param {string} [serialNumber] - The serial number of the device (optional).
|
|
1945
|
-
* @param {string} [uniqueId] - The unique ID of the device (optional).
|
|
1946
|
-
* @returns {Promise<StorageContext>} The storage context for the commissioning server.
|
|
1947
|
-
*/
|
|
1948
1326
|
async createServerNodeContext(storeId, deviceName, deviceType, vendorId, vendorName, productId, productName, serialNumber, uniqueId) {
|
|
1949
1327
|
const { randomBytes } = await import('node:crypto');
|
|
1950
1328
|
if (!this.matterStorageService)
|
|
@@ -1984,15 +1362,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1984
1362
|
this.log.debug(`- hardwareVersion: ${await storageContext.get('hardwareVersion')} hardwareVersionString: ${await storageContext.get('hardwareVersionString')}`);
|
|
1985
1363
|
return storageContext;
|
|
1986
1364
|
}
|
|
1987
|
-
/**
|
|
1988
|
-
* Creates a server node.
|
|
1989
|
-
*
|
|
1990
|
-
* @param {StorageContext} storageContext - The storage context for the server node.
|
|
1991
|
-
* @param {number} [port] - The port number for the server node. Defaults to 5540.
|
|
1992
|
-
* @param {number} [passcode] - The passcode for the server node. Defaults to 20242025.
|
|
1993
|
-
* @param {number} [discriminator] - The discriminator for the server node. Defaults to 3850.
|
|
1994
|
-
* @returns {Promise<ServerNode<ServerNode.RootEndpoint>>} A promise that resolves to the created server node.
|
|
1995
|
-
*/
|
|
1996
1365
|
async createServerNode(storageContext, port = 5540, passcode = 20242025, discriminator = 3850) {
|
|
1997
1366
|
const storeId = await storageContext.get('storeId');
|
|
1998
1367
|
this.log.notice(`Creating server node for ${storeId} on port ${port} with passcode ${passcode} and discriminator ${discriminator}...`);
|
|
@@ -2002,37 +1371,24 @@ export class Matterbridge extends EventEmitter {
|
|
|
2002
1371
|
this.log.debug(`- uniqueId: ${await storageContext.get('uniqueId')}`);
|
|
2003
1372
|
this.log.debug(`- softwareVersion: ${await storageContext.get('softwareVersion')} softwareVersionString: ${await storageContext.get('softwareVersionString')}`);
|
|
2004
1373
|
this.log.debug(`- hardwareVersion: ${await storageContext.get('hardwareVersion')} hardwareVersionString: ${await storageContext.get('hardwareVersionString')}`);
|
|
2005
|
-
/**
|
|
2006
|
-
* Create a Matter ServerNode, which contains the Root Endpoint and all relevant data and configuration
|
|
2007
|
-
*/
|
|
2008
1374
|
const serverNode = await ServerNode.create({
|
|
2009
|
-
// Required: Give the Node a unique ID which is used to store the state of this node
|
|
2010
1375
|
id: storeId,
|
|
2011
|
-
// Provide Network relevant configuration like the port
|
|
2012
|
-
// Optional when operating only one device on a host, Default port is 5540
|
|
2013
1376
|
network: {
|
|
2014
1377
|
listeningAddressIpv4: this.ipv4Address,
|
|
2015
1378
|
listeningAddressIpv6: this.ipv6Address,
|
|
2016
1379
|
port,
|
|
2017
1380
|
},
|
|
2018
|
-
// Provide the certificate for the device
|
|
2019
1381
|
operationalCredentials: {
|
|
2020
1382
|
certification: this.certification,
|
|
2021
1383
|
},
|
|
2022
|
-
// Provide Commissioning relevant settings
|
|
2023
|
-
// Optional for development/testing purposes
|
|
2024
1384
|
commissioning: {
|
|
2025
1385
|
passcode,
|
|
2026
1386
|
discriminator,
|
|
2027
1387
|
},
|
|
2028
|
-
// Provide Node announcement settings
|
|
2029
|
-
// Optional: If Ommitted some development defaults are used
|
|
2030
1388
|
productDescription: {
|
|
2031
1389
|
name: await storageContext.get('deviceName'),
|
|
2032
1390
|
deviceType: DeviceTypeId(await storageContext.get('deviceType')),
|
|
2033
1391
|
},
|
|
2034
|
-
// Provide defaults for the BasicInformation cluster on the Root endpoint
|
|
2035
|
-
// Optional: If Omitted some development defaults are used
|
|
2036
1392
|
basicInformation: {
|
|
2037
1393
|
vendorId: VendorId(await storageContext.get('vendorId')),
|
|
2038
1394
|
vendorName: await storageContext.get('vendorName'),
|
|
@@ -2049,23 +1405,17 @@ export class Matterbridge extends EventEmitter {
|
|
|
2049
1405
|
reachable: true,
|
|
2050
1406
|
},
|
|
2051
1407
|
});
|
|
2052
|
-
/**
|
|
2053
|
-
* This event is triggered when the device is initially commissioned successfully.
|
|
2054
|
-
* This means: It is added to the first fabric.
|
|
2055
|
-
*/
|
|
2056
1408
|
serverNode.lifecycle.commissioned.on(() => {
|
|
2057
1409
|
this.log.notice(`Server node for ${storeId} was initially commissioned successfully!`);
|
|
2058
1410
|
this.advertisingNodes.delete(storeId);
|
|
2059
1411
|
this.frontend.wssSendRefreshRequired('matter', { matter: { ...this.getServerNodeData(serverNode) } });
|
|
2060
1412
|
});
|
|
2061
|
-
/** This event is triggered when all fabrics are removed from the device, usually it also does a factory reset then. */
|
|
2062
1413
|
serverNode.lifecycle.decommissioned.on(() => {
|
|
2063
1414
|
this.log.notice(`Server node for ${storeId} was fully decommissioned successfully!`);
|
|
2064
1415
|
this.advertisingNodes.delete(storeId);
|
|
2065
1416
|
this.frontend.wssSendRefreshRequired('matter', { matter: { ...this.getServerNodeData(serverNode) } });
|
|
2066
1417
|
this.frontend.wssSendSnackbarMessage(`${storeId} is offline`, 5, 'warning');
|
|
2067
1418
|
});
|
|
2068
|
-
/** This event is triggered when the device went online. This means that it is discoverable in the network. */
|
|
2069
1419
|
serverNode.lifecycle.online.on(async () => {
|
|
2070
1420
|
this.log.notice(`Server node for ${storeId} is online`);
|
|
2071
1421
|
if (!serverNode.lifecycle.isCommissioned) {
|
|
@@ -2076,16 +1426,13 @@ export class Matterbridge extends EventEmitter {
|
|
|
2076
1426
|
this.log.notice(`Manual pairing code: ${manualPairingCode}`);
|
|
2077
1427
|
}
|
|
2078
1428
|
else {
|
|
2079
|
-
// istanbul ignore next
|
|
2080
1429
|
this.log.notice(`Server node for ${storeId} is already commissioned. Waiting for controllers to connect ...`);
|
|
2081
|
-
// istanbul ignore next
|
|
2082
1430
|
this.advertisingNodes.delete(storeId);
|
|
2083
1431
|
}
|
|
2084
1432
|
this.frontend.wssSendRefreshRequired('matter', { matter: { ...this.getServerNodeData(serverNode) } });
|
|
2085
1433
|
this.frontend.wssSendSnackbarMessage(`${storeId} is online`, 5, 'success');
|
|
2086
1434
|
this.emit('online', storeId);
|
|
2087
1435
|
});
|
|
2088
|
-
/** This event is triggered when the device went offline. it is not longer discoverable or connectable in the network. */
|
|
2089
1436
|
serverNode.lifecycle.offline.on(() => {
|
|
2090
1437
|
this.log.notice(`Server node for ${storeId} is offline`);
|
|
2091
1438
|
this.advertisingNodes.delete(storeId);
|
|
@@ -2093,15 +1440,11 @@ export class Matterbridge extends EventEmitter {
|
|
|
2093
1440
|
this.frontend.wssSendSnackbarMessage(`${storeId} is offline`, 5, 'warning');
|
|
2094
1441
|
this.emit('offline', storeId);
|
|
2095
1442
|
});
|
|
2096
|
-
/**
|
|
2097
|
-
* This event is triggered when a fabric is added, removed or updated on the device. Use this if more granular
|
|
2098
|
-
* information is needed.
|
|
2099
|
-
*/
|
|
2100
1443
|
serverNode.events.commissioning.fabricsChanged.on((fabricIndex, fabricAction) => {
|
|
2101
1444
|
let action = '';
|
|
2102
1445
|
switch (fabricAction) {
|
|
2103
1446
|
case FabricAction.Added:
|
|
2104
|
-
this.advertisingNodes.delete(storeId);
|
|
1447
|
+
this.advertisingNodes.delete(storeId);
|
|
2105
1448
|
action = 'added';
|
|
2106
1449
|
break;
|
|
2107
1450
|
case FabricAction.Removed:
|
|
@@ -2114,22 +1457,14 @@ export class Matterbridge extends EventEmitter {
|
|
|
2114
1457
|
this.log.notice(`Commissioned fabric index ${fabricIndex} ${action} on server node for ${storeId}: ${debugStringify(serverNode.state.commissioning.fabrics[fabricIndex])}`);
|
|
2115
1458
|
this.frontend.wssSendRefreshRequired('matter', { matter: { ...this.getServerNodeData(serverNode) } });
|
|
2116
1459
|
});
|
|
2117
|
-
/**
|
|
2118
|
-
* This event is triggered when an operative new session was opened by a Controller.
|
|
2119
|
-
* It is not triggered for the initial commissioning process, just afterwards for real connections.
|
|
2120
|
-
*/
|
|
2121
1460
|
serverNode.events.sessions.opened.on((session) => {
|
|
2122
1461
|
this.log.notice(`Session opened on server node for ${storeId}: ${debugStringify(session)}`);
|
|
2123
1462
|
this.frontend.wssSendRefreshRequired('matter', { matter: { ...this.getServerNodeData(serverNode) } });
|
|
2124
1463
|
});
|
|
2125
|
-
/**
|
|
2126
|
-
* This event is triggered when an operative session is closed by a Controller or because the Device goes offline.
|
|
2127
|
-
*/
|
|
2128
1464
|
serverNode.events.sessions.closed.on((session) => {
|
|
2129
1465
|
this.log.notice(`Session closed on server node for ${storeId}: ${debugStringify(session)}`);
|
|
2130
1466
|
this.frontend.wssSendRefreshRequired('matter', { matter: { ...this.getServerNodeData(serverNode) } });
|
|
2131
1467
|
});
|
|
2132
|
-
/** This event is triggered when a subscription gets added or removed on an operative session. */
|
|
2133
1468
|
serverNode.events.sessions.subscriptionsChanged.on((session) => {
|
|
2134
1469
|
this.log.notice(`Session subscriptions changed on server node for ${storeId}: ${debugStringify(session)}`);
|
|
2135
1470
|
this.frontend.wssSendRefreshRequired('matter', { matter: { ...this.getServerNodeData(serverNode) } });
|
|
@@ -2137,12 +1472,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2137
1472
|
this.log.info(`Created server node for ${storeId}`);
|
|
2138
1473
|
return serverNode;
|
|
2139
1474
|
}
|
|
2140
|
-
/**
|
|
2141
|
-
* Gets the matter sanitized data of the specified server node.
|
|
2142
|
-
*
|
|
2143
|
-
* @param {ServerNode} [serverNode] - The server node to start.
|
|
2144
|
-
* @returns {ApiMatter} The sanitized data of the server node.
|
|
2145
|
-
*/
|
|
2146
1475
|
getServerNodeData(serverNode) {
|
|
2147
1476
|
const advertiseTime = this.advertisingNodes.get(serverNode.id) || 0;
|
|
2148
1477
|
return {
|
|
@@ -2159,25 +1488,12 @@ export class Matterbridge extends EventEmitter {
|
|
|
2159
1488
|
serialNumber: serverNode.state.basicInformation.serialNumber,
|
|
2160
1489
|
};
|
|
2161
1490
|
}
|
|
2162
|
-
/**
|
|
2163
|
-
* Starts the specified server node.
|
|
2164
|
-
*
|
|
2165
|
-
* @param {ServerNode} [matterServerNode] - The server node to start.
|
|
2166
|
-
* @returns {Promise<void>} A promise that resolves when the server node has started.
|
|
2167
|
-
*/
|
|
2168
1491
|
async startServerNode(matterServerNode) {
|
|
2169
1492
|
if (!matterServerNode)
|
|
2170
1493
|
return;
|
|
2171
1494
|
this.log.notice(`Starting ${matterServerNode.id} server node`);
|
|
2172
1495
|
await matterServerNode.start();
|
|
2173
1496
|
}
|
|
2174
|
-
/**
|
|
2175
|
-
* Stops the specified server node.
|
|
2176
|
-
*
|
|
2177
|
-
* @param {ServerNode} matterServerNode - The server node to stop.
|
|
2178
|
-
* @param {number} [timeout] - The timeout in milliseconds for stopping the server node. Defaults to 30 seconds.
|
|
2179
|
-
* @returns {Promise<void>} A promise that resolves when the server node has stopped.
|
|
2180
|
-
*/
|
|
2181
1497
|
async stopServerNode(matterServerNode, timeout = 30000) {
|
|
2182
1498
|
if (!matterServerNode)
|
|
2183
1499
|
return;
|
|
@@ -2190,25 +1506,12 @@ export class Matterbridge extends EventEmitter {
|
|
|
2190
1506
|
this.log.error(`Failed to close ${matterServerNode.id} server node: ${error instanceof Error ? error.message : error}`);
|
|
2191
1507
|
}
|
|
2192
1508
|
}
|
|
2193
|
-
/**
|
|
2194
|
-
* Creates an aggregator node with the specified storage context.
|
|
2195
|
-
*
|
|
2196
|
-
* @param {StorageContext} storageContext - The storage context for the aggregator node.
|
|
2197
|
-
* @returns {Promise<Endpoint<AggregatorEndpoint>>} A promise that resolves to the created aggregator node.
|
|
2198
|
-
*/
|
|
2199
1509
|
async createAggregatorNode(storageContext) {
|
|
2200
1510
|
this.log.notice(`Creating ${await storageContext.get('storeId')} aggregator...`);
|
|
2201
1511
|
const aggregatorNode = new Endpoint(AggregatorEndpoint, { id: `${await storageContext.get('storeId')}` });
|
|
2202
1512
|
this.log.info(`Created ${await storageContext.get('storeId')} aggregator`);
|
|
2203
1513
|
return aggregatorNode;
|
|
2204
1514
|
}
|
|
2205
|
-
/**
|
|
2206
|
-
* Creates and configures the server node for an accessory plugin for a given device.
|
|
2207
|
-
*
|
|
2208
|
-
* @param {Plugin} plugin - The plugin to configure.
|
|
2209
|
-
* @param {MatterbridgeEndpoint} device - The device to associate with the plugin.
|
|
2210
|
-
* @returns {Promise<void>} A promise that resolves when the server node for the accessory plugin is created and configured.
|
|
2211
|
-
*/
|
|
2212
1515
|
async createAccessoryPlugin(plugin, device) {
|
|
2213
1516
|
if (!plugin.locked && device.deviceType && device.deviceName && device.vendorId && device.productId && device.vendorName && device.productName) {
|
|
2214
1517
|
plugin.locked = true;
|
|
@@ -2220,12 +1523,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2220
1523
|
await plugin.serverNode.add(device);
|
|
2221
1524
|
}
|
|
2222
1525
|
}
|
|
2223
|
-
/**
|
|
2224
|
-
* Creates and configures the server node and the aggregator node for a dynamic plugin.
|
|
2225
|
-
*
|
|
2226
|
-
* @param {Plugin} plugin - The plugin to configure.
|
|
2227
|
-
* @returns {Promise<void>} A promise that resolves when the server node and the aggregator node for the dynamic plugin is created and configured.
|
|
2228
|
-
*/
|
|
2229
1526
|
async createDynamicPlugin(plugin) {
|
|
2230
1527
|
if (!plugin.locked) {
|
|
2231
1528
|
plugin.locked = true;
|
|
@@ -2238,13 +1535,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2238
1535
|
await plugin.serverNode.add(plugin.aggregatorNode);
|
|
2239
1536
|
}
|
|
2240
1537
|
}
|
|
2241
|
-
/**
|
|
2242
|
-
* Creates and configures the server node for a single not bridged device.
|
|
2243
|
-
*
|
|
2244
|
-
* @param {Plugin} plugin - The plugin to configure.
|
|
2245
|
-
* @param {MatterbridgeEndpoint} device - The device to associate with the plugin.
|
|
2246
|
-
* @returns {Promise<void>} A promise that resolves when the server node for the accessory plugin is created and configured.
|
|
2247
|
-
*/
|
|
2248
1538
|
async createDeviceServerNode(plugin, device) {
|
|
2249
1539
|
if (device.mode === 'server' && !device.serverNode && device.deviceType && device.deviceName && device.vendorId && device.vendorName && device.productId && device.productName) {
|
|
2250
1540
|
this.log.debug(`Creating device ${plg}${plugin.name}${db}:${dev}${device.deviceName}${db} server node...`);
|
|
@@ -2255,15 +1545,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
2255
1545
|
this.log.debug(`Added ${plg}${plugin.name}${db}:${dev}${device.deviceName}${db} to server node`);
|
|
2256
1546
|
}
|
|
2257
1547
|
}
|
|
2258
|
-
/**
|
|
2259
|
-
* Adds a MatterbridgeEndpoint to the specified plugin.
|
|
2260
|
-
*
|
|
2261
|
-
* @param {string} pluginName - The name of the plugin.
|
|
2262
|
-
* @param {MatterbridgeEndpoint} device - The device to add as a bridged endpoint.
|
|
2263
|
-
* @returns {Promise<void>} A promise that resolves when the bridged endpoint has been added.
|
|
2264
|
-
*/
|
|
2265
1548
|
async addBridgedEndpoint(pluginName, device) {
|
|
2266
|
-
// Check if the plugin is registered
|
|
2267
1549
|
const plugin = this.plugins.get(pluginName);
|
|
2268
1550
|
if (!plugin) {
|
|
2269
1551
|
this.log.error(`Error adding bridged endpoint ${dev}${device.deviceName}${er} (${zb}${device.id}${er}) plugin ${plg}${pluginName}${er} not found`);
|
|
@@ -2283,7 +1565,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2283
1565
|
}
|
|
2284
1566
|
else if (this.bridgeMode === 'bridge') {
|
|
2285
1567
|
if (device.mode === 'matter') {
|
|
2286
|
-
// Register and add the device to the matterbridge server node
|
|
2287
1568
|
this.log.debug(`Adding matter endpoint ${plg}${pluginName}${db}:${dev}${device.deviceName}${db} to Matterbridge server node...`);
|
|
2288
1569
|
if (!this.serverNode) {
|
|
2289
1570
|
this.log.error('Server node not found for Matterbridge');
|
|
@@ -2300,7 +1581,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2300
1581
|
}
|
|
2301
1582
|
}
|
|
2302
1583
|
else {
|
|
2303
|
-
// Register and add the device to the matterbridge aggregator node
|
|
2304
1584
|
this.log.debug(`Adding bridged endpoint ${plg}${pluginName}${db}:${dev}${device.deviceName}${db} to Matterbridge aggregator node`);
|
|
2305
1585
|
if (!this.aggregatorNode) {
|
|
2306
1586
|
this.log.error('Aggregator node not found for Matterbridge');
|
|
@@ -2318,7 +1598,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2318
1598
|
}
|
|
2319
1599
|
}
|
|
2320
1600
|
else if (this.bridgeMode === 'childbridge') {
|
|
2321
|
-
// Register and add the device to the plugin server node
|
|
2322
1601
|
if (plugin.type === 'AccessoryPlatform') {
|
|
2323
1602
|
try {
|
|
2324
1603
|
this.log.debug(`Creating endpoint ${dev}${device.deviceName}${db} for AccessoryPlatform plugin ${plg}${plugin.name}${db} server node`);
|
|
@@ -2342,12 +1621,10 @@ export class Matterbridge extends EventEmitter {
|
|
|
2342
1621
|
return;
|
|
2343
1622
|
}
|
|
2344
1623
|
}
|
|
2345
|
-
// Register and add the device to the plugin aggregator node
|
|
2346
1624
|
if (plugin.type === 'DynamicPlatform') {
|
|
2347
1625
|
try {
|
|
2348
1626
|
this.log.debug(`Adding bridged endpoint ${dev}${device.deviceName}${db} for DynamicPlatform plugin ${plg}${plugin.name}${db} aggregator node`);
|
|
2349
1627
|
await this.createDynamicPlugin(plugin);
|
|
2350
|
-
// Fast plugins can add another device before the server node is ready, so we wait for the server node to be ready
|
|
2351
1628
|
await waiter(`createDynamicPlugin(${plugin.name})`, () => plugin.serverNode?.hasParts === true);
|
|
2352
1629
|
if (!plugin.aggregatorNode) {
|
|
2353
1630
|
this.log.error(`Aggregator node not found for plugin ${plg}${plugin.name}${er}`);
|
|
@@ -2368,28 +1645,17 @@ export class Matterbridge extends EventEmitter {
|
|
|
2368
1645
|
}
|
|
2369
1646
|
if (plugin.registeredDevices !== undefined)
|
|
2370
1647
|
plugin.registeredDevices++;
|
|
2371
|
-
// Add the device to the DeviceManager
|
|
2372
1648
|
this.devices.set(device);
|
|
2373
|
-
// Subscribe to the attributes changed event
|
|
2374
1649
|
await this.subscribeAttributeChanged(plugin, device);
|
|
2375
1650
|
this.log.info(`Added and registered bridged endpoint (${plugin.registeredDevices}) ${dev}${device.deviceName}${nf} (${dev}${device.id}${nf}) for plugin ${plg}${pluginName}${nf}`);
|
|
2376
1651
|
}
|
|
2377
|
-
/**
|
|
2378
|
-
* Removes a MatterbridgeEndpoint from the specified plugin.
|
|
2379
|
-
*
|
|
2380
|
-
* @param {string} pluginName - The name of the plugin.
|
|
2381
|
-
* @param {MatterbridgeEndpoint} device - The device to remove as a bridged endpoint.
|
|
2382
|
-
* @returns {Promise<void>} A promise that resolves when the bridged endpoint has been removed.
|
|
2383
|
-
*/
|
|
2384
1652
|
async removeBridgedEndpoint(pluginName, device) {
|
|
2385
1653
|
this.log.debug(`Removing bridged endpoint ${plg}${pluginName}${db}:${dev}${device.deviceName}${db} (${zb}${device.name}${db}) for plugin ${plg}${pluginName}${db}`);
|
|
2386
|
-
// Check if the plugin is registered
|
|
2387
1654
|
const plugin = this.plugins.get(pluginName);
|
|
2388
1655
|
if (!plugin) {
|
|
2389
1656
|
this.log.error(`Error removing bridged endpoint ${dev}${device.deviceName}${er} (${zb}${device.name}${er}) for plugin ${plg}${pluginName}${er}: plugin not found`);
|
|
2390
1657
|
return;
|
|
2391
1658
|
}
|
|
2392
|
-
// Register and add the device to the matterbridge aggregator node
|
|
2393
1659
|
if (this.bridgeMode === 'bridge') {
|
|
2394
1660
|
if (!this.aggregatorNode) {
|
|
2395
1661
|
this.log.error(`Error removing bridged endpoint ${dev}${device.deviceName}${er} (${zb}${device.name}${er}) for plugin ${plg}${pluginName}${er}: aggregator node not found`);
|
|
@@ -2402,7 +1668,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2402
1668
|
}
|
|
2403
1669
|
else if (this.bridgeMode === 'childbridge') {
|
|
2404
1670
|
if (plugin.type === 'AccessoryPlatform') {
|
|
2405
|
-
// Nothing to do here since the server node has no aggregator node but only the device itself
|
|
2406
1671
|
}
|
|
2407
1672
|
else if (plugin.type === 'DynamicPlatform') {
|
|
2408
1673
|
if (!plugin.aggregatorNode) {
|
|
@@ -2415,21 +1680,8 @@ export class Matterbridge extends EventEmitter {
|
|
|
2415
1680
|
if (plugin.registeredDevices !== undefined)
|
|
2416
1681
|
plugin.registeredDevices--;
|
|
2417
1682
|
}
|
|
2418
|
-
// Remove the device from the DeviceManager
|
|
2419
1683
|
this.devices.remove(device);
|
|
2420
1684
|
}
|
|
2421
|
-
/**
|
|
2422
|
-
* Removes all bridged endpoints from the specified plugin.
|
|
2423
|
-
*
|
|
2424
|
-
* @param {string} pluginName - The name of the plugin.
|
|
2425
|
-
* @param {number} [delay] - The delay in milliseconds between removing each bridged endpoint (default: 0).
|
|
2426
|
-
* @returns {Promise<void>} A promise that resolves when all bridged endpoints have been removed.
|
|
2427
|
-
*
|
|
2428
|
-
* @remarks
|
|
2429
|
-
* This method iterates through all devices in the DeviceManager and removes each bridged endpoint associated with the specified plugin.
|
|
2430
|
-
* It also applies a delay between each removal if specified.
|
|
2431
|
-
* The delay is useful to allow the controllers to receive a single subscription for each device removed.
|
|
2432
|
-
*/
|
|
2433
1685
|
async removeAllBridgedEndpoints(pluginName, delay = 0) {
|
|
2434
1686
|
this.log.debug(`Removing all bridged endpoints for plugin ${plg}${pluginName}${db}${delay > 0 ? ` with delay ${delay} ms` : ''}`);
|
|
2435
1687
|
for (const device of this.devices.array().filter((device) => device.plugin === pluginName)) {
|
|
@@ -2440,24 +1692,13 @@ export class Matterbridge extends EventEmitter {
|
|
|
2440
1692
|
if (delay > 0)
|
|
2441
1693
|
await wait(2000);
|
|
2442
1694
|
}
|
|
2443
|
-
/**
|
|
2444
|
-
* Subscribes to the attribute change event for the given device and plugin.
|
|
2445
|
-
* Specifically, it listens for changes in the 'reachable' attribute of the
|
|
2446
|
-
* BridgedDeviceBasicInformationServer cluster server of the bridged device or BasicInformationServer cluster server of server node.
|
|
2447
|
-
*
|
|
2448
|
-
* @param {Plugin} plugin - The plugin associated with the device.
|
|
2449
|
-
* @param {MatterbridgeEndpoint} device - The device to subscribe to attribute changes for.
|
|
2450
|
-
* @returns {Promise<void>} A promise that resolves when the subscription is set up.
|
|
2451
|
-
*/
|
|
2452
1695
|
async subscribeAttributeChanged(plugin, device) {
|
|
2453
1696
|
if (!plugin || !device || !device.plugin || !device.serialNumber || !device.uniqueId || !device.maybeNumber)
|
|
2454
1697
|
return;
|
|
2455
1698
|
this.log.info(`Subscribing attributes for endpoint ${dev}${device.deviceName}${nf} (${dev}${device.id}${nf}) plugin ${plg}${plugin.name}${nf}`);
|
|
2456
|
-
// Subscribe to the reachable$Changed event of the BasicInformationServer cluster server of the server node in childbridge mode
|
|
2457
1699
|
if (this.bridgeMode === 'childbridge' && plugin.type === 'AccessoryPlatform' && plugin.serverNode) {
|
|
2458
1700
|
plugin.serverNode.eventsOf(BasicInformationServer).reachable$Changed?.on((reachable) => {
|
|
2459
1701
|
this.log.info(`Accessory endpoint ${dev}${device.deviceName}${nf} (${dev}${device.id}${nf}) is ${reachable ? 'reachable' : 'unreachable'}`);
|
|
2460
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
2461
1702
|
this.frontend.wssSendAttributeChangedMessage(device.plugin, device.serialNumber, device.uniqueId, device.number, device.id, 'BasicInformation', 'reachable', reachable);
|
|
2462
1703
|
});
|
|
2463
1704
|
}
|
|
@@ -2507,7 +1748,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2507
1748
|
this.log.debug(`Subscribing to endpoint ${or}${device.id}${db}:${or}${device.number}${db} attribute ${dev}${sub.cluster}${db}.${dev}${sub.attribute}${db} changes...`);
|
|
2508
1749
|
await device.subscribeAttribute(sub.cluster, sub.attribute, (value) => {
|
|
2509
1750
|
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}${isValidObject(value) ? debugStringify(value) : value}${db}`);
|
|
2510
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
2511
1751
|
this.frontend.wssSendAttributeChangedMessage(device.plugin, device.serialNumber, device.uniqueId, device.number, device.id, sub.cluster, sub.attribute, value);
|
|
2512
1752
|
});
|
|
2513
1753
|
}
|
|
@@ -2516,19 +1756,12 @@ export class Matterbridge extends EventEmitter {
|
|
|
2516
1756
|
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...`);
|
|
2517
1757
|
await child.subscribeAttribute(sub.cluster, sub.attribute, (value) => {
|
|
2518
1758
|
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}${isValidObject(value) ? debugStringify(value) : value}${db}`);
|
|
2519
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
2520
1759
|
this.frontend.wssSendAttributeChangedMessage(device.plugin, device.serialNumber, device.uniqueId, child.number, child.id, sub.cluster, sub.attribute, value);
|
|
2521
1760
|
});
|
|
2522
1761
|
}
|
|
2523
1762
|
}
|
|
2524
1763
|
}
|
|
2525
1764
|
}
|
|
2526
|
-
/**
|
|
2527
|
-
* Sanitizes the fabric information by converting bigint properties to strings because `res.json` doesn't support bigint.
|
|
2528
|
-
*
|
|
2529
|
-
* @param {ExposedFabricInformation[]} fabricInfo - The array of exposed fabric information objects.
|
|
2530
|
-
* @returns {SanitizedExposedFabricInformation[]} An array of sanitized exposed fabric information objects.
|
|
2531
|
-
*/
|
|
2532
1765
|
sanitizeFabricInformations(fabricInfo) {
|
|
2533
1766
|
return fabricInfo.map((info) => {
|
|
2534
1767
|
return {
|
|
@@ -2542,12 +1775,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2542
1775
|
};
|
|
2543
1776
|
});
|
|
2544
1777
|
}
|
|
2545
|
-
/**
|
|
2546
|
-
* Sanitizes the session information by converting bigint properties to strings because `res.json` doesn't support bigint.
|
|
2547
|
-
*
|
|
2548
|
-
* @param {SessionsBehavior.Session[]} sessions - The array of session information objects.
|
|
2549
|
-
* @returns {SanitizedSession[]} An array of sanitized session information objects.
|
|
2550
|
-
*/
|
|
2551
1778
|
sanitizeSessionInformation(sessions) {
|
|
2552
1779
|
return sessions
|
|
2553
1780
|
.filter((session) => session.isPeerActive)
|
|
@@ -2574,21 +1801,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
2574
1801
|
};
|
|
2575
1802
|
});
|
|
2576
1803
|
}
|
|
2577
|
-
/**
|
|
2578
|
-
* Sets the reachability of the specified aggregator node bridged devices and trigger.
|
|
2579
|
-
*
|
|
2580
|
-
* @param {Endpoint<AggregatorEndpoint>} aggregatorNode - The aggregator node to set the reachability for.
|
|
2581
|
-
* @param {boolean} reachable - A boolean indicating the reachability status to set.
|
|
2582
|
-
*/
|
|
2583
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
2584
1804
|
async setAggregatorReachability(aggregatorNode, reachable) {
|
|
2585
|
-
/*
|
|
2586
|
-
for (const child of aggregatorNode.parts) {
|
|
2587
|
-
this.log.debug(`Setting reachability of ${(child as unknown as MatterbridgeEndpoint)?.deviceName} to ${reachable}`);
|
|
2588
|
-
await child.setStateOf(BridgedDeviceBasicInformationServer, { reachable });
|
|
2589
|
-
child.act((agent) => child.eventsOf(BridgedDeviceBasicInformationServer).reachableChanged.emit({ reachableNewValue: true }, agent.context));
|
|
2590
|
-
}
|
|
2591
|
-
*/
|
|
2592
1805
|
}
|
|
2593
1806
|
getVendorIdName = (vendorId) => {
|
|
2594
1807
|
if (!vendorId)
|
|
@@ -2628,11 +1841,10 @@ export class Matterbridge extends EventEmitter {
|
|
|
2628
1841
|
case 0x1488:
|
|
2629
1842
|
vendorName = '(ShortcutLabsFlic)';
|
|
2630
1843
|
break;
|
|
2631
|
-
case 65521:
|
|
1844
|
+
case 65521:
|
|
2632
1845
|
vendorName = '(MatterTest)';
|
|
2633
1846
|
break;
|
|
2634
1847
|
}
|
|
2635
1848
|
return vendorName;
|
|
2636
1849
|
};
|
|
2637
1850
|
}
|
|
2638
|
-
//# sourceMappingURL=matterbridge.js.map
|