matterbridge 2.1.0-dev.4 → 2.1.0-dev.6
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 +7 -7
- package/dist/frontend.js +12 -4
- package/dist/matter/behaviors.js +1 -0
- package/dist/matter/clusters.js +1 -0
- package/dist/matter/devices.js +1 -0
- package/dist/matter/endpoints.js +1 -0
- package/dist/matter/export.js +1 -4
- package/dist/matter/types.js +1 -0
- package/dist/matterbridgeEndpoint.js +29 -131
- package/dist/matterbridgeEndpointHelpers.js +199 -2
- package/npm-shrinkwrap.json +2 -2
- package/package.json +21 -1
package/CHANGELOG.md
CHANGED
|
@@ -16,12 +16,12 @@ Tamer (https://github.com/tammeryousef1006) has created the Matterbridge Discord
|
|
|
16
16
|
### Breaking Changes
|
|
17
17
|
|
|
18
18
|
Starting from v. 2.0.0 Matterbridge is running only in mode edge (no parameter needed and no badge in the frontend).
|
|
19
|
-
|
|
19
|
+
With this release v. 2.1.0, the legacy old api of matter.js have been completely removed from Matterbridge and from all plugins.
|
|
20
|
+
For this reason there is no compatibility for old versions of the plugins.
|
|
21
|
+
You need to update all plugins you use and Matterbridge in the same moment.
|
|
22
|
+
I suggest to first update all plugins without restarting and then to update Matterbridge so when it restarts, all versions will be the latest.
|
|
20
23
|
|
|
21
|
-
|
|
22
|
-
It is possible to change the mode (Classic, Dark or Light) in Settings, Matterbridge settings.
|
|
23
|
-
|
|
24
|
-
## [2.1.0.dev.4] - 2025-01-28
|
|
24
|
+
## [2.1.0.dev.6] - 2025-01-29
|
|
25
25
|
|
|
26
26
|
### Added
|
|
27
27
|
|
|
@@ -31,10 +31,10 @@ It is possible to change the mode (Classic, Dark or Light) in Settings, Matterbr
|
|
|
31
31
|
|
|
32
32
|
### Changed
|
|
33
33
|
|
|
34
|
+
- [package]: Removed legacy imports.
|
|
34
35
|
- [package]: Update dependencies.
|
|
35
|
-
- [package]: Update matter.js to 0.12.1.
|
|
36
36
|
- [package]: Update matter.js to 0.12.0.
|
|
37
|
-
- [package]:
|
|
37
|
+
- [package]: Update matter.js to 0.12.1.
|
|
38
38
|
|
|
39
39
|
### Fixed
|
|
40
40
|
|
package/dist/frontend.js
CHANGED
|
@@ -830,7 +830,7 @@ export class Frontend {
|
|
|
830
830
|
return '';
|
|
831
831
|
};
|
|
832
832
|
let attributes = '';
|
|
833
|
-
device.forEachAttribute((clusterName, attributeName, attributeValue) => {
|
|
833
|
+
device.forEachAttribute((clusterName, clusterId, attributeName, attributeId, attributeValue) => {
|
|
834
834
|
if (typeof attributeValue === 'undefined')
|
|
835
835
|
return;
|
|
836
836
|
if (clusterName === 'onOff' && attributeName === 'onOff')
|
|
@@ -881,12 +881,20 @@ export class Frontend {
|
|
|
881
881
|
attributes += `Speed: ${attributeValue} `;
|
|
882
882
|
if (clusterName === 'occupancySensing' && attributeName === 'occupancy' && isValidObject(attributeValue, 1))
|
|
883
883
|
attributes += `Occupancy: ${attributeValue.occupied} `;
|
|
884
|
-
if (clusterName === 'illuminanceMeasurement' && attributeName === 'measuredValue')
|
|
885
|
-
attributes += `Illuminance: ${attributeValue} `;
|
|
884
|
+
if (clusterName === 'illuminanceMeasurement' && attributeName === 'measuredValue' && isValidNumber(attributeValue))
|
|
885
|
+
attributes += `Illuminance: ${Math.round(Math.max(Math.pow(10, attributeValue / 10000), 0))} `;
|
|
886
886
|
if (clusterName === 'airQuality' && attributeName === 'airQuality')
|
|
887
887
|
attributes += `Air quality: ${attributeValue} `;
|
|
888
|
-
if (clusterName === '
|
|
888
|
+
if (clusterName === 'totalVolatileOrganicCompoundsConcentrationMeasurement' && attributeName === 'measuredValue')
|
|
889
889
|
attributes += `Voc: ${attributeValue} `;
|
|
890
|
+
if (clusterName === 'pm1ConcentrationMeasurement' && attributeName === 'measuredValue')
|
|
891
|
+
attributes += `Pm1: ${attributeValue} `;
|
|
892
|
+
if (clusterName === 'pm25ConcentrationMeasurement' && attributeName === 'measuredValue')
|
|
893
|
+
attributes += `Pm2.5: ${attributeValue} `;
|
|
894
|
+
if (clusterName === 'pm10ConcentrationMeasurement' && attributeName === 'measuredValue')
|
|
895
|
+
attributes += `Pm10: ${attributeValue} `;
|
|
896
|
+
if (clusterName === 'formaldehydeConcentrationMeasurement' && attributeName === 'measuredValue')
|
|
897
|
+
attributes += `CH₂O: ${attributeValue} `;
|
|
890
898
|
if (clusterName === 'temperatureMeasurement' && attributeName === 'measuredValue' && isValidNumber(attributeValue))
|
|
891
899
|
attributes += `Temperature: ${attributeValue / 100}°C `;
|
|
892
900
|
if (clusterName === 'relativeHumidityMeasurement' && attributeName === 'measuredValue' && isValidNumber(attributeValue))
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from '@matter/node/behaviors';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from '@matter/types/clusters';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from '@matter/node/devices';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { AggregatorEndpoint, ElectricalSensorEndpoint, PowerSourceEndpoint, BridgedNodeEndpoint, RootEndpoint, DeviceEnergyManagementEndpoint, OtaProviderEndpoint, OtaRequestorEndpoint } from '@matter/node/endpoints';
|
package/dist/matter/export.js
CHANGED
|
@@ -1,6 +1,3 @@
|
|
|
1
1
|
export * from '@matter/main';
|
|
2
|
-
export {
|
|
3
|
-
export * from '@matter/main/devices';
|
|
4
|
-
export * from '@matter/main/behaviors';
|
|
5
|
-
export { FabricAction, MdnsService, PaseClient, logEndpoint } from '@matter/main/protocol';
|
|
2
|
+
export { SemanticNamespace, ClosureTag, CompassDirectionTag, CompassLocationTag, DirectionTag, ElectricalMeasurementTag, LaundryTag, LevelTag, LocationTag, NumberTag, PositionTag, PowerSourceTag, RefrigeratorTag, RoomAirConditionerTag, SwitchesTag, } from '@matter/main';
|
|
6
3
|
export { AttributeElement, ClusterElement, ClusterModel, CommandElement, EventElement, FieldElement } from '@matter/main/model';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from '@matter/types';
|
|
@@ -1,14 +1,12 @@
|
|
|
1
1
|
import { AnsiLogger, BLUE, CYAN, YELLOW, db, debugStringify, er, hk, or, zb } from './logger/export.js';
|
|
2
2
|
import { bridgedNode } from './matterbridgeDeviceTypes.js';
|
|
3
|
-
import {
|
|
3
|
+
import { isValidNumber, isValidObject } from './utils/utils.js';
|
|
4
4
|
import { MatterbridgeBehavior, MatterbridgeBehaviorDevice, MatterbridgeIdentifyServer, MatterbridgeOnOffServer, MatterbridgeLevelControlServer, MatterbridgeColorControlServer, MatterbridgeWindowCoveringServer, MatterbridgeThermostatServer, MatterbridgeFanControlServer, MatterbridgeDoorLockServer, MatterbridgeModeSelectServer, MatterbridgeValveConfigurationAndControlServer, MatterbridgeSmokeCoAlarmServer, MatterbridgeBooleanStateConfigurationServer, } from './matterbridgeBehaviors.js';
|
|
5
|
-
import { addClusterServers, addOptionalClusterServers, addRequiredClusterServers, capitalizeFirstLetter, createUniqueId, getBehavior, getBehaviourTypesFromClusterClientIds, getBehaviourTypesFromClusterServerIds, lowercaseFirstLetter,
|
|
5
|
+
import { addClusterServers, addFixedLabel, addOptionalClusterServers, addRequiredClusterServers, addUserLabel, capitalizeFirstLetter, createUniqueId, getBehavior, getBehaviourTypesFromClusterClientIds, getBehaviourTypesFromClusterServerIds, getDefaultFlowMeasurementClusterServer, getDefaultIlluminanceMeasurementClusterServer, getDefaultPressureMeasurementClusterServer, getDefaultRelativeHumidityMeasurementClusterServer, getDefaultTemperatureMeasurementClusterServer, getDefaultOccupancySensingClusterServer, lowercaseFirstLetter, updateAttribute, getClusterId, getAttributeId, setAttribute, getAttribute, } from './matterbridgeEndpointHelpers.js';
|
|
6
6
|
import { Endpoint, Lifecycle, MutableEndpoint, NamedHandler, SupportedBehaviors, VendorId } from '@matter/main';
|
|
7
7
|
import { getClusterNameById, MeasurementType } from '@matter/main/types';
|
|
8
8
|
import { Descriptor } from '@matter/main/clusters/descriptor';
|
|
9
9
|
import { PowerSource } from '@matter/main/clusters/power-source';
|
|
10
|
-
import { UserLabel } from '@matter/main/clusters/user-label';
|
|
11
|
-
import { FixedLabel } from '@matter/main/clusters/fixed-label';
|
|
12
10
|
import { BridgedDeviceBasicInformation } from '@matter/main/clusters/bridged-device-basic-information';
|
|
13
11
|
import { Identify } from '@matter/main/clusters/identify';
|
|
14
12
|
import { OnOff } from '@matter/main/clusters/on-off';
|
|
@@ -26,13 +24,10 @@ import { BooleanStateConfiguration } from '@matter/main/clusters/boolean-state-c
|
|
|
26
24
|
import { PowerTopology } from '@matter/main/clusters/power-topology';
|
|
27
25
|
import { ElectricalPowerMeasurement } from '@matter/main/clusters/electrical-power-measurement';
|
|
28
26
|
import { ElectricalEnergyMeasurement } from '@matter/main/clusters/electrical-energy-measurement';
|
|
29
|
-
import { OccupancySensing } from '@matter/main/clusters/occupancy-sensing';
|
|
30
27
|
import { AirQuality } from '@matter/main/clusters/air-quality';
|
|
31
28
|
import { ConcentrationMeasurement } from '@matter/main/clusters/concentration-measurement';
|
|
32
29
|
import { DescriptorServer } from '@matter/main/behaviors/descriptor';
|
|
33
30
|
import { PowerSourceServer } from '@matter/main/behaviors/power-source';
|
|
34
|
-
import { UserLabelServer } from '@matter/main/behaviors/user-label';
|
|
35
|
-
import { FixedLabelServer } from '@matter/main/behaviors/fixed-label';
|
|
36
31
|
import { BridgedDeviceBasicInformationServer } from '@matter/main/behaviors/bridged-device-basic-information';
|
|
37
32
|
import { GroupsServer } from '@matter/main/behaviors/groups';
|
|
38
33
|
import { ScenesManagementServer } from '@matter/main/behaviors/scenes-management';
|
|
@@ -164,50 +159,14 @@ export class MatterbridgeEndpoint extends Endpoint {
|
|
|
164
159
|
return undefined;
|
|
165
160
|
return this.behaviors.optionsFor(behavior);
|
|
166
161
|
}
|
|
167
|
-
getAttribute(
|
|
168
|
-
|
|
169
|
-
if (this.construction.status !== Lifecycle.Status.Active) {
|
|
170
|
-
this.log.error(`getAttribute ${hk}${clusterName}.${attribute}${er} error: Endpoint ${or}${this.maybeId}${er}:${or}${this.maybeNumber}${er} is in the ${BLUE}${this.construction.status}${er} state`);
|
|
171
|
-
return undefined;
|
|
172
|
-
}
|
|
173
|
-
const state = this.state;
|
|
174
|
-
if (!(clusterName in state)) {
|
|
175
|
-
this.log.error(`getAttribute error: Cluster ${'0x' + clusterId.toString(16).padStart(4, '0')}:${clusterName} not found on endpoint ${or}${this.id}${er}:${or}${this.number}${er}`);
|
|
176
|
-
return undefined;
|
|
177
|
-
}
|
|
178
|
-
attribute = lowercaseFirstLetter(attribute);
|
|
179
|
-
if (!(attribute in state[clusterName])) {
|
|
180
|
-
this.log.error(`getAttribute error: Attribute ${hk}${attribute}${er} not found on Cluster ${'0x' + clusterId.toString(16).padStart(4, '0')}:${clusterName} on endpoint ${or}${this.id}${er}:${or}${this.number}${er}`);
|
|
181
|
-
return undefined;
|
|
182
|
-
}
|
|
183
|
-
const value = state[clusterName][attribute];
|
|
184
|
-
log?.info(`${db}Get endpoint ${or}${this.id}${db}:${or}${this.number}${db} attribute ${hk}${capitalizeFirstLetter(clusterName)}${db}.${hk}${attribute}${db} value ${YELLOW}${value !== null && typeof value === 'object' ? debugStringify(value) : value}${db}`);
|
|
185
|
-
return value;
|
|
162
|
+
getAttribute(cluster, attribute, log) {
|
|
163
|
+
return getAttribute(this, cluster, attribute, log);
|
|
186
164
|
}
|
|
187
165
|
async setAttribute(clusterId, attribute, value, log) {
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
}
|
|
193
|
-
const state = this.state;
|
|
194
|
-
if (!(clusterName in state)) {
|
|
195
|
-
this.log.error(`setAttribute ${hk}${attribute}${er} error: Cluster ${'0x' + clusterId.toString(16).padStart(4, '0')}:${clusterName} not found on endpoint ${or}${this.id}${er}:${or}${this.number}${er}`);
|
|
196
|
-
return false;
|
|
197
|
-
}
|
|
198
|
-
attribute = lowercaseFirstLetter(attribute);
|
|
199
|
-
if (!(attribute in state[clusterName])) {
|
|
200
|
-
this.log.error(`setAttribute error: Attribute ${hk}${attribute}${er} not found on Cluster ${'0x' + clusterId.toString(16).padStart(4, '0')}:${clusterName} on endpoint ${or}${this.id}${er}:${or}${this.number}${er}`);
|
|
201
|
-
return false;
|
|
202
|
-
}
|
|
203
|
-
let oldValue = state[clusterName][attribute];
|
|
204
|
-
if (typeof oldValue === 'object')
|
|
205
|
-
oldValue = deepCopy(oldValue);
|
|
206
|
-
await this.setStateOf(this.behaviors.supported[clusterName], { [attribute]: value });
|
|
207
|
-
log?.info(`${db}Set endpoint ${or}${this.id}${db}:${or}${this.number}${db} attribute ${hk}${capitalizeFirstLetter(clusterName)}${db}.${hk}${attribute}${db} ` +
|
|
208
|
-
`from ${YELLOW}${oldValue !== null && typeof oldValue === 'object' ? debugStringify(oldValue) : oldValue}${db} ` +
|
|
209
|
-
`to ${YELLOW}${value !== null && typeof value === 'object' ? debugStringify(value) : value}${db}`);
|
|
210
|
-
return true;
|
|
166
|
+
return await setAttribute(this, clusterId, attribute, value, log);
|
|
167
|
+
}
|
|
168
|
+
async updateAttribute(cluster, attribute, value, log) {
|
|
169
|
+
return await updateAttribute(this, cluster, attribute, value, log);
|
|
211
170
|
}
|
|
212
171
|
async subscribeAttribute(clusterId, attribute, listener, log) {
|
|
213
172
|
const clusterName = lowercaseFirstLetter(getClusterNameById(clusterId));
|
|
@@ -247,31 +206,11 @@ export class MatterbridgeEndpoint extends Endpoint {
|
|
|
247
206
|
addClusterServers(this, serverList);
|
|
248
207
|
}
|
|
249
208
|
async addFixedLabel(label, value) {
|
|
250
|
-
|
|
251
|
-
this.log.debug(`addFixedLabel: add cluster ${hk}FixedLabel${db}:${hk}fixedLabel${db} with label ${CYAN}${label}${db} value ${CYAN}${value}${db}`);
|
|
252
|
-
this.behaviors.require(FixedLabelServer, {
|
|
253
|
-
labelList: [{ label, value }],
|
|
254
|
-
});
|
|
255
|
-
return this;
|
|
256
|
-
}
|
|
257
|
-
this.log.debug(`addFixedLabel: add label ${CYAN}${label}${db} value ${CYAN}${value}${db}`);
|
|
258
|
-
const labelList = (this.getAttribute(FixedLabel.Cluster.id, 'labelList', this.log) ?? []).filter((entryLabel) => entryLabel.label !== label);
|
|
259
|
-
labelList.push({ label, value });
|
|
260
|
-
await this.setAttribute(FixedLabel.Cluster.id, 'labelList', labelList, this.log);
|
|
209
|
+
await addFixedLabel(this, label, value);
|
|
261
210
|
return this;
|
|
262
211
|
}
|
|
263
212
|
async addUserLabel(label, value) {
|
|
264
|
-
|
|
265
|
-
this.log.debug(`addUserLabel: add cluster ${hk}UserLabel${db}:${hk}userLabel${db} with label ${CYAN}${label}${db} value ${CYAN}${value}${db}`);
|
|
266
|
-
this.behaviors.require(UserLabelServer, {
|
|
267
|
-
labelList: [{ label, value }],
|
|
268
|
-
});
|
|
269
|
-
return this;
|
|
270
|
-
}
|
|
271
|
-
this.log.debug(`addUserLabel: add label ${CYAN}${label}${db} value ${CYAN}${value}${db}`);
|
|
272
|
-
const labelList = (this.getAttribute(UserLabel.Cluster.id, 'labelList', this.log) ?? []).filter((entryLabel) => entryLabel.label !== label);
|
|
273
|
-
labelList.push({ label, value });
|
|
274
|
-
await this.setAttribute(UserLabel.Cluster.id, 'labelList', labelList, this.log);
|
|
213
|
+
await addUserLabel(this, label, value);
|
|
275
214
|
return this;
|
|
276
215
|
}
|
|
277
216
|
addCommandHandler(command, handler) {
|
|
@@ -298,9 +237,15 @@ export class MatterbridgeEndpoint extends Endpoint {
|
|
|
298
237
|
forEachAttribute(callback) {
|
|
299
238
|
for (const [clusterName, clusterAttributes] of Object.entries(this.state)) {
|
|
300
239
|
for (const [attributeName, attributeValue] of Object.entries(clusterAttributes)) {
|
|
301
|
-
|
|
240
|
+
const clusterId = getClusterId(this, clusterName);
|
|
241
|
+
if (clusterId === undefined) {
|
|
242
|
+
continue;
|
|
243
|
+
}
|
|
244
|
+
const attributeId = getAttributeId(this, clusterName, attributeName);
|
|
245
|
+
if (attributeId === undefined) {
|
|
302
246
|
continue;
|
|
303
|
-
|
|
247
|
+
}
|
|
248
|
+
callback(clusterName, clusterId, attributeName, attributeId, attributeValue);
|
|
304
249
|
}
|
|
305
250
|
}
|
|
306
251
|
}
|
|
@@ -694,7 +639,6 @@ export class MatterbridgeEndpoint extends Endpoint {
|
|
|
694
639
|
await this.setAttribute(ColorControl.Cluster.id, 'colorMode', colorMode, this.log);
|
|
695
640
|
await this.setAttribute(ColorControl.Cluster.id, 'enhancedColorMode', colorMode, this.log);
|
|
696
641
|
}
|
|
697
|
-
return this;
|
|
698
642
|
}
|
|
699
643
|
createDefaultWindowCoveringClusterServer(positionPercent100ths) {
|
|
700
644
|
this.behaviors.require(MatterbridgeWindowCoveringServer.with(WindowCovering.Feature.Lift, WindowCovering.Feature.PositionAwareLift), {
|
|
@@ -718,7 +662,7 @@ export class MatterbridgeEndpoint extends Endpoint {
|
|
|
718
662
|
}
|
|
719
663
|
async setWindowCoveringTargetAsCurrentAndStopped() {
|
|
720
664
|
const position = this.getAttribute(WindowCovering.Cluster.id, 'currentPositionLiftPercent100ths', this.log);
|
|
721
|
-
if (position
|
|
665
|
+
if (isValidNumber(position, 0, 10000)) {
|
|
722
666
|
await this.setAttribute(WindowCovering.Cluster.id, 'targetPositionLiftPercent100ths', position, this.log);
|
|
723
667
|
await this.setAttribute(WindowCovering.Cluster.id, 'operationalStatus', {
|
|
724
668
|
global: WindowCovering.MovementStatus.Stopped,
|
|
@@ -748,8 +692,10 @@ export class MatterbridgeEndpoint extends Endpoint {
|
|
|
748
692
|
}
|
|
749
693
|
getWindowCoveringStatus() {
|
|
750
694
|
const status = this.getAttribute(WindowCovering.Cluster.id, 'operationalStatus', this.log);
|
|
751
|
-
|
|
752
|
-
|
|
695
|
+
if (isValidObject(status, 3) && 'global' in status && typeof status.global === 'number') {
|
|
696
|
+
this.log.debug(`Get WindowCovering operationalStatus: ${status.global}`);
|
|
697
|
+
return status.global;
|
|
698
|
+
}
|
|
753
699
|
}
|
|
754
700
|
async setWindowCoveringTargetAndCurrentPosition(position) {
|
|
755
701
|
await this.setAttribute(WindowCovering.Cluster.id, 'currentPositionLiftPercent100ths', position, this.log);
|
|
@@ -1037,76 +983,28 @@ export class MatterbridgeEndpoint extends Endpoint {
|
|
|
1037
983
|
});
|
|
1038
984
|
return this;
|
|
1039
985
|
}
|
|
1040
|
-
getDefaultTemperatureMeasurementClusterServer(measuredValue = 0) {
|
|
1041
|
-
return optionsFor(TemperatureMeasurementServer, {
|
|
1042
|
-
measuredValue,
|
|
1043
|
-
minMeasuredValue: null,
|
|
1044
|
-
maxMeasuredValue: null,
|
|
1045
|
-
tolerance: 0,
|
|
1046
|
-
});
|
|
1047
|
-
}
|
|
1048
986
|
createDefaultTemperatureMeasurementClusterServer(measuredValue = 0) {
|
|
1049
|
-
this.behaviors.require(TemperatureMeasurementServer,
|
|
987
|
+
this.behaviors.require(TemperatureMeasurementServer, getDefaultTemperatureMeasurementClusterServer(measuredValue));
|
|
1050
988
|
return this;
|
|
1051
989
|
}
|
|
1052
|
-
getDefaultRelativeHumidityMeasurementClusterServer(measuredValue = 0) {
|
|
1053
|
-
return optionsFor(RelativeHumidityMeasurementServer, {
|
|
1054
|
-
measuredValue,
|
|
1055
|
-
minMeasuredValue: null,
|
|
1056
|
-
maxMeasuredValue: null,
|
|
1057
|
-
tolerance: 0,
|
|
1058
|
-
});
|
|
1059
|
-
}
|
|
1060
990
|
createDefaultRelativeHumidityMeasurementClusterServer(measuredValue = 0) {
|
|
1061
|
-
this.behaviors.require(RelativeHumidityMeasurementServer,
|
|
991
|
+
this.behaviors.require(RelativeHumidityMeasurementServer, getDefaultRelativeHumidityMeasurementClusterServer(measuredValue));
|
|
1062
992
|
return this;
|
|
1063
993
|
}
|
|
1064
|
-
getDefaultPressureMeasurementClusterServer(measuredValue = 1000) {
|
|
1065
|
-
return optionsFor(PressureMeasurementServer, {
|
|
1066
|
-
measuredValue,
|
|
1067
|
-
minMeasuredValue: null,
|
|
1068
|
-
maxMeasuredValue: null,
|
|
1069
|
-
tolerance: 0,
|
|
1070
|
-
});
|
|
1071
|
-
}
|
|
1072
994
|
createDefaultPressureMeasurementClusterServer(measuredValue = 1000) {
|
|
1073
|
-
this.behaviors.require(PressureMeasurementServer,
|
|
995
|
+
this.behaviors.require(PressureMeasurementServer, getDefaultPressureMeasurementClusterServer(measuredValue));
|
|
1074
996
|
return this;
|
|
1075
997
|
}
|
|
1076
|
-
getDefaultIlluminanceMeasurementClusterServer(measuredValue = 0) {
|
|
1077
|
-
return optionsFor(IlluminanceMeasurementServer, {
|
|
1078
|
-
measuredValue,
|
|
1079
|
-
minMeasuredValue: null,
|
|
1080
|
-
maxMeasuredValue: null,
|
|
1081
|
-
tolerance: 0,
|
|
1082
|
-
});
|
|
1083
|
-
}
|
|
1084
998
|
createDefaultIlluminanceMeasurementClusterServer(measuredValue = 0) {
|
|
1085
|
-
this.behaviors.require(IlluminanceMeasurementServer,
|
|
999
|
+
this.behaviors.require(IlluminanceMeasurementServer, getDefaultIlluminanceMeasurementClusterServer(measuredValue));
|
|
1086
1000
|
return this;
|
|
1087
1001
|
}
|
|
1088
|
-
getDefaultFlowMeasurementClusterServer(measuredValue = 0) {
|
|
1089
|
-
return optionsFor(FlowMeasurementServer, {
|
|
1090
|
-
measuredValue,
|
|
1091
|
-
minMeasuredValue: null,
|
|
1092
|
-
maxMeasuredValue: null,
|
|
1093
|
-
tolerance: 0,
|
|
1094
|
-
});
|
|
1095
|
-
}
|
|
1096
1002
|
createDefaultFlowMeasurementClusterServer(measuredValue = 0) {
|
|
1097
|
-
this.behaviors.require(FlowMeasurementServer,
|
|
1003
|
+
this.behaviors.require(FlowMeasurementServer, getDefaultFlowMeasurementClusterServer(measuredValue));
|
|
1098
1004
|
return this;
|
|
1099
1005
|
}
|
|
1100
|
-
getDefaultOccupancySensingClusterServer(occupied = false) {
|
|
1101
|
-
return optionsFor(OccupancySensingServer, {
|
|
1102
|
-
occupancy: { occupied },
|
|
1103
|
-
occupancySensorType: OccupancySensing.OccupancySensorType.Pir,
|
|
1104
|
-
occupancySensorTypeBitmap: { pir: true, ultrasonic: false, physicalContact: false },
|
|
1105
|
-
pirOccupiedToUnoccupiedDelay: 30,
|
|
1106
|
-
});
|
|
1107
|
-
}
|
|
1108
1006
|
createDefaultOccupancySensingClusterServer(occupied = false) {
|
|
1109
|
-
this.behaviors.require(OccupancySensingServer,
|
|
1007
|
+
this.behaviors.require(OccupancySensingServer, getDefaultOccupancySensingClusterServer(occupied));
|
|
1110
1008
|
return this;
|
|
1111
1009
|
}
|
|
1112
1010
|
createDefaultAirQualityClusterServer(airQuality = AirQuality.AirQualityEnum.Unknown) {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { createHash } from 'crypto';
|
|
2
|
-
import { CYAN, db, hk, zb } from './logger/export.js';
|
|
2
|
+
import { BLUE, CYAN, db, debugStringify, er, hk, or, YELLOW, zb } from './logger/export.js';
|
|
3
3
|
import { MatterbridgeIdentifyServer, MatterbridgeOnOffServer, MatterbridgeLevelControlServer, MatterbridgeColorControlServer, MatterbridgeWindowCoveringServer, MatterbridgeThermostatServer, MatterbridgeFanControlServer, MatterbridgeDoorLockServer, MatterbridgeModeSelectServer, MatterbridgeValveConfigurationAndControlServer, MatterbridgeSmokeCoAlarmServer, MatterbridgeBooleanStateConfigurationServer, } from './matterbridgeBehaviors.js';
|
|
4
|
+
import { Lifecycle } from '@matter/main';
|
|
4
5
|
import { getClusterNameById } from '@matter/main/types';
|
|
5
6
|
import { PowerSource } from '@matter/main/clusters/power-source';
|
|
6
7
|
import { UserLabel } from '@matter/main/clusters/user-label';
|
|
@@ -72,6 +73,7 @@ import { Pm25ConcentrationMeasurementServer } from '@matter/main/behaviors/pm25-
|
|
|
72
73
|
import { Pm10ConcentrationMeasurementServer } from '@matter/main/behaviors/pm10-concentration-measurement';
|
|
73
74
|
import { RadonConcentrationMeasurementServer } from '@matter/main/behaviors/radon-concentration-measurement';
|
|
74
75
|
import { TotalVolatileOrganicCompoundsConcentrationMeasurementServer } from '@matter/main/behaviors/total-volatile-organic-compounds-concentration-measurement';
|
|
76
|
+
import { deepCopy, deepEqual, isValidArray } from './utils/utils.js';
|
|
75
77
|
export function capitalizeFirstLetter(name) {
|
|
76
78
|
if (!name)
|
|
77
79
|
return name;
|
|
@@ -305,6 +307,38 @@ export function addClusterServers(endpoint, serverList) {
|
|
|
305
307
|
if (serverList.includes(TotalVolatileOrganicCompoundsConcentrationMeasurement.Cluster.id))
|
|
306
308
|
endpoint.createDefaultTvocMeasurementClusterServer();
|
|
307
309
|
}
|
|
310
|
+
export async function addFixedLabel(endpoint, label, value) {
|
|
311
|
+
if (!endpoint.hasClusterServer(FixedLabel.Cluster.id)) {
|
|
312
|
+
endpoint.log.debug(`addFixedLabel: add cluster ${hk}FixedLabel${db}:${hk}fixedLabel${db} with label ${CYAN}${label}${db} value ${CYAN}${value}${db}`);
|
|
313
|
+
endpoint.behaviors.require(FixedLabelServer, {
|
|
314
|
+
labelList: [{ label, value }],
|
|
315
|
+
});
|
|
316
|
+
return;
|
|
317
|
+
}
|
|
318
|
+
endpoint.log.debug(`addFixedLabel: add label ${CYAN}${label}${db} value ${CYAN}${value}${db}`);
|
|
319
|
+
let labelList = endpoint.getAttribute(FixedLabel.Cluster.id, 'labelList', endpoint.log);
|
|
320
|
+
if (isValidArray(labelList)) {
|
|
321
|
+
labelList = labelList.filter((entry) => entry.label !== label);
|
|
322
|
+
labelList.push({ label, value });
|
|
323
|
+
await endpoint.setAttribute(FixedLabel.Cluster.id, 'labelList', labelList, endpoint.log);
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
export async function addUserLabel(endpoint, label, value) {
|
|
327
|
+
if (!endpoint.hasClusterServer(UserLabel.Cluster.id)) {
|
|
328
|
+
endpoint.log.debug(`addUserLabel: add cluster ${hk}UserLabel${db}:${hk}userLabel${db} with label ${CYAN}${label}${db} value ${CYAN}${value}${db}`);
|
|
329
|
+
endpoint.behaviors.require(UserLabelServer, {
|
|
330
|
+
labelList: [{ label, value }],
|
|
331
|
+
});
|
|
332
|
+
return;
|
|
333
|
+
}
|
|
334
|
+
endpoint.log.debug(`addUserLabel: add label ${CYAN}${label}${db} value ${CYAN}${value}${db}`);
|
|
335
|
+
let labelList = endpoint.getAttribute(UserLabel.Cluster.id, 'labelList', endpoint.log);
|
|
336
|
+
if (isValidArray(labelList)) {
|
|
337
|
+
labelList = labelList.filter((entry) => entry.label !== label);
|
|
338
|
+
labelList.push({ label, value });
|
|
339
|
+
await endpoint.setAttribute(UserLabel.Cluster.id, 'labelList', labelList, endpoint.log);
|
|
340
|
+
}
|
|
341
|
+
}
|
|
308
342
|
export function optionsFor(type, options) {
|
|
309
343
|
return options;
|
|
310
344
|
}
|
|
@@ -312,5 +346,168 @@ export function getClusterId(endpoint, cluster) {
|
|
|
312
346
|
return endpoint.behaviors.supported[lowercaseFirstLetter(cluster)]?.schema?.id;
|
|
313
347
|
}
|
|
314
348
|
export function getAttributeId(endpoint, cluster, attribute) {
|
|
315
|
-
|
|
349
|
+
if (attribute === 'attributeList')
|
|
350
|
+
return 0xfffb;
|
|
351
|
+
else if (attribute === 'featureMap')
|
|
352
|
+
return 0xfffc;
|
|
353
|
+
else if (attribute === 'eventList')
|
|
354
|
+
return 0xfffa;
|
|
355
|
+
else if (attribute === 'generatedCommandList')
|
|
356
|
+
return 0xfff8;
|
|
357
|
+
else if (attribute === 'acceptedCommandList')
|
|
358
|
+
return 0xfff9;
|
|
359
|
+
else if (attribute === 'clusterRevision')
|
|
360
|
+
return 0xfffd;
|
|
361
|
+
else {
|
|
362
|
+
if (endpoint.behaviors.supported[lowercaseFirstLetter(cluster)]?.schema?.type === 'ConcentrationMeasurement') {
|
|
363
|
+
if (attribute === 'measuredValue')
|
|
364
|
+
return 0x0;
|
|
365
|
+
else if (attribute === 'minMeasuredValue')
|
|
366
|
+
return 0x1;
|
|
367
|
+
else if (attribute === 'maxMeasuredValue')
|
|
368
|
+
return 0x2;
|
|
369
|
+
else if (attribute === 'peakMeasuredValue')
|
|
370
|
+
return 0x3;
|
|
371
|
+
else if (attribute === 'peakMeasuredValueWindow')
|
|
372
|
+
return 0x4;
|
|
373
|
+
else if (attribute === 'averageMeasuredValue')
|
|
374
|
+
return 0x5;
|
|
375
|
+
else if (attribute === 'averageMeasuredValueWindow')
|
|
376
|
+
return 0x6;
|
|
377
|
+
else if (attribute === 'uncertainty')
|
|
378
|
+
return 0x7;
|
|
379
|
+
else if (attribute === 'measurementUnit')
|
|
380
|
+
return 0x8;
|
|
381
|
+
else if (attribute === 'measurementMedium')
|
|
382
|
+
return 0x9;
|
|
383
|
+
else if (attribute === 'levelValue')
|
|
384
|
+
return 0xa;
|
|
385
|
+
}
|
|
386
|
+
return endpoint.behaviors.supported[lowercaseFirstLetter(cluster)]?.schema?.children?.find((child) => child.name === capitalizeFirstLetter(attribute))?.id;
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
export function getAttribute(endpoint, cluster, attribute, log) {
|
|
390
|
+
const clusterName = getBehavior(endpoint, cluster)?.id;
|
|
391
|
+
if (!clusterName) {
|
|
392
|
+
endpoint.log.error(`getAttribute ${hk}${attribute}${er} error: cluster not found on endpoint ${or}${endpoint.maybeId}${er}:${or}${endpoint.maybeNumber}${er}`);
|
|
393
|
+
return undefined;
|
|
394
|
+
}
|
|
395
|
+
if (endpoint.construction.status !== Lifecycle.Status.Active) {
|
|
396
|
+
endpoint.log.error(`getAttribute ${hk}${clusterName}.${attribute}${er} error: Endpoint ${or}${endpoint.maybeId}${er}:${or}${endpoint.maybeNumber}${er} is in the ${BLUE}${endpoint.construction.status}${er} state`);
|
|
397
|
+
return undefined;
|
|
398
|
+
}
|
|
399
|
+
const state = endpoint.state;
|
|
400
|
+
attribute = lowercaseFirstLetter(attribute);
|
|
401
|
+
if (!(attribute in state[clusterName])) {
|
|
402
|
+
endpoint.log.error(`getAttribute error: Attribute ${hk}${attribute}${er} not found on Cluster ${'0x' + getClusterId(endpoint, clusterName)?.toString(16).padStart(4, '0')}:${clusterName} on endpoint ${or}${endpoint.id}${er}:${or}${endpoint.number}${er}`);
|
|
403
|
+
return undefined;
|
|
404
|
+
}
|
|
405
|
+
let value = state[clusterName][attribute];
|
|
406
|
+
if (typeof value === 'object')
|
|
407
|
+
value = deepCopy(value);
|
|
408
|
+
log?.info(`${db}Get endpoint ${or}${endpoint.id}${db}:${or}${endpoint.number}${db} attribute ${hk}${capitalizeFirstLetter(clusterName)}${db}.${hk}${attribute}${db} value ${YELLOW}${value !== null && typeof value === 'object' ? debugStringify(value) : value}${db}`);
|
|
409
|
+
return value;
|
|
410
|
+
}
|
|
411
|
+
export async function setAttribute(endpoint, cluster, attribute, value, log) {
|
|
412
|
+
const clusterName = getBehavior(endpoint, cluster)?.id;
|
|
413
|
+
if (!clusterName) {
|
|
414
|
+
endpoint.log.error(`setAttribute ${hk}${attribute}${er} error: cluster not found on endpoint ${or}${endpoint.maybeId}${er}:${or}${endpoint.maybeNumber}${er}`);
|
|
415
|
+
return false;
|
|
416
|
+
}
|
|
417
|
+
if (endpoint.construction.status !== Lifecycle.Status.Active) {
|
|
418
|
+
endpoint.log.error(`setAttribute ${hk}${clusterName}.${attribute}${er} error: Endpoint ${or}${endpoint.maybeId}${er}:${or}${endpoint.maybeNumber}${er} is in the ${BLUE}${endpoint.construction.status}${er} state`);
|
|
419
|
+
return false;
|
|
420
|
+
}
|
|
421
|
+
const state = endpoint.state;
|
|
422
|
+
attribute = lowercaseFirstLetter(attribute);
|
|
423
|
+
if (!(attribute in state[clusterName])) {
|
|
424
|
+
endpoint.log.error(`setAttribute error: Attribute ${hk}${attribute}${er} not found on cluster ${'0x' + getClusterId(endpoint, clusterName)?.toString(16).padStart(4, '0')}:${clusterName} on endpoint ${or}${endpoint.id}${er}:${or}${endpoint.number}${er}`);
|
|
425
|
+
return false;
|
|
426
|
+
}
|
|
427
|
+
let oldValue = state[clusterName][attribute];
|
|
428
|
+
if (typeof oldValue === 'object')
|
|
429
|
+
oldValue = deepCopy(oldValue);
|
|
430
|
+
await endpoint.setStateOf(endpoint.behaviors.supported[clusterName], { [attribute]: value });
|
|
431
|
+
log?.info(`${db}Set endpoint ${or}${endpoint.id}${db}:${or}${endpoint.number}${db} attribute ${hk}${capitalizeFirstLetter(clusterName)}${db}.${hk}${attribute}${db} ` +
|
|
432
|
+
`from ${YELLOW}${oldValue !== null && typeof oldValue === 'object' ? debugStringify(oldValue) : oldValue}${db} ` +
|
|
433
|
+
`to ${YELLOW}${value !== null && typeof value === 'object' ? debugStringify(value) : value}${db}`);
|
|
434
|
+
return true;
|
|
435
|
+
}
|
|
436
|
+
export async function updateAttribute(endpoint, cluster, attribute, value, log) {
|
|
437
|
+
const clusterName = getBehavior(endpoint, cluster)?.id;
|
|
438
|
+
if (!clusterName) {
|
|
439
|
+
endpoint.log.error(`updateAttribute ${hk}${attribute}${er} error: cluster not found on endpoint ${or}${endpoint.maybeId}${er}:${or}${endpoint.maybeNumber}${er}`);
|
|
440
|
+
return false;
|
|
441
|
+
}
|
|
442
|
+
if (endpoint.construction.status !== Lifecycle.Status.Active) {
|
|
443
|
+
endpoint.log.error(`updateAttribute ${hk}${clusterName}.${attribute}${er} error: Endpoint ${or}${endpoint.maybeId}${er}:${or}${endpoint.maybeNumber}${er} is in the ${BLUE}${endpoint.construction.status}${er} state`);
|
|
444
|
+
return false;
|
|
445
|
+
}
|
|
446
|
+
const state = endpoint.state;
|
|
447
|
+
attribute = lowercaseFirstLetter(attribute);
|
|
448
|
+
if (!(attribute in state[clusterName])) {
|
|
449
|
+
endpoint.log.error(`updateAttribute error: Attribute ${hk}${attribute}${er} not found on cluster ${'0x' + getClusterId(endpoint, clusterName)?.toString(16).padStart(4, '0')}:${clusterName} on endpoint ${or}${endpoint.id}${er}:${or}${endpoint.number}${er}`);
|
|
450
|
+
return false;
|
|
451
|
+
}
|
|
452
|
+
let oldValue = state[clusterName][attribute];
|
|
453
|
+
if (typeof oldValue === 'object') {
|
|
454
|
+
if (deepEqual(oldValue, value))
|
|
455
|
+
return false;
|
|
456
|
+
oldValue = deepCopy(oldValue);
|
|
457
|
+
}
|
|
458
|
+
else if (oldValue === value)
|
|
459
|
+
return false;
|
|
460
|
+
await endpoint.setStateOf(endpoint.behaviors.supported[clusterName], { [attribute]: value });
|
|
461
|
+
log?.info(`${db}Update endpoint ${or}${endpoint.id}${db}:${or}${endpoint.number}${db} attribute ${hk}${capitalizeFirstLetter(clusterName)}${db}.${hk}${attribute}${db} ` +
|
|
462
|
+
`from ${YELLOW}${oldValue !== null && typeof oldValue === 'object' ? debugStringify(oldValue) : oldValue}${db} ` +
|
|
463
|
+
`to ${YELLOW}${value !== null && typeof value === 'object' ? debugStringify(value) : value}${db}`);
|
|
464
|
+
return true;
|
|
465
|
+
}
|
|
466
|
+
export function getDefaultTemperatureMeasurementClusterServer(measuredValue = 0) {
|
|
467
|
+
return optionsFor(TemperatureMeasurementServer, {
|
|
468
|
+
measuredValue,
|
|
469
|
+
minMeasuredValue: null,
|
|
470
|
+
maxMeasuredValue: null,
|
|
471
|
+
tolerance: 0,
|
|
472
|
+
});
|
|
473
|
+
}
|
|
474
|
+
export function getDefaultRelativeHumidityMeasurementClusterServer(measuredValue = 0) {
|
|
475
|
+
return optionsFor(RelativeHumidityMeasurementServer, {
|
|
476
|
+
measuredValue,
|
|
477
|
+
minMeasuredValue: null,
|
|
478
|
+
maxMeasuredValue: null,
|
|
479
|
+
tolerance: 0,
|
|
480
|
+
});
|
|
481
|
+
}
|
|
482
|
+
export function getDefaultPressureMeasurementClusterServer(measuredValue = 1000) {
|
|
483
|
+
return optionsFor(PressureMeasurementServer, {
|
|
484
|
+
measuredValue,
|
|
485
|
+
minMeasuredValue: null,
|
|
486
|
+
maxMeasuredValue: null,
|
|
487
|
+
tolerance: 0,
|
|
488
|
+
});
|
|
489
|
+
}
|
|
490
|
+
export function getDefaultIlluminanceMeasurementClusterServer(measuredValue = 0) {
|
|
491
|
+
return optionsFor(IlluminanceMeasurementServer, {
|
|
492
|
+
measuredValue,
|
|
493
|
+
minMeasuredValue: null,
|
|
494
|
+
maxMeasuredValue: null,
|
|
495
|
+
tolerance: 0,
|
|
496
|
+
});
|
|
497
|
+
}
|
|
498
|
+
export function getDefaultFlowMeasurementClusterServer(measuredValue = 0) {
|
|
499
|
+
return optionsFor(FlowMeasurementServer, {
|
|
500
|
+
measuredValue,
|
|
501
|
+
minMeasuredValue: null,
|
|
502
|
+
maxMeasuredValue: null,
|
|
503
|
+
tolerance: 0,
|
|
504
|
+
});
|
|
505
|
+
}
|
|
506
|
+
export function getDefaultOccupancySensingClusterServer(occupied = false) {
|
|
507
|
+
return optionsFor(OccupancySensingServer, {
|
|
508
|
+
occupancy: { occupied },
|
|
509
|
+
occupancySensorType: OccupancySensing.OccupancySensorType.Pir,
|
|
510
|
+
occupancySensorTypeBitmap: { pir: true, ultrasonic: false, physicalContact: false },
|
|
511
|
+
pirOccupiedToUnoccupiedDelay: 30,
|
|
512
|
+
});
|
|
316
513
|
}
|
package/npm-shrinkwrap.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "matterbridge",
|
|
3
|
-
"version": "2.1.0-dev.
|
|
3
|
+
"version": "2.1.0-dev.6",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "matterbridge",
|
|
9
|
-
"version": "2.1.0-dev.
|
|
9
|
+
"version": "2.1.0-dev.6",
|
|
10
10
|
"license": "Apache-2.0",
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"@matter/main": "0.12.1",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "matterbridge",
|
|
3
|
-
"version": "2.1.0-dev.
|
|
3
|
+
"version": "2.1.0-dev.6",
|
|
4
4
|
"description": "Matterbridge plugin manager for Matter",
|
|
5
5
|
"author": "https://github.com/Luligu",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -56,6 +56,26 @@
|
|
|
56
56
|
"import": "./dist/matter/export.js",
|
|
57
57
|
"types": "./dist/matter/export.d.ts"
|
|
58
58
|
},
|
|
59
|
+
"./matter/devices": {
|
|
60
|
+
"import": "./dist/matter/devices.js",
|
|
61
|
+
"types": "./dist/matter/devices.d.ts"
|
|
62
|
+
},
|
|
63
|
+
"./matter/clusters": {
|
|
64
|
+
"import": "./dist/matter/clusters.js",
|
|
65
|
+
"types": "./dist/matter/clusters.d.ts"
|
|
66
|
+
},
|
|
67
|
+
"./matter/behaviors": {
|
|
68
|
+
"import": "./dist/matter/behaviors.js",
|
|
69
|
+
"types": "./dist/matter/behaviors.d.ts"
|
|
70
|
+
},
|
|
71
|
+
"./matter/endpoints": {
|
|
72
|
+
"import": "./dist/matter/endpoints.js",
|
|
73
|
+
"types": "./dist/matter/endpoints.d.ts"
|
|
74
|
+
},
|
|
75
|
+
"./matter/types": {
|
|
76
|
+
"import": "./dist/matter/types.js",
|
|
77
|
+
"types": "./dist/matter/types.d.ts"
|
|
78
|
+
},
|
|
59
79
|
"./cluster": {
|
|
60
80
|
"import": "./dist/cluster/export.js",
|
|
61
81
|
"types": "./dist/cluster/export.d.ts"
|