matterbridge-example-dynamic-platform 1.3.9 → 1.3.10

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 CHANGED
@@ -23,6 +23,23 @@ If you like this project and find it useful, please consider giving it a star on
23
23
  <img src="bmc-button.svg" alt="Buy me a coffee" width="120">
24
24
  </a>
25
25
 
26
+ ## [1.3.10] - 2025-09-06
27
+
28
+ ### Added
29
+
30
+ - [platform]: Added AirConditioner device class from Matterbridge.
31
+ - [platform]: Added a Speaker device type.
32
+ - [platform]: Added estimatedEndTime to Rvc animation.
33
+
34
+ ### Changed
35
+
36
+ - [package]: Updated dependencies.
37
+ - [package]: Required matterbridge 3.2.6.
38
+
39
+ <a href="https://www.buymeacoffee.com/luligugithub">
40
+ <img src="bmc-button.svg" alt="Buy me a coffee" width="80">
41
+ </a>
42
+
26
43
  ## [1.3.9] - 2025-08-30
27
44
 
28
45
  ### Added
package/README.md CHANGED
@@ -76,6 +76,7 @@ It exposes 56 virtual devices:
76
76
  - a solar power device
77
77
  - a battery storage device
78
78
  - a heat pump device
79
+ - a speaker device (supported by SmartThings)
79
80
 
80
81
  All these devices continuously change state and position. The plugin also shows how to use all the command handlers (you can control all the devices), how to subscribe to attributes and how to trigger events.
81
82
 
package/dist/platform.js CHANGED
@@ -1,5 +1,5 @@
1
- import { MatterbridgeEndpoint, MatterbridgeDynamicPlatform, airQualitySensor, bridgedNode, colorTemperatureLight, coverDevice, dimmableLight, doorLockDevice, fanDevice, flowSensor, humiditySensor, onOffLight, onOffOutlet, onOffSwitch, powerSource, rainSensor, smokeCoAlarm, temperatureSensor, thermostatDevice, waterFreezeDetector, waterLeakDetector, airPurifier, pumpDevice, waterValve, genericSwitch, airConditioner, onOffMountedSwitch, dimmableMountedSwitch, extendedColorLight, pressureSensor, contactSensor, occupancySensor, lightSensor, modeSelect, } from 'matterbridge';
2
- import { RoboticVacuumCleaner, LaundryWasher, WaterHeater, Evse, SolarPower, BatteryStorage, LaundryDryer, HeatPump, Dishwasher, ExtractorHood, MicrowaveOven, Oven, Cooktop, Refrigerator, } from 'matterbridge/devices';
1
+ import { MatterbridgeEndpoint, MatterbridgeDynamicPlatform, airQualitySensor, bridgedNode, colorTemperatureLight, coverDevice, dimmableLight, doorLockDevice, fanDevice, flowSensor, humiditySensor, onOffLight, onOffOutlet, onOffSwitch, powerSource, rainSensor, smokeCoAlarm, temperatureSensor, thermostatDevice, waterFreezeDetector, waterLeakDetector, airPurifier, pumpDevice, waterValve, genericSwitch, onOffMountedSwitch, dimmableMountedSwitch, extendedColorLight, pressureSensor, contactSensor, occupancySensor, lightSensor, modeSelect, } from 'matterbridge';
2
+ import { RoboticVacuumCleaner, LaundryWasher, WaterHeater, Evse, SolarPower, BatteryStorage, LaundryDryer, HeatPump, Dishwasher, ExtractorHood, MicrowaveOven, Oven, Cooktop, Refrigerator, AirConditioner, Speaker, } from 'matterbridge/devices';
3
3
  import { isValidBoolean, isValidNumber, isValidObject, isValidString } from 'matterbridge/utils';
4
4
  import { debugStringify } from 'matterbridge/logger';
5
5
  import { AreaNamespaceTag, LocationTag, NumberTag, PositionTag, RefrigeratorTag, SwitchesTag, UINT16_MAX, UINT32_MAX } from 'matterbridge/matter';
@@ -56,7 +56,6 @@ export class ExampleMatterbridgeDynamicPlatform extends MatterbridgeDynamicPlatf
56
56
  smokeOnly;
57
57
  coOnly;
58
58
  airQuality;
59
- airConditioner;
60
59
  airPurifier;
61
60
  pump;
62
61
  valve;
@@ -77,6 +76,8 @@ export class ExampleMatterbridgeDynamicPlatform extends MatterbridgeDynamicPlatf
77
76
  oven;
78
77
  cooktop;
79
78
  refrigerator;
79
+ airConditioner;
80
+ speaker;
80
81
  phaseInterval;
81
82
  phase = -1;
82
83
  sensorInterval;
@@ -103,8 +104,8 @@ export class ExampleMatterbridgeDynamicPlatform extends MatterbridgeDynamicPlatf
103
104
  fanDirectionLookup = ['Forward', 'Reverse'];
104
105
  constructor(matterbridge, log, config) {
105
106
  super(matterbridge, log, config);
106
- if (this.verifyMatterbridgeVersion === undefined || typeof this.verifyMatterbridgeVersion !== 'function' || !this.verifyMatterbridgeVersion('3.2.5')) {
107
- throw new Error(`This plugin requires Matterbridge version >= "3.2.5". Please update Matterbridge from ${this.matterbridge.matterbridgeVersion} to the latest version in the frontend.`);
107
+ if (this.verifyMatterbridgeVersion === undefined || typeof this.verifyMatterbridgeVersion !== 'function' || !this.verifyMatterbridgeVersion('3.2.6')) {
108
+ throw new Error(`This plugin requires Matterbridge version >= "3.2.6". Please update Matterbridge from ${this.matterbridge.matterbridgeVersion} to the latest version in the frontend.`);
108
109
  }
109
110
  this.log.info('Initializing platform:', this.config.name);
110
111
  if (config.whiteList === undefined)
@@ -707,35 +708,6 @@ export class ExampleMatterbridgeDynamicPlatform extends MatterbridgeDynamicPlatf
707
708
  if (isValidNumber(newValue, 0, 100))
708
709
  this.airPurifier?.setAttribute(FanControl.Cluster.id, 'percentCurrent', newValue, this.airPurifier?.log);
709
710
  }, this.airPurifier.log);
710
- this.airConditioner = new MatterbridgeEndpoint([airConditioner, bridgedNode, powerSource], { uniqueStorageKey: 'Air Conditioner' }, this.config.debug)
711
- .createDefaultBridgedDeviceBasicInformationClusterServer('Air Conditioner', 'ACO00027', 0xfff1, 'Matterbridge', 'Matterbridge Air Conditioner')
712
- .createDefaultIdentifyClusterServer()
713
- .createDeadFrontOnOffClusterServer(true)
714
- .createDefaultThermostatClusterServer(20, 18, 22)
715
- .createDefaultThermostatUserInterfaceConfigurationClusterServer()
716
- .createDefaultFanControlClusterServer(FanControl.FanMode.Auto)
717
- .createDefaultTemperatureMeasurementClusterServer(20 * 100)
718
- .createDefaultRelativeHumidityMeasurementClusterServer(50 * 100)
719
- .createDefaultPowerSourceWiredClusterServer()
720
- .addRequiredClusterServers();
721
- this.airConditioner = await this.addDevice(this.airConditioner);
722
- this.airConditioner?.addCommandHandler('identify', async ({ request: { identifyTime } }) => {
723
- this.airConditioner?.log.info(`Command identify called identifyTime:${identifyTime}`);
724
- });
725
- this.airConditioner?.addCommandHandler('on', async () => {
726
- this.airConditioner?.log.info('Command on called');
727
- await this.airConditioner?.setAttribute(ThermostatCluster.id, 'localTemperature', 20 * 100, this.airConditioner?.log);
728
- await this.airConditioner?.setAttribute(TemperatureMeasurement.Cluster.id, 'measuredValue', 20 * 100, this.airConditioner?.log);
729
- await this.airConditioner?.setAttribute(RelativeHumidityMeasurementCluster.id, 'measuredValue', 50 * 100, this.airConditioner?.log);
730
- await this.airConditioner?.setAttribute(FanControl.Cluster.id, 'percentSetting', 50, this.airConditioner?.log);
731
- });
732
- this.airConditioner?.addCommandHandler('off', async () => {
733
- this.airConditioner?.log.info('Command off called');
734
- await this.airConditioner?.setAttribute(ThermostatCluster.id, 'localTemperature', null, this.airConditioner?.log);
735
- await this.airConditioner?.setAttribute(TemperatureMeasurement.Cluster.id, 'measuredValue', null, this.airConditioner?.log);
736
- await this.airConditioner?.setAttribute(RelativeHumidityMeasurementCluster.id, 'measuredValue', null, this.airConditioner?.log);
737
- await this.airConditioner?.setAttribute(FanControl.Cluster.id, 'percentSetting', null, this.airConditioner?.log);
738
- });
739
711
  this.pump = new MatterbridgeEndpoint([pumpDevice, bridgedNode, powerSource], { uniqueStorageKey: 'Pump' }, this.config.debug)
740
712
  .createDefaultBridgedDeviceBasicInformationClusterServer('Pump', 'PUM00028', 0xfff1, 'Matterbridge', 'Matterbridge Pump')
741
713
  .createDefaultIdentifyClusterServer()
@@ -771,6 +743,7 @@ export class ExampleMatterbridgeDynamicPlatform extends MatterbridgeDynamicPlatf
771
743
  this.fanDefault = new MatterbridgeEndpoint([fanDevice, bridgedNode, powerSource], { uniqueStorageKey: 'Fan off low medium high auto' }, this.config.debug)
772
744
  .createDefaultBridgedDeviceBasicInformationClusterServer('Fan', 'FAN00030', 0xfff1, 'Matterbridge', 'Matterbridge Fan')
773
745
  .createDefaultPowerSourceWiredClusterServer()
746
+ .createDefaultFanControlClusterServer()
774
747
  .addRequiredClusterServers();
775
748
  this.fanDefault = await this.addDevice(this.fanDefault);
776
749
  await this.fanDefault?.subscribeAttribute(FanControl.Cluster.id, 'fanMode', (newValue, oldValue, context) => {
@@ -1188,6 +1161,71 @@ export class ExampleMatterbridgeDynamicPlatform extends MatterbridgeDynamicPlatf
1188
1161
  { label: 'RapidFreeze', mode: 2, modeTags: [{ value: RefrigeratorAndTemperatureControlledCabinetMode.ModeTag.RapidFreeze }] },
1189
1162
  ], undefined, undefined, -1000);
1190
1163
  this.refrigerator = (await this.addDevice(refrigerator));
1164
+ this.airConditioner = new AirConditioner('Air Conditioner', 'ACO00027', {
1165
+ localTemperature: 20,
1166
+ occupiedCoolingSetpoint: 18,
1167
+ occupiedHeatingSetpoint: 22,
1168
+ fanMode: FanControl.FanMode.Auto,
1169
+ })
1170
+ .createDefaultTemperatureMeasurementClusterServer(20 * 100)
1171
+ .createDefaultRelativeHumidityMeasurementClusterServer(50 * 100)
1172
+ .addRequiredClusterServers();
1173
+ this.airConditioner = await this.addDevice(this.airConditioner);
1174
+ this.airConditioner?.addCommandHandler('identify', async ({ request: { identifyTime } }) => {
1175
+ this.airConditioner?.log.info(`Command identify called identifyTime:${identifyTime}`);
1176
+ });
1177
+ this.airConditioner?.addCommandHandler('on', async () => {
1178
+ this.airConditioner?.log.info('Command on called');
1179
+ await this.airConditioner?.setAttribute(ThermostatCluster.id, 'localTemperature', 20 * 100, this.airConditioner?.log);
1180
+ await this.airConditioner?.setAttribute(TemperatureMeasurement.Cluster.id, 'measuredValue', 20 * 100, this.airConditioner?.log);
1181
+ await this.airConditioner?.setAttribute(RelativeHumidityMeasurementCluster.id, 'measuredValue', 50 * 100, this.airConditioner?.log);
1182
+ await this.airConditioner?.setAttribute(FanControl.Cluster.id, 'percentSetting', 50, this.airConditioner?.log);
1183
+ });
1184
+ this.airConditioner?.addCommandHandler('off', async () => {
1185
+ this.airConditioner?.log.info('Command off called');
1186
+ await this.airConditioner?.setAttribute(ThermostatCluster.id, 'localTemperature', null, this.airConditioner?.log);
1187
+ await this.airConditioner?.setAttribute(TemperatureMeasurement.Cluster.id, 'measuredValue', null, this.airConditioner?.log);
1188
+ await this.airConditioner?.setAttribute(RelativeHumidityMeasurementCluster.id, 'measuredValue', null, this.airConditioner?.log);
1189
+ await this.airConditioner?.setAttribute(FanControl.Cluster.id, 'percentSetting', null, this.airConditioner?.log);
1190
+ });
1191
+ await this.airConditioner?.subscribeAttribute(FanControl.Cluster.id, 'fanMode', (newValue, oldValue, context) => {
1192
+ this.airConditioner?.log.info(`Fan mode changed from ${this.fanModeLookup[oldValue]} to ${this.fanModeLookup[newValue]} context: ${context.offline === true ? 'offline' : 'online'}`);
1193
+ if (context.offline === true)
1194
+ return;
1195
+ if (newValue === FanControl.FanMode.Off) {
1196
+ this.airConditioner?.setAttribute(FanControl.Cluster.id, 'percentSetting', 0, this.airConditioner?.log);
1197
+ this.airConditioner?.setAttribute(FanControl.Cluster.id, 'percentCurrent', 0, this.airConditioner?.log);
1198
+ }
1199
+ else if (newValue === FanControl.FanMode.Low) {
1200
+ this.airConditioner?.setAttribute(FanControl.Cluster.id, 'percentSetting', 33, this.airConditioner?.log);
1201
+ this.airConditioner?.setAttribute(FanControl.Cluster.id, 'percentCurrent', 33, this.airConditioner?.log);
1202
+ }
1203
+ else if (newValue === FanControl.FanMode.Medium) {
1204
+ this.airConditioner?.setAttribute(FanControl.Cluster.id, 'percentSetting', 66, this.airConditioner?.log);
1205
+ this.airConditioner?.setAttribute(FanControl.Cluster.id, 'percentCurrent', 66, this.airConditioner?.log);
1206
+ }
1207
+ else if (newValue === FanControl.FanMode.High) {
1208
+ this.airConditioner?.setAttribute(FanControl.Cluster.id, 'percentSetting', 100, this.airConditioner?.log);
1209
+ this.airConditioner?.setAttribute(FanControl.Cluster.id, 'percentCurrent', 100, this.airConditioner?.log);
1210
+ }
1211
+ else if (newValue === FanControl.FanMode.On) {
1212
+ this.airConditioner?.setAttribute(FanControl.Cluster.id, 'percentSetting', 100, this.airConditioner?.log);
1213
+ this.airConditioner?.setAttribute(FanControl.Cluster.id, 'percentCurrent', 100, this.airConditioner?.log);
1214
+ }
1215
+ else if (newValue === FanControl.FanMode.Auto) {
1216
+ this.airConditioner?.setAttribute(FanControl.Cluster.id, 'percentSetting', 50, this.airConditioner?.log);
1217
+ this.airConditioner?.setAttribute(FanControl.Cluster.id, 'percentCurrent', 50, this.airConditioner?.log);
1218
+ }
1219
+ }, this.airConditioner?.log);
1220
+ await this.airConditioner?.subscribeAttribute(FanControl.Cluster.id, 'percentSetting', (newValue, oldValue, context) => {
1221
+ this.airConditioner?.log.info(`Percent setting changed from ${oldValue} to ${newValue} context: ${context.offline === true ? 'offline' : 'online'}`);
1222
+ if (context.offline === true)
1223
+ return;
1224
+ if (isValidNumber(newValue, 0, 100))
1225
+ this.airConditioner?.setAttribute(FanControl.Cluster.id, 'percentCurrent', newValue, this.airConditioner?.log);
1226
+ }, this.airConditioner?.log);
1227
+ this.speaker = new Speaker('Speaker', 'SPE00057', false, 100);
1228
+ this.speaker = (await this.addDevice(this.speaker));
1191
1229
  }
1192
1230
  async onConfigure() {
1193
1231
  await super.onConfigure();
@@ -1203,7 +1241,7 @@ export class ExampleMatterbridgeDynamicPlatform extends MatterbridgeDynamicPlatf
1203
1241
  await this.laundryWasher?.setAttribute(OnOff.Cluster.id, 'onOff', true, this.laundryWasher.log);
1204
1242
  await this.laundryDryer?.setAttribute(OnOff.Cluster.id, 'onOff', true, this.laundryDryer.log);
1205
1243
  await this.dishwasher?.setAttribute(OnOff.Cluster.id, 'onOff', true, this.dishwasher.log);
1206
- this.cooktop?.log.info(`Set Cooktop offOnly onOff clusters to on`);
1244
+ this.cooktop?.log.info(`Set Cooktop offOnly onOff clusters to true`);
1207
1245
  await this.cooktop?.setAttribute(OnOff.Cluster.id, 'onOff', true, this.cooktop.log);
1208
1246
  await this.cooktop?.getChildEndpointByName('SurfaceTopLeft')?.setAttribute(OnOff.Cluster.id, 'onOff', true, this.cooktop?.log);
1209
1247
  await this.cooktop?.getChildEndpointByName('SurfaceTopRight')?.setAttribute(OnOff.Cluster.id, 'onOff', true, this.cooktop?.log);
@@ -1225,6 +1263,7 @@ export class ExampleMatterbridgeDynamicPlatform extends MatterbridgeDynamicPlatf
1225
1263
  await this.roboticVacuum.setAttribute('RvcRunMode', 'currentMode', 2, this.roboticVacuum.log);
1226
1264
  await this.roboticVacuum.setAttribute('RvcOperationalState', 'operationalState', RvcOperationalState.OperationalState.Running, this.roboticVacuum.log);
1227
1265
  await this.roboticVacuum.setAttribute('ServiceArea', 'currentArea', 1, this.roboticVacuum.log);
1266
+ await this.roboticVacuum.setAttribute('ServiceArea', 'estimatedEndTime', Math.floor(Date.now() / 1000) + 300, this.roboticVacuum.log);
1228
1267
  }
1229
1268
  if (this.phase === 2) {
1230
1269
  this.roboticVacuum.log.info(`RVC: pause cleaning...`);
@@ -1237,12 +1276,14 @@ export class ExampleMatterbridgeDynamicPlatform extends MatterbridgeDynamicPlatf
1237
1276
  await this.roboticVacuum.setAttribute('RvcRunMode', 'currentMode', 2, this.roboticVacuum.log);
1238
1277
  await this.roboticVacuum.setAttribute('RvcOperationalState', 'operationalState', RvcOperationalState.OperationalState.Running, this.roboticVacuum.log);
1239
1278
  await this.roboticVacuum.setAttribute('ServiceArea', 'currentArea', 2, this.roboticVacuum.log);
1279
+ await this.roboticVacuum.setAttribute('ServiceArea', 'estimatedEndTime', Math.floor(Date.now() / 1000) + 180, this.roboticVacuum.log);
1240
1280
  }
1241
1281
  if (this.phase === 4) {
1242
1282
  this.roboticVacuum.log.info(`RVC: stop cleaning...`);
1243
1283
  await this.roboticVacuum.setAttribute('PowerSource', 'batPercentRemaining', 160, this.roboticVacuum.log);
1244
1284
  await this.roboticVacuum.setAttribute('RvcRunMode', 'currentMode', 1, this.roboticVacuum.log);
1245
1285
  await this.roboticVacuum.setAttribute('RvcOperationalState', 'operationalState', RvcOperationalState.OperationalState.Stopped, this.roboticVacuum.log);
1286
+ await this.roboticVacuum.setAttribute('ServiceArea', 'estimatedEndTime', 0, this.roboticVacuum.log);
1246
1287
  }
1247
1288
  if (this.phase === 5) {
1248
1289
  this.roboticVacuum.log.info(`RVC: going home...`);
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "matterbridge-example-dynamic-platform",
3
- "version": "1.3.9",
3
+ "version": "1.3.10",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "matterbridge-example-dynamic-platform",
9
- "version": "1.3.9",
9
+ "version": "1.3.10",
10
10
  "license": "Apache-2.0",
11
11
  "dependencies": {
12
12
  "node-ansi-logger": "3.1.1",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "matterbridge-example-dynamic-platform",
3
- "version": "1.3.9",
3
+ "version": "1.3.10",
4
4
  "description": "Matterbridge dynamic plugin",
5
5
  "author": "https://github.com/Luligu",
6
6
  "license": "Apache-2.0",