matterbridge 3.5.2 → 3.5.3-dev-20260202-e19e9b6
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 +30 -1
- package/README-DOCKER.md +2 -2
- package/README.md +14 -9
- package/dist/broadcastServer.d.ts +0 -115
- package/dist/broadcastServer.js +0 -117
- package/dist/broadcastServerTypes.d.ts +0 -43
- package/dist/broadcastServerTypes.js +0 -24
- package/dist/checkUpdates.d.ts +0 -75
- package/dist/checkUpdates.js +1 -91
- package/dist/cli.d.ts +0 -24
- package/dist/cli.js +1 -97
- package/dist/cliEmitter.d.ts +0 -36
- package/dist/cliEmitter.js +0 -37
- package/dist/cliHistory.d.ts +0 -42
- package/dist/cliHistory.js +0 -38
- 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 +1 -114
- package/dist/devices/airConditioner.d.ts +0 -75
- package/dist/devices/airConditioner.js +0 -57
- package/dist/devices/basicVideoPlayer.d.ts +0 -58
- package/dist/devices/basicVideoPlayer.js +1 -56
- package/dist/devices/batteryStorage.d.ts +0 -43
- package/dist/devices/batteryStorage.js +1 -48
- package/dist/devices/castingVideoPlayer.d.ts +0 -63
- package/dist/devices/castingVideoPlayer.js +2 -65
- 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/dgram/export.d.ts +0 -1
- package/dist/dgram/export.js +0 -1
- package/dist/frontend.d.ts +0 -187
- package/dist/frontend.js +37 -498
- package/dist/frontendTypes.d.ts +0 -57
- package/dist/frontendTypes.js +0 -45
- package/dist/helpers.d.ts +0 -43
- package/dist/helpers.js +0 -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 +15 -371
- 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 +8 -359
- package/dist/matterbridge.d.ts +0 -373
- package/dist/matterbridge.js +46 -854
- package/dist/matterbridgeAccessoryPlatform.d.ts +0 -42
- package/dist/matterbridgeAccessoryPlatform.js +0 -50
- package/dist/matterbridgeBehaviors.d.ts +0 -24
- package/dist/matterbridgeBehaviors.js +5 -65
- package/dist/matterbridgeDeviceTypes.d.ts +0 -649
- package/dist/matterbridgeDeviceTypes.js +6 -673
- package/dist/matterbridgeDynamicPlatform.d.ts +0 -42
- package/dist/matterbridgeDynamicPlatform.js +0 -50
- package/dist/matterbridgeEndpoint.d.ts +0 -1369
- package/dist/matterbridgeEndpoint.js +54 -1507
- package/dist/matterbridgeEndpointHelpers.d.ts +0 -425
- package/dist/matterbridgeEndpointHelpers.js +20 -482
- package/dist/matterbridgeEndpointTypes.d.ts +0 -70
- package/dist/matterbridgeEndpointTypes.js +0 -25
- package/dist/matterbridgePlatform.d.ts +0 -434
- package/dist/matterbridgePlatform.js +1 -472
- package/dist/matterbridgePlatformTypes.d.ts +0 -29
- package/dist/matterbridgePlatformTypes.js +0 -24
- package/dist/matterbridgeTypes.d.ts +0 -46
- package/dist/matterbridgeTypes.js +0 -26
- package/dist/mb_coap.d.ts +0 -23
- package/dist/mb_coap.js +3 -41
- package/dist/mb_health.d.ts +0 -67
- package/dist/mb_health.js +0 -70
- package/dist/mb_mdns.d.ts +0 -23
- package/dist/mb_mdns.js +36 -94
- package/dist/pluginManager.d.ts +0 -305
- package/dist/pluginManager.js +5 -342
- package/dist/shelly.d.ts +0 -157
- package/dist/shelly.js +7 -178
- package/dist/spawn.d.ts +0 -32
- package/dist/spawn.js +1 -71
- package/dist/storage/export.d.ts +0 -1
- package/dist/storage/export.js +0 -1
- package/dist/utils/export.d.ts +0 -1
- package/dist/utils/export.js +0 -1
- package/dist/worker.d.ts +0 -61
- package/dist/worker.js +4 -65
- package/dist/workerCheckUpdates.d.ts +0 -24
- package/dist/workerCheckUpdates.js +5 -36
- package/dist/workerGlobalPrefix.d.ts +0 -24
- package/dist/workerGlobalPrefix.js +5 -36
- package/dist/workerTypes.d.ts +0 -25
- package/dist/workerTypes.js +0 -24
- package/frontend/build/assets/index.js +4 -4
- package/frontend/build/assets/vendor_emotion.js +1 -1
- package/frontend/build/assets/vendor_lodash.js +1 -1
- package/frontend/build/assets/vendor_mdi.js +1 -1
- package/frontend/build/assets/vendor_mui.js +22 -22
- package/frontend/build/assets/vendor_node_modules.js +20 -20
- package/frontend/build/assets/vendor_notistack.js +2 -2
- package/frontend/build/assets/vendor_qrcode.js +1 -1
- package/frontend/build/assets/vendor_rjsf.js +8 -8
- package/frontend/build/index.html +1 -1
- package/frontend/package.json +48 -47
- package/npm-shrinkwrap.json +77 -47
- package/package.json +7 -7
- 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/checkUpdates.d.ts.map +0 -1
- package/dist/checkUpdates.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/basicVideoPlayer.d.ts.map +0 -1
- package/dist/devices/basicVideoPlayer.js.map +0 -1
- package/dist/devices/batteryStorage.d.ts.map +0 -1
- package/dist/devices/batteryStorage.js.map +0 -1
- package/dist/devices/castingVideoPlayer.d.ts.map +0 -1
- package/dist/devices/castingVideoPlayer.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/export.d.ts.map +0 -1
- package/dist/dgram/export.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/matterbridgePlatformTypes.d.ts.map +0 -1
- package/dist/matterbridgePlatformTypes.js.map +0 -1
- package/dist/matterbridgeTypes.d.ts.map +0 -1
- package/dist/matterbridgeTypes.js.map +0 -1
- package/dist/mb_coap.d.ts.map +0 -1
- package/dist/mb_coap.js.map +0 -1
- package/dist/mb_health.d.ts.map +0 -1
- package/dist/mb_health.js.map +0 -1
- package/dist/mb_mdns.d.ts.map +0 -1
- package/dist/mb_mdns.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/spawn.d.ts.map +0 -1
- package/dist/spawn.js.map +0 -1
- package/dist/storage/export.d.ts.map +0 -1
- package/dist/storage/export.js.map +0 -1
- package/dist/utils/export.d.ts.map +0 -1
- package/dist/utils/export.js.map +0 -1
- package/dist/worker.d.ts.map +0 -1
- package/dist/worker.js.map +0 -1
- package/dist/workerCheckUpdates.d.ts.map +0 -1
- package/dist/workerCheckUpdates.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/matterbridge.js
CHANGED
|
@@ -1,50 +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
|
-
// @matterbridge
|
|
46
17
|
import { copyDirectory, createDirectory, formatBytes, formatPercent, formatUptime, getIntParameter, getParameter, hasParameter, isValidNumber, isValidObject, isValidString, parseVersionString } from '@matterbridge/utils';
|
|
47
|
-
// Matterbridge
|
|
48
18
|
import { dev, MATTER_LOGGER_FILE, MATTER_STORAGE_NAME, MATTERBRIDGE_LOGGER_FILE, NODE_STORAGE_DIR, plg, typ, } from './matterbridgeTypes.js';
|
|
49
19
|
import { PluginManager } from './pluginManager.js';
|
|
50
20
|
import { DeviceManager } from './deviceManager.js';
|
|
@@ -53,27 +23,19 @@ import { bridge } from './matterbridgeDeviceTypes.js';
|
|
|
53
23
|
import { Frontend } from './frontend.js';
|
|
54
24
|
import { addVirtualDevice, addVirtualDevices } from './helpers.js';
|
|
55
25
|
import { BroadcastServer } from './broadcastServer.js';
|
|
56
|
-
/**
|
|
57
|
-
* Represents the Matterbridge application.
|
|
58
|
-
*/
|
|
59
26
|
export class Matterbridge extends EventEmitter {
|
|
60
|
-
/** Matterbridge system information */
|
|
61
27
|
systemInformation = {
|
|
62
|
-
// Network properties
|
|
63
28
|
interfaceName: '',
|
|
64
29
|
macAddress: '',
|
|
65
30
|
ipv4Address: '',
|
|
66
31
|
ipv6Address: '',
|
|
67
|
-
// Node.js properties
|
|
68
32
|
nodeVersion: '',
|
|
69
|
-
// Fixed system properties
|
|
70
33
|
hostname: '',
|
|
71
34
|
user: '',
|
|
72
35
|
osType: '',
|
|
73
36
|
osRelease: '',
|
|
74
37
|
osPlatform: '',
|
|
75
38
|
osArch: '',
|
|
76
|
-
// Cpu and memory properties
|
|
77
39
|
totalMemory: '',
|
|
78
40
|
freeMemory: '',
|
|
79
41
|
systemUptime: '',
|
|
@@ -84,66 +46,39 @@ export class Matterbridge extends EventEmitter {
|
|
|
84
46
|
heapTotal: '',
|
|
85
47
|
heapUsed: '',
|
|
86
48
|
};
|
|
87
|
-
// Matterbridge settings
|
|
88
|
-
/** 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). */
|
|
89
49
|
homeDirectory = '';
|
|
90
|
-
/** It indicates the root directory of the Matterbridge application. The root directory is the directory where Matterbridge is executed. */
|
|
91
50
|
rootDirectory = '';
|
|
92
|
-
/** It indicates where the directory .matterbridge is located. */
|
|
93
51
|
matterbridgeDirectory = '';
|
|
94
|
-
/** It indicates where the directory Matterbridge is located. */
|
|
95
52
|
matterbridgePluginDirectory = '';
|
|
96
|
-
/** It indicates where the directory .mattercert is located. */
|
|
97
53
|
matterbridgeCertDirectory = '';
|
|
98
|
-
/** It indicates the global modules directory for npm. */
|
|
99
54
|
globalModulesDirectory = '';
|
|
100
55
|
matterbridgeVersion = '';
|
|
101
56
|
matterbridgeLatestVersion = '';
|
|
102
57
|
matterbridgeDevVersion = '';
|
|
103
58
|
frontendVersion = '';
|
|
104
|
-
/** It indicates the mode of the Matterbridge instance. It can be 'bridge', 'childbridge', 'controller' or ''. */
|
|
105
59
|
bridgeMode = '';
|
|
106
|
-
/** It indicates the restart mode of the Matterbridge instance. It can be 'service', 'docker' or ''. */
|
|
107
60
|
restartMode = '';
|
|
108
|
-
/** It indicates whether virtual mode is enabled and its type. The virtual mode control the creation of "Update matterbridge" and "Restart matterbridge" endpoints. */
|
|
109
61
|
virtualMode = 'outlet';
|
|
110
|
-
/** It indicates the Matterbridge profile in use. */
|
|
111
62
|
profile = getParameter('profile');
|
|
112
|
-
|
|
113
|
-
log = new AnsiLogger({ logName: 'Matterbridge', logTimestampFormat: 4 /* TimestampFormat.TIME_MILLIS */, logLevel: hasParameter('debug') ? "debug" /* LogLevel.DEBUG */ : "info" /* LogLevel.INFO */ });
|
|
114
|
-
/** Matterbridge logger level */
|
|
63
|
+
log = new AnsiLogger({ logName: 'Matterbridge', logTimestampFormat: 4, logLevel: hasParameter('debug') ? "debug" : "info" });
|
|
115
64
|
logLevel = this.log.logLevel;
|
|
116
|
-
/** Whether to log to a file */
|
|
117
65
|
fileLogger = false;
|
|
118
|
-
|
|
119
|
-
matterLog = new AnsiLogger({ logName: 'Matter', logTimestampFormat: 4 /* TimestampFormat.TIME_MILLIS */, logLevel: "debug" /* LogLevel.DEBUG */ });
|
|
120
|
-
/** Matter logger level */
|
|
66
|
+
matterLog = new AnsiLogger({ logName: 'Matter', logTimestampFormat: 4, logLevel: "debug" });
|
|
121
67
|
matterLogLevel = this.matterLog.logLevel;
|
|
122
|
-
/** Whether to log Matter to a file */
|
|
123
68
|
matterFileLogger = false;
|
|
124
|
-
// Frontend settings
|
|
125
69
|
readOnly = hasParameter('readonly') || hasParameter('shelly');
|
|
126
70
|
shellyBoard = hasParameter('shelly');
|
|
127
71
|
shellySysUpdate = false;
|
|
128
72
|
shellyMainUpdate = false;
|
|
129
|
-
/** It indicates whether a restart is required. It can be unset in childbridge mode by restarting the plugin that triggered the restart. */
|
|
130
73
|
restartRequired = false;
|
|
131
|
-
/** It indicates whether a fixed restart is required. It cannot be unset once set. */
|
|
132
74
|
fixedRestartRequired = false;
|
|
133
|
-
/** It indicates whether an update is available. */
|
|
134
75
|
updateRequired = false;
|
|
135
|
-
// Managers
|
|
136
76
|
plugins = new PluginManager(this);
|
|
137
77
|
devices = new DeviceManager();
|
|
138
|
-
// Frontend
|
|
139
78
|
frontend = new Frontend(this);
|
|
140
|
-
/** Matterbridge node storage manager created in the directory 'storage' in matterbridgeDirectory */
|
|
141
79
|
nodeStorage;
|
|
142
|
-
/** Matterbridge node context created with name 'matterbridge' */
|
|
143
80
|
nodeContext;
|
|
144
|
-
/** The main instance of the Matterbridge class (singleton) */
|
|
145
81
|
static instance;
|
|
146
|
-
// Instance properties
|
|
147
82
|
shutdown = false;
|
|
148
83
|
failCountLimit = hasParameter('shelly') ? 600 : 120;
|
|
149
84
|
hasCleanupStarted = false;
|
|
@@ -158,32 +93,19 @@ export class Matterbridge extends EventEmitter {
|
|
|
158
93
|
sigtermHandler;
|
|
159
94
|
exceptionHandler;
|
|
160
95
|
rejectionHandler;
|
|
161
|
-
/** Matter environment default */
|
|
162
96
|
environment = Environment.default;
|
|
163
|
-
/** Matter storage service from environment default */
|
|
164
97
|
matterStorageService;
|
|
165
|
-
/** Matter storage manager created with name 'Matterbridge' */
|
|
166
98
|
matterStorageManager;
|
|
167
|
-
/** Matter matterbridge storage context created in the storage manager with name 'persist' */
|
|
168
99
|
matterbridgeContext;
|
|
169
100
|
controllerContext;
|
|
170
|
-
/** Matter mdns interface e.g. 'eth0' or 'wlan0' or 'Wi-Fi' */
|
|
171
101
|
mdnsInterface;
|
|
172
|
-
/** Matter listeningAddressIpv4 address */
|
|
173
102
|
ipv4Address;
|
|
174
|
-
/** Matter listeningAddressIpv6 address */
|
|
175
103
|
ipv6Address;
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
/** Matter commissioning discriminator */
|
|
181
|
-
discriminator; // first server node discriminator
|
|
182
|
-
/** Matter device certification */
|
|
183
|
-
certification; // device certification
|
|
184
|
-
/** Matter server node in bridge mode */
|
|
104
|
+
port;
|
|
105
|
+
passcode;
|
|
106
|
+
discriminator;
|
|
107
|
+
certification;
|
|
185
108
|
serverNode;
|
|
186
|
-
/** Matter aggregator node in bridge mode */
|
|
187
109
|
aggregatorNode;
|
|
188
110
|
aggregatorVendorId = VendorId(getIntParameter('vendorId') ?? 0xfff1);
|
|
189
111
|
aggregatorVendorName = getParameter('vendorName') ?? 'Matterbridge';
|
|
@@ -192,27 +114,18 @@ export class Matterbridge extends EventEmitter {
|
|
|
192
114
|
aggregatorDeviceType = DeviceTypeId(getIntParameter('deviceType') ?? bridge.code);
|
|
193
115
|
aggregatorSerialNumber = getParameter('serialNumber');
|
|
194
116
|
aggregatorUniqueId = getParameter('uniqueId');
|
|
195
|
-
/** Advertising nodes map: time advertising started keyed by storeId */
|
|
196
117
|
advertisingNodes = new Map();
|
|
197
|
-
/** Broadcast server */
|
|
198
118
|
server;
|
|
199
119
|
verbose = hasParameter('verbose');
|
|
200
|
-
/** We load asyncronously so is private */
|
|
201
120
|
constructor() {
|
|
202
121
|
super();
|
|
203
122
|
this.log.logNameColor = '\x1b[38;5;115m';
|
|
204
123
|
this.server = new BroadcastServer('matterbridge', this.log);
|
|
205
124
|
this.server.on('broadcast_message', this.msgHandler.bind(this));
|
|
206
125
|
}
|
|
207
|
-
/** Close the broadcast server */
|
|
208
126
|
destroy() {
|
|
209
127
|
this.server.close();
|
|
210
128
|
}
|
|
211
|
-
/**
|
|
212
|
-
* Get a platform matterbridge object
|
|
213
|
-
*
|
|
214
|
-
* @returns {PlatformMatterbridge} The platform matterbridge object.
|
|
215
|
-
*/
|
|
216
129
|
getPlatformMatterbridge() {
|
|
217
130
|
return {
|
|
218
131
|
systemInformation: { ...this.systemInformation },
|
|
@@ -235,11 +148,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
235
148
|
aggregatorProductName: this.aggregatorProductName,
|
|
236
149
|
};
|
|
237
150
|
}
|
|
238
|
-
/**
|
|
239
|
-
* Get a shared matterbridge object
|
|
240
|
-
*
|
|
241
|
-
* @returns {SharedMatterbridge} The shared matterbridge object.
|
|
242
|
-
*/
|
|
243
151
|
getSharedMatterbridge() {
|
|
244
152
|
return {
|
|
245
153
|
systemInformation: { ...this.systemInformation },
|
|
@@ -318,19 +226,8 @@ export class Matterbridge extends EventEmitter {
|
|
|
318
226
|
}
|
|
319
227
|
}
|
|
320
228
|
}
|
|
321
|
-
//* ************************************************************************************************************************************ */
|
|
322
|
-
// loadInstance() and cleanup() methods */
|
|
323
|
-
//* ************************************************************************************************************************************ */
|
|
324
|
-
/**
|
|
325
|
-
* Loads an instance of the Matterbridge class.
|
|
326
|
-
* If an instance already exists, return that instance.
|
|
327
|
-
*
|
|
328
|
-
* @param {boolean} initialize - Whether to initialize the Matterbridge instance after loading. Defaults to false.
|
|
329
|
-
* @returns {Matterbridge} A promise that resolves to the Matterbridge instance.
|
|
330
|
-
*/
|
|
331
229
|
static async loadInstance(initialize = false) {
|
|
332
230
|
if (!Matterbridge.instance) {
|
|
333
|
-
// eslint-disable-next-line no-console
|
|
334
231
|
if (hasParameter('debug'))
|
|
335
232
|
console.log(GREEN + 'Creating a new instance of Matterbridge.', initialize ? 'Initializing...' : 'Not initializing...', rs);
|
|
336
233
|
Matterbridge.instance = new Matterbridge();
|
|
@@ -339,84 +236,56 @@ export class Matterbridge extends EventEmitter {
|
|
|
339
236
|
}
|
|
340
237
|
return Matterbridge.instance;
|
|
341
238
|
}
|
|
342
|
-
/**
|
|
343
|
-
* Initializes the Matterbridge application.
|
|
344
|
-
*
|
|
345
|
-
* @remarks
|
|
346
|
-
* This method performs the necessary setup and initialization steps for the Matterbridge application.
|
|
347
|
-
* It displays the help information if the 'help' parameter is provided, sets up the logger, checks the
|
|
348
|
-
* node version, registers signal handlers, initializes storage, and parses the command line.
|
|
349
|
-
*
|
|
350
|
-
* @returns {Promise<void>} A Promise that resolves when the initialization is complete.
|
|
351
|
-
*/
|
|
352
239
|
async initialize() {
|
|
353
|
-
// for (let i = 1; i <= 255; i++) console.log(`\x1b[38;5;${i}mColor: ${i}`);
|
|
354
|
-
// Emit the initialize_started event
|
|
355
240
|
this.emit('initialize_started');
|
|
356
|
-
// Set the restart mode
|
|
357
241
|
if (hasParameter('service'))
|
|
358
242
|
this.restartMode = 'service';
|
|
359
243
|
if (hasParameter('docker'))
|
|
360
244
|
this.restartMode = 'docker';
|
|
361
|
-
// Set the matterbridge home directory
|
|
362
245
|
this.homeDirectory = getParameter('homedir') ?? os.homedir();
|
|
363
246
|
await createDirectory(this.homeDirectory, 'Matterbridge Home Directory', this.log);
|
|
364
|
-
// Set the matterbridge directory
|
|
365
247
|
this.matterbridgeDirectory = this.profile ? path.join(this.homeDirectory, '.matterbridge', 'profiles', this.profile) : path.join(this.homeDirectory, '.matterbridge');
|
|
366
248
|
await createDirectory(this.matterbridgeDirectory, 'Matterbridge Directory', this.log);
|
|
367
249
|
await createDirectory(path.join(this.matterbridgeDirectory, 'certs'), 'Matterbridge Frontend Certificate Directory', this.log);
|
|
368
250
|
await createDirectory(path.join(this.matterbridgeDirectory, 'uploads'), 'Matterbridge Frontend Uploads Directory', this.log);
|
|
369
|
-
// Set the matterbridge plugin directory
|
|
370
251
|
this.matterbridgePluginDirectory = this.profile ? path.join(this.homeDirectory, 'Matterbridge', 'profiles', this.profile) : path.join(this.homeDirectory, 'Matterbridge');
|
|
371
252
|
await createDirectory(this.matterbridgePluginDirectory, 'Matterbridge Plugin Directory', this.log);
|
|
372
|
-
// Set the matterbridge cert directory
|
|
373
253
|
this.matterbridgeCertDirectory = this.profile ? path.join(this.homeDirectory, '.mattercert', 'profiles', this.profile) : path.join(this.homeDirectory, '.mattercert');
|
|
374
254
|
await createDirectory(this.matterbridgeCertDirectory, 'Matterbridge Matter Certificate Directory', this.log);
|
|
375
|
-
// Set the matterbridge root directory
|
|
376
255
|
const { fileURLToPath } = await import('node:url');
|
|
377
256
|
const currentFileDirectory = path.dirname(fileURLToPath(import.meta.url));
|
|
378
|
-
this.rootDirectory = path.resolve(currentFileDirectory, '../');
|
|
379
|
-
// Setup the matter environment with default values
|
|
257
|
+
this.rootDirectory = path.resolve(currentFileDirectory, '../');
|
|
380
258
|
this.environment.vars.set('log.level', MatterLogLevel.INFO);
|
|
381
259
|
this.environment.vars.set('log.format', MatterLogFormat.ANSI);
|
|
382
260
|
this.environment.vars.set('path.root', path.join(this.matterbridgeDirectory, MATTER_STORAGE_NAME));
|
|
383
261
|
this.environment.vars.set('runtime.signals', false);
|
|
384
262
|
this.environment.vars.set('runtime.exitcode', false);
|
|
385
|
-
// Register process handlers
|
|
386
263
|
this.registerProcessHandlers();
|
|
387
|
-
// Initialize nodeStorage and nodeContext
|
|
388
264
|
try {
|
|
389
265
|
this.log.debug(`Creating node storage manager: ${CYAN}${NODE_STORAGE_DIR}${db}`);
|
|
390
266
|
this.nodeStorage = new NodeStorageManager({ dir: path.join(this.matterbridgeDirectory, NODE_STORAGE_DIR), writeQueue: false, expiredInterval: undefined, logging: false });
|
|
391
267
|
this.log.debug('Creating node storage context for matterbridge');
|
|
392
268
|
this.nodeContext = await this.nodeStorage.createStorage('matterbridge');
|
|
393
|
-
// TODO: Remove this code when node-persist-manager is updated
|
|
394
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
395
269
|
const keys = (await this.nodeStorage?.storage.keys());
|
|
396
270
|
for (const key of keys) {
|
|
397
271
|
this.log.debug(`Checking node storage manager key: ${CYAN}${key}${db}`);
|
|
398
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
399
272
|
await this.nodeStorage?.storage.get(key);
|
|
400
273
|
}
|
|
401
274
|
const storages = await this.nodeStorage.getStorageNames();
|
|
402
275
|
for (const storage of storages) {
|
|
403
276
|
this.log.debug(`Checking storage: ${CYAN}${storage}${db}`);
|
|
404
277
|
const nodeContext = await this.nodeStorage?.createStorage(storage);
|
|
405
|
-
// TODO: Remove this code when node-persist-manager is updated
|
|
406
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
407
278
|
const keys = (await nodeContext?.storage.keys());
|
|
408
279
|
keys.forEach(async (key) => {
|
|
409
280
|
this.log.debug(`Checking key: ${CYAN}${storage}:${key}${db}`);
|
|
410
281
|
await nodeContext?.get(key);
|
|
411
282
|
});
|
|
412
283
|
}
|
|
413
|
-
// Creating a backup of the node storage since it is not corrupted
|
|
414
284
|
this.log.debug('Creating node storage backup...');
|
|
415
285
|
await copyDirectory(path.join(this.matterbridgeDirectory, NODE_STORAGE_DIR), path.join(this.matterbridgeDirectory, NODE_STORAGE_DIR + '.backup'));
|
|
416
286
|
this.log.debug('Created node storage backup');
|
|
417
287
|
}
|
|
418
288
|
catch (error) {
|
|
419
|
-
// Restoring the backup of the node storage since it is corrupted
|
|
420
289
|
this.log.error(`Error creating node storage manager and context: ${error instanceof Error ? error.message : error}`);
|
|
421
290
|
if (hasParameter('norestore')) {
|
|
422
291
|
this.log.fatal(`The matterbridge storage is corrupted. Found -norestore parameter: exiting...`);
|
|
@@ -430,19 +299,14 @@ export class Matterbridge extends EventEmitter {
|
|
|
430
299
|
if (!this.nodeStorage || !this.nodeContext) {
|
|
431
300
|
throw new Error('Fatal error creating node storage manager and context for matterbridge');
|
|
432
301
|
}
|
|
433
|
-
// Set the first port to use for the commissioning server (will be incremented in childbridge mode)
|
|
434
302
|
this.port = getIntParameter('port') ?? (await this.nodeContext.get('matterport', 5540)) ?? 5540;
|
|
435
|
-
// Set the first passcode to use for the commissioning server (will be incremented in childbridge mode)
|
|
436
303
|
this.passcode = getIntParameter('passcode') ?? (await this.nodeContext.get('matterpasscode')) ?? PaseClient.generateRandomPasscode(this.environment.get(Crypto));
|
|
437
|
-
// Set the first discriminator to use for the commissioning server (will be incremented in childbridge mode)
|
|
438
304
|
this.discriminator = getIntParameter('discriminator') ?? (await this.nodeContext.get('matterdiscriminator')) ?? PaseClient.generateRandomDiscriminator(this.environment.get(Crypto));
|
|
439
|
-
// Certificate management
|
|
440
305
|
const pairingFilePath = path.join(this.matterbridgeCertDirectory, 'pairing.json');
|
|
441
306
|
try {
|
|
442
307
|
await fs.promises.access(pairingFilePath, fs.constants.R_OK);
|
|
443
308
|
const pairingFileContent = await fs.promises.readFile(pairingFilePath, 'utf8');
|
|
444
309
|
const pairingFileJson = JSON.parse(pairingFileContent);
|
|
445
|
-
// Set the vendorId, vendorName, productId, productName, deviceType, serialNumber, uniqueId if they are present in the pairing file
|
|
446
310
|
if (isValidNumber(pairingFileJson.vendorId)) {
|
|
447
311
|
this.aggregatorVendorId = VendorId(pairingFileJson.vendorId);
|
|
448
312
|
this.log.info(`Pairing file ${CYAN}${pairingFilePath}${nf} found. Using vendorId ${CYAN}${this.aggregatorVendorId}${nf} from pairing file.`);
|
|
@@ -471,13 +335,11 @@ export class Matterbridge extends EventEmitter {
|
|
|
471
335
|
this.aggregatorUniqueId = pairingFileJson.uniqueId;
|
|
472
336
|
this.log.info(`Pairing file ${CYAN}${pairingFilePath}${nf} found. Using uniqueId ${CYAN}${this.aggregatorUniqueId}${nf} from pairing file.`);
|
|
473
337
|
}
|
|
474
|
-
// Override the passcode and discriminator if they are present in the pairing file
|
|
475
338
|
if (isValidNumber(pairingFileJson.passcode) && isValidNumber(pairingFileJson.discriminator)) {
|
|
476
339
|
this.passcode = pairingFileJson.passcode;
|
|
477
340
|
this.discriminator = pairingFileJson.discriminator;
|
|
478
341
|
this.log.info(`Pairing file ${CYAN}${pairingFilePath}${nf} found. Using passcode ${CYAN}${this.passcode}${nf} and discriminator ${CYAN}${this.discriminator}${nf} from pairing file.`);
|
|
479
342
|
}
|
|
480
|
-
// Set the certification for matter.js if it is present in the pairing file
|
|
481
343
|
if (pairingFileJson.privateKey && pairingFileJson.certificate && pairingFileJson.intermediateCertificate && pairingFileJson.declaration) {
|
|
482
344
|
const { hexToBuffer } = await import('@matterbridge/utils');
|
|
483
345
|
this.certification = {
|
|
@@ -492,44 +354,41 @@ export class Matterbridge extends EventEmitter {
|
|
|
492
354
|
catch (error) {
|
|
493
355
|
this.log.debug(`Pairing file ${CYAN}${pairingFilePath}${db} not found: ${error instanceof Error ? error.message : error}`);
|
|
494
356
|
}
|
|
495
|
-
// Store the passcode, discriminator and port in the node context
|
|
496
357
|
await this.nodeContext.set('matterport', this.port);
|
|
497
358
|
await this.nodeContext.set('matterpasscode', this.passcode);
|
|
498
359
|
await this.nodeContext.set('matterdiscriminator', this.discriminator);
|
|
499
360
|
this.log.debug(`Initializing server node for Matterbridge on port ${this.port} with passcode ${this.passcode} and discriminator ${this.discriminator}`);
|
|
500
|
-
// Set matterbridge logger level (context: matterbridgeLogLevel)
|
|
501
361
|
if (hasParameter('logger')) {
|
|
502
362
|
const level = getParameter('logger');
|
|
503
363
|
if (level === 'debug') {
|
|
504
|
-
this.log.logLevel = "debug"
|
|
364
|
+
this.log.logLevel = "debug";
|
|
505
365
|
}
|
|
506
366
|
else if (level === 'info') {
|
|
507
|
-
this.log.logLevel = "info"
|
|
367
|
+
this.log.logLevel = "info";
|
|
508
368
|
}
|
|
509
369
|
else if (level === 'notice') {
|
|
510
|
-
this.log.logLevel = "notice"
|
|
370
|
+
this.log.logLevel = "notice";
|
|
511
371
|
}
|
|
512
372
|
else if (level === 'warn') {
|
|
513
|
-
this.log.logLevel = "warn"
|
|
373
|
+
this.log.logLevel = "warn";
|
|
514
374
|
}
|
|
515
375
|
else if (level === 'error') {
|
|
516
|
-
this.log.logLevel = "error"
|
|
376
|
+
this.log.logLevel = "error";
|
|
517
377
|
}
|
|
518
378
|
else if (level === 'fatal') {
|
|
519
|
-
this.log.logLevel = "fatal"
|
|
379
|
+
this.log.logLevel = "fatal";
|
|
520
380
|
}
|
|
521
381
|
else {
|
|
522
382
|
this.log.warn(`Invalid matterbridge logger level: ${level}. Using default level "info".`);
|
|
523
|
-
this.log.logLevel = "info"
|
|
383
|
+
this.log.logLevel = "info";
|
|
524
384
|
}
|
|
525
385
|
}
|
|
526
386
|
else {
|
|
527
|
-
this.log.logLevel = await this.nodeContext.get('matterbridgeLogLevel', this.shellyBoard ? "notice"
|
|
387
|
+
this.log.logLevel = await this.nodeContext.get('matterbridgeLogLevel', this.shellyBoard ? "notice" : "info");
|
|
528
388
|
}
|
|
529
389
|
this.logLevel = this.log.logLevel;
|
|
530
390
|
this.frontend.logLevel = this.log.logLevel;
|
|
531
391
|
MatterbridgeEndpoint.logLevel = this.log.logLevel;
|
|
532
|
-
// Create the file logger for matterbridge (context: matterbridgeFileLog)
|
|
533
392
|
if (hasParameter('filelogger') || (await this.nodeContext.get('matterbridgeFileLog', false))) {
|
|
534
393
|
AnsiLogger.setGlobalLogfile(path.join(this.matterbridgeDirectory, MATTERBRIDGE_LOGGER_FILE), this.log.logLevel, true);
|
|
535
394
|
this.fileLogger = true;
|
|
@@ -538,7 +397,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
538
397
|
this.log.debug(`Matterbridge logLevel: ${this.log.logLevel} fileLoger: ${this.fileLogger}.`);
|
|
539
398
|
if (this.profile !== undefined)
|
|
540
399
|
this.log.debug(`Matterbridge profile: ${this.profile}.`);
|
|
541
|
-
// Set matter.js logger level, format and logger (context: matterLogLevel)
|
|
542
400
|
if (hasParameter('matterlogger')) {
|
|
543
401
|
const level = getParameter('matterlogger');
|
|
544
402
|
if (level === 'debug') {
|
|
@@ -569,13 +427,11 @@ export class Matterbridge extends EventEmitter {
|
|
|
569
427
|
}
|
|
570
428
|
Logger.format = MatterLogFormat.ANSI;
|
|
571
429
|
this.matterLogLevel = MatterLogLevel.names[Logger.level];
|
|
572
|
-
// Create the logger for matter.js with file logging (context: matterFileLog)
|
|
573
430
|
if (hasParameter('matterfilelogger') || (await this.nodeContext.get('matterFileLog', false))) {
|
|
574
431
|
this.matterFileLogger = true;
|
|
575
432
|
}
|
|
576
433
|
Logger.destinations.default.write = this.createDestinationMatterLogger(this.matterFileLogger);
|
|
577
434
|
this.log.debug(`Matter logLevel: ${this.matterLogLevel} fileLoger: ${this.matterFileLogger}.`);
|
|
578
|
-
// Log network interfaces
|
|
579
435
|
const networkInterfaces = os.networkInterfaces();
|
|
580
436
|
const availableAddresses = Object.entries(networkInterfaces);
|
|
581
437
|
const availableInterfaceNames = Object.keys(networkInterfaces);
|
|
@@ -588,7 +444,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
588
444
|
});
|
|
589
445
|
}
|
|
590
446
|
}
|
|
591
|
-
// Set the interface to use for matter server node mdnsInterface
|
|
592
447
|
if (hasParameter('mdnsinterface')) {
|
|
593
448
|
this.mdnsInterface = getParameter('mdnsinterface');
|
|
594
449
|
}
|
|
@@ -597,7 +452,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
597
452
|
if (this.mdnsInterface === '')
|
|
598
453
|
this.mdnsInterface = undefined;
|
|
599
454
|
}
|
|
600
|
-
// Validate mdnsInterface
|
|
601
455
|
if (this.mdnsInterface) {
|
|
602
456
|
if (!availableInterfaceNames.includes(this.mdnsInterface)) {
|
|
603
457
|
this.log.error(`Invalid mdnsinterface: ${this.mdnsInterface}. Available interfaces are: ${availableInterfaceNames.join(', ')}. Using all available interfaces.`);
|
|
@@ -610,7 +464,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
610
464
|
}
|
|
611
465
|
if (this.mdnsInterface)
|
|
612
466
|
this.environment.vars.set('mdns.networkInterface', this.mdnsInterface);
|
|
613
|
-
// Set the listeningAddressIpv4 for the matter commissioning server
|
|
614
467
|
if (hasParameter('ipv4address')) {
|
|
615
468
|
this.ipv4Address = getParameter('ipv4address');
|
|
616
469
|
}
|
|
@@ -619,7 +472,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
619
472
|
if (this.ipv4Address === '')
|
|
620
473
|
this.ipv4Address = undefined;
|
|
621
474
|
}
|
|
622
|
-
// Validate ipv4address
|
|
623
475
|
if (this.ipv4Address) {
|
|
624
476
|
let isValid = false;
|
|
625
477
|
for (const [ifaceName, ifaces] of availableAddresses) {
|
|
@@ -635,7 +487,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
635
487
|
await this.nodeContext.remove('matteripv4address');
|
|
636
488
|
}
|
|
637
489
|
}
|
|
638
|
-
// Set the listeningAddressIpv6 for the matter commissioning server
|
|
639
490
|
if (hasParameter('ipv6address')) {
|
|
640
491
|
this.ipv6Address = getParameter('ipv6address');
|
|
641
492
|
}
|
|
@@ -644,7 +495,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
644
495
|
if (this.ipv6Address === '')
|
|
645
496
|
this.ipv6Address = undefined;
|
|
646
497
|
}
|
|
647
|
-
// Validate ipv6address
|
|
648
498
|
if (this.ipv6Address) {
|
|
649
499
|
let isValid = false;
|
|
650
500
|
for (const [ifaceName, ifaces] of availableAddresses) {
|
|
@@ -653,7 +503,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
653
503
|
isValid = true;
|
|
654
504
|
break;
|
|
655
505
|
}
|
|
656
|
-
/* istanbul ignore next */
|
|
657
506
|
if (ifaces && ifaces.find((iface) => iface.scopeid && iface.scopeid > 0 && iface.address + '%' + (process.platform === 'win32' ? iface.scopeid : ifaceName) === this.ipv6Address)) {
|
|
658
507
|
this.log.info(`Using ipv6address ${CYAN}${this.ipv6Address}${nf} on interface ${CYAN}${ifaceName}${nf} for the Matter server node.`);
|
|
659
508
|
isValid = true;
|
|
@@ -666,7 +515,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
666
515
|
await this.nodeContext.remove('matteripv6address');
|
|
667
516
|
}
|
|
668
517
|
}
|
|
669
|
-
// Initialize the virtual mode
|
|
670
518
|
if (hasParameter('novirtual')) {
|
|
671
519
|
this.virtualMode = 'disabled';
|
|
672
520
|
await this.nodeContext.set('virtualmode', 'disabled');
|
|
@@ -675,15 +523,10 @@ export class Matterbridge extends EventEmitter {
|
|
|
675
523
|
this.virtualMode = (await this.nodeContext.get('virtualmode', 'outlet'));
|
|
676
524
|
}
|
|
677
525
|
this.log.debug(`Virtual mode ${this.virtualMode}.`);
|
|
678
|
-
// Initialize PluginManager
|
|
679
526
|
this.plugins.logLevel = this.log.logLevel;
|
|
680
527
|
await this.plugins.loadFromStorage();
|
|
681
|
-
// Initialize DeviceManager
|
|
682
528
|
this.devices.logLevel = this.log.logLevel;
|
|
683
|
-
// Get the plugins from node storage and create the plugins node storage contexts
|
|
684
529
|
for (const plugin of this.plugins) {
|
|
685
|
-
// Try to reinstall the plugin from npm (for Docker pull and external plugins)
|
|
686
|
-
// We don't do this when the add and other shutdown parameters are set because we shut down the process after adding the plugin
|
|
687
530
|
if (!fs.existsSync(plugin.path) && !hasParameter('add') && !hasParameter('remove') && !hasParameter('enable') && !hasParameter('disable') && !hasParameter('reset') && !hasParameter('factoryreset')) {
|
|
688
531
|
this.log.info(`Error parsing plugin ${plg}${plugin.name}${nf}. Trying to reinstall it from npm...`);
|
|
689
532
|
const { spawnCommand } = await import('./spawn.js');
|
|
@@ -713,7 +556,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
713
556
|
await plugin.nodeContext.set('description', plugin.description);
|
|
714
557
|
await plugin.nodeContext.set('author', plugin.author);
|
|
715
558
|
}
|
|
716
|
-
// Log system info and create .matterbridge directory
|
|
717
559
|
await this.logNodeAndSystemInfo();
|
|
718
560
|
this.log.notice(`Matterbridge version ${this.matterbridgeVersion} ` +
|
|
719
561
|
`${hasParameter('bridge') || (!hasParameter('childbridge') && (await this.nodeContext?.get('bridgeMode', '')) === 'bridge') ? 'mode bridge ' : ''}` +
|
|
@@ -721,7 +563,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
721
563
|
`${hasParameter('controller') ? 'mode controller ' : ''}` +
|
|
722
564
|
`${this.restartMode !== '' ? 'restart mode ' + this.restartMode + ' ' : ''}` +
|
|
723
565
|
`running on ${this.systemInformation.osType} (v.${this.systemInformation.osRelease}) platform ${this.systemInformation.osPlatform} arch ${this.systemInformation.osArch}`);
|
|
724
|
-
// Check node version and throw error
|
|
725
566
|
const minNodeVersion = 20;
|
|
726
567
|
const nodeVersion = process.versions.node;
|
|
727
568
|
const versionMajor = parseInt(nodeVersion.split('.')[0]);
|
|
@@ -729,18 +570,10 @@ export class Matterbridge extends EventEmitter {
|
|
|
729
570
|
this.log.error(`Node version ${versionMajor} is not supported. Please upgrade to ${minNodeVersion} or above.`);
|
|
730
571
|
throw new Error(`Node version ${versionMajor} is not supported. Please upgrade to ${minNodeVersion} or above.`);
|
|
731
572
|
}
|
|
732
|
-
// Parse command line
|
|
733
573
|
await this.parseCommandLine();
|
|
734
|
-
// Emit the initialize_completed event
|
|
735
574
|
this.emit('initialize_completed');
|
|
736
575
|
this.initialized = true;
|
|
737
576
|
}
|
|
738
|
-
/**
|
|
739
|
-
* Parses the command line arguments and performs the corresponding actions.
|
|
740
|
-
*
|
|
741
|
-
* @private
|
|
742
|
-
* @returns {Promise<void>} A promise that resolves when the command line arguments have been processed, or the process exits.
|
|
743
|
-
*/
|
|
744
577
|
async parseCommandLine() {
|
|
745
578
|
if (hasParameter('list')) {
|
|
746
579
|
this.log.info(`│ Registered plugins (${this.plugins.length})`);
|
|
@@ -756,19 +589,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
756
589
|
}
|
|
757
590
|
index++;
|
|
758
591
|
}
|
|
759
|
-
/*
|
|
760
|
-
const serializedRegisteredDevices = await this.nodeContext?.get<SerializedMatterbridgeEndpoint[]>('devices', []);
|
|
761
|
-
this.log.info(`│ Registered devices (${serializedRegisteredDevices?.length})`);
|
|
762
|
-
serializedRegisteredDevices?.forEach((device, index) => {
|
|
763
|
-
if (index !== serializedRegisteredDevices.length - 1) {
|
|
764
|
-
this.log.info(`├─┬─ plugin ${plg}${device.pluginName}${nf} device: ${dev}${device.deviceName}${nf} uniqueId: ${YELLOW}${device.uniqueId}${nf}`);
|
|
765
|
-
this.log.info(`│ └─ endpoint ${RED}${device.endpoint}${nf} ${typ}${device.endpointName}${nf} ${debugStringify(device.clusterServersId)}`);
|
|
766
|
-
} else {
|
|
767
|
-
this.log.info(`└─┬─ plugin ${plg}${device.pluginName}${nf} device: ${dev}${device.deviceName}${nf} uniqueId: ${YELLOW}${device.uniqueId}${nf}`);
|
|
768
|
-
this.log.info(` └─ endpoint ${RED}${device.endpoint}${nf} ${typ}${device.endpointName}${nf} ${debugStringify(device.clusterServersId)}`);
|
|
769
|
-
}
|
|
770
|
-
});
|
|
771
|
-
*/
|
|
772
592
|
this.shutdown = true;
|
|
773
593
|
return;
|
|
774
594
|
}
|
|
@@ -818,10 +638,8 @@ export class Matterbridge extends EventEmitter {
|
|
|
818
638
|
this.shutdown = true;
|
|
819
639
|
return;
|
|
820
640
|
}
|
|
821
|
-
// Initialize frontend
|
|
822
641
|
if (getIntParameter('frontend') !== 0 || getIntParameter('frontend') === undefined)
|
|
823
642
|
await this.frontend.start(getIntParameter('frontend'));
|
|
824
|
-
// Start the matter storage and create the matterbridge context
|
|
825
643
|
try {
|
|
826
644
|
await this.startMatterStorage();
|
|
827
645
|
if (this.aggregatorSerialNumber && this.aggregatorUniqueId && this.matterStorageService) {
|
|
@@ -835,21 +653,18 @@ export class Matterbridge extends EventEmitter {
|
|
|
835
653
|
this.log.fatal(`Fatal error creating matter storage: ${error instanceof Error ? error.message : error}`);
|
|
836
654
|
throw new Error(`Fatal error creating matter storage: ${error instanceof Error ? error.message : error}`);
|
|
837
655
|
}
|
|
838
|
-
// Clear the matterbridge context if the reset parameter is set (bridge mode)
|
|
839
656
|
if (hasParameter('reset') && getParameter('reset') === undefined) {
|
|
840
657
|
this.initialized = true;
|
|
841
658
|
await this.shutdownProcessAndReset();
|
|
842
659
|
this.shutdown = true;
|
|
843
660
|
return;
|
|
844
661
|
}
|
|
845
|
-
// Clear matterbridge plugin context if the reset parameter is set (childbridge mode)
|
|
846
662
|
if (hasParameter('reset') && getParameter('reset') !== undefined) {
|
|
847
663
|
this.log.debug(`Reset plugin ${getParameter('reset')}`);
|
|
848
664
|
const plugin = this.plugins.get(getParameter('reset'));
|
|
849
665
|
if (plugin) {
|
|
850
666
|
const matterStorageManager = await this.matterStorageService?.open(plugin.name);
|
|
851
667
|
if (!matterStorageManager) {
|
|
852
|
-
/* istanbul ignore next */
|
|
853
668
|
this.log.error(`Plugin ${plg}${plugin.name}${er} storageManager not found`);
|
|
854
669
|
}
|
|
855
670
|
else {
|
|
@@ -868,56 +683,47 @@ export class Matterbridge extends EventEmitter {
|
|
|
868
683
|
this.shutdown = true;
|
|
869
684
|
return;
|
|
870
685
|
}
|
|
871
|
-
// Check in 5 minutes the latest and dev versions of matterbridge and the plugins
|
|
872
686
|
clearTimeout(this.checkUpdateTimeout);
|
|
873
687
|
this.checkUpdateTimeout = setTimeout(async () => {
|
|
874
688
|
const { checkUpdates } = await import('./checkUpdates.js');
|
|
875
689
|
checkUpdates(this);
|
|
876
690
|
}, 300 * 1000).unref();
|
|
877
|
-
// Check each 12 hours the latest and dev versions of matterbridge and the plugins
|
|
878
691
|
clearInterval(this.checkUpdateInterval);
|
|
879
692
|
this.checkUpdateInterval = setInterval(async () => {
|
|
880
693
|
const { checkUpdates } = await import('./checkUpdates.js');
|
|
881
694
|
checkUpdates(this);
|
|
882
695
|
}, 12 * 60 * 60 * 1000).unref();
|
|
883
|
-
// Start the matterbridge in mode test
|
|
884
696
|
if (hasParameter('test')) {
|
|
885
697
|
this.bridgeMode = 'bridge';
|
|
886
698
|
return;
|
|
887
699
|
}
|
|
888
|
-
// Start the matterbridge in mode controller
|
|
889
700
|
if (hasParameter('controller')) {
|
|
890
701
|
this.bridgeMode = 'controller';
|
|
891
702
|
await this.startController();
|
|
892
703
|
return;
|
|
893
704
|
}
|
|
894
|
-
// Check if the bridge mode is set and start matterbridge in bridge mode if not set
|
|
895
705
|
if (!hasParameter('bridge') && !hasParameter('childbridge') && (await this.nodeContext?.get('bridgeMode', '')) === '') {
|
|
896
706
|
this.log.info('Setting default matterbridge start mode to bridge');
|
|
897
707
|
await this.nodeContext?.set('bridgeMode', 'bridge');
|
|
898
708
|
}
|
|
899
|
-
// Wait delay if specified (default 2 minutes) and the system uptime is less than 5 minutes. It solves race conditions on system startup.
|
|
900
709
|
if (hasParameter('delay') && os.uptime() <= 60 * 5) {
|
|
901
710
|
const { wait } = await import('@matterbridge/utils');
|
|
902
711
|
const delay = getIntParameter('delay') || 120;
|
|
903
712
|
this.log.warn('Delay switch found with system uptime less then 5 minutes. Waiting for ' + delay + ' seconds before starting matterbridge...');
|
|
904
713
|
await wait(delay * 1000, 'Race condition delay', true);
|
|
905
714
|
}
|
|
906
|
-
// Wait delay if specified (default 2 minutes). It solves race conditions on docker compose startup.
|
|
907
715
|
if (hasParameter('fixed_delay')) {
|
|
908
716
|
const { wait } = await import('@matterbridge/utils');
|
|
909
717
|
const delay = getIntParameter('fixed_delay') || 120;
|
|
910
718
|
this.log.warn('Fixed delay switch found. Waiting for ' + delay + ' seconds before starting matterbridge...');
|
|
911
719
|
await wait(delay * 1000, 'Fixed race condition delay', true);
|
|
912
720
|
}
|
|
913
|
-
// Start matterbridge in bridge mode
|
|
914
721
|
if (hasParameter('bridge') || (!hasParameter('childbridge') && (await this.nodeContext?.get('bridgeMode', '')) === 'bridge')) {
|
|
915
722
|
this.bridgeMode = 'bridge';
|
|
916
723
|
this.log.debug(`Starting matterbridge in mode ${this.bridgeMode}`);
|
|
917
724
|
await this.startBridge();
|
|
918
725
|
return;
|
|
919
726
|
}
|
|
920
|
-
// Start matterbridge in childbridge mode
|
|
921
727
|
if (hasParameter('childbridge') || (!hasParameter('bridge') && (await this.nodeContext?.get('bridgeMode', '')) === 'childbridge')) {
|
|
922
728
|
this.bridgeMode = 'childbridge';
|
|
923
729
|
this.log.debug(`Starting matterbridge in mode ${this.bridgeMode}`);
|
|
@@ -925,22 +731,10 @@ export class Matterbridge extends EventEmitter {
|
|
|
925
731
|
return;
|
|
926
732
|
}
|
|
927
733
|
}
|
|
928
|
-
/**
|
|
929
|
-
* Asynchronously loads and starts the registered plugins.
|
|
930
|
-
*
|
|
931
|
-
* This method is responsible for initializing and starting all enabled plugins.
|
|
932
|
-
* It ensures that each plugin is properly loaded and started before the bridge starts.
|
|
933
|
-
*
|
|
934
|
-
* @param {boolean} [wait] - If true, the method will wait for all plugins to be fully loaded and started before resolving. Defaults to false.
|
|
935
|
-
* @param {boolean} [start] - If true, the method will start the plugins after loading them. Defaults to true.
|
|
936
|
-
* @returns {Promise<void>} A promise that resolves when all plugins have been loaded and started.
|
|
937
|
-
*/
|
|
938
734
|
async startPlugins(wait = false, start = true) {
|
|
939
|
-
// Check, load and start the plugins
|
|
940
735
|
for (const plugin of this.plugins) {
|
|
941
736
|
plugin.configJson = await this.plugins.loadConfig(plugin);
|
|
942
737
|
plugin.schemaJson = await this.plugins.loadSchema(plugin);
|
|
943
|
-
// Check if the plugin is available
|
|
944
738
|
if (!(await this.plugins.resolve(plugin.path))) {
|
|
945
739
|
this.log.error(`Plugin ${plg}${plugin.name}${er} not found or not validated. Disabling it.`);
|
|
946
740
|
plugin.enabled = false;
|
|
@@ -960,16 +754,10 @@ export class Matterbridge extends EventEmitter {
|
|
|
960
754
|
if (wait)
|
|
961
755
|
await this.plugins.load(plugin, start, 'Matterbridge is starting');
|
|
962
756
|
else
|
|
963
|
-
this.plugins.load(plugin, start, 'Matterbridge is starting');
|
|
757
|
+
this.plugins.load(plugin, start, 'Matterbridge is starting');
|
|
964
758
|
}
|
|
965
759
|
this.frontend.wssSendRefreshRequired('plugins');
|
|
966
760
|
}
|
|
967
|
-
/**
|
|
968
|
-
* Registers the process handlers for uncaughtException, unhandledRejection, SIGINT and SIGTERM.
|
|
969
|
-
* - When an uncaught exception occurs, the exceptionHandler logs the error message and stack trace.
|
|
970
|
-
* - When an unhandled promise rejection occurs, the rejectionHandler logs the reason and stack trace.
|
|
971
|
-
* - When either of SIGINT and SIGTERM signals are received, the cleanup method is called with an appropriate message.
|
|
972
|
-
*/
|
|
973
761
|
registerProcessHandlers() {
|
|
974
762
|
this.log.debug(`Registering uncaughtException and unhandledRejection handlers...`);
|
|
975
763
|
process.removeAllListeners('uncaughtException');
|
|
@@ -996,9 +784,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
996
784
|
};
|
|
997
785
|
process.on('SIGTERM', this.sigtermHandler);
|
|
998
786
|
}
|
|
999
|
-
/**
|
|
1000
|
-
* Deregisters the process uncaughtException, unhandledRejection, SIGINT and SIGTERM signal handlers.
|
|
1001
|
-
*/
|
|
1002
787
|
deregisterProcessHandlers() {
|
|
1003
788
|
this.log.debug(`Deregistering uncaughtException and unhandledRejection handlers...`);
|
|
1004
789
|
if (this.exceptionHandler)
|
|
@@ -1015,20 +800,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
1015
800
|
process.off('SIGTERM', this.sigtermHandler);
|
|
1016
801
|
this.sigtermHandler = undefined;
|
|
1017
802
|
}
|
|
1018
|
-
/**
|
|
1019
|
-
* Logs the node and system information.
|
|
1020
|
-
*
|
|
1021
|
-
* @remarks
|
|
1022
|
-
* This method retrieves and logs various details about the host system, including:
|
|
1023
|
-
* - IP address information (IPv4, IPv6, MAC address)
|
|
1024
|
-
* - Node.js version
|
|
1025
|
-
* - Hostname and user information
|
|
1026
|
-
* - Operating system details (type, release, platform, architecture)
|
|
1027
|
-
* - Memory usage statistics
|
|
1028
|
-
* - Uptime information for both the system and the process
|
|
1029
|
-
*/
|
|
1030
803
|
async logNodeAndSystemInfo() {
|
|
1031
|
-
// IP address information
|
|
1032
804
|
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;
|
|
1033
805
|
const networkInterfaces = os.networkInterfaces();
|
|
1034
806
|
this.systemInformation.interfaceName = '';
|
|
@@ -1062,18 +834,16 @@ export class Matterbridge extends EventEmitter {
|
|
|
1062
834
|
break;
|
|
1063
835
|
}
|
|
1064
836
|
}
|
|
1065
|
-
// Node information
|
|
1066
837
|
this.systemInformation.nodeVersion = process.versions.node;
|
|
1067
838
|
const versionMajor = parseInt(this.systemInformation.nodeVersion.split('.')[0]);
|
|
1068
839
|
const versionMinor = parseInt(this.systemInformation.nodeVersion.split('.')[1]);
|
|
1069
840
|
const versionPatch = parseInt(this.systemInformation.nodeVersion.split('.')[2]);
|
|
1070
|
-
// Host system information
|
|
1071
841
|
this.systemInformation.hostname = os.hostname();
|
|
1072
842
|
this.systemInformation.user = os.userInfo().username;
|
|
1073
|
-
this.systemInformation.osType = os.type();
|
|
1074
|
-
this.systemInformation.osRelease = os.release();
|
|
1075
|
-
this.systemInformation.osPlatform = os.platform();
|
|
1076
|
-
this.systemInformation.osArch = os.arch();
|
|
843
|
+
this.systemInformation.osType = os.type();
|
|
844
|
+
this.systemInformation.osRelease = os.release();
|
|
845
|
+
this.systemInformation.osPlatform = os.platform();
|
|
846
|
+
this.systemInformation.osArch = os.arch();
|
|
1077
847
|
this.systemInformation.totalMemory = formatBytes(os.totalmem());
|
|
1078
848
|
this.systemInformation.freeMemory = formatBytes(os.freemem());
|
|
1079
849
|
this.systemInformation.systemUptime = formatUptime(os.uptime());
|
|
@@ -1083,7 +853,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1083
853
|
this.systemInformation.rss = formatBytes(process.memoryUsage().rss);
|
|
1084
854
|
this.systemInformation.heapTotal = formatBytes(process.memoryUsage().heapTotal);
|
|
1085
855
|
this.systemInformation.heapUsed = formatBytes(process.memoryUsage().heapUsed);
|
|
1086
|
-
// Log the system information
|
|
1087
856
|
this.log.debug('Host System Information:');
|
|
1088
857
|
this.log.debug(`- Hostname: ${this.systemInformation.hostname}`);
|
|
1089
858
|
this.log.debug(`- User: ${this.systemInformation.user}`);
|
|
@@ -1103,17 +872,14 @@ export class Matterbridge extends EventEmitter {
|
|
|
1103
872
|
this.log.debug(`- RSS: ${this.systemInformation.rss}`);
|
|
1104
873
|
this.log.debug(`- Heap Total: ${this.systemInformation.heapTotal}`);
|
|
1105
874
|
this.log.debug(`- Heap Used: ${this.systemInformation.heapUsed}`);
|
|
1106
|
-
// Log directories
|
|
1107
875
|
this.log.debug(`Root Directory: ${this.rootDirectory}`);
|
|
1108
876
|
this.log.debug(`Home Directory: ${this.homeDirectory}`);
|
|
1109
877
|
this.log.debug(`Matterbridge Directory: ${this.matterbridgeDirectory}`);
|
|
1110
878
|
this.log.debug(`Matterbridge Plugin Directory: ${this.matterbridgePluginDirectory}`);
|
|
1111
879
|
this.log.debug(`Matterbridge Matter Certificate Directory: ${this.matterbridgeCertDirectory}`);
|
|
1112
|
-
// Global node_modules directory
|
|
1113
880
|
if (this.nodeContext)
|
|
1114
881
|
this.globalModulesDirectory = await this.nodeContext.get('globalModulesDirectory', '');
|
|
1115
882
|
if (this.globalModulesDirectory === '') {
|
|
1116
|
-
// First run of Matterbridge so the node storage is empty
|
|
1117
883
|
this.log.debug(`Getting global node_modules directory...`);
|
|
1118
884
|
try {
|
|
1119
885
|
const { getGlobalNodeModules } = await import('@matterbridge/utils');
|
|
@@ -1126,42 +892,29 @@ export class Matterbridge extends EventEmitter {
|
|
|
1126
892
|
}
|
|
1127
893
|
}
|
|
1128
894
|
else {
|
|
1129
|
-
// The global node_modules directory is already set in the node storage and we check if it is still valid
|
|
1130
895
|
this.log.debug(`Global node_modules Directory: ${this.globalModulesDirectory}`);
|
|
1131
896
|
const { createESMWorker } = await import('./worker.js');
|
|
1132
897
|
createESMWorker('NpmGlobalPrefix', path.join(this.rootDirectory, 'dist/workerGlobalPrefix.js'));
|
|
1133
898
|
}
|
|
1134
|
-
// Matterbridge version
|
|
1135
899
|
this.log.debug(`Reading matterbridge package.json...`);
|
|
1136
900
|
const packageJson = JSON.parse(await fs.promises.readFile(path.join(this.rootDirectory, 'package.json'), 'utf-8'));
|
|
1137
901
|
this.matterbridgeVersion = this.matterbridgeLatestVersion = this.matterbridgeDevVersion = packageJson.version;
|
|
1138
902
|
this.log.debug(`Matterbridge Version: ${this.matterbridgeVersion}`);
|
|
1139
|
-
// Matterbridge latest version (will be set in the checkUpdate function)
|
|
1140
903
|
if (this.nodeContext)
|
|
1141
904
|
this.matterbridgeLatestVersion = await this.nodeContext.get('matterbridgeLatestVersion', this.matterbridgeVersion);
|
|
1142
905
|
this.log.debug(`Matterbridge Latest Version: ${this.matterbridgeLatestVersion}`);
|
|
1143
|
-
// Matterbridge dev version (will be set in the checkUpdate function)
|
|
1144
906
|
if (this.nodeContext)
|
|
1145
907
|
this.matterbridgeDevVersion = await this.nodeContext.get('matterbridgeDevVersion', this.matterbridgeVersion);
|
|
1146
908
|
this.log.debug(`Matterbridge Dev Version: ${this.matterbridgeDevVersion}`);
|
|
1147
|
-
// Frontend version
|
|
1148
909
|
this.log.debug(`Reading frontend package.json...`);
|
|
1149
910
|
const frontendPackageJson = JSON.parse(await fs.promises.readFile(path.join(this.rootDirectory, 'frontend/package.json'), 'utf8'));
|
|
1150
911
|
this.frontendVersion = frontendPackageJson.version;
|
|
1151
912
|
this.log.debug(`Frontend version ${CYAN}${this.frontendVersion}${db}`);
|
|
1152
|
-
// Current working directory
|
|
1153
913
|
const currentDir = process.cwd();
|
|
1154
914
|
this.log.debug(`Current Working Directory: ${currentDir}`);
|
|
1155
|
-
// Command line arguments (excluding 'node' and the script name)
|
|
1156
915
|
const cmdArgs = process.argv.slice(2).join(' ');
|
|
1157
916
|
this.log.debug(`Command Line Arguments: ${cmdArgs}`);
|
|
1158
917
|
}
|
|
1159
|
-
/**
|
|
1160
|
-
* Set the logger logLevel for the Matterbridge classes and call onChangeLoggerLevel() for each plugin.
|
|
1161
|
-
*
|
|
1162
|
-
* @param {LogLevel} logLevel The logger logLevel to set.
|
|
1163
|
-
* @returns {Promise<LogLevel>} A promise that resolves when the logLevel has been set.
|
|
1164
|
-
*/
|
|
1165
918
|
async setLogLevel(logLevel) {
|
|
1166
919
|
this.logLevel = logLevel;
|
|
1167
920
|
this.log.logLevel = logLevel;
|
|
@@ -1175,87 +928,58 @@ export class Matterbridge extends EventEmitter {
|
|
|
1175
928
|
continue;
|
|
1176
929
|
if (plugin.platform.config.debug === true)
|
|
1177
930
|
pluginDebug = true;
|
|
1178
|
-
plugin.platform.log.logLevel = plugin.platform.config.debug === true ? "debug"
|
|
1179
|
-
await plugin.platform.onChangeLoggerLevel(plugin.platform.config.debug === true ? "debug"
|
|
1180
|
-
}
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
callbackLogLevel = "debug" /* LogLevel.DEBUG */;
|
|
931
|
+
plugin.platform.log.logLevel = plugin.platform.config.debug === true ? "debug" : logLevel;
|
|
932
|
+
await plugin.platform.onChangeLoggerLevel(plugin.platform.config.debug === true ? "debug" : logLevel);
|
|
933
|
+
}
|
|
934
|
+
let callbackLogLevel = "notice";
|
|
935
|
+
if (logLevel === "info" || Logger.level === MatterLogLevel.INFO)
|
|
936
|
+
callbackLogLevel = "info";
|
|
937
|
+
if (logLevel === "debug" || Logger.level === MatterLogLevel.DEBUG || pluginDebug)
|
|
938
|
+
callbackLogLevel = "debug";
|
|
1187
939
|
AnsiLogger.setGlobalCallbackLevel(callbackLogLevel);
|
|
1188
940
|
this.log.debug(`WebSocketServer logger global callback set to ${callbackLogLevel}`);
|
|
1189
941
|
return logLevel;
|
|
1190
942
|
}
|
|
1191
|
-
/**
|
|
1192
|
-
* Get the current logger logLevel.
|
|
1193
|
-
*
|
|
1194
|
-
* @returns {LogLevel} The current logger logLevel.
|
|
1195
|
-
*/
|
|
1196
943
|
getLogLevel() {
|
|
1197
944
|
return this.log.logLevel;
|
|
1198
945
|
}
|
|
1199
|
-
/**
|
|
1200
|
-
* Creates a MatterLogger function to show the matter.js log messages in AnsiLogger (for the frontend).
|
|
1201
|
-
* It also logs to file (matter.log) if fileLogger is true.
|
|
1202
|
-
*
|
|
1203
|
-
* @param {boolean} fileLogger - Whether to log to file or not.
|
|
1204
|
-
* @returns {Function} The MatterLogger function. \x1b[35m for violet \x1b[34m is blue
|
|
1205
|
-
*/
|
|
1206
946
|
createDestinationMatterLogger(fileLogger) {
|
|
1207
|
-
this.matterLog.logNameColor = '\x1b[34m';
|
|
947
|
+
this.matterLog.logNameColor = '\x1b[34m';
|
|
1208
948
|
if (fileLogger) {
|
|
1209
949
|
this.matterLog.logFilePath = path.join(this.matterbridgeDirectory, MATTER_LOGGER_FILE);
|
|
1210
950
|
}
|
|
1211
951
|
return (text, message) => {
|
|
1212
|
-
// 2024-08-21 08:55:19.488 DEBUG InteractionMessenger Sending DataReport chunk with 28 attributes and 0 events: 1004 bytes
|
|
1213
952
|
const logger = text.slice(44, 44 + 20).trim();
|
|
1214
953
|
const msg = text.slice(65);
|
|
1215
954
|
this.matterLog.logName = logger;
|
|
1216
955
|
switch (message.level) {
|
|
1217
956
|
case MatterLogLevel.DEBUG:
|
|
1218
|
-
this.matterLog.log("debug"
|
|
957
|
+
this.matterLog.log("debug", msg);
|
|
1219
958
|
break;
|
|
1220
959
|
case MatterLogLevel.INFO:
|
|
1221
|
-
this.matterLog.log("info"
|
|
960
|
+
this.matterLog.log("info", msg);
|
|
1222
961
|
break;
|
|
1223
962
|
case MatterLogLevel.NOTICE:
|
|
1224
|
-
this.matterLog.log("notice"
|
|
963
|
+
this.matterLog.log("notice", msg);
|
|
1225
964
|
break;
|
|
1226
965
|
case MatterLogLevel.WARN:
|
|
1227
|
-
this.matterLog.log("warn"
|
|
966
|
+
this.matterLog.log("warn", msg);
|
|
1228
967
|
break;
|
|
1229
968
|
case MatterLogLevel.ERROR:
|
|
1230
|
-
this.matterLog.log("error"
|
|
969
|
+
this.matterLog.log("error", msg);
|
|
1231
970
|
break;
|
|
1232
971
|
case MatterLogLevel.FATAL:
|
|
1233
|
-
this.matterLog.log("fatal"
|
|
972
|
+
this.matterLog.log("fatal", msg);
|
|
1234
973
|
break;
|
|
1235
974
|
}
|
|
1236
975
|
};
|
|
1237
976
|
}
|
|
1238
|
-
/**
|
|
1239
|
-
* Restarts the process by exiting the current instance and loading a new instance (/api/restart).
|
|
1240
|
-
*
|
|
1241
|
-
* @returns {Promise<void>} A promise that resolves when the restart is completed.
|
|
1242
|
-
*/
|
|
1243
977
|
async restartProcess() {
|
|
1244
978
|
await this.cleanup('restarting...', true);
|
|
1245
979
|
}
|
|
1246
|
-
/**
|
|
1247
|
-
* Shut down the process (/api/shutdown).
|
|
1248
|
-
*
|
|
1249
|
-
* @returns {Promise<void>} A promise that resolves when the shutdown is completed.
|
|
1250
|
-
*/
|
|
1251
980
|
async shutdownProcess() {
|
|
1252
981
|
await this.cleanup('shutting down...', false);
|
|
1253
982
|
}
|
|
1254
|
-
/**
|
|
1255
|
-
* Update matterbridge and shut down the process (virtual device 'Update Matterbridge').
|
|
1256
|
-
*
|
|
1257
|
-
* @returns {Promise<void>} A promise that resolves when the update is completed.
|
|
1258
|
-
*/
|
|
1259
983
|
async updateProcess() {
|
|
1260
984
|
this.log.info('Updating matterbridge...');
|
|
1261
985
|
const { spawnCommand } = await import('./spawn.js');
|
|
@@ -1268,13 +992,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1268
992
|
this.frontend.wssSendRestartRequired();
|
|
1269
993
|
await this.cleanup('updating...', false);
|
|
1270
994
|
}
|
|
1271
|
-
/**
|
|
1272
|
-
* Unregister all devices and shut down the process (/api/unregister).
|
|
1273
|
-
*
|
|
1274
|
-
* @param {number} [timeout] - The timeout duration to wait for the message exchange to complete in milliseconds. Default is 1000.
|
|
1275
|
-
*
|
|
1276
|
-
* @returns {Promise<void>} A promise that resolves when the cleanup is completed.
|
|
1277
|
-
*/
|
|
1278
995
|
async unregisterAndShutdownProcess(timeout = 1000) {
|
|
1279
996
|
const { wait } = await import('@matterbridge/utils');
|
|
1280
997
|
this.log.info('Unregistering all devices and shutting down...');
|
|
@@ -1287,71 +1004,46 @@ export class Matterbridge extends EventEmitter {
|
|
|
1287
1004
|
await this.removeAllBridgedEndpoints(plugin.name, 100);
|
|
1288
1005
|
}
|
|
1289
1006
|
this.log.debug('Waiting for the MessageExchange to finish...');
|
|
1290
|
-
await wait(timeout);
|
|
1007
|
+
await wait(timeout);
|
|
1291
1008
|
this.log.debug('Cleaning up and shutting down...');
|
|
1292
1009
|
await this.cleanup('unregistered all devices and shutting down...', false, timeout);
|
|
1293
1010
|
}
|
|
1294
|
-
/**
|
|
1295
|
-
* Reset commissioning and shut down the process (/api/reset).
|
|
1296
|
-
*
|
|
1297
|
-
* @returns {Promise<void>} A promise that resolves when the cleanup is completed.
|
|
1298
|
-
*/
|
|
1299
1011
|
async shutdownProcessAndReset() {
|
|
1300
1012
|
await this.cleanup('shutting down with reset...', false);
|
|
1301
1013
|
}
|
|
1302
|
-
/**
|
|
1303
|
-
* Factory reset and shut down the process (/api/factory-reset).
|
|
1304
|
-
*
|
|
1305
|
-
* @returns {Promise<void>} A promise that resolves when the cleanup is completed.
|
|
1306
|
-
*/
|
|
1307
1014
|
async shutdownProcessAndFactoryReset() {
|
|
1308
1015
|
await this.cleanup('shutting down with factory reset...', false);
|
|
1309
1016
|
}
|
|
1310
|
-
/**
|
|
1311
|
-
* Cleans up the Matterbridge instance.
|
|
1312
|
-
*
|
|
1313
|
-
* @param {string} message - The cleanup message.
|
|
1314
|
-
* @param {boolean} [restart] - Indicates whether to restart the instance after cleanup. Default is `false`.
|
|
1315
|
-
* @param {number} [pause] - The pause in ms to wait for the message exchange to complete in milliseconds. Default is 1000.
|
|
1316
|
-
*
|
|
1317
|
-
* @returns {Promise<void>} A promise that resolves when the cleanup is completed.
|
|
1318
|
-
*/
|
|
1319
1017
|
async cleanup(message, restart = false, pause = 1000) {
|
|
1320
1018
|
if (this.initialized && !this.hasCleanupStarted) {
|
|
1321
1019
|
this.emit('cleanup_started');
|
|
1322
1020
|
this.hasCleanupStarted = true;
|
|
1323
1021
|
this.log.info(message);
|
|
1324
|
-
// Clear the start matter interval
|
|
1325
1022
|
if (this.startMatterInterval) {
|
|
1326
1023
|
clearInterval(this.startMatterInterval);
|
|
1327
1024
|
this.startMatterInterval = undefined;
|
|
1328
1025
|
this.log.debug('Start matter interval cleared');
|
|
1329
1026
|
}
|
|
1330
|
-
// Clear the check update timeout
|
|
1331
1027
|
if (this.checkUpdateTimeout) {
|
|
1332
1028
|
clearTimeout(this.checkUpdateTimeout);
|
|
1333
1029
|
this.checkUpdateTimeout = undefined;
|
|
1334
1030
|
this.log.debug('Check update timeout cleared');
|
|
1335
1031
|
}
|
|
1336
|
-
// Clear the check update interval
|
|
1337
1032
|
if (this.checkUpdateInterval) {
|
|
1338
1033
|
clearInterval(this.checkUpdateInterval);
|
|
1339
1034
|
this.checkUpdateInterval = undefined;
|
|
1340
1035
|
this.log.debug('Check update interval cleared');
|
|
1341
1036
|
}
|
|
1342
|
-
// Clear the configure timeout
|
|
1343
1037
|
if (this.configureTimeout) {
|
|
1344
1038
|
clearTimeout(this.configureTimeout);
|
|
1345
1039
|
this.configureTimeout = undefined;
|
|
1346
1040
|
this.log.debug('Matterbridge configure timeout cleared');
|
|
1347
1041
|
}
|
|
1348
|
-
// Clear the reachability timeout
|
|
1349
1042
|
if (this.reachabilityTimeout) {
|
|
1350
1043
|
clearTimeout(this.reachabilityTimeout);
|
|
1351
1044
|
this.reachabilityTimeout = undefined;
|
|
1352
1045
|
this.log.debug('Matterbridge reachability timeout cleared');
|
|
1353
1046
|
}
|
|
1354
|
-
// Call the shutdown method of each plugin and clear the plugins reachability timeout
|
|
1355
1047
|
for (const plugin of this.plugins) {
|
|
1356
1048
|
if (!plugin.enabled || plugin.error)
|
|
1357
1049
|
continue;
|
|
@@ -1362,7 +1054,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1362
1054
|
this.log.debug(`Plugin ${plg}${plugin.name}${db} reachability timeout cleared`);
|
|
1363
1055
|
}
|
|
1364
1056
|
}
|
|
1365
|
-
// Stop matter server nodes
|
|
1366
1057
|
this.log.notice(`Stopping matter server nodes in ${this.bridgeMode} mode...`);
|
|
1367
1058
|
if (pause > 0) {
|
|
1368
1059
|
const { wait } = await import('@matterbridge/utils');
|
|
@@ -1390,7 +1081,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1390
1081
|
}
|
|
1391
1082
|
}
|
|
1392
1083
|
this.log.notice('Stopped matter server nodes');
|
|
1393
|
-
// Matter commisioning reset
|
|
1394
1084
|
if (message === 'shutting down with reset...') {
|
|
1395
1085
|
this.log.info('Resetting Matterbridge commissioning information...');
|
|
1396
1086
|
await this.matterStorageManager?.createContext('events')?.clearAll();
|
|
@@ -1400,7 +1090,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1400
1090
|
await this.matterbridgeContext?.clearAll();
|
|
1401
1091
|
this.log.info('Matter storage reset done! Remove the bridge from the controller.');
|
|
1402
1092
|
}
|
|
1403
|
-
// Unregister all devices
|
|
1404
1093
|
if (message === 'unregistered all devices and shutting down...') {
|
|
1405
1094
|
if (this.bridgeMode === 'bridge') {
|
|
1406
1095
|
await this.matterStorageManager?.createContext('root')?.createContext('parts')?.createContext('Matterbridge')?.createContext('parts')?.clearAll();
|
|
@@ -1418,14 +1107,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
1418
1107
|
}
|
|
1419
1108
|
this.log.info('Matter storage reset done!');
|
|
1420
1109
|
}
|
|
1421
|
-
// Stop matter storage
|
|
1422
1110
|
await this.stopMatterStorage();
|
|
1423
|
-
/**
|
|
1424
|
-
* Unlink a file safely, ignoring errors.
|
|
1425
|
-
*
|
|
1426
|
-
* @param {string} path - The path to the file to unlink.
|
|
1427
|
-
* @param {AnsiLogger} log - The logger to use for logging.
|
|
1428
|
-
*/
|
|
1429
1111
|
function unlinkSafe(path, log) {
|
|
1430
1112
|
try {
|
|
1431
1113
|
log.debug(`Removing ${path}...`);
|
|
@@ -1433,15 +1115,12 @@ export class Matterbridge extends EventEmitter {
|
|
|
1433
1115
|
log.debug(`Removed ${path}`);
|
|
1434
1116
|
}
|
|
1435
1117
|
catch {
|
|
1436
|
-
// Ignore errors if the file does not exist
|
|
1437
1118
|
}
|
|
1438
1119
|
}
|
|
1439
|
-
// Remove the resumption records for Matterbridge (bridge mode)
|
|
1440
1120
|
this.log.debug(`Cleaning matter storage context for ${GREEN}Matterbridge${db}...`);
|
|
1441
1121
|
unlinkSafe(path.join(this.matterbridgeDirectory, MATTER_STORAGE_NAME, 'Matterbridge', 'sessions.resumptionRecords'), this.log);
|
|
1442
1122
|
unlinkSafe(path.join(this.matterbridgeDirectory, MATTER_STORAGE_NAME, 'Matterbridge', 'root.subscriptions.subscriptions'), this.log);
|
|
1443
1123
|
for (const plugin of this.plugins.array()) {
|
|
1444
|
-
// Remove the resumption records for the plugins (childbridge mode)
|
|
1445
1124
|
this.log.debug(`Cleaning matter storage context for plugin ${plg}${plugin.name}${db}...`);
|
|
1446
1125
|
unlinkSafe(path.join(this.matterbridgeDirectory, MATTER_STORAGE_NAME, plugin.name, 'sessions.resumptionRecords'), this.log);
|
|
1447
1126
|
unlinkSafe(path.join(this.matterbridgeDirectory, MATTER_STORAGE_NAME, plugin.name, 'root.subscriptions.subscriptions'), this.log);
|
|
@@ -1449,38 +1128,19 @@ export class Matterbridge extends EventEmitter {
|
|
|
1449
1128
|
for (const device of this.devices.array().filter((d) => d.mode === 'server')) {
|
|
1450
1129
|
if (!device.deviceName)
|
|
1451
1130
|
continue;
|
|
1452
|
-
// Remove the resumption records for the server mode devices
|
|
1453
1131
|
this.log.debug(`Cleaning matter storage context for server node device ${dev}${device.deviceName}${db}...`);
|
|
1454
1132
|
unlinkSafe(path.join(this.matterbridgeDirectory, MATTER_STORAGE_NAME, device.deviceName.replace(/[ .]/g, ''), 'sessions.resumptionRecords'), this.log);
|
|
1455
1133
|
unlinkSafe(path.join(this.matterbridgeDirectory, MATTER_STORAGE_NAME, device.deviceName.replace(/[ .]/g, ''), 'root.subscriptions.subscriptions'), this.log);
|
|
1456
1134
|
}
|
|
1457
|
-
// Stop the frontend
|
|
1458
1135
|
await this.frontend.stop();
|
|
1459
1136
|
this.frontend.destroy();
|
|
1460
|
-
// Close PluginManager and DeviceManager
|
|
1461
1137
|
this.plugins.destroy();
|
|
1462
1138
|
this.devices.destroy();
|
|
1463
|
-
// Stop thread messaging server
|
|
1464
1139
|
this.server.close();
|
|
1465
|
-
// Close the matterbridge node storage and context
|
|
1466
1140
|
if (this.nodeStorage && this.nodeContext) {
|
|
1467
|
-
/*
|
|
1468
|
-
TODO: Implement serialization of registered devices
|
|
1469
|
-
this.log.info('Saving registered devices...');
|
|
1470
|
-
const serializedRegisteredDevices: SerializedMatterbridgeEndpoint[] = [];
|
|
1471
|
-
this.devices.forEach(async (device) => {
|
|
1472
|
-
const serializedMatterbridgeDevice = MatterbridgeEndpoint.serialize(device);
|
|
1473
|
-
this.log.info(`- ${serializedMatterbridgeDevice.deviceName}${rs}\n`, serializedMatterbridgeDevice);
|
|
1474
|
-
if (serializedMatterbridgeDevice) serializedRegisteredDevices.push(serializedMatterbridgeDevice);
|
|
1475
|
-
});
|
|
1476
|
-
await this.nodeContext.set<SerializedMatterbridgeEndpoint[]>('devices', serializedRegisteredDevices);
|
|
1477
|
-
this.log.info(`Saved registered devices (${serializedRegisteredDevices?.length})`);
|
|
1478
|
-
*/
|
|
1479
|
-
// Clear nodeContext and nodeStorage (they just need 1000ms to write the data to disk)
|
|
1480
1141
|
this.log.debug(`Closing node storage context for ${plg}Matterbridge${db}...`);
|
|
1481
1142
|
await this.nodeContext.close();
|
|
1482
1143
|
this.nodeContext = undefined;
|
|
1483
|
-
// Clear nodeContext for each plugin (they just need 1000ms to write the data to disk)
|
|
1484
1144
|
for (const plugin of this.plugins) {
|
|
1485
1145
|
if (plugin.nodeContext) {
|
|
1486
1146
|
this.log.debug(`Closing node storage context for plugin ${plg}${plugin.name}${db}...`);
|
|
@@ -1497,10 +1157,8 @@ export class Matterbridge extends EventEmitter {
|
|
|
1497
1157
|
}
|
|
1498
1158
|
this.plugins.clear();
|
|
1499
1159
|
this.devices.clear();
|
|
1500
|
-
// Factory reset
|
|
1501
1160
|
if (message === 'shutting down with factory reset...') {
|
|
1502
1161
|
try {
|
|
1503
|
-
// Delete matter storage directory with its subdirectories and backup
|
|
1504
1162
|
const dir = path.join(this.matterbridgeDirectory, MATTER_STORAGE_NAME);
|
|
1505
1163
|
this.log.info(`Removing matter storage directory: ${dir}`);
|
|
1506
1164
|
await fs.promises.rm(dir, { recursive: true });
|
|
@@ -1509,13 +1167,11 @@ export class Matterbridge extends EventEmitter {
|
|
|
1509
1167
|
await fs.promises.rm(backup, { recursive: true });
|
|
1510
1168
|
}
|
|
1511
1169
|
catch (error) {
|
|
1512
|
-
// istanbul ignore next if
|
|
1513
1170
|
if (error instanceof Error && error.code !== 'ENOENT') {
|
|
1514
1171
|
this.log.error(`Error removing matter storage directory: ${error}`);
|
|
1515
1172
|
}
|
|
1516
1173
|
}
|
|
1517
1174
|
try {
|
|
1518
|
-
// Delete matterbridge storage directory with its subdirectories and backup
|
|
1519
1175
|
const dir = path.join(this.matterbridgeDirectory, NODE_STORAGE_DIR);
|
|
1520
1176
|
this.log.info(`Removing matterbridge storage directory: ${dir}`);
|
|
1521
1177
|
await fs.promises.rm(dir, { recursive: true });
|
|
@@ -1524,20 +1180,18 @@ export class Matterbridge extends EventEmitter {
|
|
|
1524
1180
|
await fs.promises.rm(backup, { recursive: true });
|
|
1525
1181
|
}
|
|
1526
1182
|
catch (error) {
|
|
1527
|
-
// istanbul ignore next if
|
|
1528
1183
|
if (error instanceof Error && error.code !== 'ENOENT') {
|
|
1529
1184
|
this.log.error(`Error removing matterbridge storage directory: ${error}`);
|
|
1530
1185
|
}
|
|
1531
1186
|
}
|
|
1532
1187
|
this.log.info('Factory reset done! Remove all paired fabrics from the controllers.');
|
|
1533
1188
|
}
|
|
1534
|
-
// Deregisters the process handlers
|
|
1535
1189
|
this.deregisterProcessHandlers();
|
|
1536
1190
|
if (restart) {
|
|
1537
1191
|
if (message === 'updating...') {
|
|
1538
1192
|
this.log.info('Cleanup completed. Updating...');
|
|
1539
1193
|
Matterbridge.instance = undefined;
|
|
1540
|
-
this.emit('update');
|
|
1194
|
+
this.emit('update');
|
|
1541
1195
|
}
|
|
1542
1196
|
else if (message === 'restarting...') {
|
|
1543
1197
|
this.log.info('Cleanup completed. Restarting...');
|
|
@@ -1566,14 +1220,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
1566
1220
|
this.log.debug('Cleanup already started...');
|
|
1567
1221
|
}
|
|
1568
1222
|
}
|
|
1569
|
-
/**
|
|
1570
|
-
* Starts the Matterbridge in bridge mode.
|
|
1571
|
-
*
|
|
1572
|
-
* @private
|
|
1573
|
-
* @returns {Promise<void>} A promise that resolves when the Matterbridge is started.
|
|
1574
|
-
*/
|
|
1575
1223
|
async startBridge() {
|
|
1576
|
-
// Plugins are configured by a timer when matter server is started and plugin.configured is set to true
|
|
1577
1224
|
if (!this.matterStorageManager)
|
|
1578
1225
|
throw new Error('No storage manager initialized');
|
|
1579
1226
|
if (!this.matterbridgeContext)
|
|
@@ -1587,7 +1234,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1587
1234
|
this.frontend.wssSendSnackbarMessage(`The bridge is starting...`, 0, 'info');
|
|
1588
1235
|
let failCount = 0;
|
|
1589
1236
|
this.startMatterInterval = setInterval(async () => {
|
|
1590
|
-
// istanbul ignore if cause is just a logging statement
|
|
1591
1237
|
if (failCount && failCount % 10 === 0) {
|
|
1592
1238
|
this.frontend.wssSendSnackbarMessage(`The bridge is still starting...`, 10, 'info');
|
|
1593
1239
|
this.frontend.wssSendRefreshRequired('plugins');
|
|
@@ -1621,16 +1267,13 @@ export class Matterbridge extends EventEmitter {
|
|
|
1621
1267
|
clearInterval(this.startMatterInterval);
|
|
1622
1268
|
this.startMatterInterval = undefined;
|
|
1623
1269
|
this.log.debug('Cleared startMatterInterval interval in bridge mode');
|
|
1624
|
-
|
|
1625
|
-
this.startServerNode(this.serverNode); // We don't await this, because the server node is started in the background
|
|
1626
|
-
// Start the Matter server node of single devices in mode 'server'
|
|
1270
|
+
this.startServerNode(this.serverNode);
|
|
1627
1271
|
for (const device of this.devices.array()) {
|
|
1628
1272
|
if (device.mode === 'server' && device.serverNode) {
|
|
1629
1273
|
this.log.debug(`Starting server node for device ${dev}${device.deviceName}${db} in server mode...`);
|
|
1630
|
-
this.startServerNode(device.serverNode);
|
|
1274
|
+
this.startServerNode(device.serverNode);
|
|
1631
1275
|
}
|
|
1632
1276
|
}
|
|
1633
|
-
// Configure the plugins
|
|
1634
1277
|
this.configureTimeout = setTimeout(async () => {
|
|
1635
1278
|
for (const plugin of this.plugins.array()) {
|
|
1636
1279
|
if (!plugin.enabled || !plugin.loaded || !plugin.started || plugin.error)
|
|
@@ -1648,13 +1291,11 @@ export class Matterbridge extends EventEmitter {
|
|
|
1648
1291
|
}
|
|
1649
1292
|
this.frontend.wssSendRefreshRequired('plugins');
|
|
1650
1293
|
}, 30 * 1000).unref();
|
|
1651
|
-
// Setting reachability to true
|
|
1652
1294
|
this.reachabilityTimeout = setTimeout(() => {
|
|
1653
1295
|
this.log.info(`Setting reachability to true for ${plg}Matterbridge${db}`);
|
|
1654
1296
|
if (this.aggregatorNode)
|
|
1655
1297
|
this.setAggregatorReachability(this.aggregatorNode, true);
|
|
1656
1298
|
}, 60 * 1000).unref();
|
|
1657
|
-
// Logger.get('LogServerNode').info(this.serverNode);
|
|
1658
1299
|
this.emit('bridge_started');
|
|
1659
1300
|
this.log.notice('Matterbridge bridge started successfully');
|
|
1660
1301
|
this.frontend.wssSendRefreshRequired('settings');
|
|
@@ -1662,33 +1303,22 @@ export class Matterbridge extends EventEmitter {
|
|
|
1662
1303
|
this.frontend.wssSendCloseSnackbarMessage(`The bridge is starting...`);
|
|
1663
1304
|
}, Number(process.env['MATTERBRIDGE_START_MATTER_INTERVAL_MS']) || this.startMatterIntervalMs);
|
|
1664
1305
|
}
|
|
1665
|
-
/**
|
|
1666
|
-
* Starts the Matterbridge in childbridge mode.
|
|
1667
|
-
*
|
|
1668
|
-
* @param {number} [delay] - The delay before starting the childbridge. Default is 1000 milliseconds.
|
|
1669
|
-
*
|
|
1670
|
-
* @returns {Promise<void>} A promise that resolves when the Matterbridge is started.
|
|
1671
|
-
*/
|
|
1672
1306
|
async startChildbridge(delay = 1000) {
|
|
1673
1307
|
if (!this.matterStorageManager)
|
|
1674
1308
|
throw new Error('No storage manager initialized');
|
|
1675
1309
|
const { wait } = await import('@matterbridge/utils');
|
|
1676
|
-
// Load with await all plugins but don't start them. We get the platform.type to pre-create server nodes for DynamicPlatform plugins
|
|
1677
1310
|
this.log.debug('Loading all plugins in childbridge mode...');
|
|
1678
1311
|
await this.startPlugins(true, false);
|
|
1679
|
-
// Create server nodes for DynamicPlatform plugins and start all plugins in the background
|
|
1680
1312
|
this.log.debug('Creating server nodes for DynamicPlatform plugins and starting all plugins in childbridge mode...');
|
|
1681
1313
|
for (const plugin of this.plugins.array().filter((p) => p.enabled && !p.error)) {
|
|
1682
1314
|
if (plugin.type === 'DynamicPlatform')
|
|
1683
1315
|
await this.createDynamicPlugin(plugin);
|
|
1684
|
-
this.plugins.start(plugin, 'Matterbridge is starting');
|
|
1316
|
+
this.plugins.start(plugin, 'Matterbridge is starting');
|
|
1685
1317
|
}
|
|
1686
|
-
// Start the Matterbridge in childbridge mode when all plugins are loaded and started
|
|
1687
1318
|
this.log.debug('Starting start matter interval in childbridge mode...');
|
|
1688
1319
|
this.frontend.wssSendSnackbarMessage(`The bridge is starting...`, 0, 'info');
|
|
1689
1320
|
let failCount = 0;
|
|
1690
1321
|
this.startMatterInterval = setInterval(async () => {
|
|
1691
|
-
// istanbul ignore if cause is just a logging statement
|
|
1692
1322
|
if (failCount && failCount % 10 === 0) {
|
|
1693
1323
|
this.frontend.wssSendSnackbarMessage(`The bridge is still starting...`, 10, 'info');
|
|
1694
1324
|
this.frontend.wssSendRefreshRequired('plugins');
|
|
@@ -1726,9 +1356,8 @@ export class Matterbridge extends EventEmitter {
|
|
|
1726
1356
|
clearInterval(this.startMatterInterval);
|
|
1727
1357
|
this.startMatterInterval = undefined;
|
|
1728
1358
|
if (delay > 0)
|
|
1729
|
-
await wait(Number(process.env['MATTERBRIDGE_PAUSE_MATTER_INTERVAL_MS']) || delay);
|
|
1359
|
+
await wait(Number(process.env['MATTERBRIDGE_PAUSE_MATTER_INTERVAL_MS']) || delay);
|
|
1730
1360
|
this.log.debug('Cleared startMatterInterval interval in childbridge mode');
|
|
1731
|
-
// Configure the plugins
|
|
1732
1361
|
this.configureTimeout = setTimeout(async () => {
|
|
1733
1362
|
for (const plugin of this.plugins.array()) {
|
|
1734
1363
|
if (!plugin.enabled || !plugin.loaded || !plugin.started || plugin.error)
|
|
@@ -1753,7 +1382,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
1753
1382
|
this.log.error(`Plugin ${plg}${plugin.name}${er} didn't register any devices to Matterbridge. Verify the plugin configuration.`);
|
|
1754
1383
|
continue;
|
|
1755
1384
|
}
|
|
1756
|
-
// istanbul ignore next if cause is just a safety check
|
|
1757
1385
|
if (!plugin.serverNode) {
|
|
1758
1386
|
this.log.error(`Server node not found for plugin ${plg}${plugin.name}${er}`);
|
|
1759
1387
|
continue;
|
|
@@ -1766,23 +1394,19 @@ export class Matterbridge extends EventEmitter {
|
|
|
1766
1394
|
this.log.error(`Node storage context not found for plugin ${plg}${plugin.name}${er}`);
|
|
1767
1395
|
continue;
|
|
1768
1396
|
}
|
|
1769
|
-
|
|
1770
|
-
this.startServerNode(plugin.serverNode); // We don't await this, because the server node is started in the background
|
|
1771
|
-
// Setting reachability to true
|
|
1397
|
+
this.startServerNode(plugin.serverNode);
|
|
1772
1398
|
plugin.reachabilityTimeout = setTimeout(() => {
|
|
1773
1399
|
this.log.info(`Setting reachability to true for ${plg}${plugin.name}${nf}`);
|
|
1774
1400
|
if (plugin.type === 'DynamicPlatform' && plugin.aggregatorNode)
|
|
1775
1401
|
this.setAggregatorReachability(plugin.aggregatorNode, true);
|
|
1776
1402
|
}, 60 * 1000).unref();
|
|
1777
1403
|
}
|
|
1778
|
-
// Start the Matter server node of single devices in mode 'server'
|
|
1779
1404
|
for (const device of this.devices.array()) {
|
|
1780
1405
|
if (device.mode === 'server' && device.serverNode) {
|
|
1781
1406
|
this.log.debug(`Starting server node for device ${dev}${device.deviceName}${db} in server mode...`);
|
|
1782
|
-
this.startServerNode(device.serverNode);
|
|
1407
|
+
this.startServerNode(device.serverNode);
|
|
1783
1408
|
}
|
|
1784
1409
|
}
|
|
1785
|
-
// Logger.get('LogServerNode').info(this.serverNode);
|
|
1786
1410
|
this.emit('childbridge_started');
|
|
1787
1411
|
this.log.notice('Matterbridge childbridge started successfully');
|
|
1788
1412
|
this.frontend.wssSendRefreshRequired('settings');
|
|
@@ -1790,229 +1414,9 @@ export class Matterbridge extends EventEmitter {
|
|
|
1790
1414
|
this.frontend.wssSendCloseSnackbarMessage(`The bridge is starting...`);
|
|
1791
1415
|
}, Number(process.env['MATTERBRIDGE_START_MATTER_INTERVAL_MS']) || this.startMatterIntervalMs);
|
|
1792
1416
|
}
|
|
1793
|
-
/**
|
|
1794
|
-
* Starts the Matterbridge controller.
|
|
1795
|
-
*
|
|
1796
|
-
* @private
|
|
1797
|
-
* @returns {Promise<void>} A promise that resolves when the Matterbridge is started.
|
|
1798
|
-
*/
|
|
1799
1417
|
async startController() {
|
|
1800
|
-
/*
|
|
1801
|
-
if (!this.matterStorageManager) {
|
|
1802
|
-
this.log.error('No storage manager initialized');
|
|
1803
|
-
await this.cleanup('No storage manager initialized');
|
|
1804
|
-
return;
|
|
1805
|
-
}
|
|
1806
|
-
this.log.info('Creating context: mattercontrollerContext');
|
|
1807
|
-
this.controllerContext = this.matterStorageManager.createContext('mattercontrollerContext');
|
|
1808
|
-
if (!this.controllerContext) {
|
|
1809
|
-
this.log.error('No storage context mattercontrollerContext initialized');
|
|
1810
|
-
await this.cleanup('No storage context mattercontrollerContext initialized');
|
|
1811
|
-
return;
|
|
1812
|
-
}
|
|
1813
|
-
|
|
1814
|
-
this.log.debug('Starting matterbridge in mode', this.bridgeMode);
|
|
1815
|
-
this.matterServer = await this.createMatterServer(this.storageManager);
|
|
1816
|
-
this.log.info('Creating matter commissioning controller');
|
|
1817
|
-
this.commissioningController = new CommissioningController({
|
|
1818
|
-
autoConnect: false,
|
|
1819
|
-
});
|
|
1820
|
-
this.log.info('Adding matter commissioning controller to matter server');
|
|
1821
|
-
await this.matterServer.addCommissioningController(this.commissioningController);
|
|
1822
|
-
|
|
1823
|
-
this.log.info('Starting matter server');
|
|
1824
|
-
await this.matterServer.start();
|
|
1825
|
-
this.log.info('Matter server started');
|
|
1826
|
-
const commissioningOptions: ControllerCommissioningFlowOptions = {
|
|
1827
|
-
regulatoryLocation: GeneralCommissioning.RegulatoryLocationType.IndoorOutdoor,
|
|
1828
|
-
regulatoryCountryCode: 'XX',
|
|
1829
|
-
};
|
|
1830
|
-
const commissioningController = new CommissioningController({
|
|
1831
|
-
environment: {
|
|
1832
|
-
environment,
|
|
1833
|
-
id: uniqueId,
|
|
1834
|
-
},
|
|
1835
|
-
autoConnect: false, // Do not auto connect to the commissioned nodes
|
|
1836
|
-
adminFabricLabel,
|
|
1837
|
-
});
|
|
1838
|
-
|
|
1839
|
-
if (hasParameter('pairingcode')) {
|
|
1840
|
-
this.log.info('Pairing device with pairingcode:', getParameter('pairingcode'));
|
|
1841
|
-
const pairingCode = getParameter('pairingcode');
|
|
1842
|
-
const ip = this.controllerContext.has('ip') ? this.controllerContext.get<string>('ip') : undefined;
|
|
1843
|
-
const port = this.controllerContext.has('port') ? this.controllerContext.get<number>('port') : undefined;
|
|
1844
|
-
|
|
1845
|
-
let longDiscriminator, setupPin, shortDiscriminator;
|
|
1846
|
-
if (pairingCode !== undefined) {
|
|
1847
|
-
const pairingCodeCodec = ManualPairingCodeCodec.decode(pairingCode);
|
|
1848
|
-
shortDiscriminator = pairingCodeCodec.shortDiscriminator;
|
|
1849
|
-
longDiscriminator = undefined;
|
|
1850
|
-
setupPin = pairingCodeCodec.passcode;
|
|
1851
|
-
this.log.info(`Data extracted from pairing code: ${Logger.toJSON(pairingCodeCodec)}`);
|
|
1852
|
-
} else {
|
|
1853
|
-
longDiscriminator = await this.controllerContext.get('longDiscriminator', 3840);
|
|
1854
|
-
if (longDiscriminator > 4095) throw new Error('Discriminator value must be less than 4096');
|
|
1855
|
-
setupPin = this.controllerContext.get('pin', 20202021);
|
|
1856
|
-
}
|
|
1857
|
-
if ((shortDiscriminator === undefined && longDiscriminator === undefined) || setupPin === undefined) {
|
|
1858
|
-
throw new Error('Please specify the longDiscriminator of the device to commission with -longDiscriminator or provide a valid passcode with -passcode');
|
|
1859
|
-
}
|
|
1860
|
-
|
|
1861
|
-
const options = {
|
|
1862
|
-
commissioning: commissioningOptions,
|
|
1863
|
-
discovery: {
|
|
1864
|
-
knownAddress: ip !== undefined && port !== undefined ? { ip, port, type: 'udp' } : undefined,
|
|
1865
|
-
identifierData: longDiscriminator !== undefined ? { longDiscriminator } : shortDiscriminator !== undefined ? { shortDiscriminator } : {},
|
|
1866
|
-
},
|
|
1867
|
-
passcode: setupPin,
|
|
1868
|
-
} as NodeCommissioningOptions;
|
|
1869
|
-
this.log.info('Commissioning with options:', options);
|
|
1870
|
-
const nodeId = await this.commissioningController.commissionNode(options);
|
|
1871
|
-
this.log.info(`Commissioning successfully done with nodeId: ${nodeId}`);
|
|
1872
|
-
this.log.info('ActiveSessionInformation:', this.commissioningController.getActiveSessionInformation());
|
|
1873
|
-
} // (hasParameter('pairingcode'))
|
|
1874
|
-
|
|
1875
|
-
if (hasParameter('unpairall')) {
|
|
1876
|
-
this.log.info('***Commissioning controller unpairing all nodes...');
|
|
1877
|
-
const nodeIds = this.commissioningController.getCommissionedNodes();
|
|
1878
|
-
for (const nodeId of nodeIds) {
|
|
1879
|
-
this.log.info('***Commissioning controller unpairing node:', nodeId);
|
|
1880
|
-
await this.commissioningController.removeNode(nodeId);
|
|
1881
|
-
}
|
|
1882
|
-
return;
|
|
1883
|
-
}
|
|
1884
|
-
|
|
1885
|
-
if (hasParameter('discover')) {
|
|
1886
|
-
// const discover = await this.commissioningController.discoverCommissionableDevices({ productId: 0x8000, deviceType: 0xfff1 });
|
|
1887
|
-
// console.log(discover);
|
|
1888
|
-
}
|
|
1889
|
-
|
|
1890
|
-
if (!this.commissioningController.isCommissioned()) {
|
|
1891
|
-
this.log.info('***Commissioning controller is not commissioned: use matterbridge -controller -pairingcode [pairingcode] to commission a device');
|
|
1892
|
-
return;
|
|
1893
|
-
}
|
|
1894
|
-
|
|
1895
|
-
const nodeIds = this.commissioningController.getCommissionedNodes();
|
|
1896
|
-
this.log.info(`***Commissioning controller is commissioned ${this.commissioningController.isCommissioned()} and has ${nodeIds.length} nodes commisioned: `);
|
|
1897
|
-
for (const nodeId of nodeIds) {
|
|
1898
|
-
this.log.info(`***Connecting to commissioned node: ${nodeId}`);
|
|
1899
|
-
|
|
1900
|
-
const node = await this.commissioningController.connectNode(nodeId, {
|
|
1901
|
-
autoSubscribe: false,
|
|
1902
|
-
attributeChangedCallback: (peerNodeId, { path: { nodeId, clusterId, endpointId, attributeName }, value }) =>
|
|
1903
|
-
this.log.info(`***Commissioning controller attributeChangedCallback ${peerNodeId}: attribute ${nodeId}/${endpointId}/${clusterId}/${attributeName} changed to ${Logger.toJSON(value)}`),
|
|
1904
|
-
eventTriggeredCallback: (peerNodeId, { path: { nodeId, clusterId, endpointId, eventName }, events }) =>
|
|
1905
|
-
this.log.info(`***Commissioning controller eventTriggeredCallback ${peerNodeId}: Event ${nodeId}/${endpointId}/${clusterId}/${eventName} triggered with ${Logger.toJSON(events)}`),
|
|
1906
|
-
stateInformationCallback: (peerNodeId, info) => {
|
|
1907
|
-
switch (info) {
|
|
1908
|
-
case NodeStateInformation.Connected:
|
|
1909
|
-
this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} connected`);
|
|
1910
|
-
break;
|
|
1911
|
-
case NodeStateInformation.Disconnected:
|
|
1912
|
-
this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} disconnected`);
|
|
1913
|
-
break;
|
|
1914
|
-
case NodeStateInformation.Reconnecting:
|
|
1915
|
-
this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} reconnecting`);
|
|
1916
|
-
break;
|
|
1917
|
-
case NodeStateInformation.WaitingForDeviceDiscovery:
|
|
1918
|
-
this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} waiting for device discovery`);
|
|
1919
|
-
break;
|
|
1920
|
-
case NodeStateInformation.StructureChanged:
|
|
1921
|
-
this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} structure changed`);
|
|
1922
|
-
break;
|
|
1923
|
-
case NodeStateInformation.Decommissioned:
|
|
1924
|
-
this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} decommissioned`);
|
|
1925
|
-
break;
|
|
1926
|
-
default:
|
|
1927
|
-
this.log.info(`***Commissioning controller stateInformationCallback ${peerNodeId}: Node ${nodeId} NodeStateInformation.${info}`);
|
|
1928
|
-
break;
|
|
1929
|
-
}
|
|
1930
|
-
},
|
|
1931
|
-
});
|
|
1932
|
-
|
|
1933
|
-
node.logStructure();
|
|
1934
|
-
|
|
1935
|
-
// Get the interaction client
|
|
1936
|
-
this.log.info('Getting the interaction client');
|
|
1937
|
-
const interactionClient = await node.getInteractionClient();
|
|
1938
|
-
let cluster;
|
|
1939
|
-
let attributes;
|
|
1940
|
-
|
|
1941
|
-
// Log BasicInformationCluster
|
|
1942
|
-
cluster = BasicInformationCluster;
|
|
1943
|
-
attributes = await interactionClient.getMultipleAttributes({
|
|
1944
|
-
attributes: [{ clusterId: cluster.id }],
|
|
1945
|
-
});
|
|
1946
|
-
if (attributes.length > 0) this.log.info(`Cluster: ${idn}${cluster.name}${rs}${nf} attributes:`);
|
|
1947
|
-
attributes.forEach((attribute) => {
|
|
1948
|
-
this.log.info(
|
|
1949
|
-
`- 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}`,
|
|
1950
|
-
);
|
|
1951
|
-
});
|
|
1952
|
-
|
|
1953
|
-
// Log PowerSourceCluster
|
|
1954
|
-
cluster = PowerSourceCluster;
|
|
1955
|
-
attributes = await interactionClient.getMultipleAttributes({
|
|
1956
|
-
attributes: [{ clusterId: cluster.id }],
|
|
1957
|
-
});
|
|
1958
|
-
if (attributes.length > 0) this.log.info(`Cluster: ${idn}${cluster.name}${rs}${nf} attributes:`);
|
|
1959
|
-
attributes.forEach((attribute) => {
|
|
1960
|
-
this.log.info(
|
|
1961
|
-
`- 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}`,
|
|
1962
|
-
);
|
|
1963
|
-
});
|
|
1964
|
-
|
|
1965
|
-
// Log ThreadNetworkDiagnostics
|
|
1966
|
-
cluster = ThreadNetworkDiagnosticsCluster;
|
|
1967
|
-
attributes = await interactionClient.getMultipleAttributes({
|
|
1968
|
-
attributes: [{ clusterId: cluster.id }],
|
|
1969
|
-
});
|
|
1970
|
-
if (attributes.length > 0) this.log.info(`Cluster: ${idn}${cluster.name}${rs}${nf} attributes:`);
|
|
1971
|
-
attributes.forEach((attribute) => {
|
|
1972
|
-
this.log.info(
|
|
1973
|
-
`- 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}`,
|
|
1974
|
-
);
|
|
1975
|
-
});
|
|
1976
|
-
|
|
1977
|
-
// Log SwitchCluster
|
|
1978
|
-
cluster = SwitchCluster;
|
|
1979
|
-
attributes = await interactionClient.getMultipleAttributes({
|
|
1980
|
-
attributes: [{ clusterId: cluster.id }],
|
|
1981
|
-
});
|
|
1982
|
-
if (attributes.length > 0) this.log.info(`Cluster: ${idn}${cluster.name}${rs}${nf} attributes:`);
|
|
1983
|
-
attributes.forEach((attribute) => {
|
|
1984
|
-
this.log.info(
|
|
1985
|
-
`- 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}`,
|
|
1986
|
-
);
|
|
1987
|
-
});
|
|
1988
|
-
|
|
1989
|
-
this.log.info('Subscribing to all attributes and events');
|
|
1990
|
-
await node.subscribeAllAttributesAndEvents({
|
|
1991
|
-
ignoreInitialTriggers: false,
|
|
1992
|
-
attributeChangedCallback: ({ path: { nodeId, clusterId, endpointId, attributeName }, version, value }) =>
|
|
1993
|
-
this.log.info(
|
|
1994
|
-
`***${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}`,
|
|
1995
|
-
),
|
|
1996
|
-
eventTriggeredCallback: ({ path: { nodeId, clusterId, endpointId, eventName }, events }) => {
|
|
1997
|
-
this.log.info(
|
|
1998
|
-
`***${db}Commissioning controller eventTriggeredCallback: event ${BLUE}${nodeId}${db}/${or}${endpointId}${db}/${hk}${getClusterNameById(clusterId)}${db}/${zb}${eventName}${db} triggered with ${debugStringify(events ?? { none: true })}`,
|
|
1999
|
-
);
|
|
2000
|
-
},
|
|
2001
|
-
});
|
|
2002
|
-
this.log.info('Subscribed to all attributes and events');
|
|
2003
|
-
}
|
|
2004
|
-
*/
|
|
2005
1418
|
}
|
|
2006
|
-
/** */
|
|
2007
|
-
/** Matter.js methods */
|
|
2008
|
-
/** */
|
|
2009
|
-
/**
|
|
2010
|
-
* Starts the matter storage with name Matterbridge, create the matterbridge context and performs a backup.
|
|
2011
|
-
*
|
|
2012
|
-
* @returns {Promise<void>} - A promise that resolves when the storage is started.
|
|
2013
|
-
*/
|
|
2014
1419
|
async startMatterStorage() {
|
|
2015
|
-
// Setup Matter storage
|
|
2016
1420
|
this.log.info(`Starting matter node storage...`);
|
|
2017
1421
|
this.matterStorageService = this.environment.get(StorageService);
|
|
2018
1422
|
this.log.info(`Matter node storage service created: ${this.matterStorageService.location}`);
|
|
@@ -2020,17 +1424,8 @@ export class Matterbridge extends EventEmitter {
|
|
|
2020
1424
|
this.log.info('Matter node storage manager "Matterbridge" created');
|
|
2021
1425
|
this.matterbridgeContext = await this.createServerNodeContext('Matterbridge', 'Matterbridge', this.aggregatorDeviceType, this.aggregatorVendorId, this.aggregatorVendorName, this.aggregatorProductId, this.aggregatorProductName, this.aggregatorSerialNumber, this.aggregatorUniqueId);
|
|
2022
1426
|
this.log.info('Matter node storage started');
|
|
2023
|
-
// Backup matter storage since it is created/opened correctly
|
|
2024
1427
|
await this.backupMatterStorage(path.join(this.matterbridgeDirectory, MATTER_STORAGE_NAME), path.join(this.matterbridgeDirectory, MATTER_STORAGE_NAME + '.backup'));
|
|
2025
1428
|
}
|
|
2026
|
-
/**
|
|
2027
|
-
* Makes a backup copy of the specified matter storage directory.
|
|
2028
|
-
*
|
|
2029
|
-
* @param {string} storageName - The name of the storage directory to be backed up.
|
|
2030
|
-
* @param {string} backupName - The name of the backup directory to be created.
|
|
2031
|
-
* @private
|
|
2032
|
-
* @returns {Promise<void>} A promise that resolves when the has been done.
|
|
2033
|
-
*/
|
|
2034
1429
|
async backupMatterStorage(storageName, backupName) {
|
|
2035
1430
|
this.log.info('Creating matter node storage backup...');
|
|
2036
1431
|
try {
|
|
@@ -2041,11 +1436,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2041
1436
|
this.log.error(`Error creating matter node storage backup from ${storageName} to ${backupName}:`, error);
|
|
2042
1437
|
}
|
|
2043
1438
|
}
|
|
2044
|
-
/**
|
|
2045
|
-
* Stops the matter storage.
|
|
2046
|
-
*
|
|
2047
|
-
* @returns {Promise<void>} A promise that resolves when the storage is stopped.
|
|
2048
|
-
*/
|
|
2049
1439
|
async stopMatterStorage() {
|
|
2050
1440
|
this.log.info('Closing matter node storage...');
|
|
2051
1441
|
await this.matterStorageManager?.close();
|
|
@@ -2054,20 +1444,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2054
1444
|
this.matterbridgeContext = undefined;
|
|
2055
1445
|
this.log.info('Matter node storage closed');
|
|
2056
1446
|
}
|
|
2057
|
-
/**
|
|
2058
|
-
* Creates a server node storage context.
|
|
2059
|
-
*
|
|
2060
|
-
* @param {string} storeId - The storeId.
|
|
2061
|
-
* @param {string} deviceName - The name of the device.
|
|
2062
|
-
* @param {DeviceTypeId} deviceType - The device type of the device.
|
|
2063
|
-
* @param {number} vendorId - The vendor ID.
|
|
2064
|
-
* @param {string} vendorName - The vendor name.
|
|
2065
|
-
* @param {number} productId - The product ID.
|
|
2066
|
-
* @param {string} productName - The product name.
|
|
2067
|
-
* @param {string} [serialNumber] - The serial number of the device (optional).
|
|
2068
|
-
* @param {string} [uniqueId] - The unique ID of the device (optional).
|
|
2069
|
-
* @returns {Promise<StorageContext>} The storage context for the commissioning server.
|
|
2070
|
-
*/
|
|
2071
1447
|
async createServerNodeContext(storeId, deviceName, deviceType, vendorId, vendorName, productId, productName, serialNumber, uniqueId) {
|
|
2072
1448
|
const { randomBytes } = await import('node:crypto');
|
|
2073
1449
|
if (!this.matterStorageService)
|
|
@@ -2109,15 +1485,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2109
1485
|
this.log.debug(`- hardwareVersionString: ${await storageContext.get('hardwareVersionString')}`);
|
|
2110
1486
|
return storageContext;
|
|
2111
1487
|
}
|
|
2112
|
-
/**
|
|
2113
|
-
* Creates a server node.
|
|
2114
|
-
*
|
|
2115
|
-
* @param {StorageContext} storageContext - The storage context for the server node.
|
|
2116
|
-
* @param {number} [port] - The port number for the server node. Defaults to 5540.
|
|
2117
|
-
* @param {number} [passcode] - The passcode for the server node. Defaults to 20242025.
|
|
2118
|
-
* @param {number} [discriminator] - The discriminator for the server node. Defaults to 3850.
|
|
2119
|
-
* @returns {Promise<ServerNode<ServerNode.RootEndpoint>>} A promise that resolves to the created server node.
|
|
2120
|
-
*/
|
|
2121
1488
|
async createServerNode(storageContext, port = 5540, passcode = 20242025, discriminator = 3850) {
|
|
2122
1489
|
const storeId = await storageContext.get('storeId');
|
|
2123
1490
|
this.log.notice(`Creating server node for ${storeId} on port ${port} with passcode ${passcode} and discriminator ${discriminator}...`);
|
|
@@ -2136,35 +1503,25 @@ export class Matterbridge extends EventEmitter {
|
|
|
2136
1503
|
this.log.debug(`- softwareVersionString: ${await storageContext.get('softwareVersionString')}`);
|
|
2137
1504
|
this.log.debug(`- hardwareVersion: ${await storageContext.get('hardwareVersion')}`);
|
|
2138
1505
|
this.log.debug(`- hardwareVersionString: ${await storageContext.get('hardwareVersionString')}`);
|
|
2139
|
-
/**
|
|
2140
|
-
* Create a Matter ServerNode, which contains the Root Endpoint and all relevant data and configuration
|
|
2141
|
-
*/
|
|
2142
1506
|
const serverNode = await ServerNode.create({
|
|
2143
|
-
// Required: Give the Node a unique ID which is used to store the state of this node
|
|
2144
1507
|
id: storeId,
|
|
2145
|
-
// Environment to run the server node in
|
|
2146
1508
|
environment: this.environment,
|
|
2147
|
-
// Provide Network relevant configuration like the port
|
|
2148
1509
|
network: {
|
|
2149
1510
|
listeningAddressIpv4: this.ipv4Address,
|
|
2150
1511
|
listeningAddressIpv6: this.ipv6Address,
|
|
2151
1512
|
port,
|
|
2152
1513
|
},
|
|
2153
|
-
// Provide the certificate for the device
|
|
2154
1514
|
operationalCredentials: {
|
|
2155
1515
|
certification: this.certification,
|
|
2156
1516
|
},
|
|
2157
|
-
// Provide Commissioning relevant settings
|
|
2158
1517
|
commissioning: {
|
|
2159
1518
|
passcode,
|
|
2160
1519
|
discriminator,
|
|
2161
1520
|
},
|
|
2162
|
-
// Provide Node announcement settings
|
|
2163
1521
|
productDescription: {
|
|
2164
1522
|
name: await storageContext.get('deviceName'),
|
|
2165
1523
|
deviceType: DeviceTypeId(await storageContext.get('deviceType')),
|
|
2166
1524
|
},
|
|
2167
|
-
// Provide defaults for the BasicInformation cluster on the Root endpoint
|
|
2168
1525
|
basicInformation: {
|
|
2169
1526
|
nodeLabel: await storageContext.get('nodeLabel'),
|
|
2170
1527
|
vendorId: VendorId(await storageContext.get('vendorId')),
|
|
@@ -2181,23 +1538,17 @@ export class Matterbridge extends EventEmitter {
|
|
|
2181
1538
|
reachable: true,
|
|
2182
1539
|
},
|
|
2183
1540
|
});
|
|
2184
|
-
/**
|
|
2185
|
-
* This event is triggered when the device is initially commissioned successfully.
|
|
2186
|
-
* This means: It is added to the first fabric.
|
|
2187
|
-
*/
|
|
2188
1541
|
serverNode.lifecycle.commissioned.on(() => {
|
|
2189
1542
|
this.log.notice(`Server node for ${storeId} was initially commissioned successfully!`);
|
|
2190
1543
|
this.advertisingNodes.delete(storeId);
|
|
2191
1544
|
this.frontend.wssSendRefreshRequired('matter', { matter: { ...this.getServerNodeData(serverNode) } });
|
|
2192
1545
|
});
|
|
2193
|
-
/** This event is triggered when all fabrics are removed from the device, usually it also does a factory reset then. */
|
|
2194
1546
|
serverNode.lifecycle.decommissioned.on(() => {
|
|
2195
1547
|
this.log.notice(`Server node for ${storeId} was fully decommissioned successfully!`);
|
|
2196
1548
|
this.advertisingNodes.delete(storeId);
|
|
2197
1549
|
this.frontend.wssSendRefreshRequired('matter', { matter: { ...this.getServerNodeData(serverNode) } });
|
|
2198
1550
|
this.frontend.wssSendSnackbarMessage(`${storeId} is offline`, 5, 'warning');
|
|
2199
1551
|
});
|
|
2200
|
-
/** This event is triggered when the device went online. This means that it is discoverable in the network. */
|
|
2201
1552
|
serverNode.lifecycle.online.on(async () => {
|
|
2202
1553
|
this.log.notice(`Server node for ${storeId} is online`);
|
|
2203
1554
|
if (!serverNode.lifecycle.isCommissioned) {
|
|
@@ -2208,16 +1559,13 @@ export class Matterbridge extends EventEmitter {
|
|
|
2208
1559
|
this.log.notice(`Manual pairing code: ${manualPairingCode}`);
|
|
2209
1560
|
}
|
|
2210
1561
|
else {
|
|
2211
|
-
// istanbul ignore next
|
|
2212
1562
|
this.log.notice(`Server node for ${storeId} is already commissioned. Waiting for controllers to connect ...`);
|
|
2213
|
-
// istanbul ignore next
|
|
2214
1563
|
this.advertisingNodes.delete(storeId);
|
|
2215
1564
|
}
|
|
2216
1565
|
this.frontend.wssSendRefreshRequired('matter', { matter: { ...this.getServerNodeData(serverNode) } });
|
|
2217
1566
|
this.frontend.wssSendSnackbarMessage(`${storeId} is online`, 5, 'success');
|
|
2218
1567
|
this.emit('online', storeId);
|
|
2219
1568
|
});
|
|
2220
|
-
/** This event is triggered when the device went offline. it is not longer discoverable or connectable in the network. */
|
|
2221
1569
|
serverNode.lifecycle.offline.on(() => {
|
|
2222
1570
|
this.log.notice(`Server node for ${storeId} is offline`);
|
|
2223
1571
|
this.advertisingNodes.delete(storeId);
|
|
@@ -2225,15 +1573,11 @@ export class Matterbridge extends EventEmitter {
|
|
|
2225
1573
|
this.frontend.wssSendSnackbarMessage(`${storeId} is offline`, 5, 'warning');
|
|
2226
1574
|
this.emit('offline', storeId);
|
|
2227
1575
|
});
|
|
2228
|
-
/**
|
|
2229
|
-
* This event is triggered when a fabric is added, removed or updated on the device. Use this if more granular
|
|
2230
|
-
* information is needed.
|
|
2231
|
-
*/
|
|
2232
1576
|
serverNode.events.commissioning.fabricsChanged.on((fabricIndex, fabricAction) => {
|
|
2233
1577
|
let action = '';
|
|
2234
1578
|
switch (fabricAction) {
|
|
2235
1579
|
case 'added':
|
|
2236
|
-
this.advertisingNodes.delete(storeId);
|
|
1580
|
+
this.advertisingNodes.delete(storeId);
|
|
2237
1581
|
action = 'added';
|
|
2238
1582
|
break;
|
|
2239
1583
|
case 'deleted':
|
|
@@ -2246,22 +1590,14 @@ export class Matterbridge extends EventEmitter {
|
|
|
2246
1590
|
this.log.notice(`Commissioned fabric index ${fabricIndex} ${action} on server node for ${storeId}: ${debugStringify(serverNode.state.commissioning.fabrics[fabricIndex])}`);
|
|
2247
1591
|
this.frontend.wssSendRefreshRequired('matter', { matter: { ...this.getServerNodeData(serverNode) } });
|
|
2248
1592
|
});
|
|
2249
|
-
/**
|
|
2250
|
-
* This event is triggered when an operative new session was opened by a Controller.
|
|
2251
|
-
* It is not triggered for the initial commissioning process, just afterwards for real connections.
|
|
2252
|
-
*/
|
|
2253
1593
|
serverNode.events.sessions.opened.on((session) => {
|
|
2254
1594
|
this.log.notice(`Session opened on server node for ${storeId}: ${debugStringify(session)}`);
|
|
2255
1595
|
this.frontend.wssSendRefreshRequired('matter', { matter: { ...this.getServerNodeData(serverNode) } });
|
|
2256
1596
|
});
|
|
2257
|
-
/**
|
|
2258
|
-
* This event is triggered when an operative session is closed by a Controller or because the Device goes offline.
|
|
2259
|
-
*/
|
|
2260
1597
|
serverNode.events.sessions.closed.on((session) => {
|
|
2261
1598
|
this.log.notice(`Session closed on server node for ${storeId}: ${debugStringify(session)}`);
|
|
2262
1599
|
this.frontend.wssSendRefreshRequired('matter', { matter: { ...this.getServerNodeData(serverNode) } });
|
|
2263
1600
|
});
|
|
2264
|
-
/** This event is triggered when a subscription gets added or removed on an operative session. */
|
|
2265
1601
|
serverNode.events.sessions.subscriptionsChanged.on((session) => {
|
|
2266
1602
|
this.log.notice(`Session subscriptions changed on server node for ${storeId}: ${debugStringify(session)}`);
|
|
2267
1603
|
this.frontend.wssSendRefreshRequired('matter', { matter: { ...this.getServerNodeData(serverNode) } });
|
|
@@ -2269,12 +1605,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2269
1605
|
this.log.info(`Created server node for ${storeId}`);
|
|
2270
1606
|
return serverNode;
|
|
2271
1607
|
}
|
|
2272
|
-
/**
|
|
2273
|
-
* Gets the matter sanitized data of the specified server node.
|
|
2274
|
-
*
|
|
2275
|
-
* @param {ServerNode} [serverNode] - The server node to start.
|
|
2276
|
-
* @returns {ApiMatter} The sanitized data of the server node.
|
|
2277
|
-
*/
|
|
2278
1608
|
getServerNodeData(serverNode) {
|
|
2279
1609
|
const advertiseTime = this.advertisingNodes.get(serverNode.id) || 0;
|
|
2280
1610
|
return {
|
|
@@ -2291,25 +1621,12 @@ export class Matterbridge extends EventEmitter {
|
|
|
2291
1621
|
serialNumber: serverNode.state.basicInformation.serialNumber,
|
|
2292
1622
|
};
|
|
2293
1623
|
}
|
|
2294
|
-
/**
|
|
2295
|
-
* Starts the specified server node.
|
|
2296
|
-
*
|
|
2297
|
-
* @param {ServerNode} [matterServerNode] - The server node to start.
|
|
2298
|
-
* @returns {Promise<void>} A promise that resolves when the server node has started.
|
|
2299
|
-
*/
|
|
2300
1624
|
async startServerNode(matterServerNode) {
|
|
2301
1625
|
if (!matterServerNode)
|
|
2302
1626
|
return;
|
|
2303
1627
|
this.log.notice(`Starting ${matterServerNode.id} server node`);
|
|
2304
1628
|
await matterServerNode.start();
|
|
2305
1629
|
}
|
|
2306
|
-
/**
|
|
2307
|
-
* Stops the specified server node.
|
|
2308
|
-
*
|
|
2309
|
-
* @param {ServerNode} matterServerNode - The server node to stop.
|
|
2310
|
-
* @param {number} [timeout] - The timeout in milliseconds for stopping the server node. Defaults to 10 seconds.
|
|
2311
|
-
* @returns {Promise<void>} A promise that resolves when the server node has stopped.
|
|
2312
|
-
*/
|
|
2313
1630
|
async stopServerNode(matterServerNode, timeout = 10000) {
|
|
2314
1631
|
const { withTimeout } = await import('@matterbridge/utils');
|
|
2315
1632
|
if (!matterServerNode)
|
|
@@ -2323,25 +1640,12 @@ export class Matterbridge extends EventEmitter {
|
|
|
2323
1640
|
this.log.error(`Failed to close ${matterServerNode.id} server node: ${error instanceof Error ? error.message : error}`);
|
|
2324
1641
|
}
|
|
2325
1642
|
}
|
|
2326
|
-
/**
|
|
2327
|
-
* Creates an aggregator node with the specified storage context.
|
|
2328
|
-
*
|
|
2329
|
-
* @param {StorageContext} storageContext - The storage context for the aggregator node.
|
|
2330
|
-
* @returns {Promise<Endpoint<AggregatorEndpoint>>} A promise that resolves to the created aggregator node.
|
|
2331
|
-
*/
|
|
2332
1643
|
async createAggregatorNode(storageContext) {
|
|
2333
1644
|
this.log.notice(`Creating ${await storageContext.get('storeId')} aggregator...`);
|
|
2334
1645
|
const aggregatorNode = new Endpoint(AggregatorEndpoint, { id: `${await storageContext.get('storeId')}` });
|
|
2335
1646
|
this.log.info(`Created ${await storageContext.get('storeId')} aggregator`);
|
|
2336
1647
|
return aggregatorNode;
|
|
2337
1648
|
}
|
|
2338
|
-
/**
|
|
2339
|
-
* Creates and configures the server node for an accessory plugin for a given device.
|
|
2340
|
-
*
|
|
2341
|
-
* @param {Plugin} plugin - The plugin to configure.
|
|
2342
|
-
* @param {MatterbridgeEndpoint} device - The device to associate with the plugin.
|
|
2343
|
-
* @returns {Promise<void>} A promise that resolves when the server node for the accessory plugin is created and configured.
|
|
2344
|
-
*/
|
|
2345
1649
|
async createAccessoryPlugin(plugin, device) {
|
|
2346
1650
|
if (!plugin.locked && device.deviceType && device.deviceName && device.vendorId && device.productId && device.vendorName && device.productName) {
|
|
2347
1651
|
plugin.locked = true;
|
|
@@ -2353,12 +1657,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2353
1657
|
await plugin.serverNode.add(device);
|
|
2354
1658
|
}
|
|
2355
1659
|
}
|
|
2356
|
-
/**
|
|
2357
|
-
* Creates and configures the server node and the aggregator node for a dynamic plugin.
|
|
2358
|
-
*
|
|
2359
|
-
* @param {Plugin} plugin - The plugin to configure.
|
|
2360
|
-
* @returns {Promise<void>} A promise that resolves when the server node and the aggregator node for the dynamic plugin is created and configured.
|
|
2361
|
-
*/
|
|
2362
1660
|
async createDynamicPlugin(plugin) {
|
|
2363
1661
|
if (!plugin.locked) {
|
|
2364
1662
|
plugin.locked = true;
|
|
@@ -2371,13 +1669,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2371
1669
|
await plugin.serverNode.add(plugin.aggregatorNode);
|
|
2372
1670
|
}
|
|
2373
1671
|
}
|
|
2374
|
-
/**
|
|
2375
|
-
* Creates and configures the server node for a single not bridged device.
|
|
2376
|
-
*
|
|
2377
|
-
* @param {Plugin} plugin - The plugin to configure.
|
|
2378
|
-
* @param {MatterbridgeEndpoint} device - The device to associate with the plugin.
|
|
2379
|
-
* @returns {Promise<void>} A promise that resolves when the server node for the accessory plugin is created and configured.
|
|
2380
|
-
*/
|
|
2381
1672
|
async createDeviceServerNode(plugin, device) {
|
|
2382
1673
|
if (device.mode === 'server' && !device.serverNode && device.deviceType && device.deviceName && device.vendorId && device.vendorName && device.productId && device.productName) {
|
|
2383
1674
|
this.log.debug(`Creating device ${plg}${plugin.name}${db}:${dev}${device.deviceName}${db} server node...`);
|
|
@@ -2388,16 +1679,8 @@ export class Matterbridge extends EventEmitter {
|
|
|
2388
1679
|
this.log.debug(`Added ${plg}${plugin.name}${db}:${dev}${device.deviceName}${db} to server node`);
|
|
2389
1680
|
}
|
|
2390
1681
|
}
|
|
2391
|
-
/**
|
|
2392
|
-
* Adds a MatterbridgeEndpoint to the specified plugin.
|
|
2393
|
-
*
|
|
2394
|
-
* @param {string} pluginName - The name of the plugin.
|
|
2395
|
-
* @param {MatterbridgeEndpoint} device - The device to add as a bridged endpoint.
|
|
2396
|
-
* @returns {Promise<void>} A promise that resolves when the bridged endpoint has been added.
|
|
2397
|
-
*/
|
|
2398
1682
|
async addBridgedEndpoint(pluginName, device) {
|
|
2399
1683
|
const { waiter } = await import('@matterbridge/utils');
|
|
2400
|
-
// Check if the plugin is registered
|
|
2401
1684
|
const plugin = this.plugins.get(pluginName);
|
|
2402
1685
|
if (!plugin) {
|
|
2403
1686
|
this.log.error(`Error adding bridged endpoint ${dev}${device.deviceName}${er} (${zb}${device.id}${er}) plugin ${plg}${pluginName}${er} not found`);
|
|
@@ -2417,7 +1700,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2417
1700
|
}
|
|
2418
1701
|
else if (this.bridgeMode === 'bridge') {
|
|
2419
1702
|
if (device.mode === 'matter') {
|
|
2420
|
-
// Register and add the device to the matterbridge server node
|
|
2421
1703
|
this.log.debug(`Adding matter endpoint ${plg}${pluginName}${db}:${dev}${device.deviceName}${db} to Matterbridge server node...`);
|
|
2422
1704
|
if (!this.serverNode) {
|
|
2423
1705
|
this.log.error('Server node not found for Matterbridge');
|
|
@@ -2434,7 +1716,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2434
1716
|
}
|
|
2435
1717
|
}
|
|
2436
1718
|
else {
|
|
2437
|
-
// Register and add the device to the matterbridge aggregator node
|
|
2438
1719
|
this.log.debug(`Adding bridged endpoint ${plg}${pluginName}${db}:${dev}${device.deviceName}${db} to Matterbridge aggregator node`);
|
|
2439
1720
|
if (!this.aggregatorNode) {
|
|
2440
1721
|
this.log.error('Aggregator node not found for Matterbridge');
|
|
@@ -2452,7 +1733,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2452
1733
|
}
|
|
2453
1734
|
}
|
|
2454
1735
|
else if (this.bridgeMode === 'childbridge') {
|
|
2455
|
-
// Register and add the device to the plugin server node
|
|
2456
1736
|
if (plugin.type === 'AccessoryPlatform') {
|
|
2457
1737
|
try {
|
|
2458
1738
|
this.log.debug(`Creating endpoint ${dev}${device.deviceName}${db} for AccessoryPlatform plugin ${plg}${plugin.name}${db} server node`);
|
|
@@ -2476,12 +1756,10 @@ export class Matterbridge extends EventEmitter {
|
|
|
2476
1756
|
return;
|
|
2477
1757
|
}
|
|
2478
1758
|
}
|
|
2479
|
-
// Register and add the device to the plugin aggregator node
|
|
2480
1759
|
if (plugin.type === 'DynamicPlatform') {
|
|
2481
1760
|
try {
|
|
2482
1761
|
this.log.debug(`Adding bridged endpoint ${dev}${device.deviceName}${db} for DynamicPlatform plugin ${plg}${plugin.name}${db} aggregator node`);
|
|
2483
1762
|
await this.createDynamicPlugin(plugin);
|
|
2484
|
-
// Fast plugins can add another device before the server node is ready, so we wait for the server node to be ready
|
|
2485
1763
|
await waiter(`createDynamicPlugin(${plugin.name})`, () => plugin.serverNode?.hasParts === true);
|
|
2486
1764
|
if (!plugin.aggregatorNode) {
|
|
2487
1765
|
this.log.error(`Aggregator node not found for plugin ${plg}${plugin.name}${er}`);
|
|
@@ -2502,22 +1780,12 @@ export class Matterbridge extends EventEmitter {
|
|
|
2502
1780
|
}
|
|
2503
1781
|
if (plugin.registeredDevices !== undefined)
|
|
2504
1782
|
plugin.registeredDevices++;
|
|
2505
|
-
// Add the device to the DeviceManager
|
|
2506
1783
|
this.devices.set(device);
|
|
2507
|
-
// Subscribe to the attributes changed event
|
|
2508
1784
|
await this.subscribeAttributeChanged(plugin, device);
|
|
2509
1785
|
this.log.info(`Added and registered bridged endpoint (${plugin.registeredDevices}) ${dev}${device.deviceName}${nf} (${dev}${device.id}${nf}) for plugin ${plg}${pluginName}${nf}`);
|
|
2510
1786
|
}
|
|
2511
|
-
/**
|
|
2512
|
-
* Removes a MatterbridgeEndpoint from the specified plugin.
|
|
2513
|
-
*
|
|
2514
|
-
* @param {string} pluginName - The name of the plugin.
|
|
2515
|
-
* @param {MatterbridgeEndpoint} device - The device to remove as a bridged endpoint.
|
|
2516
|
-
* @returns {Promise<void>} A promise that resolves when the bridged endpoint has been removed.
|
|
2517
|
-
*/
|
|
2518
1787
|
async removeBridgedEndpoint(pluginName, device) {
|
|
2519
1788
|
this.log.debug(`Removing bridged endpoint ${plg}${pluginName}${db}:${dev}${device.deviceName}${db} (${zb}${device.name}${db}) for plugin ${plg}${pluginName}${db}`);
|
|
2520
|
-
// Check if the plugin is registered
|
|
2521
1789
|
const plugin = this.plugins.get(pluginName);
|
|
2522
1790
|
if (!plugin) {
|
|
2523
1791
|
this.log.error(`Error removing bridged endpoint ${dev}${device.deviceName}${er} (${zb}${device.name}${er}) for plugin ${plg}${pluginName}${er}: plugin not found`);
|
|
@@ -2527,7 +1795,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2527
1795
|
this.log.info(`Removed mode server bridged endpoint(${plugin.registeredDevices}) ${dev}${device.deviceName}${nf} (${zb}${device.name}${nf}) for plugin ${plg}${pluginName}${nf}`);
|
|
2528
1796
|
}
|
|
2529
1797
|
else if (this.bridgeMode === 'bridge') {
|
|
2530
|
-
// Unregister and remove the device from the matterbridge aggregator node
|
|
2531
1798
|
if (!this.aggregatorNode) {
|
|
2532
1799
|
this.log.error(`Error removing bridged endpoint ${dev}${device.deviceName}${er} (${zb}${device.name}${er}) for plugin ${plg}${pluginName}${er}: aggregator node not found`);
|
|
2533
1800
|
return;
|
|
@@ -2539,10 +1806,8 @@ export class Matterbridge extends EventEmitter {
|
|
|
2539
1806
|
}
|
|
2540
1807
|
else if (this.bridgeMode === 'childbridge') {
|
|
2541
1808
|
if (plugin.type === 'AccessoryPlatform') {
|
|
2542
|
-
// Nothing to do here since the server node has no aggregator node but only the device itself
|
|
2543
1809
|
}
|
|
2544
1810
|
else if (plugin.type === 'DynamicPlatform') {
|
|
2545
|
-
// Unregister and remove the device from the plugin aggregator node
|
|
2546
1811
|
if (!plugin.aggregatorNode) {
|
|
2547
1812
|
this.log.error(`Error removing bridged endpoint ${dev}${device.deviceName}${er} (${zb}${device.name}${er}) for plugin ${plg}${pluginName}${er}: aggregator node not found`);
|
|
2548
1813
|
return;
|
|
@@ -2553,21 +1818,8 @@ export class Matterbridge extends EventEmitter {
|
|
|
2553
1818
|
if (plugin.registeredDevices !== undefined)
|
|
2554
1819
|
plugin.registeredDevices--;
|
|
2555
1820
|
}
|
|
2556
|
-
// Remove the device from the DeviceManager
|
|
2557
1821
|
this.devices.remove(device);
|
|
2558
1822
|
}
|
|
2559
|
-
/**
|
|
2560
|
-
* Removes all bridged endpoints from the specified plugin.
|
|
2561
|
-
*
|
|
2562
|
-
* @param {string} pluginName - The name of the plugin.
|
|
2563
|
-
* @param {number} [delay] - The delay in milliseconds between removing each bridged endpoint (default: 0).
|
|
2564
|
-
* @returns {Promise<void>} A promise that resolves when all bridged endpoints have been removed.
|
|
2565
|
-
*
|
|
2566
|
-
* @remarks
|
|
2567
|
-
* This method iterates through all devices in the DeviceManager and removes each bridged endpoint associated with the specified plugin.
|
|
2568
|
-
* It also applies a delay between each removal if specified.
|
|
2569
|
-
* The delay is useful to allow the controllers to receive a single subscription for each device removed.
|
|
2570
|
-
*/
|
|
2571
1823
|
async removeAllBridgedEndpoints(pluginName, delay = 0) {
|
|
2572
1824
|
const { wait } = await import('@matterbridge/utils');
|
|
2573
1825
|
this.log.debug(`Removing all bridged endpoints for plugin ${plg}${pluginName}${db}${delay > 0 ? ` with delay ${delay} ms` : ''}`);
|
|
@@ -2579,28 +1831,8 @@ export class Matterbridge extends EventEmitter {
|
|
|
2579
1831
|
if (delay > 0)
|
|
2580
1832
|
await wait(2000);
|
|
2581
1833
|
}
|
|
2582
|
-
/**
|
|
2583
|
-
* Registers a virtual device.
|
|
2584
|
-
* Virtual devices are only supported in bridge mode and childbridge mode with a DynamicPlatform.
|
|
2585
|
-
*
|
|
2586
|
-
* The virtual device is created as an instance of `Endpoint` with the provided device type.
|
|
2587
|
-
* When the virtual device is turned on, the provided callback function is executed.
|
|
2588
|
-
* The onOff state of the virtual device always reverts to false when the device is turned on.
|
|
2589
|
-
*
|
|
2590
|
-
* @param { string } pluginName - The name of the plugin to register the virtual device under.
|
|
2591
|
-
* @param { string } name - The name of the virtual device.
|
|
2592
|
-
* @param { 'light' | 'outlet' | 'switch' | 'mounted_switch' } type - The type of the virtual device.
|
|
2593
|
-
* @param { () => Promise<void> } callback - The callback to call when the virtual device is turned on.
|
|
2594
|
-
*
|
|
2595
|
-
* @returns {Promise<boolean>} A promise that resolves to true if the virtual device was successfully registered, false otherwise.
|
|
2596
|
-
*
|
|
2597
|
-
* @remarks
|
|
2598
|
-
* The virtual devices don't show up in the device list of the frontend.
|
|
2599
|
-
* Type 'switch' is not supported by Alexa and 'mounted_switch' is not supported by Apple Home.
|
|
2600
|
-
*/
|
|
2601
1834
|
async addVirtualEndpoint(pluginName, name, type, callback) {
|
|
2602
1835
|
this.log.debug(`Adding virtual endpoint ${plg}${pluginName}${db}:${dev}${name}${db}...`);
|
|
2603
|
-
// Check if the plugin is registered
|
|
2604
1836
|
const plugin = this.plugins.get(pluginName);
|
|
2605
1837
|
if (!plugin) {
|
|
2606
1838
|
this.log.error(`Error adding virtual endpoint ${dev}${name}${er} for plugin ${plg}${pluginName}${er}: plugin not found`);
|
|
@@ -2627,24 +1859,13 @@ export class Matterbridge extends EventEmitter {
|
|
|
2627
1859
|
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.`);
|
|
2628
1860
|
return false;
|
|
2629
1861
|
}
|
|
2630
|
-
/**
|
|
2631
|
-
* Subscribes to the attribute change event for the given device and plugin.
|
|
2632
|
-
* Specifically, it listens for changes in the 'reachable' attribute of the
|
|
2633
|
-
* BridgedDeviceBasicInformationServer cluster server of the bridged device or BasicInformationServer cluster server of server node.
|
|
2634
|
-
*
|
|
2635
|
-
* @param {Plugin} plugin - The plugin associated with the device.
|
|
2636
|
-
* @param {MatterbridgeEndpoint} device - The device to subscribe to attribute changes for.
|
|
2637
|
-
* @returns {Promise<void>} A promise that resolves when the subscription is set up.
|
|
2638
|
-
*/
|
|
2639
1862
|
async subscribeAttributeChanged(plugin, device) {
|
|
2640
1863
|
if (!plugin || !device || !device.plugin || !device.serialNumber || !device.uniqueId || !device.maybeNumber)
|
|
2641
1864
|
return;
|
|
2642
1865
|
this.log.info(`Subscribing attributes for endpoint ${dev}${device.deviceName}${nf} (${dev}${device.id}${nf}) plugin ${plg}${plugin.name}${nf}`);
|
|
2643
|
-
// Subscribe to the reachable$Changed event of the BasicInformationServer cluster server of the server node in childbridge mode
|
|
2644
1866
|
if (this.bridgeMode === 'childbridge' && plugin.type === 'AccessoryPlatform' && plugin.serverNode) {
|
|
2645
1867
|
plugin.serverNode.eventsOf(BasicInformationServer).reachable$Changed?.on((reachable) => {
|
|
2646
1868
|
this.log.info(`Accessory endpoint ${dev}${device.deviceName}${nf} (${dev}${device.id}${nf}) is ${reachable ? 'reachable' : 'unreachable'}`);
|
|
2647
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
2648
1869
|
this.frontend.wssSendAttributeChangedMessage(device.plugin, device.serialNumber, device.uniqueId, device.number, device.id, 'BasicInformation', 'reachable', reachable);
|
|
2649
1870
|
});
|
|
2650
1871
|
}
|
|
@@ -2694,7 +1915,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2694
1915
|
this.log.debug(`Subscribing to endpoint ${or}${device.id}${db}:${or}${device.number}${db} attribute ${dev}${sub.cluster}${db}.${dev}${sub.attribute}${db} changes...`);
|
|
2695
1916
|
await device.subscribeAttribute(sub.cluster, sub.attribute, (value) => {
|
|
2696
1917
|
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}`);
|
|
2697
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
2698
1918
|
this.frontend.wssSendAttributeChangedMessage(device.plugin, device.serialNumber, device.uniqueId, device.number, device.id, sub.cluster, sub.attribute, value);
|
|
2699
1919
|
});
|
|
2700
1920
|
}
|
|
@@ -2703,19 +1923,12 @@ export class Matterbridge extends EventEmitter {
|
|
|
2703
1923
|
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...`);
|
|
2704
1924
|
await child.subscribeAttribute(sub.cluster, sub.attribute, (value) => {
|
|
2705
1925
|
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}`);
|
|
2706
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
2707
1926
|
this.frontend.wssSendAttributeChangedMessage(device.plugin, device.serialNumber, device.uniqueId, child.number, child.id, sub.cluster, sub.attribute, value);
|
|
2708
1927
|
});
|
|
2709
1928
|
}
|
|
2710
1929
|
}
|
|
2711
1930
|
}
|
|
2712
1931
|
}
|
|
2713
|
-
/**
|
|
2714
|
-
* Sanitizes the fabric information by converting bigint properties to strings because `res.json` doesn't support bigint.
|
|
2715
|
-
*
|
|
2716
|
-
* @param {ExposedFabricInformation[]} fabricInfo - The array of exposed fabric information objects.
|
|
2717
|
-
* @returns {SanitizedExposedFabricInformation[]} An array of sanitized exposed fabric information objects.
|
|
2718
|
-
*/
|
|
2719
1932
|
sanitizeFabricInformations(fabricInfo) {
|
|
2720
1933
|
return fabricInfo.map((info) => {
|
|
2721
1934
|
return {
|
|
@@ -2729,12 +1942,6 @@ export class Matterbridge extends EventEmitter {
|
|
|
2729
1942
|
};
|
|
2730
1943
|
});
|
|
2731
1944
|
}
|
|
2732
|
-
/**
|
|
2733
|
-
* Sanitizes the session information by converting bigint properties to strings because `res.json` doesn't support bigint.
|
|
2734
|
-
*
|
|
2735
|
-
* @param {SessionsBehavior.Session[]} sessions - The array of session information objects.
|
|
2736
|
-
* @returns {SanitizedSession[]} An array of sanitized session information objects.
|
|
2737
|
-
*/
|
|
2738
1945
|
sanitizeSessionInformation(sessions) {
|
|
2739
1946
|
return sessions
|
|
2740
1947
|
.filter((session) => session.isPeerActive)
|
|
@@ -2761,21 +1968,7 @@ export class Matterbridge extends EventEmitter {
|
|
|
2761
1968
|
};
|
|
2762
1969
|
});
|
|
2763
1970
|
}
|
|
2764
|
-
/**
|
|
2765
|
-
* Sets the reachability of the specified aggregator node bridged devices and trigger.
|
|
2766
|
-
*
|
|
2767
|
-
* @param {Endpoint<AggregatorEndpoint>} aggregatorNode - The aggregator node to set the reachability for.
|
|
2768
|
-
* @param {boolean} reachable - A boolean indicating the reachability status to set.
|
|
2769
|
-
*/
|
|
2770
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
2771
1971
|
async setAggregatorReachability(aggregatorNode, reachable) {
|
|
2772
|
-
/*
|
|
2773
|
-
for (const child of aggregatorNode.parts) {
|
|
2774
|
-
this.log.debug(`Setting reachability of ${(child as unknown as MatterbridgeEndpoint)?.deviceName} to ${reachable}`);
|
|
2775
|
-
await child.setStateOf(BridgedDeviceBasicInformationServer, { reachable });
|
|
2776
|
-
child.act((agent) => child.eventsOf(BridgedDeviceBasicInformationServer).reachableChanged.emit({ reachableNewValue: true }, agent.context));
|
|
2777
|
-
}
|
|
2778
|
-
*/
|
|
2779
1972
|
}
|
|
2780
1973
|
getVendorIdName = (vendorId) => {
|
|
2781
1974
|
if (!vendorId)
|
|
@@ -2815,11 +2008,10 @@ export class Matterbridge extends EventEmitter {
|
|
|
2815
2008
|
case 0x1488:
|
|
2816
2009
|
vendorName = '(ShortcutLabsFlic)';
|
|
2817
2010
|
break;
|
|
2818
|
-
case 65521:
|
|
2011
|
+
case 65521:
|
|
2819
2012
|
vendorName = '(MatterTest)';
|
|
2820
2013
|
break;
|
|
2821
2014
|
}
|
|
2822
2015
|
return vendorName;
|
|
2823
2016
|
};
|
|
2824
2017
|
}
|
|
2825
|
-
//# sourceMappingURL=matterbridge.js.map
|