matterbridge 3.5.0 → 3.5.1-dev-20260122-6461be3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +18 -0
- package/README-DOCKER.md +4 -2
- package/README.md +4 -1
- package/bin/mb_coap.js +1 -1
- package/bin/mb_mdns.js +1 -1
- package/dist/broadcastServer.d.ts +0 -115
- package/dist/broadcastServer.js +1 -119
- package/dist/broadcastServerTypes.d.ts +0 -43
- package/dist/broadcastServerTypes.js +0 -24
- package/dist/cli.d.ts +1 -26
- package/dist/cli.js +2 -102
- package/dist/cliEmitter.d.ts +0 -36
- package/dist/cliEmitter.js +0 -37
- package/dist/cliHistory.d.ts +0 -42
- package/dist/cliHistory.js +1 -39
- package/dist/clusters/export.d.ts +0 -1
- package/dist/clusters/export.js +0 -2
- package/dist/deviceManager.d.ts +0 -108
- package/dist/deviceManager.js +2 -114
- package/dist/devices/airConditioner.d.ts +0 -75
- package/dist/devices/airConditioner.js +0 -57
- package/dist/devices/batteryStorage.d.ts +0 -43
- package/dist/devices/batteryStorage.js +1 -48
- package/dist/devices/cooktop.d.ts +0 -55
- package/dist/devices/cooktop.js +0 -56
- package/dist/devices/dishwasher.d.ts +0 -55
- package/dist/devices/dishwasher.js +0 -57
- package/dist/devices/evse.d.ts +0 -57
- package/dist/devices/evse.js +10 -74
- package/dist/devices/export.d.ts +0 -1
- package/dist/devices/export.js +0 -5
- package/dist/devices/extractorHood.d.ts +0 -41
- package/dist/devices/extractorHood.js +0 -43
- package/dist/devices/heatPump.d.ts +0 -43
- package/dist/devices/heatPump.js +2 -50
- package/dist/devices/laundryDryer.d.ts +0 -58
- package/dist/devices/laundryDryer.js +3 -62
- package/dist/devices/laundryWasher.d.ts +0 -64
- package/dist/devices/laundryWasher.js +4 -70
- package/dist/devices/microwaveOven.d.ts +1 -77
- package/dist/devices/microwaveOven.js +5 -88
- package/dist/devices/oven.d.ts +0 -82
- package/dist/devices/oven.js +0 -85
- package/dist/devices/refrigerator.d.ts +0 -100
- package/dist/devices/refrigerator.js +0 -102
- package/dist/devices/roboticVacuumCleaner.d.ts +0 -83
- package/dist/devices/roboticVacuumCleaner.js +9 -100
- package/dist/devices/solarPower.d.ts +0 -36
- package/dist/devices/solarPower.js +0 -38
- package/dist/devices/speaker.d.ts +0 -79
- package/dist/devices/speaker.js +0 -84
- package/dist/devices/temperatureControl.d.ts +0 -21
- package/dist/devices/temperatureControl.js +3 -24
- package/dist/devices/waterHeater.d.ts +0 -74
- package/dist/devices/waterHeater.js +2 -82
- package/dist/frontend.d.ts +4 -187
- package/dist/frontend.js +89 -505
- package/dist/frontendTypes.d.ts +0 -57
- package/dist/frontendTypes.js +0 -45
- package/dist/helpers.d.ts +0 -43
- package/dist/helpers.js +1 -54
- package/dist/index.d.ts +0 -23
- package/dist/index.js +0 -25
- package/dist/jestutils/export.d.ts +0 -1
- package/dist/jestutils/export.js +0 -1
- package/dist/jestutils/jestHelpers.d.ts +0 -255
- package/dist/jestutils/jestHelpers.js +16 -379
- package/dist/logger/export.d.ts +0 -1
- package/dist/logger/export.js +0 -1
- package/dist/matter/behaviors.d.ts +0 -1
- package/dist/matter/behaviors.js +0 -2
- package/dist/matter/clusters.d.ts +0 -1
- package/dist/matter/clusters.js +0 -2
- package/dist/matter/devices.d.ts +0 -1
- package/dist/matter/devices.js +0 -2
- package/dist/matter/endpoints.d.ts +0 -1
- package/dist/matter/endpoints.js +0 -2
- package/dist/matter/export.d.ts +0 -1
- package/dist/matter/export.js +0 -2
- package/dist/matter/types.d.ts +0 -1
- package/dist/matter/types.js +0 -2
- package/dist/matterNode.d.ts +0 -258
- package/dist/matterNode.js +9 -364
- package/dist/matterbridge.d.ts +0 -362
- package/dist/matterbridge.js +75 -864
- package/dist/matterbridgeAccessoryPlatform.d.ts +0 -36
- package/dist/matterbridgeAccessoryPlatform.js +0 -38
- package/dist/matterbridgeBehaviors.d.ts +0 -24
- package/dist/matterbridgeBehaviors.js +5 -68
- package/dist/matterbridgeDeviceTypes.d.ts +0 -649
- package/dist/matterbridgeDeviceTypes.js +6 -673
- package/dist/matterbridgeDynamicPlatform.d.ts +0 -36
- package/dist/matterbridgeDynamicPlatform.js +0 -38
- package/dist/matterbridgeEndpoint.d.ts +2 -1332
- package/dist/matterbridgeEndpoint.js +94 -1459
- package/dist/matterbridgeEndpointHelpers.d.ts +0 -425
- package/dist/matterbridgeEndpointHelpers.js +22 -487
- package/dist/matterbridgeEndpointTypes.d.ts +0 -70
- package/dist/matterbridgeEndpointTypes.js +0 -25
- package/dist/matterbridgePlatform.d.ts +0 -425
- package/dist/matterbridgePlatform.js +2 -453
- package/dist/matterbridgeTypes.d.ts +0 -46
- package/dist/matterbridgeTypes.js +0 -26
- package/dist/mb_coap.d.ts +1 -0
- package/dist/{dgram/mb_coap.js → mb_coap.js} +3 -41
- package/dist/mb_mdns.d.ts +1 -0
- package/dist/{dgram/mb_mdns.js → mb_mdns.js} +47 -81
- package/dist/pluginManager.d.ts +0 -305
- package/dist/pluginManager.js +8 -345
- package/dist/shelly.d.ts +0 -157
- package/dist/shelly.js +7 -178
- package/dist/spawn.d.ts +1 -0
- package/dist/{utils/spawn.js → spawn.js} +3 -73
- package/dist/storage/export.d.ts +0 -1
- package/dist/storage/export.js +0 -1
- package/dist/update.d.ts +0 -75
- package/dist/update.js +7 -100
- package/dist/utils/export.d.ts +1 -13
- package/dist/utils/export.js +1 -13
- package/dist/workerGlobalPrefix.d.ts +0 -24
- package/dist/workerGlobalPrefix.js +6 -40
- package/dist/workerTypes.d.ts +0 -25
- package/dist/workerTypes.js +0 -24
- package/dist/workers.d.ts +0 -61
- package/dist/workers.js +4 -68
- package/npm-shrinkwrap.json +80 -50
- package/package.json +8 -8
- package/dist/broadcastServer.d.ts.map +0 -1
- package/dist/broadcastServer.js.map +0 -1
- package/dist/broadcastServerTypes.d.ts.map +0 -1
- package/dist/broadcastServerTypes.js.map +0 -1
- package/dist/cli.d.ts.map +0 -1
- package/dist/cli.js.map +0 -1
- package/dist/cliEmitter.d.ts.map +0 -1
- package/dist/cliEmitter.js.map +0 -1
- package/dist/cliHistory.d.ts.map +0 -1
- package/dist/cliHistory.js.map +0 -1
- package/dist/clusters/export.d.ts.map +0 -1
- package/dist/clusters/export.js.map +0 -1
- package/dist/deviceManager.d.ts.map +0 -1
- package/dist/deviceManager.js.map +0 -1
- package/dist/devices/airConditioner.d.ts.map +0 -1
- package/dist/devices/airConditioner.js.map +0 -1
- package/dist/devices/batteryStorage.d.ts.map +0 -1
- package/dist/devices/batteryStorage.js.map +0 -1
- package/dist/devices/cooktop.d.ts.map +0 -1
- package/dist/devices/cooktop.js.map +0 -1
- package/dist/devices/dishwasher.d.ts.map +0 -1
- package/dist/devices/dishwasher.js.map +0 -1
- package/dist/devices/evse.d.ts.map +0 -1
- package/dist/devices/evse.js.map +0 -1
- package/dist/devices/export.d.ts.map +0 -1
- package/dist/devices/export.js.map +0 -1
- package/dist/devices/extractorHood.d.ts.map +0 -1
- package/dist/devices/extractorHood.js.map +0 -1
- package/dist/devices/heatPump.d.ts.map +0 -1
- package/dist/devices/heatPump.js.map +0 -1
- package/dist/devices/laundryDryer.d.ts.map +0 -1
- package/dist/devices/laundryDryer.js.map +0 -1
- package/dist/devices/laundryWasher.d.ts.map +0 -1
- package/dist/devices/laundryWasher.js.map +0 -1
- package/dist/devices/microwaveOven.d.ts.map +0 -1
- package/dist/devices/microwaveOven.js.map +0 -1
- package/dist/devices/oven.d.ts.map +0 -1
- package/dist/devices/oven.js.map +0 -1
- package/dist/devices/refrigerator.d.ts.map +0 -1
- package/dist/devices/refrigerator.js.map +0 -1
- package/dist/devices/roboticVacuumCleaner.d.ts.map +0 -1
- package/dist/devices/roboticVacuumCleaner.js.map +0 -1
- package/dist/devices/solarPower.d.ts.map +0 -1
- package/dist/devices/solarPower.js.map +0 -1
- package/dist/devices/speaker.d.ts.map +0 -1
- package/dist/devices/speaker.js.map +0 -1
- package/dist/devices/temperatureControl.d.ts.map +0 -1
- package/dist/devices/temperatureControl.js.map +0 -1
- package/dist/devices/waterHeater.d.ts.map +0 -1
- package/dist/devices/waterHeater.js.map +0 -1
- package/dist/dgram/coap.d.ts +0 -205
- package/dist/dgram/coap.d.ts.map +0 -1
- package/dist/dgram/coap.js +0 -365
- package/dist/dgram/coap.js.map +0 -1
- package/dist/dgram/dgram.d.ts +0 -144
- package/dist/dgram/dgram.d.ts.map +0 -1
- package/dist/dgram/dgram.js +0 -363
- package/dist/dgram/dgram.js.map +0 -1
- package/dist/dgram/mb_coap.d.ts +0 -24
- package/dist/dgram/mb_coap.d.ts.map +0 -1
- package/dist/dgram/mb_coap.js.map +0 -1
- package/dist/dgram/mb_mdns.d.ts +0 -24
- package/dist/dgram/mb_mdns.d.ts.map +0 -1
- package/dist/dgram/mb_mdns.js.map +0 -1
- package/dist/dgram/mdns.d.ts +0 -371
- package/dist/dgram/mdns.d.ts.map +0 -1
- package/dist/dgram/mdns.js +0 -934
- package/dist/dgram/mdns.js.map +0 -1
- package/dist/dgram/multicast.d.ts +0 -67
- package/dist/dgram/multicast.d.ts.map +0 -1
- package/dist/dgram/multicast.js +0 -179
- package/dist/dgram/multicast.js.map +0 -1
- package/dist/dgram/unicast.d.ts +0 -64
- package/dist/dgram/unicast.d.ts.map +0 -1
- package/dist/dgram/unicast.js +0 -100
- package/dist/dgram/unicast.js.map +0 -1
- package/dist/frontend.d.ts.map +0 -1
- package/dist/frontend.js.map +0 -1
- package/dist/frontendTypes.d.ts.map +0 -1
- package/dist/frontendTypes.js.map +0 -1
- package/dist/helpers.d.ts.map +0 -1
- package/dist/helpers.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/jestutils/export.d.ts.map +0 -1
- package/dist/jestutils/export.js.map +0 -1
- package/dist/jestutils/jestHelpers.d.ts.map +0 -1
- package/dist/jestutils/jestHelpers.js.map +0 -1
- package/dist/logger/export.d.ts.map +0 -1
- package/dist/logger/export.js.map +0 -1
- package/dist/matter/behaviors.d.ts.map +0 -1
- package/dist/matter/behaviors.js.map +0 -1
- package/dist/matter/clusters.d.ts.map +0 -1
- package/dist/matter/clusters.js.map +0 -1
- package/dist/matter/devices.d.ts.map +0 -1
- package/dist/matter/devices.js.map +0 -1
- package/dist/matter/endpoints.d.ts.map +0 -1
- package/dist/matter/endpoints.js.map +0 -1
- package/dist/matter/export.d.ts.map +0 -1
- package/dist/matter/export.js.map +0 -1
- package/dist/matter/types.d.ts.map +0 -1
- package/dist/matter/types.js.map +0 -1
- package/dist/matterNode.d.ts.map +0 -1
- package/dist/matterNode.js.map +0 -1
- package/dist/matterbridge.d.ts.map +0 -1
- package/dist/matterbridge.js.map +0 -1
- package/dist/matterbridgeAccessoryPlatform.d.ts.map +0 -1
- package/dist/matterbridgeAccessoryPlatform.js.map +0 -1
- package/dist/matterbridgeBehaviors.d.ts.map +0 -1
- package/dist/matterbridgeBehaviors.js.map +0 -1
- package/dist/matterbridgeDeviceTypes.d.ts.map +0 -1
- package/dist/matterbridgeDeviceTypes.js.map +0 -1
- package/dist/matterbridgeDynamicPlatform.d.ts.map +0 -1
- package/dist/matterbridgeDynamicPlatform.js.map +0 -1
- package/dist/matterbridgeEndpoint.d.ts.map +0 -1
- package/dist/matterbridgeEndpoint.js.map +0 -1
- package/dist/matterbridgeEndpointHelpers.d.ts.map +0 -1
- package/dist/matterbridgeEndpointHelpers.js.map +0 -1
- package/dist/matterbridgeEndpointTypes.d.ts.map +0 -1
- package/dist/matterbridgeEndpointTypes.js.map +0 -1
- package/dist/matterbridgePlatform.d.ts.map +0 -1
- package/dist/matterbridgePlatform.js.map +0 -1
- package/dist/matterbridgeTypes.d.ts.map +0 -1
- package/dist/matterbridgeTypes.js.map +0 -1
- package/dist/pluginManager.d.ts.map +0 -1
- package/dist/pluginManager.js.map +0 -1
- package/dist/shelly.d.ts.map +0 -1
- package/dist/shelly.js.map +0 -1
- package/dist/storage/export.d.ts.map +0 -1
- package/dist/storage/export.js.map +0 -1
- package/dist/update.d.ts.map +0 -1
- package/dist/update.js.map +0 -1
- package/dist/utils/colorUtils.d.ts +0 -101
- package/dist/utils/colorUtils.d.ts.map +0 -1
- package/dist/utils/colorUtils.js +0 -282
- package/dist/utils/colorUtils.js.map +0 -1
- package/dist/utils/commandLine.d.ts +0 -66
- package/dist/utils/commandLine.d.ts.map +0 -1
- package/dist/utils/commandLine.js +0 -123
- package/dist/utils/commandLine.js.map +0 -1
- package/dist/utils/copyDirectory.d.ts +0 -35
- package/dist/utils/copyDirectory.d.ts.map +0 -1
- package/dist/utils/copyDirectory.js +0 -76
- package/dist/utils/copyDirectory.js.map +0 -1
- package/dist/utils/createDirectory.d.ts +0 -34
- package/dist/utils/createDirectory.d.ts.map +0 -1
- package/dist/utils/createDirectory.js +0 -54
- package/dist/utils/createDirectory.js.map +0 -1
- package/dist/utils/createZip.d.ts +0 -39
- package/dist/utils/createZip.d.ts.map +0 -1
- package/dist/utils/createZip.js +0 -114
- package/dist/utils/createZip.js.map +0 -1
- package/dist/utils/deepCopy.d.ts +0 -32
- package/dist/utils/deepCopy.d.ts.map +0 -1
- package/dist/utils/deepCopy.js +0 -79
- package/dist/utils/deepCopy.js.map +0 -1
- package/dist/utils/deepEqual.d.ts +0 -54
- package/dist/utils/deepEqual.d.ts.map +0 -1
- package/dist/utils/deepEqual.js +0 -129
- package/dist/utils/deepEqual.js.map +0 -1
- package/dist/utils/error.d.ts +0 -45
- package/dist/utils/error.d.ts.map +0 -1
- package/dist/utils/error.js +0 -54
- package/dist/utils/error.js.map +0 -1
- package/dist/utils/export.d.ts.map +0 -1
- package/dist/utils/export.js.map +0 -1
- package/dist/utils/format.d.ts +0 -53
- package/dist/utils/format.d.ts.map +0 -1
- package/dist/utils/format.js +0 -78
- package/dist/utils/format.js.map +0 -1
- package/dist/utils/hex.d.ts +0 -89
- package/dist/utils/hex.d.ts.map +0 -1
- package/dist/utils/hex.js +0 -242
- package/dist/utils/hex.js.map +0 -1
- package/dist/utils/inspector.d.ts +0 -87
- package/dist/utils/inspector.d.ts.map +0 -1
- package/dist/utils/inspector.js +0 -268
- package/dist/utils/inspector.js.map +0 -1
- package/dist/utils/isValid.d.ts +0 -103
- package/dist/utils/isValid.d.ts.map +0 -1
- package/dist/utils/isValid.js +0 -162
- package/dist/utils/isValid.js.map +0 -1
- package/dist/utils/network.d.ts +0 -141
- package/dist/utils/network.d.ts.map +0 -1
- package/dist/utils/network.js +0 -314
- package/dist/utils/network.js.map +0 -1
- package/dist/utils/spawn.d.ts +0 -33
- package/dist/utils/spawn.d.ts.map +0 -1
- package/dist/utils/spawn.js.map +0 -1
- package/dist/utils/tracker.d.ts +0 -108
- package/dist/utils/tracker.d.ts.map +0 -1
- package/dist/utils/tracker.js +0 -264
- package/dist/utils/tracker.js.map +0 -1
- package/dist/utils/wait.d.ts +0 -54
- package/dist/utils/wait.d.ts.map +0 -1
- package/dist/utils/wait.js +0 -125
- package/dist/utils/wait.js.map +0 -1
- package/dist/workerGlobalPrefix.d.ts.map +0 -1
- package/dist/workerGlobalPrefix.js.map +0 -1
- package/dist/workerTypes.d.ts.map +0 -1
- package/dist/workerTypes.js.map +0 -1
- package/dist/workers.d.ts.map +0 -1
- package/dist/workers.js.map +0 -1
package/dist/matterNode.js
CHANGED
|
@@ -1,35 +1,8 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* This file contains the class MatterNode.
|
|
3
|
-
*
|
|
4
|
-
* @file matterNode.ts
|
|
5
|
-
* @author Luca Liguori
|
|
6
|
-
* @created 2025-10-01
|
|
7
|
-
* @version 1.0.0
|
|
8
|
-
* @license Apache-2.0
|
|
9
|
-
*
|
|
10
|
-
* Copyright 2025, 2026, 2027 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
|
-
// Node modules
|
|
25
1
|
import path from 'node:path';
|
|
26
2
|
import fs from 'node:fs';
|
|
27
3
|
import EventEmitter from 'node:events';
|
|
28
|
-
// AnsiLogger module
|
|
29
4
|
import { AnsiLogger, BLUE, CYAN, db, debugStringify, er, nf, or, zb } from 'node-ansi-logger';
|
|
30
|
-
// Node persist manager module
|
|
31
5
|
import { NodeStorageManager } from 'node-persist-manager';
|
|
32
|
-
// @matter
|
|
33
6
|
import '@matter/nodejs';
|
|
34
7
|
import { Logger, LogLevel as MatterLogLevel, LogFormat as MatterLogFormat, StorageService, UINT32_MAX, UINT16_MAX, Environment } from '@matter/general';
|
|
35
8
|
import { MdnsService } from '@matter/protocol';
|
|
@@ -38,59 +11,34 @@ import { ServerNode, Endpoint } from '@matter/node';
|
|
|
38
11
|
import { AggregatorEndpoint } from '@matter/node/endpoints/aggregator';
|
|
39
12
|
import { BasicInformationServer } from '@matter/node/behaviors/basic-information';
|
|
40
13
|
import { BridgedDeviceBasicInformationServer } from '@matter/node/behaviors/bridged-device-basic-information';
|
|
14
|
+
import { copyDirectory, getIntParameter, getParameter, hasParameter, inspectError, isValidNumber, isValidString, parseVersionString, wait, withTimeout } from '@matterbridge/utils';
|
|
41
15
|
import { dev, MATTER_LOGGER_FILE, MATTER_STORAGE_NAME, plg, NODE_STORAGE_DIR, MATTERBRIDGE_LOGGER_FILE } from './matterbridgeTypes.js';
|
|
42
16
|
import { bridge } from './matterbridgeDeviceTypes.js';
|
|
43
|
-
import { getIntParameter, getParameter, hasParameter } from './utils/commandLine.js';
|
|
44
|
-
import { copyDirectory } from './utils/copyDirectory.js';
|
|
45
|
-
import { isValidNumber, isValidString, parseVersionString } from './utils/isValid.js';
|
|
46
|
-
import { wait, withTimeout } from './utils/wait.js';
|
|
47
|
-
import { inspectError } from './utils/error.js';
|
|
48
17
|
import { BroadcastServer } from './broadcastServer.js';
|
|
49
18
|
import { toBaseDevice } from './deviceManager.js';
|
|
50
19
|
import { PluginManager } from './pluginManager.js';
|
|
51
20
|
import { addVirtualDevice } from './helpers.js';
|
|
52
|
-
/**
|
|
53
|
-
* Represents the Matter class.
|
|
54
|
-
*/
|
|
55
21
|
export class MatterNode extends EventEmitter {
|
|
56
22
|
matterbridge;
|
|
57
23
|
pluginName;
|
|
58
24
|
device;
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
/** Matter logger */
|
|
62
|
-
matterLog = new AnsiLogger({ logName: 'MatterNode', logTimestampFormat: 4 /* TimestampFormat.TIME_MILLIS */, logLevel: "debug" /* LogLevel.DEBUG */ });
|
|
63
|
-
/** Matter environment default */
|
|
25
|
+
log = new AnsiLogger({ logName: 'MatterNode', logTimestampFormat: 4, logLevel: "debug" });
|
|
26
|
+
matterLog = new AnsiLogger({ logName: 'MatterNode', logTimestampFormat: 4, logLevel: "debug" });
|
|
64
27
|
environment = Environment.default;
|
|
65
|
-
/** Matter storage id */
|
|
66
28
|
storeId;
|
|
67
|
-
/** Matter mdns service from environment default */
|
|
68
29
|
matterMdnsService;
|
|
69
|
-
/** Matter storage service from environment default */
|
|
70
30
|
matterStorageService;
|
|
71
|
-
/** Matter storage manager created with name 'Matterbridge' */
|
|
72
31
|
matterStorageManager;
|
|
73
|
-
/** Matter storage context created in the storage manager with name 'persist' */
|
|
74
32
|
matterStorageContext;
|
|
75
|
-
/** Matter mdns interface name e.g. 'eth0' or 'wlan0' or 'Wi-Fi' */
|
|
76
33
|
mdnsInterface;
|
|
77
|
-
/** Matter listeningAddressIpv4 address */
|
|
78
34
|
ipv4Address;
|
|
79
|
-
/** Matter listeningAddressIpv6 address */
|
|
80
35
|
ipv6Address;
|
|
81
|
-
/** Matter commissioning port It is incremented in childbridge mode. */
|
|
82
36
|
port;
|
|
83
|
-
/** Matter commissioning passcode. It is incremented in childbridge mode. */
|
|
84
37
|
passcode;
|
|
85
|
-
/** Matter commissioning discriminator. It is incremented in childbridge mode. */
|
|
86
38
|
discriminator;
|
|
87
|
-
/** Matter device certification */
|
|
88
39
|
certification;
|
|
89
|
-
/** Matter server node */
|
|
90
40
|
serverNode;
|
|
91
|
-
/** Matter aggregator node */
|
|
92
41
|
aggregatorNode;
|
|
93
|
-
// Default values for the aggregator node
|
|
94
42
|
aggregatorVendorId = VendorId(getIntParameter('vendorId') ?? 0xfff1);
|
|
95
43
|
aggregatorVendorName = getParameter('vendorName') ?? 'Matterbridge';
|
|
96
44
|
aggregatorProductId = getIntParameter('productId') ?? 0x8000;
|
|
@@ -98,23 +46,12 @@ export class MatterNode extends EventEmitter {
|
|
|
98
46
|
aggregatorDeviceType = DeviceTypeId(getIntParameter('deviceType') ?? bridge.code);
|
|
99
47
|
aggregatorSerialNumber = getParameter('serialNumber');
|
|
100
48
|
aggregatorUniqueId = getParameter('uniqueId');
|
|
101
|
-
/** Advertising nodes map: time advertising started keyed by storeId */
|
|
102
49
|
advertisingNodes = new Map();
|
|
103
|
-
/** Plugins */
|
|
104
50
|
pluginManager;
|
|
105
|
-
/** Dependant MatterNodes keyed by device id */
|
|
106
51
|
dependantMatterNodes = new Map();
|
|
107
|
-
/** Broadcast server */
|
|
108
52
|
server;
|
|
109
53
|
debug = hasParameter('debug') || hasParameter('verbose');
|
|
110
54
|
verbose = hasParameter('verbose');
|
|
111
|
-
/**
|
|
112
|
-
* Creates an instance of the Matter class.
|
|
113
|
-
*
|
|
114
|
-
* @param {SharedMatterbridge} matterbridge - The shared matterbridge instance.
|
|
115
|
-
* @param {PluginName} [pluginName] - The plugin name (optional). If not provided, it is assumed to be the main matter node instance and all plugins are included.
|
|
116
|
-
* @param {MatterbridgeEndpoint} [device] - The matterbridge endpoint device (optional). It is used to create a server mode device.
|
|
117
|
-
*/
|
|
118
55
|
constructor(matterbridge, pluginName, device) {
|
|
119
56
|
super();
|
|
120
57
|
this.matterbridge = matterbridge;
|
|
@@ -123,25 +60,19 @@ export class MatterNode extends EventEmitter {
|
|
|
123
60
|
this.log.logNameColor = '\x1b[38;5;65m';
|
|
124
61
|
if (this.debug)
|
|
125
62
|
this.log.debug(`MatterNode ${this.pluginName ? 'for plugin ' + this.pluginName : 'bridge'} loading...`);
|
|
126
|
-
// Setup Matter parameters
|
|
127
63
|
this.port = matterbridge.port;
|
|
128
64
|
this.passcode = matterbridge.passcode;
|
|
129
65
|
this.discriminator = matterbridge.discriminator;
|
|
130
|
-
// Setup the broadcast server
|
|
131
66
|
this.server = new BroadcastServer('matter', this.log);
|
|
132
67
|
this.server.on('broadcast_message', this.msgHandler.bind(this));
|
|
133
68
|
if (this.verbose)
|
|
134
69
|
this.log.debug(`BroadcastServer is ready`);
|
|
135
|
-
// Ensure the matterbridge directory exists
|
|
136
70
|
fs.mkdirSync(matterbridge.matterbridgeDirectory, { recursive: true });
|
|
137
|
-
// Setup the plugin manager with thread server closed
|
|
138
71
|
this.pluginManager = new PluginManager(this.matterbridge);
|
|
139
|
-
this.pluginManager.logLevel = this.debug ? "debug"
|
|
140
|
-
// @ts-expect-error access private property
|
|
72
|
+
this.pluginManager.logLevel = this.debug ? "debug" : "info";
|
|
141
73
|
this.pluginManager.server.close();
|
|
142
74
|
if (this.verbose)
|
|
143
75
|
this.log.debug(`PluginManager is ready`);
|
|
144
|
-
// Setup the matter environment
|
|
145
76
|
this.environment.vars.set('log.level', MatterLogLevel.DEBUG);
|
|
146
77
|
this.environment.vars.set('log.format', MatterLogFormat.ANSI);
|
|
147
78
|
this.environment.vars.set('path.root', path.join(matterbridge.matterbridgeDirectory, MATTER_STORAGE_NAME));
|
|
@@ -149,18 +80,15 @@ export class MatterNode extends EventEmitter {
|
|
|
149
80
|
this.environment.vars.set('runtime.exitcode', false);
|
|
150
81
|
if (this.verbose)
|
|
151
82
|
this.log.debug(`Matter Environment is ready`);
|
|
152
|
-
// Ensure MdnsService is registered in the default environment
|
|
153
83
|
this.matterMdnsService = new MdnsService(this.environment);
|
|
154
84
|
setImmediate(async () => {
|
|
155
85
|
await this.matterMdnsService?.construction.ready;
|
|
156
86
|
if (this.verbose)
|
|
157
87
|
this.log.debug(`Matter MdnsService is ready`);
|
|
158
88
|
});
|
|
159
|
-
// Setup the matterbridge logger
|
|
160
89
|
if (this.matterbridge.fileLogger) {
|
|
161
90
|
AnsiLogger.setGlobalLogfile(path.join(this.matterbridge.matterbridgeDirectory, MATTERBRIDGE_LOGGER_FILE), this.matterbridge.logLevel);
|
|
162
91
|
}
|
|
163
|
-
// Setup the matter logger
|
|
164
92
|
Logger.destinations.default.write = this.createDestinationMatterLogger();
|
|
165
93
|
const levels = ['debug', 'info', 'notice', 'warn', 'error', 'fatal'];
|
|
166
94
|
if (this.verbose)
|
|
@@ -168,11 +96,6 @@ export class MatterNode extends EventEmitter {
|
|
|
168
96
|
if (this.debug)
|
|
169
97
|
this.log.debug(`MatterNode ${this.pluginName ? 'for plugin ' + this.pluginName : 'bridge'} loaded`);
|
|
170
98
|
}
|
|
171
|
-
/**
|
|
172
|
-
* Handles incoming messages from the broadcast server.
|
|
173
|
-
*
|
|
174
|
-
* @param {WorkerMessage} msg - The incoming message.
|
|
175
|
-
*/
|
|
176
99
|
async msgHandler(msg) {
|
|
177
100
|
if (this.server.isWorkerRequest(msg) && (msg.dst === 'all' || msg.dst === 'matter')) {
|
|
178
101
|
if (this.verbose)
|
|
@@ -203,17 +126,9 @@ export class MatterNode extends EventEmitter {
|
|
|
203
126
|
}
|
|
204
127
|
}
|
|
205
128
|
}
|
|
206
|
-
/**
|
|
207
|
-
* Destroys the Matter instance.
|
|
208
|
-
* It closes the mDNS service and the broadcast server.
|
|
209
|
-
*
|
|
210
|
-
* @param {boolean} closeMdns - Whether to close the mDNS service. Default is true.
|
|
211
|
-
* @returns {Promise<void>} A promise that resolves when the instance is destroyed.
|
|
212
|
-
*/
|
|
213
129
|
async destroy(closeMdns = true) {
|
|
214
130
|
if (this.verbose)
|
|
215
131
|
this.log.debug(`Destroying MatterNode instance for ${this.storeId}...`);
|
|
216
|
-
// Close mDNS service
|
|
217
132
|
if (closeMdns) {
|
|
218
133
|
if (this.verbose)
|
|
219
134
|
this.log.debug(`Closing Matter MdnsService for ${this.storeId}...`);
|
|
@@ -221,26 +136,18 @@ export class MatterNode extends EventEmitter {
|
|
|
221
136
|
if (this.verbose)
|
|
222
137
|
this.log.debug(`Closed Matter MdnsService for ${this.storeId}`);
|
|
223
138
|
}
|
|
224
|
-
// Close the plugin manager
|
|
225
139
|
this.pluginManager.destroy();
|
|
226
|
-
// Close the broadcast server
|
|
227
140
|
this.server.close();
|
|
228
|
-
// Yield to the Node.js event loop to allow all resources to be released
|
|
229
141
|
await this.yieldToNode();
|
|
230
142
|
if (this.verbose)
|
|
231
143
|
this.log.debug(`Destroyed MatterNode instance for ${this.storeId}`);
|
|
232
144
|
}
|
|
233
145
|
async create() {
|
|
234
146
|
this.log.info('Creating Matter node...');
|
|
235
|
-
// Start matter storage
|
|
236
147
|
await this.startMatterStorage();
|
|
237
|
-
// Load plugins from storage
|
|
238
|
-
// @ts-expect-error access private property
|
|
239
148
|
this.pluginManager.matterbridge.nodeStorage = new NodeStorageManager({ dir: path.join(this.matterbridge.matterbridgeDirectory, NODE_STORAGE_DIR), writeQueue: false, expiredInterval: undefined, logging: false });
|
|
240
|
-
// @ts-expect-error access private property
|
|
241
149
|
this.pluginManager.matterbridge.nodeContext = await this.pluginManager.matterbridge.nodeStorage.createStorage('matterbridge');
|
|
242
150
|
await this.pluginManager.loadFromStorage();
|
|
243
|
-
// Create Matter node for a server mode device
|
|
244
151
|
if (this.pluginName && this.device && this.device.deviceName) {
|
|
245
152
|
this.log.debug(`Creating MatterNode instance for server node device ${CYAN}${this.device.deviceName}${db}...`);
|
|
246
153
|
await this.createDeviceServerNode(this.pluginName, this.device);
|
|
@@ -251,7 +158,6 @@ export class MatterNode extends EventEmitter {
|
|
|
251
158
|
if (!this.pluginName) {
|
|
252
159
|
this.log.debug('Creating MatterNode instance for all plugins...');
|
|
253
160
|
await this.createMatterbridgeServerNode();
|
|
254
|
-
// Load all enabled plugins
|
|
255
161
|
this.log.debug('Loading all plugins...');
|
|
256
162
|
const loadPromises = [];
|
|
257
163
|
for (const plugin of this.pluginManager.array().filter((p) => p.enabled)) {
|
|
@@ -264,7 +170,6 @@ export class MatterNode extends EventEmitter {
|
|
|
264
170
|
}
|
|
265
171
|
else {
|
|
266
172
|
this.log.debug(`Creating MatterNode instance for plugin ${CYAN}${this.pluginName}${db}...`);
|
|
267
|
-
// Load only the specified plugin
|
|
268
173
|
this.log.debug(`Loading plugin ${CYAN}${this.pluginName}${db}...`);
|
|
269
174
|
await this.pluginManager.load(this.pluginName);
|
|
270
175
|
this.log.debug(`Loaded plugin ${CYAN}${this.pluginName}${db}`);
|
|
@@ -278,16 +183,13 @@ export class MatterNode extends EventEmitter {
|
|
|
278
183
|
if (!this.serverNode && !this.pluginName)
|
|
279
184
|
throw new Error('Matter server node not created yet. Call create() first.');
|
|
280
185
|
this.log.info('Starting MatterNode...');
|
|
281
|
-
// Start Matter node for a server mode device
|
|
282
186
|
if (this.pluginName && this.device && this.device.deviceName) {
|
|
283
|
-
// Start the server node
|
|
284
187
|
this.log.debug(`Starting MatterNode for server device ${this.pluginName}:${this.device.deviceName}...`);
|
|
285
188
|
await this.startServerNode();
|
|
286
189
|
this.log.debug(`Started MatterNode for server device ${this.pluginName}:${this.device.deviceName}`);
|
|
287
190
|
return;
|
|
288
191
|
}
|
|
289
192
|
if (!this.pluginName) {
|
|
290
|
-
// Start all loaded plugins
|
|
291
193
|
this.log.debug('Starting all plugins...');
|
|
292
194
|
const startPromises = [];
|
|
293
195
|
for (const plugin of this.pluginManager.array().filter((p) => p.enabled && p.loaded)) {
|
|
@@ -295,11 +197,9 @@ export class MatterNode extends EventEmitter {
|
|
|
295
197
|
}
|
|
296
198
|
await Promise.all(startPromises);
|
|
297
199
|
this.log.debug('Started all plugins');
|
|
298
|
-
// Start the server node
|
|
299
200
|
this.log.debug('Starting MatterNode for all plugins...');
|
|
300
201
|
await this.startServerNode();
|
|
301
202
|
this.log.debug('Started MatterNode for all plugins');
|
|
302
|
-
// Configure all loaded plugins
|
|
303
203
|
this.log.debug('Configuring all plugins...');
|
|
304
204
|
const configurePromises = [];
|
|
305
205
|
for (const plugin of this.pluginManager.array().filter((p) => p.enabled && p.started)) {
|
|
@@ -309,16 +209,12 @@ export class MatterNode extends EventEmitter {
|
|
|
309
209
|
this.log.debug('Configured all plugins');
|
|
310
210
|
}
|
|
311
211
|
else {
|
|
312
|
-
// Start the loaded plugin
|
|
313
212
|
await this.pluginManager.start(this.pluginName, 'Starting MatterNode');
|
|
314
|
-
// Start the server node
|
|
315
213
|
this.log.debug(`Starting MatterNode for plugin ${this.pluginName}...`);
|
|
316
214
|
await this.startServerNode();
|
|
317
215
|
this.log.debug(`Started MatterNode for plugin ${this.pluginName}`);
|
|
318
|
-
// Configure the plugin
|
|
319
216
|
await this.pluginManager.configure(this.pluginName);
|
|
320
217
|
}
|
|
321
|
-
// Start the dependant MatterNodes
|
|
322
218
|
this.log.debug(`Starting dependant MatterNodes...`);
|
|
323
219
|
for (const dependantMatterNode of this.dependantMatterNodes.values()) {
|
|
324
220
|
await dependantMatterNode.start();
|
|
@@ -331,15 +227,13 @@ export class MatterNode extends EventEmitter {
|
|
|
331
227
|
if (!this.serverNode)
|
|
332
228
|
throw new Error('Matter server node not created yet. Call create() first.');
|
|
333
229
|
this.log.info('Stopping MatterNode...');
|
|
334
|
-
// Stop Matter node for a server mode device
|
|
335
230
|
if (this.pluginName && this.device && this.device.deviceName) {
|
|
336
|
-
// Stop the server node
|
|
337
231
|
this.log.debug(`Stopping MatterNode for server device ${this.pluginName}:${this.device.deviceName}...`);
|
|
338
232
|
await this.stopServerNode();
|
|
339
233
|
this.serverNode = undefined;
|
|
340
234
|
this.aggregatorNode = undefined;
|
|
341
235
|
await this.stopMatterStorage();
|
|
342
|
-
await this.destroy(false);
|
|
236
|
+
await this.destroy(false);
|
|
343
237
|
this.log.debug(`Stopped MatterNode for server device ${this.pluginName}:${this.device.deviceName}`);
|
|
344
238
|
this.log.info('Stopped MatterNode');
|
|
345
239
|
await this.yieldToNode();
|
|
@@ -359,7 +253,6 @@ export class MatterNode extends EventEmitter {
|
|
|
359
253
|
await this.pluginManager.shutdown(this.pluginName, 'Stopping MatterNode');
|
|
360
254
|
this.log.debug(`Stopped plugin ${this.pluginName}`);
|
|
361
255
|
}
|
|
362
|
-
// Stop the dependant MatterNodes
|
|
363
256
|
this.log.debug(`Stopping dependant MatterNodes...`);
|
|
364
257
|
for (const dependantMatterNode of this.dependantMatterNodes.values()) {
|
|
365
258
|
await dependantMatterNode.stop();
|
|
@@ -372,46 +265,24 @@ export class MatterNode extends EventEmitter {
|
|
|
372
265
|
this.log.info('Stopped MatterNode');
|
|
373
266
|
await this.yieldToNode();
|
|
374
267
|
}
|
|
375
|
-
/**
|
|
376
|
-
* Creates a MatterLogger function to show the matter.js log messages in AnsiLogger (console and frontend).
|
|
377
|
-
* It also logs to file (matter.log) if fileLogger is true.
|
|
378
|
-
*
|
|
379
|
-
* @returns {Function} The MatterLogger function. \x1b[35m for violet \x1b[34m is blue
|
|
380
|
-
*/
|
|
381
268
|
createDestinationMatterLogger() {
|
|
382
|
-
this.matterLog.logNameColor = '\x1b[34m';
|
|
269
|
+
this.matterLog.logNameColor = '\x1b[34m';
|
|
383
270
|
if (this.matterbridge.matterFileLogger) {
|
|
384
271
|
this.matterLog.logFilePath = path.join(this.matterbridge.matterbridgeDirectory, MATTER_LOGGER_FILE);
|
|
385
272
|
}
|
|
386
273
|
return (text, message) => {
|
|
387
|
-
// 2024-08-21 08:55:19.488 DEBUG InteractionMessenger Sending DataReport chunk with 28 attributes and 0 events: 1004 bytes
|
|
388
274
|
const logger = text.slice(44, 44 + 20).trim();
|
|
389
275
|
const msg = text.slice(65);
|
|
390
276
|
this.matterLog.logName = logger;
|
|
391
277
|
this.matterLog.log(MatterLogLevel.names[message.level], msg);
|
|
392
278
|
};
|
|
393
279
|
}
|
|
394
|
-
/**
|
|
395
|
-
* Starts the matter storage with name Matterbridge and performs a backup.
|
|
396
|
-
*
|
|
397
|
-
* @returns {Promise<void>} - A promise that resolves when the storage is started.
|
|
398
|
-
*/
|
|
399
280
|
async startMatterStorage() {
|
|
400
|
-
// Setup Matter storage
|
|
401
281
|
this.log.info(`Starting matter node storage...`);
|
|
402
282
|
this.matterStorageService = this.environment.get(StorageService);
|
|
403
283
|
this.log.info(`Started matter node storage in ${CYAN}${this.matterStorageService.location}${nf}`);
|
|
404
|
-
// Backup matter storage since it is created/opened correctly
|
|
405
284
|
await this.backupMatterStorage(path.join(this.matterbridge.matterbridgeDirectory, MATTER_STORAGE_NAME), path.join(this.matterbridge.matterbridgeDirectory, MATTER_STORAGE_NAME + '.backup'));
|
|
406
285
|
}
|
|
407
|
-
/**
|
|
408
|
-
* Makes a backup copy of the specified matter storage directory.
|
|
409
|
-
*
|
|
410
|
-
* @param {string} storageName - The name of the storage directory to be backed up.
|
|
411
|
-
* @param {string} backupName - The name of the backup directory to be created.
|
|
412
|
-
* @private
|
|
413
|
-
* @returns {Promise<void>} A promise that resolves when the has been done.
|
|
414
|
-
*/
|
|
415
286
|
async backupMatterStorage(storageName, backupName) {
|
|
416
287
|
this.log.info(`Creating matter node storage backup from ${CYAN}${storageName}${nf} to ${CYAN}${backupName}${nf}...`);
|
|
417
288
|
try {
|
|
@@ -419,7 +290,6 @@ export class MatterNode extends EventEmitter {
|
|
|
419
290
|
this.log.info('Created matter node storage backup');
|
|
420
291
|
}
|
|
421
292
|
catch (error) {
|
|
422
|
-
// istanbul ignore next if
|
|
423
293
|
if (error instanceof Error && error?.code === 'ENOENT') {
|
|
424
294
|
this.log.info(`No matter node storage found to backup from ${CYAN}${storageName}${nf} to ${CYAN}${backupName}${nf}`);
|
|
425
295
|
}
|
|
@@ -428,11 +298,6 @@ export class MatterNode extends EventEmitter {
|
|
|
428
298
|
}
|
|
429
299
|
}
|
|
430
300
|
}
|
|
431
|
-
/**
|
|
432
|
-
* Stops the matter storage.
|
|
433
|
-
*
|
|
434
|
-
* @returns {Promise<void>} A promise that resolves when the storage is stopped.
|
|
435
|
-
*/
|
|
436
301
|
async stopMatterStorage() {
|
|
437
302
|
this.log.info('Closing matter node storage...');
|
|
438
303
|
await this.matterStorageManager?.close();
|
|
@@ -442,21 +307,6 @@ export class MatterNode extends EventEmitter {
|
|
|
442
307
|
this.log.info('Closed matter node storage');
|
|
443
308
|
this.emit('closed');
|
|
444
309
|
}
|
|
445
|
-
/**
|
|
446
|
-
* Creates a server node storage context.
|
|
447
|
-
*
|
|
448
|
-
* @param {string} storeId - The storeId.
|
|
449
|
-
* @param {string} deviceName - The name of the device.
|
|
450
|
-
* @param {DeviceTypeId} deviceType - The device type of the device.
|
|
451
|
-
* @param {VendorId} vendorId - The vendor ID.
|
|
452
|
-
* @param {string} vendorName - The vendor name.
|
|
453
|
-
* @param {number} productId - The product ID.
|
|
454
|
-
* @param {string} productName - The product name.
|
|
455
|
-
* @param {string} [serialNumber] - The serial number of the device (optional).
|
|
456
|
-
* @param {string} [uniqueId] - The unique ID of the device (optional).
|
|
457
|
-
* @returns {Promise<StorageContext>} The storage context for the commissioning server.
|
|
458
|
-
* @throws {Error} If the storage service is not initialized.
|
|
459
|
-
*/
|
|
460
310
|
async createServerNodeContext(storeId, deviceName, deviceType, vendorId, vendorName, productId, productName, serialNumber, uniqueId) {
|
|
461
311
|
if (!this.matterStorageService) {
|
|
462
312
|
throw new Error('No storage service initialized');
|
|
@@ -499,52 +349,33 @@ export class MatterNode extends EventEmitter {
|
|
|
499
349
|
this.log.debug(`- hardwareVersionString: ${await storageContext.get('hardwareVersionString')}`);
|
|
500
350
|
return storageContext;
|
|
501
351
|
}
|
|
502
|
-
/**
|
|
503
|
-
* Creates a server node.
|
|
504
|
-
*
|
|
505
|
-
* @param {number} [port] - The port number for the server node. Defaults to 5540.
|
|
506
|
-
* @param {number} [passcode] - The passcode for the server node. Defaults to 20242025.
|
|
507
|
-
* @param {number} [discriminator] - The discriminator for the server node. Defaults to 3850.
|
|
508
|
-
* @returns {Promise<ServerNode<ServerNode.RootEndpoint>>} A promise that resolves to the created server node.
|
|
509
|
-
* @throws {Error} If the matter storage context is not created yet.
|
|
510
|
-
*/
|
|
511
352
|
async createServerNode(port = 5540, passcode = 20252026, discriminator = 3850) {
|
|
512
353
|
if (!this.matterStorageContext) {
|
|
513
354
|
throw new Error('Matter server node context not created yet. Call createServerNodeContext() first.');
|
|
514
355
|
}
|
|
515
356
|
const storeId = await this.matterStorageContext.get('storeId');
|
|
516
357
|
this.log.notice(`Creating server node for ${storeId} on port ${port} with passcode ${passcode} and discriminator ${discriminator}...`);
|
|
517
|
-
/**
|
|
518
|
-
* Create a Matter ServerNode, which contains the Root Endpoint and all relevant data and configuration
|
|
519
|
-
*/
|
|
520
358
|
const serverNode = await ServerNode.create({
|
|
521
|
-
// Required: Give the Node a unique ID which is used to store the state of this node
|
|
522
359
|
id: storeId,
|
|
523
|
-
// Provide the environment to run this node in
|
|
524
360
|
environment: this.environment,
|
|
525
|
-
// Provide Network relevant configuration like the port
|
|
526
361
|
network: {
|
|
527
362
|
listeningAddressIpv4: this.ipv4Address,
|
|
528
363
|
listeningAddressIpv6: this.ipv6Address,
|
|
529
364
|
port,
|
|
530
365
|
},
|
|
531
|
-
// Provide the certificate for the device
|
|
532
366
|
operationalCredentials: {
|
|
533
367
|
certification: this.certification,
|
|
534
368
|
},
|
|
535
|
-
// Provide Commissioning relevant settings
|
|
536
369
|
commissioning: {
|
|
537
370
|
passcode,
|
|
538
371
|
discriminator,
|
|
539
372
|
},
|
|
540
|
-
// Provide Node announcement settings
|
|
541
373
|
productDescription: {
|
|
542
374
|
name: await this.matterStorageContext.get('deviceName'),
|
|
543
375
|
deviceType: DeviceTypeId(await this.matterStorageContext.get('deviceType')),
|
|
544
376
|
vendorId: VendorId(await this.matterStorageContext.get('vendorId')),
|
|
545
377
|
productId: await this.matterStorageContext.get('productId'),
|
|
546
378
|
},
|
|
547
|
-
// Provide defaults for the BasicInformation cluster on the Root endpoint
|
|
548
379
|
basicInformation: {
|
|
549
380
|
vendorId: VendorId(await this.matterStorageContext.get('vendorId')),
|
|
550
381
|
vendorName: await this.matterStorageContext.get('vendorName'),
|
|
@@ -561,23 +392,17 @@ export class MatterNode extends EventEmitter {
|
|
|
561
392
|
reachable: true,
|
|
562
393
|
},
|
|
563
394
|
});
|
|
564
|
-
/**
|
|
565
|
-
* This event is triggered when the device is initially commissioned successfully.
|
|
566
|
-
* This means: It is added to the first fabric.
|
|
567
|
-
*/
|
|
568
395
|
serverNode.lifecycle.commissioned.on(() => {
|
|
569
396
|
this.log.notice(`Server node for ${storeId} was initially commissioned successfully!`);
|
|
570
397
|
this.advertisingNodes.delete(storeId);
|
|
571
398
|
this.server.request({ type: 'frontend_refreshrequired', src: 'matter', dst: 'frontend', params: { changed: 'matter', matter: { ...this.getServerNodeData(serverNode) } } });
|
|
572
399
|
});
|
|
573
|
-
/** This event is triggered when all fabrics are removed from the device, usually it also does a factory reset then. */
|
|
574
400
|
serverNode.lifecycle.decommissioned.on(() => {
|
|
575
401
|
this.log.notice(`Server node for ${storeId} was fully decommissioned successfully!`);
|
|
576
402
|
this.advertisingNodes.delete(storeId);
|
|
577
403
|
this.server.request({ type: 'frontend_refreshrequired', src: 'matter', dst: 'frontend', params: { changed: 'matter', matter: { ...this.getServerNodeData(serverNode) } } });
|
|
578
404
|
this.server.request({ type: 'frontend_snackbarmessage', src: 'matter', dst: 'frontend', params: { message: `${storeId} is offline`, timeout: 5, severity: 'warning' } });
|
|
579
405
|
});
|
|
580
|
-
/** This event is triggered when the device went online. This means that it is discoverable in the network. */
|
|
581
406
|
serverNode.lifecycle.online.on(async () => {
|
|
582
407
|
this.log.notice(`Server node for ${storeId} is online`);
|
|
583
408
|
if (!serverNode.lifecycle.isCommissioned) {
|
|
@@ -595,7 +420,6 @@ export class MatterNode extends EventEmitter {
|
|
|
595
420
|
this.server.request({ type: 'frontend_snackbarmessage', src: 'matter', dst: 'frontend', params: { message: `${storeId} is online`, timeout: 5, severity: 'success' } });
|
|
596
421
|
this.emit('online', storeId);
|
|
597
422
|
});
|
|
598
|
-
/** This event is triggered when the device went offline. it is not longer discoverable or connectable in the network. */
|
|
599
423
|
serverNode.lifecycle.offline.on(() => {
|
|
600
424
|
this.log.notice(`Server node for ${storeId} is offline`);
|
|
601
425
|
this.advertisingNodes.delete(storeId);
|
|
@@ -603,15 +427,11 @@ export class MatterNode extends EventEmitter {
|
|
|
603
427
|
this.server.request({ type: 'frontend_snackbarmessage', src: 'matter', dst: 'frontend', params: { message: `${storeId} is offline`, timeout: 5, severity: 'warning' } });
|
|
604
428
|
this.emit('offline', storeId);
|
|
605
429
|
});
|
|
606
|
-
/**
|
|
607
|
-
* This event is triggered when a fabric is added, removed or updated on the device. Use this if more granular
|
|
608
|
-
* information is needed.
|
|
609
|
-
*/
|
|
610
430
|
serverNode.events.commissioning.fabricsChanged.on((fabricIndex, fabricAction) => {
|
|
611
431
|
let action = '';
|
|
612
432
|
switch (fabricAction) {
|
|
613
433
|
case 'added':
|
|
614
|
-
this.advertisingNodes.delete(storeId);
|
|
434
|
+
this.advertisingNodes.delete(storeId);
|
|
615
435
|
action = 'added';
|
|
616
436
|
break;
|
|
617
437
|
case 'deleted':
|
|
@@ -624,22 +444,14 @@ export class MatterNode extends EventEmitter {
|
|
|
624
444
|
this.log.notice(`Commissioned fabric index ${fabricIndex} ${action} on server node for ${storeId}: ${debugStringify(serverNode.state.commissioning.fabrics[fabricIndex])}`);
|
|
625
445
|
this.server.request({ type: 'frontend_refreshrequired', src: 'matter', dst: 'frontend', params: { changed: 'matter', matter: { ...this.getServerNodeData(serverNode) } } });
|
|
626
446
|
});
|
|
627
|
-
/**
|
|
628
|
-
* This event is triggered when an operative new session was opened by a Controller.
|
|
629
|
-
* It is not triggered for the initial commissioning process, just afterwards for real connections.
|
|
630
|
-
*/
|
|
631
447
|
serverNode.events.sessions.opened.on((session) => {
|
|
632
448
|
this.log.notice(`Session opened on server node for ${storeId}: ${debugStringify(session)}`);
|
|
633
449
|
this.server.request({ type: 'frontend_refreshrequired', src: 'matter', dst: 'frontend', params: { changed: 'matter', matter: { ...this.getServerNodeData(serverNode) } } });
|
|
634
450
|
});
|
|
635
|
-
/**
|
|
636
|
-
* This event is triggered when an operative session is closed by a Controller or because the Device goes offline.
|
|
637
|
-
*/
|
|
638
451
|
serverNode.events.sessions.closed.on((session) => {
|
|
639
452
|
this.log.notice(`Session closed on server node for ${storeId}: ${debugStringify(session)}`);
|
|
640
453
|
this.server.request({ type: 'frontend_refreshrequired', src: 'matter', dst: 'frontend', params: { changed: 'matter', matter: { ...this.getServerNodeData(serverNode) } } });
|
|
641
454
|
});
|
|
642
|
-
/** This event is triggered when a subscription gets added or removed on an operative session. */
|
|
643
455
|
serverNode.events.sessions.subscriptionsChanged.on((session) => {
|
|
644
456
|
this.log.notice(`Session subscriptions changed on server node for ${storeId}: ${debugStringify(session)}`);
|
|
645
457
|
this.server.request({ type: 'frontend_refreshrequired', src: 'matter', dst: 'frontend', params: { changed: 'matter', matter: { ...this.getServerNodeData(serverNode) } } });
|
|
@@ -648,12 +460,6 @@ export class MatterNode extends EventEmitter {
|
|
|
648
460
|
this.log.info(`Created server node for ${this.storeId}`);
|
|
649
461
|
return serverNode;
|
|
650
462
|
}
|
|
651
|
-
/**
|
|
652
|
-
* Gets the matter serializable data of the specified server node.
|
|
653
|
-
*
|
|
654
|
-
* @param {ServerNode} [serverNode] - The server node to start.
|
|
655
|
-
* @returns {ApiMatter} The serializable data of the server node.
|
|
656
|
-
*/
|
|
657
463
|
getServerNodeData(serverNode) {
|
|
658
464
|
const advertiseTime = this.advertisingNodes.get(serverNode.id) || 0;
|
|
659
465
|
return {
|
|
@@ -670,13 +476,6 @@ export class MatterNode extends EventEmitter {
|
|
|
670
476
|
serialNumber: serverNode.state.basicInformation.serialNumber,
|
|
671
477
|
};
|
|
672
478
|
}
|
|
673
|
-
/**
|
|
674
|
-
* Starts the specified server node.
|
|
675
|
-
*
|
|
676
|
-
* @param {number} [timeout] - The timeout in milliseconds for starting the server node. Defaults to 30 seconds.
|
|
677
|
-
* @returns {Promise<void>} A promise that resolves when the server node has started.
|
|
678
|
-
* @throws {Error} If the server node is not created yet.
|
|
679
|
-
*/
|
|
680
479
|
async startServerNode(timeout = 30000) {
|
|
681
480
|
if (!this.serverNode) {
|
|
682
481
|
throw new Error('Matter server node not created yet. Call create() first.');
|
|
@@ -687,17 +486,9 @@ export class MatterNode extends EventEmitter {
|
|
|
687
486
|
this.log.notice(`Started ${this.serverNode.id} server node`);
|
|
688
487
|
}
|
|
689
488
|
catch (error) {
|
|
690
|
-
// istanbul ignore next
|
|
691
489
|
this.log.error(`Failed to start ${this.serverNode.id} server node: ${error instanceof Error ? error.message : error}`);
|
|
692
490
|
}
|
|
693
491
|
}
|
|
694
|
-
/**
|
|
695
|
-
* Stops the specified server node.
|
|
696
|
-
*
|
|
697
|
-
* @param {number} [timeout] - The timeout in milliseconds for stopping the server node. Defaults to 30 seconds.
|
|
698
|
-
* @returns {Promise<void>} A promise that resolves when the server node has stopped.
|
|
699
|
-
* @throws {Error} If the server node is not created yet.
|
|
700
|
-
*/
|
|
701
492
|
async stopServerNode(timeout = 30000) {
|
|
702
493
|
if (!this.serverNode) {
|
|
703
494
|
throw new Error('Matter server node not created yet. Call create() first.');
|
|
@@ -708,16 +499,9 @@ export class MatterNode extends EventEmitter {
|
|
|
708
499
|
this.log.info(`Closed ${this.serverNode.id} server node`);
|
|
709
500
|
}
|
|
710
501
|
catch (error) {
|
|
711
|
-
// istanbul ignore next
|
|
712
502
|
this.log.error(`Failed to close ${this.serverNode.id} server node: ${error instanceof Error ? error.message : error}`);
|
|
713
503
|
}
|
|
714
504
|
}
|
|
715
|
-
/**
|
|
716
|
-
* Creates an aggregator node with the specified storage context.
|
|
717
|
-
*
|
|
718
|
-
* @returns {Promise<Endpoint<AggregatorEndpoint>>} A promise that resolves to the created aggregator node.
|
|
719
|
-
* @throws {Error} If the matter storage context is not created yet.
|
|
720
|
-
*/
|
|
721
505
|
async createAggregatorNode() {
|
|
722
506
|
if (!this.matterStorageContext) {
|
|
723
507
|
throw new Error('Matter server node context not created yet. Call createServerNodeContext() first.');
|
|
@@ -727,16 +511,9 @@ export class MatterNode extends EventEmitter {
|
|
|
727
511
|
this.log.info(`Created ${await this.matterStorageContext.get('storeId')} aggregator`);
|
|
728
512
|
return aggregatorNode;
|
|
729
513
|
}
|
|
730
|
-
/**
|
|
731
|
-
* Creates the matterbridge server node.
|
|
732
|
-
*
|
|
733
|
-
* @returns {Promise<ServerNode<ServerNode.RootEndpoint>>} A promise that resolves to the created matterbridge server node.
|
|
734
|
-
*/
|
|
735
514
|
async createMatterbridgeServerNode() {
|
|
736
515
|
this.log.debug(`Creating ${plg}Matterbridge${db} server node...`);
|
|
737
|
-
this.matterStorageContext = await this.createServerNodeContext('Matterbridge',
|
|
738
|
-
'Matterbridge', // deviceName
|
|
739
|
-
this.aggregatorDeviceType, this.aggregatorVendorId, this.aggregatorVendorName, this.aggregatorProductId, this.aggregatorProductName, this.aggregatorSerialNumber, this.aggregatorUniqueId);
|
|
516
|
+
this.matterStorageContext = await this.createServerNodeContext('Matterbridge', 'Matterbridge', this.aggregatorDeviceType, this.aggregatorVendorId, this.aggregatorVendorName, this.aggregatorProductId, this.aggregatorProductName, this.aggregatorSerialNumber, this.aggregatorUniqueId);
|
|
740
517
|
this.serverNode = await this.createServerNode(this.port ? this.port++ : undefined, this.passcode ? this.passcode++ : undefined, this.discriminator ? this.discriminator++ : undefined);
|
|
741
518
|
this.aggregatorNode = await this.createAggregatorNode();
|
|
742
519
|
this.log.debug(`Adding ${plg}Matterbridge${db} aggregator node...`);
|
|
@@ -747,13 +524,6 @@ export class MatterNode extends EventEmitter {
|
|
|
747
524
|
this.log.debug(`Created ${plg}Matterbridge${db} server node`);
|
|
748
525
|
return this.serverNode;
|
|
749
526
|
}
|
|
750
|
-
/**
|
|
751
|
-
* Creates and configures the server node for an accessory plugin for a given device.
|
|
752
|
-
*
|
|
753
|
-
* @param {Plugin | PluginName} plugin - The plugin to configure.
|
|
754
|
-
* @param {MatterbridgeEndpoint} device - The device to associate with the plugin.
|
|
755
|
-
* @returns {Promise<ServerNode<ServerNode.RootEndpoint> | undefined>} A promise that resolves to the server node for the accessory plugin.
|
|
756
|
-
*/
|
|
757
527
|
async createAccessoryPlugin(plugin, device) {
|
|
758
528
|
if (typeof plugin === 'string') {
|
|
759
529
|
const _plugin = this.pluginManager.get(plugin);
|
|
@@ -775,12 +545,6 @@ export class MatterNode extends EventEmitter {
|
|
|
775
545
|
}
|
|
776
546
|
return this.serverNode;
|
|
777
547
|
}
|
|
778
|
-
/**
|
|
779
|
-
* Creates and configures the server node and the aggregator node for a dynamic plugin.
|
|
780
|
-
*
|
|
781
|
-
* @param {Plugin | PluginName} plugin - The plugin to configure.
|
|
782
|
-
* @returns {Promise<ServerNode<ServerNode.RootEndpoint> | undefined>} A promise that resolves to the server node for the dynamic plugin.
|
|
783
|
-
*/
|
|
784
548
|
async createDynamicPlugin(plugin) {
|
|
785
549
|
if (typeof plugin === 'string') {
|
|
786
550
|
const _plugin = this.pluginManager.get(plugin);
|
|
@@ -804,13 +568,6 @@ export class MatterNode extends EventEmitter {
|
|
|
804
568
|
}
|
|
805
569
|
return this.serverNode;
|
|
806
570
|
}
|
|
807
|
-
/**
|
|
808
|
-
* Creates and configures the server node for a single not bridged device.
|
|
809
|
-
*
|
|
810
|
-
* @param {Plugin | PluginName} plugin - The plugin to configure.
|
|
811
|
-
* @param {MatterbridgeEndpoint} device - The device to associate with the plugin.
|
|
812
|
-
* @returns {Promise<ServerNode<ServerNode.RootEndpoint> | undefined>} A promise that resolves to the server node for the device with mode server.
|
|
813
|
-
*/
|
|
814
571
|
async createDeviceServerNode(plugin, device) {
|
|
815
572
|
if (typeof plugin === 'string') {
|
|
816
573
|
const _plugin = this.pluginManager.get(plugin);
|
|
@@ -831,22 +588,13 @@ export class MatterNode extends EventEmitter {
|
|
|
831
588
|
}
|
|
832
589
|
return this.serverNode;
|
|
833
590
|
}
|
|
834
|
-
/**
|
|
835
|
-
* Adds a MatterbridgeEndpoint to the specified plugin.
|
|
836
|
-
*
|
|
837
|
-
* @param {string} pluginName - The name of the plugin.
|
|
838
|
-
* @param {MatterbridgeEndpoint} device - The device to add as a bridged endpoint.
|
|
839
|
-
* @returns {Promise<MatterbridgeEndpoint | undefined>} A promise that resolves to the added bridged endpoint, or undefined if there was an error.
|
|
840
|
-
*/
|
|
841
591
|
async addBridgedEndpoint(pluginName, device) {
|
|
842
|
-
// Check if the plugin is registered
|
|
843
592
|
const plugin = this.pluginManager.get(pluginName);
|
|
844
593
|
if (!plugin)
|
|
845
594
|
throw new Error(`Error adding bridged endpoint ${plg}${pluginName}${er}:${dev}${device.deviceName}${er} (${zb}${device.name}${er}): plugin not found`);
|
|
846
595
|
if (device.mode === 'server') {
|
|
847
596
|
try {
|
|
848
597
|
this.log.debug(`Creating MatterNode for device ${plg}${pluginName}${db}:${dev}${device.deviceName}${db} (${zb}${device.name}${db})...`);
|
|
849
|
-
// Create the MatterNode to manage the device
|
|
850
598
|
const matterNode = new MatterNode(this.matterbridge, pluginName, device);
|
|
851
599
|
matterNode.port = this.port ? this.port++ : undefined;
|
|
852
600
|
matterNode.passcode = this.passcode ? this.passcode++ : undefined;
|
|
@@ -862,7 +610,6 @@ export class MatterNode extends EventEmitter {
|
|
|
862
610
|
}
|
|
863
611
|
else if (this.matterbridge.bridgeMode === 'bridge') {
|
|
864
612
|
if (device.mode === 'matter') {
|
|
865
|
-
// Register and add the device to the Matter server node
|
|
866
613
|
this.log.debug(`Adding matter endpoint ${plg}${pluginName}${db}:${dev}${device.deviceName}${db} (${zb}${device.name}${db})...`);
|
|
867
614
|
if (!this.serverNode)
|
|
868
615
|
throw new Error(`Server node not found for matter endpoint ${plg}${pluginName}${er}:${dev}${device.deviceName}${er} (${zb}${device.name}${er})`);
|
|
@@ -875,7 +622,6 @@ export class MatterNode extends EventEmitter {
|
|
|
875
622
|
}
|
|
876
623
|
}
|
|
877
624
|
else {
|
|
878
|
-
// Register and add the device to the Matter aggregator node
|
|
879
625
|
this.log.debug(`Adding bridged endpoint ${plg}${pluginName}${db}:${dev}${device.deviceName}${db} (${zb}${device.name}${db})...`);
|
|
880
626
|
if (!this.aggregatorNode)
|
|
881
627
|
throw new Error(`Aggregator node not found for endpoint ${plg}${pluginName}${er}:${dev}${device.deviceName}${er} (${zb}${device.name}${er})`);
|
|
@@ -889,7 +635,6 @@ export class MatterNode extends EventEmitter {
|
|
|
889
635
|
}
|
|
890
636
|
}
|
|
891
637
|
else if (this.matterbridge.bridgeMode === 'childbridge') {
|
|
892
|
-
// Register and add the device to the plugin server node
|
|
893
638
|
if (plugin.type === 'AccessoryPlatform') {
|
|
894
639
|
try {
|
|
895
640
|
this.log.debug(`Adding accessory endpoint ${plg}${pluginName}${db}:${dev}${device.deviceName}${db} (${zb}${device.name}${db})...`);
|
|
@@ -905,12 +650,10 @@ export class MatterNode extends EventEmitter {
|
|
|
905
650
|
return;
|
|
906
651
|
}
|
|
907
652
|
}
|
|
908
|
-
// Register and add the device to the plugin aggregator node
|
|
909
653
|
if (plugin.type === 'DynamicPlatform') {
|
|
910
654
|
try {
|
|
911
655
|
this.log.debug(`Adding bridged endpoint ${plg}${pluginName}${db}:${dev}${device.deviceName}${db} (${zb}${device.name}${db})...`);
|
|
912
656
|
if (!this.serverNode) {
|
|
913
|
-
// Fast plugins can add another device before the server node is ready, so we wait for the server node to be ready
|
|
914
657
|
await this.createDynamicPlugin(plugin);
|
|
915
658
|
}
|
|
916
659
|
if (device.mode === 'matter')
|
|
@@ -926,31 +669,19 @@ export class MatterNode extends EventEmitter {
|
|
|
926
669
|
}
|
|
927
670
|
if (plugin.registeredDevices !== undefined)
|
|
928
671
|
plugin.registeredDevices++;
|
|
929
|
-
// Add the device to the DeviceManager
|
|
930
672
|
await this.server.fetch({ type: 'devices_set', src: this.server.name, dst: 'devices', params: { device: toBaseDevice(device) } });
|
|
931
|
-
// Add the device to the DeviceManager
|
|
932
673
|
await device.construction.ready;
|
|
933
|
-
// Subscribe to the attributes changed event
|
|
934
674
|
await this.subscribeAttributeChanged(plugin, device);
|
|
935
675
|
this.log.info(`Added endpoint #${plugin.registeredDevices} ${plg}${pluginName}${nf}:${dev}${device.deviceName}${nf} (${zb}${device.name}${nf})`);
|
|
936
676
|
await this.yieldToNode(10);
|
|
937
677
|
return device;
|
|
938
678
|
}
|
|
939
|
-
/**
|
|
940
|
-
* Removes a MatterbridgeEndpoint from the specified plugin.
|
|
941
|
-
*
|
|
942
|
-
* @param {string} pluginName - The name of the plugin.
|
|
943
|
-
* @param {MatterbridgeEndpoint} device - The device to remove as a bridged endpoint.
|
|
944
|
-
* @returns {Promise<MatterbridgeEndpoint | undefined>} A promise that resolves to the removed bridged endpoint, or undefined if there was an error.
|
|
945
|
-
*/
|
|
946
679
|
async removeBridgedEndpoint(pluginName, device) {
|
|
947
680
|
this.log.debug(`Removing bridged endpoint ${plg}${pluginName}${db}:${dev}${device.deviceName}${db} (${zb}${device.name}${db})...`);
|
|
948
|
-
// Check if the plugin is registered
|
|
949
681
|
const plugin = this.pluginManager.get(pluginName);
|
|
950
682
|
if (!plugin)
|
|
951
683
|
throw new Error(`Error removing bridged endpoint ${plg}${pluginName}${er}:${dev}${device.deviceName}${er} (${zb}${device.name}${er}): plugin not found`);
|
|
952
684
|
if (device.serverNode) {
|
|
953
|
-
// TODO: Close and remove the MatterNode managing the device
|
|
954
685
|
}
|
|
955
686
|
else if (this.matterbridge.bridgeMode === 'bridge') {
|
|
956
687
|
if (!this.aggregatorNode)
|
|
@@ -972,25 +703,11 @@ export class MatterNode extends EventEmitter {
|
|
|
972
703
|
this.log.info(`Removed bridged endpoint #${plugin.registeredDevices} ${plg}${pluginName}${nf}:${dev}${device.deviceName}${nf} (${zb}${device.name}${nf})`);
|
|
973
704
|
if (plugin.registeredDevices !== undefined)
|
|
974
705
|
plugin.registeredDevices--;
|
|
975
|
-
// Remove the device from the DeviceManager
|
|
976
706
|
await this.server.fetch({ type: 'devices_remove', src: this.server.name, dst: 'devices', params: { device: toBaseDevice(device) } });
|
|
977
707
|
await this.yieldToNode(10);
|
|
978
708
|
return device;
|
|
979
709
|
}
|
|
980
|
-
/**
|
|
981
|
-
* Removes all bridged endpoints from the specified plugin.
|
|
982
|
-
*
|
|
983
|
-
* @param {string} pluginName - The name of the plugin.
|
|
984
|
-
* @param {number} [delay] - The delay in milliseconds between removing each bridged endpoint (default: 0).
|
|
985
|
-
* @returns {Promise<void>} A promise that resolves when all bridged endpoints have been removed.
|
|
986
|
-
*
|
|
987
|
-
* @remarks
|
|
988
|
-
* This method iterates through all devices in the DeviceManager and removes each bridged endpoint associated with the specified plugin.
|
|
989
|
-
* It also applies a delay between each removal if specified.
|
|
990
|
-
* The delay is useful to allow the controllers to receive a single subscription for each device removed.
|
|
991
|
-
*/
|
|
992
710
|
async removeAllBridgedEndpoints(pluginName, delay = 0) {
|
|
993
|
-
// Check if the plugin is registered
|
|
994
711
|
const plugin = this.pluginManager.get(pluginName);
|
|
995
712
|
if (!plugin)
|
|
996
713
|
throw new Error(`Error removing all bridged endpoints for plugin ${plg}${pluginName}${er}: plugin not found`);
|
|
@@ -1005,7 +722,6 @@ export class MatterNode extends EventEmitter {
|
|
|
1005
722
|
this.log.info(`Removed bridged endpoint #${plugin.registeredDevices} ${plg}${pluginName}${nf}:${dev}${device.deviceName}${nf} (${zb}${endpoint?.name}${nf})`);
|
|
1006
723
|
if (plugin.registeredDevices !== undefined)
|
|
1007
724
|
plugin.registeredDevices--;
|
|
1008
|
-
// Remove the device from the DeviceManager
|
|
1009
725
|
await this.server.fetch({ type: 'devices_remove', src: this.server.name, dst: 'devices', params: { device: toBaseDevice(device) } });
|
|
1010
726
|
await this.yieldToNode(10);
|
|
1011
727
|
if (delay > 0)
|
|
@@ -1014,25 +730,6 @@ export class MatterNode extends EventEmitter {
|
|
|
1014
730
|
if (delay > 0)
|
|
1015
731
|
await wait(Number(process.env['MATTERBRIDGE_REMOVE_ALL_ENDPOINT_TIMEOUT_MS']) || 2000);
|
|
1016
732
|
}
|
|
1017
|
-
/**
|
|
1018
|
-
* Registers a virtual device with the Matterbridge platform.
|
|
1019
|
-
* Virtual devices are only supported in bridge mode and childbridge mode with a DynamicPlatform.
|
|
1020
|
-
*
|
|
1021
|
-
* The virtual device is created as an instance of `Endpoint` with the provided device type.
|
|
1022
|
-
* When the virtual device is turned on, the provided callback function is executed.
|
|
1023
|
-
* The onOff state of the virtual device always reverts to false when the device is turned on.
|
|
1024
|
-
*
|
|
1025
|
-
* @param {string} pluginName - The name of the plugin.
|
|
1026
|
-
* @param { string } name - The name of the virtual device.
|
|
1027
|
-
* @param { 'light' | 'outlet' | 'switch' | 'mounted_switch' } type - The type of the virtual device.
|
|
1028
|
-
* @param { () => Promise<void> } callback - The callback to call when the virtual device is turned on.
|
|
1029
|
-
*
|
|
1030
|
-
* @returns {Promise<boolean>} A promise that resolves to true if the virtual device was successfully registered, false otherwise.
|
|
1031
|
-
*
|
|
1032
|
-
* @remarks
|
|
1033
|
-
* The virtual devices don't show up in the device list of the frontend.
|
|
1034
|
-
* Type 'switch' is not supported by Alexa and 'mounted_switch' is not supported by Apple Home.
|
|
1035
|
-
*/
|
|
1036
733
|
async addVirtualEndpoint(pluginName, name, type, callback) {
|
|
1037
734
|
this.log.debug(`Creating virtual device ${plg}${pluginName}${db}:${dev}${name}${db}...`);
|
|
1038
735
|
const plugin = this.pluginManager.get(pluginName);
|
|
@@ -1057,20 +754,10 @@ export class MatterNode extends EventEmitter {
|
|
|
1057
754
|
await this.yieldToNode(10);
|
|
1058
755
|
return true;
|
|
1059
756
|
}
|
|
1060
|
-
/**
|
|
1061
|
-
* Subscribes to the attribute change event for the given device and plugin.
|
|
1062
|
-
* Specifically, it listens for changes in the 'reachable' attribute of the
|
|
1063
|
-
* BridgedDeviceBasicInformationServer cluster server of the bridged device or BasicInformationServer cluster server of server node.
|
|
1064
|
-
*
|
|
1065
|
-
* @param {Plugin} plugin - The plugin associated with the device.
|
|
1066
|
-
* @param {MatterbridgeEndpoint} device - The device to subscribe to attribute changes for.
|
|
1067
|
-
* @returns {Promise<void>} A promise that resolves when the subscription is set up.
|
|
1068
|
-
*/
|
|
1069
757
|
async subscribeAttributeChanged(plugin, device) {
|
|
1070
758
|
if (!plugin || !device || !device.plugin || !device.serialNumber || !device.uniqueId || !device.maybeNumber)
|
|
1071
759
|
return;
|
|
1072
760
|
this.log.debug(`Subscribing attributes for endpoint ${plg}${plugin.name}${db}:${dev}${device.deviceName}${db}:${or}${device.id}${db}:${or}${device.number}${db} (${zb}${device.name}${db})`);
|
|
1073
|
-
// Subscribe to the reachable$Changed event of the BasicInformationServer cluster server of the server node in childbridge mode
|
|
1074
761
|
if (this.matterbridge.bridgeMode === 'childbridge' && plugin.type === 'AccessoryPlatform' && this.serverNode) {
|
|
1075
762
|
this.serverNode.eventsOf(BasicInformationServer).reachable$Changed?.on((reachable) => {
|
|
1076
763
|
this.log.debug(`Accessory endpoint ${plg}${plugin.name}${nf}:${dev}${device.deviceName}${nf}:${or}${device.id}${nf}:${or}${device.number}${nf} is ${reachable ? 'reachable' : 'unreachable'}`);
|
|
@@ -1078,7 +765,6 @@ export class MatterNode extends EventEmitter {
|
|
|
1078
765
|
type: 'frontend_attributechanged',
|
|
1079
766
|
src: 'matter',
|
|
1080
767
|
dst: 'frontend',
|
|
1081
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
1082
768
|
params: { plugin: device.plugin, serialNumber: device.serialNumber, uniqueId: device.uniqueId, number: device.number, id: device.id, cluster: 'BasicInformation', attribute: 'reachable', value: reachable },
|
|
1083
769
|
});
|
|
1084
770
|
});
|
|
@@ -1131,7 +817,6 @@ export class MatterNode extends EventEmitter {
|
|
|
1131
817
|
type: 'frontend_attributechanged',
|
|
1132
818
|
src: 'matter',
|
|
1133
819
|
dst: 'frontend',
|
|
1134
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
1135
820
|
params: { plugin: device.plugin, serialNumber: device.serialNumber, uniqueId: device.uniqueId, number: device.number, id: device.id, cluster: sub.cluster, attribute: sub.attribute, value: value },
|
|
1136
821
|
});
|
|
1137
822
|
});
|
|
@@ -1145,7 +830,6 @@ export class MatterNode extends EventEmitter {
|
|
|
1145
830
|
type: 'frontend_attributechanged',
|
|
1146
831
|
src: 'matter',
|
|
1147
832
|
dst: 'frontend',
|
|
1148
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
1149
833
|
params: { plugin: device.plugin, serialNumber: device.serialNumber, uniqueId: device.uniqueId, number: child.number, id: child.id, cluster: sub.cluster, attribute: sub.attribute, value: value },
|
|
1150
834
|
});
|
|
1151
835
|
});
|
|
@@ -1153,12 +837,6 @@ export class MatterNode extends EventEmitter {
|
|
|
1153
837
|
}
|
|
1154
838
|
}
|
|
1155
839
|
}
|
|
1156
|
-
/**
|
|
1157
|
-
* Sanitizes the fabric information by converting bigint properties to strings because `res.json` doesn't support bigint.
|
|
1158
|
-
*
|
|
1159
|
-
* @param {ExposedFabricInformation[]} fabricInfo - The array of exposed fabric information objects.
|
|
1160
|
-
* @returns {SanitizedExposedFabricInformation[]} An array of sanitized exposed fabric information objects.
|
|
1161
|
-
*/
|
|
1162
840
|
sanitizeFabricInformations(fabricInfo) {
|
|
1163
841
|
return fabricInfo.map((info) => {
|
|
1164
842
|
return {
|
|
@@ -1172,12 +850,6 @@ export class MatterNode extends EventEmitter {
|
|
|
1172
850
|
};
|
|
1173
851
|
});
|
|
1174
852
|
}
|
|
1175
|
-
/**
|
|
1176
|
-
* Sanitizes the session information by converting bigint properties to strings because `res.json` doesn't support bigint.
|
|
1177
|
-
*
|
|
1178
|
-
* @param {SessionsBehavior.Session[]} sessions - The array of session information objects.
|
|
1179
|
-
* @returns {SanitizedSession[]} An array of sanitized session information objects.
|
|
1180
|
-
*/
|
|
1181
853
|
sanitizeSessionInformation(sessions) {
|
|
1182
854
|
return sessions
|
|
1183
855
|
.filter((session) => session.isPeerActive)
|
|
@@ -1204,21 +876,10 @@ export class MatterNode extends EventEmitter {
|
|
|
1204
876
|
};
|
|
1205
877
|
});
|
|
1206
878
|
}
|
|
1207
|
-
/**
|
|
1208
|
-
* Sets the reachability of the specified server node and trigger the corresponding event.
|
|
1209
|
-
*
|
|
1210
|
-
* @param {boolean} reachable - A boolean indicating the reachability status to set.
|
|
1211
|
-
*/
|
|
1212
879
|
async setServerReachability(reachable) {
|
|
1213
880
|
await this.serverNode?.setStateOf(BasicInformationServer, { reachable });
|
|
1214
881
|
this.serverNode?.act((agent) => this.serverNode?.eventsOf(BasicInformationServer).reachableChanged?.emit({ reachableNewValue: reachable }, agent.context));
|
|
1215
882
|
}
|
|
1216
|
-
/**
|
|
1217
|
-
* Sets the reachability of the specified aggregator node bridged devices and trigger.
|
|
1218
|
-
*
|
|
1219
|
-
* @param {Endpoint<AggregatorEndpoint>} aggregatorNode - The aggregator node to set the reachability for.
|
|
1220
|
-
* @param {boolean} reachable - A boolean indicating the reachability status to set.
|
|
1221
|
-
*/
|
|
1222
883
|
async setAggregatorReachability(aggregatorNode, reachable) {
|
|
1223
884
|
for (const child of aggregatorNode.parts) {
|
|
1224
885
|
this.log.debug(`Setting reachability of ${child?.deviceName} to ${reachable}`);
|
|
@@ -1264,35 +925,19 @@ export class MatterNode extends EventEmitter {
|
|
|
1264
925
|
case 0x1488:
|
|
1265
926
|
vendorName = '(ShortcutLabsFlic)';
|
|
1266
927
|
break;
|
|
1267
|
-
case 65521:
|
|
928
|
+
case 65521:
|
|
1268
929
|
vendorName = '(MatterTest)';
|
|
1269
930
|
break;
|
|
1270
931
|
}
|
|
1271
932
|
return vendorName;
|
|
1272
933
|
};
|
|
1273
|
-
/**
|
|
1274
|
-
* Yield to the Node.js event loop:
|
|
1275
|
-
* 1. Flushes the current microtask queue (Promise/async continuations queued so far).
|
|
1276
|
-
* 2. Yields one macrotask turn (setImmediate) and then its microtasks.
|
|
1277
|
-
* 3. Waits a bit (setTimeout) to allow other macrotasks to run.
|
|
1278
|
-
*
|
|
1279
|
-
* This does **not** guarantee that every promise in the process is settled,
|
|
1280
|
-
* but it gives all already-scheduled work a very good chance to run before continuing.
|
|
1281
|
-
*
|
|
1282
|
-
* @param {number} [timeout] - Optional timeout in milliseconds to wait after yielding. Default is 100 ms (minimum 10 ms).
|
|
1283
|
-
* @returns {Promise<void>}
|
|
1284
|
-
*/
|
|
1285
934
|
async yieldToNode(timeout = 100) {
|
|
1286
|
-
// 1. Let all currently queued microtasks run
|
|
1287
935
|
await Promise.resolve();
|
|
1288
|
-
// 2. Yield to the next event-loop turn (macrotask + its microtasks)
|
|
1289
936
|
await new Promise((resolve) => {
|
|
1290
937
|
setImmediate(resolve);
|
|
1291
938
|
});
|
|
1292
|
-
// 3. Pause a bit to allow other macrotasks to run
|
|
1293
939
|
await new Promise((resolve) => {
|
|
1294
940
|
setTimeout(resolve, Math.min(timeout, 10));
|
|
1295
941
|
});
|
|
1296
942
|
}
|
|
1297
943
|
}
|
|
1298
|
-
//# sourceMappingURL=matterNode.js.map
|