matterbridge 3.5.0 → 3.5.1-dev-20260122-6461be3
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 +18 -0
- package/README-DOCKER.md +4 -2
- package/README.md +4 -1
- package/bin/mb_coap.js +1 -1
- package/bin/mb_mdns.js +1 -1
- package/dist/broadcastServer.d.ts +0 -115
- package/dist/broadcastServer.js +1 -119
- package/dist/broadcastServerTypes.d.ts +0 -43
- package/dist/broadcastServerTypes.js +0 -24
- package/dist/cli.d.ts +1 -26
- package/dist/cli.js +2 -102
- package/dist/cliEmitter.d.ts +0 -36
- package/dist/cliEmitter.js +0 -37
- package/dist/cliHistory.d.ts +0 -42
- package/dist/cliHistory.js +1 -39
- package/dist/clusters/export.d.ts +0 -1
- package/dist/clusters/export.js +0 -2
- package/dist/deviceManager.d.ts +0 -108
- package/dist/deviceManager.js +2 -114
- package/dist/devices/airConditioner.d.ts +0 -75
- package/dist/devices/airConditioner.js +0 -57
- package/dist/devices/batteryStorage.d.ts +0 -43
- package/dist/devices/batteryStorage.js +1 -48
- package/dist/devices/cooktop.d.ts +0 -55
- package/dist/devices/cooktop.js +0 -56
- package/dist/devices/dishwasher.d.ts +0 -55
- package/dist/devices/dishwasher.js +0 -57
- package/dist/devices/evse.d.ts +0 -57
- package/dist/devices/evse.js +10 -74
- package/dist/devices/export.d.ts +0 -1
- package/dist/devices/export.js +0 -5
- package/dist/devices/extractorHood.d.ts +0 -41
- package/dist/devices/extractorHood.js +0 -43
- package/dist/devices/heatPump.d.ts +0 -43
- package/dist/devices/heatPump.js +2 -50
- package/dist/devices/laundryDryer.d.ts +0 -58
- package/dist/devices/laundryDryer.js +3 -62
- package/dist/devices/laundryWasher.d.ts +0 -64
- package/dist/devices/laundryWasher.js +4 -70
- package/dist/devices/microwaveOven.d.ts +1 -77
- package/dist/devices/microwaveOven.js +5 -88
- package/dist/devices/oven.d.ts +0 -82
- package/dist/devices/oven.js +0 -85
- package/dist/devices/refrigerator.d.ts +0 -100
- package/dist/devices/refrigerator.js +0 -102
- package/dist/devices/roboticVacuumCleaner.d.ts +0 -83
- package/dist/devices/roboticVacuumCleaner.js +9 -100
- package/dist/devices/solarPower.d.ts +0 -36
- package/dist/devices/solarPower.js +0 -38
- package/dist/devices/speaker.d.ts +0 -79
- package/dist/devices/speaker.js +0 -84
- package/dist/devices/temperatureControl.d.ts +0 -21
- package/dist/devices/temperatureControl.js +3 -24
- package/dist/devices/waterHeater.d.ts +0 -74
- package/dist/devices/waterHeater.js +2 -82
- package/dist/frontend.d.ts +4 -187
- package/dist/frontend.js +89 -505
- package/dist/frontendTypes.d.ts +0 -57
- package/dist/frontendTypes.js +0 -45
- package/dist/helpers.d.ts +0 -43
- package/dist/helpers.js +1 -54
- package/dist/index.d.ts +0 -23
- package/dist/index.js +0 -25
- package/dist/jestutils/export.d.ts +0 -1
- package/dist/jestutils/export.js +0 -1
- package/dist/jestutils/jestHelpers.d.ts +0 -255
- package/dist/jestutils/jestHelpers.js +16 -379
- package/dist/logger/export.d.ts +0 -1
- package/dist/logger/export.js +0 -1
- package/dist/matter/behaviors.d.ts +0 -1
- package/dist/matter/behaviors.js +0 -2
- package/dist/matter/clusters.d.ts +0 -1
- package/dist/matter/clusters.js +0 -2
- package/dist/matter/devices.d.ts +0 -1
- package/dist/matter/devices.js +0 -2
- package/dist/matter/endpoints.d.ts +0 -1
- package/dist/matter/endpoints.js +0 -2
- package/dist/matter/export.d.ts +0 -1
- package/dist/matter/export.js +0 -2
- package/dist/matter/types.d.ts +0 -1
- package/dist/matter/types.js +0 -2
- package/dist/matterNode.d.ts +0 -258
- package/dist/matterNode.js +9 -364
- package/dist/matterbridge.d.ts +0 -362
- package/dist/matterbridge.js +75 -864
- package/dist/matterbridgeAccessoryPlatform.d.ts +0 -36
- package/dist/matterbridgeAccessoryPlatform.js +0 -38
- package/dist/matterbridgeBehaviors.d.ts +0 -24
- package/dist/matterbridgeBehaviors.js +5 -68
- package/dist/matterbridgeDeviceTypes.d.ts +0 -649
- package/dist/matterbridgeDeviceTypes.js +6 -673
- package/dist/matterbridgeDynamicPlatform.d.ts +0 -36
- package/dist/matterbridgeDynamicPlatform.js +0 -38
- package/dist/matterbridgeEndpoint.d.ts +2 -1332
- package/dist/matterbridgeEndpoint.js +94 -1459
- package/dist/matterbridgeEndpointHelpers.d.ts +0 -425
- package/dist/matterbridgeEndpointHelpers.js +22 -487
- package/dist/matterbridgeEndpointTypes.d.ts +0 -70
- package/dist/matterbridgeEndpointTypes.js +0 -25
- package/dist/matterbridgePlatform.d.ts +0 -425
- package/dist/matterbridgePlatform.js +2 -453
- package/dist/matterbridgeTypes.d.ts +0 -46
- package/dist/matterbridgeTypes.js +0 -26
- package/dist/mb_coap.d.ts +1 -0
- package/dist/{dgram/mb_coap.js → mb_coap.js} +3 -41
- package/dist/mb_mdns.d.ts +1 -0
- package/dist/{dgram/mb_mdns.js → mb_mdns.js} +47 -81
- package/dist/pluginManager.d.ts +0 -305
- package/dist/pluginManager.js +8 -345
- package/dist/shelly.d.ts +0 -157
- package/dist/shelly.js +7 -178
- package/dist/spawn.d.ts +1 -0
- package/dist/{utils/spawn.js → spawn.js} +3 -73
- package/dist/storage/export.d.ts +0 -1
- package/dist/storage/export.js +0 -1
- package/dist/update.d.ts +0 -75
- package/dist/update.js +7 -100
- package/dist/utils/export.d.ts +1 -13
- package/dist/utils/export.js +1 -13
- package/dist/workerGlobalPrefix.d.ts +0 -24
- package/dist/workerGlobalPrefix.js +6 -40
- package/dist/workerTypes.d.ts +0 -25
- package/dist/workerTypes.js +0 -24
- package/dist/workers.d.ts +0 -61
- package/dist/workers.js +4 -68
- package/npm-shrinkwrap.json +80 -50
- package/package.json +8 -8
- package/dist/broadcastServer.d.ts.map +0 -1
- package/dist/broadcastServer.js.map +0 -1
- package/dist/broadcastServerTypes.d.ts.map +0 -1
- package/dist/broadcastServerTypes.js.map +0 -1
- package/dist/cli.d.ts.map +0 -1
- package/dist/cli.js.map +0 -1
- package/dist/cliEmitter.d.ts.map +0 -1
- package/dist/cliEmitter.js.map +0 -1
- package/dist/cliHistory.d.ts.map +0 -1
- package/dist/cliHistory.js.map +0 -1
- package/dist/clusters/export.d.ts.map +0 -1
- package/dist/clusters/export.js.map +0 -1
- package/dist/deviceManager.d.ts.map +0 -1
- package/dist/deviceManager.js.map +0 -1
- package/dist/devices/airConditioner.d.ts.map +0 -1
- package/dist/devices/airConditioner.js.map +0 -1
- package/dist/devices/batteryStorage.d.ts.map +0 -1
- package/dist/devices/batteryStorage.js.map +0 -1
- package/dist/devices/cooktop.d.ts.map +0 -1
- package/dist/devices/cooktop.js.map +0 -1
- package/dist/devices/dishwasher.d.ts.map +0 -1
- package/dist/devices/dishwasher.js.map +0 -1
- package/dist/devices/evse.d.ts.map +0 -1
- package/dist/devices/evse.js.map +0 -1
- package/dist/devices/export.d.ts.map +0 -1
- package/dist/devices/export.js.map +0 -1
- package/dist/devices/extractorHood.d.ts.map +0 -1
- package/dist/devices/extractorHood.js.map +0 -1
- package/dist/devices/heatPump.d.ts.map +0 -1
- package/dist/devices/heatPump.js.map +0 -1
- package/dist/devices/laundryDryer.d.ts.map +0 -1
- package/dist/devices/laundryDryer.js.map +0 -1
- package/dist/devices/laundryWasher.d.ts.map +0 -1
- package/dist/devices/laundryWasher.js.map +0 -1
- package/dist/devices/microwaveOven.d.ts.map +0 -1
- package/dist/devices/microwaveOven.js.map +0 -1
- package/dist/devices/oven.d.ts.map +0 -1
- package/dist/devices/oven.js.map +0 -1
- package/dist/devices/refrigerator.d.ts.map +0 -1
- package/dist/devices/refrigerator.js.map +0 -1
- package/dist/devices/roboticVacuumCleaner.d.ts.map +0 -1
- package/dist/devices/roboticVacuumCleaner.js.map +0 -1
- package/dist/devices/solarPower.d.ts.map +0 -1
- package/dist/devices/solarPower.js.map +0 -1
- package/dist/devices/speaker.d.ts.map +0 -1
- package/dist/devices/speaker.js.map +0 -1
- package/dist/devices/temperatureControl.d.ts.map +0 -1
- package/dist/devices/temperatureControl.js.map +0 -1
- 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 +0 -365
- package/dist/dgram/coap.js.map +0 -1
- package/dist/dgram/dgram.d.ts +0 -144
- package/dist/dgram/dgram.d.ts.map +0 -1
- package/dist/dgram/dgram.js +0 -363
- 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 -371
- package/dist/dgram/mdns.d.ts.map +0 -1
- package/dist/dgram/mdns.js +0 -934
- 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 +0 -179
- package/dist/dgram/multicast.js.map +0 -1
- package/dist/dgram/unicast.d.ts +0 -64
- package/dist/dgram/unicast.d.ts.map +0 -1
- package/dist/dgram/unicast.js +0 -100
- package/dist/dgram/unicast.js.map +0 -1
- package/dist/frontend.d.ts.map +0 -1
- package/dist/frontend.js.map +0 -1
- package/dist/frontendTypes.d.ts.map +0 -1
- package/dist/frontendTypes.js.map +0 -1
- package/dist/helpers.d.ts.map +0 -1
- package/dist/helpers.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/jestutils/export.d.ts.map +0 -1
- package/dist/jestutils/export.js.map +0 -1
- package/dist/jestutils/jestHelpers.d.ts.map +0 -1
- package/dist/jestutils/jestHelpers.js.map +0 -1
- package/dist/logger/export.d.ts.map +0 -1
- package/dist/logger/export.js.map +0 -1
- package/dist/matter/behaviors.d.ts.map +0 -1
- package/dist/matter/behaviors.js.map +0 -1
- package/dist/matter/clusters.d.ts.map +0 -1
- package/dist/matter/clusters.js.map +0 -1
- package/dist/matter/devices.d.ts.map +0 -1
- package/dist/matter/devices.js.map +0 -1
- package/dist/matter/endpoints.d.ts.map +0 -1
- package/dist/matter/endpoints.js.map +0 -1
- package/dist/matter/export.d.ts.map +0 -1
- package/dist/matter/export.js.map +0 -1
- package/dist/matter/types.d.ts.map +0 -1
- package/dist/matter/types.js.map +0 -1
- package/dist/matterNode.d.ts.map +0 -1
- package/dist/matterNode.js.map +0 -1
- package/dist/matterbridge.d.ts.map +0 -1
- package/dist/matterbridge.js.map +0 -1
- package/dist/matterbridgeAccessoryPlatform.d.ts.map +0 -1
- package/dist/matterbridgeAccessoryPlatform.js.map +0 -1
- package/dist/matterbridgeBehaviors.d.ts.map +0 -1
- package/dist/matterbridgeBehaviors.js.map +0 -1
- package/dist/matterbridgeDeviceTypes.d.ts.map +0 -1
- package/dist/matterbridgeDeviceTypes.js.map +0 -1
- package/dist/matterbridgeDynamicPlatform.d.ts.map +0 -1
- package/dist/matterbridgeDynamicPlatform.js.map +0 -1
- package/dist/matterbridgeEndpoint.d.ts.map +0 -1
- package/dist/matterbridgeEndpoint.js.map +0 -1
- package/dist/matterbridgeEndpointHelpers.d.ts.map +0 -1
- package/dist/matterbridgeEndpointHelpers.js.map +0 -1
- package/dist/matterbridgeEndpointTypes.d.ts.map +0 -1
- package/dist/matterbridgeEndpointTypes.js.map +0 -1
- package/dist/matterbridgePlatform.d.ts.map +0 -1
- package/dist/matterbridgePlatform.js.map +0 -1
- package/dist/matterbridgeTypes.d.ts.map +0 -1
- package/dist/matterbridgeTypes.js.map +0 -1
- package/dist/pluginManager.d.ts.map +0 -1
- package/dist/pluginManager.js.map +0 -1
- package/dist/shelly.d.ts.map +0 -1
- package/dist/shelly.js.map +0 -1
- package/dist/storage/export.d.ts.map +0 -1
- package/dist/storage/export.js.map +0 -1
- 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 +0 -282
- 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 +0 -123
- 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 +0 -76
- 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 +0 -54
- 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 +0 -114
- 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 +0 -79
- 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 +0 -129
- package/dist/utils/deepEqual.js.map +0 -1
- package/dist/utils/error.d.ts +0 -45
- package/dist/utils/error.d.ts.map +0 -1
- package/dist/utils/error.js +0 -54
- package/dist/utils/error.js.map +0 -1
- 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 +0 -78
- 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 +0 -242
- 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 +0 -268
- 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 +0 -162
- package/dist/utils/isValid.js.map +0 -1
- package/dist/utils/network.d.ts +0 -141
- package/dist/utils/network.d.ts.map +0 -1
- package/dist/utils/network.js +0 -314
- 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 +0 -264
- 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 +0 -125
- package/dist/utils/wait.js.map +0 -1
- package/dist/workerGlobalPrefix.d.ts.map +0 -1
- package/dist/workerGlobalPrefix.js.map +0 -1
- package/dist/workerTypes.d.ts.map +0 -1
- package/dist/workerTypes.js.map +0 -1
- package/dist/workers.d.ts.map +0 -1
- package/dist/workers.js.map +0 -1
package/dist/matterbridge.js
CHANGED
|
@@ -1,53 +1,20 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* This file contains the class Matterbridge.
|
|
3
|
-
*
|
|
4
|
-
* @file matterbridge.ts
|
|
5
|
-
* @author Luca Liguori
|
|
6
|
-
* @created 2023-12-29
|
|
7
|
-
* @version 1.6.2
|
|
8
|
-
* @license Apache-2.0
|
|
9
|
-
*
|
|
10
|
-
* Copyright 2023, 2024, 2025 Luca Liguori.
|
|
11
|
-
*
|
|
12
|
-
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
13
|
-
* you may not use this file except in compliance with the License.
|
|
14
|
-
* You may obtain a copy of the License at
|
|
15
|
-
*
|
|
16
|
-
* http://www.apache.org/licenses/LICENSE-2.0
|
|
17
|
-
*
|
|
18
|
-
* Unless required by applicable law or agreed to in writing, software
|
|
19
|
-
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
20
|
-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
21
|
-
* See the License for the specific language governing permissions and
|
|
22
|
-
* limitations under the License.
|
|
23
|
-
*/
|
|
24
|
-
// eslint-disable-next-line no-console
|
|
25
1
|
if (process.argv.includes('--loader') || process.argv.includes('-loader'))
|
|
26
2
|
console.log('\u001B[32mMatterbridge loaded.\u001B[40;0m');
|
|
27
|
-
// Node.js modules
|
|
28
3
|
import os from 'node:os';
|
|
29
4
|
import path from 'node:path';
|
|
30
5
|
import fs, { unlinkSync } 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 { 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
|
-
|
|
46
|
-
import { getParameter, getIntParameter, hasParameter } from './utils/commandLine.js';
|
|
47
|
-
import { copyDirectory } from './utils/copyDirectory.js';
|
|
48
|
-
import { createDirectory } from './utils/createDirectory.js';
|
|
49
|
-
import { isValidString, parseVersionString, isValidNumber, isValidObject } from './utils/isValid.js';
|
|
50
|
-
import { formatBytes, formatPercent, formatUptime } from './utils/format.js';
|
|
17
|
+
import { copyDirectory, createDirectory, formatBytes, formatPercent, formatUptime, getIntParameter, getParameter, hasParameter, isValidNumber, isValidObject, isValidString, parseVersionString } from '@matterbridge/utils';
|
|
51
18
|
import { dev, MATTER_LOGGER_FILE, MATTER_STORAGE_NAME, MATTERBRIDGE_LOGGER_FILE, NODE_STORAGE_DIR, plg, typ } from './matterbridgeTypes.js';
|
|
52
19
|
import { PluginManager } from './pluginManager.js';
|
|
53
20
|
import { DeviceManager } from './deviceManager.js';
|
|
@@ -56,27 +23,19 @@ import { bridge } from './matterbridgeDeviceTypes.js';
|
|
|
56
23
|
import { Frontend } from './frontend.js';
|
|
57
24
|
import { addVirtualDevice, addVirtualDevices } from './helpers.js';
|
|
58
25
|
import { BroadcastServer } from './broadcastServer.js';
|
|
59
|
-
/**
|
|
60
|
-
* Represents the Matterbridge application.
|
|
61
|
-
*/
|
|
62
26
|
export class Matterbridge extends EventEmitter {
|
|
63
|
-
/** Matterbridge system information */
|
|
64
27
|
systemInformation = {
|
|
65
|
-
// Network properties
|
|
66
28
|
interfaceName: '',
|
|
67
29
|
macAddress: '',
|
|
68
30
|
ipv4Address: '',
|
|
69
31
|
ipv6Address: '',
|
|
70
|
-
// Node.js properties
|
|
71
32
|
nodeVersion: '',
|
|
72
|
-
// Fixed system properties
|
|
73
33
|
hostname: '',
|
|
74
34
|
user: '',
|
|
75
35
|
osType: '',
|
|
76
36
|
osRelease: '',
|
|
77
37
|
osPlatform: '',
|
|
78
38
|
osArch: '',
|
|
79
|
-
// Cpu and memory properties
|
|
80
39
|
totalMemory: '',
|
|
81
40
|
freeMemory: '',
|
|
82
41
|
systemUptime: '',
|
|
@@ -87,66 +46,39 @@ export class Matterbridge extends EventEmitter {
|
|
|
87
46
|
heapTotal: '',
|
|
88
47
|
heapUsed: '',
|
|
89
48
|
};
|
|
90
|
-
// Matterbridge settings
|
|
91
|
-
/** It indicates the home directory of the Matterbridge application. The home directory is the base directory where Matterbridge creates the matterbridge directories (os.homedir() if not overridden). */
|
|
92
49
|
homeDirectory = '';
|
|
93
|
-
/** It indicates the root directory of the Matterbridge application. The root directory is the directory where Matterbridge is executed. */
|
|
94
50
|
rootDirectory = '';
|
|
95
|
-
/** It indicates where the directory .matterbridge is located. */
|
|
96
51
|
matterbridgeDirectory = '';
|
|
97
|
-
/** It indicates where the directory Matterbridge is located. */
|
|
98
52
|
matterbridgePluginDirectory = '';
|
|
99
|
-
/** It indicates where the directory .mattercert is located. */
|
|
100
53
|
matterbridgeCertDirectory = '';
|
|
101
|
-
/** It indicates the global modules directory for npm. */
|
|
102
54
|
globalModulesDirectory = '';
|
|
103
55
|
matterbridgeVersion = '';
|
|
104
56
|
matterbridgeLatestVersion = '';
|
|
105
57
|
matterbridgeDevVersion = '';
|
|
106
58
|
frontendVersion = '';
|
|
107
|
-
/** It indicates the mode of the Matterbridge instance. It can be 'bridge', 'childbridge', 'controller' or ''. */
|
|
108
59
|
bridgeMode = '';
|
|
109
|
-
/** It indicates the restart mode of the Matterbridge instance. It can be 'service', 'docker' or ''. */
|
|
110
60
|
restartMode = '';
|
|
111
|
-
/** It indicates whether virtual mode is enabled and its type. The virtual mode control the creation of "Update matterbridge" and "Restart matterbridge" endpoints. */
|
|
112
61
|
virtualMode = 'outlet';
|
|
113
|
-
/** It indicates the Matterbridge profile in use. */
|
|
114
62
|
profile = getParameter('profile');
|
|
115
|
-
|
|
116
|
-
log = new AnsiLogger({ logName: 'Matterbridge', logTimestampFormat: 4 /* TimestampFormat.TIME_MILLIS */, logLevel: hasParameter('debug') ? "debug" /* LogLevel.DEBUG */ : "info" /* LogLevel.INFO */ });
|
|
117
|
-
/** Matterbridge logger level */
|
|
63
|
+
log = new AnsiLogger({ logName: 'Matterbridge', logTimestampFormat: 4, logLevel: hasParameter('debug') ? "debug" : "info" });
|
|
118
64
|
logLevel = this.log.logLevel;
|
|
119
|
-
/** Whether to log to a file */
|
|
120
65
|
fileLogger = false;
|
|
121
|
-
|
|
122
|
-
matterLog = new AnsiLogger({ logName: 'Matter', logTimestampFormat: 4 /* TimestampFormat.TIME_MILLIS */, logLevel: "debug" /* LogLevel.DEBUG */ });
|
|
123
|
-
/** Matter logger level */
|
|
66
|
+
matterLog = new AnsiLogger({ logName: 'Matter', logTimestampFormat: 4, logLevel: "debug" });
|
|
124
67
|
matterLogLevel = this.matterLog.logLevel;
|
|
125
|
-
/** Whether to log Matter to a file */
|
|
126
68
|
matterFileLogger = false;
|
|
127
|
-
// Frontend settings
|
|
128
69
|
readOnly = hasParameter('readonly') || hasParameter('shelly');
|
|
129
70
|
shellyBoard = hasParameter('shelly');
|
|
130
71
|
shellySysUpdate = false;
|
|
131
72
|
shellyMainUpdate = false;
|
|
132
|
-
/** It indicates whether a restart is required. It can be unset in childbridge mode by restarting the plugin that triggered the restart. */
|
|
133
73
|
restartRequired = false;
|
|
134
|
-
/** It indicates whether a fixed restart is required. It cannot be unset once set. */
|
|
135
74
|
fixedRestartRequired = false;
|
|
136
|
-
/** It indicates whether an update is available. */
|
|
137
75
|
updateRequired = false;
|
|
138
|
-
// Managers
|
|
139
76
|
plugins = new PluginManager(this);
|
|
140
77
|
devices = new DeviceManager();
|
|
141
|
-
// Frontend
|
|
142
78
|
frontend = new Frontend(this);
|
|
143
|
-
/** Matterbridge node storage manager created in the directory 'storage' in matterbridgeDirectory */
|
|
144
79
|
nodeStorage;
|
|
145
|
-
/** Matterbridge node context created with name 'matterbridge' */
|
|
146
80
|
nodeContext;
|
|
147
|
-
/** The main instance of the Matterbridge class (singleton) */
|
|
148
81
|
static instance;
|
|
149
|
-
// Instance properties
|
|
150
82
|
shutdown = false;
|
|
151
83
|
failCountLimit = hasParameter('shelly') ? 600 : 120;
|
|
152
84
|
hasCleanupStarted = false;
|
|
@@ -161,32 +93,19 @@ export class Matterbridge extends EventEmitter {
|
|
|
161
93
|
sigtermHandler;
|
|
162
94
|
exceptionHandler;
|
|
163
95
|
rejectionHandler;
|
|
164
|
-
/** Matter environment default */
|
|
165
96
|
environment = Environment.default;
|
|
166
|
-
/** Matter storage service from environment default */
|
|
167
97
|
matterStorageService;
|
|
168
|
-
/** Matter storage manager created with name 'Matterbridge' */
|
|
169
98
|
matterStorageManager;
|
|
170
|
-
/** Matter matterbridge storage context created in the storage manager with name 'persist' */
|
|
171
99
|
matterbridgeContext;
|
|
172
100
|
controllerContext;
|
|
173
|
-
/** Matter mdns interface e.g. 'eth0' or 'wlan0' or 'Wi-Fi' */
|
|
174
101
|
mdnsInterface;
|
|
175
|
-
/** Matter listeningAddressIpv4 address */
|
|
176
102
|
ipv4Address;
|
|
177
|
-
/** Matter listeningAddressIpv6 address */
|
|
178
103
|
ipv6Address;
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
/** Matter commissioning discriminator */
|
|
184
|
-
discriminator; // first server node discriminator
|
|
185
|
-
/** Matter device certification */
|
|
186
|
-
certification; // device certification
|
|
187
|
-
/** Matter server node in bridge mode */
|
|
104
|
+
port;
|
|
105
|
+
passcode;
|
|
106
|
+
discriminator;
|
|
107
|
+
certification;
|
|
188
108
|
serverNode;
|
|
189
|
-
/** Matter aggregator node in bridge mode */
|
|
190
109
|
aggregatorNode;
|
|
191
110
|
aggregatorVendorId = VendorId(getIntParameter('vendorId') ?? 0xfff1);
|
|
192
111
|
aggregatorVendorName = getParameter('vendorName') ?? 'Matterbridge';
|
|
@@ -195,12 +114,9 @@ export class Matterbridge extends EventEmitter {
|
|
|
195
114
|
aggregatorDeviceType = DeviceTypeId(getIntParameter('deviceType') ?? bridge.code);
|
|
196
115
|
aggregatorSerialNumber = getParameter('serialNumber');
|
|
197
116
|
aggregatorUniqueId = getParameter('uniqueId');
|
|
198
|
-
/** Advertising nodes map: time advertising started keyed by storeId */
|
|
199
117
|
advertisingNodes = new Map();
|
|
200
|
-
/** Broadcast server */
|
|
201
118
|
server;
|
|
202
119
|
verbose = hasParameter('verbose');
|
|
203
|
-
/** We load asyncronously so is private */
|
|
204
120
|
constructor() {
|
|
205
121
|
super();
|
|
206
122
|
this.log.logNameColor = '\x1b[38;5;115m';
|
|
@@ -251,19 +167,8 @@ export class Matterbridge extends EventEmitter {
|
|
|
251
167
|
}
|
|
252
168
|
}
|
|
253
169
|
}
|
|
254
|
-
//* ************************************************************************************************************************************ */
|
|
255
|
-
// loadInstance() and cleanup() methods */
|
|
256
|
-
//* ************************************************************************************************************************************ */
|
|
257
|
-
/**
|
|
258
|
-
* Loads an instance of the Matterbridge class.
|
|
259
|
-
* If an instance already exists, return that instance.
|
|
260
|
-
*
|
|
261
|
-
* @param {boolean} initialize - Whether to initialize the Matterbridge instance after loading. Defaults to false.
|
|
262
|
-
* @returns {Matterbridge} A promise that resolves to the Matterbridge instance.
|
|
263
|
-
*/
|
|
264
170
|
static async loadInstance(initialize = false) {
|
|
265
171
|
if (!Matterbridge.instance) {
|
|
266
|
-
// eslint-disable-next-line no-console
|
|
267
172
|
if (hasParameter('debug'))
|
|
268
173
|
console.log(GREEN + 'Creating a new instance of Matterbridge.', initialize ? 'Initializing...' : 'Not initializing...', rs);
|
|
269
174
|
Matterbridge.instance = new Matterbridge();
|
|
@@ -272,84 +177,56 @@ export class Matterbridge extends EventEmitter {
|
|
|
272
177
|
}
|
|
273
178
|
return Matterbridge.instance;
|
|
274
179
|
}
|
|
275
|
-
/**
|
|
276
|
-
* Initializes the Matterbridge application.
|
|
277
|
-
*
|
|
278
|
-
* @remarks
|
|
279
|
-
* This method performs the necessary setup and initialization steps for the Matterbridge application.
|
|
280
|
-
* It displays the help information if the 'help' parameter is provided, sets up the logger, checks the
|
|
281
|
-
* node version, registers signal handlers, initializes storage, and parses the command line.
|
|
282
|
-
*
|
|
283
|
-
* @returns {Promise<void>} A Promise that resolves when the initialization is complete.
|
|
284
|
-
*/
|
|
285
180
|
async initialize() {
|
|
286
|
-
// for (let i = 1; i <= 255; i++) console.log(`\x1b[38;5;${i}mColor: ${i}`);
|
|
287
|
-
// Emit the initialize_started event
|
|
288
181
|
this.emit('initialize_started');
|
|
289
|
-
// Set the restart mode
|
|
290
182
|
if (hasParameter('service'))
|
|
291
183
|
this.restartMode = 'service';
|
|
292
184
|
if (hasParameter('docker'))
|
|
293
185
|
this.restartMode = 'docker';
|
|
294
|
-
// Set the matterbridge home directory
|
|
295
186
|
this.homeDirectory = getParameter('homedir') ?? os.homedir();
|
|
296
187
|
await createDirectory(this.homeDirectory, 'Matterbridge Home Directory', this.log);
|
|
297
|
-
// Set the matterbridge directory
|
|
298
188
|
this.matterbridgeDirectory = this.profile ? path.join(this.homeDirectory, '.matterbridge', 'profiles', this.profile) : path.join(this.homeDirectory, '.matterbridge');
|
|
299
189
|
await createDirectory(this.matterbridgeDirectory, 'Matterbridge Directory', this.log);
|
|
300
190
|
await createDirectory(path.join(this.matterbridgeDirectory, 'certs'), 'Matterbridge Frontend Certificate Directory', this.log);
|
|
301
191
|
await createDirectory(path.join(this.matterbridgeDirectory, 'uploads'), 'Matterbridge Frontend Uploads Directory', this.log);
|
|
302
|
-
// Set the matterbridge plugin directory
|
|
303
192
|
this.matterbridgePluginDirectory = this.profile ? path.join(this.homeDirectory, 'Matterbridge', 'profiles', this.profile) : path.join(this.homeDirectory, 'Matterbridge');
|
|
304
193
|
await createDirectory(this.matterbridgePluginDirectory, 'Matterbridge Plugin Directory', this.log);
|
|
305
|
-
// Set the matterbridge cert directory
|
|
306
194
|
this.matterbridgeCertDirectory = this.profile ? path.join(this.homeDirectory, '.mattercert', 'profiles', this.profile) : path.join(this.homeDirectory, '.mattercert');
|
|
307
195
|
await createDirectory(this.matterbridgeCertDirectory, 'Matterbridge Matter Certificate Directory', this.log);
|
|
308
|
-
// Set the matterbridge root directory
|
|
309
196
|
const { fileURLToPath } = await import('node:url');
|
|
310
197
|
const currentFileDirectory = path.dirname(fileURLToPath(import.meta.url));
|
|
311
|
-
this.rootDirectory = path.resolve(currentFileDirectory, '../');
|
|
312
|
-
// Setup the matter environment with default values
|
|
198
|
+
this.rootDirectory = path.resolve(currentFileDirectory, '../');
|
|
313
199
|
this.environment.vars.set('log.level', MatterLogLevel.INFO);
|
|
314
200
|
this.environment.vars.set('log.format', MatterLogFormat.ANSI);
|
|
315
201
|
this.environment.vars.set('path.root', path.join(this.matterbridgeDirectory, MATTER_STORAGE_NAME));
|
|
316
202
|
this.environment.vars.set('runtime.signals', false);
|
|
317
203
|
this.environment.vars.set('runtime.exitcode', false);
|
|
318
|
-
// Register process handlers
|
|
319
204
|
this.registerProcessHandlers();
|
|
320
|
-
// Initialize nodeStorage and nodeContext
|
|
321
205
|
try {
|
|
322
206
|
this.log.debug(`Creating node storage manager: ${CYAN}${NODE_STORAGE_DIR}${db}`);
|
|
323
207
|
this.nodeStorage = new NodeStorageManager({ dir: path.join(this.matterbridgeDirectory, NODE_STORAGE_DIR), writeQueue: false, expiredInterval: undefined, logging: false });
|
|
324
208
|
this.log.debug('Creating node storage context for matterbridge');
|
|
325
209
|
this.nodeContext = await this.nodeStorage.createStorage('matterbridge');
|
|
326
|
-
// TODO: Remove this code when node-persist-manager is updated
|
|
327
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
328
210
|
const keys = (await this.nodeStorage?.storage.keys());
|
|
329
211
|
for (const key of keys) {
|
|
330
212
|
this.log.debug(`Checking node storage manager key: ${CYAN}${key}${db}`);
|
|
331
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
332
213
|
await this.nodeStorage?.storage.get(key);
|
|
333
214
|
}
|
|
334
215
|
const storages = await this.nodeStorage.getStorageNames();
|
|
335
216
|
for (const storage of storages) {
|
|
336
217
|
this.log.debug(`Checking storage: ${CYAN}${storage}${db}`);
|
|
337
218
|
const nodeContext = await this.nodeStorage?.createStorage(storage);
|
|
338
|
-
// TODO: Remove this code when node-persist-manager is updated
|
|
339
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
340
219
|
const keys = (await nodeContext?.storage.keys());
|
|
341
220
|
keys.forEach(async (key) => {
|
|
342
221
|
this.log.debug(`Checking key: ${CYAN}${storage}:${key}${db}`);
|
|
343
222
|
await nodeContext?.get(key);
|
|
344
223
|
});
|
|
345
224
|
}
|
|
346
|
-
// Creating a backup of the node storage since it is not corrupted
|
|
347
225
|
this.log.debug('Creating node storage backup...');
|
|
348
226
|
await copyDirectory(path.join(this.matterbridgeDirectory, NODE_STORAGE_DIR), path.join(this.matterbridgeDirectory, NODE_STORAGE_DIR + '.backup'));
|
|
349
227
|
this.log.debug('Created node storage backup');
|
|
350
228
|
}
|
|
351
229
|
catch (error) {
|
|
352
|
-
// Restoring the backup of the node storage since it is corrupted
|
|
353
230
|
this.log.error(`Error creating node storage manager and context: ${error instanceof Error ? error.message : error}`);
|
|
354
231
|
if (hasParameter('norestore')) {
|
|
355
232
|
this.log.fatal(`The matterbridge storage is corrupted. Found -norestore parameter: exiting...`);
|
|
@@ -363,19 +240,14 @@ export class Matterbridge extends EventEmitter {
|
|
|
363
240
|
if (!this.nodeStorage || !this.nodeContext) {
|
|
364
241
|
throw new Error('Fatal error creating node storage manager and context for matterbridge');
|
|
365
242
|
}
|
|
366
|
-
// Set the first port to use for the commissioning server (will be incremented in childbridge mode)
|
|
367
243
|
this.port = getIntParameter('port') ?? (await this.nodeContext.get('matterport', 5540)) ?? 5540;
|
|
368
|
-
// Set the first passcode to use for the commissioning server (will be incremented in childbridge mode)
|
|
369
244
|
this.passcode = getIntParameter('passcode') ?? (await this.nodeContext.get('matterpasscode')) ?? PaseClient.generateRandomPasscode(this.environment.get(Crypto));
|
|
370
|
-
// Set the first discriminator to use for the commissioning server (will be incremented in childbridge mode)
|
|
371
245
|
this.discriminator = getIntParameter('discriminator') ?? (await this.nodeContext.get('matterdiscriminator')) ?? PaseClient.generateRandomDiscriminator(this.environment.get(Crypto));
|
|
372
|
-
// Certificate management
|
|
373
246
|
const pairingFilePath = path.join(this.matterbridgeCertDirectory, 'pairing.json');
|
|
374
247
|
try {
|
|
375
248
|
await fs.promises.access(pairingFilePath, fs.constants.R_OK);
|
|
376
249
|
const pairingFileContent = await fs.promises.readFile(pairingFilePath, 'utf8');
|
|
377
250
|
const pairingFileJson = JSON.parse(pairingFileContent);
|
|
378
|
-
// Set the vendorId, vendorName, productId, productName, deviceType, serialNumber, uniqueId if they are present in the pairing file
|
|
379
251
|
if (isValidNumber(pairingFileJson.vendorId)) {
|
|
380
252
|
this.aggregatorVendorId = VendorId(pairingFileJson.vendorId);
|
|
381
253
|
this.log.info(`Pairing file ${CYAN}${pairingFilePath}${nf} found. Using vendorId ${CYAN}${this.aggregatorVendorId}${nf} from pairing file.`);
|
|
@@ -404,15 +276,13 @@ export class Matterbridge extends EventEmitter {
|
|
|
404
276
|
this.aggregatorUniqueId = pairingFileJson.uniqueId;
|
|
405
277
|
this.log.info(`Pairing file ${CYAN}${pairingFilePath}${nf} found. Using uniqueId ${CYAN}${this.aggregatorUniqueId}${nf} from pairing file.`);
|
|
406
278
|
}
|
|
407
|
-
// Override the passcode and discriminator if they are present in the pairing file
|
|
408
279
|
if (isValidNumber(pairingFileJson.passcode) && isValidNumber(pairingFileJson.discriminator)) {
|
|
409
280
|
this.passcode = pairingFileJson.passcode;
|
|
410
281
|
this.discriminator = pairingFileJson.discriminator;
|
|
411
282
|
this.log.info(`Pairing file ${CYAN}${pairingFilePath}${nf} found. Using passcode ${CYAN}${this.passcode}${nf} and discriminator ${CYAN}${this.discriminator}${nf} from pairing file.`);
|
|
412
283
|
}
|
|
413
|
-
// Set the certification for matter.js if it is present in the pairing file
|
|
414
284
|
if (pairingFileJson.privateKey && pairingFileJson.certificate && pairingFileJson.intermediateCertificate && pairingFileJson.declaration) {
|
|
415
|
-
const { hexToBuffer } = await import('
|
|
285
|
+
const { hexToBuffer } = await import('@matterbridge/utils');
|
|
416
286
|
this.certification = {
|
|
417
287
|
privateKey: hexToBuffer(pairingFileJson.privateKey),
|
|
418
288
|
certificate: hexToBuffer(pairingFileJson.certificate),
|
|
@@ -425,44 +295,41 @@ export class Matterbridge extends EventEmitter {
|
|
|
425
295
|
catch (error) {
|
|
426
296
|
this.log.debug(`Pairing file ${CYAN}${pairingFilePath}${db} not found: ${error instanceof Error ? error.message : error}`);
|
|
427
297
|
}
|
|
428
|
-
// Store the passcode, discriminator and port in the node context
|
|
429
298
|
await this.nodeContext.set('matterport', this.port);
|
|
430
299
|
await this.nodeContext.set('matterpasscode', this.passcode);
|
|
431
300
|
await this.nodeContext.set('matterdiscriminator', this.discriminator);
|
|
432
301
|
this.log.debug(`Initializing server node for Matterbridge on port ${this.port} with passcode ${this.passcode} and discriminator ${this.discriminator}`);
|
|
433
|
-
// Set matterbridge logger level (context: matterbridgeLogLevel)
|
|
434
302
|
if (hasParameter('logger')) {
|
|
435
303
|
const level = getParameter('logger');
|
|
436
304
|
if (level === 'debug') {
|
|
437
|
-
this.log.logLevel = "debug"
|
|
305
|
+
this.log.logLevel = "debug";
|
|
438
306
|
}
|
|
439
307
|
else if (level === 'info') {
|
|
440
|
-
this.log.logLevel = "info"
|
|
308
|
+
this.log.logLevel = "info";
|
|
441
309
|
}
|
|
442
310
|
else if (level === 'notice') {
|
|
443
|
-
this.log.logLevel = "notice"
|
|
311
|
+
this.log.logLevel = "notice";
|
|
444
312
|
}
|
|
445
313
|
else if (level === 'warn') {
|
|
446
|
-
this.log.logLevel = "warn"
|
|
314
|
+
this.log.logLevel = "warn";
|
|
447
315
|
}
|
|
448
316
|
else if (level === 'error') {
|
|
449
|
-
this.log.logLevel = "error"
|
|
317
|
+
this.log.logLevel = "error";
|
|
450
318
|
}
|
|
451
319
|
else if (level === 'fatal') {
|
|
452
|
-
this.log.logLevel = "fatal"
|
|
320
|
+
this.log.logLevel = "fatal";
|
|
453
321
|
}
|
|
454
322
|
else {
|
|
455
323
|
this.log.warn(`Invalid matterbridge logger level: ${level}. Using default level "info".`);
|
|
456
|
-
this.log.logLevel = "info"
|
|
324
|
+
this.log.logLevel = "info";
|
|
457
325
|
}
|
|
458
326
|
}
|
|
459
327
|
else {
|
|
460
|
-
this.log.logLevel = await this.nodeContext.get('matterbridgeLogLevel', this.shellyBoard ? "notice"
|
|
328
|
+
this.log.logLevel = await this.nodeContext.get('matterbridgeLogLevel', this.shellyBoard ? "notice" : "info");
|
|
461
329
|
}
|
|
462
330
|
this.logLevel = this.log.logLevel;
|
|
463
331
|
this.frontend.logLevel = this.log.logLevel;
|
|
464
332
|
MatterbridgeEndpoint.logLevel = this.log.logLevel;
|
|
465
|
-
// Create the file logger for matterbridge (context: matterbridgeFileLog)
|
|
466
333
|
if (hasParameter('filelogger') || (await this.nodeContext.get('matterbridgeFileLog', false))) {
|
|
467
334
|
AnsiLogger.setGlobalLogfile(path.join(this.matterbridgeDirectory, MATTERBRIDGE_LOGGER_FILE), this.log.logLevel, true);
|
|
468
335
|
this.fileLogger = true;
|
|
@@ -471,7 +338,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
471
338
|
this.log.debug(`Matterbridge logLevel: ${this.log.logLevel} fileLoger: ${this.fileLogger}.`);
|
|
472
339
|
if (this.profile !== undefined)
|
|
473
340
|
this.log.debug(`Matterbridge profile: ${this.profile}.`);
|
|
474
|
-
// Set matter.js logger level, format and logger (context: matterLogLevel)
|
|
475
341
|
if (hasParameter('matterlogger')) {
|
|
476
342
|
const level = getParameter('matterlogger');
|
|
477
343
|
if (level === 'debug') {
|
|
@@ -502,13 +368,11 @@ export class Matterbridge extends EventEmitter {
|
|
|
502
368
|
}
|
|
503
369
|
Logger.format = MatterLogFormat.ANSI;
|
|
504
370
|
this.matterLogLevel = MatterLogLevel.names[Logger.level];
|
|
505
|
-
// Create the logger for matter.js with file logging (context: matterFileLog)
|
|
506
371
|
if (hasParameter('matterfilelogger') || (await this.nodeContext.get('matterFileLog', false))) {
|
|
507
372
|
this.matterFileLogger = true;
|
|
508
373
|
}
|
|
509
374
|
Logger.destinations.default.write = this.createDestinationMatterLogger(this.matterFileLogger);
|
|
510
375
|
this.log.debug(`Matter logLevel: ${this.matterLogLevel} fileLoger: ${this.matterFileLogger}.`);
|
|
511
|
-
// Log network interfaces
|
|
512
376
|
const networkInterfaces = os.networkInterfaces();
|
|
513
377
|
const availableAddresses = Object.entries(networkInterfaces);
|
|
514
378
|
const availableInterfaceNames = Object.keys(networkInterfaces);
|
|
@@ -521,7 +385,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
521
385
|
});
|
|
522
386
|
}
|
|
523
387
|
}
|
|
524
|
-
// Set the interface to use for matter server node mdnsInterface
|
|
525
388
|
if (hasParameter('mdnsinterface')) {
|
|
526
389
|
this.mdnsInterface = getParameter('mdnsinterface');
|
|
527
390
|
}
|
|
@@ -530,7 +393,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
530
393
|
if (this.mdnsInterface === '')
|
|
531
394
|
this.mdnsInterface = undefined;
|
|
532
395
|
}
|
|
533
|
-
// Validate mdnsInterface
|
|
534
396
|
if (this.mdnsInterface) {
|
|
535
397
|
if (!availableInterfaceNames.includes(this.mdnsInterface)) {
|
|
536
398
|
this.log.error(`Invalid mdnsinterface: ${this.mdnsInterface}. Available interfaces are: ${availableInterfaceNames.join(', ')}. Using all available interfaces.`);
|
|
@@ -543,7 +405,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
543
405
|
}
|
|
544
406
|
if (this.mdnsInterface)
|
|
545
407
|
this.environment.vars.set('mdns.networkInterface', this.mdnsInterface);
|
|
546
|
-
// Set the listeningAddressIpv4 for the matter commissioning server
|
|
547
408
|
if (hasParameter('ipv4address')) {
|
|
548
409
|
this.ipv4Address = getParameter('ipv4address');
|
|
549
410
|
}
|
|
@@ -552,7 +413,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
552
413
|
if (this.ipv4Address === '')
|
|
553
414
|
this.ipv4Address = undefined;
|
|
554
415
|
}
|
|
555
|
-
// Validate ipv4address
|
|
556
416
|
if (this.ipv4Address) {
|
|
557
417
|
let isValid = false;
|
|
558
418
|
for (const [ifaceName, ifaces] of availableAddresses) {
|
|
@@ -568,7 +428,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
568
428
|
await this.nodeContext.remove('matteripv4address');
|
|
569
429
|
}
|
|
570
430
|
}
|
|
571
|
-
// Set the listeningAddressIpv6 for the matter commissioning server
|
|
572
431
|
if (hasParameter('ipv6address')) {
|
|
573
432
|
this.ipv6Address = getParameter('ipv6address');
|
|
574
433
|
}
|
|
@@ -577,7 +436,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
577
436
|
if (this.ipv6Address === '')
|
|
578
437
|
this.ipv6Address = undefined;
|
|
579
438
|
}
|
|
580
|
-
// Validate ipv6address
|
|
581
439
|
if (this.ipv6Address) {
|
|
582
440
|
let isValid = false;
|
|
583
441
|
for (const [ifaceName, ifaces] of availableAddresses) {
|
|
@@ -586,7 +444,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
586
444
|
isValid = true;
|
|
587
445
|
break;
|
|
588
446
|
}
|
|
589
|
-
/* istanbul ignore next */
|
|
590
447
|
if (ifaces && ifaces.find((iface) => iface.scopeid && iface.scopeid > 0 && iface.address + '%' + (process.platform === 'win32' ? iface.scopeid : ifaceName) === this.ipv6Address)) {
|
|
591
448
|
this.log.info(`Using ipv6address ${CYAN}${this.ipv6Address}${nf} on interface ${CYAN}${ifaceName}${nf} for the Matter server node.`);
|
|
592
449
|
isValid = true;
|
|
@@ -599,7 +456,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
599
456
|
await this.nodeContext.remove('matteripv6address');
|
|
600
457
|
}
|
|
601
458
|
}
|
|
602
|
-
// Initialize the virtual mode
|
|
603
459
|
if (hasParameter('novirtual')) {
|
|
604
460
|
this.virtualMode = 'disabled';
|
|
605
461
|
await this.nodeContext.set('virtualmode', 'disabled');
|
|
@@ -608,18 +464,13 @@ export class Matterbridge extends EventEmitter {
|
|
|
608
464
|
this.virtualMode = (await this.nodeContext.get('virtualmode', 'outlet'));
|
|
609
465
|
}
|
|
610
466
|
this.log.debug(`Virtual mode ${this.virtualMode}.`);
|
|
611
|
-
// Initialize PluginManager
|
|
612
467
|
this.plugins.logLevel = this.log.logLevel;
|
|
613
468
|
await this.plugins.loadFromStorage();
|
|
614
|
-
// Initialize DeviceManager
|
|
615
469
|
this.devices.logLevel = this.log.logLevel;
|
|
616
|
-
// Get the plugins from node storage and create the plugins node storage contexts
|
|
617
470
|
for (const plugin of this.plugins) {
|
|
618
|
-
// Try to reinstall the plugin from npm (for Docker pull and external plugins)
|
|
619
|
-
// We don't do this when the add and other shutdown parameters are set because we shut down the process after adding the plugin
|
|
620
471
|
if (!fs.existsSync(plugin.path) && !hasParameter('add') && !hasParameter('remove') && !hasParameter('enable') && !hasParameter('disable') && !hasParameter('reset') && !hasParameter('factoryreset')) {
|
|
621
472
|
this.log.info(`Error parsing plugin ${plg}${plugin.name}${nf}. Trying to reinstall it from npm...`);
|
|
622
|
-
const { spawnCommand } = await import('./
|
|
473
|
+
const { spawnCommand } = await import('./spawn.js');
|
|
623
474
|
if (await spawnCommand('npm', ['install', '-g', `${plugin.name}${plugin.version.includes('-dev-') ? '@dev' : ''}`, '--omit=dev', '--verbose'], 'install', plugin.name)) {
|
|
624
475
|
this.log.info(`Plugin ${plg}${plugin.name}${nf} reinstalled.`);
|
|
625
476
|
plugin.error = false;
|
|
@@ -646,7 +497,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
646
497
|
await plugin.nodeContext.set('description', plugin.description);
|
|
647
498
|
await plugin.nodeContext.set('author', plugin.author);
|
|
648
499
|
}
|
|
649
|
-
// Log system info and create .matterbridge directory
|
|
650
500
|
await this.logNodeAndSystemInfo();
|
|
651
501
|
this.log.notice(`Matterbridge version ${this.matterbridgeVersion} ` +
|
|
652
502
|
`${hasParameter('bridge') || (!hasParameter('childbridge') && (await this.nodeContext?.get('bridgeMode', '')) === 'bridge') ? 'mode bridge ' : ''}` +
|
|
@@ -654,7 +504,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
654
504
|
`${hasParameter('controller') ? 'mode controller ' : ''}` +
|
|
655
505
|
`${this.restartMode !== '' ? 'restart mode ' + this.restartMode + ' ' : ''}` +
|
|
656
506
|
`running on ${this.systemInformation.osType} (v.${this.systemInformation.osRelease}) platform ${this.systemInformation.osPlatform} arch ${this.systemInformation.osArch}`);
|
|
657
|
-
// Check node version and throw error
|
|
658
507
|
const minNodeVersion = 20;
|
|
659
508
|
const nodeVersion = process.versions.node;
|
|
660
509
|
const versionMajor = parseInt(nodeVersion.split('.')[0]);
|
|
@@ -662,18 +511,10 @@ export class Matterbridge extends EventEmitter {
|
|
|
662
511
|
this.log.error(`Node version ${versionMajor} is not supported. Please upgrade to ${minNodeVersion} or above.`);
|
|
663
512
|
throw new Error(`Node version ${versionMajor} is not supported. Please upgrade to ${minNodeVersion} or above.`);
|
|
664
513
|
}
|
|
665
|
-
// Parse command line
|
|
666
514
|
await this.parseCommandLine();
|
|
667
|
-
// Emit the initialize_completed event
|
|
668
515
|
this.emit('initialize_completed');
|
|
669
516
|
this.initialized = true;
|
|
670
517
|
}
|
|
671
|
-
/**
|
|
672
|
-
* Parses the command line arguments and performs the corresponding actions.
|
|
673
|
-
*
|
|
674
|
-
* @private
|
|
675
|
-
* @returns {Promise<void>} A promise that resolves when the command line arguments have been processed, or the process exits.
|
|
676
|
-
*/
|
|
677
518
|
async parseCommandLine() {
|
|
678
519
|
if (hasParameter('list')) {
|
|
679
520
|
this.log.info(`│ Registered plugins (${this.plugins.length})`);
|
|
@@ -689,19 +530,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
689
530
|
}
|
|
690
531
|
index++;
|
|
691
532
|
}
|
|
692
|
-
/*
|
|
693
|
-
const serializedRegisteredDevices = await this.nodeContext?.get<SerializedMatterbridgeEndpoint[]>('devices', []);
|
|
694
|
-
this.log.info(`│ Registered devices (${serializedRegisteredDevices?.length})`);
|
|
695
|
-
serializedRegisteredDevices?.forEach((device, index) => {
|
|
696
|
-
if (index !== serializedRegisteredDevices.length - 1) {
|
|
697
|
-
this.log.info(`├─┬─ plugin ${plg}${device.pluginName}${nf} device: ${dev}${device.deviceName}${nf} uniqueId: ${YELLOW}${device.uniqueId}${nf}`);
|
|
698
|
-
this.log.info(`│ └─ endpoint ${RED}${device.endpoint}${nf} ${typ}${device.endpointName}${nf} ${debugStringify(device.clusterServersId)}`);
|
|
699
|
-
} else {
|
|
700
|
-
this.log.info(`└─┬─ plugin ${plg}${device.pluginName}${nf} device: ${dev}${device.deviceName}${nf} uniqueId: ${YELLOW}${device.uniqueId}${nf}`);
|
|
701
|
-
this.log.info(` └─ endpoint ${RED}${device.endpoint}${nf} ${typ}${device.endpointName}${nf} ${debugStringify(device.clusterServersId)}`);
|
|
702
|
-
}
|
|
703
|
-
});
|
|
704
|
-
*/
|
|
705
533
|
this.shutdown = true;
|
|
706
534
|
return;
|
|
707
535
|
}
|
|
@@ -716,7 +544,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
716
544
|
return;
|
|
717
545
|
}
|
|
718
546
|
if (hasParameter('loginterfaces')) {
|
|
719
|
-
const { logInterfaces } = await import('
|
|
547
|
+
const { logInterfaces } = await import('@matterbridge/utils');
|
|
720
548
|
logInterfaces();
|
|
721
549
|
this.shutdown = true;
|
|
722
550
|
return;
|
|
@@ -751,10 +579,8 @@ export class Matterbridge extends EventEmitter {
|
|
|
751
579
|
this.shutdown = true;
|
|
752
580
|
return;
|
|
753
581
|
}
|
|
754
|
-
// Initialize frontend
|
|
755
582
|
if (getIntParameter('frontend') !== 0 || getIntParameter('frontend') === undefined)
|
|
756
583
|
await this.frontend.start(getIntParameter('frontend'));
|
|
757
|
-
// Start the matter storage and create the matterbridge context
|
|
758
584
|
try {
|
|
759
585
|
await this.startMatterStorage();
|
|
760
586
|
if (this.aggregatorSerialNumber && this.aggregatorUniqueId && this.matterStorageService) {
|
|
@@ -768,21 +594,18 @@ export class Matterbridge extends EventEmitter {
|
|
|
768
594
|
this.log.fatal(`Fatal error creating matter storage: ${error instanceof Error ? error.message : error}`);
|
|
769
595
|
throw new Error(`Fatal error creating matter storage: ${error instanceof Error ? error.message : error}`);
|
|
770
596
|
}
|
|
771
|
-
// Clear the matterbridge context if the reset parameter is set (bridge mode)
|
|
772
597
|
if (hasParameter('reset') && getParameter('reset') === undefined) {
|
|
773
598
|
this.initialized = true;
|
|
774
599
|
await this.shutdownProcessAndReset();
|
|
775
600
|
this.shutdown = true;
|
|
776
601
|
return;
|
|
777
602
|
}
|
|
778
|
-
// Clear matterbridge plugin context if the reset parameter is set (childbridge mode)
|
|
779
603
|
if (hasParameter('reset') && getParameter('reset') !== undefined) {
|
|
780
604
|
this.log.debug(`Reset plugin ${getParameter('reset')}`);
|
|
781
605
|
const plugin = this.plugins.get(getParameter('reset'));
|
|
782
606
|
if (plugin) {
|
|
783
607
|
const matterStorageManager = await this.matterStorageService?.open(plugin.name);
|
|
784
608
|
if (!matterStorageManager) {
|
|
785
|
-
/* istanbul ignore next */
|
|
786
609
|
this.log.error(`Plugin ${plg}${plugin.name}${er} storageManager not found`);
|
|
787
610
|
}
|
|
788
611
|
else {
|
|
@@ -801,56 +624,47 @@ export class Matterbridge extends EventEmitter {
|
|
|
801
624
|
this.shutdown = true;
|
|
802
625
|
return;
|
|
803
626
|
}
|
|
804
|
-
// Check in 5 minutes the latest and dev versions of matterbridge and the plugins
|
|
805
627
|
clearTimeout(this.checkUpdateTimeout);
|
|
806
628
|
this.checkUpdateTimeout = setTimeout(async () => {
|
|
807
629
|
const { checkUpdates } = await import('./update.js');
|
|
808
630
|
checkUpdates(this);
|
|
809
631
|
}, 300 * 1000).unref();
|
|
810
|
-
// Check each 12 hours the latest and dev versions of matterbridge and the plugins
|
|
811
632
|
clearInterval(this.checkUpdateInterval);
|
|
812
633
|
this.checkUpdateInterval = setInterval(async () => {
|
|
813
634
|
const { checkUpdates } = await import('./update.js');
|
|
814
635
|
checkUpdates(this);
|
|
815
636
|
}, 12 * 60 * 60 * 1000).unref();
|
|
816
|
-
// Start the matterbridge in mode test
|
|
817
637
|
if (hasParameter('test')) {
|
|
818
638
|
this.bridgeMode = 'bridge';
|
|
819
639
|
return;
|
|
820
640
|
}
|
|
821
|
-
// Start the matterbridge in mode controller
|
|
822
641
|
if (hasParameter('controller')) {
|
|
823
642
|
this.bridgeMode = 'controller';
|
|
824
643
|
await this.startController();
|
|
825
644
|
return;
|
|
826
645
|
}
|
|
827
|
-
// Check if the bridge mode is set and start matterbridge in bridge mode if not set
|
|
828
646
|
if (!hasParameter('bridge') && !hasParameter('childbridge') && (await this.nodeContext?.get('bridgeMode', '')) === '') {
|
|
829
647
|
this.log.info('Setting default matterbridge start mode to bridge');
|
|
830
648
|
await this.nodeContext?.set('bridgeMode', 'bridge');
|
|
831
649
|
}
|
|
832
|
-
// Wait delay if specified (default 2 minutes) and the system uptime is less than 5 minutes. It solves race conditions on system startup.
|
|
833
650
|
if (hasParameter('delay') && os.uptime() <= 60 * 5) {
|
|
834
|
-
const { wait } = await import('
|
|
651
|
+
const { wait } = await import('@matterbridge/utils');
|
|
835
652
|
const delay = getIntParameter('delay') || 120;
|
|
836
653
|
this.log.warn('Delay switch found with system uptime less then 5 minutes. Waiting for ' + delay + ' seconds before starting matterbridge...');
|
|
837
654
|
await wait(delay * 1000, 'Race condition delay', true);
|
|
838
655
|
}
|
|
839
|
-
// Wait delay if specified (default 2 minutes). It solves race conditions on docker compose startup.
|
|
840
656
|
if (hasParameter('fixed_delay')) {
|
|
841
|
-
const { wait } = await import('
|
|
657
|
+
const { wait } = await import('@matterbridge/utils');
|
|
842
658
|
const delay = getIntParameter('fixed_delay') || 120;
|
|
843
659
|
this.log.warn('Fixed delay switch found. Waiting for ' + delay + ' seconds before starting matterbridge...');
|
|
844
660
|
await wait(delay * 1000, 'Fixed race condition delay', true);
|
|
845
661
|
}
|
|
846
|
-
// Start matterbridge in bridge mode
|
|
847
662
|
if (hasParameter('bridge') || (!hasParameter('childbridge') && (await this.nodeContext?.get('bridgeMode', '')) === 'bridge')) {
|
|
848
663
|
this.bridgeMode = 'bridge';
|
|
849
664
|
this.log.debug(`Starting matterbridge in mode ${this.bridgeMode}`);
|
|
850
665
|
await this.startBridge();
|
|
851
666
|
return;
|
|
852
667
|
}
|
|
853
|
-
// Start matterbridge in childbridge mode
|
|
854
668
|
if (hasParameter('childbridge') || (!hasParameter('bridge') && (await this.nodeContext?.get('bridgeMode', '')) === 'childbridge')) {
|
|
855
669
|
this.bridgeMode = 'childbridge';
|
|
856
670
|
this.log.debug(`Starting matterbridge in mode ${this.bridgeMode}`);
|
|
@@ -858,22 +672,10 @@ export class Matterbridge extends EventEmitter {
|
|
|
858
672
|
return;
|
|
859
673
|
}
|
|
860
674
|
}
|
|
861
|
-
/**
|
|
862
|
-
* Asynchronously loads and starts the registered plugins.
|
|
863
|
-
*
|
|
864
|
-
* This method is responsible for initializing and starting all enabled plugins.
|
|
865
|
-
* It ensures that each plugin is properly loaded and started before the bridge starts.
|
|
866
|
-
*
|
|
867
|
-
* @param {boolean} [wait] - If true, the method will wait for all plugins to be fully loaded and started before resolving. Defaults to false.
|
|
868
|
-
* @param {boolean} [start] - If true, the method will start the plugins after loading them. Defaults to true.
|
|
869
|
-
* @returns {Promise<void>} A promise that resolves when all plugins have been loaded and started.
|
|
870
|
-
*/
|
|
871
675
|
async startPlugins(wait = false, start = true) {
|
|
872
|
-
// Check, load and start the plugins
|
|
873
676
|
for (const plugin of this.plugins) {
|
|
874
677
|
plugin.configJson = await this.plugins.loadConfig(plugin);
|
|
875
678
|
plugin.schemaJson = await this.plugins.loadSchema(plugin);
|
|
876
|
-
// Check if the plugin is available
|
|
877
679
|
if (!(await this.plugins.resolve(plugin.path))) {
|
|
878
680
|
this.log.error(`Plugin ${plg}${plugin.name}${er} not found or not validated. Disabling it.`);
|
|
879
681
|
plugin.enabled = false;
|
|
@@ -893,16 +695,10 @@ export class Matterbridge extends EventEmitter {
|
|
|
893
695
|
if (wait)
|
|
894
696
|
await this.plugins.load(plugin, start, 'Matterbridge is starting');
|
|
895
697
|
else
|
|
896
|
-
this.plugins.load(plugin, start, 'Matterbridge is starting');
|
|
698
|
+
this.plugins.load(plugin, start, 'Matterbridge is starting');
|
|
897
699
|
}
|
|
898
700
|
this.frontend.wssSendRefreshRequired('plugins');
|
|
899
701
|
}
|
|
900
|
-
/**
|
|
901
|
-
* Registers the process handlers for uncaughtException, unhandledRejection, SIGINT and SIGTERM.
|
|
902
|
-
* - When an uncaught exception occurs, the exceptionHandler logs the error message and stack trace.
|
|
903
|
-
* - When an unhandled promise rejection occurs, the rejectionHandler logs the reason and stack trace.
|
|
904
|
-
* - When either of SIGINT and SIGTERM signals are received, the cleanup method is called with an appropriate message.
|
|
905
|
-
*/
|
|
906
702
|
registerProcessHandlers() {
|
|
907
703
|
this.log.debug(`Registering uncaughtException and unhandledRejection handlers...`);
|
|
908
704
|
process.removeAllListeners('uncaughtException');
|
|
@@ -929,9 +725,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
929
725
|
};
|
|
930
726
|
process.on('SIGTERM', this.sigtermHandler);
|
|
931
727
|
}
|
|
932
|
-
/**
|
|
933
|
-
* Deregisters the process uncaughtException, unhandledRejection, SIGINT and SIGTERM signal handlers.
|
|
934
|
-
*/
|
|
935
728
|
deregisterProcessHandlers() {
|
|
936
729
|
this.log.debug(`Deregistering uncaughtException and unhandledRejection handlers...`);
|
|
937
730
|
if (this.exceptionHandler)
|
|
@@ -948,20 +741,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
948
741
|
process.off('SIGTERM', this.sigtermHandler);
|
|
949
742
|
this.sigtermHandler = undefined;
|
|
950
743
|
}
|
|
951
|
-
/**
|
|
952
|
-
* Logs the node and system information.
|
|
953
|
-
*
|
|
954
|
-
* @remarks
|
|
955
|
-
* This method retrieves and logs various details about the host system, including:
|
|
956
|
-
* - IP address information (IPv4, IPv6, MAC address)
|
|
957
|
-
* - Node.js version
|
|
958
|
-
* - Hostname and user information
|
|
959
|
-
* - Operating system details (type, release, platform, architecture)
|
|
960
|
-
* - Memory usage statistics
|
|
961
|
-
* - Uptime information for both the system and the process
|
|
962
|
-
*/
|
|
963
744
|
async logNodeAndSystemInfo() {
|
|
964
|
-
// IP address information
|
|
965
745
|
const excludedInterfaceNamePattern = /(tailscale|wireguard|openvpn|zerotier|hamachi|\bwg\d+\b|\btun\d+\b|\btap\d+\b|\butun\d+\b|docker|podman|\bveth[a-z0-9]*\b|\bbr-[a-z0-9]+\b|cni|kube|flannel|calico|virbr\d*\b|vmware|vmnet\d*\b|virtualbox|vboxnet\d*\b|teredo|isatap)/i;
|
|
966
746
|
const networkInterfaces = os.networkInterfaces();
|
|
967
747
|
this.systemInformation.interfaceName = '';
|
|
@@ -995,18 +775,16 @@ export class Matterbridge extends EventEmitter {
|
|
|
995
775
|
break;
|
|
996
776
|
}
|
|
997
777
|
}
|
|
998
|
-
// Node information
|
|
999
778
|
this.systemInformation.nodeVersion = process.versions.node;
|
|
1000
779
|
const versionMajor = parseInt(this.systemInformation.nodeVersion.split('.')[0]);
|
|
1001
780
|
const versionMinor = parseInt(this.systemInformation.nodeVersion.split('.')[1]);
|
|
1002
781
|
const versionPatch = parseInt(this.systemInformation.nodeVersion.split('.')[2]);
|
|
1003
|
-
// Host system information
|
|
1004
782
|
this.systemInformation.hostname = os.hostname();
|
|
1005
783
|
this.systemInformation.user = os.userInfo().username;
|
|
1006
|
-
this.systemInformation.osType = os.type();
|
|
1007
|
-
this.systemInformation.osRelease = os.release();
|
|
1008
|
-
this.systemInformation.osPlatform = os.platform();
|
|
1009
|
-
this.systemInformation.osArch = os.arch();
|
|
784
|
+
this.systemInformation.osType = os.type();
|
|
785
|
+
this.systemInformation.osRelease = os.release();
|
|
786
|
+
this.systemInformation.osPlatform = os.platform();
|
|
787
|
+
this.systemInformation.osArch = os.arch();
|
|
1010
788
|
this.systemInformation.totalMemory = formatBytes(os.totalmem());
|
|
1011
789
|
this.systemInformation.freeMemory = formatBytes(os.freemem());
|
|
1012
790
|
this.systemInformation.systemUptime = formatUptime(os.uptime());
|
|
@@ -1016,7 +794,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1016
794
|
this.systemInformation.rss = formatBytes(process.memoryUsage().rss);
|
|
1017
795
|
this.systemInformation.heapTotal = formatBytes(process.memoryUsage().heapTotal);
|
|
1018
796
|
this.systemInformation.heapUsed = formatBytes(process.memoryUsage().heapUsed);
|
|
1019
|
-
// Log the system information
|
|
1020
797
|
this.log.debug('Host System Information:');
|
|
1021
798
|
this.log.debug(`- Hostname: ${this.systemInformation.hostname}`);
|
|
1022
799
|
this.log.debug(`- User: ${this.systemInformation.user}`);
|
|
@@ -1036,20 +813,17 @@ export class Matterbridge extends EventEmitter {
|
|
|
1036
813
|
this.log.debug(`- RSS: ${this.systemInformation.rss}`);
|
|
1037
814
|
this.log.debug(`- Heap Total: ${this.systemInformation.heapTotal}`);
|
|
1038
815
|
this.log.debug(`- Heap Used: ${this.systemInformation.heapUsed}`);
|
|
1039
|
-
// Log directories
|
|
1040
816
|
this.log.debug(`Root Directory: ${this.rootDirectory}`);
|
|
1041
817
|
this.log.debug(`Home Directory: ${this.homeDirectory}`);
|
|
1042
818
|
this.log.debug(`Matterbridge Directory: ${this.matterbridgeDirectory}`);
|
|
1043
819
|
this.log.debug(`Matterbridge Plugin Directory: ${this.matterbridgePluginDirectory}`);
|
|
1044
820
|
this.log.debug(`Matterbridge Matter Certificate Directory: ${this.matterbridgeCertDirectory}`);
|
|
1045
|
-
// Global node_modules directory
|
|
1046
821
|
if (this.nodeContext)
|
|
1047
822
|
this.globalModulesDirectory = await this.nodeContext.get('globalModulesDirectory', '');
|
|
1048
823
|
if (this.globalModulesDirectory === '') {
|
|
1049
|
-
// First run of Matterbridge so the node storage is empty
|
|
1050
824
|
this.log.debug(`Getting global node_modules directory...`);
|
|
1051
825
|
try {
|
|
1052
|
-
const { getGlobalNodeModules } = await import('
|
|
826
|
+
const { getGlobalNodeModules } = await import('@matterbridge/utils');
|
|
1053
827
|
this.globalModulesDirectory = await getGlobalNodeModules();
|
|
1054
828
|
this.log.debug(`Global node_modules Directory: ${this.globalModulesDirectory}`);
|
|
1055
829
|
await this.nodeContext?.set('globalModulesDirectory', this.globalModulesDirectory);
|
|
@@ -1059,42 +833,29 @@ export class Matterbridge extends EventEmitter {
|
|
|
1059
833
|
}
|
|
1060
834
|
}
|
|
1061
835
|
else {
|
|
1062
|
-
// The global node_modules directory is already set in the node storage and we check if it is still valid
|
|
1063
836
|
this.log.debug(`Global node_modules Directory: ${this.globalModulesDirectory}`);
|
|
1064
837
|
const { createESMWorker } = await import('./workers.js');
|
|
1065
838
|
createESMWorker('NpmGlobalPrefix', path.join(this.rootDirectory, 'dist/workerGlobalPrefix.js'));
|
|
1066
839
|
}
|
|
1067
|
-
// Matterbridge version
|
|
1068
840
|
this.log.debug(`Reading matterbridge package.json...`);
|
|
1069
841
|
const packageJson = JSON.parse(await fs.promises.readFile(path.join(this.rootDirectory, 'package.json'), 'utf-8'));
|
|
1070
842
|
this.matterbridgeVersion = this.matterbridgeLatestVersion = this.matterbridgeDevVersion = packageJson.version;
|
|
1071
843
|
this.log.debug(`Matterbridge Version: ${this.matterbridgeVersion}`);
|
|
1072
|
-
// Matterbridge latest version (will be set in the checkUpdate function)
|
|
1073
844
|
if (this.nodeContext)
|
|
1074
845
|
this.matterbridgeLatestVersion = await this.nodeContext.get('matterbridgeLatestVersion', this.matterbridgeVersion);
|
|
1075
846
|
this.log.debug(`Matterbridge Latest Version: ${this.matterbridgeLatestVersion}`);
|
|
1076
|
-
// Matterbridge dev version (will be set in the checkUpdate function)
|
|
1077
847
|
if (this.nodeContext)
|
|
1078
848
|
this.matterbridgeDevVersion = await this.nodeContext.get('matterbridgeDevVersion', this.matterbridgeVersion);
|
|
1079
849
|
this.log.debug(`Matterbridge Dev Version: ${this.matterbridgeDevVersion}`);
|
|
1080
|
-
// Frontend version
|
|
1081
850
|
this.log.debug(`Reading frontend package.json...`);
|
|
1082
851
|
const frontendPackageJson = JSON.parse(await fs.promises.readFile(path.join(this.rootDirectory, 'frontend/package.json'), 'utf8'));
|
|
1083
852
|
this.frontendVersion = frontendPackageJson.version;
|
|
1084
853
|
this.log.debug(`Frontend version ${CYAN}${this.frontendVersion}${db}`);
|
|
1085
|
-
// Current working directory
|
|
1086
854
|
const currentDir = process.cwd();
|
|
1087
855
|
this.log.debug(`Current Working Directory: ${currentDir}`);
|
|
1088
|
-
// Command line arguments (excluding 'node' and the script name)
|
|
1089
856
|
const cmdArgs = process.argv.slice(2).join(' ');
|
|
1090
857
|
this.log.debug(`Command Line Arguments: ${cmdArgs}`);
|
|
1091
858
|
}
|
|
1092
|
-
/**
|
|
1093
|
-
* Set the logger logLevel for the Matterbridge classes and call onChangeLoggerLevel() for each plugin.
|
|
1094
|
-
*
|
|
1095
|
-
* @param {LogLevel} logLevel The logger logLevel to set.
|
|
1096
|
-
* @returns {Promise<LogLevel>} A promise that resolves when the logLevel has been set.
|
|
1097
|
-
*/
|
|
1098
859
|
async setLogLevel(logLevel) {
|
|
1099
860
|
this.logLevel = logLevel;
|
|
1100
861
|
this.log.logLevel = logLevel;
|
|
@@ -1108,90 +869,61 @@ export class Matterbridge extends EventEmitter {
|
|
|
1108
869
|
continue;
|
|
1109
870
|
if (plugin.platform.config.debug === true)
|
|
1110
871
|
pluginDebug = true;
|
|
1111
|
-
plugin.platform.log.logLevel = plugin.platform.config.debug === true ? "debug"
|
|
1112
|
-
await plugin.platform.onChangeLoggerLevel(plugin.platform.config.debug === true ? "debug"
|
|
1113
|
-
}
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
callbackLogLevel = "debug" /* LogLevel.DEBUG */;
|
|
872
|
+
plugin.platform.log.logLevel = plugin.platform.config.debug === true ? "debug" : logLevel;
|
|
873
|
+
await plugin.platform.onChangeLoggerLevel(plugin.platform.config.debug === true ? "debug" : logLevel);
|
|
874
|
+
}
|
|
875
|
+
let callbackLogLevel = "notice";
|
|
876
|
+
if (logLevel === "info" || Logger.level === MatterLogLevel.INFO)
|
|
877
|
+
callbackLogLevel = "info";
|
|
878
|
+
if (logLevel === "debug" || Logger.level === MatterLogLevel.DEBUG || pluginDebug)
|
|
879
|
+
callbackLogLevel = "debug";
|
|
1120
880
|
AnsiLogger.setGlobalCallbackLevel(callbackLogLevel);
|
|
1121
881
|
this.log.debug(`WebSocketServer logger global callback set to ${callbackLogLevel}`);
|
|
1122
882
|
return logLevel;
|
|
1123
883
|
}
|
|
1124
|
-
/**
|
|
1125
|
-
* Get the current logger logLevel.
|
|
1126
|
-
*
|
|
1127
|
-
* @returns {LogLevel} The current logger logLevel.
|
|
1128
|
-
*/
|
|
1129
884
|
getLogLevel() {
|
|
1130
885
|
return this.log.logLevel;
|
|
1131
886
|
}
|
|
1132
|
-
/**
|
|
1133
|
-
* Creates a MatterLogger function to show the matter.js log messages in AnsiLogger (for the frontend).
|
|
1134
|
-
* It also logs to file (matter.log) if fileLogger is true.
|
|
1135
|
-
*
|
|
1136
|
-
* @param {boolean} fileLogger - Whether to log to file or not.
|
|
1137
|
-
* @returns {Function} The MatterLogger function. \x1b[35m for violet \x1b[34m is blue
|
|
1138
|
-
*/
|
|
1139
887
|
createDestinationMatterLogger(fileLogger) {
|
|
1140
|
-
this.matterLog.logNameColor = '\x1b[34m';
|
|
888
|
+
this.matterLog.logNameColor = '\x1b[34m';
|
|
1141
889
|
if (fileLogger) {
|
|
1142
890
|
this.matterLog.logFilePath = path.join(this.matterbridgeDirectory, MATTER_LOGGER_FILE);
|
|
1143
891
|
}
|
|
1144
892
|
return (text, message) => {
|
|
1145
|
-
// 2024-08-21 08:55:19.488 DEBUG InteractionMessenger Sending DataReport chunk with 28 attributes and 0 events: 1004 bytes
|
|
1146
893
|
const logger = text.slice(44, 44 + 20).trim();
|
|
1147
894
|
const msg = text.slice(65);
|
|
1148
895
|
this.matterLog.logName = logger;
|
|
1149
896
|
switch (message.level) {
|
|
1150
897
|
case MatterLogLevel.DEBUG:
|
|
1151
|
-
this.matterLog.log("debug"
|
|
898
|
+
this.matterLog.log("debug", msg);
|
|
1152
899
|
break;
|
|
1153
900
|
case MatterLogLevel.INFO:
|
|
1154
|
-
this.matterLog.log("info"
|
|
901
|
+
this.matterLog.log("info", msg);
|
|
1155
902
|
break;
|
|
1156
903
|
case MatterLogLevel.NOTICE:
|
|
1157
|
-
this.matterLog.log("notice"
|
|
904
|
+
this.matterLog.log("notice", msg);
|
|
1158
905
|
break;
|
|
1159
906
|
case MatterLogLevel.WARN:
|
|
1160
|
-
this.matterLog.log("warn"
|
|
907
|
+
this.matterLog.log("warn", msg);
|
|
1161
908
|
break;
|
|
1162
909
|
case MatterLogLevel.ERROR:
|
|
1163
|
-
this.matterLog.log("error"
|
|
910
|
+
this.matterLog.log("error", msg);
|
|
1164
911
|
break;
|
|
1165
912
|
case MatterLogLevel.FATAL:
|
|
1166
|
-
this.matterLog.log("fatal"
|
|
913
|
+
this.matterLog.log("fatal", msg);
|
|
1167
914
|
break;
|
|
1168
915
|
}
|
|
1169
916
|
};
|
|
1170
917
|
}
|
|
1171
|
-
/**
|
|
1172
|
-
* Restarts the process by exiting the current instance and loading a new instance (/api/restart).
|
|
1173
|
-
*
|
|
1174
|
-
* @returns {Promise<void>} A promise that resolves when the restart is completed.
|
|
1175
|
-
*/
|
|
1176
918
|
async restartProcess() {
|
|
1177
919
|
await this.cleanup('restarting...', true);
|
|
1178
920
|
}
|
|
1179
|
-
/**
|
|
1180
|
-
* Shut down the process (/api/shutdown).
|
|
1181
|
-
*
|
|
1182
|
-
* @returns {Promise<void>} A promise that resolves when the shutdown is completed.
|
|
1183
|
-
*/
|
|
1184
921
|
async shutdownProcess() {
|
|
1185
922
|
await this.cleanup('shutting down...', false);
|
|
1186
923
|
}
|
|
1187
|
-
/**
|
|
1188
|
-
* Update matterbridge and shut down the process (virtual device 'Update Matterbridge').
|
|
1189
|
-
*
|
|
1190
|
-
* @returns {Promise<void>} A promise that resolves when the update is completed.
|
|
1191
|
-
*/
|
|
1192
924
|
async updateProcess() {
|
|
1193
925
|
this.log.info('Updating matterbridge...');
|
|
1194
|
-
const { spawnCommand } = await import('./
|
|
926
|
+
const { spawnCommand } = await import('./spawn.js');
|
|
1195
927
|
if (await spawnCommand('npm', ['install', '-g', 'matterbridge', '--omit=dev', '--verbose'], 'install', 'matterbridge')) {
|
|
1196
928
|
this.log.info('Matterbridge has been updated. Full restart required.');
|
|
1197
929
|
}
|
|
@@ -1201,15 +933,8 @@ export class Matterbridge extends EventEmitter {
|
|
|
1201
933
|
this.frontend.wssSendRestartRequired();
|
|
1202
934
|
await this.cleanup('updating...', false);
|
|
1203
935
|
}
|
|
1204
|
-
/**
|
|
1205
|
-
* Unregister all devices and shut down the process (/api/unregister).
|
|
1206
|
-
*
|
|
1207
|
-
* @param {number} [timeout] - The timeout duration to wait for the message exchange to complete in milliseconds. Default is 1000.
|
|
1208
|
-
*
|
|
1209
|
-
* @returns {Promise<void>} A promise that resolves when the cleanup is completed.
|
|
1210
|
-
*/
|
|
1211
936
|
async unregisterAndShutdownProcess(timeout = 1000) {
|
|
1212
|
-
const { wait } = await import('
|
|
937
|
+
const { wait } = await import('@matterbridge/utils');
|
|
1213
938
|
this.log.info('Unregistering all devices and shutting down...');
|
|
1214
939
|
for (const plugin of this.plugins.array()) {
|
|
1215
940
|
if (plugin.error || !plugin.enabled)
|
|
@@ -1220,71 +945,46 @@ export class Matterbridge extends EventEmitter {
|
|
|
1220
945
|
await this.removeAllBridgedEndpoints(plugin.name, 100);
|
|
1221
946
|
}
|
|
1222
947
|
this.log.debug('Waiting for the MessageExchange to finish...');
|
|
1223
|
-
await wait(timeout);
|
|
948
|
+
await wait(timeout);
|
|
1224
949
|
this.log.debug('Cleaning up and shutting down...');
|
|
1225
950
|
await this.cleanup('unregistered all devices and shutting down...', false, timeout);
|
|
1226
951
|
}
|
|
1227
|
-
/**
|
|
1228
|
-
* Reset commissioning and shut down the process (/api/reset).
|
|
1229
|
-
*
|
|
1230
|
-
* @returns {Promise<void>} A promise that resolves when the cleanup is completed.
|
|
1231
|
-
*/
|
|
1232
952
|
async shutdownProcessAndReset() {
|
|
1233
953
|
await this.cleanup('shutting down with reset...', false);
|
|
1234
954
|
}
|
|
1235
|
-
/**
|
|
1236
|
-
* Factory reset and shut down the process (/api/factory-reset).
|
|
1237
|
-
*
|
|
1238
|
-
* @returns {Promise<void>} A promise that resolves when the cleanup is completed.
|
|
1239
|
-
*/
|
|
1240
955
|
async shutdownProcessAndFactoryReset() {
|
|
1241
956
|
await this.cleanup('shutting down with factory reset...', false);
|
|
1242
957
|
}
|
|
1243
|
-
/**
|
|
1244
|
-
* Cleans up the Matterbridge instance.
|
|
1245
|
-
*
|
|
1246
|
-
* @param {string} message - The cleanup message.
|
|
1247
|
-
* @param {boolean} [restart] - Indicates whether to restart the instance after cleanup. Default is `false`.
|
|
1248
|
-
* @param {number} [pause] - The pause in ms to wait for the message exchange to complete in milliseconds. Default is 1000.
|
|
1249
|
-
*
|
|
1250
|
-
* @returns {Promise<void>} A promise that resolves when the cleanup is completed.
|
|
1251
|
-
*/
|
|
1252
958
|
async cleanup(message, restart = false, pause = 1000) {
|
|
1253
959
|
if (this.initialized && !this.hasCleanupStarted) {
|
|
1254
960
|
this.emit('cleanup_started');
|
|
1255
961
|
this.hasCleanupStarted = true;
|
|
1256
962
|
this.log.info(message);
|
|
1257
|
-
// Clear the start matter interval
|
|
1258
963
|
if (this.startMatterInterval) {
|
|
1259
964
|
clearInterval(this.startMatterInterval);
|
|
1260
965
|
this.startMatterInterval = undefined;
|
|
1261
966
|
this.log.debug('Start matter interval cleared');
|
|
1262
967
|
}
|
|
1263
|
-
// Clear the check update timeout
|
|
1264
968
|
if (this.checkUpdateTimeout) {
|
|
1265
969
|
clearTimeout(this.checkUpdateTimeout);
|
|
1266
970
|
this.checkUpdateTimeout = undefined;
|
|
1267
971
|
this.log.debug('Check update timeout cleared');
|
|
1268
972
|
}
|
|
1269
|
-
// Clear the check update interval
|
|
1270
973
|
if (this.checkUpdateInterval) {
|
|
1271
974
|
clearInterval(this.checkUpdateInterval);
|
|
1272
975
|
this.checkUpdateInterval = undefined;
|
|
1273
976
|
this.log.debug('Check update interval cleared');
|
|
1274
977
|
}
|
|
1275
|
-
// Clear the configure timeout
|
|
1276
978
|
if (this.configureTimeout) {
|
|
1277
979
|
clearTimeout(this.configureTimeout);
|
|
1278
980
|
this.configureTimeout = undefined;
|
|
1279
981
|
this.log.debug('Matterbridge configure timeout cleared');
|
|
1280
982
|
}
|
|
1281
|
-
// Clear the reachability timeout
|
|
1282
983
|
if (this.reachabilityTimeout) {
|
|
1283
984
|
clearTimeout(this.reachabilityTimeout);
|
|
1284
985
|
this.reachabilityTimeout = undefined;
|
|
1285
986
|
this.log.debug('Matterbridge reachability timeout cleared');
|
|
1286
987
|
}
|
|
1287
|
-
// Call the shutdown method of each plugin and clear the plugins reachability timeout
|
|
1288
988
|
for (const plugin of this.plugins) {
|
|
1289
989
|
if (!plugin.enabled || plugin.error)
|
|
1290
990
|
continue;
|
|
@@ -1295,10 +995,9 @@ export class Matterbridge extends EventEmitter {
|
|
|
1295
995
|
this.log.debug(`Plugin ${plg}${plugin.name}${db} reachability timeout cleared`);
|
|
1296
996
|
}
|
|
1297
997
|
}
|
|
1298
|
-
// Stop matter server nodes
|
|
1299
998
|
this.log.notice(`Stopping matter server nodes in ${this.bridgeMode} mode...`);
|
|
1300
999
|
if (pause > 0) {
|
|
1301
|
-
const { wait } = await import('
|
|
1000
|
+
const { wait } = await import('@matterbridge/utils');
|
|
1302
1001
|
this.log.debug(`Waiting ${pause}ms for the MessageExchange to finish...`);
|
|
1303
1002
|
await wait(pause, `Waiting ${pause}ms for the MessageExchange to finish...`, false);
|
|
1304
1003
|
}
|
|
@@ -1323,7 +1022,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1323
1022
|
}
|
|
1324
1023
|
}
|
|
1325
1024
|
this.log.notice('Stopped matter server nodes');
|
|
1326
|
-
// Matter commisioning reset
|
|
1327
1025
|
if (message === 'shutting down with reset...') {
|
|
1328
1026
|
this.log.info('Resetting Matterbridge commissioning information...');
|
|
1329
1027
|
await this.matterStorageManager?.createContext('events')?.clearAll();
|
|
@@ -1333,7 +1031,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1333
1031
|
await this.matterbridgeContext?.clearAll();
|
|
1334
1032
|
this.log.info('Matter storage reset done! Remove the bridge from the controller.');
|
|
1335
1033
|
}
|
|
1336
|
-
// Unregister all devices
|
|
1337
1034
|
if (message === 'unregistered all devices and shutting down...') {
|
|
1338
1035
|
if (this.bridgeMode === 'bridge') {
|
|
1339
1036
|
await this.matterStorageManager?.createContext('root')?.createContext('parts')?.createContext('Matterbridge')?.createContext('parts')?.clearAll();
|
|
@@ -1351,14 +1048,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
1351
1048
|
}
|
|
1352
1049
|
this.log.info('Matter storage reset done!');
|
|
1353
1050
|
}
|
|
1354
|
-
// Stop matter storage
|
|
1355
1051
|
await this.stopMatterStorage();
|
|
1356
|
-
/**
|
|
1357
|
-
* Unlink a file safely, ignoring errors.
|
|
1358
|
-
*
|
|
1359
|
-
* @param {string} path - The path to the file to unlink.
|
|
1360
|
-
* @param {AnsiLogger} log - The logger to use for logging.
|
|
1361
|
-
*/
|
|
1362
1052
|
function unlinkSafe(path, log) {
|
|
1363
1053
|
try {
|
|
1364
1054
|
log.debug(`Removing ${path}...`);
|
|
@@ -1366,15 +1056,12 @@ export class Matterbridge extends EventEmitter {
|
|
|
1366
1056
|
log.debug(`Removed ${path}`);
|
|
1367
1057
|
}
|
|
1368
1058
|
catch {
|
|
1369
|
-
// Ignore errors if the file does not exist
|
|
1370
1059
|
}
|
|
1371
1060
|
}
|
|
1372
|
-
// Remove the resumption records for Matterbridge (bridge mode)
|
|
1373
1061
|
this.log.debug(`Cleaning matter storage context for ${GREEN}Matterbridge${db}...`);
|
|
1374
1062
|
unlinkSafe(path.join(this.matterbridgeDirectory, MATTER_STORAGE_NAME, 'Matterbridge', 'sessions.resumptionRecords'), this.log);
|
|
1375
1063
|
unlinkSafe(path.join(this.matterbridgeDirectory, MATTER_STORAGE_NAME, 'Matterbridge', 'root.subscriptions.subscriptions'), this.log);
|
|
1376
1064
|
for (const plugin of this.plugins.array()) {
|
|
1377
|
-
// Remove the resumption records for the plugins (childbridge mode)
|
|
1378
1065
|
this.log.debug(`Cleaning matter storage context for plugin ${plg}${plugin.name}${db}...`);
|
|
1379
1066
|
unlinkSafe(path.join(this.matterbridgeDirectory, MATTER_STORAGE_NAME, plugin.name, 'sessions.resumptionRecords'), this.log);
|
|
1380
1067
|
unlinkSafe(path.join(this.matterbridgeDirectory, MATTER_STORAGE_NAME, plugin.name, 'root.subscriptions.subscriptions'), this.log);
|
|
@@ -1382,38 +1069,19 @@ export class Matterbridge extends EventEmitter {
|
|
|
1382
1069
|
for (const device of this.devices.array().filter((d) => d.mode === 'server')) {
|
|
1383
1070
|
if (!device.deviceName)
|
|
1384
1071
|
continue;
|
|
1385
|
-
// Remove the resumption records for the server mode devices
|
|
1386
1072
|
this.log.debug(`Cleaning matter storage context for server node device ${dev}${device.deviceName}${db}...`);
|
|
1387
1073
|
unlinkSafe(path.join(this.matterbridgeDirectory, MATTER_STORAGE_NAME, device.deviceName.replace(/[ .]/g, ''), 'sessions.resumptionRecords'), this.log);
|
|
1388
1074
|
unlinkSafe(path.join(this.matterbridgeDirectory, MATTER_STORAGE_NAME, device.deviceName.replace(/[ .]/g, ''), 'root.subscriptions.subscriptions'), this.log);
|
|
1389
1075
|
}
|
|
1390
|
-
// Stop the frontend
|
|
1391
1076
|
await this.frontend.stop();
|
|
1392
1077
|
this.frontend.destroy();
|
|
1393
|
-
// Close PluginManager and DeviceManager
|
|
1394
1078
|
this.plugins.destroy();
|
|
1395
1079
|
this.devices.destroy();
|
|
1396
|
-
// Stop thread messaging server
|
|
1397
1080
|
this.server.close();
|
|
1398
|
-
// Close the matterbridge node storage and context
|
|
1399
1081
|
if (this.nodeStorage && this.nodeContext) {
|
|
1400
|
-
/*
|
|
1401
|
-
TODO: Implement serialization of registered devices
|
|
1402
|
-
this.log.info('Saving registered devices...');
|
|
1403
|
-
const serializedRegisteredDevices: SerializedMatterbridgeEndpoint[] = [];
|
|
1404
|
-
this.devices.forEach(async (device) => {
|
|
1405
|
-
const serializedMatterbridgeDevice = MatterbridgeEndpoint.serialize(device);
|
|
1406
|
-
this.log.info(`- ${serializedMatterbridgeDevice.deviceName}${rs}\n`, serializedMatterbridgeDevice);
|
|
1407
|
-
if (serializedMatterbridgeDevice) serializedRegisteredDevices.push(serializedMatterbridgeDevice);
|
|
1408
|
-
});
|
|
1409
|
-
await this.nodeContext.set<SerializedMatterbridgeEndpoint[]>('devices', serializedRegisteredDevices);
|
|
1410
|
-
this.log.info(`Saved registered devices (${serializedRegisteredDevices?.length})`);
|
|
1411
|
-
*/
|
|
1412
|
-
// Clear nodeContext and nodeStorage (they just need 1000ms to write the data to disk)
|
|
1413
1082
|
this.log.debug(`Closing node storage context for ${plg}Matterbridge${db}...`);
|
|
1414
1083
|
await this.nodeContext.close();
|
|
1415
1084
|
this.nodeContext = undefined;
|
|
1416
|
-
// Clear nodeContext for each plugin (they just need 1000ms to write the data to disk)
|
|
1417
1085
|
for (const plugin of this.plugins) {
|
|
1418
1086
|
if (plugin.nodeContext) {
|
|
1419
1087
|
this.log.debug(`Closing node storage context for plugin ${plg}${plugin.name}${db}...`);
|
|
@@ -1430,10 +1098,8 @@ export class Matterbridge extends EventEmitter {
|
|
|
1430
1098
|
}
|
|
1431
1099
|
this.plugins.clear();
|
|
1432
1100
|
this.devices.clear();
|
|
1433
|
-
// Factory reset
|
|
1434
1101
|
if (message === 'shutting down with factory reset...') {
|
|
1435
1102
|
try {
|
|
1436
|
-
// Delete matter storage directory with its subdirectories and backup
|
|
1437
1103
|
const dir = path.join(this.matterbridgeDirectory, MATTER_STORAGE_NAME);
|
|
1438
1104
|
this.log.info(`Removing matter storage directory: ${dir}`);
|
|
1439
1105
|
await fs.promises.rm(dir, { recursive: true });
|
|
@@ -1442,13 +1108,11 @@ export class Matterbridge extends EventEmitter {
|
|
|
1442
1108
|
await fs.promises.rm(backup, { recursive: true });
|
|
1443
1109
|
}
|
|
1444
1110
|
catch (error) {
|
|
1445
|
-
// istanbul ignore next if
|
|
1446
1111
|
if (error instanceof Error && error.code !== 'ENOENT') {
|
|
1447
1112
|
this.log.error(`Error removing matter storage directory: ${error}`);
|
|
1448
1113
|
}
|
|
1449
1114
|
}
|
|
1450
1115
|
try {
|
|
1451
|
-
// Delete matterbridge storage directory with its subdirectories and backup
|
|
1452
1116
|
const dir = path.join(this.matterbridgeDirectory, NODE_STORAGE_DIR);
|
|
1453
1117
|
this.log.info(`Removing matterbridge storage directory: ${dir}`);
|
|
1454
1118
|
await fs.promises.rm(dir, { recursive: true });
|
|
@@ -1457,20 +1121,18 @@ export class Matterbridge extends EventEmitter {
|
|
|
1457
1121
|
await fs.promises.rm(backup, { recursive: true });
|
|
1458
1122
|
}
|
|
1459
1123
|
catch (error) {
|
|
1460
|
-
// istanbul ignore next if
|
|
1461
1124
|
if (error instanceof Error && error.code !== 'ENOENT') {
|
|
1462
1125
|
this.log.error(`Error removing matterbridge storage directory: ${error}`);
|
|
1463
1126
|
}
|
|
1464
1127
|
}
|
|
1465
1128
|
this.log.info('Factory reset done! Remove all paired fabrics from the controllers.');
|
|
1466
1129
|
}
|
|
1467
|
-
// Deregisters the process handlers
|
|
1468
1130
|
this.deregisterProcessHandlers();
|
|
1469
1131
|
if (restart) {
|
|
1470
1132
|
if (message === 'updating...') {
|
|
1471
1133
|
this.log.info('Cleanup completed. Updating...');
|
|
1472
1134
|
Matterbridge.instance = undefined;
|
|
1473
|
-
this.emit('update');
|
|
1135
|
+
this.emit('update');
|
|
1474
1136
|
}
|
|
1475
1137
|
else if (message === 'restarting...') {
|
|
1476
1138
|
this.log.info('Cleanup completed. Restarting...');
|
|
@@ -1499,14 +1161,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
1499
1161
|
this.log.debug('Cleanup already started...');
|
|
1500
1162
|
}
|
|
1501
1163
|
}
|
|
1502
|
-
/**
|
|
1503
|
-
* Starts the Matterbridge in bridge mode.
|
|
1504
|
-
*
|
|
1505
|
-
* @private
|
|
1506
|
-
* @returns {Promise<void>} A promise that resolves when the Matterbridge is started.
|
|
1507
|
-
*/
|
|
1508
1164
|
async startBridge() {
|
|
1509
|
-
// Plugins are configured by a timer when matter server is started and plugin.configured is set to true
|
|
1510
1165
|
if (!this.matterStorageManager)
|
|
1511
1166
|
throw new Error('No storage manager initialized');
|
|
1512
1167
|
if (!this.matterbridgeContext)
|
|
@@ -1520,7 +1175,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1520
1175
|
this.frontend.wssSendSnackbarMessage(`The bridge is starting...`, 0, 'info');
|
|
1521
1176
|
let failCount = 0;
|
|
1522
1177
|
this.startMatterInterval = setInterval(async () => {
|
|
1523
|
-
// istanbul ignore if cause is just a logging statement
|
|
1524
1178
|
if (failCount && failCount % 10 === 0) {
|
|
1525
1179
|
this.frontend.wssSendSnackbarMessage(`The bridge is still starting...`, 10, 'info');
|
|
1526
1180
|
this.frontend.wssSendRefreshRequired('plugins');
|
|
@@ -1554,16 +1208,13 @@ export class Matterbridge extends EventEmitter {
|
|
|
1554
1208
|
clearInterval(this.startMatterInterval);
|
|
1555
1209
|
this.startMatterInterval = undefined;
|
|
1556
1210
|
this.log.debug('Cleared startMatterInterval interval in bridge mode');
|
|
1557
|
-
|
|
1558
|
-
this.startServerNode(this.serverNode); // We don't await this, because the server node is started in the background
|
|
1559
|
-
// Start the Matter server node of single devices in mode 'server'
|
|
1211
|
+
this.startServerNode(this.serverNode);
|
|
1560
1212
|
for (const device of this.devices.array()) {
|
|
1561
1213
|
if (device.mode === 'server' && device.serverNode) {
|
|
1562
1214
|
this.log.debug(`Starting server node for device ${dev}${device.deviceName}${db} in server mode...`);
|
|
1563
|
-
this.startServerNode(device.serverNode);
|
|
1215
|
+
this.startServerNode(device.serverNode);
|
|
1564
1216
|
}
|
|
1565
1217
|
}
|
|
1566
|
-
// Configure the plugins
|
|
1567
1218
|
this.configureTimeout = setTimeout(async () => {
|
|
1568
1219
|
for (const plugin of this.plugins.array()) {
|
|
1569
1220
|
if (!plugin.enabled || !plugin.loaded || !plugin.started || plugin.error)
|
|
@@ -1581,13 +1232,11 @@ export class Matterbridge extends EventEmitter {
|
|
|
1581
1232
|
}
|
|
1582
1233
|
this.frontend.wssSendRefreshRequired('plugins');
|
|
1583
1234
|
}, 30 * 1000).unref();
|
|
1584
|
-
// Setting reachability to true
|
|
1585
1235
|
this.reachabilityTimeout = setTimeout(() => {
|
|
1586
1236
|
this.log.info(`Setting reachability to true for ${plg}Matterbridge${db}`);
|
|
1587
1237
|
if (this.aggregatorNode)
|
|
1588
1238
|
this.setAggregatorReachability(this.aggregatorNode, true);
|
|
1589
1239
|
}, 60 * 1000).unref();
|
|
1590
|
-
// Logger.get('LogServerNode').info(this.serverNode);
|
|
1591
1240
|
this.emit('bridge_started');
|
|
1592
1241
|
this.log.notice('Matterbridge bridge started successfully');
|
|
1593
1242
|
this.frontend.wssSendRefreshRequired('settings');
|
|
@@ -1595,33 +1244,22 @@ export class Matterbridge extends EventEmitter {
|
|
|
1595
1244
|
this.frontend.wssSendCloseSnackbarMessage(`The bridge is starting...`);
|
|
1596
1245
|
}, Number(process.env['MATTERBRIDGE_START_MATTER_INTERVAL_MS']) || this.startMatterIntervalMs);
|
|
1597
1246
|
}
|
|
1598
|
-
/**
|
|
1599
|
-
* Starts the Matterbridge in childbridge mode.
|
|
1600
|
-
*
|
|
1601
|
-
* @param {number} [delay] - The delay before starting the childbridge. Default is 1000 milliseconds.
|
|
1602
|
-
*
|
|
1603
|
-
* @returns {Promise<void>} A promise that resolves when the Matterbridge is started.
|
|
1604
|
-
*/
|
|
1605
1247
|
async startChildbridge(delay = 1000) {
|
|
1606
1248
|
if (!this.matterStorageManager)
|
|
1607
1249
|
throw new Error('No storage manager initialized');
|
|
1608
|
-
const { wait } = await import('
|
|
1609
|
-
// Load with await all plugins but don't start them. We get the platform.type to pre-create server nodes for DynamicPlatform plugins
|
|
1250
|
+
const { wait } = await import('@matterbridge/utils');
|
|
1610
1251
|
this.log.debug('Loading all plugins in childbridge mode...');
|
|
1611
1252
|
await this.startPlugins(true, false);
|
|
1612
|
-
// Create server nodes for DynamicPlatform plugins and start all plugins in the background
|
|
1613
1253
|
this.log.debug('Creating server nodes for DynamicPlatform plugins and starting all plugins in childbridge mode...');
|
|
1614
1254
|
for (const plugin of this.plugins.array().filter((p) => p.enabled && !p.error)) {
|
|
1615
1255
|
if (plugin.type === 'DynamicPlatform')
|
|
1616
1256
|
await this.createDynamicPlugin(plugin);
|
|
1617
|
-
this.plugins.start(plugin, 'Matterbridge is starting');
|
|
1257
|
+
this.plugins.start(plugin, 'Matterbridge is starting');
|
|
1618
1258
|
}
|
|
1619
|
-
// Start the Matterbridge in childbridge mode when all plugins are loaded and started
|
|
1620
1259
|
this.log.debug('Starting start matter interval in childbridge mode...');
|
|
1621
1260
|
this.frontend.wssSendSnackbarMessage(`The bridge is starting...`, 0, 'info');
|
|
1622
1261
|
let failCount = 0;
|
|
1623
1262
|
this.startMatterInterval = setInterval(async () => {
|
|
1624
|
-
// istanbul ignore if cause is just a logging statement
|
|
1625
1263
|
if (failCount && failCount % 10 === 0) {
|
|
1626
1264
|
this.frontend.wssSendSnackbarMessage(`The bridge is still starting...`, 10, 'info');
|
|
1627
1265
|
this.frontend.wssSendRefreshRequired('plugins');
|
|
@@ -1659,9 +1297,8 @@ export class Matterbridge extends EventEmitter {
|
|
|
1659
1297
|
clearInterval(this.startMatterInterval);
|
|
1660
1298
|
this.startMatterInterval = undefined;
|
|
1661
1299
|
if (delay > 0)
|
|
1662
|
-
await wait(Number(process.env['MATTERBRIDGE_PAUSE_MATTER_INTERVAL_MS']) || delay);
|
|
1300
|
+
await wait(Number(process.env['MATTERBRIDGE_PAUSE_MATTER_INTERVAL_MS']) || delay);
|
|
1663
1301
|
this.log.debug('Cleared startMatterInterval interval in childbridge mode');
|
|
1664
|
-
// Configure the plugins
|
|
1665
1302
|
this.configureTimeout = setTimeout(async () => {
|
|
1666
1303
|
for (const plugin of this.plugins.array()) {
|
|
1667
1304
|
if (!plugin.enabled || !plugin.loaded || !plugin.started || plugin.error)
|
|
@@ -1686,7 +1323,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1686
1323
|
this.log.error(`Plugin ${plg}${plugin.name}${er} didn't register any devices to Matterbridge. Verify the plugin configuration.`);
|
|
1687
1324
|
continue;
|
|
1688
1325
|
}
|
|
1689
|
-
// istanbul ignore next if cause is just a safety check
|
|
1690
1326
|
if (!plugin.serverNode) {
|
|
1691
1327
|
this.log.error(`Server node not found for plugin ${plg}${plugin.name}${er}`);
|
|
1692
1328
|
continue;
|
|
@@ -1699,23 +1335,19 @@ export class Matterbridge extends EventEmitter {
|
|
|
1699
1335
|
this.log.error(`Node storage context not found for plugin ${plg}${plugin.name}${er}`);
|
|
1700
1336
|
continue;
|
|
1701
1337
|
}
|
|
1702
|
-
|
|
1703
|
-
this.startServerNode(plugin.serverNode); // We don't await this, because the server node is started in the background
|
|
1704
|
-
// Setting reachability to true
|
|
1338
|
+
this.startServerNode(plugin.serverNode);
|
|
1705
1339
|
plugin.reachabilityTimeout = setTimeout(() => {
|
|
1706
1340
|
this.log.info(`Setting reachability to true for ${plg}${plugin.name}${nf}`);
|
|
1707
1341
|
if (plugin.type === 'DynamicPlatform' && plugin.aggregatorNode)
|
|
1708
1342
|
this.setAggregatorReachability(plugin.aggregatorNode, true);
|
|
1709
1343
|
}, 60 * 1000).unref();
|
|
1710
1344
|
}
|
|
1711
|
-
// Start the Matter server node of single devices in mode 'server'
|
|
1712
1345
|
for (const device of this.devices.array()) {
|
|
1713
1346
|
if (device.mode === 'server' && device.serverNode) {
|
|
1714
1347
|
this.log.debug(`Starting server node for device ${dev}${device.deviceName}${db} in server mode...`);
|
|
1715
|
-
this.startServerNode(device.serverNode);
|
|
1348
|
+
this.startServerNode(device.serverNode);
|
|
1716
1349
|
}
|
|
1717
1350
|
}
|
|
1718
|
-
// Logger.get('LogServerNode').info(this.serverNode);
|
|
1719
1351
|
this.emit('childbridge_started');
|
|
1720
1352
|
this.log.notice('Matterbridge childbridge started successfully');
|
|
1721
1353
|
this.frontend.wssSendRefreshRequired('settings');
|
|
@@ -1723,229 +1355,9 @@ export class Matterbridge extends EventEmitter {
|
|
|
1723
1355
|
this.frontend.wssSendCloseSnackbarMessage(`The bridge is starting...`);
|
|
1724
1356
|
}, Number(process.env['MATTERBRIDGE_START_MATTER_INTERVAL_MS']) || this.startMatterIntervalMs);
|
|
1725
1357
|
}
|
|
1726
|
-
/**
|
|
1727
|
-
* Starts the Matterbridge controller.
|
|
1728
|
-
*
|
|
1729
|
-
* @private
|
|
1730
|
-
* @returns {Promise<void>} A promise that resolves when the Matterbridge is started.
|
|
1731
|
-
*/
|
|
1732
1358
|
async startController() {
|
|
1733
|
-
/*
|
|
1734
|
-
if (!this.matterStorageManager) {
|
|
1735
|
-
this.log.error('No storage manager initialized');
|
|
1736
|
-
await this.cleanup('No storage manager initialized');
|
|
1737
|
-
return;
|
|
1738
|
-
}
|
|
1739
|
-
this.log.info('Creating context: mattercontrollerContext');
|
|
1740
|
-
this.controllerContext = this.matterStorageManager.createContext('mattercontrollerContext');
|
|
1741
|
-
if (!this.controllerContext) {
|
|
1742
|
-
this.log.error('No storage context mattercontrollerContext initialized');
|
|
1743
|
-
await this.cleanup('No storage context mattercontrollerContext initialized');
|
|
1744
|
-
return;
|
|
1745
|
-
}
|
|
1746
|
-
|
|
1747
|
-
this.log.debug('Starting matterbridge in mode', this.bridgeMode);
|
|
1748
|
-
this.matterServer = await this.createMatterServer(this.storageManager);
|
|
1749
|
-
this.log.info('Creating matter commissioning controller');
|
|
1750
|
-
this.commissioningController = new CommissioningController({
|
|
1751
|
-
autoConnect: false,
|
|
1752
|
-
});
|
|
1753
|
-
this.log.info('Adding matter commissioning controller to matter server');
|
|
1754
|
-
await this.matterServer.addCommissioningController(this.commissioningController);
|
|
1755
|
-
|
|
1756
|
-
this.log.info('Starting matter server');
|
|
1757
|
-
await this.matterServer.start();
|
|
1758
|
-
this.log.info('Matter server started');
|
|
1759
|
-
const commissioningOptions: ControllerCommissioningFlowOptions = {
|
|
1760
|
-
regulatoryLocation: GeneralCommissioning.RegulatoryLocationType.IndoorOutdoor,
|
|
1761
|
-
regulatoryCountryCode: 'XX',
|
|
1762
|
-
};
|
|
1763
|
-
const commissioningController = new CommissioningController({
|
|
1764
|
-
environment: {
|
|
1765
|
-
environment,
|
|
1766
|
-
id: uniqueId,
|
|
1767
|
-
},
|
|
1768
|
-
autoConnect: false, // Do not auto connect to the commissioned nodes
|
|
1769
|
-
adminFabricLabel,
|
|
1770
|
-
});
|
|
1771
|
-
|
|
1772
|
-
if (hasParameter('pairingcode')) {
|
|
1773
|
-
this.log.info('Pairing device with pairingcode:', getParameter('pairingcode'));
|
|
1774
|
-
const pairingCode = getParameter('pairingcode');
|
|
1775
|
-
const ip = this.controllerContext.has('ip') ? this.controllerContext.get<string>('ip') : undefined;
|
|
1776
|
-
const port = this.controllerContext.has('port') ? this.controllerContext.get<number>('port') : undefined;
|
|
1777
|
-
|
|
1778
|
-
let longDiscriminator, setupPin, shortDiscriminator;
|
|
1779
|
-
if (pairingCode !== undefined) {
|
|
1780
|
-
const pairingCodeCodec = ManualPairingCodeCodec.decode(pairingCode);
|
|
1781
|
-
shortDiscriminator = pairingCodeCodec.shortDiscriminator;
|
|
1782
|
-
longDiscriminator = undefined;
|
|
1783
|
-
setupPin = pairingCodeCodec.passcode;
|
|
1784
|
-
this.log.info(`Data extracted from pairing code: ${Logger.toJSON(pairingCodeCodec)}`);
|
|
1785
|
-
} else {
|
|
1786
|
-
longDiscriminator = await this.controllerContext.get('longDiscriminator', 3840);
|
|
1787
|
-
if (longDiscriminator > 4095) throw new Error('Discriminator value must be less than 4096');
|
|
1788
|
-
setupPin = this.controllerContext.get('pin', 20202021);
|
|
1789
|
-
}
|
|
1790
|
-
if ((shortDiscriminator === undefined && longDiscriminator === undefined) || setupPin === undefined) {
|
|
1791
|
-
throw new Error('Please specify the longDiscriminator of the device to commission with -longDiscriminator or provide a valid passcode with -passcode');
|
|
1792
|
-
}
|
|
1793
|
-
|
|
1794
|
-
const options = {
|
|
1795
|
-
commissioning: commissioningOptions,
|
|
1796
|
-
discovery: {
|
|
1797
|
-
knownAddress: ip !== undefined && port !== undefined ? { ip, port, type: 'udp' } : undefined,
|
|
1798
|
-
identifierData: longDiscriminator !== undefined ? { longDiscriminator } : shortDiscriminator !== undefined ? { shortDiscriminator } : {},
|
|
1799
|
-
},
|
|
1800
|
-
passcode: setupPin,
|
|
1801
|
-
} as NodeCommissioningOptions;
|
|
1802
|
-
this.log.info('Commissioning with options:', options);
|
|
1803
|
-
const nodeId = await this.commissioningController.commissionNode(options);
|
|
1804
|
-
this.log.info(`Commissioning successfully done with nodeId: ${nodeId}`);
|
|
1805
|
-
this.log.info('ActiveSessionInformation:', this.commissioningController.getActiveSessionInformation());
|
|
1806
|
-
} // (hasParameter('pairingcode'))
|
|
1807
|
-
|
|
1808
|
-
if (hasParameter('unpairall')) {
|
|
1809
|
-
this.log.info('***Commissioning controller unpairing all nodes...');
|
|
1810
|
-
const nodeIds = this.commissioningController.getCommissionedNodes();
|
|
1811
|
-
for (const nodeId of nodeIds) {
|
|
1812
|
-
this.log.info('***Commissioning controller unpairing node:', nodeId);
|
|
1813
|
-
await this.commissioningController.removeNode(nodeId);
|
|
1814
|
-
}
|
|
1815
|
-
return;
|
|
1816
|
-
}
|
|
1817
|
-
|
|
1818
|
-
if (hasParameter('discover')) {
|
|
1819
|
-
// const discover = await this.commissioningController.discoverCommissionableDevices({ productId: 0x8000, deviceType: 0xfff1 });
|
|
1820
|
-
// console.log(discover);
|
|
1821
|
-
}
|
|
1822
|
-
|
|
1823
|
-
if (!this.commissioningController.isCommissioned()) {
|
|
1824
|
-
this.log.info('***Commissioning controller is not commissioned: use matterbridge -controller -pairingcode [pairingcode] to commission a device');
|
|
1825
|
-
return;
|
|
1826
|
-
}
|
|
1827
|
-
|
|
1828
|
-
const nodeIds = this.commissioningController.getCommissionedNodes();
|
|
1829
|
-
this.log.info(`***Commissioning controller is commissioned ${this.commissioningController.isCommissioned()} and has ${nodeIds.length} nodes commisioned: `);
|
|
1830
|
-
for (const nodeId of nodeIds) {
|
|
1831
|
-
this.log.info(`***Connecting to commissioned node: ${nodeId}`);
|
|
1832
|
-
|
|
1833
|
-
const node = await this.commissioningController.connectNode(nodeId, {
|
|
1834
|
-
autoSubscribe: false,
|
|
1835
|
-
attributeChangedCallback: (peerNodeId, { path: { nodeId, clusterId, endpointId, attributeName }, value }) =>
|
|
1836
|
-
this.log.info(`***Commissioning controller attributeChangedCallback ${peerNodeId}: attribute ${nodeId}/${endpointId}/${clusterId}/${attributeName} changed to ${Logger.toJSON(value)}`),
|
|
1837
|
-
eventTriggeredCallback: (peerNodeId, { path: { nodeId, clusterId, endpointId, eventName }, events }) =>
|
|
1838
|
-
this.log.info(`***Commissioning controller eventTriggeredCallback ${peerNodeId}: Event ${nodeId}/${endpointId}/${clusterId}/${eventName} triggered with ${Logger.toJSON(events)}`),
|
|
1839
|
-
stateInformationCallback: (peerNodeId, info) => {
|
|
1840
|
-
switch (info) {
|
|
1841
|
-
case NodeStateInformation.Connected:
|
|
1842
|
-
this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} connected`);
|
|
1843
|
-
break;
|
|
1844
|
-
case NodeStateInformation.Disconnected:
|
|
1845
|
-
this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} disconnected`);
|
|
1846
|
-
break;
|
|
1847
|
-
case NodeStateInformation.Reconnecting:
|
|
1848
|
-
this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} reconnecting`);
|
|
1849
|
-
break;
|
|
1850
|
-
case NodeStateInformation.WaitingForDeviceDiscovery:
|
|
1851
|
-
this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} waiting for device discovery`);
|
|
1852
|
-
break;
|
|
1853
|
-
case NodeStateInformation.StructureChanged:
|
|
1854
|
-
this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} structure changed`);
|
|
1855
|
-
break;
|
|
1856
|
-
case NodeStateInformation.Decommissioned:
|
|
1857
|
-
this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} decommissioned`);
|
|
1858
|
-
break;
|
|
1859
|
-
default:
|
|
1860
|
-
this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} NodeStateInformation.${info}`);
|
|
1861
|
-
break;
|
|
1862
|
-
}
|
|
1863
|
-
},
|
|
1864
|
-
});
|
|
1865
|
-
|
|
1866
|
-
node.logStructure();
|
|
1867
|
-
|
|
1868
|
-
// Get the interaction client
|
|
1869
|
-
this.log.info('Getting the interaction client');
|
|
1870
|
-
const interactionClient = await node.getInteractionClient();
|
|
1871
|
-
let cluster;
|
|
1872
|
-
let attributes;
|
|
1873
|
-
|
|
1874
|
-
// Log BasicInformationCluster
|
|
1875
|
-
cluster = BasicInformationCluster;
|
|
1876
|
-
attributes = await interactionClient.getMultipleAttributes({
|
|
1877
|
-
attributes: [{ clusterId: cluster.id }],
|
|
1878
|
-
});
|
|
1879
|
-
if (attributes.length > 0) this.log.info(`Cluster: ${idn}${cluster.name}${rs}${nf} attributes:`);
|
|
1880
|
-
attributes.forEach((attribute) => {
|
|
1881
|
-
this.log.info(
|
|
1882
|
-
`- 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}`,
|
|
1883
|
-
);
|
|
1884
|
-
});
|
|
1885
|
-
|
|
1886
|
-
// Log PowerSourceCluster
|
|
1887
|
-
cluster = PowerSourceCluster;
|
|
1888
|
-
attributes = await interactionClient.getMultipleAttributes({
|
|
1889
|
-
attributes: [{ clusterId: cluster.id }],
|
|
1890
|
-
});
|
|
1891
|
-
if (attributes.length > 0) this.log.info(`Cluster: ${idn}${cluster.name}${rs}${nf} attributes:`);
|
|
1892
|
-
attributes.forEach((attribute) => {
|
|
1893
|
-
this.log.info(
|
|
1894
|
-
`- 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}`,
|
|
1895
|
-
);
|
|
1896
|
-
});
|
|
1897
|
-
|
|
1898
|
-
// Log ThreadNetworkDiagnostics
|
|
1899
|
-
cluster = ThreadNetworkDiagnosticsCluster;
|
|
1900
|
-
attributes = await interactionClient.getMultipleAttributes({
|
|
1901
|
-
attributes: [{ clusterId: cluster.id }],
|
|
1902
|
-
});
|
|
1903
|
-
if (attributes.length > 0) this.log.info(`Cluster: ${idn}${cluster.name}${rs}${nf} attributes:`);
|
|
1904
|
-
attributes.forEach((attribute) => {
|
|
1905
|
-
this.log.info(
|
|
1906
|
-
`- 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}`,
|
|
1907
|
-
);
|
|
1908
|
-
});
|
|
1909
|
-
|
|
1910
|
-
// Log SwitchCluster
|
|
1911
|
-
cluster = SwitchCluster;
|
|
1912
|
-
attributes = await interactionClient.getMultipleAttributes({
|
|
1913
|
-
attributes: [{ clusterId: cluster.id }],
|
|
1914
|
-
});
|
|
1915
|
-
if (attributes.length > 0) this.log.info(`Cluster: ${idn}${cluster.name}${rs}${nf} attributes:`);
|
|
1916
|
-
attributes.forEach((attribute) => {
|
|
1917
|
-
this.log.info(
|
|
1918
|
-
`- 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}`,
|
|
1919
|
-
);
|
|
1920
|
-
});
|
|
1921
|
-
|
|
1922
|
-
this.log.info('Subscribing to all attributes and events');
|
|
1923
|
-
await node.subscribeAllAttributesAndEvents({
|
|
1924
|
-
ignoreInitialTriggers: false,
|
|
1925
|
-
attributeChangedCallback: ({ path: { nodeId, clusterId, endpointId, attributeName }, version, value }) =>
|
|
1926
|
-
this.log.info(
|
|
1927
|
-
`***${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}`,
|
|
1928
|
-
),
|
|
1929
|
-
eventTriggeredCallback: ({ path: { nodeId, clusterId, endpointId, eventName }, events }) => {
|
|
1930
|
-
this.log.info(
|
|
1931
|
-
`***${db}Commissioning controller eventTriggeredCallback: event ${BLUE}${nodeId}${db}/${or}${endpointId}${db}/${hk}${getClusterNameById(clusterId)}${db}/${zb}${eventName}${db} triggered with ${debugStringify(events ?? { none: true })}`,
|
|
1932
|
-
);
|
|
1933
|
-
},
|
|
1934
|
-
});
|
|
1935
|
-
this.log.info('Subscribed to all attributes and events');
|
|
1936
|
-
}
|
|
1937
|
-
*/
|
|
1938
1359
|
}
|
|
1939
|
-
/** */
|
|
1940
|
-
/** Matter.js methods */
|
|
1941
|
-
/** */
|
|
1942
|
-
/**
|
|
1943
|
-
* Starts the matter storage with name Matterbridge, create the matterbridge context and performs a backup.
|
|
1944
|
-
*
|
|
1945
|
-
* @returns {Promise<void>} - A promise that resolves when the storage is started.
|
|
1946
|
-
*/
|
|
1947
1360
|
async startMatterStorage() {
|
|
1948
|
-
// Setup Matter storage
|
|
1949
1361
|
this.log.info(`Starting matter node storage...`);
|
|
1950
1362
|
this.matterStorageService = this.environment.get(StorageService);
|
|
1951
1363
|
this.log.info(`Matter node storage service created: ${this.matterStorageService.location}`);
|
|
@@ -1953,17 +1365,8 @@ export class Matterbridge extends EventEmitter {
|
|
|
1953
1365
|
this.log.info('Matter node storage manager "Matterbridge" created');
|
|
1954
1366
|
this.matterbridgeContext = await this.createServerNodeContext('Matterbridge', 'Matterbridge', this.aggregatorDeviceType, this.aggregatorVendorId, this.aggregatorVendorName, this.aggregatorProductId, this.aggregatorProductName, this.aggregatorSerialNumber, this.aggregatorUniqueId);
|
|
1955
1367
|
this.log.info('Matter node storage started');
|
|
1956
|
-
// Backup matter storage since it is created/opened correctly
|
|
1957
1368
|
await this.backupMatterStorage(path.join(this.matterbridgeDirectory, MATTER_STORAGE_NAME), path.join(this.matterbridgeDirectory, MATTER_STORAGE_NAME + '.backup'));
|
|
1958
1369
|
}
|
|
1959
|
-
/**
|
|
1960
|
-
* Makes a backup copy of the specified matter storage directory.
|
|
1961
|
-
*
|
|
1962
|
-
* @param {string} storageName - The name of the storage directory to be backed up.
|
|
1963
|
-
* @param {string} backupName - The name of the backup directory to be created.
|
|
1964
|
-
* @private
|
|
1965
|
-
* @returns {Promise<void>} A promise that resolves when the has been done.
|
|
1966
|
-
*/
|
|
1967
1370
|
async backupMatterStorage(storageName, backupName) {
|
|
1968
1371
|
this.log.info('Creating matter node storage backup...');
|
|
1969
1372
|
try {
|
|
@@ -1974,11 +1377,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1974
1377
|
this.log.error(`Error creating matter node storage backup from ${storageName} to ${backupName}:`, error);
|
|
1975
1378
|
}
|
|
1976
1379
|
}
|
|
1977
|
-
/**
|
|
1978
|
-
* Stops the matter storage.
|
|
1979
|
-
*
|
|
1980
|
-
* @returns {Promise<void>} A promise that resolves when the storage is stopped.
|
|
1981
|
-
*/
|
|
1982
1380
|
async stopMatterStorage() {
|
|
1983
1381
|
this.log.info('Closing matter node storage...');
|
|
1984
1382
|
await this.matterStorageManager?.close();
|
|
@@ -1987,20 +1385,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1987
1385
|
this.matterbridgeContext = undefined;
|
|
1988
1386
|
this.log.info('Matter node storage closed');
|
|
1989
1387
|
}
|
|
1990
|
-
/**
|
|
1991
|
-
* Creates a server node storage context.
|
|
1992
|
-
*
|
|
1993
|
-
* @param {string} storeId - The storeId.
|
|
1994
|
-
* @param {string} deviceName - The name of the device.
|
|
1995
|
-
* @param {DeviceTypeId} deviceType - The device type of the device.
|
|
1996
|
-
* @param {number} vendorId - The vendor ID.
|
|
1997
|
-
* @param {string} vendorName - The vendor name.
|
|
1998
|
-
* @param {number} productId - The product ID.
|
|
1999
|
-
* @param {string} productName - The product name.
|
|
2000
|
-
* @param {string} [serialNumber] - The serial number of the device (optional).
|
|
2001
|
-
* @param {string} [uniqueId] - The unique ID of the device (optional).
|
|
2002
|
-
* @returns {Promise<StorageContext>} The storage context for the commissioning server.
|
|
2003
|
-
*/
|
|
2004
1388
|
async createServerNodeContext(storeId, deviceName, deviceType, vendorId, vendorName, productId, productName, serialNumber, uniqueId) {
|
|
2005
1389
|
const { randomBytes } = await import('node:crypto');
|
|
2006
1390
|
if (!this.matterStorageService)
|
|
@@ -2036,57 +1420,49 @@ export class Matterbridge extends EventEmitter {
|
|
|
2036
1420
|
this.log.debug(`- nodeLabel: ${await storageContext.get('nodeLabel')}`);
|
|
2037
1421
|
this.log.debug(`- serialNumber: ${await storageContext.get('serialNumber')}`);
|
|
2038
1422
|
this.log.debug(`- uniqueId: ${await storageContext.get('uniqueId')}`);
|
|
2039
|
-
this.log.debug(`- softwareVersion: ${await storageContext.get('softwareVersion')}
|
|
2040
|
-
this.log.debug(`-
|
|
1423
|
+
this.log.debug(`- softwareVersion: ${await storageContext.get('softwareVersion')}`);
|
|
1424
|
+
this.log.debug(`- softwareVersionString: ${await storageContext.get('softwareVersionString')}`);
|
|
1425
|
+
this.log.debug(`- hardwareVersion: ${await storageContext.get('hardwareVersion')}`);
|
|
1426
|
+
this.log.debug(`- hardwareVersionString: ${await storageContext.get('hardwareVersionString')}`);
|
|
2041
1427
|
return storageContext;
|
|
2042
1428
|
}
|
|
2043
|
-
/**
|
|
2044
|
-
* Creates a server node.
|
|
2045
|
-
*
|
|
2046
|
-
* @param {StorageContext} storageContext - The storage context for the server node.
|
|
2047
|
-
* @param {number} [port] - The port number for the server node. Defaults to 5540.
|
|
2048
|
-
* @param {number} [passcode] - The passcode for the server node. Defaults to 20242025.
|
|
2049
|
-
* @param {number} [discriminator] - The discriminator for the server node. Defaults to 3850.
|
|
2050
|
-
* @returns {Promise<ServerNode<ServerNode.RootEndpoint>>} A promise that resolves to the created server node.
|
|
2051
|
-
*/
|
|
2052
1429
|
async createServerNode(storageContext, port = 5540, passcode = 20242025, discriminator = 3850) {
|
|
2053
1430
|
const storeId = await storageContext.get('storeId');
|
|
2054
1431
|
this.log.notice(`Creating server node for ${storeId} on port ${port} with passcode ${passcode} and discriminator ${discriminator}...`);
|
|
1432
|
+
this.log.debug(`- storeId: ${await storageContext.get('storeId')}`);
|
|
2055
1433
|
this.log.debug(`- deviceName: ${await storageContext.get('deviceName')}`);
|
|
2056
1434
|
this.log.debug(`- deviceType: ${await storageContext.get('deviceType')}(0x${(await storageContext.get('deviceType'))?.toString(16).padStart(4, '0')})`);
|
|
1435
|
+
this.log.debug(`- vendorId: ${await storageContext.get('vendorId')}`);
|
|
1436
|
+
this.log.debug(`- vendorName: ${await storageContext.get('vendorName')}`);
|
|
1437
|
+
this.log.debug(`- productId: ${await storageContext.get('productId')}`);
|
|
1438
|
+
this.log.debug(`- productName: ${await storageContext.get('productName')}`);
|
|
1439
|
+
this.log.debug(`- productLabel: ${await storageContext.get('productLabel')}`);
|
|
1440
|
+
this.log.debug(`- nodeLabel: ${await storageContext.get('nodeLabel')}`);
|
|
2057
1441
|
this.log.debug(`- serialNumber: ${await storageContext.get('serialNumber')}`);
|
|
2058
1442
|
this.log.debug(`- uniqueId: ${await storageContext.get('uniqueId')}`);
|
|
2059
|
-
this.log.debug(`- softwareVersion: ${await storageContext.get('softwareVersion')}
|
|
2060
|
-
this.log.debug(`-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
*/
|
|
1443
|
+
this.log.debug(`- softwareVersion: ${await storageContext.get('softwareVersion')}`);
|
|
1444
|
+
this.log.debug(`- softwareVersionString: ${await storageContext.get('softwareVersionString')}`);
|
|
1445
|
+
this.log.debug(`- hardwareVersion: ${await storageContext.get('hardwareVersion')}`);
|
|
1446
|
+
this.log.debug(`- hardwareVersionString: ${await storageContext.get('hardwareVersionString')}`);
|
|
2064
1447
|
const serverNode = await ServerNode.create({
|
|
2065
|
-
// Required: Give the Node a unique ID which is used to store the state of this node
|
|
2066
1448
|
id: storeId,
|
|
2067
|
-
// Environment to run the server node in
|
|
2068
1449
|
environment: this.environment,
|
|
2069
|
-
// Provide Network relevant configuration like the port
|
|
2070
1450
|
network: {
|
|
2071
1451
|
listeningAddressIpv4: this.ipv4Address,
|
|
2072
1452
|
listeningAddressIpv6: this.ipv6Address,
|
|
2073
1453
|
port,
|
|
2074
1454
|
},
|
|
2075
|
-
// Provide the certificate for the device
|
|
2076
1455
|
operationalCredentials: {
|
|
2077
1456
|
certification: this.certification,
|
|
2078
1457
|
},
|
|
2079
|
-
// Provide Commissioning relevant settings
|
|
2080
1458
|
commissioning: {
|
|
2081
1459
|
passcode,
|
|
2082
1460
|
discriminator,
|
|
2083
1461
|
},
|
|
2084
|
-
// Provide Node announcement settings
|
|
2085
1462
|
productDescription: {
|
|
2086
1463
|
name: await storageContext.get('deviceName'),
|
|
2087
1464
|
deviceType: DeviceTypeId(await storageContext.get('deviceType')),
|
|
2088
1465
|
},
|
|
2089
|
-
// Provide defaults for the BasicInformation cluster on the Root endpoint
|
|
2090
1466
|
basicInformation: {
|
|
2091
1467
|
nodeLabel: await storageContext.get('nodeLabel'),
|
|
2092
1468
|
vendorId: VendorId(await storageContext.get('vendorId')),
|
|
@@ -2103,23 +1479,17 @@ export class Matterbridge extends EventEmitter {
|
|
|
2103
1479
|
reachable: true,
|
|
2104
1480
|
},
|
|
2105
1481
|
});
|
|
2106
|
-
/**
|
|
2107
|
-
* This event is triggered when the device is initially commissioned successfully.
|
|
2108
|
-
* This means: It is added to the first fabric.
|
|
2109
|
-
*/
|
|
2110
1482
|
serverNode.lifecycle.commissioned.on(() => {
|
|
2111
1483
|
this.log.notice(`Server node for ${storeId} was initially commissioned successfully!`);
|
|
2112
1484
|
this.advertisingNodes.delete(storeId);
|
|
2113
1485
|
this.frontend.wssSendRefreshRequired('matter', { matter: { ...this.getServerNodeData(serverNode) } });
|
|
2114
1486
|
});
|
|
2115
|
-
/** This event is triggered when all fabrics are removed from the device, usually it also does a factory reset then. */
|
|
2116
1487
|
serverNode.lifecycle.decommissioned.on(() => {
|
|
2117
1488
|
this.log.notice(`Server node for ${storeId} was fully decommissioned successfully!`);
|
|
2118
1489
|
this.advertisingNodes.delete(storeId);
|
|
2119
1490
|
this.frontend.wssSendRefreshRequired('matter', { matter: { ...this.getServerNodeData(serverNode) } });
|
|
2120
1491
|
this.frontend.wssSendSnackbarMessage(`${storeId} is offline`, 5, 'warning');
|
|
2121
1492
|
});
|
|
2122
|
-
/** This event is triggered when the device went online. This means that it is discoverable in the network. */
|
|
2123
1493
|
serverNode.lifecycle.online.on(async () => {
|
|
2124
1494
|
this.log.notice(`Server node for ${storeId} is online`);
|
|
2125
1495
|
if (!serverNode.lifecycle.isCommissioned) {
|
|
@@ -2130,16 +1500,13 @@ export class Matterbridge extends EventEmitter {
|
|
|
2130
1500
|
this.log.notice(`Manual pairing code: ${manualPairingCode}`);
|
|
2131
1501
|
}
|
|
2132
1502
|
else {
|
|
2133
|
-
// istanbul ignore next
|
|
2134
1503
|
this.log.notice(`Server node for ${storeId} is already commissioned. Waiting for controllers to connect ...`);
|
|
2135
|
-
// istanbul ignore next
|
|
2136
1504
|
this.advertisingNodes.delete(storeId);
|
|
2137
1505
|
}
|
|
2138
1506
|
this.frontend.wssSendRefreshRequired('matter', { matter: { ...this.getServerNodeData(serverNode) } });
|
|
2139
1507
|
this.frontend.wssSendSnackbarMessage(`${storeId} is online`, 5, 'success');
|
|
2140
1508
|
this.emit('online', storeId);
|
|
2141
1509
|
});
|
|
2142
|
-
/** This event is triggered when the device went offline. it is not longer discoverable or connectable in the network. */
|
|
2143
1510
|
serverNode.lifecycle.offline.on(() => {
|
|
2144
1511
|
this.log.notice(`Server node for ${storeId} is offline`);
|
|
2145
1512
|
this.advertisingNodes.delete(storeId);
|
|
@@ -2147,15 +1514,11 @@ export class Matterbridge extends EventEmitter {
|
|
|
2147
1514
|
this.frontend.wssSendSnackbarMessage(`${storeId} is offline`, 5, 'warning');
|
|
2148
1515
|
this.emit('offline', storeId);
|
|
2149
1516
|
});
|
|
2150
|
-
/**
|
|
2151
|
-
* This event is triggered when a fabric is added, removed or updated on the device. Use this if more granular
|
|
2152
|
-
* information is needed.
|
|
2153
|
-
*/
|
|
2154
1517
|
serverNode.events.commissioning.fabricsChanged.on((fabricIndex, fabricAction) => {
|
|
2155
1518
|
let action = '';
|
|
2156
1519
|
switch (fabricAction) {
|
|
2157
1520
|
case 'added':
|
|
2158
|
-
this.advertisingNodes.delete(storeId);
|
|
1521
|
+
this.advertisingNodes.delete(storeId);
|
|
2159
1522
|
action = 'added';
|
|
2160
1523
|
break;
|
|
2161
1524
|
case 'deleted':
|
|
@@ -2168,22 +1531,14 @@ export class Matterbridge extends EventEmitter {
|
|
|
2168
1531
|
this.log.notice(`Commissioned fabric index ${fabricIndex} ${action} on server node for ${storeId}: ${debugStringify(serverNode.state.commissioning.fabrics[fabricIndex])}`);
|
|
2169
1532
|
this.frontend.wssSendRefreshRequired('matter', { matter: { ...this.getServerNodeData(serverNode) } });
|
|
2170
1533
|
});
|
|
2171
|
-
/**
|
|
2172
|
-
* This event is triggered when an operative new session was opened by a Controller.
|
|
2173
|
-
* It is not triggered for the initial commissioning process, just afterwards for real connections.
|
|
2174
|
-
*/
|
|
2175
1534
|
serverNode.events.sessions.opened.on((session) => {
|
|
2176
1535
|
this.log.notice(`Session opened on server node for ${storeId}: ${debugStringify(session)}`);
|
|
2177
1536
|
this.frontend.wssSendRefreshRequired('matter', { matter: { ...this.getServerNodeData(serverNode) } });
|
|
2178
1537
|
});
|
|
2179
|
-
/**
|
|
2180
|
-
* This event is triggered when an operative session is closed by a Controller or because the Device goes offline.
|
|
2181
|
-
*/
|
|
2182
1538
|
serverNode.events.sessions.closed.on((session) => {
|
|
2183
1539
|
this.log.notice(`Session closed on server node for ${storeId}: ${debugStringify(session)}`);
|
|
2184
1540
|
this.frontend.wssSendRefreshRequired('matter', { matter: { ...this.getServerNodeData(serverNode) } });
|
|
2185
1541
|
});
|
|
2186
|
-
/** This event is triggered when a subscription gets added or removed on an operative session. */
|
|
2187
1542
|
serverNode.events.sessions.subscriptionsChanged.on((session) => {
|
|
2188
1543
|
this.log.notice(`Session subscriptions changed on server node for ${storeId}: ${debugStringify(session)}`);
|
|
2189
1544
|
this.frontend.wssSendRefreshRequired('matter', { matter: { ...this.getServerNodeData(serverNode) } });
|
|
@@ -2191,12 +1546,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2191
1546
|
this.log.info(`Created server node for ${storeId}`);
|
|
2192
1547
|
return serverNode;
|
|
2193
1548
|
}
|
|
2194
|
-
/**
|
|
2195
|
-
* Gets the matter sanitized data of the specified server node.
|
|
2196
|
-
*
|
|
2197
|
-
* @param {ServerNode} [serverNode] - The server node to start.
|
|
2198
|
-
* @returns {ApiMatter} The sanitized data of the server node.
|
|
2199
|
-
*/
|
|
2200
1549
|
getServerNodeData(serverNode) {
|
|
2201
1550
|
const advertiseTime = this.advertisingNodes.get(serverNode.id) || 0;
|
|
2202
1551
|
return {
|
|
@@ -2213,27 +1562,14 @@ export class Matterbridge extends EventEmitter {
|
|
|
2213
1562
|
serialNumber: serverNode.state.basicInformation.serialNumber,
|
|
2214
1563
|
};
|
|
2215
1564
|
}
|
|
2216
|
-
/**
|
|
2217
|
-
* Starts the specified server node.
|
|
2218
|
-
*
|
|
2219
|
-
* @param {ServerNode} [matterServerNode] - The server node to start.
|
|
2220
|
-
* @returns {Promise<void>} A promise that resolves when the server node has started.
|
|
2221
|
-
*/
|
|
2222
1565
|
async startServerNode(matterServerNode) {
|
|
2223
1566
|
if (!matterServerNode)
|
|
2224
1567
|
return;
|
|
2225
1568
|
this.log.notice(`Starting ${matterServerNode.id} server node`);
|
|
2226
1569
|
await matterServerNode.start();
|
|
2227
1570
|
}
|
|
2228
|
-
/**
|
|
2229
|
-
* Stops the specified server node.
|
|
2230
|
-
*
|
|
2231
|
-
* @param {ServerNode} matterServerNode - The server node to stop.
|
|
2232
|
-
* @param {number} [timeout] - The timeout in milliseconds for stopping the server node. Defaults to 10 seconds.
|
|
2233
|
-
* @returns {Promise<void>} A promise that resolves when the server node has stopped.
|
|
2234
|
-
*/
|
|
2235
1571
|
async stopServerNode(matterServerNode, timeout = 10000) {
|
|
2236
|
-
const { withTimeout } = await import('
|
|
1572
|
+
const { withTimeout } = await import('@matterbridge/utils');
|
|
2237
1573
|
if (!matterServerNode)
|
|
2238
1574
|
return;
|
|
2239
1575
|
this.log.notice(`Closing ${matterServerNode.id} server node`);
|
|
@@ -2245,25 +1581,12 @@ export class Matterbridge extends EventEmitter {
|
|
|
2245
1581
|
this.log.error(`Failed to close ${matterServerNode.id} server node: ${error instanceof Error ? error.message : error}`);
|
|
2246
1582
|
}
|
|
2247
1583
|
}
|
|
2248
|
-
/**
|
|
2249
|
-
* Creates an aggregator node with the specified storage context.
|
|
2250
|
-
*
|
|
2251
|
-
* @param {StorageContext} storageContext - The storage context for the aggregator node.
|
|
2252
|
-
* @returns {Promise<Endpoint<AggregatorEndpoint>>} A promise that resolves to the created aggregator node.
|
|
2253
|
-
*/
|
|
2254
1584
|
async createAggregatorNode(storageContext) {
|
|
2255
1585
|
this.log.notice(`Creating ${await storageContext.get('storeId')} aggregator...`);
|
|
2256
1586
|
const aggregatorNode = new Endpoint(AggregatorEndpoint, { id: `${await storageContext.get('storeId')}` });
|
|
2257
1587
|
this.log.info(`Created ${await storageContext.get('storeId')} aggregator`);
|
|
2258
1588
|
return aggregatorNode;
|
|
2259
1589
|
}
|
|
2260
|
-
/**
|
|
2261
|
-
* Creates and configures the server node for an accessory plugin for a given device.
|
|
2262
|
-
*
|
|
2263
|
-
* @param {Plugin} plugin - The plugin to configure.
|
|
2264
|
-
* @param {MatterbridgeEndpoint} device - The device to associate with the plugin.
|
|
2265
|
-
* @returns {Promise<void>} A promise that resolves when the server node for the accessory plugin is created and configured.
|
|
2266
|
-
*/
|
|
2267
1590
|
async createAccessoryPlugin(plugin, device) {
|
|
2268
1591
|
if (!plugin.locked && device.deviceType && device.deviceName && device.vendorId && device.productId && device.vendorName && device.productName) {
|
|
2269
1592
|
plugin.locked = true;
|
|
@@ -2275,12 +1598,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2275
1598
|
await plugin.serverNode.add(device);
|
|
2276
1599
|
}
|
|
2277
1600
|
}
|
|
2278
|
-
/**
|
|
2279
|
-
* Creates and configures the server node and the aggregator node for a dynamic plugin.
|
|
2280
|
-
*
|
|
2281
|
-
* @param {Plugin} plugin - The plugin to configure.
|
|
2282
|
-
* @returns {Promise<void>} A promise that resolves when the server node and the aggregator node for the dynamic plugin is created and configured.
|
|
2283
|
-
*/
|
|
2284
1601
|
async createDynamicPlugin(plugin) {
|
|
2285
1602
|
if (!plugin.locked) {
|
|
2286
1603
|
plugin.locked = true;
|
|
@@ -2293,13 +1610,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2293
1610
|
await plugin.serverNode.add(plugin.aggregatorNode);
|
|
2294
1611
|
}
|
|
2295
1612
|
}
|
|
2296
|
-
/**
|
|
2297
|
-
* Creates and configures the server node for a single not bridged device.
|
|
2298
|
-
*
|
|
2299
|
-
* @param {Plugin} plugin - The plugin to configure.
|
|
2300
|
-
* @param {MatterbridgeEndpoint} device - The device to associate with the plugin.
|
|
2301
|
-
* @returns {Promise<void>} A promise that resolves when the server node for the accessory plugin is created and configured.
|
|
2302
|
-
*/
|
|
2303
1613
|
async createDeviceServerNode(plugin, device) {
|
|
2304
1614
|
if (device.mode === 'server' && !device.serverNode && device.deviceType && device.deviceName && device.vendorId && device.vendorName && device.productId && device.productName) {
|
|
2305
1615
|
this.log.debug(`Creating device ${plg}${plugin.name}${db}:${dev}${device.deviceName}${db} server node...`);
|
|
@@ -2310,16 +1620,8 @@ export class Matterbridge extends EventEmitter {
|
|
|
2310
1620
|
this.log.debug(`Added ${plg}${plugin.name}${db}:${dev}${device.deviceName}${db} to server node`);
|
|
2311
1621
|
}
|
|
2312
1622
|
}
|
|
2313
|
-
/**
|
|
2314
|
-
* Adds a MatterbridgeEndpoint to the specified plugin.
|
|
2315
|
-
*
|
|
2316
|
-
* @param {string} pluginName - The name of the plugin.
|
|
2317
|
-
* @param {MatterbridgeEndpoint} device - The device to add as a bridged endpoint.
|
|
2318
|
-
* @returns {Promise<void>} A promise that resolves when the bridged endpoint has been added.
|
|
2319
|
-
*/
|
|
2320
1623
|
async addBridgedEndpoint(pluginName, device) {
|
|
2321
|
-
const { waiter } = await import('
|
|
2322
|
-
// Check if the plugin is registered
|
|
1624
|
+
const { waiter } = await import('@matterbridge/utils');
|
|
2323
1625
|
const plugin = this.plugins.get(pluginName);
|
|
2324
1626
|
if (!plugin) {
|
|
2325
1627
|
this.log.error(`Error adding bridged endpoint ${dev}${device.deviceName}${er} (${zb}${device.id}${er}) plugin ${plg}${pluginName}${er} not found`);
|
|
@@ -2339,7 +1641,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2339
1641
|
}
|
|
2340
1642
|
else if (this.bridgeMode === 'bridge') {
|
|
2341
1643
|
if (device.mode === 'matter') {
|
|
2342
|
-
// Register and add the device to the matterbridge server node
|
|
2343
1644
|
this.log.debug(`Adding matter endpoint ${plg}${pluginName}${db}:${dev}${device.deviceName}${db} to Matterbridge server node...`);
|
|
2344
1645
|
if (!this.serverNode) {
|
|
2345
1646
|
this.log.error('Server node not found for Matterbridge');
|
|
@@ -2356,7 +1657,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2356
1657
|
}
|
|
2357
1658
|
}
|
|
2358
1659
|
else {
|
|
2359
|
-
// Register and add the device to the matterbridge aggregator node
|
|
2360
1660
|
this.log.debug(`Adding bridged endpoint ${plg}${pluginName}${db}:${dev}${device.deviceName}${db} to Matterbridge aggregator node`);
|
|
2361
1661
|
if (!this.aggregatorNode) {
|
|
2362
1662
|
this.log.error('Aggregator node not found for Matterbridge');
|
|
@@ -2374,7 +1674,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2374
1674
|
}
|
|
2375
1675
|
}
|
|
2376
1676
|
else if (this.bridgeMode === 'childbridge') {
|
|
2377
|
-
// Register and add the device to the plugin server node
|
|
2378
1677
|
if (plugin.type === 'AccessoryPlatform') {
|
|
2379
1678
|
try {
|
|
2380
1679
|
this.log.debug(`Creating endpoint ${dev}${device.deviceName}${db} for AccessoryPlatform plugin ${plg}${plugin.name}${db} server node`);
|
|
@@ -2398,12 +1697,10 @@ export class Matterbridge extends EventEmitter {
|
|
|
2398
1697
|
return;
|
|
2399
1698
|
}
|
|
2400
1699
|
}
|
|
2401
|
-
// Register and add the device to the plugin aggregator node
|
|
2402
1700
|
if (plugin.type === 'DynamicPlatform') {
|
|
2403
1701
|
try {
|
|
2404
1702
|
this.log.debug(`Adding bridged endpoint ${dev}${device.deviceName}${db} for DynamicPlatform plugin ${plg}${plugin.name}${db} aggregator node`);
|
|
2405
1703
|
await this.createDynamicPlugin(plugin);
|
|
2406
|
-
// Fast plugins can add another device before the server node is ready, so we wait for the server node to be ready
|
|
2407
1704
|
await waiter(`createDynamicPlugin(${plugin.name})`, () => plugin.serverNode?.hasParts === true);
|
|
2408
1705
|
if (!plugin.aggregatorNode) {
|
|
2409
1706
|
this.log.error(`Aggregator node not found for plugin ${plg}${plugin.name}${er}`);
|
|
@@ -2424,22 +1721,12 @@ export class Matterbridge extends EventEmitter {
|
|
|
2424
1721
|
}
|
|
2425
1722
|
if (plugin.registeredDevices !== undefined)
|
|
2426
1723
|
plugin.registeredDevices++;
|
|
2427
|
-
// Add the device to the DeviceManager
|
|
2428
1724
|
this.devices.set(device);
|
|
2429
|
-
// Subscribe to the attributes changed event
|
|
2430
1725
|
await this.subscribeAttributeChanged(plugin, device);
|
|
2431
1726
|
this.log.info(`Added and registered bridged endpoint (${plugin.registeredDevices}) ${dev}${device.deviceName}${nf} (${dev}${device.id}${nf}) for plugin ${plg}${pluginName}${nf}`);
|
|
2432
1727
|
}
|
|
2433
|
-
/**
|
|
2434
|
-
* Removes a MatterbridgeEndpoint from the specified plugin.
|
|
2435
|
-
*
|
|
2436
|
-
* @param {string} pluginName - The name of the plugin.
|
|
2437
|
-
* @param {MatterbridgeEndpoint} device - The device to remove as a bridged endpoint.
|
|
2438
|
-
* @returns {Promise<void>} A promise that resolves when the bridged endpoint has been removed.
|
|
2439
|
-
*/
|
|
2440
1728
|
async removeBridgedEndpoint(pluginName, device) {
|
|
2441
1729
|
this.log.debug(`Removing bridged endpoint ${plg}${pluginName}${db}:${dev}${device.deviceName}${db} (${zb}${device.name}${db}) for plugin ${plg}${pluginName}${db}`);
|
|
2442
|
-
// Check if the plugin is registered
|
|
2443
1730
|
const plugin = this.plugins.get(pluginName);
|
|
2444
1731
|
if (!plugin) {
|
|
2445
1732
|
this.log.error(`Error removing bridged endpoint ${dev}${device.deviceName}${er} (${zb}${device.name}${er}) for plugin ${plg}${pluginName}${er}: plugin not found`);
|
|
@@ -2449,7 +1736,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2449
1736
|
this.log.info(`Removed mode server bridged endpoint(${plugin.registeredDevices}) ${dev}${device.deviceName}${nf} (${zb}${device.name}${nf}) for plugin ${plg}${pluginName}${nf}`);
|
|
2450
1737
|
}
|
|
2451
1738
|
else if (this.bridgeMode === 'bridge') {
|
|
2452
|
-
// Unregister and remove the device from the matterbridge aggregator node
|
|
2453
1739
|
if (!this.aggregatorNode) {
|
|
2454
1740
|
this.log.error(`Error removing bridged endpoint ${dev}${device.deviceName}${er} (${zb}${device.name}${er}) for plugin ${plg}${pluginName}${er}: aggregator node not found`);
|
|
2455
1741
|
return;
|
|
@@ -2461,10 +1747,8 @@ export class Matterbridge extends EventEmitter {
|
|
|
2461
1747
|
}
|
|
2462
1748
|
else if (this.bridgeMode === 'childbridge') {
|
|
2463
1749
|
if (plugin.type === 'AccessoryPlatform') {
|
|
2464
|
-
// Nothing to do here since the server node has no aggregator node but only the device itself
|
|
2465
1750
|
}
|
|
2466
1751
|
else if (plugin.type === 'DynamicPlatform') {
|
|
2467
|
-
// Unregister and remove the device from the plugin aggregator node
|
|
2468
1752
|
if (!plugin.aggregatorNode) {
|
|
2469
1753
|
this.log.error(`Error removing bridged endpoint ${dev}${device.deviceName}${er} (${zb}${device.name}${er}) for plugin ${plg}${pluginName}${er}: aggregator node not found`);
|
|
2470
1754
|
return;
|
|
@@ -2475,23 +1759,10 @@ export class Matterbridge extends EventEmitter {
|
|
|
2475
1759
|
if (plugin.registeredDevices !== undefined)
|
|
2476
1760
|
plugin.registeredDevices--;
|
|
2477
1761
|
}
|
|
2478
|
-
// Remove the device from the DeviceManager
|
|
2479
1762
|
this.devices.remove(device);
|
|
2480
1763
|
}
|
|
2481
|
-
/**
|
|
2482
|
-
* Removes all bridged endpoints from the specified plugin.
|
|
2483
|
-
*
|
|
2484
|
-
* @param {string} pluginName - The name of the plugin.
|
|
2485
|
-
* @param {number} [delay] - The delay in milliseconds between removing each bridged endpoint (default: 0).
|
|
2486
|
-
* @returns {Promise<void>} A promise that resolves when all bridged endpoints have been removed.
|
|
2487
|
-
*
|
|
2488
|
-
* @remarks
|
|
2489
|
-
* This method iterates through all devices in the DeviceManager and removes each bridged endpoint associated with the specified plugin.
|
|
2490
|
-
* It also applies a delay between each removal if specified.
|
|
2491
|
-
* The delay is useful to allow the controllers to receive a single subscription for each device removed.
|
|
2492
|
-
*/
|
|
2493
1764
|
async removeAllBridgedEndpoints(pluginName, delay = 0) {
|
|
2494
|
-
const { wait } = await import('
|
|
1765
|
+
const { wait } = await import('@matterbridge/utils');
|
|
2495
1766
|
this.log.debug(`Removing all bridged endpoints for plugin ${plg}${pluginName}${db}${delay > 0 ? ` with delay ${delay} ms` : ''}`);
|
|
2496
1767
|
for (const device of this.devices.array().filter((device) => device.plugin === pluginName)) {
|
|
2497
1768
|
await this.removeBridgedEndpoint(pluginName, device);
|
|
@@ -2501,28 +1772,8 @@ export class Matterbridge extends EventEmitter {
|
|
|
2501
1772
|
if (delay > 0)
|
|
2502
1773
|
await wait(2000);
|
|
2503
1774
|
}
|
|
2504
|
-
/**
|
|
2505
|
-
* Registers a virtual device.
|
|
2506
|
-
* Virtual devices are only supported in bridge mode and childbridge mode with a DynamicPlatform.
|
|
2507
|
-
*
|
|
2508
|
-
* The virtual device is created as an instance of `Endpoint` with the provided device type.
|
|
2509
|
-
* When the virtual device is turned on, the provided callback function is executed.
|
|
2510
|
-
* The onOff state of the virtual device always reverts to false when the device is turned on.
|
|
2511
|
-
*
|
|
2512
|
-
* @param { string } pluginName - The name of the plugin to register the virtual device under.
|
|
2513
|
-
* @param { string } name - The name of the virtual device.
|
|
2514
|
-
* @param { 'light' | 'outlet' | 'switch' | 'mounted_switch' } type - The type of the virtual device.
|
|
2515
|
-
* @param { () => Promise<void> } callback - The callback to call when the virtual device is turned on.
|
|
2516
|
-
*
|
|
2517
|
-
* @returns {Promise<boolean>} A promise that resolves to true if the virtual device was successfully registered, false otherwise.
|
|
2518
|
-
*
|
|
2519
|
-
* @remarks
|
|
2520
|
-
* The virtual devices don't show up in the device list of the frontend.
|
|
2521
|
-
* Type 'switch' is not supported by Alexa and 'mounted_switch' is not supported by Apple Home.
|
|
2522
|
-
*/
|
|
2523
1775
|
async addVirtualEndpoint(pluginName, name, type, callback) {
|
|
2524
1776
|
this.log.debug(`Adding virtual endpoint ${plg}${pluginName}${db}:${dev}${name}${db}...`);
|
|
2525
|
-
// Check if the plugin is registered
|
|
2526
1777
|
const plugin = this.plugins.get(pluginName);
|
|
2527
1778
|
if (!plugin) {
|
|
2528
1779
|
this.log.error(`Error adding virtual endpoint ${dev}${name}${er} for plugin ${plg}${pluginName}${er}: plugin not found`);
|
|
@@ -2549,24 +1800,13 @@ export class Matterbridge extends EventEmitter {
|
|
|
2549
1800
|
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.`);
|
|
2550
1801
|
return false;
|
|
2551
1802
|
}
|
|
2552
|
-
/**
|
|
2553
|
-
* Subscribes to the attribute change event for the given device and plugin.
|
|
2554
|
-
* Specifically, it listens for changes in the 'reachable' attribute of the
|
|
2555
|
-
* BridgedDeviceBasicInformationServer cluster server of the bridged device or BasicInformationServer cluster server of server node.
|
|
2556
|
-
*
|
|
2557
|
-
* @param {Plugin} plugin - The plugin associated with the device.
|
|
2558
|
-
* @param {MatterbridgeEndpoint} device - The device to subscribe to attribute changes for.
|
|
2559
|
-
* @returns {Promise<void>} A promise that resolves when the subscription is set up.
|
|
2560
|
-
*/
|
|
2561
1803
|
async subscribeAttributeChanged(plugin, device) {
|
|
2562
1804
|
if (!plugin || !device || !device.plugin || !device.serialNumber || !device.uniqueId || !device.maybeNumber)
|
|
2563
1805
|
return;
|
|
2564
1806
|
this.log.info(`Subscribing attributes for endpoint ${dev}${device.deviceName}${nf} (${dev}${device.id}${nf}) plugin ${plg}${plugin.name}${nf}`);
|
|
2565
|
-
// Subscribe to the reachable$Changed event of the BasicInformationServer cluster server of the server node in childbridge mode
|
|
2566
1807
|
if (this.bridgeMode === 'childbridge' && plugin.type === 'AccessoryPlatform' && plugin.serverNode) {
|
|
2567
1808
|
plugin.serverNode.eventsOf(BasicInformationServer).reachable$Changed?.on((reachable) => {
|
|
2568
1809
|
this.log.info(`Accessory endpoint ${dev}${device.deviceName}${nf} (${dev}${device.id}${nf}) is ${reachable ? 'reachable' : 'unreachable'}`);
|
|
2569
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
2570
1810
|
this.frontend.wssSendAttributeChangedMessage(device.plugin, device.serialNumber, device.uniqueId, device.number, device.id, 'BasicInformation', 'reachable', reachable);
|
|
2571
1811
|
});
|
|
2572
1812
|
}
|
|
@@ -2616,7 +1856,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2616
1856
|
this.log.debug(`Subscribing to endpoint ${or}${device.id}${db}:${or}${device.number}${db} attribute ${dev}${sub.cluster}${db}.${dev}${sub.attribute}${db} changes...`);
|
|
2617
1857
|
await device.subscribeAttribute(sub.cluster, sub.attribute, (value) => {
|
|
2618
1858
|
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}`);
|
|
2619
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
2620
1859
|
this.frontend.wssSendAttributeChangedMessage(device.plugin, device.serialNumber, device.uniqueId, device.number, device.id, sub.cluster, sub.attribute, value);
|
|
2621
1860
|
});
|
|
2622
1861
|
}
|
|
@@ -2625,19 +1864,12 @@ export class Matterbridge extends EventEmitter {
|
|
|
2625
1864
|
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...`);
|
|
2626
1865
|
await child.subscribeAttribute(sub.cluster, sub.attribute, (value) => {
|
|
2627
1866
|
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}`);
|
|
2628
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
2629
1867
|
this.frontend.wssSendAttributeChangedMessage(device.plugin, device.serialNumber, device.uniqueId, child.number, child.id, sub.cluster, sub.attribute, value);
|
|
2630
1868
|
});
|
|
2631
1869
|
}
|
|
2632
1870
|
}
|
|
2633
1871
|
}
|
|
2634
1872
|
}
|
|
2635
|
-
/**
|
|
2636
|
-
* Sanitizes the fabric information by converting bigint properties to strings because `res.json` doesn't support bigint.
|
|
2637
|
-
*
|
|
2638
|
-
* @param {ExposedFabricInformation[]} fabricInfo - The array of exposed fabric information objects.
|
|
2639
|
-
* @returns {SanitizedExposedFabricInformation[]} An array of sanitized exposed fabric information objects.
|
|
2640
|
-
*/
|
|
2641
1873
|
sanitizeFabricInformations(fabricInfo) {
|
|
2642
1874
|
return fabricInfo.map((info) => {
|
|
2643
1875
|
return {
|
|
@@ -2651,12 +1883,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2651
1883
|
};
|
|
2652
1884
|
});
|
|
2653
1885
|
}
|
|
2654
|
-
/**
|
|
2655
|
-
* Sanitizes the session information by converting bigint properties to strings because `res.json` doesn't support bigint.
|
|
2656
|
-
*
|
|
2657
|
-
* @param {SessionsBehavior.Session[]} sessions - The array of session information objects.
|
|
2658
|
-
* @returns {SanitizedSession[]} An array of sanitized session information objects.
|
|
2659
|
-
*/
|
|
2660
1886
|
sanitizeSessionInformation(sessions) {
|
|
2661
1887
|
return sessions
|
|
2662
1888
|
.filter((session) => session.isPeerActive)
|
|
@@ -2683,21 +1909,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
2683
1909
|
};
|
|
2684
1910
|
});
|
|
2685
1911
|
}
|
|
2686
|
-
/**
|
|
2687
|
-
* Sets the reachability of the specified aggregator node bridged devices and trigger.
|
|
2688
|
-
*
|
|
2689
|
-
* @param {Endpoint<AggregatorEndpoint>} aggregatorNode - The aggregator node to set the reachability for.
|
|
2690
|
-
* @param {boolean} reachable - A boolean indicating the reachability status to set.
|
|
2691
|
-
*/
|
|
2692
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
2693
1912
|
async setAggregatorReachability(aggregatorNode, reachable) {
|
|
2694
|
-
/*
|
|
2695
|
-
for (const child of aggregatorNode.parts) {
|
|
2696
|
-
this.log.debug(`Setting reachability of ${(child as unknown as MatterbridgeEndpoint)?.deviceName} to ${reachable}`);
|
|
2697
|
-
await child.setStateOf(BridgedDeviceBasicInformationServer, { reachable });
|
|
2698
|
-
child.act((agent) => child.eventsOf(BridgedDeviceBasicInformationServer).reachableChanged.emit({ reachableNewValue: true }, agent.context));
|
|
2699
|
-
}
|
|
2700
|
-
*/
|
|
2701
1913
|
}
|
|
2702
1914
|
getVendorIdName = (vendorId) => {
|
|
2703
1915
|
if (!vendorId)
|
|
@@ -2737,11 +1949,10 @@ export class Matterbridge extends EventEmitter {
|
|
|
2737
1949
|
case 0x1488:
|
|
2738
1950
|
vendorName = '(ShortcutLabsFlic)';
|
|
2739
1951
|
break;
|
|
2740
|
-
case 65521:
|
|
1952
|
+
case 65521:
|
|
2741
1953
|
vendorName = '(MatterTest)';
|
|
2742
1954
|
break;
|
|
2743
1955
|
}
|
|
2744
1956
|
return vendorName;
|
|
2745
1957
|
};
|
|
2746
1958
|
}
|
|
2747
|
-
//# sourceMappingURL=matterbridge.js.map
|