matterbridge 1.6.0 → 1.6.2
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 +53 -1
- package/README-DEV.md +0 -4
- package/README-NGINX.md +63 -0
- package/README.md +8 -0
- package/dist/cli.d.ts +1 -1
- package/dist/cli.js +18 -8
- package/dist/cli.js.map +1 -1
- package/dist/defaultConfigSchema.d.ts +1 -1
- package/dist/defaultConfigSchema.js +1 -1
- package/dist/deviceManager.d.ts +2 -2
- package/dist/deviceManager.d.ts.map +1 -1
- package/dist/deviceManager.js +2 -2
- package/dist/deviceManager.js.map +1 -1
- package/dist/index.d.ts +10 -10
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +24 -11
- package/dist/index.js.map +1 -1
- package/dist/matter/export.d.ts +5 -0
- package/dist/matter/export.d.ts.map +1 -0
- package/dist/matter/export.js +5 -0
- package/dist/matter/export.js.map +1 -0
- package/dist/matterbridge.d.ts +32 -16
- package/dist/matterbridge.d.ts.map +1 -1
- package/dist/matterbridge.js +254 -115
- package/dist/matterbridge.js.map +1 -1
- package/dist/matterbridgeAccessoryPlatform.d.ts +1 -1
- package/dist/matterbridgeAccessoryPlatform.d.ts.map +1 -1
- package/dist/matterbridgeAccessoryPlatform.js +1 -1
- package/dist/matterbridgeAccessoryPlatform.js.map +1 -1
- package/dist/matterbridgeBehaviors.d.ts +1123 -0
- package/dist/matterbridgeBehaviors.d.ts.map +1 -0
- package/dist/matterbridgeBehaviors.js +281 -0
- package/dist/matterbridgeBehaviors.js.map +1 -0
- package/dist/matterbridgeDevice.d.ts +2069 -1511
- package/dist/matterbridgeDevice.d.ts.map +1 -1
- package/dist/matterbridgeDevice.js +192 -196
- package/dist/matterbridgeDevice.js.map +1 -1
- package/dist/matterbridgeDeviceTypes.d.ts +58 -0
- package/dist/matterbridgeDeviceTypes.d.ts.map +1 -0
- package/dist/matterbridgeDeviceTypes.js +308 -0
- package/dist/matterbridgeDeviceTypes.js.map +1 -0
- package/dist/matterbridgeDynamicPlatform.d.ts +1 -1
- package/dist/matterbridgeDynamicPlatform.d.ts.map +1 -1
- package/dist/matterbridgeDynamicPlatform.js +1 -1
- package/dist/matterbridgeDynamicPlatform.js.map +1 -1
- package/dist/matterbridgeEdge.d.ts +20 -21
- package/dist/matterbridgeEdge.d.ts.map +1 -1
- package/dist/matterbridgeEdge.js +506 -103
- package/dist/matterbridgeEdge.js.map +1 -1
- package/dist/matterbridgeEndpoint.d.ts +3545 -2336
- package/dist/matterbridgeEndpoint.d.ts.map +1 -1
- package/dist/matterbridgeEndpoint.js +672 -468
- package/dist/matterbridgeEndpoint.js.map +1 -1
- package/dist/matterbridgePlatform.d.ts +5 -4
- package/dist/matterbridgePlatform.d.ts.map +1 -1
- package/dist/matterbridgePlatform.js +11 -3
- package/dist/matterbridgePlatform.js.map +1 -1
- package/dist/matterbridgeTypes.d.ts +7 -8
- package/dist/matterbridgeTypes.d.ts.map +1 -1
- package/dist/matterbridgeTypes.js.map +1 -1
- package/dist/matterbridgeWebsocket.d.ts +1 -1
- package/dist/matterbridgeWebsocket.d.ts.map +1 -1
- package/dist/matterbridgeWebsocket.js +41 -3
- package/dist/matterbridgeWebsocket.js.map +1 -1
- package/dist/pluginManager.d.ts +1 -1
- package/dist/pluginManager.d.ts.map +1 -1
- package/dist/pluginManager.js +17 -8
- package/dist/pluginManager.js.map +1 -1
- package/dist/utils/colorUtils.d.ts +1 -1
- package/dist/utils/colorUtils.js +2 -2
- package/dist/utils/colorUtils.js.map +1 -1
- package/dist/utils/utils.d.ts +42 -1
- package/dist/utils/utils.d.ts.map +1 -1
- package/dist/utils/utils.js +105 -3
- package/dist/utils/utils.js.map +1 -1
- package/frontend/build/asset-manifest.json +62 -6
- package/frontend/build/index.html +1 -1
- package/frontend/build/static/css/main.823e08b6.css +2 -0
- package/frontend/build/static/css/main.823e08b6.css.map +1 -0
- package/frontend/build/static/js/main.a14c87e7.js +115 -0
- package/frontend/build/static/js/{main.045d08f7.js.LICENSE.txt → main.a14c87e7.js.LICENSE.txt} +3 -3
- package/frontend/build/static/js/main.a14c87e7.js.map +1 -0
- package/frontend/build/static/media/roboto-cyrillic-300-normal.1b79538ccd585c259996.woff2 +0 -0
- package/frontend/build/static/media/roboto-cyrillic-300-normal.5f077fd7b977d1715acf.woff +0 -0
- package/frontend/build/static/media/roboto-cyrillic-400-normal.5d2930082227d172f62c.woff +0 -0
- package/frontend/build/static/media/roboto-cyrillic-400-normal.a9e19870cf6c4b973427.woff2 +0 -0
- package/frontend/build/static/media/roboto-cyrillic-500-normal.0ae2428323939af5e1ad.woff2 +0 -0
- package/frontend/build/static/media/roboto-cyrillic-500-normal.dd7bc8a52c6c70c5a3f5.woff +0 -0
- package/frontend/build/static/media/roboto-cyrillic-700-normal.3f6e1548bd5175a8c342.woff +0 -0
- package/frontend/build/static/media/roboto-cyrillic-700-normal.4fdfc29a10e7d4b7c527.woff2 +0 -0
- package/frontend/build/static/media/roboto-cyrillic-ext-300-normal.795dbc8140e3fef82983.woff +0 -0
- package/frontend/build/static/media/roboto-cyrillic-ext-300-normal.80947a31d23c70204b47.woff2 +0 -0
- package/frontend/build/static/media/roboto-cyrillic-ext-400-normal.135d076fa32aa0b4d105.woff +0 -0
- package/frontend/build/static/media/roboto-cyrillic-ext-400-normal.5cec61a21cc20180fbe1.woff2 +0 -0
- package/frontend/build/static/media/roboto-cyrillic-ext-500-normal.6de16332fda843a3dc3d.woff2 +0 -0
- package/frontend/build/static/media/roboto-cyrillic-ext-500-normal.c0a0638f90b31d6454ba.woff +0 -0
- package/frontend/build/static/media/roboto-cyrillic-ext-700-normal.4750292c47fa2bc6ac1a.woff2 +0 -0
- package/frontend/build/static/media/roboto-cyrillic-ext-700-normal.ca247189fc12d00de361.woff +0 -0
- package/frontend/build/static/media/roboto-greek-300-normal.285f3e6261d8eb20417d.woff2 +0 -0
- package/frontend/build/static/media/roboto-greek-300-normal.889beddda1c9bd9f97df.woff +0 -0
- package/frontend/build/static/media/roboto-greek-400-normal.160a791a8e4f46bca3cc.woff +0 -0
- package/frontend/build/static/media/roboto-greek-400-normal.2c32b1315be61477013a.woff2 +0 -0
- package/frontend/build/static/media/roboto-greek-500-normal.60810e07c7b0273013aa.woff +0 -0
- package/frontend/build/static/media/roboto-greek-500-normal.f95e757c5483310f9c11.woff2 +0 -0
- package/frontend/build/static/media/roboto-greek-700-normal.77dd370f2001e184ba0d.woff2 +0 -0
- package/frontend/build/static/media/roboto-greek-700-normal.df87b053fae3d7ad5f7a.woff +0 -0
- package/frontend/build/static/media/roboto-greek-ext-300-normal.b590dbe5c639944366d1.woff +0 -0
- package/frontend/build/static/media/roboto-greek-ext-300-normal.d6049cb54aa6fbe14c42.woff2 +0 -0
- package/frontend/build/static/media/roboto-greek-ext-400-normal.16eb83b4a3b1ea994243.woff +0 -0
- package/frontend/build/static/media/roboto-greek-ext-400-normal.1df4abad55796d11a0c8.woff2 +0 -0
- package/frontend/build/static/media/roboto-greek-ext-500-normal.4a96ba31abcce0f5d52b.woff2 +0 -0
- package/frontend/build/static/media/roboto-greek-ext-500-normal.fd28d9c008bf3af1bed7.woff +0 -0
- package/frontend/build/static/media/roboto-greek-ext-700-normal.2dd6febad11502dec6a6.woff2 +0 -0
- package/frontend/build/static/media/roboto-greek-ext-700-normal.4abdc9fff4507f17d726.woff +0 -0
- package/frontend/build/static/media/roboto-latin-300-normal.b850f1ff581ea232fac9.woff2 +0 -0
- package/frontend/build/static/media/roboto-latin-300-normal.c4bc0593c9954d79cb3a.woff +0 -0
- package/frontend/build/static/media/roboto-latin-400-normal.047a7839f69b209db815.woff +0 -0
- package/frontend/build/static/media/roboto-latin-400-normal.297d48e1b5a10c0831a9.woff2 +0 -0
- package/frontend/build/static/media/roboto-latin-500-normal.68d40d6d01c6f85d24ba.woff +0 -0
- package/frontend/build/static/media/roboto-latin-500-normal.7077203b1982951ecf76.woff2 +0 -0
- package/frontend/build/static/media/roboto-latin-700-normal.4535474e1cf8598695ad.woff2 +0 -0
- package/frontend/build/static/media/roboto-latin-700-normal.9f6a16a7770c87b2042b.woff +0 -0
- package/frontend/build/static/media/roboto-latin-ext-300-normal.14982a9e4857a93b6dce.woff +0 -0
- package/frontend/build/static/media/roboto-latin-ext-300-normal.97cbc447d4a8d41a9543.woff2 +0 -0
- package/frontend/build/static/media/roboto-latin-ext-400-normal.27da5b36b6d3a16f53f4.woff +0 -0
- package/frontend/build/static/media/roboto-latin-ext-400-normal.2eeae187764baf05867d.woff2 +0 -0
- package/frontend/build/static/media/roboto-latin-ext-500-normal.06c30711d588145a4541.woff +0 -0
- package/frontend/build/static/media/roboto-latin-ext-500-normal.9a18d7bb9ff7a6af7b32.woff2 +0 -0
- package/frontend/build/static/media/roboto-latin-ext-700-normal.18841836e391d39e83a8.woff2 +0 -0
- package/frontend/build/static/media/roboto-latin-ext-700-normal.3c5bcdd0e69c4c3ffafe.woff +0 -0
- package/frontend/build/static/media/roboto-vietnamese-300-normal.c96b16e5c05c7b7c3e89.woff2 +0 -0
- package/frontend/build/static/media/roboto-vietnamese-300-normal.f5e7cea32756dfe7af40.woff +0 -0
- package/frontend/build/static/media/roboto-vietnamese-400-normal.0dc97c66f9b542d6fa17.woff +0 -0
- package/frontend/build/static/media/roboto-vietnamese-400-normal.d3f8e26d6c27de8102b6.woff2 +0 -0
- package/frontend/build/static/media/roboto-vietnamese-500-normal.090fabef926bdc0e9b9f.woff2 +0 -0
- package/frontend/build/static/media/roboto-vietnamese-500-normal.23b7b8a2524d2d4b637b.woff +0 -0
- package/frontend/build/static/media/roboto-vietnamese-700-normal.0a79a9fabfc32e33f360.woff2 +0 -0
- package/frontend/build/static/media/roboto-vietnamese-700-normal.35ed0597568ff6f19c16.woff +0 -0
- package/npm-shrinkwrap.json +120 -39
- package/package.json +8 -3
- package/dist/matterbridgeController.d.ts +0 -24
- package/dist/matterbridgeController.d.ts.map +0 -1
- package/dist/matterbridgeController.js +0 -386
- package/dist/matterbridgeController.js.map +0 -1
- package/frontend/build/static/css/main.1cf003ae.css +0 -2
- package/frontend/build/static/css/main.1cf003ae.css.map +0 -1
- package/frontend/build/static/js/main.045d08f7.js +0 -3
- package/frontend/build/static/js/main.045d08f7.js.map +0 -1
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* @date 2024-10-01
|
|
7
7
|
* @version 1.0.0
|
|
8
8
|
*
|
|
9
|
-
* Copyright 2024 Luca Liguori.
|
|
9
|
+
* Copyright 2024, 2025, 2026 Luca Liguori.
|
|
10
10
|
*
|
|
11
11
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
12
12
|
* you may not use this file except in compliance with the License.
|
|
@@ -21,81 +21,124 @@
|
|
|
21
21
|
* limitations under the License. *
|
|
22
22
|
*/
|
|
23
23
|
/* eslint-disable @typescript-eslint/no-unused-vars */
|
|
24
|
-
//
|
|
25
|
-
import { Endpoint } from '@project-chip/matter.js/endpoint';
|
|
26
|
-
import { MutableEndpoint } from '@project-chip/matter.js/endpoint/type';
|
|
27
|
-
import { SupportedBehaviors } from '@project-chip/matter.js/endpoint/properties';
|
|
28
|
-
import { DescriptorServer } from '@project-chip/matter.js/behavior/definitions/descriptor';
|
|
29
|
-
import { IdentifyServer } from '@project-chip/matter.js/behavior/definitions/identify';
|
|
30
|
-
import { GroupsServer } from '@project-chip/matter.js/behavior/definitions/groups';
|
|
31
|
-
// import { ScenesServer, ScenesBehavior } from '@project-chip/matter.js/behavior/definitions/scenes';
|
|
32
|
-
import { OnOffServer } from '@project-chip/matter.js/behavior/definitions/on-off';
|
|
33
|
-
import { TemperatureMeasurementServer } from '@project-chip/matter.js/behavior/definitions/temperature-measurement';
|
|
34
|
-
import { RelativeHumidityMeasurementServer } from '@project-chip/matter.js/behavior/definitions/relative-humidity-measurement';
|
|
35
|
-
import { PressureMeasurementServer } from '@project-chip/matter.js/behavior/definitions/pressure-measurement';
|
|
36
|
-
import { BridgedDeviceBasicInformationServer } from '@project-chip/matter.js/behavior/definitions/bridged-device-basic-information';
|
|
37
|
-
import { AirQuality, AirQualityCluster, BasicInformationCluster, BooleanState, BooleanStateCluster, BooleanStateConfiguration, BooleanStateConfigurationCluster, BridgedDeviceBasicInformation, BridgedDeviceBasicInformationCluster, CarbonDioxideConcentrationMeasurement, CarbonDioxideConcentrationMeasurementCluster, CarbonMonoxideConcentrationMeasurement, CarbonMonoxideConcentrationMeasurementCluster, ClusterServer, ColorControl, ColorControlCluster, ConcentrationMeasurement, DoorLock, DoorLockCluster, ElectricalEnergyMeasurement, ElectricalEnergyMeasurementCluster, ElectricalPowerMeasurement, ElectricalPowerMeasurementCluster, FanControl, FanControlCluster, FlowMeasurement, FlowMeasurementCluster, FormaldehydeConcentrationMeasurement, FormaldehydeConcentrationMeasurementCluster, Groups, GroupsCluster, GroupsClusterHandler, Identify, IdentifyCluster, IlluminanceMeasurement, IlluminanceMeasurementCluster, LevelControl, LevelControlCluster, MeasurementType, ModeSelectCluster, NitrogenDioxideConcentrationMeasurement, NitrogenDioxideConcentrationMeasurementCluster, OccupancySensing, OccupancySensingCluster, OnOff, OnOffCluster, OzoneConcentrationMeasurement, OzoneConcentrationMeasurementCluster, Pm10ConcentrationMeasurement, Pm10ConcentrationMeasurementCluster, Pm1ConcentrationMeasurement, Pm1ConcentrationMeasurementCluster, Pm25ConcentrationMeasurement, Pm25ConcentrationMeasurementCluster, PowerSource, PowerSourceCluster, PowerSourceConfigurationCluster, PowerTopology, PowerTopologyCluster, PressureMeasurement, PressureMeasurementCluster, RadonConcentrationMeasurement, RadonConcentrationMeasurementCluster, RelativeHumidityMeasurement, RelativeHumidityMeasurementCluster, SmokeCoAlarm, SmokeCoAlarmCluster, Switch, SwitchCluster, TemperatureMeasurement, TemperatureMeasurementCluster, Thermostat, ThermostatCluster, ThreadNetworkDiagnostics, ThreadNetworkDiagnosticsCluster, TimeSynchronization, TimeSynchronizationCluster, TotalVolatileOrganicCompoundsConcentrationMeasurement, TotalVolatileOrganicCompoundsConcentrationMeasurementCluster, WindowCovering, WindowCoveringCluster, getClusterNameById, } from '@project-chip/matter-node.js/cluster';
|
|
38
|
-
import { NamedHandler } from '@project-chip/matter-node.js/util';
|
|
39
|
-
import { EndpointNumber, VendorId } from '@project-chip/matter-node.js/datatype';
|
|
40
|
-
// Matterbridge imports
|
|
41
|
-
import { AnsiLogger, YELLOW, db, debugStringify, er, hk, or, rs, zb } from 'node-ansi-logger';
|
|
24
|
+
// Node.js modules
|
|
42
25
|
import { createHash } from 'crypto';
|
|
43
|
-
|
|
44
|
-
import {
|
|
45
|
-
|
|
46
|
-
import {
|
|
47
|
-
import {
|
|
48
|
-
import {
|
|
49
|
-
|
|
50
|
-
import {
|
|
51
|
-
import {
|
|
52
|
-
import {
|
|
53
|
-
import {
|
|
54
|
-
import {
|
|
55
|
-
import {
|
|
26
|
+
// AnsiLogger module
|
|
27
|
+
import { AnsiLogger, BLUE, CYAN, YELLOW, db, debugStringify, er, hk, or, rs, zb } from 'node-ansi-logger';
|
|
28
|
+
// Matterbridge
|
|
29
|
+
import { MatterbridgeBehavior, MatterbridgeBehaviorDevice, MatterbridgeBooleanStateConfigurationServer, MatterbridgeColorControlServer, MatterbridgeDoorLockServer, MatterbridgeFanControlServer, MatterbridgeIdentifyServer, MatterbridgeLevelControlServer, MatterbridgeOnOffServer, MatterbridgeThermostatServer, MatterbridgeWindowCoveringServer, } from './matterbridgeBehaviors.js';
|
|
30
|
+
import { bridgedNode } from './matterbridgeDeviceTypes.js';
|
|
31
|
+
import { deepCopy, isValidNumber } from './utils/utils.js';
|
|
32
|
+
// @matter
|
|
33
|
+
import { Endpoint, MutableEndpoint, SupportedBehaviors, NamedHandler, Lifecycle } from '@matter/main';
|
|
34
|
+
import { EndpointNumber, VendorId } from '@matter/main';
|
|
35
|
+
import { AirQuality, AirQualityCluster, BasicInformation, BasicInformationCluster, BooleanState, BooleanStateCluster, BooleanStateConfiguration, BooleanStateConfigurationCluster, BridgedDeviceBasicInformation, BridgedDeviceBasicInformationCluster, CarbonDioxideConcentrationMeasurement, CarbonDioxideConcentrationMeasurementCluster, CarbonMonoxideConcentrationMeasurement, CarbonMonoxideConcentrationMeasurementCluster, ColorControl, ColorControlCluster, ConcentrationMeasurement, Descriptor, DoorLock, DoorLockCluster, ElectricalEnergyMeasurement, ElectricalEnergyMeasurementCluster, ElectricalPowerMeasurement, ElectricalPowerMeasurementCluster, FanControl, FanControlCluster, FixedLabel, FixedLabelCluster, FlowMeasurement, FlowMeasurementCluster, FormaldehydeConcentrationMeasurement, FormaldehydeConcentrationMeasurementCluster, Groups, GroupsCluster, Identify, IdentifyCluster, IlluminanceMeasurement, IlluminanceMeasurementCluster, LevelControl, LevelControlCluster, ModeSelect, ModeSelectCluster, NitrogenDioxideConcentrationMeasurement, NitrogenDioxideConcentrationMeasurementCluster, OccupancySensing, OccupancySensingCluster, OnOff, OnOffCluster, OzoneConcentrationMeasurement, OzoneConcentrationMeasurementCluster, Pm10ConcentrationMeasurement, Pm10ConcentrationMeasurementCluster, Pm1ConcentrationMeasurement, Pm1ConcentrationMeasurementCluster, Pm25ConcentrationMeasurement, Pm25ConcentrationMeasurementCluster, PowerSource, PowerSourceCluster, PowerSourceConfigurationCluster, PowerTopology, PowerTopologyCluster, PressureMeasurement, PressureMeasurementCluster, RadonConcentrationMeasurement, RadonConcentrationMeasurementCluster, RelativeHumidityMeasurement, RelativeHumidityMeasurementCluster, SmokeCoAlarm, SmokeCoAlarmCluster, Switch, SwitchCluster, TemperatureMeasurement, TemperatureMeasurementCluster, Thermostat, ThermostatCluster, TotalVolatileOrganicCompoundsConcentrationMeasurement, TotalVolatileOrganicCompoundsConcentrationMeasurementCluster, UserLabel, UserLabelCluster, WindowCovering, WindowCoveringCluster, } from '@matter/main/clusters';
|
|
36
|
+
import { MeasurementType, getClusterNameById } from '@matter/main/types';
|
|
37
|
+
import { Specification } from '@matter/main/model';
|
|
38
|
+
import { DescriptorServer } from '@matter/node/behaviors/descriptor';
|
|
39
|
+
import { IdentifyBehavior } from '@matter/node/behaviors/identify';
|
|
40
|
+
import { GroupsServer } from '@matter/node/behaviors/groups';
|
|
41
|
+
import { TemperatureMeasurementServer } from '@matter/node/behaviors/temperature-measurement';
|
|
42
|
+
import { RelativeHumidityMeasurementServer } from '@matter/node/behaviors/relative-humidity-measurement';
|
|
43
|
+
import { PressureMeasurementServer } from '@matter/node/behaviors/pressure-measurement';
|
|
44
|
+
import { BridgedDeviceBasicInformationServer } from '@matter/node/behaviors/bridged-device-basic-information';
|
|
45
|
+
import { FlowMeasurementServer } from '@matter/node/behaviors/flow-measurement';
|
|
46
|
+
import { IlluminanceMeasurementServer } from '@matter/node/behaviors/illuminance-measurement';
|
|
47
|
+
import { BooleanStateServer } from '@matter/node/behaviors/boolean-state';
|
|
48
|
+
import { OccupancySensingServer } from '@matter/node/behaviors/occupancy-sensing';
|
|
49
|
+
import { AirQualityServer, BasicInformationServer, CarbonDioxideConcentrationMeasurementServer, CarbonMonoxideConcentrationMeasurementServer, ElectricalEnergyMeasurementServer, ElectricalPowerMeasurementServer, FixedLabelServer, FormaldehydeConcentrationMeasurementServer, ModeSelectServer, NitrogenDioxideConcentrationMeasurementServer, OzoneConcentrationMeasurementServer, Pm10ConcentrationMeasurementServer, Pm1ConcentrationMeasurementServer, Pm25ConcentrationMeasurementServer, PowerSourceServer, PowerTopologyServer, RadonConcentrationMeasurementServer, SmokeCoAlarmServer, SwitchServer, TotalVolatileOrganicCompoundsConcentrationMeasurementServer, UserLabelServer, } from '@matter/main/behaviors';
|
|
50
|
+
import { ClusterServer, GroupsClusterHandler } from '@project-chip/matter.js/cluster';
|
|
56
51
|
export class MatterbridgeEndpoint extends Endpoint {
|
|
57
52
|
static bridgeMode = '';
|
|
53
|
+
static logLevel = "info" /* LogLevel.INFO */;
|
|
58
54
|
log;
|
|
59
|
-
|
|
55
|
+
plugin = undefined;
|
|
60
56
|
deviceName = undefined;
|
|
57
|
+
serialNumber = undefined;
|
|
61
58
|
uniqueId = undefined;
|
|
59
|
+
vendorId = undefined;
|
|
60
|
+
vendorName = undefined;
|
|
61
|
+
productId = undefined;
|
|
62
|
+
productName = undefined;
|
|
63
|
+
softwareVersion = undefined;
|
|
64
|
+
softwareVersionString = undefined;
|
|
65
|
+
hardwareVersion = undefined;
|
|
66
|
+
hardwareVersionString = undefined;
|
|
67
|
+
uniqueStorageKey = undefined;
|
|
68
|
+
tagList = undefined;
|
|
62
69
|
// Maps matter deviceTypes and endpoints
|
|
63
70
|
deviceTypes = new Map();
|
|
64
71
|
clusterServers = new Map();
|
|
65
72
|
clusterClients = new Map();
|
|
66
73
|
commandHandler = new NamedHandler();
|
|
67
74
|
/**
|
|
68
|
-
* Represents a MatterbridgeEndpoint
|
|
75
|
+
* Represents a MatterbridgeEndpoint.
|
|
69
76
|
* @constructor
|
|
70
|
-
* @param {DeviceTypeDefinition} definition - The
|
|
77
|
+
* @param {DeviceTypeDefinition | AtLeastOne<DeviceTypeDefinition>} definition - The DeviceTypeDefinition(s) of the endpoint.
|
|
71
78
|
* @param {EndpointOptions} [options={}] - The options for the device.
|
|
72
79
|
*/
|
|
73
|
-
constructor(definition, options = {}) {
|
|
74
|
-
|
|
80
|
+
constructor(definition, options = {}, debug = false) {
|
|
81
|
+
const { uniqueStorageKey, endpointId, tagList, colorControlFeatures } = options;
|
|
82
|
+
// Get the first DeviceTypeDefinition
|
|
83
|
+
let firstDefinition;
|
|
84
|
+
if (Array.isArray(definition))
|
|
85
|
+
firstDefinition = definition[0];
|
|
86
|
+
else
|
|
87
|
+
firstDefinition = definition;
|
|
88
|
+
// Convert the first DeviceTypeDefinition to an EndpointType.Options
|
|
75
89
|
const deviceTypeDefinitionV8 = {
|
|
76
|
-
name:
|
|
77
|
-
deviceType:
|
|
78
|
-
deviceRevision:
|
|
79
|
-
deviceClass:
|
|
90
|
+
name: firstDefinition.name.replace('-', '_'),
|
|
91
|
+
deviceType: firstDefinition.code,
|
|
92
|
+
deviceRevision: firstDefinition.revision,
|
|
93
|
+
deviceClass: firstDefinition.deviceClass.toLowerCase(),
|
|
80
94
|
requirements: {
|
|
81
95
|
server: {
|
|
82
|
-
mandatory: SupportedBehaviors(...MatterbridgeEndpoint.getBehaviourTypesFromClusterServerIds(
|
|
83
|
-
optional: SupportedBehaviors(...MatterbridgeEndpoint.getBehaviourTypesFromClusterServerIds(
|
|
96
|
+
mandatory: SupportedBehaviors(...MatterbridgeEndpoint.getBehaviourTypesFromClusterServerIds(firstDefinition.requiredServerClusters)),
|
|
97
|
+
optional: SupportedBehaviors(...MatterbridgeEndpoint.getBehaviourTypesFromClusterServerIds(firstDefinition.optionalServerClusters)),
|
|
84
98
|
},
|
|
85
99
|
client: {
|
|
86
|
-
mandatory: SupportedBehaviors(...MatterbridgeEndpoint.getBehaviourTypesFromClusterClientIds(
|
|
87
|
-
optional: SupportedBehaviors(...MatterbridgeEndpoint.getBehaviourTypesFromClusterClientIds(
|
|
100
|
+
mandatory: SupportedBehaviors(...MatterbridgeEndpoint.getBehaviourTypesFromClusterClientIds(firstDefinition.requiredClientClusters)),
|
|
101
|
+
optional: SupportedBehaviors(...MatterbridgeEndpoint.getBehaviourTypesFromClusterClientIds(firstDefinition.optionalClientClusters)),
|
|
88
102
|
},
|
|
89
103
|
},
|
|
90
|
-
behaviors: SupportedBehaviors(
|
|
104
|
+
behaviors: tagList ? SupportedBehaviors(DescriptorServer.with(Descriptor.Feature.TagList)) : {},
|
|
91
105
|
};
|
|
92
106
|
const endpointV8 = MutableEndpoint(deviceTypeDefinitionV8);
|
|
107
|
+
// Convert the options to an Endpoint.Options
|
|
108
|
+
// [{ mfgCode: null, namespaceId: 0x07, tag: 1, label: 'Switch1' }]
|
|
109
|
+
// endpoint = endpoint.enable({features: { tagList: true }});
|
|
93
110
|
const optionsV8 = {
|
|
94
|
-
id:
|
|
111
|
+
// id: uniqueStorageKey,
|
|
112
|
+
id: uniqueStorageKey?.replace(/[ .]/g, ''),
|
|
113
|
+
// id: uniqueStorageKey?.replace(/[^a-zA-Z0-9_-]/g, ''),
|
|
114
|
+
number: endpointId,
|
|
115
|
+
descriptor: tagList ? { tagList } : undefined,
|
|
116
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
95
117
|
};
|
|
96
118
|
super(endpointV8, optionsV8);
|
|
97
|
-
this.
|
|
98
|
-
this.
|
|
119
|
+
this.uniqueStorageKey = uniqueStorageKey;
|
|
120
|
+
this.tagList = tagList;
|
|
121
|
+
// Update the endpoint
|
|
122
|
+
this.log = new AnsiLogger({ logName: 'MatterbridgeEndpoint', logTimestampFormat: 4 /* TimestampFormat.TIME_MILLIS */, logLevel: debug === true ? "debug" /* LogLevel.DEBUG */ : MatterbridgeEndpoint.logLevel });
|
|
123
|
+
this.log.debug(`${YELLOW}new${db} MatterbridgeEndpoint: ${zb}${'0x' + firstDefinition.code.toString(16).padStart(4, '0')}${db}-${zb}${firstDefinition.name}${db} id: ${CYAN}${this.id}${db}`);
|
|
124
|
+
this.deviceTypes.set(firstDefinition.code, firstDefinition);
|
|
125
|
+
// Add the other device types to the descriptor server
|
|
126
|
+
if (Array.isArray(definition)) {
|
|
127
|
+
definition.forEach((deviceType) => {
|
|
128
|
+
this.addDeviceType(deviceType);
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
// Add MatterbridgeBehavior with MatterbridgeBehaviorDevice
|
|
132
|
+
this.behaviors.require(MatterbridgeBehavior, { deviceCommand: new MatterbridgeBehaviorDevice(this.log, this.commandHandler, undefined) });
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Loads an instance of the MatterbridgeDevice class.
|
|
136
|
+
*
|
|
137
|
+
* @param {DeviceTypeDefinition | AtLeastOne<DeviceTypeDefinition>} definition - The DeviceTypeDefinition(s) of the device.
|
|
138
|
+
* @returns MatterbridgeDevice instance.
|
|
139
|
+
*/
|
|
140
|
+
static async loadInstance(definition, options = {}, debug = false) {
|
|
141
|
+
return new MatterbridgeEndpoint(definition, options, debug);
|
|
99
142
|
}
|
|
100
143
|
static getBehaviourTypesFromClusterServerIds(clusterServerList) {
|
|
101
144
|
// Map Server ClusterId to Behavior.Type
|
|
@@ -113,57 +156,97 @@ export class MatterbridgeEndpoint extends Endpoint {
|
|
|
113
156
|
});
|
|
114
157
|
return behaviorTypes;
|
|
115
158
|
}
|
|
116
|
-
static getBehaviourTypeFromClusterServerId(clusterId) {
|
|
159
|
+
static getBehaviourTypeFromClusterServerId(clusterId, type) {
|
|
117
160
|
// Map ClusterId to Behavior.Type
|
|
118
161
|
if (clusterId === Identify.Cluster.id)
|
|
119
|
-
return
|
|
162
|
+
return MatterbridgeIdentifyServer;
|
|
120
163
|
if (clusterId === Groups.Cluster.id)
|
|
121
164
|
return GroupsServer;
|
|
122
|
-
// if (clusterId === Scenes.Cluster.id) return ScenesServer;
|
|
123
165
|
if (clusterId === OnOff.Cluster.id)
|
|
124
|
-
return
|
|
166
|
+
return MatterbridgeOnOffServer;
|
|
125
167
|
if (clusterId === LevelControl.Cluster.id)
|
|
126
|
-
return
|
|
168
|
+
return MatterbridgeLevelControlServer;
|
|
127
169
|
if (clusterId === ColorControl.Cluster.id)
|
|
128
|
-
return
|
|
170
|
+
return MatterbridgeColorControlServer;
|
|
129
171
|
if (clusterId === DoorLock.Cluster.id)
|
|
130
|
-
return
|
|
172
|
+
return MatterbridgeDoorLockServer;
|
|
131
173
|
if (clusterId === Thermostat.Cluster.id)
|
|
132
|
-
return
|
|
133
|
-
if (clusterId === TimeSynchronization.Cluster.id)
|
|
134
|
-
return TimeSynchronizationServer.with(TimeSynchronization.Feature.TimeZone);
|
|
174
|
+
return MatterbridgeThermostatServer;
|
|
135
175
|
if (clusterId === WindowCovering.Cluster.id)
|
|
136
|
-
return
|
|
176
|
+
return MatterbridgeWindowCoveringServer;
|
|
137
177
|
if (clusterId === FanControl.Cluster.id)
|
|
138
|
-
return
|
|
178
|
+
return MatterbridgeFanControlServer;
|
|
179
|
+
if (clusterId === Switch.Cluster.id && type === 'MomentarySwitch')
|
|
180
|
+
return SwitchServer.with('MomentarySwitch', 'MomentarySwitchRelease', 'MomentarySwitchLongPress', 'MomentarySwitchMultiPress');
|
|
181
|
+
if (clusterId === Switch.Cluster.id && type === 'LatchingSwitch')
|
|
182
|
+
return SwitchServer.with('LatchingSwitch');
|
|
139
183
|
if (clusterId === TemperatureMeasurement.Cluster.id)
|
|
140
184
|
return TemperatureMeasurementServer;
|
|
141
185
|
if (clusterId === RelativeHumidityMeasurement.Cluster.id)
|
|
142
186
|
return RelativeHumidityMeasurementServer;
|
|
143
187
|
if (clusterId === PressureMeasurement.Cluster.id)
|
|
144
|
-
return PressureMeasurementServer
|
|
188
|
+
return PressureMeasurementServer;
|
|
145
189
|
if (clusterId === FlowMeasurement.Cluster.id)
|
|
146
190
|
return FlowMeasurementServer;
|
|
147
191
|
if (clusterId === BooleanState.Cluster.id)
|
|
148
192
|
return BooleanStateServer;
|
|
149
193
|
if (clusterId === BooleanStateConfiguration.Cluster.id)
|
|
150
|
-
return
|
|
194
|
+
return MatterbridgeBooleanStateConfigurationServer;
|
|
151
195
|
if (clusterId === OccupancySensing.Cluster.id)
|
|
152
196
|
return OccupancySensingServer;
|
|
153
197
|
if (clusterId === IlluminanceMeasurement.Cluster.id)
|
|
154
198
|
return IlluminanceMeasurementServer;
|
|
199
|
+
if (clusterId === SmokeCoAlarm.Cluster.id)
|
|
200
|
+
return SmokeCoAlarmServer.with(SmokeCoAlarm.Feature.SmokeAlarm, SmokeCoAlarm.Feature.CoAlarm);
|
|
201
|
+
if (clusterId === AirQuality.Cluster.id)
|
|
202
|
+
return AirQualityServer.with(AirQuality.Feature.Fair, AirQuality.Feature.Moderate, AirQuality.Feature.VeryPoor, AirQuality.Feature.ExtremelyPoor);
|
|
203
|
+
if (clusterId === CarbonMonoxideConcentrationMeasurement.Cluster.id)
|
|
204
|
+
return CarbonMonoxideConcentrationMeasurementServer.with('NumericMeasurement');
|
|
205
|
+
if (clusterId === CarbonDioxideConcentrationMeasurement.Cluster.id)
|
|
206
|
+
return CarbonDioxideConcentrationMeasurementServer.with('NumericMeasurement');
|
|
207
|
+
if (clusterId === NitrogenDioxideConcentrationMeasurement.Cluster.id)
|
|
208
|
+
return NitrogenDioxideConcentrationMeasurementServer.with('NumericMeasurement');
|
|
209
|
+
if (clusterId === OzoneConcentrationMeasurement.Cluster.id)
|
|
210
|
+
return OzoneConcentrationMeasurementServer.with('NumericMeasurement');
|
|
211
|
+
if (clusterId === FormaldehydeConcentrationMeasurement.Cluster.id)
|
|
212
|
+
return FormaldehydeConcentrationMeasurementServer.with('NumericMeasurement');
|
|
213
|
+
if (clusterId === Pm1ConcentrationMeasurement.Cluster.id)
|
|
214
|
+
return Pm1ConcentrationMeasurementServer.with('NumericMeasurement');
|
|
215
|
+
if (clusterId === Pm25ConcentrationMeasurement.Cluster.id)
|
|
216
|
+
return Pm25ConcentrationMeasurementServer.with('NumericMeasurement');
|
|
217
|
+
if (clusterId === Pm10ConcentrationMeasurement.Cluster.id)
|
|
218
|
+
return Pm10ConcentrationMeasurementServer.with('NumericMeasurement');
|
|
219
|
+
if (clusterId === RadonConcentrationMeasurement.Cluster.id)
|
|
220
|
+
return RadonConcentrationMeasurementServer.with('NumericMeasurement');
|
|
221
|
+
if (clusterId === TotalVolatileOrganicCompoundsConcentrationMeasurement.Cluster.id)
|
|
222
|
+
return TotalVolatileOrganicCompoundsConcentrationMeasurementServer.with('NumericMeasurement');
|
|
223
|
+
if (clusterId === ModeSelect.Cluster.id)
|
|
224
|
+
return ModeSelectServer;
|
|
225
|
+
if (clusterId === UserLabel.Cluster.id)
|
|
226
|
+
return UserLabelServer;
|
|
227
|
+
if (clusterId === FixedLabel.Cluster.id)
|
|
228
|
+
return FixedLabelServer;
|
|
229
|
+
if (clusterId === PowerTopology.Cluster.id)
|
|
230
|
+
return PowerTopologyServer.with('TreeTopology');
|
|
231
|
+
if (clusterId === ElectricalPowerMeasurement.Cluster.id)
|
|
232
|
+
return ElectricalPowerMeasurementServer.with('AlternatingCurrent');
|
|
233
|
+
if (clusterId === ElectricalEnergyMeasurement.Cluster.id)
|
|
234
|
+
return ElectricalEnergyMeasurementServer.with('ImportedEnergy', 'ExportedEnergy', 'CumulativeEnergy');
|
|
235
|
+
if (clusterId === PowerSource.Cluster.id && type === 'WiredPowerSource')
|
|
236
|
+
return PowerSourceServer.with(PowerSource.Feature.Wired);
|
|
237
|
+
if (clusterId === PowerSource.Cluster.id && type === 'BatteryReplaceablePowerSource')
|
|
238
|
+
return PowerSourceServer.with(PowerSource.Feature.Battery, PowerSource.Feature.Replaceable);
|
|
239
|
+
if (clusterId === PowerSource.Cluster.id && type === 'BatteryRechargeablePowerSource')
|
|
240
|
+
return PowerSourceServer.with(PowerSource.Feature.Battery, PowerSource.Feature.Rechargeable);
|
|
241
|
+
if (clusterId === BasicInformation.Cluster.id)
|
|
242
|
+
return BasicInformationServer;
|
|
155
243
|
if (clusterId === BridgedDeviceBasicInformation.Cluster.id)
|
|
156
244
|
return BridgedDeviceBasicInformationServer;
|
|
157
|
-
return
|
|
245
|
+
return MatterbridgeIdentifyServer;
|
|
158
246
|
}
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
* @param {DeviceTypeDefinition} definition - The DeviceTypeDefinition of the device.
|
|
163
|
-
* @returns MatterbridgeDevice instance.
|
|
164
|
-
*/
|
|
165
|
-
static async loadInstance(definition, options = {}) {
|
|
166
|
-
return new MatterbridgeEndpoint(definition, options);
|
|
247
|
+
static getBehaviourTypeFromClusterClientId(clusterId) {
|
|
248
|
+
// Map ClusterId to Behavior.Type
|
|
249
|
+
return IdentifyBehavior;
|
|
167
250
|
}
|
|
168
251
|
/**
|
|
169
252
|
* Adds a device type to the list of device types.
|
|
@@ -174,16 +257,24 @@ export class MatterbridgeEndpoint extends Endpoint {
|
|
|
174
257
|
addDeviceType(deviceType) {
|
|
175
258
|
if (!this.deviceTypes.has(deviceType.code)) {
|
|
176
259
|
// Keep the Matterbridge internal map
|
|
177
|
-
this.log.debug(`addDeviceType: ${zb}${deviceType.code}${db}-${zb}${deviceType.name}${db}`);
|
|
260
|
+
this.log.debug(`addDeviceType: ${zb}${'0x' + deviceType.code.toString(16).padStart(4, '0')}${db}-${zb}${deviceType.name}${db}`);
|
|
178
261
|
this.deviceTypes.set(deviceType.code, deviceType);
|
|
179
262
|
// Add the device types to the descriptor server
|
|
180
263
|
const deviceTypeList = Array.from(this.deviceTypes.values()).map((dt) => ({
|
|
181
264
|
deviceType: dt.code,
|
|
182
265
|
revision: dt.revision,
|
|
183
266
|
}));
|
|
184
|
-
this.
|
|
185
|
-
|
|
186
|
-
|
|
267
|
+
if (this.tagList) {
|
|
268
|
+
this.behaviors.require(DescriptorServer.with(Descriptor.Feature.TagList), {
|
|
269
|
+
tagList: this.tagList,
|
|
270
|
+
deviceTypeList,
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
else {
|
|
274
|
+
this.behaviors.require(DescriptorServer, {
|
|
275
|
+
deviceTypeList,
|
|
276
|
+
});
|
|
277
|
+
}
|
|
187
278
|
}
|
|
188
279
|
}
|
|
189
280
|
/**
|
|
@@ -195,20 +286,64 @@ export class MatterbridgeEndpoint extends Endpoint {
|
|
|
195
286
|
addDeviceTypeWithClusterServer(deviceTypes, includeServerList) {
|
|
196
287
|
this.log.debug('addDeviceTypeWithClusterServer:');
|
|
197
288
|
deviceTypes.forEach((deviceType) => {
|
|
198
|
-
this.log.debug(`- with deviceType: ${zb}${deviceType.code}${db}-${zb}${deviceType.name}${db}`);
|
|
289
|
+
this.log.debug(`- with deviceType: ${zb}${'0x' + deviceType.code.toString(16).padStart(4, '0')}${db}-${zb}${deviceType.name}${db}`);
|
|
199
290
|
deviceType.requiredServerClusters.forEach((clusterId) => {
|
|
200
291
|
if (!includeServerList.includes(clusterId))
|
|
201
292
|
includeServerList.push(clusterId);
|
|
202
293
|
});
|
|
203
294
|
});
|
|
204
295
|
includeServerList.forEach((clusterId) => {
|
|
205
|
-
this.log.debug(`- with cluster: ${hk}${clusterId}${db}-${hk}${getClusterNameById(clusterId)}${db}`);
|
|
296
|
+
this.log.debug(`- with cluster: ${hk}${'0x' + clusterId.toString(16).padStart(4, '0')}${db}-${hk}${getClusterNameById(clusterId)}${db}`);
|
|
206
297
|
});
|
|
207
298
|
deviceTypes.forEach((deviceType) => {
|
|
208
299
|
this.addDeviceType(deviceType);
|
|
209
300
|
});
|
|
210
301
|
this.addClusterServerFromList(this, includeServerList);
|
|
211
302
|
}
|
|
303
|
+
/**
|
|
304
|
+
* Adds the required cluster servers (only if they are not present) for the device types of the specified endpoint.
|
|
305
|
+
*
|
|
306
|
+
* @param {MatterbridgeEndpoint} endpoint - The endpoint to add the required cluster servers to.
|
|
307
|
+
* @returns {MatterbridgeEndpoint} The updated endpoint with the required cluster servers added.
|
|
308
|
+
*/
|
|
309
|
+
addRequiredClusterServers(endpoint) {
|
|
310
|
+
const requiredServerList = [];
|
|
311
|
+
this.log.debug(`addRequiredClusterServer for ${CYAN}${endpoint.id}${db}`);
|
|
312
|
+
endpoint.getDeviceTypes().forEach((deviceType) => {
|
|
313
|
+
this.log.debug(`- for deviceType: ${zb}${'0x' + deviceType.code.toString(16).padStart(4, '0')}${db}-${zb}${deviceType.name}${db}`);
|
|
314
|
+
deviceType.requiredServerClusters.forEach((clusterId) => {
|
|
315
|
+
if (!requiredServerList.includes(clusterId) && !endpoint.getClusterServerById(clusterId))
|
|
316
|
+
requiredServerList.push(clusterId);
|
|
317
|
+
});
|
|
318
|
+
});
|
|
319
|
+
requiredServerList.forEach((clusterId) => {
|
|
320
|
+
this.log.debug(`- with cluster: ${hk}${'0x' + clusterId.toString(16).padStart(4, '0')}${db}-${hk}${getClusterNameById(clusterId)}${db}`);
|
|
321
|
+
});
|
|
322
|
+
this.addClusterServerFromList(endpoint, requiredServerList);
|
|
323
|
+
return endpoint;
|
|
324
|
+
}
|
|
325
|
+
/**
|
|
326
|
+
* Adds the optional cluster servers (only if they are not present) for the device types of the specified endpoint.
|
|
327
|
+
*
|
|
328
|
+
* @param {MatterbridgeEndpoint} endpoint - The endpoint to add the required cluster servers to.
|
|
329
|
+
* @returns {MatterbridgeEndpoint} The updated endpoint with the required cluster servers added.
|
|
330
|
+
*/
|
|
331
|
+
addOptionalClusterServers(endpoint) {
|
|
332
|
+
const optionalServerList = [];
|
|
333
|
+
this.log.debug(`addRequiredClusterServer for ${CYAN}${endpoint.id}${db}`);
|
|
334
|
+
endpoint.getDeviceTypes().forEach((deviceType) => {
|
|
335
|
+
this.log.debug(`- for deviceType: ${zb}${'0x' + deviceType.code.toString(16).padStart(4, '0')}${db}-${zb}${deviceType.name}${db}`);
|
|
336
|
+
deviceType.optionalServerClusters.forEach((clusterId) => {
|
|
337
|
+
if (!optionalServerList.includes(clusterId) && !endpoint.getClusterServerById(clusterId))
|
|
338
|
+
optionalServerList.push(clusterId);
|
|
339
|
+
});
|
|
340
|
+
});
|
|
341
|
+
optionalServerList.forEach((clusterId) => {
|
|
342
|
+
this.log.debug(`- with cluster: ${hk}${'0x' + clusterId.toString(16).padStart(4, '0')}${db}-${hk}${getClusterNameById(clusterId)}${db}`);
|
|
343
|
+
});
|
|
344
|
+
this.addClusterServerFromList(endpoint, optionalServerList);
|
|
345
|
+
return endpoint;
|
|
346
|
+
}
|
|
212
347
|
/**
|
|
213
348
|
* Adds a child endpoint with one or more device types with the required cluster servers and the specified cluster servers.
|
|
214
349
|
* If the child endpoint is not already present in the childEndpoints, it will be added.
|
|
@@ -216,30 +351,60 @@ export class MatterbridgeEndpoint extends Endpoint {
|
|
|
216
351
|
*
|
|
217
352
|
* @param {string} endpointName - The name of the new enpoint to add.
|
|
218
353
|
* @param {AtLeastOne<DeviceTypeDefinition>} deviceTypes - The device types to add.
|
|
219
|
-
* @param {ClusterId[]} includeServerList - The list of cluster IDs to include.
|
|
220
|
-
* @
|
|
354
|
+
* @param {ClusterId[]} [includeServerList=[]] - The list of cluster IDs to include.
|
|
355
|
+
* @param {EndpointOptions} [options={}] - The options for the device.
|
|
356
|
+
* @param {boolean} [debug=false] - Whether to enable debug logging.
|
|
357
|
+
* @returns {MatterbridgeEndpoint} - The child endpoint that was found or added.
|
|
221
358
|
*/
|
|
222
|
-
addChildDeviceTypeWithClusterServer(endpointName, deviceTypes, includeServerList) {
|
|
223
|
-
/*
|
|
359
|
+
addChildDeviceTypeWithClusterServer(endpointName, deviceTypes, includeServerList = [], options = {}, debug = false) {
|
|
224
360
|
this.log.debug(`addChildDeviceTypeWithClusterServer: ${CYAN}${endpointName}${db}`);
|
|
225
|
-
let child = this.
|
|
361
|
+
let child = this.getChildEndpointByName(endpointName);
|
|
226
362
|
if (!child) {
|
|
227
|
-
|
|
228
|
-
child.addFixedLabel('endpointName', endpointName);
|
|
363
|
+
child = new MatterbridgeEndpoint(deviceTypes[0], { uniqueStorageKey: endpointName }, debug);
|
|
229
364
|
}
|
|
230
365
|
deviceTypes.forEach((deviceType) => {
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
366
|
+
this.log.debug(`- with deviceType: ${zb}${'0x' + deviceType.code.toString(16).padStart(4, '0')}${db}-${zb}${deviceType.name}${db}`);
|
|
367
|
+
deviceType.requiredServerClusters.forEach((clusterId) => {
|
|
368
|
+
if (!includeServerList.includes(clusterId))
|
|
369
|
+
includeServerList.push(clusterId);
|
|
370
|
+
});
|
|
235
371
|
});
|
|
236
372
|
includeServerList.forEach((clusterId) => {
|
|
237
|
-
|
|
373
|
+
this.log.debug(`- with cluster: ${hk}${'0x' + clusterId.toString(16).padStart(4, '0')}${db}-${hk}${getClusterNameById(clusterId)}${db}`);
|
|
238
374
|
});
|
|
239
375
|
this.addClusterServerFromList(child, includeServerList);
|
|
240
|
-
this.
|
|
376
|
+
if (this.lifecycle.isInstalled) {
|
|
377
|
+
this.log.debug(`- with lifecycle installed`);
|
|
378
|
+
this.add(child);
|
|
379
|
+
}
|
|
380
|
+
else {
|
|
381
|
+
this.log.debug(`- with lifecycle NOT installed`);
|
|
382
|
+
this.parts.add(child);
|
|
383
|
+
}
|
|
241
384
|
return child;
|
|
242
|
-
|
|
385
|
+
}
|
|
386
|
+
/**
|
|
387
|
+
* Retrieves a child endpoint by its name.
|
|
388
|
+
*
|
|
389
|
+
* @param {string} endpointName - The name of the endpoint to retrieve.
|
|
390
|
+
* @returns {Endpoint | undefined} The child endpoint with the specified name, or undefined if not found.
|
|
391
|
+
*/
|
|
392
|
+
getChildEndpointByName(endpointName) {
|
|
393
|
+
return this.parts.find((part) => part.id === endpointName);
|
|
394
|
+
}
|
|
395
|
+
getChildEndpoint(endpointNumber) {
|
|
396
|
+
return this.parts.find((part) => part.number === endpointNumber);
|
|
397
|
+
}
|
|
398
|
+
getChildEndpoints() {
|
|
399
|
+
return Array.from(this.parts);
|
|
400
|
+
}
|
|
401
|
+
getDeviceTypes() {
|
|
402
|
+
return Array.from(this.deviceTypes.values());
|
|
403
|
+
}
|
|
404
|
+
setDeviceTypes(deviceTypes) {
|
|
405
|
+
deviceTypes.forEach((deviceType) => {
|
|
406
|
+
this.addDeviceType(deviceType);
|
|
407
|
+
});
|
|
243
408
|
}
|
|
244
409
|
getClusterServer(cluster) {
|
|
245
410
|
const clusterServer = this.clusterServers.get(cluster.id);
|
|
@@ -254,17 +419,28 @@ export class MatterbridgeEndpoint extends Endpoint {
|
|
|
254
419
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
255
420
|
const options = {};
|
|
256
421
|
for (const attribute of Object.values(cluster.attributes)) {
|
|
422
|
+
// console.error('Attribute:', (attribute as any).id, (attribute as any).name, (attribute as any).value);
|
|
257
423
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
258
424
|
if (attribute.id < 0xfff0) {
|
|
259
|
-
// No issue here for value, as cluster here is just a definition without getter setter
|
|
260
425
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
261
426
|
options[attribute.name] = attribute.value;
|
|
262
427
|
}
|
|
263
428
|
}
|
|
264
|
-
this.log.debug(`addClusterServer: ${hk}${cluster.id}${db}-${hk}${getClusterNameById(cluster.id)}${db} with options: ${debugStringify(options)}${rs}`);
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
429
|
+
this.log.debug(`addClusterServer: ${hk}${'0x' + cluster.id.toString(16).padStart(4, '0')}${db}-${hk}${getClusterNameById(cluster.id)}${db} with options: ${debugStringify(options)}${rs}`);
|
|
430
|
+
let type = undefined;
|
|
431
|
+
if (cluster.id === SwitchCluster.id && cluster.isEventSupportedByName('multiPressComplete'))
|
|
432
|
+
type = 'MomentarySwitch';
|
|
433
|
+
if (cluster.id === SwitchCluster.id && cluster.isEventSupportedByName('switchLatched'))
|
|
434
|
+
type = 'LatchingSwitch';
|
|
435
|
+
if (cluster.id === PowerSourceCluster.id && cluster.isAttributeSupportedByName('wiredCurrentType'))
|
|
436
|
+
type = 'WiredPowerSource';
|
|
437
|
+
if (cluster.id === PowerSourceCluster.id && cluster.isAttributeSupportedByName('batReplacementDescription'))
|
|
438
|
+
type = 'BatteryReplaceablePowerSource';
|
|
439
|
+
if (cluster.id === PowerSourceCluster.id && cluster.isAttributeSupportedByName('batChargeState'))
|
|
440
|
+
type = 'BatteryRechargeablePowerSource';
|
|
441
|
+
const behavior = MatterbridgeEndpoint.getBehaviourTypeFromClusterServerId(cluster.id, type);
|
|
442
|
+
if (cluster.id !== BasicInformationCluster.id)
|
|
443
|
+
this.behaviors.require(behavior, options);
|
|
268
444
|
this.clusterServers.set(cluster.id, cluster);
|
|
269
445
|
}
|
|
270
446
|
/**
|
|
@@ -279,21 +455,18 @@ export class MatterbridgeEndpoint extends Endpoint {
|
|
|
279
455
|
endpoint.addClusterServer(this.getDefaultIdentifyClusterServer());
|
|
280
456
|
if (includeServerList.includes(Groups.Cluster.id))
|
|
281
457
|
endpoint.addClusterServer(this.getDefaultGroupsClusterServer());
|
|
282
|
-
// if (includeServerList.includes(Scenes.Cluster.id)) endpoint.addClusterServer(this.getDefaultScenesClusterServer());
|
|
283
458
|
if (includeServerList.includes(OnOff.Cluster.id))
|
|
284
459
|
endpoint.addClusterServer(this.getDefaultOnOffClusterServer());
|
|
285
460
|
if (includeServerList.includes(LevelControl.Cluster.id))
|
|
286
461
|
endpoint.addClusterServer(this.getDefaultLevelControlClusterServer());
|
|
287
462
|
if (includeServerList.includes(ColorControl.Cluster.id))
|
|
288
|
-
endpoint.addClusterServer(this.
|
|
463
|
+
endpoint.addClusterServer(this.getDefaultColorControlClusterServer());
|
|
289
464
|
if (includeServerList.includes(Switch.Cluster.id))
|
|
290
465
|
endpoint.addClusterServer(this.getDefaultSwitchClusterServer());
|
|
291
466
|
if (includeServerList.includes(DoorLock.Cluster.id))
|
|
292
467
|
endpoint.addClusterServer(this.getDefaultDoorLockClusterServer());
|
|
293
468
|
if (includeServerList.includes(Thermostat.Cluster.id))
|
|
294
469
|
endpoint.addClusterServer(this.getDefaultThermostatClusterServer());
|
|
295
|
-
if (includeServerList.includes(TimeSynchronization.Cluster.id))
|
|
296
|
-
endpoint.addClusterServer(this.getDefaultTimeSyncClusterServer());
|
|
297
470
|
if (includeServerList.includes(WindowCovering.Cluster.id))
|
|
298
471
|
endpoint.addClusterServer(this.getDefaultWindowCoveringClusterServer());
|
|
299
472
|
if (includeServerList.includes(FanControl.Cluster.id))
|
|
@@ -349,20 +522,38 @@ export class MatterbridgeEndpoint extends Endpoint {
|
|
|
349
522
|
// if (includeServerList.includes(DeviceEnergyManagement.Cluster.id)) endpoint.addClusterServer(this.getDefaultDeviceEnergyManagementClusterServer());
|
|
350
523
|
// if (includeServerList.includes(DeviceEnergyManagementMode.Cluster.id)) endpoint.addClusterServer(this.getDefaultDeviceEnergyManagementModeClusterServer());
|
|
351
524
|
}
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
525
|
+
async addFixedLabel(label, value) {
|
|
526
|
+
if (!this.clusterServers.get(FixedLabelCluster.id)) {
|
|
527
|
+
this.addClusterServer(ClusterServer(FixedLabelCluster, {
|
|
528
|
+
labelList: [{ label, value }],
|
|
529
|
+
}, {}));
|
|
530
|
+
return;
|
|
531
|
+
}
|
|
532
|
+
const labelList = (this.getAttribute(FixedLabelCluster.id, 'labelList', this.log) ?? []).filter((entryLabel) => entryLabel.label !== label);
|
|
533
|
+
labelList.push({ label, value });
|
|
534
|
+
await this.setAttribute(FixedLabelCluster.id, 'labelList', labelList, this.log);
|
|
535
|
+
}
|
|
536
|
+
async addUserLabel(label, value) {
|
|
537
|
+
if (!this.clusterServers.get(UserLabelCluster.id)) {
|
|
538
|
+
this.addClusterServer(ClusterServer(UserLabelCluster, {
|
|
539
|
+
labelList: [{ label, value }],
|
|
540
|
+
}, {}));
|
|
541
|
+
return;
|
|
542
|
+
}
|
|
543
|
+
const labelList = (this.getAttribute(UserLabelCluster.id, 'labelList', this.log) ?? []).filter((entryLabel) => entryLabel.label !== label);
|
|
544
|
+
labelList.push({ label, value });
|
|
545
|
+
await this.setAttribute(UserLabelCluster.id, 'labelList', labelList, this.log);
|
|
546
|
+
}
|
|
547
|
+
capitalizeFirstLetter(name) {
|
|
548
|
+
if (!name)
|
|
549
|
+
return name;
|
|
550
|
+
return name.charAt(0).toUpperCase() + name.slice(1);
|
|
551
|
+
}
|
|
552
|
+
lowercaseFirstLetter(name) {
|
|
553
|
+
if (!name)
|
|
554
|
+
return name;
|
|
555
|
+
return name.charAt(0).toLowerCase() + name.slice(1);
|
|
364
556
|
}
|
|
365
|
-
*/
|
|
366
557
|
/**
|
|
367
558
|
* Retrieves the value of the specified attribute from the given endpoint and cluster.
|
|
368
559
|
*
|
|
@@ -372,33 +563,30 @@ export class MatterbridgeEndpoint extends Endpoint {
|
|
|
372
563
|
* @param {Endpoint} [endpoint] - Optional the child endpoint to retrieve the attribute from.
|
|
373
564
|
* @returns {any} The value of the attribute, or undefined if the attribute is not found.
|
|
374
565
|
*/
|
|
375
|
-
|
|
376
|
-
getAttribute(clusterId
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
if (
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
log?.info(`${db}Get endpoint ${or}${endpoint.name}:${endpoint.number}${db} attribute ${hk}${clusterServer.name}.${capitalizedAttributeName}${db} value ${YELLOW}${typeof value === 'object' ? debugStringify(value) : value}${db}`);
|
|
399
|
-
return value;
|
|
566
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
567
|
+
getAttribute(clusterId, attribute, log, endpoint) {
|
|
568
|
+
if (!endpoint)
|
|
569
|
+
endpoint = this;
|
|
570
|
+
const clusterName = this.lowercaseFirstLetter(getClusterNameById(clusterId));
|
|
571
|
+
if (endpoint.construction.status !== Lifecycle.Status.Active) {
|
|
572
|
+
log?.error(`getAttribute ${hk}${clusterName}.${attribute}${er} error: Endpoint ${or}${endpoint.id}${er} is in the ${BLUE}${endpoint.construction.status}${er} state`);
|
|
573
|
+
return undefined;
|
|
574
|
+
}
|
|
575
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
576
|
+
const state = endpoint.state;
|
|
577
|
+
if (!(clusterName in state)) {
|
|
578
|
+
log?.error(`getAttribute error: Cluster ${'0x' + clusterId.toString(16).padStart(4, '0')}:${clusterName} not found on endpoint ${or}${endpoint.id}${er}:${or}${endpoint.number}${er}`);
|
|
579
|
+
return undefined;
|
|
580
|
+
}
|
|
581
|
+
attribute = this.lowercaseFirstLetter(attribute);
|
|
582
|
+
if (!(attribute in state[clusterName])) {
|
|
583
|
+
log?.error(`getAttribute error: Attribute ${hk}${attribute}${er} not found on Cluster ${'0x' + clusterId.toString(16).padStart(4, '0')}:${clusterName} on endpoint ${or}${endpoint.id}${er}:${or}${endpoint.number}${er}`);
|
|
584
|
+
return undefined;
|
|
585
|
+
}
|
|
586
|
+
const value = state[clusterName][attribute];
|
|
587
|
+
log?.info(`${db}Get endpoint ${or}${endpoint.id}${db}:${or}${endpoint.number}${db} attribute ${hk}${this.capitalizeFirstLetter(clusterName)}${db}.${hk}${attribute}${db} value ${YELLOW}${typeof value === 'object' ? debugStringify(value) : value}${db}`);
|
|
588
|
+
return value;
|
|
400
589
|
}
|
|
401
|
-
*/
|
|
402
590
|
/**
|
|
403
591
|
* Sets the value of an attribute on a cluster server endpoint.
|
|
404
592
|
*
|
|
@@ -408,46 +596,36 @@ export class MatterbridgeEndpoint extends Endpoint {
|
|
|
408
596
|
* @param {AnsiLogger} [log] - (Optional) The logger to use for logging errors and information.
|
|
409
597
|
* @param {Endpoint} [endpoint] - (Optional) The endpoint to set the attribute on. If not provided, the attribute will be set on the current endpoint.
|
|
410
598
|
*/
|
|
411
|
-
|
|
412
|
-
setAttribute(clusterId
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
if (
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
log?.
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
const oldValue = getter();
|
|
442
|
-
setter(value);
|
|
443
|
-
log?.info(
|
|
444
|
-
`${db}Set endpoint ${or}${endpoint.name}:${endpoint.number}${db} attribute ${hk}${clusterServer.name}.${capitalizedAttributeName}${db} ` +
|
|
445
|
-
`from ${YELLOW}${typeof oldValue === 'object' ? debugStringify(oldValue) : oldValue}${db} ` +
|
|
446
|
-
`to ${YELLOW}${typeof value === 'object' ? debugStringify(value) : value}${db}`,
|
|
447
|
-
);
|
|
448
|
-
return true;
|
|
449
|
-
}
|
|
450
|
-
*/
|
|
599
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
600
|
+
async setAttribute(clusterId, attribute, value, log, endpoint) {
|
|
601
|
+
if (!endpoint)
|
|
602
|
+
endpoint = this;
|
|
603
|
+
const clusterName = this.lowercaseFirstLetter(getClusterNameById(clusterId));
|
|
604
|
+
if (endpoint.construction.status !== Lifecycle.Status.Active) {
|
|
605
|
+
log?.error(`setAttribute ${hk}${clusterName}.${attribute}${er} error: Endpoint ${or}${endpoint.id}${er} is in the ${BLUE}${endpoint.construction.status}${er} state`);
|
|
606
|
+
return false;
|
|
607
|
+
}
|
|
608
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
609
|
+
const state = endpoint.state;
|
|
610
|
+
if (!(clusterName in state)) {
|
|
611
|
+
log?.error(`setAttribute ${hk}${attribute}${er} error: Cluster ${'0x' + clusterId.toString(16).padStart(4, '0')}:${clusterName} not found on endpoint ${or}${endpoint.id}${er}:${or}${endpoint.number}${er}`);
|
|
612
|
+
return false;
|
|
613
|
+
}
|
|
614
|
+
attribute = this.lowercaseFirstLetter(attribute);
|
|
615
|
+
if (!(attribute in state[clusterName])) {
|
|
616
|
+
log?.error(`setAttribute error: Attribute ${hk}${attribute}${er} not found on Cluster ${'0x' + clusterId.toString(16).padStart(4, '0')}:${clusterName} on endpoint ${or}${endpoint.id}${er}:${or}${endpoint.number}${er}`);
|
|
617
|
+
return false;
|
|
618
|
+
}
|
|
619
|
+
let oldValue = state[clusterName][attribute];
|
|
620
|
+
if (typeof oldValue === 'object')
|
|
621
|
+
oldValue = deepCopy(oldValue);
|
|
622
|
+
await endpoint.setStateOf(endpoint.behaviors.supported[clusterName], { [attribute]: value });
|
|
623
|
+
// await endpoint.set({ [clusterName]: { [attribute]: value } });
|
|
624
|
+
log?.info(`${db}Set endpoint ${or}${endpoint.id}${db}:${or}${endpoint.number}${db} attribute ${hk}${this.capitalizeFirstLetter(clusterName)}${db}.${hk}${attribute}${db} ` +
|
|
625
|
+
`from ${YELLOW}${typeof oldValue === 'object' ? debugStringify(oldValue) : oldValue}${db} ` +
|
|
626
|
+
`to ${YELLOW}${typeof value === 'object' ? debugStringify(value) : value}${db}`);
|
|
627
|
+
return true;
|
|
628
|
+
}
|
|
451
629
|
/**
|
|
452
630
|
* Subscribes to an attribute on a cluster.
|
|
453
631
|
*
|
|
@@ -458,58 +636,50 @@ export class MatterbridgeEndpoint extends Endpoint {
|
|
|
458
636
|
* @param {Endpoint} endpoint - (Optional) The endpoint to subscribe the attribute on. If not provided, the current endpoint will be used.
|
|
459
637
|
* @returns A boolean indicating whether the subscription was successful.
|
|
460
638
|
*/
|
|
461
|
-
/*
|
|
462
|
-
subscribeAttribute(clusterId: ClusterId, attribute: string, listener: (newValue: any, oldValue: any) => void, log?: AnsiLogger, endpoint?: Endpoint): boolean {
|
|
463
|
-
if (!endpoint) endpoint = this as Endpoint;
|
|
464
|
-
|
|
465
|
-
const clusterServer = endpoint.getClusterServerById(clusterId);
|
|
466
|
-
if (!clusterServer) {
|
|
467
|
-
log?.error(`subscribeAttribute error: Cluster ${clusterId} not found on endpoint ${endpoint.name}:${endpoint.number}`);
|
|
468
|
-
return false;
|
|
469
|
-
}
|
|
470
|
-
const capitalizedAttributeName = attribute.charAt(0).toUpperCase() + attribute.slice(1);
|
|
471
|
-
if (!clusterServer.isAttributeSupportedByName(attribute) && !clusterServer.isAttributeSupportedByName(capitalizedAttributeName)) {
|
|
472
|
-
if (log) log.error(`subscribeAttribute error: Attribute ${attribute} not found on Cluster ${clusterServer.name} on endpoint ${endpoint.name}:${endpoint.number}`);
|
|
473
|
-
return false;
|
|
474
|
-
}
|
|
475
|
-
// Find the subscribe method
|
|
476
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
477
|
-
if (!(clusterServer as any)[`subscribe${capitalizedAttributeName}Attribute`]) {
|
|
478
|
-
log?.error(`subscribeAttribute error: subscribe${capitalizedAttributeName}Attribute not found on Cluster ${clusterServer.name} on endpoint ${endpoint.name}:${endpoint.number}`);
|
|
479
|
-
return false;
|
|
480
|
-
}
|
|
481
|
-
// Subscribe to the attribute
|
|
482
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-empty-object-type
|
|
483
|
-
const subscribe = (clusterServer as any)[`subscribe${capitalizedAttributeName}Attribute`] as (listener: (newValue: any, oldValue: any) => void) => {};
|
|
484
|
-
subscribe(listener);
|
|
485
|
-
log?.info(`${db}Subscribe endpoint ${or}${endpoint.name}:${endpoint.number}${db} attribute ${hk}${clusterServer.name}.${capitalizedAttributeName}${db}`);
|
|
486
|
-
return true;
|
|
487
|
-
}
|
|
488
|
-
*/
|
|
489
639
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
640
|
+
subscribeAttribute(clusterId, attribute, listener, log, endpoint) {
|
|
641
|
+
if (!endpoint)
|
|
642
|
+
endpoint = this;
|
|
643
|
+
const clusterName = this.lowercaseFirstLetter(getClusterNameById(clusterId));
|
|
644
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
645
|
+
const events = endpoint.events;
|
|
646
|
+
if (!(clusterName in events)) {
|
|
647
|
+
log?.error(`subscribeAttribute ${hk}${attribute}${er} error: Cluster ${'0x' + clusterId.toString(16).padStart(4, '0')}:${clusterName} not found on endpoint ${or}${endpoint.id}${er}:${or}${endpoint.number}${er}`);
|
|
648
|
+
return false;
|
|
649
|
+
}
|
|
650
|
+
attribute = this.lowercaseFirstLetter(attribute) + '$Changed';
|
|
651
|
+
if (!(attribute in events[clusterName])) {
|
|
652
|
+
log?.error(`subscribeAttribute error: Attribute ${hk}${attribute}${er} not found on Cluster ${'0x' + clusterId.toString(16).padStart(4, '0')}:${clusterName} on endpoint ${or}${endpoint.id}${er}:${or}${endpoint.number}${er}`);
|
|
494
653
|
return false;
|
|
495
654
|
}
|
|
496
|
-
|
|
497
|
-
|
|
655
|
+
events[clusterName][attribute].on(listener);
|
|
656
|
+
log?.info(`${db}Subscribe endpoint ${or}${endpoint.id}${db}:${or}${endpoint.number}${db} attribute ${hk}${this.capitalizeFirstLetter(clusterName)}${db}.${hk}${attribute}${db}`);
|
|
657
|
+
return true;
|
|
658
|
+
}
|
|
659
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
660
|
+
async triggerEvent(clusterId, event, payload, log, endpoint) {
|
|
661
|
+
if (!endpoint)
|
|
662
|
+
endpoint = this;
|
|
663
|
+
const clusterName = this.lowercaseFirstLetter(getClusterNameById(clusterId));
|
|
664
|
+
if (endpoint.construction.status !== Lifecycle.Status.Active) {
|
|
665
|
+
log?.error(`triggerEvent ${hk}${clusterName}.${event}${er} error: Endpoint ${or}${endpoint.id}${er} is in the ${BLUE}${endpoint.construction.status}${er} state`);
|
|
498
666
|
return false;
|
|
499
667
|
}
|
|
500
|
-
// Find the command
|
|
501
668
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
502
|
-
const
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
value.handler = handler;
|
|
507
|
-
this.log.info(`${db}Command handler added for endpoint ${or}${this.id}:${this.number}${db} ${hk}${clusterServer.name}.${command}${db}`);
|
|
508
|
-
return true;
|
|
509
|
-
}
|
|
669
|
+
const events = endpoint.events;
|
|
670
|
+
if (!(clusterName in events) || !(event in events[clusterName])) {
|
|
671
|
+
log?.error(`triggerEvent ${hk}${event}${er} error: Cluster ${'0x' + clusterId.toString(16).padStart(4, '0')}:${clusterName} not found on endpoint ${or}${endpoint.id}${er}:${or}${endpoint.number}${er}`);
|
|
672
|
+
return false;
|
|
510
673
|
}
|
|
511
|
-
|
|
512
|
-
|
|
674
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
675
|
+
// @ts-ignore
|
|
676
|
+
await endpoint.act((agent) => agent[clusterName].events[event].emit(payload, agent.context));
|
|
677
|
+
log?.info(`${db}Trigger event ${hk}${this.capitalizeFirstLetter(clusterName)}${db}.${hk}${event}${db} with ${debugStringify(payload)}${db} on endpoint ${or}${endpoint.id}${db}:${or}${endpoint.number}${db} `);
|
|
678
|
+
return true;
|
|
679
|
+
}
|
|
680
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
681
|
+
addCommandHandler(command, handler) {
|
|
682
|
+
this.commandHandler.addHandler(command, handler);
|
|
513
683
|
}
|
|
514
684
|
/**
|
|
515
685
|
* Serializes the Matterbridge device into a serialized object.
|
|
@@ -517,64 +687,69 @@ export class MatterbridgeEndpoint extends Endpoint {
|
|
|
517
687
|
* @param pluginName - The name of the plugin.
|
|
518
688
|
* @returns The serialized Matterbridge device object.
|
|
519
689
|
*/
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
690
|
+
serialize() {
|
|
691
|
+
return undefined;
|
|
692
|
+
/*
|
|
693
|
+
if (!this.serialNumber || !this.deviceName || !this.uniqueId) return;
|
|
694
|
+
const cluster = this.getClusterServer(BasicInformationCluster) ?? this.getClusterServer(BridgedDeviceBasicInformationCluster);
|
|
695
|
+
if (!cluster) return;
|
|
696
|
+
const serialized: SerializedMatterbridgeDevice = {
|
|
697
|
+
pluginName: this.plugin ?? 'Unknown',
|
|
698
|
+
serialNumber: this.serialNumber,
|
|
699
|
+
deviceName: this.deviceName,
|
|
700
|
+
uniqueId: this.uniqueId,
|
|
701
|
+
productName: cluster.attributes.productName?.getLocal(),
|
|
702
|
+
vendorId: cluster.attributes.vendorId?.getLocal(),
|
|
703
|
+
vendorName: cluster.attributes.vendorName?.getLocal(),
|
|
704
|
+
deviceTypes: Array.from(this.deviceTypes.values()),
|
|
705
|
+
endpoint: this.number,
|
|
706
|
+
endpointName: this.id,
|
|
707
|
+
clusterServersId: [],
|
|
708
|
+
};
|
|
709
|
+
this.getAllClusterServers().forEach((clusterServer) => {
|
|
710
|
+
serialized.clusterServersId.push(clusterServer.id);
|
|
711
|
+
});
|
|
712
|
+
return serialized;
|
|
713
|
+
*/
|
|
714
|
+
}
|
|
544
715
|
/**
|
|
545
716
|
* Deserializes the device into a serialized object.
|
|
546
717
|
*
|
|
547
718
|
* @returns The deserialized MatterbridgeDevice.
|
|
548
719
|
*/
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
720
|
+
static deserialize(serializedDevice) {
|
|
721
|
+
return undefined;
|
|
722
|
+
/*
|
|
723
|
+
const device = new MatterbridgeDevice(serializedDevice.deviceTypes);
|
|
724
|
+
device.serialNumber = serializedDevice.serialNumber;
|
|
725
|
+
device.deviceName = serializedDevice.deviceName;
|
|
726
|
+
device.uniqueId = serializedDevice.uniqueId;
|
|
727
|
+
for (const clusterId of serializedDevice.clusterServersId) {
|
|
728
|
+
if (clusterId === BasicInformationCluster.id)
|
|
729
|
+
device.createDefaultBasicInformationClusterServer(
|
|
730
|
+
serializedDevice.deviceName,
|
|
731
|
+
serializedDevice.serialNumber,
|
|
732
|
+
serializedDevice.vendorId ?? 0xfff1,
|
|
733
|
+
serializedDevice.vendorName ?? 'Matterbridge',
|
|
734
|
+
serializedDevice.productId ?? 0x8000,
|
|
735
|
+
serializedDevice.productName ?? 'Matterbridge device',
|
|
736
|
+
);
|
|
737
|
+
else if (clusterId === BridgedDeviceBasicInformationCluster.id)
|
|
738
|
+
device.createDefaultBridgedDeviceBasicInformationClusterServer(
|
|
739
|
+
serializedDevice.deviceName,
|
|
740
|
+
serializedDevice.serialNumber,
|
|
741
|
+
serializedDevice.vendorId ?? 0xfff1,
|
|
742
|
+
serializedDevice.vendorName ?? 'Matterbridge',
|
|
743
|
+
serializedDevice.productName ?? 'Matterbridge device',
|
|
744
|
+
);
|
|
745
|
+
else device.addClusterServerFromList(device, [clusterId]);
|
|
746
|
+
}
|
|
747
|
+
return device;
|
|
748
|
+
*/
|
|
749
|
+
}
|
|
750
|
+
/**
|
|
751
|
+
* From here copy paste from MatterbridgeDevice
|
|
752
|
+
*/
|
|
578
753
|
/**
|
|
579
754
|
* Get a default IdentifyCluster server.
|
|
580
755
|
*/
|
|
@@ -676,6 +851,18 @@ export class MatterbridgeEndpoint extends Endpoint {
|
|
|
676
851
|
* @param hardwareVersionString - The hardware version string of the device. Default is 'v.1.0.0'.
|
|
677
852
|
*/
|
|
678
853
|
getDefaultBasicInformationClusterServer(deviceName, serialNumber, vendorId, vendorName, productId, productName, softwareVersion = 1, softwareVersionString = '1.0.0', hardwareVersion = 1, hardwareVersionString = '1.0.0') {
|
|
854
|
+
this.log.logName = deviceName;
|
|
855
|
+
this.deviceName = deviceName;
|
|
856
|
+
this.serialNumber = serialNumber;
|
|
857
|
+
this.uniqueId = this.createUniqueId(deviceName, serialNumber, vendorName, productName);
|
|
858
|
+
this.productId = productId;
|
|
859
|
+
this.productName = productName;
|
|
860
|
+
this.vendorId = vendorId;
|
|
861
|
+
this.vendorName = vendorName;
|
|
862
|
+
this.softwareVersion = softwareVersion;
|
|
863
|
+
this.softwareVersionString = softwareVersionString;
|
|
864
|
+
this.hardwareVersion = hardwareVersion;
|
|
865
|
+
this.hardwareVersionString = hardwareVersionString;
|
|
679
866
|
return ClusterServer(BasicInformationCluster, {
|
|
680
867
|
dataModelRevision: 1,
|
|
681
868
|
location: 'XX',
|
|
@@ -717,10 +904,8 @@ export class MatterbridgeEndpoint extends Endpoint {
|
|
|
717
904
|
* @param hardwareVersionString - The hardware version string of the device. Default is 'v.1.0.0'.
|
|
718
905
|
*/
|
|
719
906
|
createDefaultBasicInformationClusterServer(deviceName, serialNumber, vendorId, vendorName, productId, productName, softwareVersion = 1, softwareVersionString = '1.0.0', hardwareVersion = 1, hardwareVersionString = '1.0.0') {
|
|
720
|
-
this.deviceName = deviceName;
|
|
721
|
-
this.serialNumber = serialNumber;
|
|
722
|
-
this.uniqueId = this.createUniqueId(deviceName, serialNumber, vendorName, productName);
|
|
723
907
|
if (MatterbridgeEndpoint.bridgeMode === 'bridge') {
|
|
908
|
+
this.addDeviceType(bridgedNode);
|
|
724
909
|
this.createDefaultBridgedDeviceBasicInformationClusterServer(deviceName, serialNumber, vendorId, vendorName, productName, softwareVersion, softwareVersionString, hardwareVersion, hardwareVersionString);
|
|
725
910
|
return;
|
|
726
911
|
}
|
|
@@ -740,6 +925,18 @@ export class MatterbridgeEndpoint extends Endpoint {
|
|
|
740
925
|
* @param hardwareVersionString - The hardware version string of the device. Default is 'v.1.0.0'.
|
|
741
926
|
*/
|
|
742
927
|
getDefaultBridgedDeviceBasicInformationClusterServer(deviceName, serialNumber, vendorId, vendorName, productName, softwareVersion = 1, softwareVersionString = '1.0.0', hardwareVersion = 1, hardwareVersionString = '1.0.0') {
|
|
928
|
+
this.log.logName = deviceName;
|
|
929
|
+
this.deviceName = deviceName;
|
|
930
|
+
this.serialNumber = serialNumber;
|
|
931
|
+
this.uniqueId = this.createUniqueId(deviceName, serialNumber, vendorName, productName);
|
|
932
|
+
this.productId = undefined;
|
|
933
|
+
this.productName = productName;
|
|
934
|
+
this.vendorId = vendorId;
|
|
935
|
+
this.vendorName = vendorName;
|
|
936
|
+
this.softwareVersion = softwareVersion;
|
|
937
|
+
this.softwareVersionString = softwareVersionString;
|
|
938
|
+
this.hardwareVersion = hardwareVersion;
|
|
939
|
+
this.hardwareVersionString = hardwareVersionString;
|
|
743
940
|
return ClusterServer(BridgedDeviceBasicInformationCluster, {
|
|
744
941
|
vendorId: vendorId !== undefined ? VendorId(vendorId) : undefined, // 4874
|
|
745
942
|
vendorName: vendorName.slice(0, 32),
|
|
@@ -774,9 +971,6 @@ export class MatterbridgeEndpoint extends Endpoint {
|
|
|
774
971
|
* @param hardwareVersionString - The hardware version string of the device. Default is 'v.1.0.0'.
|
|
775
972
|
*/
|
|
776
973
|
createDefaultBridgedDeviceBasicInformationClusterServer(deviceName, serialNumber, vendorId, vendorName, productName, softwareVersion = 1, softwareVersionString = '1.0.0', hardwareVersion = 1, hardwareVersionString = '1.0.0') {
|
|
777
|
-
this.deviceName = deviceName;
|
|
778
|
-
this.serialNumber = serialNumber;
|
|
779
|
-
this.uniqueId = this.createUniqueId(deviceName, serialNumber, vendorName, productName);
|
|
780
974
|
this.addClusterServer(this.getDefaultBridgedDeviceBasicInformationClusterServer(deviceName, serialNumber, vendorId, vendorName, productName, softwareVersion, softwareVersionString, hardwareVersion, hardwareVersionString));
|
|
781
975
|
}
|
|
782
976
|
/**
|
|
@@ -821,7 +1015,7 @@ export class MatterbridgeEndpoint extends Endpoint {
|
|
|
821
1015
|
getDefaultElectricalPowerMeasurementClusterServer(voltage = null, current = null, power = null, frequency = null) {
|
|
822
1016
|
return ClusterServer(ElectricalPowerMeasurementCluster.with(ElectricalPowerMeasurement.Feature.AlternatingCurrent), {
|
|
823
1017
|
powerMode: ElectricalPowerMeasurement.PowerMode.Ac,
|
|
824
|
-
numberOfMeasurementTypes:
|
|
1018
|
+
numberOfMeasurementTypes: 4,
|
|
825
1019
|
accuracy: [
|
|
826
1020
|
{
|
|
827
1021
|
measurementType: MeasurementType.Voltage,
|
|
@@ -858,42 +1052,6 @@ export class MatterbridgeEndpoint extends Endpoint {
|
|
|
858
1052
|
frequency: frequency,
|
|
859
1053
|
}, {}, {});
|
|
860
1054
|
}
|
|
861
|
-
/**
|
|
862
|
-
* Creates a default Dummy Thread Network Diagnostics Cluster server.
|
|
863
|
-
* @deprecated This method is deprecated and is only used for testing.
|
|
864
|
-
*
|
|
865
|
-
* @remarks
|
|
866
|
-
* This method adds a cluster server used only to give the networkName to Eve app.
|
|
867
|
-
*
|
|
868
|
-
* @returns void
|
|
869
|
-
*/
|
|
870
|
-
createDefaultDummyThreadNetworkDiagnosticsClusterServer() {
|
|
871
|
-
this.addClusterServer(ClusterServer(ThreadNetworkDiagnosticsCluster.with(ThreadNetworkDiagnostics.Feature.PacketCounts, ThreadNetworkDiagnostics.Feature.ErrorCounts), {
|
|
872
|
-
channel: 1,
|
|
873
|
-
routingRole: ThreadNetworkDiagnostics.RoutingRole.Router,
|
|
874
|
-
networkName: 'MyMatterThread',
|
|
875
|
-
panId: 0,
|
|
876
|
-
extendedPanId: 0,
|
|
877
|
-
meshLocalPrefix: null,
|
|
878
|
-
neighborTable: [],
|
|
879
|
-
routeTable: [],
|
|
880
|
-
partitionId: null,
|
|
881
|
-
weighting: null,
|
|
882
|
-
dataVersion: null,
|
|
883
|
-
stableDataVersion: null,
|
|
884
|
-
leaderRouterId: null,
|
|
885
|
-
securityPolicy: null,
|
|
886
|
-
channelPage0Mask: null,
|
|
887
|
-
operationalDatasetComponents: null,
|
|
888
|
-
overrunCount: 0,
|
|
889
|
-
activeNetworkFaultsList: [],
|
|
890
|
-
}, {
|
|
891
|
-
resetCounts: async (data) => {
|
|
892
|
-
this.log.debug('Matter command: resetCounts');
|
|
893
|
-
await this.commandHandler.executeHandler('resetCounts', data);
|
|
894
|
-
},
|
|
895
|
-
}, {}));
|
|
896
|
-
}
|
|
897
1055
|
/**
|
|
898
1056
|
* Get a default OnOff cluster server.
|
|
899
1057
|
*
|
|
@@ -921,6 +1079,7 @@ export class MatterbridgeEndpoint extends Endpoint {
|
|
|
921
1079
|
* Creates a default OnOff cluster server.
|
|
922
1080
|
*
|
|
923
1081
|
* @param onOff - The initial state of the OnOff cluster (default: false).
|
|
1082
|
+
* @returns {void}
|
|
924
1083
|
*/
|
|
925
1084
|
createDefaultOnOffClusterServer(onOff = false) {
|
|
926
1085
|
this.addClusterServer(this.getDefaultOnOffClusterServer(onOff));
|
|
@@ -928,12 +1087,17 @@ export class MatterbridgeEndpoint extends Endpoint {
|
|
|
928
1087
|
/**
|
|
929
1088
|
* Get a default level control cluster server.
|
|
930
1089
|
*
|
|
931
|
-
* @param currentLevel - The current level (default:
|
|
1090
|
+
* @param currentLevel - The current level (default: 254).
|
|
1091
|
+
* @param minLevel - The minimum level (default: 1).
|
|
1092
|
+
* @param maxLevel - The maximum level (default: 254).
|
|
1093
|
+
* @param onLevel - The on level (default: null).
|
|
932
1094
|
*/
|
|
933
|
-
getDefaultLevelControlClusterServer(currentLevel =
|
|
1095
|
+
getDefaultLevelControlClusterServer(currentLevel = 254, minLevel = 1, maxLevel = 254, onLevel = null) {
|
|
934
1096
|
return ClusterServer(LevelControlCluster.with(LevelControl.Feature.OnOff), {
|
|
935
1097
|
currentLevel,
|
|
936
|
-
|
|
1098
|
+
minLevel,
|
|
1099
|
+
maxLevel,
|
|
1100
|
+
onLevel,
|
|
937
1101
|
options: {
|
|
938
1102
|
executeIfOff: false,
|
|
939
1103
|
coupleColorTempToLevel: false,
|
|
@@ -970,10 +1134,13 @@ export class MatterbridgeEndpoint extends Endpoint {
|
|
|
970
1134
|
/**
|
|
971
1135
|
* Creates a default level control cluster server.
|
|
972
1136
|
*
|
|
973
|
-
* @param currentLevel - The current level (default:
|
|
1137
|
+
* @param currentLevel - The current level (default: 254).
|
|
1138
|
+
* @param minLevel - The minimum level (default: 1).
|
|
1139
|
+
* @param maxLevel - The maximum level (default: 254).
|
|
1140
|
+
* @param onLevel - The on level (default: null).
|
|
974
1141
|
*/
|
|
975
|
-
createDefaultLevelControlClusterServer(currentLevel =
|
|
976
|
-
this.addClusterServer(this.getDefaultLevelControlClusterServer(currentLevel));
|
|
1142
|
+
createDefaultLevelControlClusterServer(currentLevel = 254, minLevel = 1, maxLevel = 254, onLevel = null) {
|
|
1143
|
+
this.addClusterServer(this.getDefaultLevelControlClusterServer(currentLevel, minLevel, maxLevel, onLevel));
|
|
977
1144
|
}
|
|
978
1145
|
/**
|
|
979
1146
|
* Get a default color control cluster server.
|
|
@@ -986,7 +1153,7 @@ export class MatterbridgeEndpoint extends Endpoint {
|
|
|
986
1153
|
* @param colorTempPhysicalMinMireds - The physical minimum color temperature in mireds.
|
|
987
1154
|
* @param colorTempPhysicalMaxMireds - The physical maximum color temperature in mireds.
|
|
988
1155
|
*/
|
|
989
|
-
|
|
1156
|
+
getDefaultColorControlClusterServer(currentX = 0, currentY = 0, currentHue = 0, currentSaturation = 0, colorTemperatureMireds = 500, colorTempPhysicalMinMireds = 147, colorTempPhysicalMaxMireds = 500) {
|
|
990
1157
|
return ClusterServer(ColorControlCluster.with(ColorControl.Feature.Xy, ColorControl.Feature.HueSaturation, ColorControl.Feature.ColorTemperature), {
|
|
991
1158
|
colorMode: ColorControl.ColorMode.CurrentHueAndCurrentSaturation,
|
|
992
1159
|
enhancedColorMode: ColorControl.EnhancedColorMode.CurrentHueAndCurrentSaturation,
|
|
@@ -1002,6 +1169,8 @@ export class MatterbridgeEndpoint extends Endpoint {
|
|
|
1002
1169
|
colorTemperatureMireds,
|
|
1003
1170
|
colorTempPhysicalMinMireds,
|
|
1004
1171
|
colorTempPhysicalMaxMireds,
|
|
1172
|
+
coupleColorTempToLevelMinMireds: colorTempPhysicalMinMireds,
|
|
1173
|
+
startUpColorTemperatureMireds: null,
|
|
1005
1174
|
}, {
|
|
1006
1175
|
moveToColor: async (data) => {
|
|
1007
1176
|
this.log.debug('Matter command: moveToColor request:', data.request, 'attributes.currentX:', data.attributes.currentX.getLocal(), 'attributes.currentY:', data.attributes.currentY.getLocal());
|
|
@@ -1063,14 +1232,15 @@ export class MatterbridgeEndpoint extends Endpoint {
|
|
|
1063
1232
|
* @param colorTempPhysicalMinMireds - The physical minimum color temperature in mireds.
|
|
1064
1233
|
* @param colorTempPhysicalMaxMireds - The physical maximum color temperature in mireds.
|
|
1065
1234
|
*/
|
|
1066
|
-
|
|
1067
|
-
this.addClusterServer(this.
|
|
1235
|
+
createDefaultColorControlClusterServer(currentX = 0, currentY = 0, currentHue = 0, currentSaturation = 0, colorTemperatureMireds = 500, colorTempPhysicalMinMireds = 147, colorTempPhysicalMaxMireds = 500) {
|
|
1236
|
+
this.addClusterServer(this.getDefaultColorControlClusterServer(currentX, currentY, currentHue, currentSaturation, colorTemperatureMireds, colorTempPhysicalMinMireds, colorTempPhysicalMaxMireds));
|
|
1068
1237
|
}
|
|
1238
|
+
isColorControlConfigured = false;
|
|
1069
1239
|
/**
|
|
1070
1240
|
* Configures the color control cluster for a device.
|
|
1071
1241
|
*
|
|
1072
|
-
* @remark This method must be called only after creating the cluster with
|
|
1073
|
-
* and before starting the matter
|
|
1242
|
+
* @remark This method must be called only after creating the cluster with getDefaultColorControlClusterServer or createDefaultColorControlClusterServer
|
|
1243
|
+
* and before starting the matter node.
|
|
1074
1244
|
*
|
|
1075
1245
|
* @param {boolean} hueSaturation - A boolean indicating whether the device supports hue and saturation control.
|
|
1076
1246
|
* @param {boolean} xy - A boolean indicating whether the device supports XY control.
|
|
@@ -1078,15 +1248,27 @@ export class MatterbridgeEndpoint extends Endpoint {
|
|
|
1078
1248
|
* @param {ColorControl.ColorMode} colorMode - An optional parameter specifying the color mode of the device.
|
|
1079
1249
|
* @param {Endpoint} endpoint - An optional parameter specifying the endpoint to configure. If not provided, the device endpoint will be used.
|
|
1080
1250
|
*/
|
|
1081
|
-
configureColorControlCluster(hueSaturation, xy, colorTemperature, colorMode, endpoint) {
|
|
1251
|
+
async configureColorControlCluster(hueSaturation, xy, colorTemperature, colorMode, endpoint) {
|
|
1082
1252
|
if (!endpoint)
|
|
1083
1253
|
endpoint = this;
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
if (
|
|
1087
|
-
|
|
1088
|
-
|
|
1254
|
+
if (this.isColorControlConfigured)
|
|
1255
|
+
return;
|
|
1256
|
+
if (endpoint.construction.status !== Lifecycle.Status.Active) {
|
|
1257
|
+
this.log.debug(`**configureColorControlCluster() delaying for endpoint construction ${endpoint.construction.status}`);
|
|
1258
|
+
setTimeout(async () => {
|
|
1259
|
+
await endpoint.configureColorControlCluster(hueSaturation, xy, colorTemperature, colorMode, endpoint);
|
|
1260
|
+
this.isColorControlConfigured = true;
|
|
1261
|
+
}, 500);
|
|
1262
|
+
return;
|
|
1263
|
+
}
|
|
1264
|
+
this.log.debug(`**configureColorControlCluster()`);
|
|
1265
|
+
await endpoint.setAttribute(ColorControlCluster.id, 'featureMap', { hueSaturation, enhancedHue: false, colorLoop: false, xy, colorTemperature }, this.log, endpoint);
|
|
1266
|
+
await endpoint.setAttribute(ColorControlCluster.id, 'colorCapabilities', { hueSaturation, enhancedHue: false, colorLoop: false, xy, colorTemperature }, this.log, endpoint);
|
|
1267
|
+
if (isValidNumber(colorMode, ColorControl.ColorMode.CurrentHueAndCurrentSaturation, ColorControl.ColorMode.ColorTemperatureMireds)) {
|
|
1268
|
+
await endpoint.setAttribute(ColorControlCluster.id, 'colorMode', colorMode, this.log, endpoint);
|
|
1269
|
+
await endpoint.setAttribute(ColorControlCluster.id, 'enhancedColorMode', colorMode, this.log, endpoint);
|
|
1089
1270
|
}
|
|
1271
|
+
this.isColorControlConfigured = true;
|
|
1090
1272
|
}
|
|
1091
1273
|
/**
|
|
1092
1274
|
* Configures the color control mode for the device.
|
|
@@ -1094,18 +1276,18 @@ export class MatterbridgeEndpoint extends Endpoint {
|
|
|
1094
1276
|
* @param {ColorControl.ColorMode} colorMode - The color mode to set.
|
|
1095
1277
|
* @param {Endpoint} endpoint - The optional endpoint to configure. If not provided, the method will configure the current endpoint.
|
|
1096
1278
|
*/
|
|
1097
|
-
configureColorControlMode(colorMode, endpoint) {
|
|
1279
|
+
async configureColorControlMode(colorMode, endpoint) {
|
|
1098
1280
|
if (!endpoint)
|
|
1099
1281
|
endpoint = this;
|
|
1100
|
-
if (colorMode
|
|
1101
|
-
endpoint.
|
|
1102
|
-
endpoint.
|
|
1282
|
+
if (isValidNumber(colorMode, ColorControl.ColorMode.CurrentHueAndCurrentSaturation, ColorControl.ColorMode.ColorTemperatureMireds)) {
|
|
1283
|
+
await endpoint.setAttribute(ColorControlCluster.id, 'colorMode', colorMode, this.log, endpoint);
|
|
1284
|
+
await endpoint.setAttribute(ColorControlCluster.id, 'enhancedColorMode', colorMode, this.log, endpoint);
|
|
1103
1285
|
}
|
|
1104
1286
|
}
|
|
1105
1287
|
/**
|
|
1106
1288
|
* Get a default window covering cluster server.
|
|
1107
1289
|
*
|
|
1108
|
-
* @param positionPercent100ths - The position percentage in 100ths (0-10000). Defaults to 0.
|
|
1290
|
+
* @param positionPercent100ths - The position percentage in 100ths (0-10000). Defaults to 0. Matter uses 10000 = fully closed 0 = fully opened.
|
|
1109
1291
|
*/
|
|
1110
1292
|
getDefaultWindowCoveringClusterServer(positionPercent100ths) {
|
|
1111
1293
|
return ClusterServer(WindowCoveringCluster.with(WindowCovering.Feature.Lift, WindowCovering.Feature.PositionAwareLift), {
|
|
@@ -1124,8 +1306,6 @@ export class MatterbridgeEndpoint extends Endpoint {
|
|
|
1124
1306
|
mode: { motorDirectionReversed: false, calibrationMode: false, maintenanceMode: false, ledFeedback: false },
|
|
1125
1307
|
targetPositionLiftPercent100ths: positionPercent100ths ?? 0, // 0 Fully open 10000 fully closed
|
|
1126
1308
|
currentPositionLiftPercent100ths: positionPercent100ths ?? 0, // 0 Fully open 10000 fully closed
|
|
1127
|
-
// installedClosedLimitLift: 10000,
|
|
1128
|
-
// installedOpenLimitLift: 0,
|
|
1129
1309
|
}, {
|
|
1130
1310
|
upOrOpen: async (data) => {
|
|
1131
1311
|
this.log.debug('Matter command: upOrOpen');
|
|
@@ -1149,7 +1329,7 @@ export class MatterbridgeEndpoint extends Endpoint {
|
|
|
1149
1329
|
/**
|
|
1150
1330
|
* Creates a default window covering cluster server.
|
|
1151
1331
|
*
|
|
1152
|
-
* @param positionPercent100ths - The position percentage in 100ths (0-10000). Defaults to 0.
|
|
1332
|
+
* @param positionPercent100ths - The position percentage in 100ths (0-10000). Defaults to 0. Matter uses 10000 = fully closed 0 = fully opened.
|
|
1153
1333
|
*/
|
|
1154
1334
|
createDefaultWindowCoveringClusterServer(positionPercent100ths) {
|
|
1155
1335
|
this.addClusterServer(this.getDefaultWindowCoveringClusterServer(positionPercent100ths));
|
|
@@ -1158,22 +1338,19 @@ export class MatterbridgeEndpoint extends Endpoint {
|
|
|
1158
1338
|
* Sets the window covering target position as the current position and stops the movement.
|
|
1159
1339
|
* @param {Endpoint} endpoint - The endpoint on which to set the window covering (default the device endpoint).
|
|
1160
1340
|
*/
|
|
1161
|
-
setWindowCoveringTargetAsCurrentAndStopped(endpoint) {
|
|
1341
|
+
async setWindowCoveringTargetAsCurrentAndStopped(endpoint) {
|
|
1162
1342
|
if (!endpoint)
|
|
1163
1343
|
endpoint = this;
|
|
1164
|
-
const
|
|
1165
|
-
if (
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
tilt: WindowCovering.MovementStatus.Stopped,
|
|
1173
|
-
});
|
|
1174
|
-
}
|
|
1175
|
-
this.log.debug(`Set WindowCovering currentPositionLiftPercent100ths and targetPositionLiftPercent100ths to ${position} and operationalStatus to Stopped.`);
|
|
1344
|
+
const position = endpoint.getAttribute(WindowCoveringCluster.id, 'currentPositionLiftPercent100ths', this.log, endpoint); // windowCoveringCluster.getCurrentPositionLiftPercent100thsAttribute();
|
|
1345
|
+
if (position !== null) {
|
|
1346
|
+
await endpoint.setAttribute(WindowCoveringCluster.id, 'targetPositionLiftPercent100ths', position, this.log, endpoint);
|
|
1347
|
+
await endpoint.setAttribute(WindowCoveringCluster.id, 'operationalStatus', {
|
|
1348
|
+
global: WindowCovering.MovementStatus.Stopped,
|
|
1349
|
+
lift: WindowCovering.MovementStatus.Stopped,
|
|
1350
|
+
tilt: WindowCovering.MovementStatus.Stopped,
|
|
1351
|
+
}, this.log, endpoint);
|
|
1176
1352
|
}
|
|
1353
|
+
this.log.debug(`Set WindowCovering currentPositionLiftPercent100ths and targetPositionLiftPercent100ths to ${position} and operationalStatus to Stopped.`);
|
|
1177
1354
|
}
|
|
1178
1355
|
/**
|
|
1179
1356
|
* Sets the current and target status of a window covering.
|
|
@@ -1182,19 +1359,16 @@ export class MatterbridgeEndpoint extends Endpoint {
|
|
|
1182
1359
|
* @param {WindowCovering.MovementStatus} status - The movement status of the window covering.
|
|
1183
1360
|
* @param {Endpoint} endpoint - The endpoint on which to set the window covering (default the device endpoint).
|
|
1184
1361
|
*/
|
|
1185
|
-
setWindowCoveringCurrentTargetStatus(current, target, status, endpoint) {
|
|
1362
|
+
async setWindowCoveringCurrentTargetStatus(current, target, status, endpoint) {
|
|
1186
1363
|
if (!endpoint)
|
|
1187
1364
|
endpoint = this;
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
tilt: status,
|
|
1196
|
-
});
|
|
1197
|
-
}
|
|
1365
|
+
await endpoint.setAttribute(WindowCoveringCluster.id, 'currentPositionLiftPercent100ths', current, this.log, endpoint);
|
|
1366
|
+
await endpoint.setAttribute(WindowCoveringCluster.id, 'targetPositionLiftPercent100ths', target, this.log, endpoint);
|
|
1367
|
+
await endpoint.setAttribute(WindowCoveringCluster.id, 'operationalStatus', {
|
|
1368
|
+
global: status,
|
|
1369
|
+
lift: status,
|
|
1370
|
+
tilt: status,
|
|
1371
|
+
}, this.log, endpoint);
|
|
1198
1372
|
this.log.debug(`Set WindowCovering currentPositionLiftPercent100ths: ${current}, targetPositionLiftPercent100ths: ${target} and operationalStatus: ${status}.`);
|
|
1199
1373
|
}
|
|
1200
1374
|
/**
|
|
@@ -1202,13 +1376,14 @@ export class MatterbridgeEndpoint extends Endpoint {
|
|
|
1202
1376
|
* @param {WindowCovering.MovementStatus} status - The movement status to set.
|
|
1203
1377
|
* @param {Endpoint} endpoint - The endpoint on which to set the window covering (default the device endpoint).
|
|
1204
1378
|
*/
|
|
1205
|
-
setWindowCoveringStatus(status, endpoint) {
|
|
1379
|
+
async setWindowCoveringStatus(status, endpoint) {
|
|
1206
1380
|
if (!endpoint)
|
|
1207
1381
|
endpoint = this;
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1382
|
+
await endpoint.setAttribute(WindowCoveringCluster.id, 'operationalStatus', {
|
|
1383
|
+
global: status,
|
|
1384
|
+
lift: status,
|
|
1385
|
+
tilt: status,
|
|
1386
|
+
}, this.log, endpoint);
|
|
1212
1387
|
this.log.debug(`Set WindowCovering operationalStatus: ${status}`);
|
|
1213
1388
|
}
|
|
1214
1389
|
/**
|
|
@@ -1220,10 +1395,7 @@ export class MatterbridgeEndpoint extends Endpoint {
|
|
|
1220
1395
|
getWindowCoveringStatus(endpoint) {
|
|
1221
1396
|
if (!endpoint)
|
|
1222
1397
|
endpoint = this;
|
|
1223
|
-
const
|
|
1224
|
-
if (!windowCovering)
|
|
1225
|
-
return undefined;
|
|
1226
|
-
const status = windowCovering.getOperationalStatusAttribute();
|
|
1398
|
+
const status = endpoint.getAttribute(WindowCoveringCluster.id, 'operationalStatus', this.log, endpoint);
|
|
1227
1399
|
this.log.debug(`Get WindowCovering operationalStatus: ${status.global}`);
|
|
1228
1400
|
return status.global;
|
|
1229
1401
|
}
|
|
@@ -1233,14 +1405,11 @@ export class MatterbridgeEndpoint extends Endpoint {
|
|
|
1233
1405
|
* @param position - The position to set, specified as a number.
|
|
1234
1406
|
* @param {Endpoint} endpoint - The endpoint on which to set the window covering (default the device endpoint).
|
|
1235
1407
|
*/
|
|
1236
|
-
setWindowCoveringTargetAndCurrentPosition(position, endpoint) {
|
|
1408
|
+
async setWindowCoveringTargetAndCurrentPosition(position, endpoint) {
|
|
1237
1409
|
if (!endpoint)
|
|
1238
1410
|
endpoint = this;
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
return;
|
|
1242
|
-
windowCovering.setCurrentPositionLiftPercent100thsAttribute(position);
|
|
1243
|
-
windowCovering.setTargetPositionLiftPercent100thsAttribute(position);
|
|
1411
|
+
await endpoint.setAttribute(WindowCoveringCluster.id, 'currentPositionLiftPercent100ths', position, this.log, endpoint);
|
|
1412
|
+
await endpoint.setAttribute(WindowCoveringCluster.id, 'targetPositionLiftPercent100ths', position, this.log, endpoint);
|
|
1244
1413
|
this.log.debug(`Set WindowCovering currentPositionLiftPercent100ths: ${position} and targetPositionLiftPercent100ths: ${position}.`);
|
|
1245
1414
|
}
|
|
1246
1415
|
/**
|
|
@@ -1341,7 +1510,7 @@ export class MatterbridgeEndpoint extends Endpoint {
|
|
|
1341
1510
|
* @param {Endpoint} endpoint - The endpoint on which to trigger the event (default the device endpoint).
|
|
1342
1511
|
* @returns {void}
|
|
1343
1512
|
*/
|
|
1344
|
-
triggerSwitchEvent(event, log, endpoint) {
|
|
1513
|
+
async triggerSwitchEvent(event, log, endpoint) {
|
|
1345
1514
|
if (!endpoint)
|
|
1346
1515
|
endpoint = this;
|
|
1347
1516
|
if (['Single', 'Double', 'Long'].includes(event)) {
|
|
@@ -1355,38 +1524,38 @@ export class MatterbridgeEndpoint extends Endpoint {
|
|
|
1355
1524
|
return false;
|
|
1356
1525
|
}
|
|
1357
1526
|
if (event === 'Single') {
|
|
1358
|
-
cluster.
|
|
1359
|
-
cluster.
|
|
1360
|
-
cluster.
|
|
1361
|
-
cluster.
|
|
1362
|
-
cluster.
|
|
1363
|
-
cluster.
|
|
1527
|
+
await endpoint.setAttribute(cluster.id, 'currentPosition', 1, log);
|
|
1528
|
+
endpoint.triggerEvent(cluster.id, 'initialPress', { newPosition: 1 }, log);
|
|
1529
|
+
await endpoint.setAttribute(cluster.id, 'currentPosition', 0, log);
|
|
1530
|
+
endpoint.triggerEvent(cluster.id, 'shortRelease', { previousPosition: 1 }, log);
|
|
1531
|
+
await endpoint.setAttribute(cluster.id, 'currentPosition', 0, log);
|
|
1532
|
+
endpoint.triggerEvent(cluster.id, 'multiPressComplete', { previousPosition: 1, totalNumberOfPressesCounted: 1 }, log);
|
|
1364
1533
|
log?.info(`${db}Trigger endpoint ${or}${endpoint.id}:${endpoint.number}${db} event ${hk}${cluster.name}.SinglePress${db}`);
|
|
1365
1534
|
}
|
|
1366
1535
|
if (event === 'Double') {
|
|
1367
|
-
cluster.
|
|
1368
|
-
cluster.
|
|
1369
|
-
cluster.
|
|
1370
|
-
cluster.
|
|
1371
|
-
cluster.
|
|
1372
|
-
cluster.
|
|
1373
|
-
cluster.
|
|
1374
|
-
cluster.
|
|
1375
|
-
cluster.
|
|
1376
|
-
cluster.
|
|
1536
|
+
await endpoint.setAttribute(cluster.id, 'currentPosition', 1, log);
|
|
1537
|
+
endpoint.triggerEvent(cluster.id, 'initialPress', { newPosition: 1 }, log);
|
|
1538
|
+
await endpoint.setAttribute(cluster.id, 'currentPosition', 0, log);
|
|
1539
|
+
endpoint.triggerEvent(cluster.id, 'shortRelease', { previousPosition: 1 }, log);
|
|
1540
|
+
await endpoint.setAttribute(cluster.id, 'currentPosition', 1, log);
|
|
1541
|
+
endpoint.triggerEvent(cluster.id, 'initialPress', { newPosition: 1 }, log);
|
|
1542
|
+
endpoint.triggerEvent(cluster.id, 'multiPressOngoing', { newPosition: 1, currentNumberOfPressesCounted: 2 }, log);
|
|
1543
|
+
await endpoint.setAttribute(cluster.id, 'currentPosition', 0, log);
|
|
1544
|
+
endpoint.triggerEvent(cluster.id, 'shortRelease', { previousPosition: 1 }, log);
|
|
1545
|
+
endpoint.triggerEvent(cluster.id, 'multiPressComplete', { previousPosition: 1, totalNumberOfPressesCounted: 2 }, log);
|
|
1377
1546
|
log?.info(`${db}Trigger endpoint ${or}${endpoint.id}:${endpoint.number}${db} event ${hk}${cluster.name}.DoublePress${db}`);
|
|
1378
1547
|
}
|
|
1379
1548
|
if (event === 'Long') {
|
|
1380
|
-
cluster.
|
|
1381
|
-
cluster.
|
|
1382
|
-
cluster.
|
|
1383
|
-
cluster.
|
|
1384
|
-
cluster.
|
|
1549
|
+
await endpoint.setAttribute(cluster.id, 'currentPosition', 1, log);
|
|
1550
|
+
endpoint.triggerEvent(cluster.id, 'initialPress', { newPosition: 1 }, log);
|
|
1551
|
+
endpoint.triggerEvent(cluster.id, 'longPress', { newPosition: 1 }, log);
|
|
1552
|
+
await endpoint.setAttribute(cluster.id, 'currentPosition', 0, log);
|
|
1553
|
+
endpoint.triggerEvent(cluster.id, 'longRelease', { previousPosition: 1 }, log);
|
|
1385
1554
|
log?.info(`${db}Trigger endpoint ${or}${endpoint.id}:${endpoint.number}${db} event ${hk}${cluster.name}.LongPress${db}`);
|
|
1386
1555
|
}
|
|
1387
1556
|
}
|
|
1388
1557
|
if (['Press', 'Release'].includes(event)) {
|
|
1389
|
-
const cluster = endpoint.getClusterServer(Switch.
|
|
1558
|
+
const cluster = endpoint.getClusterServer(SwitchCluster.with(Switch.Feature.LatchingSwitch));
|
|
1390
1559
|
if (!cluster || !cluster.getFeatureMapAttribute().latchingSwitch) {
|
|
1391
1560
|
log?.error(`triggerSwitchEvent ${event} error: Switch cluster with LatchingSwitch not found on endpoint ${endpoint.id}:${endpoint.number}`);
|
|
1392
1561
|
return false;
|
|
@@ -1396,17 +1565,13 @@ export class MatterbridgeEndpoint extends Endpoint {
|
|
|
1396
1565
|
return false;
|
|
1397
1566
|
}
|
|
1398
1567
|
if (event === 'Press') {
|
|
1399
|
-
cluster.
|
|
1400
|
-
|
|
1401
|
-
if (cluster.triggerSwitchLatchedEvent)
|
|
1402
|
-
cluster.triggerSwitchLatchedEvent({ newPosition: 1 });
|
|
1568
|
+
await endpoint.setAttribute(cluster.id, 'currentPosition', 1, log);
|
|
1569
|
+
endpoint.triggerEvent(cluster.id, 'switchLatched', { newPosition: 1 }, log);
|
|
1403
1570
|
log?.info(`${db}Trigger endpoint ${or}${endpoint.id}:${endpoint.number}${db} event ${hk}${cluster.name}.Press${db}`);
|
|
1404
1571
|
}
|
|
1405
1572
|
if (event === 'Release') {
|
|
1406
|
-
cluster.
|
|
1407
|
-
|
|
1408
|
-
if (cluster.triggerSwitchLatchedEvent)
|
|
1409
|
-
cluster.triggerSwitchLatchedEvent({ newPosition: 0 });
|
|
1573
|
+
await endpoint.setAttribute(cluster.id, 'currentPosition', 0, log);
|
|
1574
|
+
endpoint.triggerEvent(cluster.id, 'switchLatched', { newPosition: 0 }, log);
|
|
1410
1575
|
log?.info(`${db}Trigger endpoint ${or}${endpoint.id}:${endpoint.number}${db} event ${hk}${cluster.name}.Release${db}`);
|
|
1411
1576
|
}
|
|
1412
1577
|
}
|
|
@@ -1438,10 +1603,12 @@ export class MatterbridgeEndpoint extends Endpoint {
|
|
|
1438
1603
|
/**
|
|
1439
1604
|
* Creates a default mode select cluster server.
|
|
1440
1605
|
*
|
|
1441
|
-
* @
|
|
1442
|
-
*
|
|
1443
|
-
*
|
|
1606
|
+
* @param description - The description of the cluster server.
|
|
1607
|
+
* @param supportedModes - The supported modes for the cluster server.
|
|
1608
|
+
* @param currentMode - The current mode of the cluster server. Defaults to 0.
|
|
1609
|
+
* @param startUpMode - The startup mode of the cluster server. Defaults to 0.
|
|
1444
1610
|
* @param endpoint - The endpoint to add the cluster server to. Defaults to `this` if not provided.
|
|
1611
|
+
*
|
|
1445
1612
|
*/
|
|
1446
1613
|
createDefaultModeSelectClusterServer(description, supportedModes, currentMode = 0, startUpMode = 0, endpoint) {
|
|
1447
1614
|
if (!endpoint)
|
|
@@ -1493,7 +1660,7 @@ export class MatterbridgeEndpoint extends Endpoint {
|
|
|
1493
1660
|
/**
|
|
1494
1661
|
* Get a default flow measurement cluster server.
|
|
1495
1662
|
*
|
|
1496
|
-
* @param measuredValue - The measured value of the
|
|
1663
|
+
* @param measuredValue - The measured value of the flow in 10 x m/h.
|
|
1497
1664
|
*/
|
|
1498
1665
|
getDefaultFlowMeasurementClusterServer(measuredValue = 0) {
|
|
1499
1666
|
return ClusterServer(FlowMeasurementCluster, {
|
|
@@ -1506,7 +1673,7 @@ export class MatterbridgeEndpoint extends Endpoint {
|
|
|
1506
1673
|
/**
|
|
1507
1674
|
* Creates a default flow measurement cluster server.
|
|
1508
1675
|
*
|
|
1509
|
-
* @param measuredValue - The measured value of the
|
|
1676
|
+
* @param measuredValue - The measured value of the flow in 10 x m/h.
|
|
1510
1677
|
*/
|
|
1511
1678
|
createDefaultFlowMeasurementClusterServer(measuredValue = 0) {
|
|
1512
1679
|
this.addClusterServer(this.getDefaultFlowMeasurementClusterServer(measuredValue));
|
|
@@ -1514,7 +1681,7 @@ export class MatterbridgeEndpoint extends Endpoint {
|
|
|
1514
1681
|
/**
|
|
1515
1682
|
* Get a default temperature measurement cluster server.
|
|
1516
1683
|
*
|
|
1517
|
-
* @param measuredValue - The measured value of the temperature.
|
|
1684
|
+
* @param measuredValue - The measured value of the temperature x 100.
|
|
1518
1685
|
*/
|
|
1519
1686
|
getDefaultTemperatureMeasurementClusterServer(measuredValue = 0) {
|
|
1520
1687
|
return ClusterServer(TemperatureMeasurementCluster, {
|
|
@@ -1527,7 +1694,7 @@ export class MatterbridgeEndpoint extends Endpoint {
|
|
|
1527
1694
|
/**
|
|
1528
1695
|
* Creates a default temperature measurement cluster server.
|
|
1529
1696
|
*
|
|
1530
|
-
* @param measuredValue - The measured value of the temperature.
|
|
1697
|
+
* @param measuredValue - The measured value of the temperature x 100.
|
|
1531
1698
|
*/
|
|
1532
1699
|
createDefaultTemperatureMeasurementClusterServer(measuredValue = 0) {
|
|
1533
1700
|
this.addClusterServer(this.getDefaultTemperatureMeasurementClusterServer(measuredValue));
|
|
@@ -1535,7 +1702,7 @@ export class MatterbridgeEndpoint extends Endpoint {
|
|
|
1535
1702
|
/**
|
|
1536
1703
|
* Get a default RelativeHumidityMeasurementCluster server.
|
|
1537
1704
|
*
|
|
1538
|
-
* @param measuredValue - The measured value of the relative humidity.
|
|
1705
|
+
* @param measuredValue - The measured value of the relative humidity x 100.
|
|
1539
1706
|
*/
|
|
1540
1707
|
getDefaultRelativeHumidityMeasurementClusterServer(measuredValue = 0) {
|
|
1541
1708
|
return ClusterServer(RelativeHumidityMeasurementCluster, {
|
|
@@ -1548,7 +1715,7 @@ export class MatterbridgeEndpoint extends Endpoint {
|
|
|
1548
1715
|
/**
|
|
1549
1716
|
* Creates a default RelativeHumidityMeasurementCluster server.
|
|
1550
1717
|
*
|
|
1551
|
-
* @param measuredValue - The measured value of the relative humidity.
|
|
1718
|
+
* @param measuredValue - The measured value of the relative humidity x 100.
|
|
1552
1719
|
*/
|
|
1553
1720
|
createDefaultRelativeHumidityMeasurementClusterServer(measuredValue = 0) {
|
|
1554
1721
|
this.addClusterServer(this.getDefaultRelativeHumidityMeasurementClusterServer(measuredValue));
|
|
@@ -1769,6 +1936,80 @@ export class MatterbridgeEndpoint extends Endpoint {
|
|
|
1769
1936
|
createDefaultTvocMeasurementClusterServer(measuredValue = 0, measurementUnit = ConcentrationMeasurement.MeasurementUnit.Ppm, measurementMedium = ConcentrationMeasurement.MeasurementMedium.Air) {
|
|
1770
1937
|
this.addClusterServer(this.getDefaultTvocMeasurementClusterServer(measuredValue, measurementUnit, measurementMedium));
|
|
1771
1938
|
}
|
|
1939
|
+
/**
|
|
1940
|
+
* Get a default heating thermostat cluster server with the specified parameters.
|
|
1941
|
+
* @param {number} [localTemperature] - The local temperature value in degrees Celsius. Defaults to 23°.
|
|
1942
|
+
* @param {number} [occupiedHeatingSetpoint] - The occupied heating setpoint value in degrees Celsius. Defaults to 21°.
|
|
1943
|
+
* @param {number} [minHeatSetpointLimit] - The minimum heat setpoint limit value. Defaults to 0°.
|
|
1944
|
+
* @param {number} [maxHeatSetpointLimit] - The maximum heat setpoint limit value. Defaults to 50°.
|
|
1945
|
+
* @returns {ThermostatClusterServer} A default thermostat cluster server configured with the specified parameters.
|
|
1946
|
+
*/
|
|
1947
|
+
getDefaultHeatingThermostatClusterServer(localTemperature = 23, occupiedHeatingSetpoint = 21, minHeatSetpointLimit = 0, maxHeatSetpointLimit = 50) {
|
|
1948
|
+
return ClusterServer(ThermostatCluster.with(Thermostat.Feature.Heating), {
|
|
1949
|
+
localTemperature: localTemperature * 100,
|
|
1950
|
+
systemMode: Thermostat.SystemMode.Heat,
|
|
1951
|
+
controlSequenceOfOperation: Thermostat.ControlSequenceOfOperation.HeatingOnly,
|
|
1952
|
+
// Thermostat.Feature.Heating
|
|
1953
|
+
occupiedHeatingSetpoint: occupiedHeatingSetpoint * 100,
|
|
1954
|
+
minHeatSetpointLimit: minHeatSetpointLimit * 100,
|
|
1955
|
+
maxHeatSetpointLimit: maxHeatSetpointLimit * 100,
|
|
1956
|
+
absMinHeatSetpointLimit: minHeatSetpointLimit * 100,
|
|
1957
|
+
absMaxHeatSetpointLimit: maxHeatSetpointLimit * 100,
|
|
1958
|
+
}, {
|
|
1959
|
+
setpointRaiseLower: async (data) => {
|
|
1960
|
+
this.log.debug('Matter command: setpointRaiseLower', data.request);
|
|
1961
|
+
await this.commandHandler.executeHandler('setpointRaiseLower', data);
|
|
1962
|
+
},
|
|
1963
|
+
}, {});
|
|
1964
|
+
}
|
|
1965
|
+
/**
|
|
1966
|
+
* Creates and adds a default heating thermostat cluster server to the device.
|
|
1967
|
+
*
|
|
1968
|
+
* @param {number} [localTemperature] - The local temperature value in degrees Celsius. Defaults to 23°.
|
|
1969
|
+
* @param {number} [occupiedHeatingSetpoint] - The occupied heating setpoint value in degrees Celsius. Defaults to 21°.
|
|
1970
|
+
* @param {number} [minHeatSetpointLimit] - The minimum heat setpoint limit value. Defaults to 0°.
|
|
1971
|
+
* @param {number} [maxHeatSetpointLimit] - The maximum heat setpoint limit value. Defaults to 50°.
|
|
1972
|
+
*/
|
|
1973
|
+
createDefaultHeatingThermostatClusterServer(localTemperature = 23, occupiedHeatingSetpoint = 25, minHeatSetpointLimit = 0, maxHeatSetpointLimit = 50) {
|
|
1974
|
+
this.addClusterServer(this.getDefaultHeatingThermostatClusterServer(localTemperature, occupiedHeatingSetpoint, minHeatSetpointLimit, maxHeatSetpointLimit));
|
|
1975
|
+
}
|
|
1976
|
+
/**
|
|
1977
|
+
* Get a default cooling thermostat cluster server with the specified parameters.
|
|
1978
|
+
* @param {number} [localTemperature] - The local temperature value in degrees Celsius. Defaults to 23°.
|
|
1979
|
+
* @param {number} [occupiedCoolingSetpoint] - The occupied cooling setpoint value in degrees Celsius. Defaults to 25°.
|
|
1980
|
+
* @param {number} [minCoolSetpointLimit] - The minimum cool setpoint limit value. Defaults to 0°.
|
|
1981
|
+
* @param {number} [maxCoolSetpointLimit] - The maximum cool setpoint limit value. Defaults to 50°.
|
|
1982
|
+
* @returns {ThermostatClusterServer} A default thermostat cluster server configured with the specified parameters.
|
|
1983
|
+
*/
|
|
1984
|
+
getDefaultCoolingThermostatClusterServer(localTemperature = 23, occupiedCoolingSetpoint = 25, minCoolSetpointLimit = 0, maxCoolSetpointLimit = 50) {
|
|
1985
|
+
return ClusterServer(ThermostatCluster.with(Thermostat.Feature.Cooling), {
|
|
1986
|
+
localTemperature: localTemperature * 100,
|
|
1987
|
+
systemMode: Thermostat.SystemMode.Cool,
|
|
1988
|
+
controlSequenceOfOperation: Thermostat.ControlSequenceOfOperation.CoolingOnly,
|
|
1989
|
+
// Thermostat.Feature.Cooling
|
|
1990
|
+
occupiedCoolingSetpoint: occupiedCoolingSetpoint * 100,
|
|
1991
|
+
minCoolSetpointLimit: minCoolSetpointLimit * 100,
|
|
1992
|
+
maxCoolSetpointLimit: maxCoolSetpointLimit * 100,
|
|
1993
|
+
absMinCoolSetpointLimit: minCoolSetpointLimit * 100,
|
|
1994
|
+
absMaxCoolSetpointLimit: maxCoolSetpointLimit * 100,
|
|
1995
|
+
}, {
|
|
1996
|
+
setpointRaiseLower: async (data) => {
|
|
1997
|
+
this.log.debug('Matter command: setpointRaiseLower', data.request);
|
|
1998
|
+
await this.commandHandler.executeHandler('setpointRaiseLower', data);
|
|
1999
|
+
},
|
|
2000
|
+
}, {});
|
|
2001
|
+
}
|
|
2002
|
+
/**
|
|
2003
|
+
* Creates and adds a default cooling thermostat cluster server to the device.
|
|
2004
|
+
*
|
|
2005
|
+
* @param {number} [localTemperature] - The local temperature value in degrees Celsius. Defaults to 23°.
|
|
2006
|
+
* @param {number} [occupiedCoolingSetpoint] - The occupied cooling setpoint value in degrees Celsius. Defaults to 25°.
|
|
2007
|
+
* @param {number} [minCoolSetpointLimit] - The minimum cool setpoint limit value. Defaults to 0°.
|
|
2008
|
+
* @param {number} [maxCoolSetpointLimit] - The maximum cool setpoint limit value. Defaults to 50°.
|
|
2009
|
+
*/
|
|
2010
|
+
createDefaultCoolingThermostatClusterServer(localTemperature = 23, occupiedCoolingSetpoint = 25, minCoolSetpointLimit = 0, maxCoolSetpointLimit = 50) {
|
|
2011
|
+
this.addClusterServer(this.getDefaultCoolingThermostatClusterServer(localTemperature, occupiedCoolingSetpoint, minCoolSetpointLimit, maxCoolSetpointLimit));
|
|
2012
|
+
}
|
|
1772
2013
|
/**
|
|
1773
2014
|
* Get a default thermostat cluster server with the specified parameters.
|
|
1774
2015
|
*
|
|
@@ -1785,19 +2026,22 @@ export class MatterbridgeEndpoint extends Endpoint {
|
|
|
1785
2026
|
getDefaultThermostatClusterServer(localTemperature = 23, occupiedHeatingSetpoint = 21, occupiedCoolingSetpoint = 25, minSetpointDeadBand = 1, minHeatSetpointLimit = 0, maxHeatSetpointLimit = 50, minCoolSetpointLimit = 0, maxCoolSetpointLimit = 50) {
|
|
1786
2027
|
return ClusterServer(ThermostatCluster.with(Thermostat.Feature.Heating, Thermostat.Feature.Cooling, Thermostat.Feature.AutoMode), {
|
|
1787
2028
|
localTemperature: localTemperature * 100,
|
|
2029
|
+
systemMode: Thermostat.SystemMode.Auto,
|
|
2030
|
+
controlSequenceOfOperation: Thermostat.ControlSequenceOfOperation.CoolingAndHeating,
|
|
2031
|
+
// Thermostat.Feature.Heating
|
|
1788
2032
|
occupiedHeatingSetpoint: occupiedHeatingSetpoint * 100,
|
|
1789
|
-
occupiedCoolingSetpoint: occupiedCoolingSetpoint * 100,
|
|
1790
2033
|
minHeatSetpointLimit: minHeatSetpointLimit * 100,
|
|
1791
2034
|
maxHeatSetpointLimit: maxHeatSetpointLimit * 100,
|
|
1792
2035
|
absMinHeatSetpointLimit: minHeatSetpointLimit * 100,
|
|
1793
2036
|
absMaxHeatSetpointLimit: maxHeatSetpointLimit * 100,
|
|
2037
|
+
// Thermostat.Feature.Cooling
|
|
2038
|
+
occupiedCoolingSetpoint: occupiedCoolingSetpoint * 100,
|
|
1794
2039
|
minCoolSetpointLimit: minCoolSetpointLimit * 100,
|
|
1795
2040
|
maxCoolSetpointLimit: maxCoolSetpointLimit * 100,
|
|
1796
2041
|
absMinCoolSetpointLimit: minCoolSetpointLimit * 100,
|
|
1797
2042
|
absMaxCoolSetpointLimit: maxCoolSetpointLimit * 100,
|
|
2043
|
+
// Thermostat.Feature.AutoMode
|
|
1798
2044
|
minSetpointDeadBand: minSetpointDeadBand * 100,
|
|
1799
|
-
systemMode: Thermostat.SystemMode.Off,
|
|
1800
|
-
controlSequenceOfOperation: Thermostat.ControlSequenceOfOperation.CoolingAndHeating,
|
|
1801
2045
|
thermostatRunningMode: Thermostat.ThermostatRunningMode.Off,
|
|
1802
2046
|
}, {
|
|
1803
2047
|
setpointRaiseLower: async (data) => {
|
|
@@ -1821,46 +2065,6 @@ export class MatterbridgeEndpoint extends Endpoint {
|
|
|
1821
2065
|
createDefaultThermostatClusterServer(localTemperature = 23, occupiedHeatingSetpoint = 21, occupiedCoolingSetpoint = 25, minSetpointDeadBand = 1, minHeatSetpointLimit = 0, maxHeatSetpointLimit = 50, minCoolSetpointLimit = 0, maxCoolSetpointLimit = 50) {
|
|
1822
2066
|
this.addClusterServer(this.getDefaultThermostatClusterServer(localTemperature, occupiedHeatingSetpoint, occupiedCoolingSetpoint, minSetpointDeadBand, minHeatSetpointLimit, maxHeatSetpointLimit, minCoolSetpointLimit, maxCoolSetpointLimit));
|
|
1823
2067
|
}
|
|
1824
|
-
/**
|
|
1825
|
-
* Get a default dummy time sync cluster server. Only needed to create a thermostat.
|
|
1826
|
-
*/
|
|
1827
|
-
getDefaultTimeSyncClusterServer() {
|
|
1828
|
-
return ClusterServer(TimeSynchronizationCluster.with(TimeSynchronization.Feature.TimeZone), {
|
|
1829
|
-
utcTime: null,
|
|
1830
|
-
granularity: TimeSynchronization.Granularity.NoTimeGranularity,
|
|
1831
|
-
timeZone: [{ offset: 0, validAt: 0 }],
|
|
1832
|
-
dstOffset: [],
|
|
1833
|
-
localTime: null,
|
|
1834
|
-
timeZoneDatabase: TimeSynchronization.TimeZoneDatabase.None,
|
|
1835
|
-
timeZoneListMaxSize: 1,
|
|
1836
|
-
dstOffsetListMaxSize: 1,
|
|
1837
|
-
}, {
|
|
1838
|
-
setTimeZone: async (data) => {
|
|
1839
|
-
this.log.debug('Matter command: setTimeZone', data.request);
|
|
1840
|
-
await this.commandHandler.executeHandler('setTimeZone', data);
|
|
1841
|
-
return { dstOffsetsRequired: false };
|
|
1842
|
-
},
|
|
1843
|
-
setDstOffset: async (data) => {
|
|
1844
|
-
this.log.debug('Matter command: setDstOffset', data.request);
|
|
1845
|
-
await this.commandHandler.executeHandler('setDstOffset', data);
|
|
1846
|
-
},
|
|
1847
|
-
setUtcTime: async (data) => {
|
|
1848
|
-
this.log.debug('Matter command: setUtcTime', data.request);
|
|
1849
|
-
await this.commandHandler.executeHandler('setUtcTime', data);
|
|
1850
|
-
},
|
|
1851
|
-
}, {
|
|
1852
|
-
dstTableEmpty: true,
|
|
1853
|
-
dstStatus: true,
|
|
1854
|
-
timeZoneStatus: true,
|
|
1855
|
-
timeFailure: true,
|
|
1856
|
-
});
|
|
1857
|
-
}
|
|
1858
|
-
/**
|
|
1859
|
-
* Creates a default dummy time sync cluster server. Only needed to create a thermostat.
|
|
1860
|
-
*/
|
|
1861
|
-
createDefaultTimeSyncClusterServer() {
|
|
1862
|
-
this.addClusterServer(this.getDefaultTimeSyncClusterServer());
|
|
1863
|
-
}
|
|
1864
2068
|
/**
|
|
1865
2069
|
* Returns the default SmokeCOAlarm Cluster Server.
|
|
1866
2070
|
*
|