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