matterbridge 3.4.0 → 3.4.1-dev-20251127-826b2bf
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 +128 -112
- package/README-DEV.md +2 -2
- package/README-DOCKER.md +1 -1
- package/README-MACOS-PLIST.md +1 -1
- package/README-NGINX.md +1 -1
- package/README-PODMAN.md +1 -1
- package/README-SERVICE-LOCAL.md +1 -1
- package/README-SERVICE-OPT.md +6 -1
- package/README-SERVICE.md +1 -1
- package/README.md +57 -49
- package/dist/broadcastServer.js +1 -93
- package/dist/broadcastServerTypes.js +0 -24
- package/dist/cli.js +1 -97
- package/dist/cliEmitter.js +0 -37
- package/dist/cliHistory.js +0 -38
- package/dist/clusters/export.js +0 -2
- package/dist/defaultConfigSchema.js +0 -24
- package/dist/deviceManager.js +1 -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 -352
- 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 +74 -788
- 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 +2 -460
- package/dist/matterbridgeTypes.js +0 -26
- package/dist/pluginManager.js +5 -340
- package/dist/shelly.js +7 -168
- package/dist/storage/export.js +0 -1
- package/dist/update.js +0 -69
- package/dist/utils/colorUtils.js +2 -97
- package/dist/utils/commandLine.js +0 -60
- package/dist/utils/copyDirectory.js +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 -41
- package/dist/utils/export.js +0 -1
- package/dist/utils/format.js +0 -49
- package/dist/utils/hex.js +0 -124
- package/dist/utils/inspector.js +1 -69
- package/dist/utils/isvalid.js +0 -101
- package/dist/utils/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/frontend/build/assets/index.js +4 -4
- package/frontend/build/assets/vendor_mui.js +1 -1
- package/frontend/build/assets/vendor_node_modules.js +19 -83
- package/frontend/build/assets/vendor_qrcode.js +1 -9
- package/frontend/build/assets/vendor_rjsf.js +1 -9
- package/frontend/package-lock.json +229 -439
- package/frontend/package.json +15 -15
- package/marked.ps1 +15 -0
- package/npm-shrinkwrap.json +165 -231
- package/package.json +2 -3
- package/dist/broadcastServer.d.ts +0 -115
- package/dist/broadcastServer.d.ts.map +0 -1
- package/dist/broadcastServer.js.map +0 -1
- package/dist/broadcastServerTypes.d.ts +0 -838
- package/dist/broadcastServerTypes.d.ts.map +0 -1
- package/dist/broadcastServerTypes.js.map +0 -1
- package/dist/cli.d.ts +0 -30
- package/dist/cli.d.ts.map +0 -1
- package/dist/cli.js.map +0 -1
- package/dist/cliEmitter.d.ts +0 -50
- package/dist/cliEmitter.d.ts.map +0 -1
- package/dist/cliEmitter.js.map +0 -1
- package/dist/cliHistory.d.ts +0 -48
- package/dist/cliHistory.d.ts.map +0 -1
- package/dist/cliHistory.js.map +0 -1
- package/dist/clusters/export.d.ts +0 -2
- package/dist/clusters/export.d.ts.map +0 -1
- package/dist/clusters/export.js.map +0 -1
- package/dist/defaultConfigSchema.d.ts +0 -28
- package/dist/defaultConfigSchema.d.ts.map +0 -1
- package/dist/defaultConfigSchema.js.map +0 -1
- package/dist/deviceManager.d.ts +0 -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 -303
- 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 -473
- 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 -524
- 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 -371
- package/dist/pluginManager.d.ts.map +0 -1
- package/dist/pluginManager.js.map +0 -1
- package/dist/shelly.d.ts +0 -174
- package/dist/shelly.d.ts.map +0 -1
- package/dist/shelly.js.map +0 -1
- package/dist/storage/export.d.ts +0 -2
- package/dist/storage/export.d.ts.map +0 -1
- package/dist/storage/export.js.map +0 -1
- package/dist/update.d.ts +0 -75
- package/dist/update.d.ts.map +0 -1
- package/dist/update.js.map +0 -1
- package/dist/utils/colorUtils.d.ts +0 -101
- package/dist/utils/colorUtils.d.ts.map +0 -1
- package/dist/utils/colorUtils.js.map +0 -1
- package/dist/utils/commandLine.d.ts +0 -66
- package/dist/utils/commandLine.d.ts.map +0 -1
- package/dist/utils/commandLine.js.map +0 -1
- package/dist/utils/copyDirectory.d.ts +0 -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 -44
- package/dist/utils/error.d.ts.map +0 -1
- package/dist/utils/error.js.map +0 -1
- package/dist/utils/export.d.ts +0 -13
- package/dist/utils/export.d.ts.map +0 -1
- package/dist/utils/export.js.map +0 -1
- package/dist/utils/format.d.ts +0 -53
- package/dist/utils/format.d.ts.map +0 -1
- package/dist/utils/format.js.map +0 -1
- package/dist/utils/hex.d.ts +0 -89
- package/dist/utils/hex.d.ts.map +0 -1
- package/dist/utils/hex.js.map +0 -1
- package/dist/utils/inspector.d.ts +0 -87
- package/dist/utils/inspector.d.ts.map +0 -1
- package/dist/utils/inspector.js.map +0 -1
- package/dist/utils/isvalid.d.ts +0 -103
- package/dist/utils/isvalid.d.ts.map +0 -1
- package/dist/utils/isvalid.js.map +0 -1
- package/dist/utils/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/matterbridge.js
CHANGED
|
@@ -1,48 +1,19 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* This file contains the class Matterbridge.
|
|
3
|
-
*
|
|
4
|
-
* @file matterbridge.ts
|
|
5
|
-
* @author Luca Liguori
|
|
6
|
-
* @created 2023-12-29
|
|
7
|
-
* @version 1.6.0
|
|
8
|
-
* @license Apache-2.0
|
|
9
|
-
*
|
|
10
|
-
* Copyright 2023, 2024, 2025 Luca Liguori.
|
|
11
|
-
*
|
|
12
|
-
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
13
|
-
* you may not use this file except in compliance with the License.
|
|
14
|
-
* You may obtain a copy of the License at
|
|
15
|
-
*
|
|
16
|
-
* http://www.apache.org/licenses/LICENSE-2.0
|
|
17
|
-
*
|
|
18
|
-
* Unless required by applicable law or agreed to in writing, software
|
|
19
|
-
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
20
|
-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
21
|
-
* See the License for the specific language governing permissions and
|
|
22
|
-
* limitations under the License.
|
|
23
|
-
*/
|
|
24
|
-
// eslint-disable-next-line no-console
|
|
25
1
|
if (process.argv.includes('--loader') || process.argv.includes('-loader'))
|
|
26
2
|
console.log('\u001B[32mMatterbridge loaded.\u001B[40;0m');
|
|
27
|
-
// Node.js modules
|
|
28
3
|
import os from 'node:os';
|
|
29
4
|
import path from 'node:path';
|
|
30
5
|
import fs from 'node:fs';
|
|
31
6
|
import EventEmitter from 'node:events';
|
|
32
7
|
import { inspect } from 'node:util';
|
|
33
|
-
// AnsiLogger module
|
|
34
8
|
import { AnsiLogger, UNDERLINE, UNDERLINEOFF, db, debugStringify, BRIGHT, RESET, er, nf, rs, wr, RED, GREEN, zb, CYAN, nt, BLUE, or } from 'node-ansi-logger';
|
|
35
|
-
// NodeStorage module
|
|
36
9
|
import { NodeStorageManager } from 'node-persist-manager';
|
|
37
|
-
|
|
38
|
-
import '@matter/nodejs'; // Set up Node.js environment for matter.js
|
|
10
|
+
import '@matter/nodejs';
|
|
39
11
|
import { Logger, LogLevel as MatterLogLevel, LogFormat as MatterLogFormat, UINT32_MAX, UINT16_MAX, Crypto, Environment, StorageService } from '@matter/general';
|
|
40
12
|
import { FabricAction, PaseClient } from '@matter/protocol';
|
|
41
13
|
import { Endpoint, ServerNode } from '@matter/node';
|
|
42
14
|
import { DeviceTypeId, VendorId } from '@matter/types/datatype';
|
|
43
15
|
import { AggregatorEndpoint } from '@matter/node/endpoints';
|
|
44
16
|
import { BasicInformationServer } from '@matter/node/behaviors/basic-information';
|
|
45
|
-
// Matterbridge
|
|
46
17
|
import { getParameter, getIntParameter, hasParameter } from './utils/commandLine.js';
|
|
47
18
|
import { copyDirectory } from './utils/copyDirectory.js';
|
|
48
19
|
import { createDirectory } from './utils/createDirectory.js';
|
|
@@ -55,29 +26,21 @@ import { DeviceManager } from './deviceManager.js';
|
|
|
55
26
|
import { MatterbridgeEndpoint } from './matterbridgeEndpoint.js';
|
|
56
27
|
import { bridge } from './matterbridgeDeviceTypes.js';
|
|
57
28
|
import { Frontend } from './frontend.js';
|
|
58
|
-
import { addVirtualDevices } from './helpers.js';
|
|
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';
|
|
@@ -229,19 +162,8 @@ export class Matterbridge extends EventEmitter {
|
|
|
229
162
|
}
|
|
230
163
|
}
|
|
231
164
|
}
|
|
232
|
-
//* ************************************************************************************************************************************ */
|
|
233
|
-
// loadInstance() and cleanup() methods */
|
|
234
|
-
//* ************************************************************************************************************************************ */
|
|
235
|
-
/**
|
|
236
|
-
* Loads an instance of the Matterbridge class.
|
|
237
|
-
* If an instance already exists, return that instance.
|
|
238
|
-
*
|
|
239
|
-
* @param {boolean} initialize - Whether to initialize the Matterbridge instance after loading. Defaults to false.
|
|
240
|
-
* @returns {Matterbridge} A promise that resolves to the Matterbridge instance.
|
|
241
|
-
*/
|
|
242
165
|
static async loadInstance(initialize = false) {
|
|
243
166
|
if (!Matterbridge.instance) {
|
|
244
|
-
// eslint-disable-next-line no-console
|
|
245
167
|
if (hasParameter('debug'))
|
|
246
168
|
console.log(GREEN + 'Creating a new instance of Matterbridge.', initialize ? 'Initializing...' : 'Not initializing...', rs);
|
|
247
169
|
Matterbridge.instance = new Matterbridge();
|
|
@@ -250,84 +172,56 @@ export class Matterbridge extends EventEmitter {
|
|
|
250
172
|
}
|
|
251
173
|
return Matterbridge.instance;
|
|
252
174
|
}
|
|
253
|
-
/**
|
|
254
|
-
* Initializes the Matterbridge application.
|
|
255
|
-
*
|
|
256
|
-
* @remarks
|
|
257
|
-
* This method performs the necessary setup and initialization steps for the Matterbridge application.
|
|
258
|
-
* It displays the help information if the 'help' parameter is provided, sets up the logger, checks the
|
|
259
|
-
* node version, registers signal handlers, initializes storage, and parses the command line.
|
|
260
|
-
*
|
|
261
|
-
* @returns {Promise<void>} A Promise that resolves when the initialization is complete.
|
|
262
|
-
*/
|
|
263
175
|
async initialize() {
|
|
264
|
-
// for (let i = 1; i <= 255; i++) console.log(`\x1b[38;5;${i}mColor: ${i}`);
|
|
265
|
-
// Emit the initialize_started event
|
|
266
176
|
this.emit('initialize_started');
|
|
267
|
-
// Set the restart mode
|
|
268
177
|
if (hasParameter('service'))
|
|
269
178
|
this.restartMode = 'service';
|
|
270
179
|
if (hasParameter('docker'))
|
|
271
180
|
this.restartMode = 'docker';
|
|
272
|
-
// Set the matterbridge home directory
|
|
273
181
|
this.homeDirectory = getParameter('homedir') ?? os.homedir();
|
|
274
182
|
await createDirectory(this.homeDirectory, 'Matterbridge Home Directory', this.log);
|
|
275
|
-
// Set the matterbridge directory
|
|
276
183
|
this.matterbridgeDirectory = this.profile ? path.join(this.homeDirectory, '.matterbridge', 'profiles', this.profile) : path.join(this.homeDirectory, '.matterbridge');
|
|
277
184
|
await createDirectory(this.matterbridgeDirectory, 'Matterbridge Directory', this.log);
|
|
278
185
|
await createDirectory(path.join(this.matterbridgeDirectory, 'certs'), 'Matterbridge Frontend Certificate Directory', this.log);
|
|
279
186
|
await createDirectory(path.join(this.matterbridgeDirectory, 'uploads'), 'Matterbridge Frontend Uploads Directory', this.log);
|
|
280
|
-
// Set the matterbridge plugin directory
|
|
281
187
|
this.matterbridgePluginDirectory = this.profile ? path.join(this.homeDirectory, 'Matterbridge', 'profiles', this.profile) : path.join(this.homeDirectory, 'Matterbridge');
|
|
282
188
|
await createDirectory(this.matterbridgePluginDirectory, 'Matterbridge Plugin Directory', this.log);
|
|
283
|
-
// Set the matterbridge cert directory
|
|
284
189
|
this.matterbridgeCertDirectory = this.profile ? path.join(this.homeDirectory, '.mattercert', 'profiles', this.profile) : path.join(this.homeDirectory, '.mattercert');
|
|
285
190
|
await createDirectory(this.matterbridgeCertDirectory, 'Matterbridge Matter Certificate Directory', this.log);
|
|
286
|
-
// Set the matterbridge root directory
|
|
287
191
|
const { fileURLToPath } = await import('node:url');
|
|
288
192
|
const currentFileDirectory = path.dirname(fileURLToPath(import.meta.url));
|
|
289
|
-
this.rootDirectory = path.resolve(currentFileDirectory, '../');
|
|
290
|
-
// Setup the matter environment with default values
|
|
193
|
+
this.rootDirectory = path.resolve(currentFileDirectory, '../');
|
|
291
194
|
this.environment.vars.set('log.level', MatterLogLevel.INFO);
|
|
292
195
|
this.environment.vars.set('log.format', MatterLogFormat.ANSI);
|
|
293
196
|
this.environment.vars.set('path.root', path.join(this.matterbridgeDirectory, MATTER_STORAGE_NAME));
|
|
294
197
|
this.environment.vars.set('runtime.signals', false);
|
|
295
198
|
this.environment.vars.set('runtime.exitcode', false);
|
|
296
|
-
// Register process handlers
|
|
297
199
|
this.registerProcessHandlers();
|
|
298
|
-
// Initialize nodeStorage and nodeContext
|
|
299
200
|
try {
|
|
300
201
|
this.log.debug(`Creating node storage manager: ${CYAN}${NODE_STORAGE_DIR}${db}`);
|
|
301
202
|
this.nodeStorage = new NodeStorageManager({ dir: path.join(this.matterbridgeDirectory, NODE_STORAGE_DIR), writeQueue: false, expiredInterval: undefined, logging: false });
|
|
302
203
|
this.log.debug('Creating node storage context for matterbridge');
|
|
303
204
|
this.nodeContext = await this.nodeStorage.createStorage('matterbridge');
|
|
304
|
-
// TODO: Remove this code when node-persist-manager is updated
|
|
305
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
306
205
|
const keys = (await this.nodeStorage?.storage.keys());
|
|
307
206
|
for (const key of keys) {
|
|
308
207
|
this.log.debug(`Checking node storage manager key: ${CYAN}${key}${db}`);
|
|
309
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
310
208
|
await this.nodeStorage?.storage.get(key);
|
|
311
209
|
}
|
|
312
210
|
const storages = await this.nodeStorage.getStorageNames();
|
|
313
211
|
for (const storage of storages) {
|
|
314
212
|
this.log.debug(`Checking storage: ${CYAN}${storage}${db}`);
|
|
315
213
|
const nodeContext = await this.nodeStorage?.createStorage(storage);
|
|
316
|
-
// TODO: Remove this code when node-persist-manager is updated
|
|
317
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
318
214
|
const keys = (await nodeContext?.storage.keys());
|
|
319
215
|
keys.forEach(async (key) => {
|
|
320
216
|
this.log.debug(`Checking key: ${CYAN}${storage}:${key}${db}`);
|
|
321
217
|
await nodeContext?.get(key);
|
|
322
218
|
});
|
|
323
219
|
}
|
|
324
|
-
// Creating a backup of the node storage since it is not corrupted
|
|
325
220
|
this.log.debug('Creating node storage backup...');
|
|
326
221
|
await copyDirectory(path.join(this.matterbridgeDirectory, NODE_STORAGE_DIR), path.join(this.matterbridgeDirectory, NODE_STORAGE_DIR + '.backup'));
|
|
327
222
|
this.log.debug('Created node storage backup');
|
|
328
223
|
}
|
|
329
224
|
catch (error) {
|
|
330
|
-
// Restoring the backup of the node storage since it is corrupted
|
|
331
225
|
this.log.error(`Error creating node storage manager and context: ${error instanceof Error ? error.message : error}`);
|
|
332
226
|
if (hasParameter('norestore')) {
|
|
333
227
|
this.log.fatal(`The matterbridge storage is corrupted. Found -norestore parameter: exiting...`);
|
|
@@ -341,19 +235,14 @@ export class Matterbridge extends EventEmitter {
|
|
|
341
235
|
if (!this.nodeStorage || !this.nodeContext) {
|
|
342
236
|
throw new Error('Fatal error creating node storage manager and context for matterbridge');
|
|
343
237
|
}
|
|
344
|
-
// Set the first port to use for the commissioning server (will be incremented in childbridge mode)
|
|
345
238
|
this.port = getIntParameter('port') ?? (await this.nodeContext.get('matterport', 5540)) ?? 5540;
|
|
346
|
-
// Set the first passcode to use for the commissioning server (will be incremented in childbridge mode)
|
|
347
239
|
this.passcode = getIntParameter('passcode') ?? (await this.nodeContext.get('matterpasscode')) ?? PaseClient.generateRandomPasscode(this.environment.get(Crypto));
|
|
348
|
-
// Set the first discriminator to use for the commissioning server (will be incremented in childbridge mode)
|
|
349
240
|
this.discriminator = getIntParameter('discriminator') ?? (await this.nodeContext.get('matterdiscriminator')) ?? PaseClient.generateRandomDiscriminator(this.environment.get(Crypto));
|
|
350
|
-
// Certificate management
|
|
351
241
|
const pairingFilePath = path.join(this.matterbridgeCertDirectory, 'pairing.json');
|
|
352
242
|
try {
|
|
353
243
|
await fs.promises.access(pairingFilePath, fs.constants.R_OK);
|
|
354
244
|
const pairingFileContent = await fs.promises.readFile(pairingFilePath, 'utf8');
|
|
355
245
|
const pairingFileJson = JSON.parse(pairingFileContent);
|
|
356
|
-
// Set the vendorId, vendorName, productId, productName, deviceType, serialNumber, uniqueId if they are present in the pairing file
|
|
357
246
|
if (isValidNumber(pairingFileJson.vendorId)) {
|
|
358
247
|
this.aggregatorVendorId = VendorId(pairingFileJson.vendorId);
|
|
359
248
|
this.log.info(`Pairing file ${CYAN}${pairingFilePath}${nf} found. Using vendorId ${CYAN}${this.aggregatorVendorId}${nf} from pairing file.`);
|
|
@@ -382,13 +271,11 @@ export class Matterbridge extends EventEmitter {
|
|
|
382
271
|
this.aggregatorUniqueId = pairingFileJson.uniqueId;
|
|
383
272
|
this.log.info(`Pairing file ${CYAN}${pairingFilePath}${nf} found. Using uniqueId ${CYAN}${this.aggregatorUniqueId}${nf} from pairing file.`);
|
|
384
273
|
}
|
|
385
|
-
// Override the passcode and discriminator if they are present in the pairing file
|
|
386
274
|
if (isValidNumber(pairingFileJson.passcode) && isValidNumber(pairingFileJson.discriminator)) {
|
|
387
275
|
this.passcode = pairingFileJson.passcode;
|
|
388
276
|
this.discriminator = pairingFileJson.discriminator;
|
|
389
277
|
this.log.info(`Pairing file ${CYAN}${pairingFilePath}${nf} found. Using passcode ${CYAN}${this.passcode}${nf} and discriminator ${CYAN}${this.discriminator}${nf} from pairing file.`);
|
|
390
278
|
}
|
|
391
|
-
// Set the certification for matter.js if it is present in the pairing file
|
|
392
279
|
if (pairingFileJson.privateKey && pairingFileJson.certificate && pairingFileJson.intermediateCertificate && pairingFileJson.declaration) {
|
|
393
280
|
const { hexToBuffer } = await import('./utils/hex.js');
|
|
394
281
|
this.certification = {
|
|
@@ -403,44 +290,41 @@ export class Matterbridge extends EventEmitter {
|
|
|
403
290
|
catch (error) {
|
|
404
291
|
this.log.debug(`Pairing file ${CYAN}${pairingFilePath}${db} not found: ${error instanceof Error ? error.message : error}`);
|
|
405
292
|
}
|
|
406
|
-
// Store the passcode, discriminator and port in the node context
|
|
407
293
|
await this.nodeContext.set('matterport', this.port);
|
|
408
294
|
await this.nodeContext.set('matterpasscode', this.passcode);
|
|
409
295
|
await this.nodeContext.set('matterdiscriminator', this.discriminator);
|
|
410
296
|
this.log.debug(`Initializing server node for Matterbridge on port ${this.port} with passcode ${this.passcode} and discriminator ${this.discriminator}`);
|
|
411
|
-
// Set matterbridge logger level (context: matterbridgeLogLevel)
|
|
412
297
|
if (hasParameter('logger')) {
|
|
413
298
|
const level = getParameter('logger');
|
|
414
299
|
if (level === 'debug') {
|
|
415
|
-
this.log.logLevel = "debug"
|
|
300
|
+
this.log.logLevel = "debug";
|
|
416
301
|
}
|
|
417
302
|
else if (level === 'info') {
|
|
418
|
-
this.log.logLevel = "info"
|
|
303
|
+
this.log.logLevel = "info";
|
|
419
304
|
}
|
|
420
305
|
else if (level === 'notice') {
|
|
421
|
-
this.log.logLevel = "notice"
|
|
306
|
+
this.log.logLevel = "notice";
|
|
422
307
|
}
|
|
423
308
|
else if (level === 'warn') {
|
|
424
|
-
this.log.logLevel = "warn"
|
|
309
|
+
this.log.logLevel = "warn";
|
|
425
310
|
}
|
|
426
311
|
else if (level === 'error') {
|
|
427
|
-
this.log.logLevel = "error"
|
|
312
|
+
this.log.logLevel = "error";
|
|
428
313
|
}
|
|
429
314
|
else if (level === 'fatal') {
|
|
430
|
-
this.log.logLevel = "fatal"
|
|
315
|
+
this.log.logLevel = "fatal";
|
|
431
316
|
}
|
|
432
317
|
else {
|
|
433
318
|
this.log.warn(`Invalid matterbridge logger level: ${level}. Using default level "info".`);
|
|
434
|
-
this.log.logLevel = "info"
|
|
319
|
+
this.log.logLevel = "info";
|
|
435
320
|
}
|
|
436
321
|
}
|
|
437
322
|
else {
|
|
438
|
-
this.log.logLevel = await this.nodeContext.get('matterbridgeLogLevel', this.shellyBoard ? "notice"
|
|
323
|
+
this.log.logLevel = await this.nodeContext.get('matterbridgeLogLevel', this.shellyBoard ? "notice" : "info");
|
|
439
324
|
}
|
|
440
325
|
this.logLevel = this.log.logLevel;
|
|
441
326
|
this.frontend.logLevel = this.log.logLevel;
|
|
442
327
|
MatterbridgeEndpoint.logLevel = this.log.logLevel;
|
|
443
|
-
// Create the file logger for matterbridge (context: matterbridgeFileLog)
|
|
444
328
|
if (hasParameter('filelogger') || (await this.nodeContext.get('matterbridgeFileLog', false))) {
|
|
445
329
|
AnsiLogger.setGlobalLogfile(path.join(this.matterbridgeDirectory, MATTERBRIDGE_LOGGER_FILE), this.log.logLevel, true);
|
|
446
330
|
this.fileLogger = true;
|
|
@@ -449,7 +333,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
449
333
|
this.log.debug(`Matterbridge logLevel: ${this.log.logLevel} fileLoger: ${this.fileLogger}.`);
|
|
450
334
|
if (this.profile !== undefined)
|
|
451
335
|
this.log.debug(`Matterbridge profile: ${this.profile}.`);
|
|
452
|
-
// Set matter.js logger level, format and logger (context: matterLogLevel)
|
|
453
336
|
if (hasParameter('matterlogger')) {
|
|
454
337
|
const level = getParameter('matterlogger');
|
|
455
338
|
if (level === 'debug') {
|
|
@@ -480,13 +363,11 @@ export class Matterbridge extends EventEmitter {
|
|
|
480
363
|
}
|
|
481
364
|
Logger.format = MatterLogFormat.ANSI;
|
|
482
365
|
this.matterLogLevel = MatterLogLevel.names[Logger.level];
|
|
483
|
-
// Create the logger for matter.js with file logging (context: matterFileLog)
|
|
484
366
|
if (hasParameter('matterfilelogger') || (await this.nodeContext.get('matterFileLog', false))) {
|
|
485
367
|
this.matterFileLogger = true;
|
|
486
368
|
}
|
|
487
369
|
Logger.destinations.default.write = this.createDestinationMatterLogger(this.matterFileLogger);
|
|
488
370
|
this.log.debug(`Matter logLevel: ${this.matterLogLevel} fileLoger: ${this.matterFileLogger}.`);
|
|
489
|
-
// Log network interfaces
|
|
490
371
|
const networkInterfaces = os.networkInterfaces();
|
|
491
372
|
const availableAddresses = Object.entries(networkInterfaces);
|
|
492
373
|
const availableInterfaceNames = Object.keys(networkInterfaces);
|
|
@@ -499,7 +380,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
499
380
|
});
|
|
500
381
|
}
|
|
501
382
|
}
|
|
502
|
-
// Set the interface to use for matter server node mdnsInterface
|
|
503
383
|
if (hasParameter('mdnsinterface')) {
|
|
504
384
|
this.mdnsInterface = getParameter('mdnsinterface');
|
|
505
385
|
}
|
|
@@ -508,7 +388,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
508
388
|
if (this.mdnsInterface === '')
|
|
509
389
|
this.mdnsInterface = undefined;
|
|
510
390
|
}
|
|
511
|
-
// Validate mdnsInterface
|
|
512
391
|
if (this.mdnsInterface) {
|
|
513
392
|
if (!availableInterfaceNames.includes(this.mdnsInterface)) {
|
|
514
393
|
this.log.error(`Invalid mdnsinterface: ${this.mdnsInterface}. Available interfaces are: ${availableInterfaceNames.join(', ')}. Using all available interfaces.`);
|
|
@@ -521,7 +400,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
521
400
|
}
|
|
522
401
|
if (this.mdnsInterface)
|
|
523
402
|
this.environment.vars.set('mdns.networkInterface', this.mdnsInterface);
|
|
524
|
-
// Set the listeningAddressIpv4 for the matter commissioning server
|
|
525
403
|
if (hasParameter('ipv4address')) {
|
|
526
404
|
this.ipv4Address = getParameter('ipv4address');
|
|
527
405
|
}
|
|
@@ -530,7 +408,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
530
408
|
if (this.ipv4Address === '')
|
|
531
409
|
this.ipv4Address = undefined;
|
|
532
410
|
}
|
|
533
|
-
// Validate ipv4address
|
|
534
411
|
if (this.ipv4Address) {
|
|
535
412
|
let isValid = false;
|
|
536
413
|
for (const [ifaceName, ifaces] of availableAddresses) {
|
|
@@ -546,7 +423,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
546
423
|
await this.nodeContext.remove('matteripv4address');
|
|
547
424
|
}
|
|
548
425
|
}
|
|
549
|
-
// Set the listeningAddressIpv6 for the matter commissioning server
|
|
550
426
|
if (hasParameter('ipv6address')) {
|
|
551
427
|
this.ipv6Address = getParameter('ipv6address');
|
|
552
428
|
}
|
|
@@ -555,7 +431,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
555
431
|
if (this.ipv6Address === '')
|
|
556
432
|
this.ipv6Address = undefined;
|
|
557
433
|
}
|
|
558
|
-
// Validate ipv6address
|
|
559
434
|
if (this.ipv6Address) {
|
|
560
435
|
let isValid = false;
|
|
561
436
|
for (const [ifaceName, ifaces] of availableAddresses) {
|
|
@@ -564,7 +439,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
564
439
|
isValid = true;
|
|
565
440
|
break;
|
|
566
441
|
}
|
|
567
|
-
/* istanbul ignore next */
|
|
568
442
|
if (ifaces && ifaces.find((iface) => iface.scopeid && iface.scopeid > 0 && iface.address + '%' + (process.platform === 'win32' ? iface.scopeid : ifaceName) === this.ipv6Address)) {
|
|
569
443
|
this.log.info(`Using ipv6address ${CYAN}${this.ipv6Address}${nf} on interface ${CYAN}${ifaceName}${nf} for the Matter server node.`);
|
|
570
444
|
isValid = true;
|
|
@@ -577,7 +451,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
577
451
|
await this.nodeContext.remove('matteripv6address');
|
|
578
452
|
}
|
|
579
453
|
}
|
|
580
|
-
// Initialize the virtual mode
|
|
581
454
|
if (hasParameter('novirtual')) {
|
|
582
455
|
this.virtualMode = 'disabled';
|
|
583
456
|
await this.nodeContext.set('virtualmode', 'disabled');
|
|
@@ -586,15 +459,10 @@ export class Matterbridge extends EventEmitter {
|
|
|
586
459
|
this.virtualMode = (await this.nodeContext.get('virtualmode', 'outlet'));
|
|
587
460
|
}
|
|
588
461
|
this.log.debug(`Virtual mode ${this.virtualMode}.`);
|
|
589
|
-
// Initialize PluginManager
|
|
590
462
|
this.plugins.logLevel = this.log.logLevel;
|
|
591
463
|
await this.plugins.loadFromStorage();
|
|
592
|
-
// Initialize DeviceManager
|
|
593
464
|
this.devices.logLevel = this.log.logLevel;
|
|
594
|
-
// Get the plugins from node storage and create the plugins node storage contexts
|
|
595
465
|
for (const plugin of this.plugins) {
|
|
596
|
-
// Try to reinstall the plugin from npm (for Docker pull and external plugins)
|
|
597
|
-
// We don't do this when the add and other shutdown parameters are set because we shut down the process after adding the plugin
|
|
598
466
|
if (!fs.existsSync(plugin.path) && !hasParameter('add') && !hasParameter('remove') && !hasParameter('enable') && !hasParameter('disable') && !hasParameter('reset') && !hasParameter('factoryreset')) {
|
|
599
467
|
this.log.info(`Error parsing plugin ${plg}${plugin.name}${nf}. Trying to reinstall it from npm...`);
|
|
600
468
|
const { spawnCommand } = await import('./utils/spawn.js');
|
|
@@ -624,7 +492,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
624
492
|
await plugin.nodeContext.set('description', plugin.description);
|
|
625
493
|
await plugin.nodeContext.set('author', plugin.author);
|
|
626
494
|
}
|
|
627
|
-
// Log system info and create .matterbridge directory
|
|
628
495
|
await this.logNodeAndSystemInfo();
|
|
629
496
|
this.log.notice(`Matterbridge version ${this.matterbridgeVersion} ` +
|
|
630
497
|
`${hasParameter('bridge') || (!hasParameter('childbridge') && (await this.nodeContext?.get('bridgeMode', '')) === 'bridge') ? 'mode bridge ' : ''}` +
|
|
@@ -632,7 +499,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
632
499
|
`${hasParameter('controller') ? 'mode controller ' : ''}` +
|
|
633
500
|
`${this.restartMode !== '' ? 'restart mode ' + this.restartMode + ' ' : ''}` +
|
|
634
501
|
`running on ${this.systemInformation.osType} (v.${this.systemInformation.osRelease}) platform ${this.systemInformation.osPlatform} arch ${this.systemInformation.osArch}`);
|
|
635
|
-
// Check node version and throw error
|
|
636
502
|
const minNodeVersion = 20;
|
|
637
503
|
const nodeVersion = process.versions.node;
|
|
638
504
|
const versionMajor = parseInt(nodeVersion.split('.')[0]);
|
|
@@ -640,18 +506,10 @@ export class Matterbridge extends EventEmitter {
|
|
|
640
506
|
this.log.error(`Node version ${versionMajor} is not supported. Please upgrade to ${minNodeVersion} or above.`);
|
|
641
507
|
throw new Error(`Node version ${versionMajor} is not supported. Please upgrade to ${minNodeVersion} or above.`);
|
|
642
508
|
}
|
|
643
|
-
// Parse command line
|
|
644
509
|
await this.parseCommandLine();
|
|
645
|
-
// Emit the initialize_completed event
|
|
646
510
|
this.emit('initialize_completed');
|
|
647
511
|
this.initialized = true;
|
|
648
512
|
}
|
|
649
|
-
/**
|
|
650
|
-
* Parses the command line arguments and performs the corresponding actions.
|
|
651
|
-
*
|
|
652
|
-
* @private
|
|
653
|
-
* @returns {Promise<void>} A promise that resolves when the command line arguments have been processed, or the process exits.
|
|
654
|
-
*/
|
|
655
513
|
async parseCommandLine() {
|
|
656
514
|
if (hasParameter('list')) {
|
|
657
515
|
this.log.info(`│ Registered plugins (${this.plugins.length})`);
|
|
@@ -667,19 +525,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
667
525
|
}
|
|
668
526
|
index++;
|
|
669
527
|
}
|
|
670
|
-
/*
|
|
671
|
-
const serializedRegisteredDevices = await this.nodeContext?.get<SerializedMatterbridgeEndpoint[]>('devices', []);
|
|
672
|
-
this.log.info(`│ Registered devices (${serializedRegisteredDevices?.length})`);
|
|
673
|
-
serializedRegisteredDevices?.forEach((device, index) => {
|
|
674
|
-
if (index !== serializedRegisteredDevices.length - 1) {
|
|
675
|
-
this.log.info(`├─┬─ plugin ${plg}${device.pluginName}${nf} device: ${dev}${device.deviceName}${nf} uniqueId: ${YELLOW}${device.uniqueId}${nf}`);
|
|
676
|
-
this.log.info(`│ └─ endpoint ${RED}${device.endpoint}${nf} ${typ}${device.endpointName}${nf} ${debugStringify(device.clusterServersId)}`);
|
|
677
|
-
} else {
|
|
678
|
-
this.log.info(`└─┬─ plugin ${plg}${device.pluginName}${nf} device: ${dev}${device.deviceName}${nf} uniqueId: ${YELLOW}${device.uniqueId}${nf}`);
|
|
679
|
-
this.log.info(` └─ endpoint ${RED}${device.endpoint}${nf} ${typ}${device.endpointName}${nf} ${debugStringify(device.clusterServersId)}`);
|
|
680
|
-
}
|
|
681
|
-
});
|
|
682
|
-
*/
|
|
683
528
|
this.shutdown = true;
|
|
684
529
|
return;
|
|
685
530
|
}
|
|
@@ -729,10 +574,8 @@ export class Matterbridge extends EventEmitter {
|
|
|
729
574
|
this.shutdown = true;
|
|
730
575
|
return;
|
|
731
576
|
}
|
|
732
|
-
// Initialize frontend
|
|
733
577
|
if (getIntParameter('frontend') !== 0 || getIntParameter('frontend') === undefined)
|
|
734
578
|
await this.frontend.start(getIntParameter('frontend'));
|
|
735
|
-
// Start the matter storage and create the matterbridge context
|
|
736
579
|
try {
|
|
737
580
|
await this.startMatterStorage();
|
|
738
581
|
if (this.aggregatorSerialNumber && this.aggregatorUniqueId && this.matterStorageService) {
|
|
@@ -746,21 +589,18 @@ export class Matterbridge extends EventEmitter {
|
|
|
746
589
|
this.log.fatal(`Fatal error creating matter storage: ${error instanceof Error ? error.message : error}`);
|
|
747
590
|
throw new Error(`Fatal error creating matter storage: ${error instanceof Error ? error.message : error}`);
|
|
748
591
|
}
|
|
749
|
-
// Clear the matterbridge context if the reset parameter is set (bridge mode)
|
|
750
592
|
if (hasParameter('reset') && getParameter('reset') === undefined) {
|
|
751
593
|
this.initialized = true;
|
|
752
594
|
await this.shutdownProcessAndReset();
|
|
753
595
|
this.shutdown = true;
|
|
754
596
|
return;
|
|
755
597
|
}
|
|
756
|
-
// Clear matterbridge plugin context if the reset parameter is set (childbridge mode)
|
|
757
598
|
if (hasParameter('reset') && getParameter('reset') !== undefined) {
|
|
758
599
|
this.log.debug(`Reset plugin ${getParameter('reset')}`);
|
|
759
600
|
const plugin = this.plugins.get(getParameter('reset'));
|
|
760
601
|
if (plugin) {
|
|
761
602
|
const matterStorageManager = await this.matterStorageService?.open(plugin.name);
|
|
762
603
|
if (!matterStorageManager) {
|
|
763
|
-
/* istanbul ignore next */
|
|
764
604
|
this.log.error(`Plugin ${plg}${plugin.name}${er} storageManager not found`);
|
|
765
605
|
}
|
|
766
606
|
else {
|
|
@@ -779,42 +619,35 @@ export class Matterbridge extends EventEmitter {
|
|
|
779
619
|
this.shutdown = true;
|
|
780
620
|
return;
|
|
781
621
|
}
|
|
782
|
-
// Check in 30 seconds the latest and dev versions of matterbridge and the plugins
|
|
783
622
|
clearTimeout(this.checkUpdateTimeout);
|
|
784
623
|
this.checkUpdateTimeout = setTimeout(async () => {
|
|
785
624
|
const { checkUpdates } = await import('./update.js');
|
|
786
625
|
checkUpdates(this);
|
|
787
626
|
}, 30 * 1000).unref();
|
|
788
|
-
// Check each 12 hours the latest and dev versions of matterbridge and the plugins
|
|
789
627
|
clearInterval(this.checkUpdateInterval);
|
|
790
628
|
this.checkUpdateInterval = setInterval(async () => {
|
|
791
629
|
const { checkUpdates } = await import('./update.js');
|
|
792
630
|
checkUpdates(this);
|
|
793
631
|
}, 12 * 60 * 60 * 1000).unref();
|
|
794
|
-
// Start the matterbridge in mode test
|
|
795
632
|
if (hasParameter('test')) {
|
|
796
633
|
this.bridgeMode = 'bridge';
|
|
797
634
|
return;
|
|
798
635
|
}
|
|
799
|
-
// Start the matterbridge in mode controller
|
|
800
636
|
if (hasParameter('controller')) {
|
|
801
637
|
this.bridgeMode = 'controller';
|
|
802
638
|
await this.startController();
|
|
803
639
|
return;
|
|
804
640
|
}
|
|
805
|
-
// Check if the bridge mode is set and start matterbridge in bridge mode if not set
|
|
806
641
|
if (!hasParameter('bridge') && !hasParameter('childbridge') && (await this.nodeContext?.get('bridgeMode', '')) === '') {
|
|
807
642
|
this.log.info('Setting default matterbridge start mode to bridge');
|
|
808
643
|
await this.nodeContext?.set('bridgeMode', 'bridge');
|
|
809
644
|
}
|
|
810
|
-
// Start matterbridge in bridge mode
|
|
811
645
|
if (hasParameter('bridge') || (!hasParameter('childbridge') && (await this.nodeContext?.get('bridgeMode', '')) === 'bridge')) {
|
|
812
646
|
this.bridgeMode = 'bridge';
|
|
813
647
|
this.log.debug(`Starting matterbridge in mode ${this.bridgeMode}`);
|
|
814
648
|
await this.startBridge();
|
|
815
649
|
return;
|
|
816
650
|
}
|
|
817
|
-
// Start matterbridge in childbridge mode
|
|
818
651
|
if (hasParameter('childbridge') || (!hasParameter('bridge') && (await this.nodeContext?.get('bridgeMode', '')) === 'childbridge')) {
|
|
819
652
|
this.bridgeMode = 'childbridge';
|
|
820
653
|
this.log.debug(`Starting matterbridge in mode ${this.bridgeMode}`);
|
|
@@ -822,22 +655,10 @@ export class Matterbridge extends EventEmitter {
|
|
|
822
655
|
return;
|
|
823
656
|
}
|
|
824
657
|
}
|
|
825
|
-
/**
|
|
826
|
-
* Asynchronously loads and starts the registered plugins.
|
|
827
|
-
*
|
|
828
|
-
* This method is responsible for initializing and starting all enabled plugins.
|
|
829
|
-
* It ensures that each plugin is properly loaded and started before the bridge starts.
|
|
830
|
-
*
|
|
831
|
-
* @param {boolean} [wait] - If true, the method will wait for all plugins to be fully loaded and started before resolving. Defaults to false.
|
|
832
|
-
* @param {boolean} [start] - If true, the method will start the plugins after loading them. Defaults to true.
|
|
833
|
-
* @returns {Promise<void>} A promise that resolves when all plugins have been loaded and started.
|
|
834
|
-
*/
|
|
835
658
|
async startPlugins(wait = false, start = true) {
|
|
836
|
-
// Check, load and start the plugins
|
|
837
659
|
for (const plugin of this.plugins) {
|
|
838
660
|
plugin.configJson = await this.plugins.loadConfig(plugin);
|
|
839
661
|
plugin.schemaJson = await this.plugins.loadSchema(plugin);
|
|
840
|
-
// Check if the plugin is available
|
|
841
662
|
if (!(await this.plugins.resolve(plugin.path))) {
|
|
842
663
|
this.log.error(`Plugin ${plg}${plugin.name}${er} not found or not validated. Disabling it.`);
|
|
843
664
|
plugin.enabled = false;
|
|
@@ -857,16 +678,10 @@ export class Matterbridge extends EventEmitter {
|
|
|
857
678
|
if (wait)
|
|
858
679
|
await this.plugins.load(plugin, start, 'Matterbridge is starting');
|
|
859
680
|
else
|
|
860
|
-
this.plugins.load(plugin, start, 'Matterbridge is starting');
|
|
681
|
+
this.plugins.load(plugin, start, 'Matterbridge is starting');
|
|
861
682
|
}
|
|
862
683
|
this.frontend.wssSendRefreshRequired('plugins');
|
|
863
684
|
}
|
|
864
|
-
/**
|
|
865
|
-
* Registers the process handlers for uncaughtException, unhandledRejection, SIGINT and SIGTERM.
|
|
866
|
-
* - When an uncaught exception occurs, the exceptionHandler logs the error message and stack trace.
|
|
867
|
-
* - When an unhandled promise rejection occurs, the rejectionHandler logs the reason and stack trace.
|
|
868
|
-
* - When either of SIGINT and SIGTERM signals are received, the cleanup method is called with an appropriate message.
|
|
869
|
-
*/
|
|
870
685
|
registerProcessHandlers() {
|
|
871
686
|
this.log.debug(`Registering uncaughtException and unhandledRejection handlers...`);
|
|
872
687
|
process.removeAllListeners('uncaughtException');
|
|
@@ -893,9 +708,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
893
708
|
};
|
|
894
709
|
process.on('SIGTERM', this.sigtermHandler);
|
|
895
710
|
}
|
|
896
|
-
/**
|
|
897
|
-
* Deregisters the process uncaughtException, unhandledRejection, SIGINT and SIGTERM signal handlers.
|
|
898
|
-
*/
|
|
899
711
|
deregisterProcessHandlers() {
|
|
900
712
|
this.log.debug(`Deregistering uncaughtException and unhandledRejection handlers...`);
|
|
901
713
|
if (this.exceptionHandler)
|
|
@@ -912,18 +724,13 @@ export class Matterbridge extends EventEmitter {
|
|
|
912
724
|
process.off('SIGTERM', this.sigtermHandler);
|
|
913
725
|
this.sigtermHandler = undefined;
|
|
914
726
|
}
|
|
915
|
-
/**
|
|
916
|
-
* Logs the node and system information.
|
|
917
|
-
*/
|
|
918
727
|
async logNodeAndSystemInfo() {
|
|
919
|
-
// IP address information
|
|
920
728
|
const networkInterfaces = os.networkInterfaces();
|
|
921
729
|
this.systemInformation.interfaceName = '';
|
|
922
730
|
this.systemInformation.ipv4Address = '';
|
|
923
731
|
this.systemInformation.ipv6Address = '';
|
|
924
732
|
this.systemInformation.macAddress = '';
|
|
925
733
|
for (const [interfaceName, interfaceDetails] of Object.entries(networkInterfaces)) {
|
|
926
|
-
// this.log.debug(`Checking interface: '${interfaceName}' for '${this.mdnsInterface}'`);
|
|
927
734
|
if (this.mdnsInterface && interfaceName !== this.mdnsInterface)
|
|
928
735
|
continue;
|
|
929
736
|
if (!interfaceDetails) {
|
|
@@ -949,18 +756,16 @@ export class Matterbridge extends EventEmitter {
|
|
|
949
756
|
break;
|
|
950
757
|
}
|
|
951
758
|
}
|
|
952
|
-
// Node information
|
|
953
759
|
this.systemInformation.nodeVersion = process.versions.node;
|
|
954
760
|
const versionMajor = parseInt(this.systemInformation.nodeVersion.split('.')[0]);
|
|
955
761
|
const versionMinor = parseInt(this.systemInformation.nodeVersion.split('.')[1]);
|
|
956
762
|
const versionPatch = parseInt(this.systemInformation.nodeVersion.split('.')[2]);
|
|
957
|
-
// Host system information
|
|
958
763
|
this.systemInformation.hostname = os.hostname();
|
|
959
764
|
this.systemInformation.user = os.userInfo().username;
|
|
960
|
-
this.systemInformation.osType = os.type();
|
|
961
|
-
this.systemInformation.osRelease = os.release();
|
|
962
|
-
this.systemInformation.osPlatform = os.platform();
|
|
963
|
-
this.systemInformation.osArch = os.arch();
|
|
765
|
+
this.systemInformation.osType = os.type();
|
|
766
|
+
this.systemInformation.osRelease = os.release();
|
|
767
|
+
this.systemInformation.osPlatform = os.platform();
|
|
768
|
+
this.systemInformation.osArch = os.arch();
|
|
964
769
|
this.systemInformation.totalMemory = formatBytes(os.totalmem());
|
|
965
770
|
this.systemInformation.freeMemory = formatBytes(os.freemem());
|
|
966
771
|
this.systemInformation.systemUptime = formatUptime(os.uptime());
|
|
@@ -970,7 +775,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
970
775
|
this.systemInformation.rss = formatBytes(process.memoryUsage().rss);
|
|
971
776
|
this.systemInformation.heapTotal = formatBytes(process.memoryUsage().heapTotal);
|
|
972
777
|
this.systemInformation.heapUsed = formatBytes(process.memoryUsage().heapUsed);
|
|
973
|
-
// Log the system information
|
|
974
778
|
this.log.debug('Host System Information:');
|
|
975
779
|
this.log.debug(`- Hostname: ${this.systemInformation.hostname}`);
|
|
976
780
|
this.log.debug(`- User: ${this.systemInformation.user}`);
|
|
@@ -990,17 +794,14 @@ export class Matterbridge extends EventEmitter {
|
|
|
990
794
|
this.log.debug(`- RSS: ${this.systemInformation.rss}`);
|
|
991
795
|
this.log.debug(`- Heap Total: ${this.systemInformation.heapTotal}`);
|
|
992
796
|
this.log.debug(`- Heap Used: ${this.systemInformation.heapUsed}`);
|
|
993
|
-
// Log directories
|
|
994
797
|
this.log.debug(`Root Directory: ${this.rootDirectory}`);
|
|
995
798
|
this.log.debug(`Home Directory: ${this.homeDirectory}`);
|
|
996
799
|
this.log.debug(`Matterbridge Directory: ${this.matterbridgeDirectory}`);
|
|
997
800
|
this.log.debug(`Matterbridge Plugin Directory: ${this.matterbridgePluginDirectory}`);
|
|
998
801
|
this.log.debug(`Matterbridge Matter Certificate Directory: ${this.matterbridgeCertDirectory}`);
|
|
999
|
-
// Global node_modules directory
|
|
1000
802
|
if (this.nodeContext)
|
|
1001
803
|
this.globalModulesDirectory = await this.nodeContext.get('globalModulesDirectory', '');
|
|
1002
804
|
if (this.globalModulesDirectory === '') {
|
|
1003
|
-
// First run of Matterbridge so the node storage is empty
|
|
1004
805
|
this.log.debug(`Getting global node_modules directory...`);
|
|
1005
806
|
try {
|
|
1006
807
|
const { getGlobalNodeModules } = await import('./utils/network.js');
|
|
@@ -1013,7 +814,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1013
814
|
}
|
|
1014
815
|
}
|
|
1015
816
|
else {
|
|
1016
|
-
// The global node_modules directory is already set in the node storage and we check if it is still valid
|
|
1017
817
|
this.log.debug(`Checking global node_modules directory: ${this.globalModulesDirectory}`);
|
|
1018
818
|
try {
|
|
1019
819
|
const { getGlobalNodeModules } = await import('./utils/network.js');
|
|
@@ -1025,37 +825,25 @@ export class Matterbridge extends EventEmitter {
|
|
|
1025
825
|
this.log.error(`Error checking global node_modules directory: ${error}`);
|
|
1026
826
|
}
|
|
1027
827
|
}
|
|
1028
|
-
// Matterbridge version
|
|
1029
828
|
this.log.debug(`Reading matterbridge package.json...`);
|
|
1030
829
|
const packageJson = JSON.parse(await fs.promises.readFile(path.join(this.rootDirectory, 'package.json'), 'utf-8'));
|
|
1031
830
|
this.matterbridgeVersion = this.matterbridgeLatestVersion = this.matterbridgeDevVersion = packageJson.version;
|
|
1032
831
|
this.log.debug(`Matterbridge Version: ${this.matterbridgeVersion}`);
|
|
1033
|
-
// Matterbridge latest version (will be set in the checkUpdate function)
|
|
1034
832
|
if (this.nodeContext)
|
|
1035
833
|
this.matterbridgeLatestVersion = await this.nodeContext.get('matterbridgeLatestVersion', this.matterbridgeVersion);
|
|
1036
834
|
this.log.debug(`Matterbridge Latest Version: ${this.matterbridgeLatestVersion}`);
|
|
1037
|
-
// Matterbridge dev version (will be set in the checkUpdate function)
|
|
1038
835
|
if (this.nodeContext)
|
|
1039
836
|
this.matterbridgeDevVersion = await this.nodeContext.get('matterbridgeDevVersion', this.matterbridgeVersion);
|
|
1040
837
|
this.log.debug(`Matterbridge Dev Version: ${this.matterbridgeDevVersion}`);
|
|
1041
|
-
// Frontend version
|
|
1042
838
|
this.log.debug(`Reading frontend package.json...`);
|
|
1043
839
|
const frontendPackageJson = JSON.parse(await fs.promises.readFile(path.join(this.rootDirectory, 'frontend/package.json'), 'utf8'));
|
|
1044
840
|
this.frontendVersion = frontendPackageJson.version;
|
|
1045
841
|
this.log.debug(`Frontend version ${CYAN}${this.frontendVersion}${db}`);
|
|
1046
|
-
// Current working directory
|
|
1047
842
|
const currentDir = process.cwd();
|
|
1048
843
|
this.log.debug(`Current Working Directory: ${currentDir}`);
|
|
1049
|
-
// Command line arguments (excluding 'node' and the script name)
|
|
1050
844
|
const cmdArgs = process.argv.slice(2).join(' ');
|
|
1051
845
|
this.log.debug(`Command Line Arguments: ${cmdArgs}`);
|
|
1052
846
|
}
|
|
1053
|
-
/**
|
|
1054
|
-
* Set the logger logLevel for the Matterbridge classes and call onChangeLoggerLevel() for each plugin.
|
|
1055
|
-
*
|
|
1056
|
-
* @param {LogLevel} logLevel The logger logLevel to set.
|
|
1057
|
-
* @returns {Promise<LogLevel>} A promise that resolves when the logLevel has been set.
|
|
1058
|
-
*/
|
|
1059
847
|
async setLogLevel(logLevel) {
|
|
1060
848
|
this.logLevel = logLevel;
|
|
1061
849
|
this.log.logLevel = logLevel;
|
|
@@ -1066,87 +854,58 @@ export class Matterbridge extends EventEmitter {
|
|
|
1066
854
|
for (const plugin of this.plugins) {
|
|
1067
855
|
if (!plugin.platform || !plugin.platform.log || !plugin.platform.config)
|
|
1068
856
|
continue;
|
|
1069
|
-
plugin.platform.log.logLevel = plugin.platform.config.debug === true ? "debug"
|
|
1070
|
-
await plugin.platform.onChangeLoggerLevel(plugin.platform.config.debug === true ? "debug"
|
|
1071
|
-
}
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
callbackLogLevel = "debug" /* LogLevel.DEBUG */;
|
|
857
|
+
plugin.platform.log.logLevel = plugin.platform.config.debug === true ? "debug" : logLevel;
|
|
858
|
+
await plugin.platform.onChangeLoggerLevel(plugin.platform.config.debug === true ? "debug" : logLevel);
|
|
859
|
+
}
|
|
860
|
+
let callbackLogLevel = "notice";
|
|
861
|
+
if (logLevel === "info" || Logger.level === MatterLogLevel.INFO)
|
|
862
|
+
callbackLogLevel = "info";
|
|
863
|
+
if (logLevel === "debug" || Logger.level === MatterLogLevel.DEBUG)
|
|
864
|
+
callbackLogLevel = "debug";
|
|
1078
865
|
AnsiLogger.setGlobalCallbackLevel(callbackLogLevel);
|
|
1079
866
|
this.log.debug(`WebSocketServer logger global callback set to ${callbackLogLevel}`);
|
|
1080
867
|
return logLevel;
|
|
1081
868
|
}
|
|
1082
|
-
/**
|
|
1083
|
-
* Get the current logger logLevel.
|
|
1084
|
-
*
|
|
1085
|
-
* @returns {LogLevel} The current logger logLevel.
|
|
1086
|
-
*/
|
|
1087
869
|
getLogLevel() {
|
|
1088
870
|
return this.log.logLevel;
|
|
1089
871
|
}
|
|
1090
|
-
/**
|
|
1091
|
-
* Creates a MatterLogger function to show the matter.js log messages in AnsiLogger (for the frontend).
|
|
1092
|
-
* It also logs to file (matter.log) if fileLogger is true.
|
|
1093
|
-
*
|
|
1094
|
-
* @param {boolean} fileLogger - Whether to log to file or not.
|
|
1095
|
-
* @returns {Function} The MatterLogger function. \x1b[35m for violet \x1b[34m is blue
|
|
1096
|
-
*/
|
|
1097
872
|
createDestinationMatterLogger(fileLogger) {
|
|
1098
|
-
this.matterLog.logNameColor = '\x1b[34m';
|
|
873
|
+
this.matterLog.logNameColor = '\x1b[34m';
|
|
1099
874
|
if (fileLogger) {
|
|
1100
875
|
this.matterLog.logFilePath = path.join(this.matterbridgeDirectory, MATTER_LOGGER_FILE);
|
|
1101
876
|
}
|
|
1102
877
|
return (text, message) => {
|
|
1103
|
-
// 2024-08-21 08:55:19.488 DEBUG InteractionMessenger Sending DataReport chunk with 28 attributes and 0 events: 1004 bytes
|
|
1104
878
|
const logger = text.slice(44, 44 + 20).trim();
|
|
1105
879
|
const msg = text.slice(65);
|
|
1106
880
|
this.matterLog.logName = logger;
|
|
1107
881
|
switch (message.level) {
|
|
1108
882
|
case MatterLogLevel.DEBUG:
|
|
1109
|
-
this.matterLog.log("debug"
|
|
883
|
+
this.matterLog.log("debug", msg);
|
|
1110
884
|
break;
|
|
1111
885
|
case MatterLogLevel.INFO:
|
|
1112
|
-
this.matterLog.log("info"
|
|
886
|
+
this.matterLog.log("info", msg);
|
|
1113
887
|
break;
|
|
1114
888
|
case MatterLogLevel.NOTICE:
|
|
1115
|
-
this.matterLog.log("notice"
|
|
889
|
+
this.matterLog.log("notice", msg);
|
|
1116
890
|
break;
|
|
1117
891
|
case MatterLogLevel.WARN:
|
|
1118
|
-
this.matterLog.log("warn"
|
|
892
|
+
this.matterLog.log("warn", msg);
|
|
1119
893
|
break;
|
|
1120
894
|
case MatterLogLevel.ERROR:
|
|
1121
|
-
this.matterLog.log("error"
|
|
895
|
+
this.matterLog.log("error", msg);
|
|
1122
896
|
break;
|
|
1123
897
|
case MatterLogLevel.FATAL:
|
|
1124
|
-
this.matterLog.log("fatal"
|
|
898
|
+
this.matterLog.log("fatal", msg);
|
|
1125
899
|
break;
|
|
1126
900
|
}
|
|
1127
901
|
};
|
|
1128
902
|
}
|
|
1129
|
-
/**
|
|
1130
|
-
* Restarts the process by exiting the current instance and loading a new instance (/api/restart).
|
|
1131
|
-
*
|
|
1132
|
-
* @returns {Promise<void>} A promise that resolves when the restart is completed.
|
|
1133
|
-
*/
|
|
1134
903
|
async restartProcess() {
|
|
1135
904
|
await this.cleanup('restarting...', true);
|
|
1136
905
|
}
|
|
1137
|
-
/**
|
|
1138
|
-
* Shut down the process (/api/shutdown).
|
|
1139
|
-
*
|
|
1140
|
-
* @returns {Promise<void>} A promise that resolves when the shutdown is completed.
|
|
1141
|
-
*/
|
|
1142
906
|
async shutdownProcess() {
|
|
1143
907
|
await this.cleanup('shutting down...', false);
|
|
1144
908
|
}
|
|
1145
|
-
/**
|
|
1146
|
-
* Update matterbridge and shut down the process (virtual device 'Update Matterbridge').
|
|
1147
|
-
*
|
|
1148
|
-
* @returns {Promise<void>} A promise that resolves when the update is completed.
|
|
1149
|
-
*/
|
|
1150
909
|
async updateProcess() {
|
|
1151
910
|
this.log.info('Updating matterbridge...');
|
|
1152
911
|
const { spawnCommand } = await import('./utils/spawn.js');
|
|
@@ -1159,13 +918,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1159
918
|
this.frontend.wssSendRestartRequired();
|
|
1160
919
|
await this.cleanup('updating...', false);
|
|
1161
920
|
}
|
|
1162
|
-
/**
|
|
1163
|
-
* Unregister all devices and shut down the process (/api/unregister).
|
|
1164
|
-
*
|
|
1165
|
-
* @param {number} [timeout] - The timeout duration to wait for the message exchange to complete in milliseconds. Default is 1000.
|
|
1166
|
-
*
|
|
1167
|
-
* @returns {Promise<void>} A promise that resolves when the cleanup is completed.
|
|
1168
|
-
*/
|
|
1169
921
|
async unregisterAndShutdownProcess(timeout = 1000) {
|
|
1170
922
|
this.log.info('Unregistering all devices and shutting down...');
|
|
1171
923
|
for (const plugin of this.plugins.array()) {
|
|
@@ -1177,71 +929,46 @@ export class Matterbridge extends EventEmitter {
|
|
|
1177
929
|
await this.removeAllBridgedEndpoints(plugin.name, 100);
|
|
1178
930
|
}
|
|
1179
931
|
this.log.debug('Waiting for the MessageExchange to finish...');
|
|
1180
|
-
await wait(timeout);
|
|
932
|
+
await wait(timeout);
|
|
1181
933
|
this.log.debug('Cleaning up and shutting down...');
|
|
1182
934
|
await this.cleanup('unregistered all devices and shutting down...', false, timeout);
|
|
1183
935
|
}
|
|
1184
|
-
/**
|
|
1185
|
-
* Reset commissioning and shut down the process (/api/reset).
|
|
1186
|
-
*
|
|
1187
|
-
* @returns {Promise<void>} A promise that resolves when the cleanup is completed.
|
|
1188
|
-
*/
|
|
1189
936
|
async shutdownProcessAndReset() {
|
|
1190
937
|
await this.cleanup('shutting down with reset...', false);
|
|
1191
938
|
}
|
|
1192
|
-
/**
|
|
1193
|
-
* Factory reset and shut down the process (/api/factory-reset).
|
|
1194
|
-
*
|
|
1195
|
-
* @returns {Promise<void>} A promise that resolves when the cleanup is completed.
|
|
1196
|
-
*/
|
|
1197
939
|
async shutdownProcessAndFactoryReset() {
|
|
1198
940
|
await this.cleanup('shutting down with factory reset...', false);
|
|
1199
941
|
}
|
|
1200
|
-
/**
|
|
1201
|
-
* Cleans up the Matterbridge instance.
|
|
1202
|
-
*
|
|
1203
|
-
* @param {string} message - The cleanup message.
|
|
1204
|
-
* @param {boolean} [restart] - Indicates whether to restart the instance after cleanup. Default is `false`.
|
|
1205
|
-
* @param {number} [pause] - The pause in ms to wait for the message exchange to complete in milliseconds. Default is 1000.
|
|
1206
|
-
*
|
|
1207
|
-
* @returns {Promise<void>} A promise that resolves when the cleanup is completed.
|
|
1208
|
-
*/
|
|
1209
942
|
async cleanup(message, restart = false, pause = 1000) {
|
|
1210
943
|
if (this.initialized && !this.hasCleanupStarted) {
|
|
1211
944
|
this.emit('cleanup_started');
|
|
1212
945
|
this.hasCleanupStarted = true;
|
|
1213
946
|
this.log.info(message);
|
|
1214
|
-
// Clear the start matter interval
|
|
1215
947
|
if (this.startMatterInterval) {
|
|
1216
948
|
clearInterval(this.startMatterInterval);
|
|
1217
949
|
this.startMatterInterval = undefined;
|
|
1218
950
|
this.log.debug('Start matter interval cleared');
|
|
1219
951
|
}
|
|
1220
|
-
// Clear the check update timeout
|
|
1221
952
|
if (this.checkUpdateTimeout) {
|
|
1222
953
|
clearTimeout(this.checkUpdateTimeout);
|
|
1223
954
|
this.checkUpdateTimeout = undefined;
|
|
1224
955
|
this.log.debug('Check update timeout cleared');
|
|
1225
956
|
}
|
|
1226
|
-
// Clear the check update interval
|
|
1227
957
|
if (this.checkUpdateInterval) {
|
|
1228
958
|
clearInterval(this.checkUpdateInterval);
|
|
1229
959
|
this.checkUpdateInterval = undefined;
|
|
1230
960
|
this.log.debug('Check update interval cleared');
|
|
1231
961
|
}
|
|
1232
|
-
// Clear the configure timeout
|
|
1233
962
|
if (this.configureTimeout) {
|
|
1234
963
|
clearTimeout(this.configureTimeout);
|
|
1235
964
|
this.configureTimeout = undefined;
|
|
1236
965
|
this.log.debug('Matterbridge configure timeout cleared');
|
|
1237
966
|
}
|
|
1238
|
-
// Clear the reachability timeout
|
|
1239
967
|
if (this.reachabilityTimeout) {
|
|
1240
968
|
clearTimeout(this.reachabilityTimeout);
|
|
1241
969
|
this.reachabilityTimeout = undefined;
|
|
1242
970
|
this.log.debug('Matterbridge reachability timeout cleared');
|
|
1243
971
|
}
|
|
1244
|
-
// Call the shutdown method of each plugin and clear the plugins reachability timeout
|
|
1245
972
|
for (const plugin of this.plugins) {
|
|
1246
973
|
if (!plugin.enabled || plugin.error)
|
|
1247
974
|
continue;
|
|
@@ -1252,7 +979,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1252
979
|
this.log.debug(`Plugin ${plg}${plugin.name}${db} reachability timeout cleared`);
|
|
1253
980
|
}
|
|
1254
981
|
}
|
|
1255
|
-
// Stop matter server nodes
|
|
1256
982
|
this.log.notice(`Stopping matter server nodes in ${this.bridgeMode} mode...`);
|
|
1257
983
|
if (pause > 0) {
|
|
1258
984
|
this.log.debug(`Waiting ${pause}ms for the MessageExchange to finish...`);
|
|
@@ -1279,7 +1005,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1279
1005
|
}
|
|
1280
1006
|
}
|
|
1281
1007
|
this.log.notice('Stopped matter server nodes');
|
|
1282
|
-
// Matter commisioning reset
|
|
1283
1008
|
if (message === 'shutting down with reset...') {
|
|
1284
1009
|
this.log.info('Resetting Matterbridge commissioning information...');
|
|
1285
1010
|
await this.matterStorageManager?.createContext('events')?.clearAll();
|
|
@@ -1289,7 +1014,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1289
1014
|
await this.matterbridgeContext?.clearAll();
|
|
1290
1015
|
this.log.info('Matter storage reset done! Remove the bridge from the controller.');
|
|
1291
1016
|
}
|
|
1292
|
-
// Unregister all devices
|
|
1293
1017
|
if (message === 'unregistered all devices and shutting down...') {
|
|
1294
1018
|
if (this.bridgeMode === 'bridge') {
|
|
1295
1019
|
await this.matterStorageManager?.createContext('root')?.createContext('parts')?.createContext('Matterbridge')?.createContext('parts')?.clearAll();
|
|
@@ -1307,35 +1031,16 @@ export class Matterbridge extends EventEmitter {
|
|
|
1307
1031
|
}
|
|
1308
1032
|
this.log.info('Matter storage reset done!');
|
|
1309
1033
|
}
|
|
1310
|
-
// Stop matter storage
|
|
1311
1034
|
await this.stopMatterStorage();
|
|
1312
|
-
// Stop the frontend
|
|
1313
1035
|
await this.frontend.stop();
|
|
1314
1036
|
this.frontend.destroy();
|
|
1315
|
-
// Close PluginManager and DeviceManager
|
|
1316
1037
|
this.plugins.destroy();
|
|
1317
1038
|
this.devices.destroy();
|
|
1318
|
-
// Stop thread messaging server
|
|
1319
1039
|
this.server.close();
|
|
1320
|
-
// Close the matterbridge node storage and context
|
|
1321
1040
|
if (this.nodeStorage && this.nodeContext) {
|
|
1322
|
-
/*
|
|
1323
|
-
TODO: Implement serialization of registered devices
|
|
1324
|
-
this.log.info('Saving registered devices...');
|
|
1325
|
-
const serializedRegisteredDevices: SerializedMatterbridgeEndpoint[] = [];
|
|
1326
|
-
this.devices.forEach(async (device) => {
|
|
1327
|
-
const serializedMatterbridgeDevice = MatterbridgeEndpoint.serialize(device);
|
|
1328
|
-
this.log.info(`- ${serializedMatterbridgeDevice.deviceName}${rs}\n`, serializedMatterbridgeDevice);
|
|
1329
|
-
if (serializedMatterbridgeDevice) serializedRegisteredDevices.push(serializedMatterbridgeDevice);
|
|
1330
|
-
});
|
|
1331
|
-
await this.nodeContext.set<SerializedMatterbridgeEndpoint[]>('devices', serializedRegisteredDevices);
|
|
1332
|
-
this.log.info(`Saved registered devices (${serializedRegisteredDevices?.length})`);
|
|
1333
|
-
*/
|
|
1334
|
-
// Clear nodeContext and nodeStorage (they just need 1000ms to write the data to disk)
|
|
1335
1041
|
this.log.debug(`Closing node storage context for ${plg}Matterbridge${db}...`);
|
|
1336
1042
|
await this.nodeContext.close();
|
|
1337
1043
|
this.nodeContext = undefined;
|
|
1338
|
-
// Clear nodeContext for each plugin (they just need 1000ms to write the data to disk)
|
|
1339
1044
|
for (const plugin of this.plugins) {
|
|
1340
1045
|
if (plugin.nodeContext) {
|
|
1341
1046
|
this.log.debug(`Closing node storage context for plugin ${plg}${plugin.name}${db}...`);
|
|
@@ -1352,10 +1057,8 @@ export class Matterbridge extends EventEmitter {
|
|
|
1352
1057
|
}
|
|
1353
1058
|
this.plugins.clear();
|
|
1354
1059
|
this.devices.clear();
|
|
1355
|
-
// Factory reset
|
|
1356
1060
|
if (message === 'shutting down with factory reset...') {
|
|
1357
1061
|
try {
|
|
1358
|
-
// Delete matter storage directory with its subdirectories and backup
|
|
1359
1062
|
const dir = path.join(this.matterbridgeDirectory, MATTER_STORAGE_NAME);
|
|
1360
1063
|
this.log.info(`Removing matter storage directory: ${dir}`);
|
|
1361
1064
|
await fs.promises.rm(dir, { recursive: true });
|
|
@@ -1364,13 +1067,11 @@ export class Matterbridge extends EventEmitter {
|
|
|
1364
1067
|
await fs.promises.rm(backup, { recursive: true });
|
|
1365
1068
|
}
|
|
1366
1069
|
catch (error) {
|
|
1367
|
-
// istanbul ignore next if
|
|
1368
1070
|
if (error instanceof Error && error.code !== 'ENOENT') {
|
|
1369
1071
|
this.log.error(`Error removing matter storage directory: ${error}`);
|
|
1370
1072
|
}
|
|
1371
1073
|
}
|
|
1372
1074
|
try {
|
|
1373
|
-
// Delete matterbridge storage directory with its subdirectories and backup
|
|
1374
1075
|
const dir = path.join(this.matterbridgeDirectory, NODE_STORAGE_DIR);
|
|
1375
1076
|
this.log.info(`Removing matterbridge storage directory: ${dir}`);
|
|
1376
1077
|
await fs.promises.rm(dir, { recursive: true });
|
|
@@ -1379,20 +1080,18 @@ export class Matterbridge extends EventEmitter {
|
|
|
1379
1080
|
await fs.promises.rm(backup, { recursive: true });
|
|
1380
1081
|
}
|
|
1381
1082
|
catch (error) {
|
|
1382
|
-
// istanbul ignore next if
|
|
1383
1083
|
if (error instanceof Error && error.code !== 'ENOENT') {
|
|
1384
1084
|
this.log.error(`Error removing matterbridge storage directory: ${error}`);
|
|
1385
1085
|
}
|
|
1386
1086
|
}
|
|
1387
1087
|
this.log.info('Factory reset done! Remove all paired fabrics from the controllers.');
|
|
1388
1088
|
}
|
|
1389
|
-
// Deregisters the process handlers
|
|
1390
1089
|
this.deregisterProcessHandlers();
|
|
1391
1090
|
if (restart) {
|
|
1392
1091
|
if (message === 'updating...') {
|
|
1393
1092
|
this.log.info('Cleanup completed. Updating...');
|
|
1394
1093
|
Matterbridge.instance = undefined;
|
|
1395
|
-
this.emit('update');
|
|
1094
|
+
this.emit('update');
|
|
1396
1095
|
}
|
|
1397
1096
|
else if (message === 'restarting...') {
|
|
1398
1097
|
this.log.info('Cleanup completed. Restarting...');
|
|
@@ -1421,14 +1120,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
1421
1120
|
this.log.debug('Cleanup already started...');
|
|
1422
1121
|
}
|
|
1423
1122
|
}
|
|
1424
|
-
/**
|
|
1425
|
-
* Starts the Matterbridge in bridge mode.
|
|
1426
|
-
*
|
|
1427
|
-
* @private
|
|
1428
|
-
* @returns {Promise<void>} A promise that resolves when the Matterbridge is started.
|
|
1429
|
-
*/
|
|
1430
1123
|
async startBridge() {
|
|
1431
|
-
// Plugins are configured by a timer when matter server is started and plugin.configured is set to true
|
|
1432
1124
|
if (!this.matterStorageManager)
|
|
1433
1125
|
throw new Error('No storage manager initialized');
|
|
1434
1126
|
if (!this.matterbridgeContext)
|
|
@@ -1467,16 +1159,13 @@ export class Matterbridge extends EventEmitter {
|
|
|
1467
1159
|
clearInterval(this.startMatterInterval);
|
|
1468
1160
|
this.startMatterInterval = undefined;
|
|
1469
1161
|
this.log.debug('Cleared startMatterInterval interval in bridge mode');
|
|
1470
|
-
|
|
1471
|
-
this.startServerNode(this.serverNode); // We don't await this, because the server node is started in the background
|
|
1472
|
-
// Start the Matter server node of single devices in mode 'server'
|
|
1162
|
+
this.startServerNode(this.serverNode);
|
|
1473
1163
|
for (const device of this.devices.array()) {
|
|
1474
1164
|
if (device.mode === 'server' && device.serverNode) {
|
|
1475
1165
|
this.log.debug(`Starting server node for device ${dev}${device.deviceName}${db} in server mode...`);
|
|
1476
|
-
this.startServerNode(device.serverNode);
|
|
1166
|
+
this.startServerNode(device.serverNode);
|
|
1477
1167
|
}
|
|
1478
1168
|
}
|
|
1479
|
-
// Configure the plugins
|
|
1480
1169
|
this.configureTimeout = setTimeout(async () => {
|
|
1481
1170
|
for (const plugin of this.plugins.array()) {
|
|
1482
1171
|
if (!plugin.enabled || !plugin.loaded || !plugin.started || plugin.error)
|
|
@@ -1494,40 +1183,28 @@ export class Matterbridge extends EventEmitter {
|
|
|
1494
1183
|
}
|
|
1495
1184
|
this.frontend.wssSendRefreshRequired('plugins');
|
|
1496
1185
|
}, 30 * 1000).unref();
|
|
1497
|
-
// Setting reachability to true
|
|
1498
1186
|
this.reachabilityTimeout = setTimeout(() => {
|
|
1499
1187
|
this.log.info(`Setting reachability to true for ${plg}Matterbridge${db}`);
|
|
1500
1188
|
if (this.aggregatorNode)
|
|
1501
1189
|
this.setAggregatorReachability(this.aggregatorNode, true);
|
|
1502
1190
|
}, 60 * 1000).unref();
|
|
1503
|
-
// Logger.get('LogServerNode').info(this.serverNode);
|
|
1504
1191
|
this.emit('bridge_started');
|
|
1505
1192
|
this.log.notice('Matterbridge bridge started successfully');
|
|
1506
1193
|
this.frontend.wssSendRefreshRequired('settings');
|
|
1507
1194
|
this.frontend.wssSendRefreshRequired('plugins');
|
|
1508
1195
|
}, Number(process.env['MATTERBRIDGE_START_MATTER_INTERVAL_MS']) || this.startMatterIntervalMs);
|
|
1509
1196
|
}
|
|
1510
|
-
/**
|
|
1511
|
-
* Starts the Matterbridge in childbridge mode.
|
|
1512
|
-
*
|
|
1513
|
-
* @param {number} [delay] - The delay before starting the childbridge. Default is 1000 milliseconds.
|
|
1514
|
-
*
|
|
1515
|
-
* @returns {Promise<void>} A promise that resolves when the Matterbridge is started.
|
|
1516
|
-
*/
|
|
1517
1197
|
async startChildbridge(delay = 1000) {
|
|
1518
1198
|
if (!this.matterStorageManager)
|
|
1519
1199
|
throw new Error('No storage manager initialized');
|
|
1520
|
-
// Load with await all plugins but don't start them. We get the platform.type to pre-create server nodes for DynamicPlatform plugins
|
|
1521
1200
|
this.log.debug('Loading all plugins in childbridge mode...');
|
|
1522
1201
|
await this.startPlugins(true, false);
|
|
1523
|
-
// Create server nodes for DynamicPlatform plugins and start all plugins in the background
|
|
1524
1202
|
this.log.debug('Creating server nodes for DynamicPlatform plugins and starting all plugins in childbridge mode...');
|
|
1525
1203
|
for (const plugin of this.plugins.array().filter((p) => p.enabled && !p.error)) {
|
|
1526
1204
|
if (plugin.type === 'DynamicPlatform')
|
|
1527
1205
|
await this.createDynamicPlugin(plugin);
|
|
1528
|
-
this.plugins.start(plugin, 'Matterbridge is starting');
|
|
1206
|
+
this.plugins.start(plugin, 'Matterbridge is starting');
|
|
1529
1207
|
}
|
|
1530
|
-
// Start the Matterbridge in childbridge mode when all plugins are loaded and started
|
|
1531
1208
|
this.log.debug('Starting start matter interval in childbridge mode...');
|
|
1532
1209
|
let failCount = 0;
|
|
1533
1210
|
this.startMatterInterval = setInterval(async () => {
|
|
@@ -1561,9 +1238,8 @@ export class Matterbridge extends EventEmitter {
|
|
|
1561
1238
|
clearInterval(this.startMatterInterval);
|
|
1562
1239
|
this.startMatterInterval = undefined;
|
|
1563
1240
|
if (delay > 0)
|
|
1564
|
-
await wait(Number(process.env['MATTERBRIDGE_PAUSE_MATTER_INTERVAL_MS']) || delay);
|
|
1241
|
+
await wait(Number(process.env['MATTERBRIDGE_PAUSE_MATTER_INTERVAL_MS']) || delay);
|
|
1565
1242
|
this.log.debug('Cleared startMatterInterval interval in childbridge mode');
|
|
1566
|
-
// Configure the plugins
|
|
1567
1243
|
this.configureTimeout = setTimeout(async () => {
|
|
1568
1244
|
for (const plugin of this.plugins.array()) {
|
|
1569
1245
|
if (!plugin.enabled || !plugin.loaded || !plugin.started || plugin.error)
|
|
@@ -1588,7 +1264,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1588
1264
|
this.log.error(`Plugin ${plg}${plugin.name}${er} didn't register any devices to Matterbridge. Verify the plugin configuration.`);
|
|
1589
1265
|
continue;
|
|
1590
1266
|
}
|
|
1591
|
-
// istanbul ignore next if cause is just a safety check
|
|
1592
1267
|
if (!plugin.serverNode) {
|
|
1593
1268
|
this.log.error(`Server node not found for plugin ${plg}${plugin.name}${er}`);
|
|
1594
1269
|
continue;
|
|
@@ -1601,252 +1276,28 @@ export class Matterbridge extends EventEmitter {
|
|
|
1601
1276
|
this.log.error(`Node storage context not found for plugin ${plg}${plugin.name}${er}`);
|
|
1602
1277
|
continue;
|
|
1603
1278
|
}
|
|
1604
|
-
|
|
1605
|
-
this.startServerNode(plugin.serverNode); // We don't await this, because the server node is started in the background
|
|
1606
|
-
// Setting reachability to true
|
|
1279
|
+
this.startServerNode(plugin.serverNode);
|
|
1607
1280
|
plugin.reachabilityTimeout = setTimeout(() => {
|
|
1608
1281
|
this.log.info(`Setting reachability to true for ${plg}${plugin.name}${nf}`);
|
|
1609
1282
|
if (plugin.type === 'DynamicPlatform' && plugin.aggregatorNode)
|
|
1610
1283
|
this.setAggregatorReachability(plugin.aggregatorNode, true);
|
|
1611
1284
|
}, 60 * 1000).unref();
|
|
1612
1285
|
}
|
|
1613
|
-
// Start the Matter server node of single devices in mode 'server'
|
|
1614
1286
|
for (const device of this.devices.array()) {
|
|
1615
1287
|
if (device.mode === 'server' && device.serverNode) {
|
|
1616
1288
|
this.log.debug(`Starting server node for device ${dev}${device.deviceName}${db} in server mode...`);
|
|
1617
|
-
this.startServerNode(device.serverNode);
|
|
1289
|
+
this.startServerNode(device.serverNode);
|
|
1618
1290
|
}
|
|
1619
1291
|
}
|
|
1620
|
-
// Logger.get('LogServerNode').info(this.serverNode);
|
|
1621
1292
|
this.emit('childbridge_started');
|
|
1622
1293
|
this.log.notice('Matterbridge childbridge started successfully');
|
|
1623
1294
|
this.frontend.wssSendRefreshRequired('settings');
|
|
1624
1295
|
this.frontend.wssSendRefreshRequired('plugins');
|
|
1625
1296
|
}, Number(process.env['MATTERBRIDGE_START_MATTER_INTERVAL_MS']) || this.startMatterIntervalMs);
|
|
1626
1297
|
}
|
|
1627
|
-
/**
|
|
1628
|
-
* Starts the Matterbridge controller.
|
|
1629
|
-
*
|
|
1630
|
-
* @private
|
|
1631
|
-
* @returns {Promise<void>} A promise that resolves when the Matterbridge is started.
|
|
1632
|
-
*/
|
|
1633
1298
|
async startController() {
|
|
1634
|
-
/*
|
|
1635
|
-
if (!this.matterStorageManager) {
|
|
1636
|
-
this.log.error('No storage manager initialized');
|
|
1637
|
-
await this.cleanup('No storage manager initialized');
|
|
1638
|
-
return;
|
|
1639
|
-
}
|
|
1640
|
-
this.log.info('Creating context: mattercontrollerContext');
|
|
1641
|
-
this.controllerContext = this.matterStorageManager.createContext('mattercontrollerContext');
|
|
1642
|
-
if (!this.controllerContext) {
|
|
1643
|
-
this.log.error('No storage context mattercontrollerContext initialized');
|
|
1644
|
-
await this.cleanup('No storage context mattercontrollerContext initialized');
|
|
1645
|
-
return;
|
|
1646
|
-
}
|
|
1647
|
-
|
|
1648
|
-
this.log.debug('Starting matterbridge in mode', this.bridgeMode);
|
|
1649
|
-
this.matterServer = await this.createMatterServer(this.storageManager);
|
|
1650
|
-
this.log.info('Creating matter commissioning controller');
|
|
1651
|
-
this.commissioningController = new CommissioningController({
|
|
1652
|
-
autoConnect: false,
|
|
1653
|
-
});
|
|
1654
|
-
this.log.info('Adding matter commissioning controller to matter server');
|
|
1655
|
-
await this.matterServer.addCommissioningController(this.commissioningController);
|
|
1656
|
-
|
|
1657
|
-
this.log.info('Starting matter server');
|
|
1658
|
-
await this.matterServer.start();
|
|
1659
|
-
this.log.info('Matter server started');
|
|
1660
|
-
const commissioningOptions: ControllerCommissioningFlowOptions = {
|
|
1661
|
-
regulatoryLocation: GeneralCommissioning.RegulatoryLocationType.IndoorOutdoor,
|
|
1662
|
-
regulatoryCountryCode: 'XX',
|
|
1663
|
-
};
|
|
1664
|
-
const commissioningController = new CommissioningController({
|
|
1665
|
-
environment: {
|
|
1666
|
-
environment,
|
|
1667
|
-
id: uniqueId,
|
|
1668
|
-
},
|
|
1669
|
-
autoConnect: false, // Do not auto connect to the commissioned nodes
|
|
1670
|
-
adminFabricLabel,
|
|
1671
|
-
});
|
|
1672
|
-
|
|
1673
|
-
if (hasParameter('pairingcode')) {
|
|
1674
|
-
this.log.info('Pairing device with pairingcode:', getParameter('pairingcode'));
|
|
1675
|
-
const pairingCode = getParameter('pairingcode');
|
|
1676
|
-
const ip = this.controllerContext.has('ip') ? this.controllerContext.get<string>('ip') : undefined;
|
|
1677
|
-
const port = this.controllerContext.has('port') ? this.controllerContext.get<number>('port') : undefined;
|
|
1678
|
-
|
|
1679
|
-
let longDiscriminator, setupPin, shortDiscriminator;
|
|
1680
|
-
if (pairingCode !== undefined) {
|
|
1681
|
-
const pairingCodeCodec = ManualPairingCodeCodec.decode(pairingCode);
|
|
1682
|
-
shortDiscriminator = pairingCodeCodec.shortDiscriminator;
|
|
1683
|
-
longDiscriminator = undefined;
|
|
1684
|
-
setupPin = pairingCodeCodec.passcode;
|
|
1685
|
-
this.log.info(`Data extracted from pairing code: ${Logger.toJSON(pairingCodeCodec)}`);
|
|
1686
|
-
} else {
|
|
1687
|
-
longDiscriminator = await this.controllerContext.get('longDiscriminator', 3840);
|
|
1688
|
-
if (longDiscriminator > 4095) throw new Error('Discriminator value must be less than 4096');
|
|
1689
|
-
setupPin = this.controllerContext.get('pin', 20202021);
|
|
1690
|
-
}
|
|
1691
|
-
if ((shortDiscriminator === undefined && longDiscriminator === undefined) || setupPin === undefined) {
|
|
1692
|
-
throw new Error('Please specify the longDiscriminator of the device to commission with -longDiscriminator or provide a valid passcode with -passcode');
|
|
1693
|
-
}
|
|
1694
|
-
|
|
1695
|
-
const options = {
|
|
1696
|
-
commissioning: commissioningOptions,
|
|
1697
|
-
discovery: {
|
|
1698
|
-
knownAddress: ip !== undefined && port !== undefined ? { ip, port, type: 'udp' } : undefined,
|
|
1699
|
-
identifierData: longDiscriminator !== undefined ? { longDiscriminator } : shortDiscriminator !== undefined ? { shortDiscriminator } : {},
|
|
1700
|
-
},
|
|
1701
|
-
passcode: setupPin,
|
|
1702
|
-
} as NodeCommissioningOptions;
|
|
1703
|
-
this.log.info('Commissioning with options:', options);
|
|
1704
|
-
const nodeId = await this.commissioningController.commissionNode(options);
|
|
1705
|
-
this.log.info(`Commissioning successfully done with nodeId: ${nodeId}`);
|
|
1706
|
-
this.log.info('ActiveSessionInformation:', this.commissioningController.getActiveSessionInformation());
|
|
1707
|
-
} // (hasParameter('pairingcode'))
|
|
1708
|
-
|
|
1709
|
-
if (hasParameter('unpairall')) {
|
|
1710
|
-
this.log.info('***Commissioning controller unpairing all nodes...');
|
|
1711
|
-
const nodeIds = this.commissioningController.getCommissionedNodes();
|
|
1712
|
-
for (const nodeId of nodeIds) {
|
|
1713
|
-
this.log.info('***Commissioning controller unpairing node:', nodeId);
|
|
1714
|
-
await this.commissioningController.removeNode(nodeId);
|
|
1715
|
-
}
|
|
1716
|
-
return;
|
|
1717
|
-
}
|
|
1718
|
-
|
|
1719
|
-
if (hasParameter('discover')) {
|
|
1720
|
-
// const discover = await this.commissioningController.discoverCommissionableDevices({ productId: 0x8000, deviceType: 0xfff1 });
|
|
1721
|
-
// console.log(discover);
|
|
1722
|
-
}
|
|
1723
|
-
|
|
1724
|
-
if (!this.commissioningController.isCommissioned()) {
|
|
1725
|
-
this.log.info('***Commissioning controller is not commissioned: use matterbridge -controller -pairingcode [pairingcode] to commission a device');
|
|
1726
|
-
return;
|
|
1727
|
-
}
|
|
1728
|
-
|
|
1729
|
-
const nodeIds = this.commissioningController.getCommissionedNodes();
|
|
1730
|
-
this.log.info(`***Commissioning controller is commissioned ${this.commissioningController.isCommissioned()} and has ${nodeIds.length} nodes commisioned: `);
|
|
1731
|
-
for (const nodeId of nodeIds) {
|
|
1732
|
-
this.log.info(`***Connecting to commissioned node: ${nodeId}`);
|
|
1733
|
-
|
|
1734
|
-
const node = await this.commissioningController.connectNode(nodeId, {
|
|
1735
|
-
autoSubscribe: false,
|
|
1736
|
-
attributeChangedCallback: (peerNodeId, { path: { nodeId, clusterId, endpointId, attributeName }, value }) =>
|
|
1737
|
-
this.log.info(`***Commissioning controller attributeChangedCallback ${peerNodeId}: attribute ${nodeId}/${endpointId}/${clusterId}/${attributeName} changed to ${Logger.toJSON(value)}`),
|
|
1738
|
-
eventTriggeredCallback: (peerNodeId, { path: { nodeId, clusterId, endpointId, eventName }, events }) =>
|
|
1739
|
-
this.log.info(`***Commissioning controller eventTriggeredCallback ${peerNodeId}: Event ${nodeId}/${endpointId}/${clusterId}/${eventName} triggered with ${Logger.toJSON(events)}`),
|
|
1740
|
-
stateInformationCallback: (peerNodeId, info) => {
|
|
1741
|
-
switch (info) {
|
|
1742
|
-
case NodeStateInformation.Connected:
|
|
1743
|
-
this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} connected`);
|
|
1744
|
-
break;
|
|
1745
|
-
case NodeStateInformation.Disconnected:
|
|
1746
|
-
this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} disconnected`);
|
|
1747
|
-
break;
|
|
1748
|
-
case NodeStateInformation.Reconnecting:
|
|
1749
|
-
this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} reconnecting`);
|
|
1750
|
-
break;
|
|
1751
|
-
case NodeStateInformation.WaitingForDeviceDiscovery:
|
|
1752
|
-
this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} waiting for device discovery`);
|
|
1753
|
-
break;
|
|
1754
|
-
case NodeStateInformation.StructureChanged:
|
|
1755
|
-
this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} structure changed`);
|
|
1756
|
-
break;
|
|
1757
|
-
case NodeStateInformation.Decommissioned:
|
|
1758
|
-
this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} decommissioned`);
|
|
1759
|
-
break;
|
|
1760
|
-
default:
|
|
1761
|
-
this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} NodeStateInformation.${info}`);
|
|
1762
|
-
break;
|
|
1763
|
-
}
|
|
1764
|
-
},
|
|
1765
|
-
});
|
|
1766
|
-
|
|
1767
|
-
node.logStructure();
|
|
1768
|
-
|
|
1769
|
-
// Get the interaction client
|
|
1770
|
-
this.log.info('Getting the interaction client');
|
|
1771
|
-
const interactionClient = await node.getInteractionClient();
|
|
1772
|
-
let cluster;
|
|
1773
|
-
let attributes;
|
|
1774
|
-
|
|
1775
|
-
// Log BasicInformationCluster
|
|
1776
|
-
cluster = BasicInformationCluster;
|
|
1777
|
-
attributes = await interactionClient.getMultipleAttributes({
|
|
1778
|
-
attributes: [{ clusterId: cluster.id }],
|
|
1779
|
-
});
|
|
1780
|
-
if (attributes.length > 0) this.log.info(`Cluster: ${idn}${cluster.name}${rs}${nf} attributes:`);
|
|
1781
|
-
attributes.forEach((attribute) => {
|
|
1782
|
-
this.log.info(
|
|
1783
|
-
`- 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}`,
|
|
1784
|
-
);
|
|
1785
|
-
});
|
|
1786
|
-
|
|
1787
|
-
// Log PowerSourceCluster
|
|
1788
|
-
cluster = PowerSourceCluster;
|
|
1789
|
-
attributes = await interactionClient.getMultipleAttributes({
|
|
1790
|
-
attributes: [{ clusterId: cluster.id }],
|
|
1791
|
-
});
|
|
1792
|
-
if (attributes.length > 0) this.log.info(`Cluster: ${idn}${cluster.name}${rs}${nf} attributes:`);
|
|
1793
|
-
attributes.forEach((attribute) => {
|
|
1794
|
-
this.log.info(
|
|
1795
|
-
`- 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}`,
|
|
1796
|
-
);
|
|
1797
|
-
});
|
|
1798
|
-
|
|
1799
|
-
// Log ThreadNetworkDiagnostics
|
|
1800
|
-
cluster = ThreadNetworkDiagnosticsCluster;
|
|
1801
|
-
attributes = await interactionClient.getMultipleAttributes({
|
|
1802
|
-
attributes: [{ clusterId: cluster.id }],
|
|
1803
|
-
});
|
|
1804
|
-
if (attributes.length > 0) this.log.info(`Cluster: ${idn}${cluster.name}${rs}${nf} attributes:`);
|
|
1805
|
-
attributes.forEach((attribute) => {
|
|
1806
|
-
this.log.info(
|
|
1807
|
-
`- 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}`,
|
|
1808
|
-
);
|
|
1809
|
-
});
|
|
1810
|
-
|
|
1811
|
-
// Log SwitchCluster
|
|
1812
|
-
cluster = SwitchCluster;
|
|
1813
|
-
attributes = await interactionClient.getMultipleAttributes({
|
|
1814
|
-
attributes: [{ clusterId: cluster.id }],
|
|
1815
|
-
});
|
|
1816
|
-
if (attributes.length > 0) this.log.info(`Cluster: ${idn}${cluster.name}${rs}${nf} attributes:`);
|
|
1817
|
-
attributes.forEach((attribute) => {
|
|
1818
|
-
this.log.info(
|
|
1819
|
-
`- 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}`,
|
|
1820
|
-
);
|
|
1821
|
-
});
|
|
1822
|
-
|
|
1823
|
-
this.log.info('Subscribing to all attributes and events');
|
|
1824
|
-
await node.subscribeAllAttributesAndEvents({
|
|
1825
|
-
ignoreInitialTriggers: false,
|
|
1826
|
-
attributeChangedCallback: ({ path: { nodeId, clusterId, endpointId, attributeName }, version, value }) =>
|
|
1827
|
-
this.log.info(
|
|
1828
|
-
`***${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}`,
|
|
1829
|
-
),
|
|
1830
|
-
eventTriggeredCallback: ({ path: { nodeId, clusterId, endpointId, eventName }, events }) => {
|
|
1831
|
-
this.log.info(
|
|
1832
|
-
`***${db}Commissioning controller eventTriggeredCallback: event ${BLUE}${nodeId}${db}/${or}${endpointId}${db}/${hk}${getClusterNameById(clusterId)}${db}/${zb}${eventName}${db} triggered with ${debugStringify(events ?? { none: true })}`,
|
|
1833
|
-
);
|
|
1834
|
-
},
|
|
1835
|
-
});
|
|
1836
|
-
this.log.info('Subscribed to all attributes and events');
|
|
1837
|
-
}
|
|
1838
|
-
*/
|
|
1839
1299
|
}
|
|
1840
|
-
/** */
|
|
1841
|
-
/** Matter.js methods */
|
|
1842
|
-
/** */
|
|
1843
|
-
/**
|
|
1844
|
-
* Starts the matter storage with name Matterbridge, create the matterbridge context and performs a backup.
|
|
1845
|
-
*
|
|
1846
|
-
* @returns {Promise<void>} - A promise that resolves when the storage is started.
|
|
1847
|
-
*/
|
|
1848
1300
|
async startMatterStorage() {
|
|
1849
|
-
// Setup Matter storage
|
|
1850
1301
|
this.log.info(`Starting matter node storage...`);
|
|
1851
1302
|
this.matterStorageService = this.environment.get(StorageService);
|
|
1852
1303
|
this.log.info(`Matter node storage service created: ${this.matterStorageService.location}`);
|
|
@@ -1854,17 +1305,8 @@ export class Matterbridge extends EventEmitter {
|
|
|
1854
1305
|
this.log.info('Matter node storage manager "Matterbridge" created');
|
|
1855
1306
|
this.matterbridgeContext = await this.createServerNodeContext('Matterbridge', 'Matterbridge', this.aggregatorDeviceType, this.aggregatorVendorId, this.aggregatorVendorName, this.aggregatorProductId, this.aggregatorProductName, this.aggregatorSerialNumber, this.aggregatorUniqueId);
|
|
1856
1307
|
this.log.info('Matter node storage started');
|
|
1857
|
-
// Backup matter storage since it is created/opened correctly
|
|
1858
1308
|
await this.backupMatterStorage(path.join(this.matterbridgeDirectory, MATTER_STORAGE_NAME), path.join(this.matterbridgeDirectory, MATTER_STORAGE_NAME + '.backup'));
|
|
1859
1309
|
}
|
|
1860
|
-
/**
|
|
1861
|
-
* Makes a backup copy of the specified matter storage directory.
|
|
1862
|
-
*
|
|
1863
|
-
* @param {string} storageName - The name of the storage directory to be backed up.
|
|
1864
|
-
* @param {string} backupName - The name of the backup directory to be created.
|
|
1865
|
-
* @private
|
|
1866
|
-
* @returns {Promise<void>} A promise that resolves when the has been done.
|
|
1867
|
-
*/
|
|
1868
1310
|
async backupMatterStorage(storageName, backupName) {
|
|
1869
1311
|
this.log.info('Creating matter node storage backup...');
|
|
1870
1312
|
try {
|
|
@@ -1875,11 +1317,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1875
1317
|
this.log.error(`Error creating matter node storage backup from ${storageName} to ${backupName}:`, error);
|
|
1876
1318
|
}
|
|
1877
1319
|
}
|
|
1878
|
-
/**
|
|
1879
|
-
* Stops the matter storage.
|
|
1880
|
-
*
|
|
1881
|
-
* @returns {Promise<void>} A promise that resolves when the storage is stopped.
|
|
1882
|
-
*/
|
|
1883
1320
|
async stopMatterStorage() {
|
|
1884
1321
|
this.log.info('Closing matter node storage...');
|
|
1885
1322
|
await this.matterStorageManager?.close();
|
|
@@ -1888,20 +1325,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1888
1325
|
this.matterbridgeContext = undefined;
|
|
1889
1326
|
this.log.info('Matter node storage closed');
|
|
1890
1327
|
}
|
|
1891
|
-
/**
|
|
1892
|
-
* Creates a server node storage context.
|
|
1893
|
-
*
|
|
1894
|
-
* @param {string} storeId - The storeId.
|
|
1895
|
-
* @param {string} deviceName - The name of the device.
|
|
1896
|
-
* @param {DeviceTypeId} deviceType - The device type of the device.
|
|
1897
|
-
* @param {number} vendorId - The vendor ID.
|
|
1898
|
-
* @param {string} vendorName - The vendor name.
|
|
1899
|
-
* @param {number} productId - The product ID.
|
|
1900
|
-
* @param {string} productName - The product name.
|
|
1901
|
-
* @param {string} [serialNumber] - The serial number of the device (optional).
|
|
1902
|
-
* @param {string} [uniqueId] - The unique ID of the device (optional).
|
|
1903
|
-
* @returns {Promise<StorageContext>} The storage context for the commissioning server.
|
|
1904
|
-
*/
|
|
1905
1328
|
async createServerNodeContext(storeId, deviceName, deviceType, vendorId, vendorName, productId, productName, serialNumber, uniqueId) {
|
|
1906
1329
|
const { randomBytes } = await import('node:crypto');
|
|
1907
1330
|
if (!this.matterStorageService)
|
|
@@ -1941,15 +1364,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1941
1364
|
this.log.debug(`- hardwareVersion: ${await storageContext.get('hardwareVersion')} hardwareVersionString: ${await storageContext.get('hardwareVersionString')}`);
|
|
1942
1365
|
return storageContext;
|
|
1943
1366
|
}
|
|
1944
|
-
/**
|
|
1945
|
-
* Creates a server node.
|
|
1946
|
-
*
|
|
1947
|
-
* @param {StorageContext} storageContext - The storage context for the server node.
|
|
1948
|
-
* @param {number} [port] - The port number for the server node. Defaults to 5540.
|
|
1949
|
-
* @param {number} [passcode] - The passcode for the server node. Defaults to 20242025.
|
|
1950
|
-
* @param {number} [discriminator] - The discriminator for the server node. Defaults to 3850.
|
|
1951
|
-
* @returns {Promise<ServerNode<ServerNode.RootEndpoint>>} A promise that resolves to the created server node.
|
|
1952
|
-
*/
|
|
1953
1367
|
async createServerNode(storageContext, port = 5540, passcode = 20242025, discriminator = 3850) {
|
|
1954
1368
|
const storeId = await storageContext.get('storeId');
|
|
1955
1369
|
this.log.notice(`Creating server node for ${storeId} on port ${port} with passcode ${passcode} and discriminator ${discriminator}...`);
|
|
@@ -1959,35 +1373,25 @@ export class Matterbridge extends EventEmitter {
|
|
|
1959
1373
|
this.log.debug(`- uniqueId: ${await storageContext.get('uniqueId')}`);
|
|
1960
1374
|
this.log.debug(`- softwareVersion: ${await storageContext.get('softwareVersion')} softwareVersionString: ${await storageContext.get('softwareVersionString')}`);
|
|
1961
1375
|
this.log.debug(`- hardwareVersion: ${await storageContext.get('hardwareVersion')} hardwareVersionString: ${await storageContext.get('hardwareVersionString')}`);
|
|
1962
|
-
/**
|
|
1963
|
-
* Create a Matter ServerNode, which contains the Root Endpoint and all relevant data and configuration
|
|
1964
|
-
*/
|
|
1965
1376
|
const serverNode = await ServerNode.create({
|
|
1966
|
-
// Required: Give the Node a unique ID which is used to store the state of this node
|
|
1967
1377
|
id: storeId,
|
|
1968
|
-
// Environment to run the server node in
|
|
1969
1378
|
environment: this.environment,
|
|
1970
|
-
// Provide Network relevant configuration like the port
|
|
1971
1379
|
network: {
|
|
1972
1380
|
listeningAddressIpv4: this.ipv4Address,
|
|
1973
1381
|
listeningAddressIpv6: this.ipv6Address,
|
|
1974
1382
|
port,
|
|
1975
1383
|
},
|
|
1976
|
-
// Provide the certificate for the device
|
|
1977
1384
|
operationalCredentials: {
|
|
1978
1385
|
certification: this.certification,
|
|
1979
1386
|
},
|
|
1980
|
-
// Provide Commissioning relevant settings
|
|
1981
1387
|
commissioning: {
|
|
1982
1388
|
passcode,
|
|
1983
1389
|
discriminator,
|
|
1984
1390
|
},
|
|
1985
|
-
// Provide Node announcement settings
|
|
1986
1391
|
productDescription: {
|
|
1987
1392
|
name: await storageContext.get('deviceName'),
|
|
1988
1393
|
deviceType: DeviceTypeId(await storageContext.get('deviceType')),
|
|
1989
1394
|
},
|
|
1990
|
-
// Provide defaults for the BasicInformation cluster on the Root endpoint
|
|
1991
1395
|
basicInformation: {
|
|
1992
1396
|
vendorId: VendorId(await storageContext.get('vendorId')),
|
|
1993
1397
|
vendorName: await storageContext.get('vendorName'),
|
|
@@ -2004,23 +1408,17 @@ export class Matterbridge extends EventEmitter {
|
|
|
2004
1408
|
reachable: true,
|
|
2005
1409
|
},
|
|
2006
1410
|
});
|
|
2007
|
-
/**
|
|
2008
|
-
* This event is triggered when the device is initially commissioned successfully.
|
|
2009
|
-
* This means: It is added to the first fabric.
|
|
2010
|
-
*/
|
|
2011
1411
|
serverNode.lifecycle.commissioned.on(() => {
|
|
2012
1412
|
this.log.notice(`Server node for ${storeId} was initially commissioned successfully!`);
|
|
2013
1413
|
this.advertisingNodes.delete(storeId);
|
|
2014
1414
|
this.frontend.wssSendRefreshRequired('matter', { matter: { ...this.getServerNodeData(serverNode) } });
|
|
2015
1415
|
});
|
|
2016
|
-
/** This event is triggered when all fabrics are removed from the device, usually it also does a factory reset then. */
|
|
2017
1416
|
serverNode.lifecycle.decommissioned.on(() => {
|
|
2018
1417
|
this.log.notice(`Server node for ${storeId} was fully decommissioned successfully!`);
|
|
2019
1418
|
this.advertisingNodes.delete(storeId);
|
|
2020
1419
|
this.frontend.wssSendRefreshRequired('matter', { matter: { ...this.getServerNodeData(serverNode) } });
|
|
2021
1420
|
this.frontend.wssSendSnackbarMessage(`${storeId} is offline`, 5, 'warning');
|
|
2022
1421
|
});
|
|
2023
|
-
/** This event is triggered when the device went online. This means that it is discoverable in the network. */
|
|
2024
1422
|
serverNode.lifecycle.online.on(async () => {
|
|
2025
1423
|
this.log.notice(`Server node for ${storeId} is online`);
|
|
2026
1424
|
if (!serverNode.lifecycle.isCommissioned) {
|
|
@@ -2031,16 +1429,13 @@ export class Matterbridge extends EventEmitter {
|
|
|
2031
1429
|
this.log.notice(`Manual pairing code: ${manualPairingCode}`);
|
|
2032
1430
|
}
|
|
2033
1431
|
else {
|
|
2034
|
-
// istanbul ignore next
|
|
2035
1432
|
this.log.notice(`Server node for ${storeId} is already commissioned. Waiting for controllers to connect ...`);
|
|
2036
|
-
// istanbul ignore next
|
|
2037
1433
|
this.advertisingNodes.delete(storeId);
|
|
2038
1434
|
}
|
|
2039
1435
|
this.frontend.wssSendRefreshRequired('matter', { matter: { ...this.getServerNodeData(serverNode) } });
|
|
2040
1436
|
this.frontend.wssSendSnackbarMessage(`${storeId} is online`, 5, 'success');
|
|
2041
1437
|
this.emit('online', storeId);
|
|
2042
1438
|
});
|
|
2043
|
-
/** This event is triggered when the device went offline. it is not longer discoverable or connectable in the network. */
|
|
2044
1439
|
serverNode.lifecycle.offline.on(() => {
|
|
2045
1440
|
this.log.notice(`Server node for ${storeId} is offline`);
|
|
2046
1441
|
this.advertisingNodes.delete(storeId);
|
|
@@ -2048,15 +1443,11 @@ export class Matterbridge extends EventEmitter {
|
|
|
2048
1443
|
this.frontend.wssSendSnackbarMessage(`${storeId} is offline`, 5, 'warning');
|
|
2049
1444
|
this.emit('offline', storeId);
|
|
2050
1445
|
});
|
|
2051
|
-
/**
|
|
2052
|
-
* This event is triggered when a fabric is added, removed or updated on the device. Use this if more granular
|
|
2053
|
-
* information is needed.
|
|
2054
|
-
*/
|
|
2055
1446
|
serverNode.events.commissioning.fabricsChanged.on((fabricIndex, fabricAction) => {
|
|
2056
1447
|
let action = '';
|
|
2057
1448
|
switch (fabricAction) {
|
|
2058
1449
|
case FabricAction.Added:
|
|
2059
|
-
this.advertisingNodes.delete(storeId);
|
|
1450
|
+
this.advertisingNodes.delete(storeId);
|
|
2060
1451
|
action = 'added';
|
|
2061
1452
|
break;
|
|
2062
1453
|
case FabricAction.Removed:
|
|
@@ -2069,22 +1460,14 @@ export class Matterbridge extends EventEmitter {
|
|
|
2069
1460
|
this.log.notice(`Commissioned fabric index ${fabricIndex} ${action} on server node for ${storeId}: ${debugStringify(serverNode.state.commissioning.fabrics[fabricIndex])}`);
|
|
2070
1461
|
this.frontend.wssSendRefreshRequired('matter', { matter: { ...this.getServerNodeData(serverNode) } });
|
|
2071
1462
|
});
|
|
2072
|
-
/**
|
|
2073
|
-
* This event is triggered when an operative new session was opened by a Controller.
|
|
2074
|
-
* It is not triggered for the initial commissioning process, just afterwards for real connections.
|
|
2075
|
-
*/
|
|
2076
1463
|
serverNode.events.sessions.opened.on((session) => {
|
|
2077
1464
|
this.log.notice(`Session opened on server node for ${storeId}: ${debugStringify(session)}`);
|
|
2078
1465
|
this.frontend.wssSendRefreshRequired('matter', { matter: { ...this.getServerNodeData(serverNode) } });
|
|
2079
1466
|
});
|
|
2080
|
-
/**
|
|
2081
|
-
* This event is triggered when an operative session is closed by a Controller or because the Device goes offline.
|
|
2082
|
-
*/
|
|
2083
1467
|
serverNode.events.sessions.closed.on((session) => {
|
|
2084
1468
|
this.log.notice(`Session closed on server node for ${storeId}: ${debugStringify(session)}`);
|
|
2085
1469
|
this.frontend.wssSendRefreshRequired('matter', { matter: { ...this.getServerNodeData(serverNode) } });
|
|
2086
1470
|
});
|
|
2087
|
-
/** This event is triggered when a subscription gets added or removed on an operative session. */
|
|
2088
1471
|
serverNode.events.sessions.subscriptionsChanged.on((session) => {
|
|
2089
1472
|
this.log.notice(`Session subscriptions changed on server node for ${storeId}: ${debugStringify(session)}`);
|
|
2090
1473
|
this.frontend.wssSendRefreshRequired('matter', { matter: { ...this.getServerNodeData(serverNode) } });
|
|
@@ -2092,12 +1475,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2092
1475
|
this.log.info(`Created server node for ${storeId}`);
|
|
2093
1476
|
return serverNode;
|
|
2094
1477
|
}
|
|
2095
|
-
/**
|
|
2096
|
-
* Gets the matter sanitized data of the specified server node.
|
|
2097
|
-
*
|
|
2098
|
-
* @param {ServerNode} [serverNode] - The server node to start.
|
|
2099
|
-
* @returns {ApiMatter} The sanitized data of the server node.
|
|
2100
|
-
*/
|
|
2101
1478
|
getServerNodeData(serverNode) {
|
|
2102
1479
|
const advertiseTime = this.advertisingNodes.get(serverNode.id) || 0;
|
|
2103
1480
|
return {
|
|
@@ -2114,25 +1491,12 @@ export class Matterbridge extends EventEmitter {
|
|
|
2114
1491
|
serialNumber: serverNode.state.basicInformation.serialNumber,
|
|
2115
1492
|
};
|
|
2116
1493
|
}
|
|
2117
|
-
/**
|
|
2118
|
-
* Starts the specified server node.
|
|
2119
|
-
*
|
|
2120
|
-
* @param {ServerNode} [matterServerNode] - The server node to start.
|
|
2121
|
-
* @returns {Promise<void>} A promise that resolves when the server node has started.
|
|
2122
|
-
*/
|
|
2123
1494
|
async startServerNode(matterServerNode) {
|
|
2124
1495
|
if (!matterServerNode)
|
|
2125
1496
|
return;
|
|
2126
1497
|
this.log.notice(`Starting ${matterServerNode.id} server node`);
|
|
2127
1498
|
await matterServerNode.start();
|
|
2128
1499
|
}
|
|
2129
|
-
/**
|
|
2130
|
-
* Stops the specified server node.
|
|
2131
|
-
*
|
|
2132
|
-
* @param {ServerNode} matterServerNode - The server node to stop.
|
|
2133
|
-
* @param {number} [timeout] - The timeout in milliseconds for stopping the server node. Defaults to 30 seconds.
|
|
2134
|
-
* @returns {Promise<void>} A promise that resolves when the server node has stopped.
|
|
2135
|
-
*/
|
|
2136
1500
|
async stopServerNode(matterServerNode, timeout = 30000) {
|
|
2137
1501
|
if (!matterServerNode)
|
|
2138
1502
|
return;
|
|
@@ -2145,25 +1509,12 @@ export class Matterbridge extends EventEmitter {
|
|
|
2145
1509
|
this.log.error(`Failed to close ${matterServerNode.id} server node: ${error instanceof Error ? error.message : error}`);
|
|
2146
1510
|
}
|
|
2147
1511
|
}
|
|
2148
|
-
/**
|
|
2149
|
-
* Creates an aggregator node with the specified storage context.
|
|
2150
|
-
*
|
|
2151
|
-
* @param {StorageContext} storageContext - The storage context for the aggregator node.
|
|
2152
|
-
* @returns {Promise<Endpoint<AggregatorEndpoint>>} A promise that resolves to the created aggregator node.
|
|
2153
|
-
*/
|
|
2154
1512
|
async createAggregatorNode(storageContext) {
|
|
2155
1513
|
this.log.notice(`Creating ${await storageContext.get('storeId')} aggregator...`);
|
|
2156
1514
|
const aggregatorNode = new Endpoint(AggregatorEndpoint, { id: `${await storageContext.get('storeId')}` });
|
|
2157
1515
|
this.log.info(`Created ${await storageContext.get('storeId')} aggregator`);
|
|
2158
1516
|
return aggregatorNode;
|
|
2159
1517
|
}
|
|
2160
|
-
/**
|
|
2161
|
-
* Creates and configures the server node for an accessory plugin for a given device.
|
|
2162
|
-
*
|
|
2163
|
-
* @param {Plugin} plugin - The plugin to configure.
|
|
2164
|
-
* @param {MatterbridgeEndpoint} device - The device to associate with the plugin.
|
|
2165
|
-
* @returns {Promise<void>} A promise that resolves when the server node for the accessory plugin is created and configured.
|
|
2166
|
-
*/
|
|
2167
1518
|
async createAccessoryPlugin(plugin, device) {
|
|
2168
1519
|
if (!plugin.locked && device.deviceType && device.deviceName && device.vendorId && device.productId && device.vendorName && device.productName) {
|
|
2169
1520
|
plugin.locked = true;
|
|
@@ -2175,12 +1526,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2175
1526
|
await plugin.serverNode.add(device);
|
|
2176
1527
|
}
|
|
2177
1528
|
}
|
|
2178
|
-
/**
|
|
2179
|
-
* Creates and configures the server node and the aggregator node for a dynamic plugin.
|
|
2180
|
-
*
|
|
2181
|
-
* @param {Plugin} plugin - The plugin to configure.
|
|
2182
|
-
* @returns {Promise<void>} A promise that resolves when the server node and the aggregator node for the dynamic plugin is created and configured.
|
|
2183
|
-
*/
|
|
2184
1529
|
async createDynamicPlugin(plugin) {
|
|
2185
1530
|
if (!plugin.locked) {
|
|
2186
1531
|
plugin.locked = true;
|
|
@@ -2193,13 +1538,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2193
1538
|
await plugin.serverNode.add(plugin.aggregatorNode);
|
|
2194
1539
|
}
|
|
2195
1540
|
}
|
|
2196
|
-
/**
|
|
2197
|
-
* Creates and configures the server node for a single not bridged device.
|
|
2198
|
-
*
|
|
2199
|
-
* @param {Plugin} plugin - The plugin to configure.
|
|
2200
|
-
* @param {MatterbridgeEndpoint} device - The device to associate with the plugin.
|
|
2201
|
-
* @returns {Promise<void>} A promise that resolves when the server node for the accessory plugin is created and configured.
|
|
2202
|
-
*/
|
|
2203
1541
|
async createDeviceServerNode(plugin, device) {
|
|
2204
1542
|
if (device.mode === 'server' && !device.serverNode && device.deviceType && device.deviceName && device.vendorId && device.vendorName && device.productId && device.productName) {
|
|
2205
1543
|
this.log.debug(`Creating device ${plg}${plugin.name}${db}:${dev}${device.deviceName}${db} server node...`);
|
|
@@ -2210,15 +1548,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
2210
1548
|
this.log.debug(`Added ${plg}${plugin.name}${db}:${dev}${device.deviceName}${db} to server node`);
|
|
2211
1549
|
}
|
|
2212
1550
|
}
|
|
2213
|
-
/**
|
|
2214
|
-
* Adds a MatterbridgeEndpoint to the specified plugin.
|
|
2215
|
-
*
|
|
2216
|
-
* @param {string} pluginName - The name of the plugin.
|
|
2217
|
-
* @param {MatterbridgeEndpoint} device - The device to add as a bridged endpoint.
|
|
2218
|
-
* @returns {Promise<void>} A promise that resolves when the bridged endpoint has been added.
|
|
2219
|
-
*/
|
|
2220
1551
|
async addBridgedEndpoint(pluginName, device) {
|
|
2221
|
-
// Check if the plugin is registered
|
|
2222
1552
|
const plugin = this.plugins.get(pluginName);
|
|
2223
1553
|
if (!plugin) {
|
|
2224
1554
|
this.log.error(`Error adding bridged endpoint ${dev}${device.deviceName}${er} (${zb}${device.id}${er}) plugin ${plg}${pluginName}${er} not found`);
|
|
@@ -2238,7 +1568,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2238
1568
|
}
|
|
2239
1569
|
else if (this.bridgeMode === 'bridge') {
|
|
2240
1570
|
if (device.mode === 'matter') {
|
|
2241
|
-
// Register and add the device to the matterbridge server node
|
|
2242
1571
|
this.log.debug(`Adding matter endpoint ${plg}${pluginName}${db}:${dev}${device.deviceName}${db} to Matterbridge server node...`);
|
|
2243
1572
|
if (!this.serverNode) {
|
|
2244
1573
|
this.log.error('Server node not found for Matterbridge');
|
|
@@ -2255,7 +1584,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2255
1584
|
}
|
|
2256
1585
|
}
|
|
2257
1586
|
else {
|
|
2258
|
-
// Register and add the device to the matterbridge aggregator node
|
|
2259
1587
|
this.log.debug(`Adding bridged endpoint ${plg}${pluginName}${db}:${dev}${device.deviceName}${db} to Matterbridge aggregator node`);
|
|
2260
1588
|
if (!this.aggregatorNode) {
|
|
2261
1589
|
this.log.error('Aggregator node not found for Matterbridge');
|
|
@@ -2273,7 +1601,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2273
1601
|
}
|
|
2274
1602
|
}
|
|
2275
1603
|
else if (this.bridgeMode === 'childbridge') {
|
|
2276
|
-
// Register and add the device to the plugin server node
|
|
2277
1604
|
if (plugin.type === 'AccessoryPlatform') {
|
|
2278
1605
|
try {
|
|
2279
1606
|
this.log.debug(`Creating endpoint ${dev}${device.deviceName}${db} for AccessoryPlatform plugin ${plg}${plugin.name}${db} server node`);
|
|
@@ -2297,12 +1624,10 @@ export class Matterbridge extends EventEmitter {
|
|
|
2297
1624
|
return;
|
|
2298
1625
|
}
|
|
2299
1626
|
}
|
|
2300
|
-
// Register and add the device to the plugin aggregator node
|
|
2301
1627
|
if (plugin.type === 'DynamicPlatform') {
|
|
2302
1628
|
try {
|
|
2303
1629
|
this.log.debug(`Adding bridged endpoint ${dev}${device.deviceName}${db} for DynamicPlatform plugin ${plg}${plugin.name}${db} aggregator node`);
|
|
2304
1630
|
await this.createDynamicPlugin(plugin);
|
|
2305
|
-
// Fast plugins can add another device before the server node is ready, so we wait for the server node to be ready
|
|
2306
1631
|
await waiter(`createDynamicPlugin(${plugin.name})`, () => plugin.serverNode?.hasParts === true);
|
|
2307
1632
|
if (!plugin.aggregatorNode) {
|
|
2308
1633
|
this.log.error(`Aggregator node not found for plugin ${plg}${plugin.name}${er}`);
|
|
@@ -2323,28 +1648,17 @@ export class Matterbridge extends EventEmitter {
|
|
|
2323
1648
|
}
|
|
2324
1649
|
if (plugin.registeredDevices !== undefined)
|
|
2325
1650
|
plugin.registeredDevices++;
|
|
2326
|
-
// Add the device to the DeviceManager
|
|
2327
1651
|
this.devices.set(device);
|
|
2328
|
-
// Subscribe to the attributes changed event
|
|
2329
1652
|
await this.subscribeAttributeChanged(plugin, device);
|
|
2330
1653
|
this.log.info(`Added and registered bridged endpoint (${plugin.registeredDevices}) ${dev}${device.deviceName}${nf} (${dev}${device.id}${nf}) for plugin ${plg}${pluginName}${nf}`);
|
|
2331
1654
|
}
|
|
2332
|
-
/**
|
|
2333
|
-
* Removes a MatterbridgeEndpoint from the specified plugin.
|
|
2334
|
-
*
|
|
2335
|
-
* @param {string} pluginName - The name of the plugin.
|
|
2336
|
-
* @param {MatterbridgeEndpoint} device - The device to remove as a bridged endpoint.
|
|
2337
|
-
* @returns {Promise<void>} A promise that resolves when the bridged endpoint has been removed.
|
|
2338
|
-
*/
|
|
2339
1655
|
async removeBridgedEndpoint(pluginName, device) {
|
|
2340
1656
|
this.log.debug(`Removing bridged endpoint ${plg}${pluginName}${db}:${dev}${device.deviceName}${db} (${zb}${device.name}${db}) for plugin ${plg}${pluginName}${db}`);
|
|
2341
|
-
// Check if the plugin is registered
|
|
2342
1657
|
const plugin = this.plugins.get(pluginName);
|
|
2343
1658
|
if (!plugin) {
|
|
2344
1659
|
this.log.error(`Error removing bridged endpoint ${dev}${device.deviceName}${er} (${zb}${device.name}${er}) for plugin ${plg}${pluginName}${er}: plugin not found`);
|
|
2345
1660
|
return;
|
|
2346
1661
|
}
|
|
2347
|
-
// Unregister and remove the device from the matterbridge aggregator node
|
|
2348
1662
|
if (this.bridgeMode === 'bridge') {
|
|
2349
1663
|
if (!this.aggregatorNode) {
|
|
2350
1664
|
this.log.error(`Error removing bridged endpoint ${dev}${device.deviceName}${er} (${zb}${device.name}${er}) for plugin ${plg}${pluginName}${er}: aggregator node not found`);
|
|
@@ -2357,10 +1671,8 @@ export class Matterbridge extends EventEmitter {
|
|
|
2357
1671
|
}
|
|
2358
1672
|
else if (this.bridgeMode === 'childbridge') {
|
|
2359
1673
|
if (plugin.type === 'AccessoryPlatform') {
|
|
2360
|
-
// Nothing to do here since the server node has no aggregator node but only the device itself
|
|
2361
1674
|
}
|
|
2362
1675
|
else if (plugin.type === 'DynamicPlatform') {
|
|
2363
|
-
// Unregister and remove the device from the plugin aggregator node
|
|
2364
1676
|
if (!plugin.aggregatorNode) {
|
|
2365
1677
|
this.log.error(`Error removing bridged endpoint ${dev}${device.deviceName}${er} (${zb}${device.name}${er}) for plugin ${plg}${pluginName}${er}: aggregator node not found`);
|
|
2366
1678
|
return;
|
|
@@ -2371,21 +1683,8 @@ export class Matterbridge extends EventEmitter {
|
|
|
2371
1683
|
if (plugin.registeredDevices !== undefined)
|
|
2372
1684
|
plugin.registeredDevices--;
|
|
2373
1685
|
}
|
|
2374
|
-
// Remove the device from the DeviceManager
|
|
2375
1686
|
this.devices.remove(device);
|
|
2376
1687
|
}
|
|
2377
|
-
/**
|
|
2378
|
-
* Removes all bridged endpoints from the specified plugin.
|
|
2379
|
-
*
|
|
2380
|
-
* @param {string} pluginName - The name of the plugin.
|
|
2381
|
-
* @param {number} [delay] - The delay in milliseconds between removing each bridged endpoint (default: 0).
|
|
2382
|
-
* @returns {Promise<void>} A promise that resolves when all bridged endpoints have been removed.
|
|
2383
|
-
*
|
|
2384
|
-
* @remarks
|
|
2385
|
-
* This method iterates through all devices in the DeviceManager and removes each bridged endpoint associated with the specified plugin.
|
|
2386
|
-
* It also applies a delay between each removal if specified.
|
|
2387
|
-
* The delay is useful to allow the controllers to receive a single subscription for each device removed.
|
|
2388
|
-
*/
|
|
2389
1688
|
async removeAllBridgedEndpoints(pluginName, delay = 0) {
|
|
2390
1689
|
this.log.debug(`Removing all bridged endpoints for plugin ${plg}${pluginName}${db}${delay > 0 ? ` with delay ${delay} ms` : ''}`);
|
|
2391
1690
|
for (const device of this.devices.array().filter((device) => device.plugin === pluginName)) {
|
|
@@ -2396,24 +1695,40 @@ export class Matterbridge extends EventEmitter {
|
|
|
2396
1695
|
if (delay > 0)
|
|
2397
1696
|
await wait(2000);
|
|
2398
1697
|
}
|
|
2399
|
-
|
|
2400
|
-
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
|
|
1698
|
+
async addVirtualEndpoint(pluginName, name, type, callback) {
|
|
1699
|
+
const plugin = this.plugins.get(pluginName);
|
|
1700
|
+
if (!plugin) {
|
|
1701
|
+
this.log.error(`Error adding virtual endpoint ${dev}${name}${er} for plugin ${plg}${pluginName}${er}: plugin not found`);
|
|
1702
|
+
return false;
|
|
1703
|
+
}
|
|
1704
|
+
let aggregator;
|
|
1705
|
+
if (this.bridgeMode === 'bridge') {
|
|
1706
|
+
aggregator = this.aggregatorNode;
|
|
1707
|
+
}
|
|
1708
|
+
else if (this.bridgeMode === 'childbridge' && plugin.type === 'DynamicPlatform') {
|
|
1709
|
+
aggregator = plugin.aggregatorNode;
|
|
1710
|
+
}
|
|
1711
|
+
if (aggregator) {
|
|
1712
|
+
if (aggregator.parts.has(name.replaceAll(' ', '') + ':' + type)) {
|
|
1713
|
+
this.log.error(`Virtual endpoint ${dev}${name}${er} already registered for plugin ${plg}${pluginName}${er}. Please use a different name.`);
|
|
1714
|
+
return false;
|
|
1715
|
+
}
|
|
1716
|
+
else {
|
|
1717
|
+
await addVirtualDevice(aggregator, name.slice(0, 32), type, callback);
|
|
1718
|
+
this.log.info(`Created virtual endpoint ${dev}${name}${nf} for plugin ${plg}${pluginName}${nf}`);
|
|
1719
|
+
return true;
|
|
1720
|
+
}
|
|
1721
|
+
}
|
|
1722
|
+
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.`);
|
|
1723
|
+
return false;
|
|
1724
|
+
}
|
|
2408
1725
|
async subscribeAttributeChanged(plugin, device) {
|
|
2409
1726
|
if (!plugin || !device || !device.plugin || !device.serialNumber || !device.uniqueId || !device.maybeNumber)
|
|
2410
1727
|
return;
|
|
2411
1728
|
this.log.info(`Subscribing attributes for endpoint ${dev}${device.deviceName}${nf} (${dev}${device.id}${nf}) plugin ${plg}${plugin.name}${nf}`);
|
|
2412
|
-
// Subscribe to the reachable$Changed event of the BasicInformationServer cluster server of the server node in childbridge mode
|
|
2413
1729
|
if (this.bridgeMode === 'childbridge' && plugin.type === 'AccessoryPlatform' && plugin.serverNode) {
|
|
2414
1730
|
plugin.serverNode.eventsOf(BasicInformationServer).reachable$Changed?.on((reachable) => {
|
|
2415
1731
|
this.log.info(`Accessory endpoint ${dev}${device.deviceName}${nf} (${dev}${device.id}${nf}) is ${reachable ? 'reachable' : 'unreachable'}`);
|
|
2416
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
2417
1732
|
this.frontend.wssSendAttributeChangedMessage(device.plugin, device.serialNumber, device.uniqueId, device.number, device.id, 'BasicInformation', 'reachable', reachable);
|
|
2418
1733
|
});
|
|
2419
1734
|
}
|
|
@@ -2463,7 +1778,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2463
1778
|
this.log.debug(`Subscribing to endpoint ${or}${device.id}${db}:${or}${device.number}${db} attribute ${dev}${sub.cluster}${db}.${dev}${sub.attribute}${db} changes...`);
|
|
2464
1779
|
await device.subscribeAttribute(sub.cluster, sub.attribute, (value) => {
|
|
2465
1780
|
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}`);
|
|
2466
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
2467
1781
|
this.frontend.wssSendAttributeChangedMessage(device.plugin, device.serialNumber, device.uniqueId, device.number, device.id, sub.cluster, sub.attribute, value);
|
|
2468
1782
|
});
|
|
2469
1783
|
}
|
|
@@ -2472,19 +1786,12 @@ export class Matterbridge extends EventEmitter {
|
|
|
2472
1786
|
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...`);
|
|
2473
1787
|
await child.subscribeAttribute(sub.cluster, sub.attribute, (value) => {
|
|
2474
1788
|
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}`);
|
|
2475
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
2476
1789
|
this.frontend.wssSendAttributeChangedMessage(device.plugin, device.serialNumber, device.uniqueId, child.number, child.id, sub.cluster, sub.attribute, value);
|
|
2477
1790
|
});
|
|
2478
1791
|
}
|
|
2479
1792
|
}
|
|
2480
1793
|
}
|
|
2481
1794
|
}
|
|
2482
|
-
/**
|
|
2483
|
-
* Sanitizes the fabric information by converting bigint properties to strings because `res.json` doesn't support bigint.
|
|
2484
|
-
*
|
|
2485
|
-
* @param {ExposedFabricInformation[]} fabricInfo - The array of exposed fabric information objects.
|
|
2486
|
-
* @returns {SanitizedExposedFabricInformation[]} An array of sanitized exposed fabric information objects.
|
|
2487
|
-
*/
|
|
2488
1795
|
sanitizeFabricInformations(fabricInfo) {
|
|
2489
1796
|
return fabricInfo.map((info) => {
|
|
2490
1797
|
return {
|
|
@@ -2498,12 +1805,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2498
1805
|
};
|
|
2499
1806
|
});
|
|
2500
1807
|
}
|
|
2501
|
-
/**
|
|
2502
|
-
* Sanitizes the session information by converting bigint properties to strings because `res.json` doesn't support bigint.
|
|
2503
|
-
*
|
|
2504
|
-
* @param {SessionsBehavior.Session[]} sessions - The array of session information objects.
|
|
2505
|
-
* @returns {SanitizedSession[]} An array of sanitized session information objects.
|
|
2506
|
-
*/
|
|
2507
1808
|
sanitizeSessionInformation(sessions) {
|
|
2508
1809
|
return sessions
|
|
2509
1810
|
.filter((session) => session.isPeerActive)
|
|
@@ -2530,21 +1831,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
2530
1831
|
};
|
|
2531
1832
|
});
|
|
2532
1833
|
}
|
|
2533
|
-
/**
|
|
2534
|
-
* Sets the reachability of the specified aggregator node bridged devices and trigger.
|
|
2535
|
-
*
|
|
2536
|
-
* @param {Endpoint<AggregatorEndpoint>} aggregatorNode - The aggregator node to set the reachability for.
|
|
2537
|
-
* @param {boolean} reachable - A boolean indicating the reachability status to set.
|
|
2538
|
-
*/
|
|
2539
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
2540
1834
|
async setAggregatorReachability(aggregatorNode, reachable) {
|
|
2541
|
-
/*
|
|
2542
|
-
for (const child of aggregatorNode.parts) {
|
|
2543
|
-
this.log.debug(`Setting reachability of ${(child as unknown as MatterbridgeEndpoint)?.deviceName} to ${reachable}`);
|
|
2544
|
-
await child.setStateOf(BridgedDeviceBasicInformationServer, { reachable });
|
|
2545
|
-
child.act((agent) => child.eventsOf(BridgedDeviceBasicInformationServer).reachableChanged.emit({ reachableNewValue: true }, agent.context));
|
|
2546
|
-
}
|
|
2547
|
-
*/
|
|
2548
1835
|
}
|
|
2549
1836
|
getVendorIdName = (vendorId) => {
|
|
2550
1837
|
if (!vendorId)
|
|
@@ -2584,11 +1871,10 @@ export class Matterbridge extends EventEmitter {
|
|
|
2584
1871
|
case 0x1488:
|
|
2585
1872
|
vendorName = '(ShortcutLabsFlic)';
|
|
2586
1873
|
break;
|
|
2587
|
-
case 65521:
|
|
1874
|
+
case 65521:
|
|
2588
1875
|
vendorName = '(MatterTest)';
|
|
2589
1876
|
break;
|
|
2590
1877
|
}
|
|
2591
1878
|
return vendorName;
|
|
2592
1879
|
};
|
|
2593
1880
|
}
|
|
2594
|
-
//# sourceMappingURL=matterbridge.js.map
|