matterbridge 3.4.1 → 3.4.2-dev-20251202-c41a119
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 +20 -0
- package/dist/broadcastServer.js +0 -112
- 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/deviceManager.js +1 -113
- package/dist/devices/airConditioner.js +0 -57
- package/dist/devices/batteryStorage.js +1 -48
- package/dist/devices/cooktop.js +0 -56
- 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 -43
- 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 +35 -455
- package/dist/frontendTypes.js +0 -45
- package/dist/helpers.js +0 -53
- package/dist/index.js +0 -25
- package/dist/jestutils/export.js +0 -1
- package/dist/jestutils/jestHelpers.js +13 -368
- 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/matterNode.js +8 -369
- package/dist/matterbridge.js +46 -807
- package/dist/matterbridgeAccessoryPlatform.js +0 -38
- package/dist/matterbridgeBehaviors.js +5 -68
- package/dist/matterbridgeDeviceTypes.js +14 -635
- package/dist/matterbridgeDynamicPlatform.js +0 -38
- package/dist/matterbridgeEndpoint.js +53 -1444
- package/dist/matterbridgeEndpointHelpers.js +20 -483
- package/dist/matterbridgeEndpointTypes.js +0 -25
- package/dist/matterbridgePlatform.js +1 -450
- package/dist/matterbridgeTypes.js +0 -26
- package/dist/pluginManager.js +5 -341
- package/dist/shelly.js +7 -178
- package/dist/storage/export.js +0 -1
- package/dist/update.js +1 -93
- package/dist/utils/colorUtils.js +2 -97
- package/dist/utils/commandLine.js +0 -60
- package/dist/utils/copyDirectory.js +0 -37
- 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 -42
- package/dist/utils/export.js +0 -1
- package/dist/utils/format.js +0 -49
- package/dist/utils/hex.js +0 -124
- package/dist/utils/inspector.js +1 -69
- package/dist/utils/isvalid.js +0 -101
- package/dist/utils/network.js +5 -96
- package/dist/utils/spawn.js +1 -71
- package/dist/utils/tracker.js +1 -64
- package/dist/utils/wait.js +8 -60
- package/dist/workerGlobalPrefix.js +5 -37
- package/dist/workerTypes.js +0 -24
- package/dist/workers.js +4 -68
- package/frontend/build/assets/index.js +4 -4
- package/frontend/build/assets/vendor_mui.js +1 -1
- package/frontend/package.json +1 -1
- package/npm-shrinkwrap.json +14 -10
- package/package.json +1 -2
- package/dist/broadcastServer.d.ts +0 -136
- package/dist/broadcastServer.d.ts.map +0 -1
- package/dist/broadcastServer.js.map +0 -1
- package/dist/broadcastServerTypes.d.ts +0 -841
- 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/deviceManager.d.ts +0 -135
- 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 -61
- package/dist/devices/cooktop.d.ts.map +0 -1
- package/dist/devices/cooktop.js.map +0 -1
- package/dist/devices/dishwasher.d.ts +0 -71
- package/dist/devices/dishwasher.d.ts.map +0 -1
- package/dist/devices/dishwasher.js.map +0 -1
- package/dist/devices/evse.d.ts +0 -76
- package/dist/devices/evse.d.ts.map +0 -1
- package/dist/devices/evse.js.map +0 -1
- package/dist/devices/export.d.ts +0 -17
- package/dist/devices/export.d.ts.map +0 -1
- package/dist/devices/export.js.map +0 -1
- package/dist/devices/extractorHood.d.ts +0 -46
- package/dist/devices/extractorHood.d.ts.map +0 -1
- package/dist/devices/extractorHood.js.map +0 -1
- package/dist/devices/heatPump.d.ts +0 -47
- package/dist/devices/heatPump.d.ts.map +0 -1
- package/dist/devices/heatPump.js.map +0 -1
- package/dist/devices/laundryDryer.d.ts +0 -67
- package/dist/devices/laundryDryer.d.ts.map +0 -1
- package/dist/devices/laundryDryer.js.map +0 -1
- package/dist/devices/laundryWasher.d.ts +0 -81
- package/dist/devices/laundryWasher.d.ts.map +0 -1
- package/dist/devices/laundryWasher.js.map +0 -1
- package/dist/devices/microwaveOven.d.ts +0 -168
- package/dist/devices/microwaveOven.d.ts.map +0 -1
- package/dist/devices/microwaveOven.js.map +0 -1
- package/dist/devices/oven.d.ts +0 -105
- package/dist/devices/oven.d.ts.map +0 -1
- package/dist/devices/oven.js.map +0 -1
- package/dist/devices/refrigerator.d.ts +0 -118
- package/dist/devices/refrigerator.d.ts.map +0 -1
- package/dist/devices/refrigerator.js.map +0 -1
- package/dist/devices/roboticVacuumCleaner.d.ts +0 -112
- package/dist/devices/roboticVacuumCleaner.d.ts.map +0 -1
- package/dist/devices/roboticVacuumCleaner.js.map +0 -1
- package/dist/devices/solarPower.d.ts +0 -40
- package/dist/devices/solarPower.d.ts.map +0 -1
- package/dist/devices/solarPower.js.map +0 -1
- package/dist/devices/speaker.d.ts +0 -87
- package/dist/devices/speaker.d.ts.map +0 -1
- package/dist/devices/speaker.js.map +0 -1
- package/dist/devices/temperatureControl.d.ts +0 -166
- package/dist/devices/temperatureControl.d.ts.map +0 -1
- package/dist/devices/temperatureControl.js.map +0 -1
- package/dist/devices/waterHeater.d.ts +0 -111
- package/dist/devices/waterHeater.d.ts.map +0 -1
- package/dist/devices/waterHeater.js.map +0 -1
- package/dist/dgram/coap.d.ts +0 -205
- package/dist/dgram/coap.d.ts.map +0 -1
- package/dist/dgram/coap.js.map +0 -1
- package/dist/dgram/dgram.d.ts +0 -141
- package/dist/dgram/dgram.d.ts.map +0 -1
- package/dist/dgram/dgram.js.map +0 -1
- package/dist/dgram/mb_coap.d.ts +0 -24
- package/dist/dgram/mb_coap.d.ts.map +0 -1
- package/dist/dgram/mb_coap.js.map +0 -1
- package/dist/dgram/mb_mdns.d.ts +0 -24
- package/dist/dgram/mb_mdns.d.ts.map +0 -1
- package/dist/dgram/mb_mdns.js.map +0 -1
- package/dist/dgram/mdns.d.ts +0 -290
- package/dist/dgram/mdns.d.ts.map +0 -1
- package/dist/dgram/mdns.js.map +0 -1
- package/dist/dgram/multicast.d.ts +0 -67
- package/dist/dgram/multicast.d.ts.map +0 -1
- package/dist/dgram/multicast.js.map +0 -1
- package/dist/dgram/unicast.d.ts +0 -56
- package/dist/dgram/unicast.d.ts.map +0 -1
- package/dist/dgram/unicast.js.map +0 -1
- package/dist/frontend.d.ts +0 -238
- package/dist/frontend.d.ts.map +0 -1
- package/dist/frontend.js.map +0 -1
- package/dist/frontendTypes.d.ts +0 -529
- package/dist/frontendTypes.d.ts.map +0 -1
- package/dist/frontendTypes.js.map +0 -1
- package/dist/helpers.d.ts +0 -48
- package/dist/helpers.d.ts.map +0 -1
- package/dist/helpers.js.map +0 -1
- package/dist/index.d.ts +0 -34
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/jestutils/export.d.ts +0 -2
- package/dist/jestutils/export.d.ts.map +0 -1
- package/dist/jestutils/export.js.map +0 -1
- package/dist/jestutils/jestHelpers.d.ts +0 -340
- package/dist/jestutils/jestHelpers.d.ts.map +0 -1
- package/dist/jestutils/jestHelpers.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/matterNode.d.ts +0 -342
- package/dist/matterNode.d.ts.map +0 -1
- package/dist/matterNode.js.map +0 -1
- package/dist/matterbridge.d.ts +0 -493
- package/dist/matterbridge.d.ts.map +0 -1
- package/dist/matterbridge.js.map +0 -1
- package/dist/matterbridgeAccessoryPlatform.d.ts +0 -41
- 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 -698
- package/dist/matterbridgeDeviceTypes.d.ts.map +0 -1
- package/dist/matterbridgeDeviceTypes.js.map +0 -1
- package/dist/matterbridgeDynamicPlatform.d.ts +0 -41
- package/dist/matterbridgeDynamicPlatform.d.ts.map +0 -1
- package/dist/matterbridgeDynamicPlatform.js.map +0 -1
- package/dist/matterbridgeEndpoint.d.ts +0 -1507
- package/dist/matterbridgeEndpoint.d.ts.map +0 -1
- package/dist/matterbridgeEndpoint.js.map +0 -1
- package/dist/matterbridgeEndpointHelpers.d.ts +0 -787
- package/dist/matterbridgeEndpointHelpers.d.ts.map +0 -1
- package/dist/matterbridgeEndpointHelpers.js.map +0 -1
- package/dist/matterbridgeEndpointTypes.d.ts +0 -166
- package/dist/matterbridgeEndpointTypes.d.ts.map +0 -1
- package/dist/matterbridgeEndpointTypes.js.map +0 -1
- package/dist/matterbridgePlatform.d.ts +0 -537
- package/dist/matterbridgePlatform.d.ts.map +0 -1
- package/dist/matterbridgePlatform.js.map +0 -1
- package/dist/matterbridgeTypes.d.ts +0 -251
- package/dist/matterbridgeTypes.d.ts.map +0 -1
- package/dist/matterbridgeTypes.js.map +0 -1
- package/dist/pluginManager.d.ts +0 -372
- package/dist/pluginManager.d.ts.map +0 -1
- package/dist/pluginManager.js.map +0 -1
- package/dist/shelly.d.ts +0 -181
- 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 -84
- 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 -35
- 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 -45
- 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/network.d.ts +0 -111
- package/dist/utils/network.d.ts.map +0 -1
- package/dist/utils/network.js.map +0 -1
- package/dist/utils/spawn.d.ts +0 -33
- 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/workerGlobalPrefix.d.ts +0 -25
- package/dist/workerGlobalPrefix.d.ts.map +0 -1
- package/dist/workerGlobalPrefix.js.map +0 -1
- package/dist/workerTypes.d.ts +0 -52
- package/dist/workerTypes.d.ts.map +0 -1
- package/dist/workerTypes.js.map +0 -1
- package/dist/workers.d.ts +0 -69
- package/dist/workers.d.ts.map +0 -1
- package/dist/workers.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.2
|
|
8
|
-
* @license Apache-2.0
|
|
9
|
-
*
|
|
10
|
-
* Copyright 2023, 2024, 2025 Luca Liguori.
|
|
11
|
-
*
|
|
12
|
-
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
13
|
-
* you may not use this file except in compliance with the License.
|
|
14
|
-
* You may obtain a copy of the License at
|
|
15
|
-
*
|
|
16
|
-
* http://www.apache.org/licenses/LICENSE-2.0
|
|
17
|
-
*
|
|
18
|
-
* Unless required by applicable law or agreed to in writing, software
|
|
19
|
-
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
20
|
-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
21
|
-
* See the License for the specific language governing permissions and
|
|
22
|
-
* limitations under the License.
|
|
23
|
-
*/
|
|
24
|
-
// eslint-disable-next-line no-console
|
|
25
1
|
if (process.argv.includes('--loader') || process.argv.includes('-loader'))
|
|
26
2
|
console.log('\u001B[32mMatterbridge loaded.\u001B[40;0m');
|
|
27
|
-
// Node.js modules
|
|
28
3
|
import os from 'node:os';
|
|
29
4
|
import path from 'node:path';
|
|
30
5
|
import fs from 'node:fs';
|
|
31
6
|
import EventEmitter from 'node:events';
|
|
32
7
|
import { inspect } from 'node:util';
|
|
33
|
-
// AnsiLogger module
|
|
34
8
|
import { AnsiLogger, UNDERLINE, UNDERLINEOFF, db, debugStringify, BRIGHT, RESET, er, nf, rs, wr, RED, GREEN, zb, CYAN, nt, BLUE, or } from 'node-ansi-logger';
|
|
35
|
-
// NodeStorage module
|
|
36
9
|
import { NodeStorageManager } from 'node-persist-manager';
|
|
37
|
-
|
|
38
|
-
import '@matter/nodejs'; // Set up Node.js environment for matter.js
|
|
10
|
+
import '@matter/nodejs';
|
|
39
11
|
import { Logger, LogLevel as MatterLogLevel, LogFormat as MatterLogFormat, UINT32_MAX, UINT16_MAX, Crypto, Environment, StorageService } from '@matter/general';
|
|
40
12
|
import { FabricAction, PaseClient } from '@matter/protocol';
|
|
41
13
|
import { Endpoint, ServerNode } from '@matter/node';
|
|
42
14
|
import { DeviceTypeId, VendorId } from '@matter/types/datatype';
|
|
43
15
|
import { AggregatorEndpoint } from '@matter/node/endpoints';
|
|
44
16
|
import { BasicInformationServer } from '@matter/node/behaviors/basic-information';
|
|
45
|
-
// Matterbridge
|
|
46
17
|
import { getParameter, getIntParameter, hasParameter } from './utils/commandLine.js';
|
|
47
18
|
import { copyDirectory } from './utils/copyDirectory.js';
|
|
48
19
|
import { createDirectory } from './utils/createDirectory.js';
|
|
@@ -57,27 +28,19 @@ import { bridge } from './matterbridgeDeviceTypes.js';
|
|
|
57
28
|
import { Frontend } from './frontend.js';
|
|
58
29
|
import { addVirtualDevice, addVirtualDevices } from './helpers.js';
|
|
59
30
|
import { BroadcastServer } from './broadcastServer.js';
|
|
60
|
-
/**
|
|
61
|
-
* Represents the Matterbridge application.
|
|
62
|
-
*/
|
|
63
31
|
export class Matterbridge extends EventEmitter {
|
|
64
|
-
/** Matterbridge system information */
|
|
65
32
|
systemInformation = {
|
|
66
|
-
// Network properties
|
|
67
33
|
interfaceName: '',
|
|
68
34
|
macAddress: '',
|
|
69
35
|
ipv4Address: '',
|
|
70
36
|
ipv6Address: '',
|
|
71
|
-
// Node.js properties
|
|
72
37
|
nodeVersion: '',
|
|
73
|
-
// Fixed system properties
|
|
74
38
|
hostname: '',
|
|
75
39
|
user: '',
|
|
76
40
|
osType: '',
|
|
77
41
|
osRelease: '',
|
|
78
42
|
osPlatform: '',
|
|
79
43
|
osArch: '',
|
|
80
|
-
// Cpu and memory properties
|
|
81
44
|
totalMemory: '',
|
|
82
45
|
freeMemory: '',
|
|
83
46
|
systemUptime: '',
|
|
@@ -88,7 +51,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
88
51
|
heapTotal: '',
|
|
89
52
|
heapUsed: '',
|
|
90
53
|
};
|
|
91
|
-
// Matterbridge settings
|
|
92
54
|
homeDirectory = '';
|
|
93
55
|
rootDirectory = '';
|
|
94
56
|
matterbridgeDirectory = '';
|
|
@@ -103,19 +65,12 @@ export class Matterbridge extends EventEmitter {
|
|
|
103
65
|
restartMode = '';
|
|
104
66
|
virtualMode = 'outlet';
|
|
105
67
|
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
|
-
/** Matterbridge logger level */
|
|
68
|
+
log = new AnsiLogger({ logName: 'Matterbridge', logTimestampFormat: 4, logLevel: hasParameter('debug') ? "debug" : "info" });
|
|
109
69
|
logLevel = this.log.logLevel;
|
|
110
|
-
/** Whether to log to a file */
|
|
111
70
|
fileLogger = false;
|
|
112
|
-
|
|
113
|
-
matterLog = new AnsiLogger({ logName: 'Matter', logTimestampFormat: 4 /* TimestampFormat.TIME_MILLIS */, logLevel: hasParameter('debug') ? "debug" /* LogLevel.DEBUG */ : "info" /* LogLevel.INFO */ });
|
|
114
|
-
/** Matter logger level */
|
|
71
|
+
matterLog = new AnsiLogger({ logName: 'Matter', logTimestampFormat: 4, logLevel: hasParameter('debug') ? "debug" : "info" });
|
|
115
72
|
matterLogLevel = this.matterLog.logLevel;
|
|
116
|
-
/** Whether to log Matter to a file */
|
|
117
73
|
matterFileLogger = false;
|
|
118
|
-
// Frontend settings
|
|
119
74
|
readOnly = hasParameter('readonly') || hasParameter('shelly');
|
|
120
75
|
shellyBoard = hasParameter('shelly');
|
|
121
76
|
shellySysUpdate = false;
|
|
@@ -123,18 +78,12 @@ export class Matterbridge extends EventEmitter {
|
|
|
123
78
|
restartRequired = false;
|
|
124
79
|
fixedRestartRequired = false;
|
|
125
80
|
updateRequired = false;
|
|
126
|
-
// Managers
|
|
127
81
|
plugins = new PluginManager(this);
|
|
128
82
|
devices = new DeviceManager();
|
|
129
|
-
// Frontend
|
|
130
83
|
frontend = new Frontend(this);
|
|
131
|
-
/** Matterbridge node storage manager created in the directory 'storage' in matterbridgeDirectory */
|
|
132
84
|
nodeStorage;
|
|
133
|
-
/** Matterbridge node context created with name 'matterbridge' */
|
|
134
85
|
nodeContext;
|
|
135
|
-
/** The main instance of the Matterbridge class (singleton) */
|
|
136
86
|
static instance;
|
|
137
|
-
// Instance properties
|
|
138
87
|
shutdown = false;
|
|
139
88
|
failCountLimit = hasParameter('shelly') ? 600 : 120;
|
|
140
89
|
hasCleanupStarted = false;
|
|
@@ -149,32 +98,19 @@ export class Matterbridge extends EventEmitter {
|
|
|
149
98
|
sigtermHandler;
|
|
150
99
|
exceptionHandler;
|
|
151
100
|
rejectionHandler;
|
|
152
|
-
/** Matter environment default */
|
|
153
101
|
environment = Environment.default;
|
|
154
|
-
/** Matter storage service from environment default */
|
|
155
102
|
matterStorageService;
|
|
156
|
-
/** Matter storage manager created with name 'Matterbridge' */
|
|
157
103
|
matterStorageManager;
|
|
158
|
-
/** Matter matterbridge storage context created in the storage manager with name 'persist' */
|
|
159
104
|
matterbridgeContext;
|
|
160
105
|
controllerContext;
|
|
161
|
-
/** Matter mdns interface e.g. 'eth0' or 'wlan0' or 'Wi-Fi' */
|
|
162
106
|
mdnsInterface;
|
|
163
|
-
/** Matter listeningAddressIpv4 address */
|
|
164
107
|
ipv4Address;
|
|
165
|
-
/** Matter listeningAddressIpv6 address */
|
|
166
108
|
ipv6Address;
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
/** Matter commissioning discriminator */
|
|
172
|
-
discriminator; // first server node discriminator
|
|
173
|
-
/** Matter device certification */
|
|
174
|
-
certification; // device certification
|
|
175
|
-
/** Matter server node in bridge mode */
|
|
109
|
+
port;
|
|
110
|
+
passcode;
|
|
111
|
+
discriminator;
|
|
112
|
+
certification;
|
|
176
113
|
serverNode;
|
|
177
|
-
/** Matter aggregator node in bridge mode */
|
|
178
114
|
aggregatorNode;
|
|
179
115
|
aggregatorVendorId = VendorId(getIntParameter('vendorId') ?? 0xfff1);
|
|
180
116
|
aggregatorVendorName = getParameter('vendorName') ?? 'Matterbridge';
|
|
@@ -183,13 +119,10 @@ export class Matterbridge extends EventEmitter {
|
|
|
183
119
|
aggregatorDeviceType = DeviceTypeId(getIntParameter('deviceType') ?? bridge.code);
|
|
184
120
|
aggregatorSerialNumber = getParameter('serialNumber');
|
|
185
121
|
aggregatorUniqueId = getParameter('uniqueId');
|
|
186
|
-
/** Advertising nodes map: time advertising started keyed by storeId */
|
|
187
122
|
advertisingNodes = new Map();
|
|
188
|
-
/** Broadcast server */
|
|
189
123
|
server;
|
|
190
124
|
debug = hasParameter('debug') || hasParameter('verbose');
|
|
191
125
|
verbose = hasParameter('verbose');
|
|
192
|
-
/** We load asyncronously so is private */
|
|
193
126
|
constructor() {
|
|
194
127
|
super();
|
|
195
128
|
this.log.logNameColor = '\x1b[38;5;115m';
|
|
@@ -240,19 +173,8 @@ export class Matterbridge extends EventEmitter {
|
|
|
240
173
|
}
|
|
241
174
|
}
|
|
242
175
|
}
|
|
243
|
-
//* ************************************************************************************************************************************ */
|
|
244
|
-
// loadInstance() and cleanup() methods */
|
|
245
|
-
//* ************************************************************************************************************************************ */
|
|
246
|
-
/**
|
|
247
|
-
* Loads an instance of the Matterbridge class.
|
|
248
|
-
* If an instance already exists, return that instance.
|
|
249
|
-
*
|
|
250
|
-
* @param {boolean} initialize - Whether to initialize the Matterbridge instance after loading. Defaults to false.
|
|
251
|
-
* @returns {Matterbridge} A promise that resolves to the Matterbridge instance.
|
|
252
|
-
*/
|
|
253
176
|
static async loadInstance(initialize = false) {
|
|
254
177
|
if (!Matterbridge.instance) {
|
|
255
|
-
// eslint-disable-next-line no-console
|
|
256
178
|
if (hasParameter('debug'))
|
|
257
179
|
console.log(GREEN + 'Creating a new instance of Matterbridge.', initialize ? 'Initializing...' : 'Not initializing...', rs);
|
|
258
180
|
Matterbridge.instance = new Matterbridge();
|
|
@@ -261,84 +183,56 @@ export class Matterbridge extends EventEmitter {
|
|
|
261
183
|
}
|
|
262
184
|
return Matterbridge.instance;
|
|
263
185
|
}
|
|
264
|
-
/**
|
|
265
|
-
* Initializes the Matterbridge application.
|
|
266
|
-
*
|
|
267
|
-
* @remarks
|
|
268
|
-
* This method performs the necessary setup and initialization steps for the Matterbridge application.
|
|
269
|
-
* It displays the help information if the 'help' parameter is provided, sets up the logger, checks the
|
|
270
|
-
* node version, registers signal handlers, initializes storage, and parses the command line.
|
|
271
|
-
*
|
|
272
|
-
* @returns {Promise<void>} A Promise that resolves when the initialization is complete.
|
|
273
|
-
*/
|
|
274
186
|
async initialize() {
|
|
275
|
-
// for (let i = 1; i <= 255; i++) console.log(`\x1b[38;5;${i}mColor: ${i}`);
|
|
276
|
-
// Emit the initialize_started event
|
|
277
187
|
this.emit('initialize_started');
|
|
278
|
-
// Set the restart mode
|
|
279
188
|
if (hasParameter('service'))
|
|
280
189
|
this.restartMode = 'service';
|
|
281
190
|
if (hasParameter('docker'))
|
|
282
191
|
this.restartMode = 'docker';
|
|
283
|
-
// Set the matterbridge home directory
|
|
284
192
|
this.homeDirectory = getParameter('homedir') ?? os.homedir();
|
|
285
193
|
await createDirectory(this.homeDirectory, 'Matterbridge Home Directory', this.log);
|
|
286
|
-
// Set the matterbridge directory
|
|
287
194
|
this.matterbridgeDirectory = this.profile ? path.join(this.homeDirectory, '.matterbridge', 'profiles', this.profile) : path.join(this.homeDirectory, '.matterbridge');
|
|
288
195
|
await createDirectory(this.matterbridgeDirectory, 'Matterbridge Directory', this.log);
|
|
289
196
|
await createDirectory(path.join(this.matterbridgeDirectory, 'certs'), 'Matterbridge Frontend Certificate Directory', this.log);
|
|
290
197
|
await createDirectory(path.join(this.matterbridgeDirectory, 'uploads'), 'Matterbridge Frontend Uploads Directory', this.log);
|
|
291
|
-
// Set the matterbridge plugin directory
|
|
292
198
|
this.matterbridgePluginDirectory = this.profile ? path.join(this.homeDirectory, 'Matterbridge', 'profiles', this.profile) : path.join(this.homeDirectory, 'Matterbridge');
|
|
293
199
|
await createDirectory(this.matterbridgePluginDirectory, 'Matterbridge Plugin Directory', this.log);
|
|
294
|
-
// Set the matterbridge cert directory
|
|
295
200
|
this.matterbridgeCertDirectory = this.profile ? path.join(this.homeDirectory, '.mattercert', 'profiles', this.profile) : path.join(this.homeDirectory, '.mattercert');
|
|
296
201
|
await createDirectory(this.matterbridgeCertDirectory, 'Matterbridge Matter Certificate Directory', this.log);
|
|
297
|
-
// Set the matterbridge root directory
|
|
298
202
|
const { fileURLToPath } = await import('node:url');
|
|
299
203
|
const currentFileDirectory = path.dirname(fileURLToPath(import.meta.url));
|
|
300
|
-
this.rootDirectory = path.resolve(currentFileDirectory, '../');
|
|
301
|
-
// Setup the matter environment with default values
|
|
204
|
+
this.rootDirectory = path.resolve(currentFileDirectory, '../');
|
|
302
205
|
this.environment.vars.set('log.level', MatterLogLevel.INFO);
|
|
303
206
|
this.environment.vars.set('log.format', MatterLogFormat.ANSI);
|
|
304
207
|
this.environment.vars.set('path.root', path.join(this.matterbridgeDirectory, MATTER_STORAGE_NAME));
|
|
305
208
|
this.environment.vars.set('runtime.signals', false);
|
|
306
209
|
this.environment.vars.set('runtime.exitcode', false);
|
|
307
|
-
// Register process handlers
|
|
308
210
|
this.registerProcessHandlers();
|
|
309
|
-
// Initialize nodeStorage and nodeContext
|
|
310
211
|
try {
|
|
311
212
|
this.log.debug(`Creating node storage manager: ${CYAN}${NODE_STORAGE_DIR}${db}`);
|
|
312
213
|
this.nodeStorage = new NodeStorageManager({ dir: path.join(this.matterbridgeDirectory, NODE_STORAGE_DIR), writeQueue: false, expiredInterval: undefined, logging: false });
|
|
313
214
|
this.log.debug('Creating node storage context for matterbridge');
|
|
314
215
|
this.nodeContext = await this.nodeStorage.createStorage('matterbridge');
|
|
315
|
-
// TODO: Remove this code when node-persist-manager is updated
|
|
316
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
317
216
|
const keys = (await this.nodeStorage?.storage.keys());
|
|
318
217
|
for (const key of keys) {
|
|
319
218
|
this.log.debug(`Checking node storage manager key: ${CYAN}${key}${db}`);
|
|
320
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
321
219
|
await this.nodeStorage?.storage.get(key);
|
|
322
220
|
}
|
|
323
221
|
const storages = await this.nodeStorage.getStorageNames();
|
|
324
222
|
for (const storage of storages) {
|
|
325
223
|
this.log.debug(`Checking storage: ${CYAN}${storage}${db}`);
|
|
326
224
|
const nodeContext = await this.nodeStorage?.createStorage(storage);
|
|
327
|
-
// TODO: Remove this code when node-persist-manager is updated
|
|
328
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
329
225
|
const keys = (await nodeContext?.storage.keys());
|
|
330
226
|
keys.forEach(async (key) => {
|
|
331
227
|
this.log.debug(`Checking key: ${CYAN}${storage}:${key}${db}`);
|
|
332
228
|
await nodeContext?.get(key);
|
|
333
229
|
});
|
|
334
230
|
}
|
|
335
|
-
// Creating a backup of the node storage since it is not corrupted
|
|
336
231
|
this.log.debug('Creating node storage backup...');
|
|
337
232
|
await copyDirectory(path.join(this.matterbridgeDirectory, NODE_STORAGE_DIR), path.join(this.matterbridgeDirectory, NODE_STORAGE_DIR + '.backup'));
|
|
338
233
|
this.log.debug('Created node storage backup');
|
|
339
234
|
}
|
|
340
235
|
catch (error) {
|
|
341
|
-
// Restoring the backup of the node storage since it is corrupted
|
|
342
236
|
this.log.error(`Error creating node storage manager and context: ${error instanceof Error ? error.message : error}`);
|
|
343
237
|
if (hasParameter('norestore')) {
|
|
344
238
|
this.log.fatal(`The matterbridge storage is corrupted. Found -norestore parameter: exiting...`);
|
|
@@ -352,19 +246,14 @@ export class Matterbridge extends EventEmitter {
|
|
|
352
246
|
if (!this.nodeStorage || !this.nodeContext) {
|
|
353
247
|
throw new Error('Fatal error creating node storage manager and context for matterbridge');
|
|
354
248
|
}
|
|
355
|
-
// Set the first port to use for the commissioning server (will be incremented in childbridge mode)
|
|
356
249
|
this.port = getIntParameter('port') ?? (await this.nodeContext.get('matterport', 5540)) ?? 5540;
|
|
357
|
-
// Set the first passcode to use for the commissioning server (will be incremented in childbridge mode)
|
|
358
250
|
this.passcode = getIntParameter('passcode') ?? (await this.nodeContext.get('matterpasscode')) ?? PaseClient.generateRandomPasscode(this.environment.get(Crypto));
|
|
359
|
-
// Set the first discriminator to use for the commissioning server (will be incremented in childbridge mode)
|
|
360
251
|
this.discriminator = getIntParameter('discriminator') ?? (await this.nodeContext.get('matterdiscriminator')) ?? PaseClient.generateRandomDiscriminator(this.environment.get(Crypto));
|
|
361
|
-
// Certificate management
|
|
362
252
|
const pairingFilePath = path.join(this.matterbridgeCertDirectory, 'pairing.json');
|
|
363
253
|
try {
|
|
364
254
|
await fs.promises.access(pairingFilePath, fs.constants.R_OK);
|
|
365
255
|
const pairingFileContent = await fs.promises.readFile(pairingFilePath, 'utf8');
|
|
366
256
|
const pairingFileJson = JSON.parse(pairingFileContent);
|
|
367
|
-
// Set the vendorId, vendorName, productId, productName, deviceType, serialNumber, uniqueId if they are present in the pairing file
|
|
368
257
|
if (isValidNumber(pairingFileJson.vendorId)) {
|
|
369
258
|
this.aggregatorVendorId = VendorId(pairingFileJson.vendorId);
|
|
370
259
|
this.log.info(`Pairing file ${CYAN}${pairingFilePath}${nf} found. Using vendorId ${CYAN}${this.aggregatorVendorId}${nf} from pairing file.`);
|
|
@@ -393,13 +282,11 @@ export class Matterbridge extends EventEmitter {
|
|
|
393
282
|
this.aggregatorUniqueId = pairingFileJson.uniqueId;
|
|
394
283
|
this.log.info(`Pairing file ${CYAN}${pairingFilePath}${nf} found. Using uniqueId ${CYAN}${this.aggregatorUniqueId}${nf} from pairing file.`);
|
|
395
284
|
}
|
|
396
|
-
// Override the passcode and discriminator if they are present in the pairing file
|
|
397
285
|
if (isValidNumber(pairingFileJson.passcode) && isValidNumber(pairingFileJson.discriminator)) {
|
|
398
286
|
this.passcode = pairingFileJson.passcode;
|
|
399
287
|
this.discriminator = pairingFileJson.discriminator;
|
|
400
288
|
this.log.info(`Pairing file ${CYAN}${pairingFilePath}${nf} found. Using passcode ${CYAN}${this.passcode}${nf} and discriminator ${CYAN}${this.discriminator}${nf} from pairing file.`);
|
|
401
289
|
}
|
|
402
|
-
// Set the certification for matter.js if it is present in the pairing file
|
|
403
290
|
if (pairingFileJson.privateKey && pairingFileJson.certificate && pairingFileJson.intermediateCertificate && pairingFileJson.declaration) {
|
|
404
291
|
const { hexToBuffer } = await import('./utils/hex.js');
|
|
405
292
|
this.certification = {
|
|
@@ -414,44 +301,41 @@ export class Matterbridge extends EventEmitter {
|
|
|
414
301
|
catch (error) {
|
|
415
302
|
this.log.debug(`Pairing file ${CYAN}${pairingFilePath}${db} not found: ${error instanceof Error ? error.message : error}`);
|
|
416
303
|
}
|
|
417
|
-
// Store the passcode, discriminator and port in the node context
|
|
418
304
|
await this.nodeContext.set('matterport', this.port);
|
|
419
305
|
await this.nodeContext.set('matterpasscode', this.passcode);
|
|
420
306
|
await this.nodeContext.set('matterdiscriminator', this.discriminator);
|
|
421
307
|
this.log.debug(`Initializing server node for Matterbridge on port ${this.port} with passcode ${this.passcode} and discriminator ${this.discriminator}`);
|
|
422
|
-
// Set matterbridge logger level (context: matterbridgeLogLevel)
|
|
423
308
|
if (hasParameter('logger')) {
|
|
424
309
|
const level = getParameter('logger');
|
|
425
310
|
if (level === 'debug') {
|
|
426
|
-
this.log.logLevel = "debug"
|
|
311
|
+
this.log.logLevel = "debug";
|
|
427
312
|
}
|
|
428
313
|
else if (level === 'info') {
|
|
429
|
-
this.log.logLevel = "info"
|
|
314
|
+
this.log.logLevel = "info";
|
|
430
315
|
}
|
|
431
316
|
else if (level === 'notice') {
|
|
432
|
-
this.log.logLevel = "notice"
|
|
317
|
+
this.log.logLevel = "notice";
|
|
433
318
|
}
|
|
434
319
|
else if (level === 'warn') {
|
|
435
|
-
this.log.logLevel = "warn"
|
|
320
|
+
this.log.logLevel = "warn";
|
|
436
321
|
}
|
|
437
322
|
else if (level === 'error') {
|
|
438
|
-
this.log.logLevel = "error"
|
|
323
|
+
this.log.logLevel = "error";
|
|
439
324
|
}
|
|
440
325
|
else if (level === 'fatal') {
|
|
441
|
-
this.log.logLevel = "fatal"
|
|
326
|
+
this.log.logLevel = "fatal";
|
|
442
327
|
}
|
|
443
328
|
else {
|
|
444
329
|
this.log.warn(`Invalid matterbridge logger level: ${level}. Using default level "info".`);
|
|
445
|
-
this.log.logLevel = "info"
|
|
330
|
+
this.log.logLevel = "info";
|
|
446
331
|
}
|
|
447
332
|
}
|
|
448
333
|
else {
|
|
449
|
-
this.log.logLevel = await this.nodeContext.get('matterbridgeLogLevel', this.shellyBoard ? "notice"
|
|
334
|
+
this.log.logLevel = await this.nodeContext.get('matterbridgeLogLevel', this.shellyBoard ? "notice" : "info");
|
|
450
335
|
}
|
|
451
336
|
this.logLevel = this.log.logLevel;
|
|
452
337
|
this.frontend.logLevel = this.log.logLevel;
|
|
453
338
|
MatterbridgeEndpoint.logLevel = this.log.logLevel;
|
|
454
|
-
// Create the file logger for matterbridge (context: matterbridgeFileLog)
|
|
455
339
|
if (hasParameter('filelogger') || (await this.nodeContext.get('matterbridgeFileLog', false))) {
|
|
456
340
|
AnsiLogger.setGlobalLogfile(path.join(this.matterbridgeDirectory, MATTERBRIDGE_LOGGER_FILE), this.log.logLevel, true);
|
|
457
341
|
this.fileLogger = true;
|
|
@@ -460,7 +344,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
460
344
|
this.log.debug(`Matterbridge logLevel: ${this.log.logLevel} fileLoger: ${this.fileLogger}.`);
|
|
461
345
|
if (this.profile !== undefined)
|
|
462
346
|
this.log.debug(`Matterbridge profile: ${this.profile}.`);
|
|
463
|
-
// Set matter.js logger level, format and logger (context: matterLogLevel)
|
|
464
347
|
if (hasParameter('matterlogger')) {
|
|
465
348
|
const level = getParameter('matterlogger');
|
|
466
349
|
if (level === 'debug') {
|
|
@@ -491,13 +374,11 @@ export class Matterbridge extends EventEmitter {
|
|
|
491
374
|
}
|
|
492
375
|
Logger.format = MatterLogFormat.ANSI;
|
|
493
376
|
this.matterLogLevel = MatterLogLevel.names[Logger.level];
|
|
494
|
-
// Create the logger for matter.js with file logging (context: matterFileLog)
|
|
495
377
|
if (hasParameter('matterfilelogger') || (await this.nodeContext.get('matterFileLog', false))) {
|
|
496
378
|
this.matterFileLogger = true;
|
|
497
379
|
}
|
|
498
380
|
Logger.destinations.default.write = this.createDestinationMatterLogger(this.matterFileLogger);
|
|
499
381
|
this.log.debug(`Matter logLevel: ${this.matterLogLevel} fileLoger: ${this.matterFileLogger}.`);
|
|
500
|
-
// Log network interfaces
|
|
501
382
|
const networkInterfaces = os.networkInterfaces();
|
|
502
383
|
const availableAddresses = Object.entries(networkInterfaces);
|
|
503
384
|
const availableInterfaceNames = Object.keys(networkInterfaces);
|
|
@@ -510,7 +391,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
510
391
|
});
|
|
511
392
|
}
|
|
512
393
|
}
|
|
513
|
-
// Set the interface to use for matter server node mdnsInterface
|
|
514
394
|
if (hasParameter('mdnsinterface')) {
|
|
515
395
|
this.mdnsInterface = getParameter('mdnsinterface');
|
|
516
396
|
}
|
|
@@ -519,7 +399,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
519
399
|
if (this.mdnsInterface === '')
|
|
520
400
|
this.mdnsInterface = undefined;
|
|
521
401
|
}
|
|
522
|
-
// Validate mdnsInterface
|
|
523
402
|
if (this.mdnsInterface) {
|
|
524
403
|
if (!availableInterfaceNames.includes(this.mdnsInterface)) {
|
|
525
404
|
this.log.error(`Invalid mdnsinterface: ${this.mdnsInterface}. Available interfaces are: ${availableInterfaceNames.join(', ')}. Using all available interfaces.`);
|
|
@@ -532,7 +411,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
532
411
|
}
|
|
533
412
|
if (this.mdnsInterface)
|
|
534
413
|
this.environment.vars.set('mdns.networkInterface', this.mdnsInterface);
|
|
535
|
-
// Set the listeningAddressIpv4 for the matter commissioning server
|
|
536
414
|
if (hasParameter('ipv4address')) {
|
|
537
415
|
this.ipv4Address = getParameter('ipv4address');
|
|
538
416
|
}
|
|
@@ -541,7 +419,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
541
419
|
if (this.ipv4Address === '')
|
|
542
420
|
this.ipv4Address = undefined;
|
|
543
421
|
}
|
|
544
|
-
// Validate ipv4address
|
|
545
422
|
if (this.ipv4Address) {
|
|
546
423
|
let isValid = false;
|
|
547
424
|
for (const [ifaceName, ifaces] of availableAddresses) {
|
|
@@ -557,7 +434,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
557
434
|
await this.nodeContext.remove('matteripv4address');
|
|
558
435
|
}
|
|
559
436
|
}
|
|
560
|
-
// Set the listeningAddressIpv6 for the matter commissioning server
|
|
561
437
|
if (hasParameter('ipv6address')) {
|
|
562
438
|
this.ipv6Address = getParameter('ipv6address');
|
|
563
439
|
}
|
|
@@ -566,7 +442,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
566
442
|
if (this.ipv6Address === '')
|
|
567
443
|
this.ipv6Address = undefined;
|
|
568
444
|
}
|
|
569
|
-
// Validate ipv6address
|
|
570
445
|
if (this.ipv6Address) {
|
|
571
446
|
let isValid = false;
|
|
572
447
|
for (const [ifaceName, ifaces] of availableAddresses) {
|
|
@@ -575,7 +450,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
575
450
|
isValid = true;
|
|
576
451
|
break;
|
|
577
452
|
}
|
|
578
|
-
/* istanbul ignore next */
|
|
579
453
|
if (ifaces && ifaces.find((iface) => iface.scopeid && iface.scopeid > 0 && iface.address + '%' + (process.platform === 'win32' ? iface.scopeid : ifaceName) === this.ipv6Address)) {
|
|
580
454
|
this.log.info(`Using ipv6address ${CYAN}${this.ipv6Address}${nf} on interface ${CYAN}${ifaceName}${nf} for the Matter server node.`);
|
|
581
455
|
isValid = true;
|
|
@@ -588,7 +462,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
588
462
|
await this.nodeContext.remove('matteripv6address');
|
|
589
463
|
}
|
|
590
464
|
}
|
|
591
|
-
// Initialize the virtual mode
|
|
592
465
|
if (hasParameter('novirtual')) {
|
|
593
466
|
this.virtualMode = 'disabled';
|
|
594
467
|
await this.nodeContext.set('virtualmode', 'disabled');
|
|
@@ -597,15 +470,10 @@ export class Matterbridge extends EventEmitter {
|
|
|
597
470
|
this.virtualMode = (await this.nodeContext.get('virtualmode', 'outlet'));
|
|
598
471
|
}
|
|
599
472
|
this.log.debug(`Virtual mode ${this.virtualMode}.`);
|
|
600
|
-
// Initialize PluginManager
|
|
601
473
|
this.plugins.logLevel = this.log.logLevel;
|
|
602
474
|
await this.plugins.loadFromStorage();
|
|
603
|
-
// Initialize DeviceManager
|
|
604
475
|
this.devices.logLevel = this.log.logLevel;
|
|
605
|
-
// Get the plugins from node storage and create the plugins node storage contexts
|
|
606
476
|
for (const plugin of this.plugins) {
|
|
607
|
-
// Try to reinstall the plugin from npm (for Docker pull and external plugins)
|
|
608
|
-
// We don't do this when the add and other shutdown parameters are set because we shut down the process after adding the plugin
|
|
609
477
|
if (!fs.existsSync(plugin.path) && !hasParameter('add') && !hasParameter('remove') && !hasParameter('enable') && !hasParameter('disable') && !hasParameter('reset') && !hasParameter('factoryreset')) {
|
|
610
478
|
this.log.info(`Error parsing plugin ${plg}${plugin.name}${nf}. Trying to reinstall it from npm...`);
|
|
611
479
|
const { spawnCommand } = await import('./utils/spawn.js');
|
|
@@ -635,7 +503,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
635
503
|
await plugin.nodeContext.set('description', plugin.description);
|
|
636
504
|
await plugin.nodeContext.set('author', plugin.author);
|
|
637
505
|
}
|
|
638
|
-
// Log system info and create .matterbridge directory
|
|
639
506
|
await this.logNodeAndSystemInfo();
|
|
640
507
|
this.log.notice(`Matterbridge version ${this.matterbridgeVersion} ` +
|
|
641
508
|
`${hasParameter('bridge') || (!hasParameter('childbridge') && (await this.nodeContext?.get('bridgeMode', '')) === 'bridge') ? 'mode bridge ' : ''}` +
|
|
@@ -643,7 +510,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
643
510
|
`${hasParameter('controller') ? 'mode controller ' : ''}` +
|
|
644
511
|
`${this.restartMode !== '' ? 'restart mode ' + this.restartMode + ' ' : ''}` +
|
|
645
512
|
`running on ${this.systemInformation.osType} (v.${this.systemInformation.osRelease}) platform ${this.systemInformation.osPlatform} arch ${this.systemInformation.osArch}`);
|
|
646
|
-
// Check node version and throw error
|
|
647
513
|
const minNodeVersion = 20;
|
|
648
514
|
const nodeVersion = process.versions.node;
|
|
649
515
|
const versionMajor = parseInt(nodeVersion.split('.')[0]);
|
|
@@ -651,18 +517,10 @@ export class Matterbridge extends EventEmitter {
|
|
|
651
517
|
this.log.error(`Node version ${versionMajor} is not supported. Please upgrade to ${minNodeVersion} or above.`);
|
|
652
518
|
throw new Error(`Node version ${versionMajor} is not supported. Please upgrade to ${minNodeVersion} or above.`);
|
|
653
519
|
}
|
|
654
|
-
// Parse command line
|
|
655
520
|
await this.parseCommandLine();
|
|
656
|
-
// Emit the initialize_completed event
|
|
657
521
|
this.emit('initialize_completed');
|
|
658
522
|
this.initialized = true;
|
|
659
523
|
}
|
|
660
|
-
/**
|
|
661
|
-
* Parses the command line arguments and performs the corresponding actions.
|
|
662
|
-
*
|
|
663
|
-
* @private
|
|
664
|
-
* @returns {Promise<void>} A promise that resolves when the command line arguments have been processed, or the process exits.
|
|
665
|
-
*/
|
|
666
524
|
async parseCommandLine() {
|
|
667
525
|
if (hasParameter('list')) {
|
|
668
526
|
this.log.info(`│ Registered plugins (${this.plugins.length})`);
|
|
@@ -678,19 +536,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
678
536
|
}
|
|
679
537
|
index++;
|
|
680
538
|
}
|
|
681
|
-
/*
|
|
682
|
-
const serializedRegisteredDevices = await this.nodeContext?.get<SerializedMatterbridgeEndpoint[]>('devices', []);
|
|
683
|
-
this.log.info(`│ Registered devices (${serializedRegisteredDevices?.length})`);
|
|
684
|
-
serializedRegisteredDevices?.forEach((device, index) => {
|
|
685
|
-
if (index !== serializedRegisteredDevices.length - 1) {
|
|
686
|
-
this.log.info(`├─┬─ plugin ${plg}${device.pluginName}${nf} device: ${dev}${device.deviceName}${nf} uniqueId: ${YELLOW}${device.uniqueId}${nf}`);
|
|
687
|
-
this.log.info(`│ └─ endpoint ${RED}${device.endpoint}${nf} ${typ}${device.endpointName}${nf} ${debugStringify(device.clusterServersId)}`);
|
|
688
|
-
} else {
|
|
689
|
-
this.log.info(`└─┬─ plugin ${plg}${device.pluginName}${nf} device: ${dev}${device.deviceName}${nf} uniqueId: ${YELLOW}${device.uniqueId}${nf}`);
|
|
690
|
-
this.log.info(` └─ endpoint ${RED}${device.endpoint}${nf} ${typ}${device.endpointName}${nf} ${debugStringify(device.clusterServersId)}`);
|
|
691
|
-
}
|
|
692
|
-
});
|
|
693
|
-
*/
|
|
694
539
|
this.shutdown = true;
|
|
695
540
|
return;
|
|
696
541
|
}
|
|
@@ -740,10 +585,8 @@ export class Matterbridge extends EventEmitter {
|
|
|
740
585
|
this.shutdown = true;
|
|
741
586
|
return;
|
|
742
587
|
}
|
|
743
|
-
// Initialize frontend
|
|
744
588
|
if (getIntParameter('frontend') !== 0 || getIntParameter('frontend') === undefined)
|
|
745
589
|
await this.frontend.start(getIntParameter('frontend'));
|
|
746
|
-
// Start the matter storage and create the matterbridge context
|
|
747
590
|
try {
|
|
748
591
|
await this.startMatterStorage();
|
|
749
592
|
if (this.aggregatorSerialNumber && this.aggregatorUniqueId && this.matterStorageService) {
|
|
@@ -757,21 +600,18 @@ export class Matterbridge extends EventEmitter {
|
|
|
757
600
|
this.log.fatal(`Fatal error creating matter storage: ${error instanceof Error ? error.message : error}`);
|
|
758
601
|
throw new Error(`Fatal error creating matter storage: ${error instanceof Error ? error.message : error}`);
|
|
759
602
|
}
|
|
760
|
-
// Clear the matterbridge context if the reset parameter is set (bridge mode)
|
|
761
603
|
if (hasParameter('reset') && getParameter('reset') === undefined) {
|
|
762
604
|
this.initialized = true;
|
|
763
605
|
await this.shutdownProcessAndReset();
|
|
764
606
|
this.shutdown = true;
|
|
765
607
|
return;
|
|
766
608
|
}
|
|
767
|
-
// Clear matterbridge plugin context if the reset parameter is set (childbridge mode)
|
|
768
609
|
if (hasParameter('reset') && getParameter('reset') !== undefined) {
|
|
769
610
|
this.log.debug(`Reset plugin ${getParameter('reset')}`);
|
|
770
611
|
const plugin = this.plugins.get(getParameter('reset'));
|
|
771
612
|
if (plugin) {
|
|
772
613
|
const matterStorageManager = await this.matterStorageService?.open(plugin.name);
|
|
773
614
|
if (!matterStorageManager) {
|
|
774
|
-
/* istanbul ignore next */
|
|
775
615
|
this.log.error(`Plugin ${plg}${plugin.name}${er} storageManager not found`);
|
|
776
616
|
}
|
|
777
617
|
else {
|
|
@@ -790,42 +630,35 @@ export class Matterbridge extends EventEmitter {
|
|
|
790
630
|
this.shutdown = true;
|
|
791
631
|
return;
|
|
792
632
|
}
|
|
793
|
-
// Check in 5 minutes the latest and dev versions of matterbridge and the plugins
|
|
794
633
|
clearTimeout(this.checkUpdateTimeout);
|
|
795
634
|
this.checkUpdateTimeout = setTimeout(async () => {
|
|
796
635
|
const { checkUpdates } = await import('./update.js');
|
|
797
636
|
checkUpdates(this);
|
|
798
637
|
}, 300 * 1000).unref();
|
|
799
|
-
// Check each 12 hours the latest and dev versions of matterbridge and the plugins
|
|
800
638
|
clearInterval(this.checkUpdateInterval);
|
|
801
639
|
this.checkUpdateInterval = setInterval(async () => {
|
|
802
640
|
const { checkUpdates } = await import('./update.js');
|
|
803
641
|
checkUpdates(this);
|
|
804
642
|
}, 12 * 60 * 60 * 1000).unref();
|
|
805
|
-
// Start the matterbridge in mode test
|
|
806
643
|
if (hasParameter('test')) {
|
|
807
644
|
this.bridgeMode = 'bridge';
|
|
808
645
|
return;
|
|
809
646
|
}
|
|
810
|
-
// Start the matterbridge in mode controller
|
|
811
647
|
if (hasParameter('controller')) {
|
|
812
648
|
this.bridgeMode = 'controller';
|
|
813
649
|
await this.startController();
|
|
814
650
|
return;
|
|
815
651
|
}
|
|
816
|
-
// Check if the bridge mode is set and start matterbridge in bridge mode if not set
|
|
817
652
|
if (!hasParameter('bridge') && !hasParameter('childbridge') && (await this.nodeContext?.get('bridgeMode', '')) === '') {
|
|
818
653
|
this.log.info('Setting default matterbridge start mode to bridge');
|
|
819
654
|
await this.nodeContext?.set('bridgeMode', 'bridge');
|
|
820
655
|
}
|
|
821
|
-
// Start matterbridge in bridge mode
|
|
822
656
|
if (hasParameter('bridge') || (!hasParameter('childbridge') && (await this.nodeContext?.get('bridgeMode', '')) === 'bridge')) {
|
|
823
657
|
this.bridgeMode = 'bridge';
|
|
824
658
|
this.log.debug(`Starting matterbridge in mode ${this.bridgeMode}`);
|
|
825
659
|
await this.startBridge();
|
|
826
660
|
return;
|
|
827
661
|
}
|
|
828
|
-
// Start matterbridge in childbridge mode
|
|
829
662
|
if (hasParameter('childbridge') || (!hasParameter('bridge') && (await this.nodeContext?.get('bridgeMode', '')) === 'childbridge')) {
|
|
830
663
|
this.bridgeMode = 'childbridge';
|
|
831
664
|
this.log.debug(`Starting matterbridge in mode ${this.bridgeMode}`);
|
|
@@ -833,22 +666,10 @@ export class Matterbridge extends EventEmitter {
|
|
|
833
666
|
return;
|
|
834
667
|
}
|
|
835
668
|
}
|
|
836
|
-
/**
|
|
837
|
-
* Asynchronously loads and starts the registered plugins.
|
|
838
|
-
*
|
|
839
|
-
* This method is responsible for initializing and starting all enabled plugins.
|
|
840
|
-
* It ensures that each plugin is properly loaded and started before the bridge starts.
|
|
841
|
-
*
|
|
842
|
-
* @param {boolean} [wait] - If true, the method will wait for all plugins to be fully loaded and started before resolving. Defaults to false.
|
|
843
|
-
* @param {boolean} [start] - If true, the method will start the plugins after loading them. Defaults to true.
|
|
844
|
-
* @returns {Promise<void>} A promise that resolves when all plugins have been loaded and started.
|
|
845
|
-
*/
|
|
846
669
|
async startPlugins(wait = false, start = true) {
|
|
847
|
-
// Check, load and start the plugins
|
|
848
670
|
for (const plugin of this.plugins) {
|
|
849
671
|
plugin.configJson = await this.plugins.loadConfig(plugin);
|
|
850
672
|
plugin.schemaJson = await this.plugins.loadSchema(plugin);
|
|
851
|
-
// Check if the plugin is available
|
|
852
673
|
if (!(await this.plugins.resolve(plugin.path))) {
|
|
853
674
|
this.log.error(`Plugin ${plg}${plugin.name}${er} not found or not validated. Disabling it.`);
|
|
854
675
|
plugin.enabled = false;
|
|
@@ -868,16 +689,10 @@ export class Matterbridge extends EventEmitter {
|
|
|
868
689
|
if (wait)
|
|
869
690
|
await this.plugins.load(plugin, start, 'Matterbridge is starting');
|
|
870
691
|
else
|
|
871
|
-
this.plugins.load(plugin, start, 'Matterbridge is starting');
|
|
692
|
+
this.plugins.load(plugin, start, 'Matterbridge is starting');
|
|
872
693
|
}
|
|
873
694
|
this.frontend.wssSendRefreshRequired('plugins');
|
|
874
695
|
}
|
|
875
|
-
/**
|
|
876
|
-
* Registers the process handlers for uncaughtException, unhandledRejection, SIGINT and SIGTERM.
|
|
877
|
-
* - When an uncaught exception occurs, the exceptionHandler logs the error message and stack trace.
|
|
878
|
-
* - When an unhandled promise rejection occurs, the rejectionHandler logs the reason and stack trace.
|
|
879
|
-
* - When either of SIGINT and SIGTERM signals are received, the cleanup method is called with an appropriate message.
|
|
880
|
-
*/
|
|
881
696
|
registerProcessHandlers() {
|
|
882
697
|
this.log.debug(`Registering uncaughtException and unhandledRejection handlers...`);
|
|
883
698
|
process.removeAllListeners('uncaughtException');
|
|
@@ -904,9 +719,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
904
719
|
};
|
|
905
720
|
process.on('SIGTERM', this.sigtermHandler);
|
|
906
721
|
}
|
|
907
|
-
/**
|
|
908
|
-
* Deregisters the process uncaughtException, unhandledRejection, SIGINT and SIGTERM signal handlers.
|
|
909
|
-
*/
|
|
910
722
|
deregisterProcessHandlers() {
|
|
911
723
|
this.log.debug(`Deregistering uncaughtException and unhandledRejection handlers...`);
|
|
912
724
|
if (this.exceptionHandler)
|
|
@@ -923,18 +735,13 @@ export class Matterbridge extends EventEmitter {
|
|
|
923
735
|
process.off('SIGTERM', this.sigtermHandler);
|
|
924
736
|
this.sigtermHandler = undefined;
|
|
925
737
|
}
|
|
926
|
-
/**
|
|
927
|
-
* Logs the node and system information.
|
|
928
|
-
*/
|
|
929
738
|
async logNodeAndSystemInfo() {
|
|
930
|
-
// IP address information
|
|
931
739
|
const networkInterfaces = os.networkInterfaces();
|
|
932
740
|
this.systemInformation.interfaceName = '';
|
|
933
741
|
this.systemInformation.ipv4Address = '';
|
|
934
742
|
this.systemInformation.ipv6Address = '';
|
|
935
743
|
this.systemInformation.macAddress = '';
|
|
936
744
|
for (const [interfaceName, interfaceDetails] of Object.entries(networkInterfaces)) {
|
|
937
|
-
// this.log.debug(`Checking interface: '${interfaceName}' for '${this.mdnsInterface}'`);
|
|
938
745
|
if (this.mdnsInterface && interfaceName !== this.mdnsInterface)
|
|
939
746
|
continue;
|
|
940
747
|
if (!interfaceDetails) {
|
|
@@ -960,18 +767,16 @@ export class Matterbridge extends EventEmitter {
|
|
|
960
767
|
break;
|
|
961
768
|
}
|
|
962
769
|
}
|
|
963
|
-
// Node information
|
|
964
770
|
this.systemInformation.nodeVersion = process.versions.node;
|
|
965
771
|
const versionMajor = parseInt(this.systemInformation.nodeVersion.split('.')[0]);
|
|
966
772
|
const versionMinor = parseInt(this.systemInformation.nodeVersion.split('.')[1]);
|
|
967
773
|
const versionPatch = parseInt(this.systemInformation.nodeVersion.split('.')[2]);
|
|
968
|
-
// Host system information
|
|
969
774
|
this.systemInformation.hostname = os.hostname();
|
|
970
775
|
this.systemInformation.user = os.userInfo().username;
|
|
971
|
-
this.systemInformation.osType = os.type();
|
|
972
|
-
this.systemInformation.osRelease = os.release();
|
|
973
|
-
this.systemInformation.osPlatform = os.platform();
|
|
974
|
-
this.systemInformation.osArch = os.arch();
|
|
776
|
+
this.systemInformation.osType = os.type();
|
|
777
|
+
this.systemInformation.osRelease = os.release();
|
|
778
|
+
this.systemInformation.osPlatform = os.platform();
|
|
779
|
+
this.systemInformation.osArch = os.arch();
|
|
975
780
|
this.systemInformation.totalMemory = formatBytes(os.totalmem());
|
|
976
781
|
this.systemInformation.freeMemory = formatBytes(os.freemem());
|
|
977
782
|
this.systemInformation.systemUptime = formatUptime(os.uptime());
|
|
@@ -981,7 +786,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
981
786
|
this.systemInformation.rss = formatBytes(process.memoryUsage().rss);
|
|
982
787
|
this.systemInformation.heapTotal = formatBytes(process.memoryUsage().heapTotal);
|
|
983
788
|
this.systemInformation.heapUsed = formatBytes(process.memoryUsage().heapUsed);
|
|
984
|
-
// Log the system information
|
|
985
789
|
this.log.debug('Host System Information:');
|
|
986
790
|
this.log.debug(`- Hostname: ${this.systemInformation.hostname}`);
|
|
987
791
|
this.log.debug(`- User: ${this.systemInformation.user}`);
|
|
@@ -1001,17 +805,14 @@ export class Matterbridge extends EventEmitter {
|
|
|
1001
805
|
this.log.debug(`- RSS: ${this.systemInformation.rss}`);
|
|
1002
806
|
this.log.debug(`- Heap Total: ${this.systemInformation.heapTotal}`);
|
|
1003
807
|
this.log.debug(`- Heap Used: ${this.systemInformation.heapUsed}`);
|
|
1004
|
-
// Log directories
|
|
1005
808
|
this.log.debug(`Root Directory: ${this.rootDirectory}`);
|
|
1006
809
|
this.log.debug(`Home Directory: ${this.homeDirectory}`);
|
|
1007
810
|
this.log.debug(`Matterbridge Directory: ${this.matterbridgeDirectory}`);
|
|
1008
811
|
this.log.debug(`Matterbridge Plugin Directory: ${this.matterbridgePluginDirectory}`);
|
|
1009
812
|
this.log.debug(`Matterbridge Matter Certificate Directory: ${this.matterbridgeCertDirectory}`);
|
|
1010
|
-
// Global node_modules directory
|
|
1011
813
|
if (this.nodeContext)
|
|
1012
814
|
this.globalModulesDirectory = await this.nodeContext.get('globalModulesDirectory', '');
|
|
1013
815
|
if (this.globalModulesDirectory === '') {
|
|
1014
|
-
// First run of Matterbridge so the node storage is empty
|
|
1015
816
|
this.log.debug(`Getting global node_modules directory...`);
|
|
1016
817
|
try {
|
|
1017
818
|
const { getGlobalNodeModules } = await import('./utils/network.js');
|
|
@@ -1024,42 +825,29 @@ export class Matterbridge extends EventEmitter {
|
|
|
1024
825
|
}
|
|
1025
826
|
}
|
|
1026
827
|
else {
|
|
1027
|
-
// The global node_modules directory is already set in the node storage and we check if it is still valid
|
|
1028
828
|
this.log.debug(`Global node_modules Directory: ${this.globalModulesDirectory}`);
|
|
1029
829
|
const { createESMWorker } = await import('./workers.js');
|
|
1030
830
|
createESMWorker('NpmGlobalPrefix', path.join(this.rootDirectory, 'dist/workerGlobalPrefix.js'));
|
|
1031
831
|
}
|
|
1032
|
-
// Matterbridge version
|
|
1033
832
|
this.log.debug(`Reading matterbridge package.json...`);
|
|
1034
833
|
const packageJson = JSON.parse(await fs.promises.readFile(path.join(this.rootDirectory, 'package.json'), 'utf-8'));
|
|
1035
834
|
this.matterbridgeVersion = this.matterbridgeLatestVersion = this.matterbridgeDevVersion = packageJson.version;
|
|
1036
835
|
this.log.debug(`Matterbridge Version: ${this.matterbridgeVersion}`);
|
|
1037
|
-
// Matterbridge latest version (will be set in the checkUpdate function)
|
|
1038
836
|
if (this.nodeContext)
|
|
1039
837
|
this.matterbridgeLatestVersion = await this.nodeContext.get('matterbridgeLatestVersion', this.matterbridgeVersion);
|
|
1040
838
|
this.log.debug(`Matterbridge Latest Version: ${this.matterbridgeLatestVersion}`);
|
|
1041
|
-
// Matterbridge dev version (will be set in the checkUpdate function)
|
|
1042
839
|
if (this.nodeContext)
|
|
1043
840
|
this.matterbridgeDevVersion = await this.nodeContext.get('matterbridgeDevVersion', this.matterbridgeVersion);
|
|
1044
841
|
this.log.debug(`Matterbridge Dev Version: ${this.matterbridgeDevVersion}`);
|
|
1045
|
-
// Frontend version
|
|
1046
842
|
this.log.debug(`Reading frontend package.json...`);
|
|
1047
843
|
const frontendPackageJson = JSON.parse(await fs.promises.readFile(path.join(this.rootDirectory, 'frontend/package.json'), 'utf8'));
|
|
1048
844
|
this.frontendVersion = frontendPackageJson.version;
|
|
1049
845
|
this.log.debug(`Frontend version ${CYAN}${this.frontendVersion}${db}`);
|
|
1050
|
-
// Current working directory
|
|
1051
846
|
const currentDir = process.cwd();
|
|
1052
847
|
this.log.debug(`Current Working Directory: ${currentDir}`);
|
|
1053
|
-
// Command line arguments (excluding 'node' and the script name)
|
|
1054
848
|
const cmdArgs = process.argv.slice(2).join(' ');
|
|
1055
849
|
this.log.debug(`Command Line Arguments: ${cmdArgs}`);
|
|
1056
850
|
}
|
|
1057
|
-
/**
|
|
1058
|
-
* Set the logger logLevel for the Matterbridge classes and call onChangeLoggerLevel() for each plugin.
|
|
1059
|
-
*
|
|
1060
|
-
* @param {LogLevel} logLevel The logger logLevel to set.
|
|
1061
|
-
* @returns {Promise<LogLevel>} A promise that resolves when the logLevel has been set.
|
|
1062
|
-
*/
|
|
1063
851
|
async setLogLevel(logLevel) {
|
|
1064
852
|
this.logLevel = logLevel;
|
|
1065
853
|
this.log.logLevel = logLevel;
|
|
@@ -1070,87 +858,58 @@ export class Matterbridge extends EventEmitter {
|
|
|
1070
858
|
for (const plugin of this.plugins) {
|
|
1071
859
|
if (!plugin.platform || !plugin.platform.log || !plugin.platform.config)
|
|
1072
860
|
continue;
|
|
1073
|
-
plugin.platform.log.logLevel = plugin.platform.config.debug === true ? "debug"
|
|
1074
|
-
await plugin.platform.onChangeLoggerLevel(plugin.platform.config.debug === true ? "debug"
|
|
1075
|
-
}
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
callbackLogLevel = "debug" /* LogLevel.DEBUG */;
|
|
861
|
+
plugin.platform.log.logLevel = plugin.platform.config.debug === true ? "debug" : logLevel;
|
|
862
|
+
await plugin.platform.onChangeLoggerLevel(plugin.platform.config.debug === true ? "debug" : logLevel);
|
|
863
|
+
}
|
|
864
|
+
let callbackLogLevel = "notice";
|
|
865
|
+
if (logLevel === "info" || Logger.level === MatterLogLevel.INFO)
|
|
866
|
+
callbackLogLevel = "info";
|
|
867
|
+
if (logLevel === "debug" || Logger.level === MatterLogLevel.DEBUG)
|
|
868
|
+
callbackLogLevel = "debug";
|
|
1082
869
|
AnsiLogger.setGlobalCallbackLevel(callbackLogLevel);
|
|
1083
870
|
this.log.debug(`WebSocketServer logger global callback set to ${callbackLogLevel}`);
|
|
1084
871
|
return logLevel;
|
|
1085
872
|
}
|
|
1086
|
-
/**
|
|
1087
|
-
* Get the current logger logLevel.
|
|
1088
|
-
*
|
|
1089
|
-
* @returns {LogLevel} The current logger logLevel.
|
|
1090
|
-
*/
|
|
1091
873
|
getLogLevel() {
|
|
1092
874
|
return this.log.logLevel;
|
|
1093
875
|
}
|
|
1094
|
-
/**
|
|
1095
|
-
* Creates a MatterLogger function to show the matter.js log messages in AnsiLogger (for the frontend).
|
|
1096
|
-
* It also logs to file (matter.log) if fileLogger is true.
|
|
1097
|
-
*
|
|
1098
|
-
* @param {boolean} fileLogger - Whether to log to file or not.
|
|
1099
|
-
* @returns {Function} The MatterLogger function. \x1b[35m for violet \x1b[34m is blue
|
|
1100
|
-
*/
|
|
1101
876
|
createDestinationMatterLogger(fileLogger) {
|
|
1102
|
-
this.matterLog.logNameColor = '\x1b[34m';
|
|
877
|
+
this.matterLog.logNameColor = '\x1b[34m';
|
|
1103
878
|
if (fileLogger) {
|
|
1104
879
|
this.matterLog.logFilePath = path.join(this.matterbridgeDirectory, MATTER_LOGGER_FILE);
|
|
1105
880
|
}
|
|
1106
881
|
return (text, message) => {
|
|
1107
|
-
// 2024-08-21 08:55:19.488 DEBUG InteractionMessenger Sending DataReport chunk with 28 attributes and 0 events: 1004 bytes
|
|
1108
882
|
const logger = text.slice(44, 44 + 20).trim();
|
|
1109
883
|
const msg = text.slice(65);
|
|
1110
884
|
this.matterLog.logName = logger;
|
|
1111
885
|
switch (message.level) {
|
|
1112
886
|
case MatterLogLevel.DEBUG:
|
|
1113
|
-
this.matterLog.log("debug"
|
|
887
|
+
this.matterLog.log("debug", msg);
|
|
1114
888
|
break;
|
|
1115
889
|
case MatterLogLevel.INFO:
|
|
1116
|
-
this.matterLog.log("info"
|
|
890
|
+
this.matterLog.log("info", msg);
|
|
1117
891
|
break;
|
|
1118
892
|
case MatterLogLevel.NOTICE:
|
|
1119
|
-
this.matterLog.log("notice"
|
|
893
|
+
this.matterLog.log("notice", msg);
|
|
1120
894
|
break;
|
|
1121
895
|
case MatterLogLevel.WARN:
|
|
1122
|
-
this.matterLog.log("warn"
|
|
896
|
+
this.matterLog.log("warn", msg);
|
|
1123
897
|
break;
|
|
1124
898
|
case MatterLogLevel.ERROR:
|
|
1125
|
-
this.matterLog.log("error"
|
|
899
|
+
this.matterLog.log("error", msg);
|
|
1126
900
|
break;
|
|
1127
901
|
case MatterLogLevel.FATAL:
|
|
1128
|
-
this.matterLog.log("fatal"
|
|
902
|
+
this.matterLog.log("fatal", msg);
|
|
1129
903
|
break;
|
|
1130
904
|
}
|
|
1131
905
|
};
|
|
1132
906
|
}
|
|
1133
|
-
/**
|
|
1134
|
-
* Restarts the process by exiting the current instance and loading a new instance (/api/restart).
|
|
1135
|
-
*
|
|
1136
|
-
* @returns {Promise<void>} A promise that resolves when the restart is completed.
|
|
1137
|
-
*/
|
|
1138
907
|
async restartProcess() {
|
|
1139
908
|
await this.cleanup('restarting...', true);
|
|
1140
909
|
}
|
|
1141
|
-
/**
|
|
1142
|
-
* Shut down the process (/api/shutdown).
|
|
1143
|
-
*
|
|
1144
|
-
* @returns {Promise<void>} A promise that resolves when the shutdown is completed.
|
|
1145
|
-
*/
|
|
1146
910
|
async shutdownProcess() {
|
|
1147
911
|
await this.cleanup('shutting down...', false);
|
|
1148
912
|
}
|
|
1149
|
-
/**
|
|
1150
|
-
* Update matterbridge and shut down the process (virtual device 'Update Matterbridge').
|
|
1151
|
-
*
|
|
1152
|
-
* @returns {Promise<void>} A promise that resolves when the update is completed.
|
|
1153
|
-
*/
|
|
1154
913
|
async updateProcess() {
|
|
1155
914
|
this.log.info('Updating matterbridge...');
|
|
1156
915
|
const { spawnCommand } = await import('./utils/spawn.js');
|
|
@@ -1163,13 +922,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1163
922
|
this.frontend.wssSendRestartRequired();
|
|
1164
923
|
await this.cleanup('updating...', false);
|
|
1165
924
|
}
|
|
1166
|
-
/**
|
|
1167
|
-
* Unregister all devices and shut down the process (/api/unregister).
|
|
1168
|
-
*
|
|
1169
|
-
* @param {number} [timeout] - The timeout duration to wait for the message exchange to complete in milliseconds. Default is 1000.
|
|
1170
|
-
*
|
|
1171
|
-
* @returns {Promise<void>} A promise that resolves when the cleanup is completed.
|
|
1172
|
-
*/
|
|
1173
925
|
async unregisterAndShutdownProcess(timeout = 1000) {
|
|
1174
926
|
this.log.info('Unregistering all devices and shutting down...');
|
|
1175
927
|
for (const plugin of this.plugins.array()) {
|
|
@@ -1181,71 +933,46 @@ export class Matterbridge extends EventEmitter {
|
|
|
1181
933
|
await this.removeAllBridgedEndpoints(plugin.name, 100);
|
|
1182
934
|
}
|
|
1183
935
|
this.log.debug('Waiting for the MessageExchange to finish...');
|
|
1184
|
-
await wait(timeout);
|
|
936
|
+
await wait(timeout);
|
|
1185
937
|
this.log.debug('Cleaning up and shutting down...');
|
|
1186
938
|
await this.cleanup('unregistered all devices and shutting down...', false, timeout);
|
|
1187
939
|
}
|
|
1188
|
-
/**
|
|
1189
|
-
* Reset commissioning and shut down the process (/api/reset).
|
|
1190
|
-
*
|
|
1191
|
-
* @returns {Promise<void>} A promise that resolves when the cleanup is completed.
|
|
1192
|
-
*/
|
|
1193
940
|
async shutdownProcessAndReset() {
|
|
1194
941
|
await this.cleanup('shutting down with reset...', false);
|
|
1195
942
|
}
|
|
1196
|
-
/**
|
|
1197
|
-
* Factory reset and shut down the process (/api/factory-reset).
|
|
1198
|
-
*
|
|
1199
|
-
* @returns {Promise<void>} A promise that resolves when the cleanup is completed.
|
|
1200
|
-
*/
|
|
1201
943
|
async shutdownProcessAndFactoryReset() {
|
|
1202
944
|
await this.cleanup('shutting down with factory reset...', false);
|
|
1203
945
|
}
|
|
1204
|
-
/**
|
|
1205
|
-
* Cleans up the Matterbridge instance.
|
|
1206
|
-
*
|
|
1207
|
-
* @param {string} message - The cleanup message.
|
|
1208
|
-
* @param {boolean} [restart] - Indicates whether to restart the instance after cleanup. Default is `false`.
|
|
1209
|
-
* @param {number} [pause] - The pause in ms to wait for the message exchange to complete in milliseconds. Default is 1000.
|
|
1210
|
-
*
|
|
1211
|
-
* @returns {Promise<void>} A promise that resolves when the cleanup is completed.
|
|
1212
|
-
*/
|
|
1213
946
|
async cleanup(message, restart = false, pause = 1000) {
|
|
1214
947
|
if (this.initialized && !this.hasCleanupStarted) {
|
|
1215
948
|
this.emit('cleanup_started');
|
|
1216
949
|
this.hasCleanupStarted = true;
|
|
1217
950
|
this.log.info(message);
|
|
1218
|
-
// Clear the start matter interval
|
|
1219
951
|
if (this.startMatterInterval) {
|
|
1220
952
|
clearInterval(this.startMatterInterval);
|
|
1221
953
|
this.startMatterInterval = undefined;
|
|
1222
954
|
this.log.debug('Start matter interval cleared');
|
|
1223
955
|
}
|
|
1224
|
-
// Clear the check update timeout
|
|
1225
956
|
if (this.checkUpdateTimeout) {
|
|
1226
957
|
clearTimeout(this.checkUpdateTimeout);
|
|
1227
958
|
this.checkUpdateTimeout = undefined;
|
|
1228
959
|
this.log.debug('Check update timeout cleared');
|
|
1229
960
|
}
|
|
1230
|
-
// Clear the check update interval
|
|
1231
961
|
if (this.checkUpdateInterval) {
|
|
1232
962
|
clearInterval(this.checkUpdateInterval);
|
|
1233
963
|
this.checkUpdateInterval = undefined;
|
|
1234
964
|
this.log.debug('Check update interval cleared');
|
|
1235
965
|
}
|
|
1236
|
-
// Clear the configure timeout
|
|
1237
966
|
if (this.configureTimeout) {
|
|
1238
967
|
clearTimeout(this.configureTimeout);
|
|
1239
968
|
this.configureTimeout = undefined;
|
|
1240
969
|
this.log.debug('Matterbridge configure timeout cleared');
|
|
1241
970
|
}
|
|
1242
|
-
// Clear the reachability timeout
|
|
1243
971
|
if (this.reachabilityTimeout) {
|
|
1244
972
|
clearTimeout(this.reachabilityTimeout);
|
|
1245
973
|
this.reachabilityTimeout = undefined;
|
|
1246
974
|
this.log.debug('Matterbridge reachability timeout cleared');
|
|
1247
975
|
}
|
|
1248
|
-
// Call the shutdown method of each plugin and clear the plugins reachability timeout
|
|
1249
976
|
for (const plugin of this.plugins) {
|
|
1250
977
|
if (!plugin.enabled || plugin.error)
|
|
1251
978
|
continue;
|
|
@@ -1256,7 +983,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1256
983
|
this.log.debug(`Plugin ${plg}${plugin.name}${db} reachability timeout cleared`);
|
|
1257
984
|
}
|
|
1258
985
|
}
|
|
1259
|
-
// Stop matter server nodes
|
|
1260
986
|
this.log.notice(`Stopping matter server nodes in ${this.bridgeMode} mode...`);
|
|
1261
987
|
if (pause > 0) {
|
|
1262
988
|
this.log.debug(`Waiting ${pause}ms for the MessageExchange to finish...`);
|
|
@@ -1283,7 +1009,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1283
1009
|
}
|
|
1284
1010
|
}
|
|
1285
1011
|
this.log.notice('Stopped matter server nodes');
|
|
1286
|
-
// Matter commisioning reset
|
|
1287
1012
|
if (message === 'shutting down with reset...') {
|
|
1288
1013
|
this.log.info('Resetting Matterbridge commissioning information...');
|
|
1289
1014
|
await this.matterStorageManager?.createContext('events')?.clearAll();
|
|
@@ -1293,7 +1018,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1293
1018
|
await this.matterbridgeContext?.clearAll();
|
|
1294
1019
|
this.log.info('Matter storage reset done! Remove the bridge from the controller.');
|
|
1295
1020
|
}
|
|
1296
|
-
// Unregister all devices
|
|
1297
1021
|
if (message === 'unregistered all devices and shutting down...') {
|
|
1298
1022
|
if (this.bridgeMode === 'bridge') {
|
|
1299
1023
|
await this.matterStorageManager?.createContext('root')?.createContext('parts')?.createContext('Matterbridge')?.createContext('parts')?.clearAll();
|
|
@@ -1311,35 +1035,16 @@ export class Matterbridge extends EventEmitter {
|
|
|
1311
1035
|
}
|
|
1312
1036
|
this.log.info('Matter storage reset done!');
|
|
1313
1037
|
}
|
|
1314
|
-
// Stop matter storage
|
|
1315
1038
|
await this.stopMatterStorage();
|
|
1316
|
-
// Stop the frontend
|
|
1317
1039
|
await this.frontend.stop();
|
|
1318
1040
|
this.frontend.destroy();
|
|
1319
|
-
// Close PluginManager and DeviceManager
|
|
1320
1041
|
this.plugins.destroy();
|
|
1321
1042
|
this.devices.destroy();
|
|
1322
|
-
// Stop thread messaging server
|
|
1323
1043
|
this.server.close();
|
|
1324
|
-
// Close the matterbridge node storage and context
|
|
1325
1044
|
if (this.nodeStorage && this.nodeContext) {
|
|
1326
|
-
/*
|
|
1327
|
-
TODO: Implement serialization of registered devices
|
|
1328
|
-
this.log.info('Saving registered devices...');
|
|
1329
|
-
const serializedRegisteredDevices: SerializedMatterbridgeEndpoint[] = [];
|
|
1330
|
-
this.devices.forEach(async (device) => {
|
|
1331
|
-
const serializedMatterbridgeDevice = MatterbridgeEndpoint.serialize(device);
|
|
1332
|
-
this.log.info(`- ${serializedMatterbridgeDevice.deviceName}${rs}\n`, serializedMatterbridgeDevice);
|
|
1333
|
-
if (serializedMatterbridgeDevice) serializedRegisteredDevices.push(serializedMatterbridgeDevice);
|
|
1334
|
-
});
|
|
1335
|
-
await this.nodeContext.set<SerializedMatterbridgeEndpoint[]>('devices', serializedRegisteredDevices);
|
|
1336
|
-
this.log.info(`Saved registered devices (${serializedRegisteredDevices?.length})`);
|
|
1337
|
-
*/
|
|
1338
|
-
// Clear nodeContext and nodeStorage (they just need 1000ms to write the data to disk)
|
|
1339
1045
|
this.log.debug(`Closing node storage context for ${plg}Matterbridge${db}...`);
|
|
1340
1046
|
await this.nodeContext.close();
|
|
1341
1047
|
this.nodeContext = undefined;
|
|
1342
|
-
// Clear nodeContext for each plugin (they just need 1000ms to write the data to disk)
|
|
1343
1048
|
for (const plugin of this.plugins) {
|
|
1344
1049
|
if (plugin.nodeContext) {
|
|
1345
1050
|
this.log.debug(`Closing node storage context for plugin ${plg}${plugin.name}${db}...`);
|
|
@@ -1356,10 +1061,8 @@ export class Matterbridge extends EventEmitter {
|
|
|
1356
1061
|
}
|
|
1357
1062
|
this.plugins.clear();
|
|
1358
1063
|
this.devices.clear();
|
|
1359
|
-
// Factory reset
|
|
1360
1064
|
if (message === 'shutting down with factory reset...') {
|
|
1361
1065
|
try {
|
|
1362
|
-
// Delete matter storage directory with its subdirectories and backup
|
|
1363
1066
|
const dir = path.join(this.matterbridgeDirectory, MATTER_STORAGE_NAME);
|
|
1364
1067
|
this.log.info(`Removing matter storage directory: ${dir}`);
|
|
1365
1068
|
await fs.promises.rm(dir, { recursive: true });
|
|
@@ -1368,13 +1071,11 @@ export class Matterbridge extends EventEmitter {
|
|
|
1368
1071
|
await fs.promises.rm(backup, { recursive: true });
|
|
1369
1072
|
}
|
|
1370
1073
|
catch (error) {
|
|
1371
|
-
// istanbul ignore next if
|
|
1372
1074
|
if (error instanceof Error && error.code !== 'ENOENT') {
|
|
1373
1075
|
this.log.error(`Error removing matter storage directory: ${error}`);
|
|
1374
1076
|
}
|
|
1375
1077
|
}
|
|
1376
1078
|
try {
|
|
1377
|
-
// Delete matterbridge storage directory with its subdirectories and backup
|
|
1378
1079
|
const dir = path.join(this.matterbridgeDirectory, NODE_STORAGE_DIR);
|
|
1379
1080
|
this.log.info(`Removing matterbridge storage directory: ${dir}`);
|
|
1380
1081
|
await fs.promises.rm(dir, { recursive: true });
|
|
@@ -1383,20 +1084,18 @@ export class Matterbridge extends EventEmitter {
|
|
|
1383
1084
|
await fs.promises.rm(backup, { recursive: true });
|
|
1384
1085
|
}
|
|
1385
1086
|
catch (error) {
|
|
1386
|
-
// istanbul ignore next if
|
|
1387
1087
|
if (error instanceof Error && error.code !== 'ENOENT') {
|
|
1388
1088
|
this.log.error(`Error removing matterbridge storage directory: ${error}`);
|
|
1389
1089
|
}
|
|
1390
1090
|
}
|
|
1391
1091
|
this.log.info('Factory reset done! Remove all paired fabrics from the controllers.');
|
|
1392
1092
|
}
|
|
1393
|
-
// Deregisters the process handlers
|
|
1394
1093
|
this.deregisterProcessHandlers();
|
|
1395
1094
|
if (restart) {
|
|
1396
1095
|
if (message === 'updating...') {
|
|
1397
1096
|
this.log.info('Cleanup completed. Updating...');
|
|
1398
1097
|
Matterbridge.instance = undefined;
|
|
1399
|
-
this.emit('update');
|
|
1098
|
+
this.emit('update');
|
|
1400
1099
|
}
|
|
1401
1100
|
else if (message === 'restarting...') {
|
|
1402
1101
|
this.log.info('Cleanup completed. Restarting...');
|
|
@@ -1425,14 +1124,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
1425
1124
|
this.log.debug('Cleanup already started...');
|
|
1426
1125
|
}
|
|
1427
1126
|
}
|
|
1428
|
-
/**
|
|
1429
|
-
* Starts the Matterbridge in bridge mode.
|
|
1430
|
-
*
|
|
1431
|
-
* @private
|
|
1432
|
-
* @returns {Promise<void>} A promise that resolves when the Matterbridge is started.
|
|
1433
|
-
*/
|
|
1434
1127
|
async startBridge() {
|
|
1435
|
-
// Plugins are configured by a timer when matter server is started and plugin.configured is set to true
|
|
1436
1128
|
if (!this.matterStorageManager)
|
|
1437
1129
|
throw new Error('No storage manager initialized');
|
|
1438
1130
|
if (!this.matterbridgeContext)
|
|
@@ -1471,16 +1163,13 @@ export class Matterbridge extends EventEmitter {
|
|
|
1471
1163
|
clearInterval(this.startMatterInterval);
|
|
1472
1164
|
this.startMatterInterval = undefined;
|
|
1473
1165
|
this.log.debug('Cleared startMatterInterval interval in bridge mode');
|
|
1474
|
-
|
|
1475
|
-
this.startServerNode(this.serverNode); // We don't await this, because the server node is started in the background
|
|
1476
|
-
// Start the Matter server node of single devices in mode 'server'
|
|
1166
|
+
this.startServerNode(this.serverNode);
|
|
1477
1167
|
for (const device of this.devices.array()) {
|
|
1478
1168
|
if (device.mode === 'server' && device.serverNode) {
|
|
1479
1169
|
this.log.debug(`Starting server node for device ${dev}${device.deviceName}${db} in server mode...`);
|
|
1480
|
-
this.startServerNode(device.serverNode);
|
|
1170
|
+
this.startServerNode(device.serverNode);
|
|
1481
1171
|
}
|
|
1482
1172
|
}
|
|
1483
|
-
// Configure the plugins
|
|
1484
1173
|
this.configureTimeout = setTimeout(async () => {
|
|
1485
1174
|
for (const plugin of this.plugins.array()) {
|
|
1486
1175
|
if (!plugin.enabled || !plugin.loaded || !plugin.started || plugin.error)
|
|
@@ -1498,40 +1187,28 @@ export class Matterbridge extends EventEmitter {
|
|
|
1498
1187
|
}
|
|
1499
1188
|
this.frontend.wssSendRefreshRequired('plugins');
|
|
1500
1189
|
}, 30 * 1000).unref();
|
|
1501
|
-
// Setting reachability to true
|
|
1502
1190
|
this.reachabilityTimeout = setTimeout(() => {
|
|
1503
1191
|
this.log.info(`Setting reachability to true for ${plg}Matterbridge${db}`);
|
|
1504
1192
|
if (this.aggregatorNode)
|
|
1505
1193
|
this.setAggregatorReachability(this.aggregatorNode, true);
|
|
1506
1194
|
}, 60 * 1000).unref();
|
|
1507
|
-
// Logger.get('LogServerNode').info(this.serverNode);
|
|
1508
1195
|
this.emit('bridge_started');
|
|
1509
1196
|
this.log.notice('Matterbridge bridge started successfully');
|
|
1510
1197
|
this.frontend.wssSendRefreshRequired('settings');
|
|
1511
1198
|
this.frontend.wssSendRefreshRequired('plugins');
|
|
1512
1199
|
}, Number(process.env['MATTERBRIDGE_START_MATTER_INTERVAL_MS']) || this.startMatterIntervalMs);
|
|
1513
1200
|
}
|
|
1514
|
-
/**
|
|
1515
|
-
* Starts the Matterbridge in childbridge mode.
|
|
1516
|
-
*
|
|
1517
|
-
* @param {number} [delay] - The delay before starting the childbridge. Default is 1000 milliseconds.
|
|
1518
|
-
*
|
|
1519
|
-
* @returns {Promise<void>} A promise that resolves when the Matterbridge is started.
|
|
1520
|
-
*/
|
|
1521
1201
|
async startChildbridge(delay = 1000) {
|
|
1522
1202
|
if (!this.matterStorageManager)
|
|
1523
1203
|
throw new Error('No storage manager initialized');
|
|
1524
|
-
// Load with await all plugins but don't start them. We get the platform.type to pre-create server nodes for DynamicPlatform plugins
|
|
1525
1204
|
this.log.debug('Loading all plugins in childbridge mode...');
|
|
1526
1205
|
await this.startPlugins(true, false);
|
|
1527
|
-
// Create server nodes for DynamicPlatform plugins and start all plugins in the background
|
|
1528
1206
|
this.log.debug('Creating server nodes for DynamicPlatform plugins and starting all plugins in childbridge mode...');
|
|
1529
1207
|
for (const plugin of this.plugins.array().filter((p) => p.enabled && !p.error)) {
|
|
1530
1208
|
if (plugin.type === 'DynamicPlatform')
|
|
1531
1209
|
await this.createDynamicPlugin(plugin);
|
|
1532
|
-
this.plugins.start(plugin, 'Matterbridge is starting');
|
|
1210
|
+
this.plugins.start(plugin, 'Matterbridge is starting');
|
|
1533
1211
|
}
|
|
1534
|
-
// Start the Matterbridge in childbridge mode when all plugins are loaded and started
|
|
1535
1212
|
this.log.debug('Starting start matter interval in childbridge mode...');
|
|
1536
1213
|
let failCount = 0;
|
|
1537
1214
|
this.startMatterInterval = setInterval(async () => {
|
|
@@ -1565,9 +1242,8 @@ export class Matterbridge extends EventEmitter {
|
|
|
1565
1242
|
clearInterval(this.startMatterInterval);
|
|
1566
1243
|
this.startMatterInterval = undefined;
|
|
1567
1244
|
if (delay > 0)
|
|
1568
|
-
await wait(Number(process.env['MATTERBRIDGE_PAUSE_MATTER_INTERVAL_MS']) || delay);
|
|
1245
|
+
await wait(Number(process.env['MATTERBRIDGE_PAUSE_MATTER_INTERVAL_MS']) || delay);
|
|
1569
1246
|
this.log.debug('Cleared startMatterInterval interval in childbridge mode');
|
|
1570
|
-
// Configure the plugins
|
|
1571
1247
|
this.configureTimeout = setTimeout(async () => {
|
|
1572
1248
|
for (const plugin of this.plugins.array()) {
|
|
1573
1249
|
if (!plugin.enabled || !plugin.loaded || !plugin.started || plugin.error)
|
|
@@ -1592,7 +1268,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1592
1268
|
this.log.error(`Plugin ${plg}${plugin.name}${er} didn't register any devices to Matterbridge. Verify the plugin configuration.`);
|
|
1593
1269
|
continue;
|
|
1594
1270
|
}
|
|
1595
|
-
// istanbul ignore next if cause is just a safety check
|
|
1596
1271
|
if (!plugin.serverNode) {
|
|
1597
1272
|
this.log.error(`Server node not found for plugin ${plg}${plugin.name}${er}`);
|
|
1598
1273
|
continue;
|
|
@@ -1605,252 +1280,28 @@ export class Matterbridge extends EventEmitter {
|
|
|
1605
1280
|
this.log.error(`Node storage context not found for plugin ${plg}${plugin.name}${er}`);
|
|
1606
1281
|
continue;
|
|
1607
1282
|
}
|
|
1608
|
-
|
|
1609
|
-
this.startServerNode(plugin.serverNode); // We don't await this, because the server node is started in the background
|
|
1610
|
-
// Setting reachability to true
|
|
1283
|
+
this.startServerNode(plugin.serverNode);
|
|
1611
1284
|
plugin.reachabilityTimeout = setTimeout(() => {
|
|
1612
1285
|
this.log.info(`Setting reachability to true for ${plg}${plugin.name}${nf}`);
|
|
1613
1286
|
if (plugin.type === 'DynamicPlatform' && plugin.aggregatorNode)
|
|
1614
1287
|
this.setAggregatorReachability(plugin.aggregatorNode, true);
|
|
1615
1288
|
}, 60 * 1000).unref();
|
|
1616
1289
|
}
|
|
1617
|
-
// Start the Matter server node of single devices in mode 'server'
|
|
1618
1290
|
for (const device of this.devices.array()) {
|
|
1619
1291
|
if (device.mode === 'server' && device.serverNode) {
|
|
1620
1292
|
this.log.debug(`Starting server node for device ${dev}${device.deviceName}${db} in server mode...`);
|
|
1621
|
-
this.startServerNode(device.serverNode);
|
|
1293
|
+
this.startServerNode(device.serverNode);
|
|
1622
1294
|
}
|
|
1623
1295
|
}
|
|
1624
|
-
// Logger.get('LogServerNode').info(this.serverNode);
|
|
1625
1296
|
this.emit('childbridge_started');
|
|
1626
1297
|
this.log.notice('Matterbridge childbridge started successfully');
|
|
1627
1298
|
this.frontend.wssSendRefreshRequired('settings');
|
|
1628
1299
|
this.frontend.wssSendRefreshRequired('plugins');
|
|
1629
1300
|
}, Number(process.env['MATTERBRIDGE_START_MATTER_INTERVAL_MS']) || this.startMatterIntervalMs);
|
|
1630
1301
|
}
|
|
1631
|
-
/**
|
|
1632
|
-
* Starts the Matterbridge controller.
|
|
1633
|
-
*
|
|
1634
|
-
* @private
|
|
1635
|
-
* @returns {Promise<void>} A promise that resolves when the Matterbridge is started.
|
|
1636
|
-
*/
|
|
1637
1302
|
async startController() {
|
|
1638
|
-
/*
|
|
1639
|
-
if (!this.matterStorageManager) {
|
|
1640
|
-
this.log.error('No storage manager initialized');
|
|
1641
|
-
await this.cleanup('No storage manager initialized');
|
|
1642
|
-
return;
|
|
1643
|
-
}
|
|
1644
|
-
this.log.info('Creating context: mattercontrollerContext');
|
|
1645
|
-
this.controllerContext = this.matterStorageManager.createContext('mattercontrollerContext');
|
|
1646
|
-
if (!this.controllerContext) {
|
|
1647
|
-
this.log.error('No storage context mattercontrollerContext initialized');
|
|
1648
|
-
await this.cleanup('No storage context mattercontrollerContext initialized');
|
|
1649
|
-
return;
|
|
1650
|
-
}
|
|
1651
|
-
|
|
1652
|
-
this.log.debug('Starting matterbridge in mode', this.bridgeMode);
|
|
1653
|
-
this.matterServer = await this.createMatterServer(this.storageManager);
|
|
1654
|
-
this.log.info('Creating matter commissioning controller');
|
|
1655
|
-
this.commissioningController = new CommissioningController({
|
|
1656
|
-
autoConnect: false,
|
|
1657
|
-
});
|
|
1658
|
-
this.log.info('Adding matter commissioning controller to matter server');
|
|
1659
|
-
await this.matterServer.addCommissioningController(this.commissioningController);
|
|
1660
|
-
|
|
1661
|
-
this.log.info('Starting matter server');
|
|
1662
|
-
await this.matterServer.start();
|
|
1663
|
-
this.log.info('Matter server started');
|
|
1664
|
-
const commissioningOptions: ControllerCommissioningFlowOptions = {
|
|
1665
|
-
regulatoryLocation: GeneralCommissioning.RegulatoryLocationType.IndoorOutdoor,
|
|
1666
|
-
regulatoryCountryCode: 'XX',
|
|
1667
|
-
};
|
|
1668
|
-
const commissioningController = new CommissioningController({
|
|
1669
|
-
environment: {
|
|
1670
|
-
environment,
|
|
1671
|
-
id: uniqueId,
|
|
1672
|
-
},
|
|
1673
|
-
autoConnect: false, // Do not auto connect to the commissioned nodes
|
|
1674
|
-
adminFabricLabel,
|
|
1675
|
-
});
|
|
1676
|
-
|
|
1677
|
-
if (hasParameter('pairingcode')) {
|
|
1678
|
-
this.log.info('Pairing device with pairingcode:', getParameter('pairingcode'));
|
|
1679
|
-
const pairingCode = getParameter('pairingcode');
|
|
1680
|
-
const ip = this.controllerContext.has('ip') ? this.controllerContext.get<string>('ip') : undefined;
|
|
1681
|
-
const port = this.controllerContext.has('port') ? this.controllerContext.get<number>('port') : undefined;
|
|
1682
|
-
|
|
1683
|
-
let longDiscriminator, setupPin, shortDiscriminator;
|
|
1684
|
-
if (pairingCode !== undefined) {
|
|
1685
|
-
const pairingCodeCodec = ManualPairingCodeCodec.decode(pairingCode);
|
|
1686
|
-
shortDiscriminator = pairingCodeCodec.shortDiscriminator;
|
|
1687
|
-
longDiscriminator = undefined;
|
|
1688
|
-
setupPin = pairingCodeCodec.passcode;
|
|
1689
|
-
this.log.info(`Data extracted from pairing code: ${Logger.toJSON(pairingCodeCodec)}`);
|
|
1690
|
-
} else {
|
|
1691
|
-
longDiscriminator = await this.controllerContext.get('longDiscriminator', 3840);
|
|
1692
|
-
if (longDiscriminator > 4095) throw new Error('Discriminator value must be less than 4096');
|
|
1693
|
-
setupPin = this.controllerContext.get('pin', 20202021);
|
|
1694
|
-
}
|
|
1695
|
-
if ((shortDiscriminator === undefined && longDiscriminator === undefined) || setupPin === undefined) {
|
|
1696
|
-
throw new Error('Please specify the longDiscriminator of the device to commission with -longDiscriminator or provide a valid passcode with -passcode');
|
|
1697
|
-
}
|
|
1698
|
-
|
|
1699
|
-
const options = {
|
|
1700
|
-
commissioning: commissioningOptions,
|
|
1701
|
-
discovery: {
|
|
1702
|
-
knownAddress: ip !== undefined && port !== undefined ? { ip, port, type: 'udp' } : undefined,
|
|
1703
|
-
identifierData: longDiscriminator !== undefined ? { longDiscriminator } : shortDiscriminator !== undefined ? { shortDiscriminator } : {},
|
|
1704
|
-
},
|
|
1705
|
-
passcode: setupPin,
|
|
1706
|
-
} as NodeCommissioningOptions;
|
|
1707
|
-
this.log.info('Commissioning with options:', options);
|
|
1708
|
-
const nodeId = await this.commissioningController.commissionNode(options);
|
|
1709
|
-
this.log.info(`Commissioning successfully done with nodeId: ${nodeId}`);
|
|
1710
|
-
this.log.info('ActiveSessionInformation:', this.commissioningController.getActiveSessionInformation());
|
|
1711
|
-
} // (hasParameter('pairingcode'))
|
|
1712
|
-
|
|
1713
|
-
if (hasParameter('unpairall')) {
|
|
1714
|
-
this.log.info('***Commissioning controller unpairing all nodes...');
|
|
1715
|
-
const nodeIds = this.commissioningController.getCommissionedNodes();
|
|
1716
|
-
for (const nodeId of nodeIds) {
|
|
1717
|
-
this.log.info('***Commissioning controller unpairing node:', nodeId);
|
|
1718
|
-
await this.commissioningController.removeNode(nodeId);
|
|
1719
|
-
}
|
|
1720
|
-
return;
|
|
1721
|
-
}
|
|
1722
|
-
|
|
1723
|
-
if (hasParameter('discover')) {
|
|
1724
|
-
// const discover = await this.commissioningController.discoverCommissionableDevices({ productId: 0x8000, deviceType: 0xfff1 });
|
|
1725
|
-
// console.log(discover);
|
|
1726
|
-
}
|
|
1727
|
-
|
|
1728
|
-
if (!this.commissioningController.isCommissioned()) {
|
|
1729
|
-
this.log.info('***Commissioning controller is not commissioned: use matterbridge -controller -pairingcode [pairingcode] to commission a device');
|
|
1730
|
-
return;
|
|
1731
|
-
}
|
|
1732
|
-
|
|
1733
|
-
const nodeIds = this.commissioningController.getCommissionedNodes();
|
|
1734
|
-
this.log.info(`***Commissioning controller is commissioned ${this.commissioningController.isCommissioned()} and has ${nodeIds.length} nodes commisioned: `);
|
|
1735
|
-
for (const nodeId of nodeIds) {
|
|
1736
|
-
this.log.info(`***Connecting to commissioned node: ${nodeId}`);
|
|
1737
|
-
|
|
1738
|
-
const node = await this.commissioningController.connectNode(nodeId, {
|
|
1739
|
-
autoSubscribe: false,
|
|
1740
|
-
attributeChangedCallback: (peerNodeId, { path: { nodeId, clusterId, endpointId, attributeName }, value }) =>
|
|
1741
|
-
this.log.info(`***Commissioning controller attributeChangedCallback ${peerNodeId}: attribute ${nodeId}/${endpointId}/${clusterId}/${attributeName} changed to ${Logger.toJSON(value)}`),
|
|
1742
|
-
eventTriggeredCallback: (peerNodeId, { path: { nodeId, clusterId, endpointId, eventName }, events }) =>
|
|
1743
|
-
this.log.info(`***Commissioning controller eventTriggeredCallback ${peerNodeId}: Event ${nodeId}/${endpointId}/${clusterId}/${eventName} triggered with ${Logger.toJSON(events)}`),
|
|
1744
|
-
stateInformationCallback: (peerNodeId, info) => {
|
|
1745
|
-
switch (info) {
|
|
1746
|
-
case NodeStateInformation.Connected:
|
|
1747
|
-
this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} connected`);
|
|
1748
|
-
break;
|
|
1749
|
-
case NodeStateInformation.Disconnected:
|
|
1750
|
-
this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} disconnected`);
|
|
1751
|
-
break;
|
|
1752
|
-
case NodeStateInformation.Reconnecting:
|
|
1753
|
-
this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} reconnecting`);
|
|
1754
|
-
break;
|
|
1755
|
-
case NodeStateInformation.WaitingForDeviceDiscovery:
|
|
1756
|
-
this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} waiting for device discovery`);
|
|
1757
|
-
break;
|
|
1758
|
-
case NodeStateInformation.StructureChanged:
|
|
1759
|
-
this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} structure changed`);
|
|
1760
|
-
break;
|
|
1761
|
-
case NodeStateInformation.Decommissioned:
|
|
1762
|
-
this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} decommissioned`);
|
|
1763
|
-
break;
|
|
1764
|
-
default:
|
|
1765
|
-
this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} NodeStateInformation.${info}`);
|
|
1766
|
-
break;
|
|
1767
|
-
}
|
|
1768
|
-
},
|
|
1769
|
-
});
|
|
1770
|
-
|
|
1771
|
-
node.logStructure();
|
|
1772
|
-
|
|
1773
|
-
// Get the interaction client
|
|
1774
|
-
this.log.info('Getting the interaction client');
|
|
1775
|
-
const interactionClient = await node.getInteractionClient();
|
|
1776
|
-
let cluster;
|
|
1777
|
-
let attributes;
|
|
1778
|
-
|
|
1779
|
-
// Log BasicInformationCluster
|
|
1780
|
-
cluster = BasicInformationCluster;
|
|
1781
|
-
attributes = await interactionClient.getMultipleAttributes({
|
|
1782
|
-
attributes: [{ clusterId: cluster.id }],
|
|
1783
|
-
});
|
|
1784
|
-
if (attributes.length > 0) this.log.info(`Cluster: ${idn}${cluster.name}${rs}${nf} attributes:`);
|
|
1785
|
-
attributes.forEach((attribute) => {
|
|
1786
|
-
this.log.info(
|
|
1787
|
-
`- 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}`,
|
|
1788
|
-
);
|
|
1789
|
-
});
|
|
1790
|
-
|
|
1791
|
-
// Log PowerSourceCluster
|
|
1792
|
-
cluster = PowerSourceCluster;
|
|
1793
|
-
attributes = await interactionClient.getMultipleAttributes({
|
|
1794
|
-
attributes: [{ clusterId: cluster.id }],
|
|
1795
|
-
});
|
|
1796
|
-
if (attributes.length > 0) this.log.info(`Cluster: ${idn}${cluster.name}${rs}${nf} attributes:`);
|
|
1797
|
-
attributes.forEach((attribute) => {
|
|
1798
|
-
this.log.info(
|
|
1799
|
-
`- 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}`,
|
|
1800
|
-
);
|
|
1801
|
-
});
|
|
1802
|
-
|
|
1803
|
-
// Log ThreadNetworkDiagnostics
|
|
1804
|
-
cluster = ThreadNetworkDiagnosticsCluster;
|
|
1805
|
-
attributes = await interactionClient.getMultipleAttributes({
|
|
1806
|
-
attributes: [{ clusterId: cluster.id }],
|
|
1807
|
-
});
|
|
1808
|
-
if (attributes.length > 0) this.log.info(`Cluster: ${idn}${cluster.name}${rs}${nf} attributes:`);
|
|
1809
|
-
attributes.forEach((attribute) => {
|
|
1810
|
-
this.log.info(
|
|
1811
|
-
`- 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}`,
|
|
1812
|
-
);
|
|
1813
|
-
});
|
|
1814
|
-
|
|
1815
|
-
// Log SwitchCluster
|
|
1816
|
-
cluster = SwitchCluster;
|
|
1817
|
-
attributes = await interactionClient.getMultipleAttributes({
|
|
1818
|
-
attributes: [{ clusterId: cluster.id }],
|
|
1819
|
-
});
|
|
1820
|
-
if (attributes.length > 0) this.log.info(`Cluster: ${idn}${cluster.name}${rs}${nf} attributes:`);
|
|
1821
|
-
attributes.forEach((attribute) => {
|
|
1822
|
-
this.log.info(
|
|
1823
|
-
`- 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}`,
|
|
1824
|
-
);
|
|
1825
|
-
});
|
|
1826
|
-
|
|
1827
|
-
this.log.info('Subscribing to all attributes and events');
|
|
1828
|
-
await node.subscribeAllAttributesAndEvents({
|
|
1829
|
-
ignoreInitialTriggers: false,
|
|
1830
|
-
attributeChangedCallback: ({ path: { nodeId, clusterId, endpointId, attributeName }, version, value }) =>
|
|
1831
|
-
this.log.info(
|
|
1832
|
-
`***${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}`,
|
|
1833
|
-
),
|
|
1834
|
-
eventTriggeredCallback: ({ path: { nodeId, clusterId, endpointId, eventName }, events }) => {
|
|
1835
|
-
this.log.info(
|
|
1836
|
-
`***${db}Commissioning controller eventTriggeredCallback: event ${BLUE}${nodeId}${db}/${or}${endpointId}${db}/${hk}${getClusterNameById(clusterId)}${db}/${zb}${eventName}${db} triggered with ${debugStringify(events ?? { none: true })}`,
|
|
1837
|
-
);
|
|
1838
|
-
},
|
|
1839
|
-
});
|
|
1840
|
-
this.log.info('Subscribed to all attributes and events');
|
|
1841
|
-
}
|
|
1842
|
-
*/
|
|
1843
1303
|
}
|
|
1844
|
-
/** */
|
|
1845
|
-
/** Matter.js methods */
|
|
1846
|
-
/** */
|
|
1847
|
-
/**
|
|
1848
|
-
* Starts the matter storage with name Matterbridge, create the matterbridge context and performs a backup.
|
|
1849
|
-
*
|
|
1850
|
-
* @returns {Promise<void>} - A promise that resolves when the storage is started.
|
|
1851
|
-
*/
|
|
1852
1304
|
async startMatterStorage() {
|
|
1853
|
-
// Setup Matter storage
|
|
1854
1305
|
this.log.info(`Starting matter node storage...`);
|
|
1855
1306
|
this.matterStorageService = this.environment.get(StorageService);
|
|
1856
1307
|
this.log.info(`Matter node storage service created: ${this.matterStorageService.location}`);
|
|
@@ -1858,17 +1309,8 @@ export class Matterbridge extends EventEmitter {
|
|
|
1858
1309
|
this.log.info('Matter node storage manager "Matterbridge" created');
|
|
1859
1310
|
this.matterbridgeContext = await this.createServerNodeContext('Matterbridge', 'Matterbridge', this.aggregatorDeviceType, this.aggregatorVendorId, this.aggregatorVendorName, this.aggregatorProductId, this.aggregatorProductName, this.aggregatorSerialNumber, this.aggregatorUniqueId);
|
|
1860
1311
|
this.log.info('Matter node storage started');
|
|
1861
|
-
// Backup matter storage since it is created/opened correctly
|
|
1862
1312
|
await this.backupMatterStorage(path.join(this.matterbridgeDirectory, MATTER_STORAGE_NAME), path.join(this.matterbridgeDirectory, MATTER_STORAGE_NAME + '.backup'));
|
|
1863
1313
|
}
|
|
1864
|
-
/**
|
|
1865
|
-
* Makes a backup copy of the specified matter storage directory.
|
|
1866
|
-
*
|
|
1867
|
-
* @param {string} storageName - The name of the storage directory to be backed up.
|
|
1868
|
-
* @param {string} backupName - The name of the backup directory to be created.
|
|
1869
|
-
* @private
|
|
1870
|
-
* @returns {Promise<void>} A promise that resolves when the has been done.
|
|
1871
|
-
*/
|
|
1872
1314
|
async backupMatterStorage(storageName, backupName) {
|
|
1873
1315
|
this.log.info('Creating matter node storage backup...');
|
|
1874
1316
|
try {
|
|
@@ -1879,11 +1321,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1879
1321
|
this.log.error(`Error creating matter node storage backup from ${storageName} to ${backupName}:`, error);
|
|
1880
1322
|
}
|
|
1881
1323
|
}
|
|
1882
|
-
/**
|
|
1883
|
-
* Stops the matter storage.
|
|
1884
|
-
*
|
|
1885
|
-
* @returns {Promise<void>} A promise that resolves when the storage is stopped.
|
|
1886
|
-
*/
|
|
1887
1324
|
async stopMatterStorage() {
|
|
1888
1325
|
this.log.info('Closing matter node storage...');
|
|
1889
1326
|
await this.matterStorageManager?.close();
|
|
@@ -1892,20 +1329,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1892
1329
|
this.matterbridgeContext = undefined;
|
|
1893
1330
|
this.log.info('Matter node storage closed');
|
|
1894
1331
|
}
|
|
1895
|
-
/**
|
|
1896
|
-
* Creates a server node storage context.
|
|
1897
|
-
*
|
|
1898
|
-
* @param {string} storeId - The storeId.
|
|
1899
|
-
* @param {string} deviceName - The name of the device.
|
|
1900
|
-
* @param {DeviceTypeId} deviceType - The device type of the device.
|
|
1901
|
-
* @param {number} vendorId - The vendor ID.
|
|
1902
|
-
* @param {string} vendorName - The vendor name.
|
|
1903
|
-
* @param {number} productId - The product ID.
|
|
1904
|
-
* @param {string} productName - The product name.
|
|
1905
|
-
* @param {string} [serialNumber] - The serial number of the device (optional).
|
|
1906
|
-
* @param {string} [uniqueId] - The unique ID of the device (optional).
|
|
1907
|
-
* @returns {Promise<StorageContext>} The storage context for the commissioning server.
|
|
1908
|
-
*/
|
|
1909
1332
|
async createServerNodeContext(storeId, deviceName, deviceType, vendorId, vendorName, productId, productName, serialNumber, uniqueId) {
|
|
1910
1333
|
const { randomBytes } = await import('node:crypto');
|
|
1911
1334
|
if (!this.matterStorageService)
|
|
@@ -1945,15 +1368,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1945
1368
|
this.log.debug(`- hardwareVersion: ${await storageContext.get('hardwareVersion')} hardwareVersionString: ${await storageContext.get('hardwareVersionString')}`);
|
|
1946
1369
|
return storageContext;
|
|
1947
1370
|
}
|
|
1948
|
-
/**
|
|
1949
|
-
* Creates a server node.
|
|
1950
|
-
*
|
|
1951
|
-
* @param {StorageContext} storageContext - The storage context for the server node.
|
|
1952
|
-
* @param {number} [port] - The port number for the server node. Defaults to 5540.
|
|
1953
|
-
* @param {number} [passcode] - The passcode for the server node. Defaults to 20242025.
|
|
1954
|
-
* @param {number} [discriminator] - The discriminator for the server node. Defaults to 3850.
|
|
1955
|
-
* @returns {Promise<ServerNode<ServerNode.RootEndpoint>>} A promise that resolves to the created server node.
|
|
1956
|
-
*/
|
|
1957
1371
|
async createServerNode(storageContext, port = 5540, passcode = 20242025, discriminator = 3850) {
|
|
1958
1372
|
const storeId = await storageContext.get('storeId');
|
|
1959
1373
|
this.log.notice(`Creating server node for ${storeId} on port ${port} with passcode ${passcode} and discriminator ${discriminator}...`);
|
|
@@ -1963,35 +1377,25 @@ export class Matterbridge extends EventEmitter {
|
|
|
1963
1377
|
this.log.debug(`- uniqueId: ${await storageContext.get('uniqueId')}`);
|
|
1964
1378
|
this.log.debug(`- softwareVersion: ${await storageContext.get('softwareVersion')} softwareVersionString: ${await storageContext.get('softwareVersionString')}`);
|
|
1965
1379
|
this.log.debug(`- hardwareVersion: ${await storageContext.get('hardwareVersion')} hardwareVersionString: ${await storageContext.get('hardwareVersionString')}`);
|
|
1966
|
-
/**
|
|
1967
|
-
* Create a Matter ServerNode, which contains the Root Endpoint and all relevant data and configuration
|
|
1968
|
-
*/
|
|
1969
1380
|
const serverNode = await ServerNode.create({
|
|
1970
|
-
// Required: Give the Node a unique ID which is used to store the state of this node
|
|
1971
1381
|
id: storeId,
|
|
1972
|
-
// Environment to run the server node in
|
|
1973
1382
|
environment: this.environment,
|
|
1974
|
-
// Provide Network relevant configuration like the port
|
|
1975
1383
|
network: {
|
|
1976
1384
|
listeningAddressIpv4: this.ipv4Address,
|
|
1977
1385
|
listeningAddressIpv6: this.ipv6Address,
|
|
1978
1386
|
port,
|
|
1979
1387
|
},
|
|
1980
|
-
// Provide the certificate for the device
|
|
1981
1388
|
operationalCredentials: {
|
|
1982
1389
|
certification: this.certification,
|
|
1983
1390
|
},
|
|
1984
|
-
// Provide Commissioning relevant settings
|
|
1985
1391
|
commissioning: {
|
|
1986
1392
|
passcode,
|
|
1987
1393
|
discriminator,
|
|
1988
1394
|
},
|
|
1989
|
-
// Provide Node announcement settings
|
|
1990
1395
|
productDescription: {
|
|
1991
1396
|
name: await storageContext.get('deviceName'),
|
|
1992
1397
|
deviceType: DeviceTypeId(await storageContext.get('deviceType')),
|
|
1993
1398
|
},
|
|
1994
|
-
// Provide defaults for the BasicInformation cluster on the Root endpoint
|
|
1995
1399
|
basicInformation: {
|
|
1996
1400
|
vendorId: VendorId(await storageContext.get('vendorId')),
|
|
1997
1401
|
vendorName: await storageContext.get('vendorName'),
|
|
@@ -2008,23 +1412,17 @@ export class Matterbridge extends EventEmitter {
|
|
|
2008
1412
|
reachable: true,
|
|
2009
1413
|
},
|
|
2010
1414
|
});
|
|
2011
|
-
/**
|
|
2012
|
-
* This event is triggered when the device is initially commissioned successfully.
|
|
2013
|
-
* This means: It is added to the first fabric.
|
|
2014
|
-
*/
|
|
2015
1415
|
serverNode.lifecycle.commissioned.on(() => {
|
|
2016
1416
|
this.log.notice(`Server node for ${storeId} was initially commissioned successfully!`);
|
|
2017
1417
|
this.advertisingNodes.delete(storeId);
|
|
2018
1418
|
this.frontend.wssSendRefreshRequired('matter', { matter: { ...this.getServerNodeData(serverNode) } });
|
|
2019
1419
|
});
|
|
2020
|
-
/** This event is triggered when all fabrics are removed from the device, usually it also does a factory reset then. */
|
|
2021
1420
|
serverNode.lifecycle.decommissioned.on(() => {
|
|
2022
1421
|
this.log.notice(`Server node for ${storeId} was fully decommissioned successfully!`);
|
|
2023
1422
|
this.advertisingNodes.delete(storeId);
|
|
2024
1423
|
this.frontend.wssSendRefreshRequired('matter', { matter: { ...this.getServerNodeData(serverNode) } });
|
|
2025
1424
|
this.frontend.wssSendSnackbarMessage(`${storeId} is offline`, 5, 'warning');
|
|
2026
1425
|
});
|
|
2027
|
-
/** This event is triggered when the device went online. This means that it is discoverable in the network. */
|
|
2028
1426
|
serverNode.lifecycle.online.on(async () => {
|
|
2029
1427
|
this.log.notice(`Server node for ${storeId} is online`);
|
|
2030
1428
|
if (!serverNode.lifecycle.isCommissioned) {
|
|
@@ -2035,16 +1433,13 @@ export class Matterbridge extends EventEmitter {
|
|
|
2035
1433
|
this.log.notice(`Manual pairing code: ${manualPairingCode}`);
|
|
2036
1434
|
}
|
|
2037
1435
|
else {
|
|
2038
|
-
// istanbul ignore next
|
|
2039
1436
|
this.log.notice(`Server node for ${storeId} is already commissioned. Waiting for controllers to connect ...`);
|
|
2040
|
-
// istanbul ignore next
|
|
2041
1437
|
this.advertisingNodes.delete(storeId);
|
|
2042
1438
|
}
|
|
2043
1439
|
this.frontend.wssSendRefreshRequired('matter', { matter: { ...this.getServerNodeData(serverNode) } });
|
|
2044
1440
|
this.frontend.wssSendSnackbarMessage(`${storeId} is online`, 5, 'success');
|
|
2045
1441
|
this.emit('online', storeId);
|
|
2046
1442
|
});
|
|
2047
|
-
/** This event is triggered when the device went offline. it is not longer discoverable or connectable in the network. */
|
|
2048
1443
|
serverNode.lifecycle.offline.on(() => {
|
|
2049
1444
|
this.log.notice(`Server node for ${storeId} is offline`);
|
|
2050
1445
|
this.advertisingNodes.delete(storeId);
|
|
@@ -2052,15 +1447,11 @@ export class Matterbridge extends EventEmitter {
|
|
|
2052
1447
|
this.frontend.wssSendSnackbarMessage(`${storeId} is offline`, 5, 'warning');
|
|
2053
1448
|
this.emit('offline', storeId);
|
|
2054
1449
|
});
|
|
2055
|
-
/**
|
|
2056
|
-
* This event is triggered when a fabric is added, removed or updated on the device. Use this if more granular
|
|
2057
|
-
* information is needed.
|
|
2058
|
-
*/
|
|
2059
1450
|
serverNode.events.commissioning.fabricsChanged.on((fabricIndex, fabricAction) => {
|
|
2060
1451
|
let action = '';
|
|
2061
1452
|
switch (fabricAction) {
|
|
2062
1453
|
case FabricAction.Added:
|
|
2063
|
-
this.advertisingNodes.delete(storeId);
|
|
1454
|
+
this.advertisingNodes.delete(storeId);
|
|
2064
1455
|
action = 'added';
|
|
2065
1456
|
break;
|
|
2066
1457
|
case FabricAction.Removed:
|
|
@@ -2073,22 +1464,14 @@ export class Matterbridge extends EventEmitter {
|
|
|
2073
1464
|
this.log.notice(`Commissioned fabric index ${fabricIndex} ${action} on server node for ${storeId}: ${debugStringify(serverNode.state.commissioning.fabrics[fabricIndex])}`);
|
|
2074
1465
|
this.frontend.wssSendRefreshRequired('matter', { matter: { ...this.getServerNodeData(serverNode) } });
|
|
2075
1466
|
});
|
|
2076
|
-
/**
|
|
2077
|
-
* This event is triggered when an operative new session was opened by a Controller.
|
|
2078
|
-
* It is not triggered for the initial commissioning process, just afterwards for real connections.
|
|
2079
|
-
*/
|
|
2080
1467
|
serverNode.events.sessions.opened.on((session) => {
|
|
2081
1468
|
this.log.notice(`Session opened on server node for ${storeId}: ${debugStringify(session)}`);
|
|
2082
1469
|
this.frontend.wssSendRefreshRequired('matter', { matter: { ...this.getServerNodeData(serverNode) } });
|
|
2083
1470
|
});
|
|
2084
|
-
/**
|
|
2085
|
-
* This event is triggered when an operative session is closed by a Controller or because the Device goes offline.
|
|
2086
|
-
*/
|
|
2087
1471
|
serverNode.events.sessions.closed.on((session) => {
|
|
2088
1472
|
this.log.notice(`Session closed on server node for ${storeId}: ${debugStringify(session)}`);
|
|
2089
1473
|
this.frontend.wssSendRefreshRequired('matter', { matter: { ...this.getServerNodeData(serverNode) } });
|
|
2090
1474
|
});
|
|
2091
|
-
/** This event is triggered when a subscription gets added or removed on an operative session. */
|
|
2092
1475
|
serverNode.events.sessions.subscriptionsChanged.on((session) => {
|
|
2093
1476
|
this.log.notice(`Session subscriptions changed on server node for ${storeId}: ${debugStringify(session)}`);
|
|
2094
1477
|
this.frontend.wssSendRefreshRequired('matter', { matter: { ...this.getServerNodeData(serverNode) } });
|
|
@@ -2096,12 +1479,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2096
1479
|
this.log.info(`Created server node for ${storeId}`);
|
|
2097
1480
|
return serverNode;
|
|
2098
1481
|
}
|
|
2099
|
-
/**
|
|
2100
|
-
* Gets the matter sanitized data of the specified server node.
|
|
2101
|
-
*
|
|
2102
|
-
* @param {ServerNode} [serverNode] - The server node to start.
|
|
2103
|
-
* @returns {ApiMatter} The sanitized data of the server node.
|
|
2104
|
-
*/
|
|
2105
1482
|
getServerNodeData(serverNode) {
|
|
2106
1483
|
const advertiseTime = this.advertisingNodes.get(serverNode.id) || 0;
|
|
2107
1484
|
return {
|
|
@@ -2118,25 +1495,12 @@ export class Matterbridge extends EventEmitter {
|
|
|
2118
1495
|
serialNumber: serverNode.state.basicInformation.serialNumber,
|
|
2119
1496
|
};
|
|
2120
1497
|
}
|
|
2121
|
-
/**
|
|
2122
|
-
* Starts the specified server node.
|
|
2123
|
-
*
|
|
2124
|
-
* @param {ServerNode} [matterServerNode] - The server node to start.
|
|
2125
|
-
* @returns {Promise<void>} A promise that resolves when the server node has started.
|
|
2126
|
-
*/
|
|
2127
1498
|
async startServerNode(matterServerNode) {
|
|
2128
1499
|
if (!matterServerNode)
|
|
2129
1500
|
return;
|
|
2130
1501
|
this.log.notice(`Starting ${matterServerNode.id} server node`);
|
|
2131
1502
|
await matterServerNode.start();
|
|
2132
1503
|
}
|
|
2133
|
-
/**
|
|
2134
|
-
* Stops the specified server node.
|
|
2135
|
-
*
|
|
2136
|
-
* @param {ServerNode} matterServerNode - The server node to stop.
|
|
2137
|
-
* @param {number} [timeout] - The timeout in milliseconds for stopping the server node. Defaults to 30 seconds.
|
|
2138
|
-
* @returns {Promise<void>} A promise that resolves when the server node has stopped.
|
|
2139
|
-
*/
|
|
2140
1504
|
async stopServerNode(matterServerNode, timeout = 30000) {
|
|
2141
1505
|
if (!matterServerNode)
|
|
2142
1506
|
return;
|
|
@@ -2149,25 +1513,12 @@ export class Matterbridge extends EventEmitter {
|
|
|
2149
1513
|
this.log.error(`Failed to close ${matterServerNode.id} server node: ${error instanceof Error ? error.message : error}`);
|
|
2150
1514
|
}
|
|
2151
1515
|
}
|
|
2152
|
-
/**
|
|
2153
|
-
* Creates an aggregator node with the specified storage context.
|
|
2154
|
-
*
|
|
2155
|
-
* @param {StorageContext} storageContext - The storage context for the aggregator node.
|
|
2156
|
-
* @returns {Promise<Endpoint<AggregatorEndpoint>>} A promise that resolves to the created aggregator node.
|
|
2157
|
-
*/
|
|
2158
1516
|
async createAggregatorNode(storageContext) {
|
|
2159
1517
|
this.log.notice(`Creating ${await storageContext.get('storeId')} aggregator...`);
|
|
2160
1518
|
const aggregatorNode = new Endpoint(AggregatorEndpoint, { id: `${await storageContext.get('storeId')}` });
|
|
2161
1519
|
this.log.info(`Created ${await storageContext.get('storeId')} aggregator`);
|
|
2162
1520
|
return aggregatorNode;
|
|
2163
1521
|
}
|
|
2164
|
-
/**
|
|
2165
|
-
* Creates and configures the server node for an accessory plugin for a given device.
|
|
2166
|
-
*
|
|
2167
|
-
* @param {Plugin} plugin - The plugin to configure.
|
|
2168
|
-
* @param {MatterbridgeEndpoint} device - The device to associate with the plugin.
|
|
2169
|
-
* @returns {Promise<void>} A promise that resolves when the server node for the accessory plugin is created and configured.
|
|
2170
|
-
*/
|
|
2171
1522
|
async createAccessoryPlugin(plugin, device) {
|
|
2172
1523
|
if (!plugin.locked && device.deviceType && device.deviceName && device.vendorId && device.productId && device.vendorName && device.productName) {
|
|
2173
1524
|
plugin.locked = true;
|
|
@@ -2179,12 +1530,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2179
1530
|
await plugin.serverNode.add(device);
|
|
2180
1531
|
}
|
|
2181
1532
|
}
|
|
2182
|
-
/**
|
|
2183
|
-
* Creates and configures the server node and the aggregator node for a dynamic plugin.
|
|
2184
|
-
*
|
|
2185
|
-
* @param {Plugin} plugin - The plugin to configure.
|
|
2186
|
-
* @returns {Promise<void>} A promise that resolves when the server node and the aggregator node for the dynamic plugin is created and configured.
|
|
2187
|
-
*/
|
|
2188
1533
|
async createDynamicPlugin(plugin) {
|
|
2189
1534
|
if (!plugin.locked) {
|
|
2190
1535
|
plugin.locked = true;
|
|
@@ -2197,13 +1542,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2197
1542
|
await plugin.serverNode.add(plugin.aggregatorNode);
|
|
2198
1543
|
}
|
|
2199
1544
|
}
|
|
2200
|
-
/**
|
|
2201
|
-
* Creates and configures the server node for a single not bridged device.
|
|
2202
|
-
*
|
|
2203
|
-
* @param {Plugin} plugin - The plugin to configure.
|
|
2204
|
-
* @param {MatterbridgeEndpoint} device - The device to associate with the plugin.
|
|
2205
|
-
* @returns {Promise<void>} A promise that resolves when the server node for the accessory plugin is created and configured.
|
|
2206
|
-
*/
|
|
2207
1545
|
async createDeviceServerNode(plugin, device) {
|
|
2208
1546
|
if (device.mode === 'server' && !device.serverNode && device.deviceType && device.deviceName && device.vendorId && device.vendorName && device.productId && device.productName) {
|
|
2209
1547
|
this.log.debug(`Creating device ${plg}${plugin.name}${db}:${dev}${device.deviceName}${db} server node...`);
|
|
@@ -2214,15 +1552,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
2214
1552
|
this.log.debug(`Added ${plg}${plugin.name}${db}:${dev}${device.deviceName}${db} to server node`);
|
|
2215
1553
|
}
|
|
2216
1554
|
}
|
|
2217
|
-
/**
|
|
2218
|
-
* Adds a MatterbridgeEndpoint to the specified plugin.
|
|
2219
|
-
*
|
|
2220
|
-
* @param {string} pluginName - The name of the plugin.
|
|
2221
|
-
* @param {MatterbridgeEndpoint} device - The device to add as a bridged endpoint.
|
|
2222
|
-
* @returns {Promise<void>} A promise that resolves when the bridged endpoint has been added.
|
|
2223
|
-
*/
|
|
2224
1555
|
async addBridgedEndpoint(pluginName, device) {
|
|
2225
|
-
// Check if the plugin is registered
|
|
2226
1556
|
const plugin = this.plugins.get(pluginName);
|
|
2227
1557
|
if (!plugin) {
|
|
2228
1558
|
this.log.error(`Error adding bridged endpoint ${dev}${device.deviceName}${er} (${zb}${device.id}${er}) plugin ${plg}${pluginName}${er} not found`);
|
|
@@ -2242,7 +1572,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2242
1572
|
}
|
|
2243
1573
|
else if (this.bridgeMode === 'bridge') {
|
|
2244
1574
|
if (device.mode === 'matter') {
|
|
2245
|
-
// Register and add the device to the matterbridge server node
|
|
2246
1575
|
this.log.debug(`Adding matter endpoint ${plg}${pluginName}${db}:${dev}${device.deviceName}${db} to Matterbridge server node...`);
|
|
2247
1576
|
if (!this.serverNode) {
|
|
2248
1577
|
this.log.error('Server node not found for Matterbridge');
|
|
@@ -2259,7 +1588,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2259
1588
|
}
|
|
2260
1589
|
}
|
|
2261
1590
|
else {
|
|
2262
|
-
// Register and add the device to the matterbridge aggregator node
|
|
2263
1591
|
this.log.debug(`Adding bridged endpoint ${plg}${pluginName}${db}:${dev}${device.deviceName}${db} to Matterbridge aggregator node`);
|
|
2264
1592
|
if (!this.aggregatorNode) {
|
|
2265
1593
|
this.log.error('Aggregator node not found for Matterbridge');
|
|
@@ -2277,7 +1605,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2277
1605
|
}
|
|
2278
1606
|
}
|
|
2279
1607
|
else if (this.bridgeMode === 'childbridge') {
|
|
2280
|
-
// Register and add the device to the plugin server node
|
|
2281
1608
|
if (plugin.type === 'AccessoryPlatform') {
|
|
2282
1609
|
try {
|
|
2283
1610
|
this.log.debug(`Creating endpoint ${dev}${device.deviceName}${db} for AccessoryPlatform plugin ${plg}${plugin.name}${db} server node`);
|
|
@@ -2301,12 +1628,10 @@ export class Matterbridge extends EventEmitter {
|
|
|
2301
1628
|
return;
|
|
2302
1629
|
}
|
|
2303
1630
|
}
|
|
2304
|
-
// Register and add the device to the plugin aggregator node
|
|
2305
1631
|
if (plugin.type === 'DynamicPlatform') {
|
|
2306
1632
|
try {
|
|
2307
1633
|
this.log.debug(`Adding bridged endpoint ${dev}${device.deviceName}${db} for DynamicPlatform plugin ${plg}${plugin.name}${db} aggregator node`);
|
|
2308
1634
|
await this.createDynamicPlugin(plugin);
|
|
2309
|
-
// Fast plugins can add another device before the server node is ready, so we wait for the server node to be ready
|
|
2310
1635
|
await waiter(`createDynamicPlugin(${plugin.name})`, () => plugin.serverNode?.hasParts === true);
|
|
2311
1636
|
if (!plugin.aggregatorNode) {
|
|
2312
1637
|
this.log.error(`Aggregator node not found for plugin ${plg}${plugin.name}${er}`);
|
|
@@ -2327,28 +1652,17 @@ export class Matterbridge extends EventEmitter {
|
|
|
2327
1652
|
}
|
|
2328
1653
|
if (plugin.registeredDevices !== undefined)
|
|
2329
1654
|
plugin.registeredDevices++;
|
|
2330
|
-
// Add the device to the DeviceManager
|
|
2331
1655
|
this.devices.set(device);
|
|
2332
|
-
// Subscribe to the attributes changed event
|
|
2333
1656
|
await this.subscribeAttributeChanged(plugin, device);
|
|
2334
1657
|
this.log.info(`Added and registered bridged endpoint (${plugin.registeredDevices}) ${dev}${device.deviceName}${nf} (${dev}${device.id}${nf}) for plugin ${plg}${pluginName}${nf}`);
|
|
2335
1658
|
}
|
|
2336
|
-
/**
|
|
2337
|
-
* Removes a MatterbridgeEndpoint from the specified plugin.
|
|
2338
|
-
*
|
|
2339
|
-
* @param {string} pluginName - The name of the plugin.
|
|
2340
|
-
* @param {MatterbridgeEndpoint} device - The device to remove as a bridged endpoint.
|
|
2341
|
-
* @returns {Promise<void>} A promise that resolves when the bridged endpoint has been removed.
|
|
2342
|
-
*/
|
|
2343
1659
|
async removeBridgedEndpoint(pluginName, device) {
|
|
2344
1660
|
this.log.debug(`Removing bridged endpoint ${plg}${pluginName}${db}:${dev}${device.deviceName}${db} (${zb}${device.name}${db}) for plugin ${plg}${pluginName}${db}`);
|
|
2345
|
-
// Check if the plugin is registered
|
|
2346
1661
|
const plugin = this.plugins.get(pluginName);
|
|
2347
1662
|
if (!plugin) {
|
|
2348
1663
|
this.log.error(`Error removing bridged endpoint ${dev}${device.deviceName}${er} (${zb}${device.name}${er}) for plugin ${plg}${pluginName}${er}: plugin not found`);
|
|
2349
1664
|
return;
|
|
2350
1665
|
}
|
|
2351
|
-
// Unregister and remove the device from the matterbridge aggregator node
|
|
2352
1666
|
if (this.bridgeMode === 'bridge') {
|
|
2353
1667
|
if (!this.aggregatorNode) {
|
|
2354
1668
|
this.log.error(`Error removing bridged endpoint ${dev}${device.deviceName}${er} (${zb}${device.name}${er}) for plugin ${plg}${pluginName}${er}: aggregator node not found`);
|
|
@@ -2361,10 +1675,8 @@ export class Matterbridge extends EventEmitter {
|
|
|
2361
1675
|
}
|
|
2362
1676
|
else if (this.bridgeMode === 'childbridge') {
|
|
2363
1677
|
if (plugin.type === 'AccessoryPlatform') {
|
|
2364
|
-
// Nothing to do here since the server node has no aggregator node but only the device itself
|
|
2365
1678
|
}
|
|
2366
1679
|
else if (plugin.type === 'DynamicPlatform') {
|
|
2367
|
-
// Unregister and remove the device from the plugin aggregator node
|
|
2368
1680
|
if (!plugin.aggregatorNode) {
|
|
2369
1681
|
this.log.error(`Error removing bridged endpoint ${dev}${device.deviceName}${er} (${zb}${device.name}${er}) for plugin ${plg}${pluginName}${er}: aggregator node not found`);
|
|
2370
1682
|
return;
|
|
@@ -2375,21 +1687,8 @@ export class Matterbridge extends EventEmitter {
|
|
|
2375
1687
|
if (plugin.registeredDevices !== undefined)
|
|
2376
1688
|
plugin.registeredDevices--;
|
|
2377
1689
|
}
|
|
2378
|
-
// Remove the device from the DeviceManager
|
|
2379
1690
|
this.devices.remove(device);
|
|
2380
1691
|
}
|
|
2381
|
-
/**
|
|
2382
|
-
* Removes all bridged endpoints from the specified plugin.
|
|
2383
|
-
*
|
|
2384
|
-
* @param {string} pluginName - The name of the plugin.
|
|
2385
|
-
* @param {number} [delay] - The delay in milliseconds between removing each bridged endpoint (default: 0).
|
|
2386
|
-
* @returns {Promise<void>} A promise that resolves when all bridged endpoints have been removed.
|
|
2387
|
-
*
|
|
2388
|
-
* @remarks
|
|
2389
|
-
* This method iterates through all devices in the DeviceManager and removes each bridged endpoint associated with the specified plugin.
|
|
2390
|
-
* It also applies a delay between each removal if specified.
|
|
2391
|
-
* The delay is useful to allow the controllers to receive a single subscription for each device removed.
|
|
2392
|
-
*/
|
|
2393
1692
|
async removeAllBridgedEndpoints(pluginName, delay = 0) {
|
|
2394
1693
|
this.log.debug(`Removing all bridged endpoints for plugin ${plg}${pluginName}${db}${delay > 0 ? ` with delay ${delay} ms` : ''}`);
|
|
2395
1694
|
for (const device of this.devices.array().filter((device) => device.plugin === pluginName)) {
|
|
@@ -2400,28 +1699,8 @@ export class Matterbridge extends EventEmitter {
|
|
|
2400
1699
|
if (delay > 0)
|
|
2401
1700
|
await wait(2000);
|
|
2402
1701
|
}
|
|
2403
|
-
/**
|
|
2404
|
-
* Registers a virtual device.
|
|
2405
|
-
* Virtual devices are only supported in bridge mode and childbridge mode with a DynamicPlatform.
|
|
2406
|
-
*
|
|
2407
|
-
* The virtual device is created as an instance of `Endpoint` with the provided device type.
|
|
2408
|
-
* When the virtual device is turned on, the provided callback function is executed.
|
|
2409
|
-
* The onOff state of the virtual device always reverts to false when the device is turned on.
|
|
2410
|
-
*
|
|
2411
|
-
* @param { string } pluginName - The name of the plugin to register the virtual device under.
|
|
2412
|
-
* @param { string } name - The name of the virtual device.
|
|
2413
|
-
* @param { 'light' | 'outlet' | 'switch' | 'mounted_switch' } type - The type of the virtual device.
|
|
2414
|
-
* @param { () => Promise<void> } callback - The callback to call when the virtual device is turned on.
|
|
2415
|
-
*
|
|
2416
|
-
* @returns {Promise<boolean>} A promise that resolves to true if the virtual device was successfully registered, false otherwise.
|
|
2417
|
-
*
|
|
2418
|
-
* @remarks
|
|
2419
|
-
* The virtual devices don't show up in the device list of the frontend.
|
|
2420
|
-
* Type 'switch' is not supported by Alexa and 'mounted_switch' is not supported by Apple Home.
|
|
2421
|
-
*/
|
|
2422
1702
|
async addVirtualEndpoint(pluginName, name, type, callback) {
|
|
2423
1703
|
this.log.debug(`Adding virtual endpoint ${plg}${pluginName}${db}:${dev}${name}${db}...`);
|
|
2424
|
-
// Check if the plugin is registered
|
|
2425
1704
|
const plugin = this.plugins.get(pluginName);
|
|
2426
1705
|
if (!plugin) {
|
|
2427
1706
|
this.log.error(`Error adding virtual endpoint ${dev}${name}${er} for plugin ${plg}${pluginName}${er}: plugin not found`);
|
|
@@ -2448,24 +1727,13 @@ export class Matterbridge extends EventEmitter {
|
|
|
2448
1727
|
this.log.error(`Virtual endpoint ${dev}${name}${er} for plugin ${plg}${pluginName}${er} not created. Virtual endpoints are only supported in bridge mode and childbridge mode with a DynamicPlatform.`);
|
|
2449
1728
|
return false;
|
|
2450
1729
|
}
|
|
2451
|
-
/**
|
|
2452
|
-
* Subscribes to the attribute change event for the given device and plugin.
|
|
2453
|
-
* Specifically, it listens for changes in the 'reachable' attribute of the
|
|
2454
|
-
* BridgedDeviceBasicInformationServer cluster server of the bridged device or BasicInformationServer cluster server of server node.
|
|
2455
|
-
*
|
|
2456
|
-
* @param {Plugin} plugin - The plugin associated with the device.
|
|
2457
|
-
* @param {MatterbridgeEndpoint} device - The device to subscribe to attribute changes for.
|
|
2458
|
-
* @returns {Promise<void>} A promise that resolves when the subscription is set up.
|
|
2459
|
-
*/
|
|
2460
1730
|
async subscribeAttributeChanged(plugin, device) {
|
|
2461
1731
|
if (!plugin || !device || !device.plugin || !device.serialNumber || !device.uniqueId || !device.maybeNumber)
|
|
2462
1732
|
return;
|
|
2463
1733
|
this.log.info(`Subscribing attributes for endpoint ${dev}${device.deviceName}${nf} (${dev}${device.id}${nf}) plugin ${plg}${plugin.name}${nf}`);
|
|
2464
|
-
// Subscribe to the reachable$Changed event of the BasicInformationServer cluster server of the server node in childbridge mode
|
|
2465
1734
|
if (this.bridgeMode === 'childbridge' && plugin.type === 'AccessoryPlatform' && plugin.serverNode) {
|
|
2466
1735
|
plugin.serverNode.eventsOf(BasicInformationServer).reachable$Changed?.on((reachable) => {
|
|
2467
1736
|
this.log.info(`Accessory endpoint ${dev}${device.deviceName}${nf} (${dev}${device.id}${nf}) is ${reachable ? 'reachable' : 'unreachable'}`);
|
|
2468
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
2469
1737
|
this.frontend.wssSendAttributeChangedMessage(device.plugin, device.serialNumber, device.uniqueId, device.number, device.id, 'BasicInformation', 'reachable', reachable);
|
|
2470
1738
|
});
|
|
2471
1739
|
}
|
|
@@ -2515,7 +1783,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2515
1783
|
this.log.debug(`Subscribing to endpoint ${or}${device.id}${db}:${or}${device.number}${db} attribute ${dev}${sub.cluster}${db}.${dev}${sub.attribute}${db} changes...`);
|
|
2516
1784
|
await device.subscribeAttribute(sub.cluster, sub.attribute, (value) => {
|
|
2517
1785
|
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}`);
|
|
2518
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
2519
1786
|
this.frontend.wssSendAttributeChangedMessage(device.plugin, device.serialNumber, device.uniqueId, device.number, device.id, sub.cluster, sub.attribute, value);
|
|
2520
1787
|
});
|
|
2521
1788
|
}
|
|
@@ -2524,19 +1791,12 @@ export class Matterbridge extends EventEmitter {
|
|
|
2524
1791
|
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...`);
|
|
2525
1792
|
await child.subscribeAttribute(sub.cluster, sub.attribute, (value) => {
|
|
2526
1793
|
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}`);
|
|
2527
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
2528
1794
|
this.frontend.wssSendAttributeChangedMessage(device.plugin, device.serialNumber, device.uniqueId, child.number, child.id, sub.cluster, sub.attribute, value);
|
|
2529
1795
|
});
|
|
2530
1796
|
}
|
|
2531
1797
|
}
|
|
2532
1798
|
}
|
|
2533
1799
|
}
|
|
2534
|
-
/**
|
|
2535
|
-
* Sanitizes the fabric information by converting bigint properties to strings because `res.json` doesn't support bigint.
|
|
2536
|
-
*
|
|
2537
|
-
* @param {ExposedFabricInformation[]} fabricInfo - The array of exposed fabric information objects.
|
|
2538
|
-
* @returns {SanitizedExposedFabricInformation[]} An array of sanitized exposed fabric information objects.
|
|
2539
|
-
*/
|
|
2540
1800
|
sanitizeFabricInformations(fabricInfo) {
|
|
2541
1801
|
return fabricInfo.map((info) => {
|
|
2542
1802
|
return {
|
|
@@ -2550,12 +1810,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2550
1810
|
};
|
|
2551
1811
|
});
|
|
2552
1812
|
}
|
|
2553
|
-
/**
|
|
2554
|
-
* Sanitizes the session information by converting bigint properties to strings because `res.json` doesn't support bigint.
|
|
2555
|
-
*
|
|
2556
|
-
* @param {SessionsBehavior.Session[]} sessions - The array of session information objects.
|
|
2557
|
-
* @returns {SanitizedSession[]} An array of sanitized session information objects.
|
|
2558
|
-
*/
|
|
2559
1813
|
sanitizeSessionInformation(sessions) {
|
|
2560
1814
|
return sessions
|
|
2561
1815
|
.filter((session) => session.isPeerActive)
|
|
@@ -2582,21 +1836,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
2582
1836
|
};
|
|
2583
1837
|
});
|
|
2584
1838
|
}
|
|
2585
|
-
/**
|
|
2586
|
-
* Sets the reachability of the specified aggregator node bridged devices and trigger.
|
|
2587
|
-
*
|
|
2588
|
-
* @param {Endpoint<AggregatorEndpoint>} aggregatorNode - The aggregator node to set the reachability for.
|
|
2589
|
-
* @param {boolean} reachable - A boolean indicating the reachability status to set.
|
|
2590
|
-
*/
|
|
2591
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
2592
1839
|
async setAggregatorReachability(aggregatorNode, reachable) {
|
|
2593
|
-
/*
|
|
2594
|
-
for (const child of aggregatorNode.parts) {
|
|
2595
|
-
this.log.debug(`Setting reachability of ${(child as unknown as MatterbridgeEndpoint)?.deviceName} to ${reachable}`);
|
|
2596
|
-
await child.setStateOf(BridgedDeviceBasicInformationServer, { reachable });
|
|
2597
|
-
child.act((agent) => child.eventsOf(BridgedDeviceBasicInformationServer).reachableChanged.emit({ reachableNewValue: true }, agent.context));
|
|
2598
|
-
}
|
|
2599
|
-
*/
|
|
2600
1840
|
}
|
|
2601
1841
|
getVendorIdName = (vendorId) => {
|
|
2602
1842
|
if (!vendorId)
|
|
@@ -2636,11 +1876,10 @@ export class Matterbridge extends EventEmitter {
|
|
|
2636
1876
|
case 0x1488:
|
|
2637
1877
|
vendorName = '(ShortcutLabsFlic)';
|
|
2638
1878
|
break;
|
|
2639
|
-
case 65521:
|
|
1879
|
+
case 65521:
|
|
2640
1880
|
vendorName = '(MatterTest)';
|
|
2641
1881
|
break;
|
|
2642
1882
|
}
|
|
2643
1883
|
return vendorName;
|
|
2644
1884
|
};
|
|
2645
1885
|
}
|
|
2646
|
-
//# sourceMappingURL=matterbridge.js.map
|