matterbridge-example-dynamic-platform 2.0.4 → 2.0.5-dev-20260125-f72d187

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
 
24
24
  <a href="https://www.buymeacoffee.com/luligugithub"><img src="https://matterbridge.io/assets/bmc-button.svg" alt="Buy me a coffee" width="120"></a>
25
25
 
26
+ ## [2.0.5] - 2026-01-24
27
+
28
+ ### Added
29
+
30
+ - [thermostat]: Added a thermostat auto mode with presets (Comfort and Away modes) including 3 sub endpoints with flowMeasurement, temperature and humidity sensors. Thanks Ludovic BOUÉ (<https://github.com/Luligu/matterbridge-example-dynamic-platform/pull/42> and https://github.com/Luligu/matterbridge-example-dynamic-platform/pull/43).
31
+ - [presets]: Added support for Matter Thermostat Presets feature with preset activation and setpoint management.
32
+ - [Energy]: Added an outlet (plug) with onOff cluster, energy measurements and power measurements
33
+ - [EnergyApparent]: Added an outlet (plug) with onOff cluster, apparent energy measurements and power measurements
34
+
35
+ ### Changed
36
+
37
+ - [package]: Updated dependencies.
38
+ - [package]: Bumped package to automator v.3.0.2.
39
+ - [package]: Required matterbridge 3.5.1.
40
+
41
+ <a href="https://www.buymeacoffee.com/luligugithub"><img src="https://matterbridge.io/assets/bmc-button.svg" alt="Buy me a coffee" width="80"></a>
42
+
26
43
  ## [2.0.4] - 2026-01-20
27
44
 
28
45
  ### Added
package/README.md CHANGED
@@ -19,7 +19,7 @@
19
19
 
20
20
  Matterbridge dynamic platform example plugin is a template to develop your own plugin using the dynamic platform.
21
21
 
22
- It exposes 58 virtual devices:
22
+ It exposes 61 virtual devices:
23
23
 
24
24
  - a door contact sensor
25
25
  - a motion sensor
@@ -38,12 +38,15 @@ It exposes 58 virtual devices:
38
38
  - a light with onOff, levelControl and colorControl (with XY and CT) clusters
39
39
  - a light with onOff, levelControl and colorControl (with CT only) clusters
40
40
  - an outlet (plug) with onOff cluster
41
+ - an outlet (plug) with onOff cluster, energy measurements and power measurements
42
+ - an outlet (plug) with onOff cluster, apparent energy measurements and power measurements
41
43
  - a cover with windowCovering cluster and lift feature
42
44
  - a cover with windowCovering cluster and both lift and tilt features
43
45
  - a lock with doorLock cluster
44
46
  - a thermostat auto mode (i.e. with Auto Heat and Cool features) with thermostat cluster and 3 sub endpoints with flowMeasurement cluster, temperatureMeasurement cluster
45
47
  and relativeHumidityMeasurement cluster (to show how to create a composed device with sub endpoints)
46
48
  - a thermostat with auto mode (i.e. with Auto Heat and Cool features), occupancy and outdoorTemperature
49
+ - a thermostat auto mode with presets (Home, Away, Sleep, Wake, Vacation and GoingToSleep modes) including 2 sub endpoints with temperature and humidity sensors
47
50
  - a thermostat heat only with two external temperature sensors (tagged like Indoor and Outdoor)
48
51
  - a thermostat cool only
49
52
  - a fan with Off High presets
package/dist/module.d.ts CHANGED
@@ -29,11 +29,14 @@ export declare class ExampleMatterbridgeDynamicPlatform extends MatterbridgeDyna
29
29
  lightHS: MatterbridgeEndpoint | undefined;
30
30
  lightCT: MatterbridgeEndpoint | undefined;
31
31
  outlet: MatterbridgeEndpoint | undefined;
32
+ outletEnergy: MatterbridgeEndpoint | undefined;
33
+ outletEnergyApparent: MatterbridgeEndpoint | undefined;
32
34
  coverLift: MatterbridgeEndpoint | undefined;
33
35
  coverLiftTilt: MatterbridgeEndpoint | undefined;
34
36
  lock: MatterbridgeEndpoint | undefined;
35
37
  thermoAuto: MatterbridgeEndpoint | undefined;
36
38
  thermoAutoOccupancy: MatterbridgeEndpoint | undefined;
39
+ thermoAutoPresets: MatterbridgeEndpoint | undefined;
37
40
  thermoHeat: MatterbridgeEndpoint | undefined;
38
41
  thermoCool: MatterbridgeEndpoint | undefined;
39
42
  fanBase: MatterbridgeEndpoint | undefined;
package/dist/module.js CHANGED
@@ -1,9 +1,9 @@
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';
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, electricalSensor, } from 'matterbridge';
2
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';
6
- import { PowerSource, BooleanState, OnOff, LevelControl, AirQuality, CarbonDioxideConcentrationMeasurement, CarbonMonoxideConcentrationMeasurement, FlowMeasurement, ColorControl, DoorLock, FanControl, FormaldehydeConcentrationMeasurement, NitrogenDioxideConcentrationMeasurement, OzoneConcentrationMeasurement, Pm10ConcentrationMeasurement, Pm1ConcentrationMeasurement, Pm25ConcentrationMeasurement, RadonConcentrationMeasurement, RelativeHumidityMeasurement, RelativeHumidityMeasurementCluster, SmokeCoAlarm, TemperatureMeasurement, Thermostat, ThermostatCluster, TotalVolatileOrganicCompoundsConcentrationMeasurement, WindowCovering, EnergyEvseMode, EnergyEvse, RvcRunMode, RvcCleanMode, Descriptor, BridgedDeviceBasicInformation, OvenMode, OperationalState, OccupancySensing, IlluminanceMeasurement, PressureMeasurement, RefrigeratorAndTemperatureControlledCabinetMode, RvcOperationalState, DeviceEnergyManagement, } from 'matterbridge/matter/clusters';
6
+ import { PowerSource, BooleanState, OnOff, LevelControl, AirQuality, CarbonDioxideConcentrationMeasurement, CarbonMonoxideConcentrationMeasurement, FlowMeasurement, ColorControl, DoorLock, FanControl, FormaldehydeConcentrationMeasurement, NitrogenDioxideConcentrationMeasurement, OzoneConcentrationMeasurement, Pm10ConcentrationMeasurement, Pm1ConcentrationMeasurement, Pm25ConcentrationMeasurement, RadonConcentrationMeasurement, RelativeHumidityMeasurement, RelativeHumidityMeasurementCluster, SmokeCoAlarm, TemperatureMeasurement, Thermostat, ThermostatCluster, TotalVolatileOrganicCompoundsConcentrationMeasurement, WindowCovering, EnergyEvseMode, EnergyEvse, RvcRunMode, RvcCleanMode, Descriptor, BridgedDeviceBasicInformation, OvenMode, OperationalState, OccupancySensing, IlluminanceMeasurement, PressureMeasurement, RefrigeratorAndTemperatureControlledCabinetMode, RvcOperationalState, DeviceEnergyManagement, ElectricalEnergyMeasurement, ElectricalPowerMeasurement, } from 'matterbridge/matter/clusters';
7
7
  function luxToMatter(lux) {
8
8
  if (!Number.isFinite(lux) || lux <= 0)
9
9
  return 0;
@@ -43,11 +43,14 @@ export class ExampleMatterbridgeDynamicPlatform extends MatterbridgeDynamicPlatf
43
43
  lightHS;
44
44
  lightCT;
45
45
  outlet;
46
+ outletEnergy;
47
+ outletEnergyApparent;
46
48
  coverLift;
47
49
  coverLiftTilt;
48
50
  lock;
49
51
  thermoAuto;
50
52
  thermoAutoOccupancy;
53
+ thermoAutoPresets;
51
54
  thermoHeat;
52
55
  thermoCool;
53
56
  fanBase;
@@ -110,8 +113,8 @@ export class ExampleMatterbridgeDynamicPlatform extends MatterbridgeDynamicPlatf
110
113
  constructor(matterbridge, log, config) {
111
114
  super(matterbridge, log, config);
112
115
  this.config = config;
113
- if (this.verifyMatterbridgeVersion === undefined || typeof this.verifyMatterbridgeVersion !== 'function' || !this.verifyMatterbridgeVersion('3.4.0')) {
114
- throw new Error(`This plugin requires Matterbridge version >= "3.4.0". Please update Matterbridge from ${this.matterbridge.matterbridgeVersion} to the latest version in the frontend.`);
116
+ if (this.verifyMatterbridgeVersion === undefined || typeof this.verifyMatterbridgeVersion !== 'function' || !this.verifyMatterbridgeVersion('3.5.1')) {
117
+ throw new Error(`This plugin requires Matterbridge version >= "3.5.1". Please update Matterbridge from ${this.matterbridge.matterbridgeVersion} to the latest version in the frontend.`);
115
118
  }
116
119
  this.log.info('Initializing platform:', this.config.name);
117
120
  }
@@ -432,6 +435,42 @@ export class ExampleMatterbridgeDynamicPlatform extends MatterbridgeDynamicPlatf
432
435
  this.outlet?.addCommandHandler('off', async () => {
433
436
  this.outlet?.log.info('Command off called');
434
437
  });
438
+ this.outletEnergy = new MatterbridgeEndpoint([onOffOutlet, electricalSensor, bridgedNode, powerSource], { id: 'OutletEnergy' }, this.config.debug)
439
+ .createDefaultIdentifyClusterServer()
440
+ .createDefaultElectricalEnergyMeasurementClusterServer(0, 0)
441
+ .createDefaultElectricalPowerMeasurementClusterServer(220_000, 0, 0, 50_000)
442
+ .createDefaultBridgedDeviceBasicInformationClusterServer('OutletEnergy', 'OEN00019', 0xfff1, 'Matterbridge', 'Matterbridge Outlet With Energy')
443
+ .createDefaultOnOffClusterServer()
444
+ .createDefaultPowerSourceWiredClusterServer()
445
+ .addRequiredClusterServers();
446
+ this.outletEnergy = await this.addDevice(this.outletEnergy);
447
+ this.outletEnergy?.addCommandHandler('identify', async ({ request: { identifyTime } }) => {
448
+ this.outletEnergy?.log.info(`Command identify called identifyTime:${identifyTime}`);
449
+ });
450
+ this.outletEnergy?.addCommandHandler('on', async () => {
451
+ this.outletEnergy?.log.info('Command on called');
452
+ });
453
+ this.outletEnergy?.addCommandHandler('off', async () => {
454
+ this.outletEnergy?.log.info('Command off called');
455
+ });
456
+ this.outletEnergyApparent = new MatterbridgeEndpoint([onOffOutlet, electricalSensor, bridgedNode, powerSource], { id: 'OutletEnergyApparent' }, this.config.debug)
457
+ .createDefaultIdentifyClusterServer()
458
+ .createDefaultElectricalEnergyMeasurementClusterServer(0, 0)
459
+ .createApparentElectricalPowerMeasurementClusterServer(220_000, 0, 0, 50_000)
460
+ .createDefaultBridgedDeviceBasicInformationClusterServer('OutletEnergyApparent', 'OEA00019', 0xfff1, 'Matterbridge', 'Matterbridge Outlet With Apparent Energy')
461
+ .createDefaultOnOffClusterServer()
462
+ .createDefaultPowerSourceWiredClusterServer()
463
+ .addRequiredClusterServers();
464
+ this.outletEnergyApparent = await this.addDevice(this.outletEnergyApparent);
465
+ this.outletEnergyApparent?.addCommandHandler('identify', async ({ request: { identifyTime } }) => {
466
+ this.outletEnergyApparent?.log.info(`Command identify called identifyTime:${identifyTime}`);
467
+ });
468
+ this.outletEnergyApparent?.addCommandHandler('on', async () => {
469
+ this.outletEnergyApparent?.log.info('Command on called');
470
+ });
471
+ this.outletEnergyApparent?.addCommandHandler('off', async () => {
472
+ this.outletEnergyApparent?.log.info('Command off called');
473
+ });
435
474
  this.coverLift = new MatterbridgeEndpoint([coverDevice, bridgedNode, powerSource], { id: 'CoverLift' }, this.config.debug)
436
475
  .createDefaultIdentifyClusterServer()
437
476
  .createDefaultGroupsClusterServer()
@@ -550,7 +589,7 @@ export class ExampleMatterbridgeDynamicPlatform extends MatterbridgeDynamicPlatf
550
589
  this.thermoAutoOccupancy = new MatterbridgeEndpoint([thermostatDevice, bridgedNode, powerSource], { id: 'Thermostat (AutoModeOccupancy)' }, this.config.debug)
551
590
  .createDefaultIdentifyClusterServer()
552
591
  .createDefaultGroupsClusterServer()
553
- .createDefaultBridgedDeviceBasicInformationClusterServer('Thermostat (AutoOccupancy)', 'TAO00058', 0xfff1, 'Matterbridge', 'Matterbridge Thermostat')
592
+ .createDefaultBridgedDeviceBasicInformationClusterServer('Thermostat (AutoOccupancy)', 'TAO00058', 0xfff1, 'Matterbridge', 'Matterbridge Thermostat Presets')
554
593
  .createDefaultThermostatClusterServer(20, 18, 22, 1, 0, 35, 15, 50, 10, 30, false, 20.5)
555
594
  .createDefaultPowerSourceWiredClusterServer();
556
595
  this.thermoAutoOccupancy = await this.addDevice(this.thermoAutoOccupancy);
@@ -570,6 +609,165 @@ export class ExampleMatterbridgeDynamicPlatform extends MatterbridgeDynamicPlatf
570
609
  await this.thermoAutoOccupancy?.subscribeAttribute(ThermostatCluster.id, 'unoccupiedCoolingSetpoint', (value) => {
571
610
  this.thermoAutoOccupancy?.log.info('Subscribe unoccupiedCoolingSetpoint called with:', value / 100);
572
611
  }, this.thermoAutoOccupancy.log);
612
+ const presets_List = [
613
+ {
614
+ presetHandle: new Uint8Array([0]),
615
+ presetScenario: Thermostat.PresetScenario.Occupied,
616
+ name: 'Home',
617
+ coolingSetpoint: 2300,
618
+ heatingSetpoint: 2200,
619
+ builtIn: true,
620
+ },
621
+ {
622
+ presetHandle: new Uint8Array([1]),
623
+ presetScenario: Thermostat.PresetScenario.Unoccupied,
624
+ name: 'Away',
625
+ coolingSetpoint: 2600,
626
+ heatingSetpoint: 1800,
627
+ builtIn: true,
628
+ },
629
+ {
630
+ presetHandle: new Uint8Array([2]),
631
+ presetScenario: Thermostat.PresetScenario.Sleep,
632
+ name: 'Sleep',
633
+ coolingSetpoint: 2100,
634
+ heatingSetpoint: 1800,
635
+ builtIn: true,
636
+ },
637
+ {
638
+ presetHandle: new Uint8Array([3]),
639
+ presetScenario: Thermostat.PresetScenario.Wake,
640
+ name: 'Wake',
641
+ coolingSetpoint: 2400,
642
+ heatingSetpoint: 1900,
643
+ builtIn: true,
644
+ },
645
+ {
646
+ presetHandle: new Uint8Array([4]),
647
+ presetScenario: Thermostat.PresetScenario.Vacation,
648
+ name: 'Vacation',
649
+ coolingSetpoint: 2700,
650
+ heatingSetpoint: 1600,
651
+ builtIn: true,
652
+ },
653
+ {
654
+ presetHandle: new Uint8Array([5]),
655
+ presetScenario: Thermostat.PresetScenario.GoingToSleep,
656
+ name: 'GoingToSleep',
657
+ coolingSetpoint: 2200,
658
+ heatingSetpoint: 1850,
659
+ builtIn: true,
660
+ },
661
+ ];
662
+ const presetTypeDefinitions = [
663
+ {
664
+ presetScenario: Thermostat.PresetScenario.Occupied,
665
+ numberOfPresets: presets_List.filter((p) => p.presetScenario === Thermostat.PresetScenario.Occupied).length,
666
+ presetTypeFeatures: {
667
+ automatic: false,
668
+ supportsNames: true,
669
+ },
670
+ },
671
+ {
672
+ presetScenario: Thermostat.PresetScenario.Unoccupied,
673
+ numberOfPresets: presets_List.filter((p) => p.presetScenario === Thermostat.PresetScenario.Unoccupied).length,
674
+ presetTypeFeatures: {
675
+ automatic: false,
676
+ supportsNames: true,
677
+ },
678
+ },
679
+ {
680
+ presetScenario: Thermostat.PresetScenario.Sleep,
681
+ numberOfPresets: presets_List.filter((p) => p.presetScenario === Thermostat.PresetScenario.Sleep).length,
682
+ presetTypeFeatures: {
683
+ automatic: false,
684
+ supportsNames: true,
685
+ },
686
+ },
687
+ {
688
+ presetScenario: Thermostat.PresetScenario.Wake,
689
+ numberOfPresets: presets_List.filter((p) => p.presetScenario === Thermostat.PresetScenario.Wake).length,
690
+ presetTypeFeatures: {
691
+ automatic: false,
692
+ supportsNames: true,
693
+ },
694
+ },
695
+ {
696
+ presetScenario: Thermostat.PresetScenario.Vacation,
697
+ numberOfPresets: presets_List.filter((p) => p.presetScenario === Thermostat.PresetScenario.Vacation).length,
698
+ presetTypeFeatures: {
699
+ automatic: false,
700
+ supportsNames: true,
701
+ },
702
+ },
703
+ {
704
+ presetScenario: Thermostat.PresetScenario.GoingToSleep,
705
+ numberOfPresets: presets_List.filter((p) => p.presetScenario === Thermostat.PresetScenario.GoingToSleep).length,
706
+ presetTypeFeatures: {
707
+ automatic: false,
708
+ supportsNames: true,
709
+ },
710
+ },
711
+ ];
712
+ this.thermoAutoPresets = new MatterbridgeEndpoint([thermostatDevice, bridgedNode, powerSource], { id: 'Thermostat (AutoModePresets)' }, this.config.debug)
713
+ .createDefaultIdentifyClusterServer()
714
+ .createDefaultGroupsClusterServer()
715
+ .createDefaultBridgedDeviceBasicInformationClusterServer('Thermostat (AutoModePresets)', 'TAP00058', 0xfff1, 'Matterbridge', 'Matterbridge Thermostat With Presets')
716
+ .createDefaultPresetsThermostatClusterServer(20, 18, 22, 1, 0, 35, 15, 50, 10, 30, false, 20.5, undefined, presets_List, presetTypeDefinitions)
717
+ .createDefaultPowerSourceWiredClusterServer();
718
+ if (this.thermoAutoPresets) {
719
+ this.thermoAutoPresets
720
+ .addChildDeviceType('Temperature', temperatureSensor)
721
+ .createDefaultTemperatureMeasurementClusterServer(21 * 100)
722
+ .addRequiredClusterServers();
723
+ this.thermoAutoPresets
724
+ .addChildDeviceType('Humidity', humiditySensor)
725
+ .createDefaultRelativeHumidityMeasurementClusterServer(50 * 100)
726
+ .addRequiredClusterServers();
727
+ this.thermoAutoPresets = await this.addDevice(this.thermoAutoPresets);
728
+ }
729
+ this.thermoAutoPresets?.addCommandHandler('identify', async ({ request: { identifyTime } }) => {
730
+ this.thermoAutoPresets?.log.info(`Command identify called identifyTime ${identifyTime}`);
731
+ });
732
+ this.thermoAutoPresets?.addCommandHandler('triggerEffect', async ({ request: { effectIdentifier, effectVariant } }) => {
733
+ this.thermoAutoPresets?.log.info(`Command identify called effectIdentifier ${effectIdentifier} effectVariant ${effectVariant}`);
734
+ });
735
+ this.thermoAutoPresets?.addCommandHandler('setpointRaiseLower', async ({ request: { mode, amount } }) => {
736
+ const lookupSetpointAdjustMode = ['Heat', 'Cool', 'Both'];
737
+ this.thermoAutoPresets?.log.info(`Command setpointRaiseLower called with mode: ${lookupSetpointAdjustMode[mode]} amount: ${amount / 10}`);
738
+ });
739
+ this.thermoAutoPresets?.addCommandHandler('setActivePresetRequest', async ({ request: { presetHandle } }) => {
740
+ const handle = Uint8Array.from(presetHandle);
741
+ const preset = presets_List.find((p) => p.presetHandle.length === handle.length && p.presetHandle.every((v, i) => v === handle[i]));
742
+ if (!preset) {
743
+ this.thermoAutoPresets?.log.error(`Command setActivePresetRequest received unknown presetHandle: ${Array.from(handle).join(',')}`);
744
+ return;
745
+ }
746
+ await this.thermoAutoPresets?.setAttribute(ThermostatCluster.id, 'activePresetHandle', handle, this.thermoAutoPresets?.log);
747
+ if (preset.heatingSetpoint !== undefined) {
748
+ await this.thermoAutoPresets?.setAttribute(ThermostatCluster.id, 'occupiedHeatingSetpoint', preset.heatingSetpoint, this.thermoAutoPresets?.log);
749
+ }
750
+ if (preset.coolingSetpoint !== undefined) {
751
+ await this.thermoAutoPresets?.setAttribute(ThermostatCluster.id, 'occupiedCoolingSetpoint', preset.coolingSetpoint, this.thermoAutoPresets?.log);
752
+ }
753
+ this.thermoAutoPresets?.log.info(`Command setActivePresetRequest applied. Active preset is now '${preset.name}' (handle ${Array.from(handle).join(',')}) with heating setpoint ${preset.heatingSetpoint / 100}°C and cooling setpoint ${preset.coolingSetpoint / 100}°C.`);
754
+ });
755
+ await this.thermoAutoPresets?.subscribeAttribute(ThermostatCluster.id, 'systemMode', (value) => {
756
+ const lookupSystemMode = ['Off', 'Auto', '', 'Cool', 'Heat', 'EmergencyHeat', 'Precooling', 'FanOnly', 'Dry', 'Sleep'];
757
+ this.thermoAutoPresets?.log.info('Subscribe systemMode called with:', lookupSystemMode[value]);
758
+ }, this.thermoAutoPresets.log);
759
+ await this.thermoAutoPresets?.subscribeAttribute(ThermostatCluster.id, 'occupiedHeatingSetpoint', (value) => {
760
+ this.thermoAutoPresets?.log.info('Subscribe occupiedHeatingSetpoint called with:', value / 100);
761
+ }, this.thermoAutoPresets.log);
762
+ await this.thermoAutoPresets?.subscribeAttribute(ThermostatCluster.id, 'occupiedCoolingSetpoint', (value) => {
763
+ this.thermoAutoPresets?.log.info('Subscribe occupiedCoolingSetpoint called with:', value / 100);
764
+ }, this.thermoAutoPresets.log);
765
+ await this.thermoAutoPresets?.subscribeAttribute(ThermostatCluster.id, 'activePresetHandle', (value) => {
766
+ this.thermoAutoPresets?.log.info('Subscribe activePresetHandle called with:', value);
767
+ }, this.thermoAutoPresets.log);
768
+ await this.thermoAutoPresets?.subscribeAttribute(ThermostatCluster.id, 'presets', (value) => {
769
+ this.thermoAutoPresets?.log.info('Subscribe presets called with:', value);
770
+ }, this.thermoAutoPresets.log);
573
771
  this.thermoHeat = new MatterbridgeEndpoint([thermostatDevice, bridgedNode, powerSource], { id: 'Thermostat (Heat)' }, this.config.debug)
574
772
  .createDefaultIdentifyClusterServer()
575
773
  .createDefaultGroupsClusterServer()
@@ -1545,14 +1743,69 @@ export class ExampleMatterbridgeDynamicPlatform extends MatterbridgeDynamicPlatf
1545
1743
  this.log.info(`Set lights colorTemperatureMireds to ${this.intervalColorTemperature}`);
1546
1744
  }, 60 * 1000 + 200);
1547
1745
  }
1548
- await this.outlet?.setAttribute(OnOff.Cluster.id, 'onOff', false, this.outlet.log);
1549
1746
  this.outlet?.log.info('Set outlet initial onOff to false');
1747
+ await this.outlet?.setAttribute(OnOff.Cluster.id, 'onOff', false, this.outlet.log);
1748
+ this.outletEnergy?.log.info('Set outlet initial onOff to false and energy/power to 0');
1749
+ await this.outletEnergy?.setAttribute(OnOff.Cluster.id, 'onOff', false, this.outletEnergy.log);
1750
+ await this.outletEnergy?.setAttribute(ElectricalEnergyMeasurement.Cluster.id, 'cumulativeEnergyImported', { energy: 0 }, this.outletEnergy.log);
1751
+ await this.outletEnergy?.setAttribute(ElectricalPowerMeasurement.Cluster.id, 'voltage', 220_000, this.outletEnergy.log);
1752
+ await this.outletEnergy?.setAttribute(ElectricalPowerMeasurement.Cluster.id, 'activeCurrent', 0, this.outletEnergy.log);
1753
+ await this.outletEnergy?.setAttribute(ElectricalPowerMeasurement.Cluster.id, 'activePower', 0, this.outletEnergy.log);
1754
+ this.outletEnergyApparent?.log.info('Set outlet initial onOff to false and apparent energy/power to 0');
1755
+ await this.outletEnergyApparent?.setAttribute(OnOff.Cluster.id, 'onOff', false, this.outletEnergyApparent.log);
1756
+ await this.outletEnergyApparent?.setAttribute(ElectricalEnergyMeasurement.Cluster.id, 'cumulativeEnergyImported', { energy: 0 }, this.outletEnergyApparent.log);
1757
+ await this.outletEnergyApparent?.setAttribute(ElectricalPowerMeasurement.Cluster.id, 'voltage', 220_000, this.outletEnergyApparent.log);
1758
+ await this.outletEnergyApparent?.setAttribute(ElectricalPowerMeasurement.Cluster.id, 'apparentCurrent', 0, this.outletEnergyApparent.log);
1759
+ await this.outletEnergyApparent?.setAttribute(ElectricalPowerMeasurement.Cluster.id, 'apparentPower', 0, this.outletEnergyApparent.log);
1550
1760
  if (this.config.useInterval) {
1551
1761
  this.outletInterval = setInterval(async () => {
1552
- const state = this.outlet?.getAttribute(OnOff.Cluster.id, 'onOff', this.outlet.log);
1762
+ let state = this.outlet?.getAttribute(OnOff.Cluster.id, 'onOff', this.outlet.log);
1553
1763
  if (isValidBoolean(state)) {
1554
- await this.outlet?.setAttribute(OnOff.Cluster.id, 'onOff', !state, this.outlet.log);
1555
1764
  this.outlet?.log.info(`Set outlet onOff to ${!state}`);
1765
+ await this.outlet?.setAttribute(OnOff.Cluster.id, 'onOff', !state, this.outlet.log);
1766
+ }
1767
+ state = this.outletEnergy?.getAttribute(OnOff.Cluster.id, 'onOff', this.outletEnergy.log);
1768
+ if (isValidBoolean(state)) {
1769
+ this.outletEnergy?.log.info(`Set outlet onOff to ${!state}`);
1770
+ await this.outletEnergy?.setAttribute(OnOff.Cluster.id, 'onOff', !state, this.outletEnergy.log);
1771
+ if (!state) {
1772
+ await this.outletEnergy?.setAttribute(ElectricalPowerMeasurement.Cluster.id, 'activeCurrent', 100_000, this.outletEnergy.log);
1773
+ await this.outletEnergy?.setAttribute(ElectricalPowerMeasurement.Cluster.id, 'activePower', 220_000_000, this.outletEnergy.log);
1774
+ const energy = this.outletEnergy?.getAttribute(ElectricalEnergyMeasurement.Cluster.id, 'cumulativeEnergyImported', this.outletEnergy.log);
1775
+ if (isValidObject(energy, 1)) {
1776
+ if (typeof energy.energy === 'bigint')
1777
+ energy.energy += 5000n;
1778
+ else
1779
+ energy.energy += 5000;
1780
+ await this.outletEnergy?.setAttribute(ElectricalEnergyMeasurement.Cluster.id, 'cumulativeEnergyImported', energy, this.outletEnergy.log);
1781
+ }
1782
+ await this.outletEnergy?.setAttribute(ElectricalEnergyMeasurement.Cluster.id, 'cumulativeEnergyImported', { energy: 0 }, this.outletEnergy.log);
1783
+ }
1784
+ else {
1785
+ await this.outletEnergy?.setAttribute(ElectricalPowerMeasurement.Cluster.id, 'activeCurrent', 0, this.outletEnergy.log);
1786
+ await this.outletEnergy?.setAttribute(ElectricalPowerMeasurement.Cluster.id, 'activePower', 0, this.outletEnergy.log);
1787
+ }
1788
+ }
1789
+ state = this.outletEnergyApparent?.getAttribute(OnOff.Cluster.id, 'onOff', this.outletEnergyApparent.log);
1790
+ if (isValidBoolean(state)) {
1791
+ this.outletEnergyApparent?.log.info(`Set outlet onOff to ${!state}`);
1792
+ await this.outletEnergyApparent?.setAttribute(OnOff.Cluster.id, 'onOff', !state, this.outletEnergyApparent.log);
1793
+ if (!state) {
1794
+ await this.outletEnergyApparent?.setAttribute(ElectricalPowerMeasurement.Cluster.id, 'apparentCurrent', 100_000, this.outletEnergyApparent.log);
1795
+ await this.outletEnergyApparent?.setAttribute(ElectricalPowerMeasurement.Cluster.id, 'apparentPower', 220_000_000, this.outletEnergyApparent.log);
1796
+ const energy = this.outletEnergyApparent?.getAttribute(ElectricalEnergyMeasurement.Cluster.id, 'cumulativeEnergyImported', this.outletEnergyApparent.log);
1797
+ if (isValidObject(energy, 1)) {
1798
+ if (typeof energy.energy === 'bigint')
1799
+ energy.energy += 5000n;
1800
+ else
1801
+ energy.energy += 5000;
1802
+ await this.outletEnergyApparent?.setAttribute(ElectricalEnergyMeasurement.Cluster.id, 'cumulativeEnergyImported', energy, this.outletEnergyApparent.log);
1803
+ }
1804
+ }
1805
+ else {
1806
+ await this.outletEnergyApparent?.setAttribute(ElectricalPowerMeasurement.Cluster.id, 'apparentCurrent', 0, this.outletEnergyApparent.log);
1807
+ await this.outletEnergyApparent?.setAttribute(ElectricalPowerMeasurement.Cluster.id, 'apparentPower', 0, this.outletEnergyApparent.log);
1808
+ }
1556
1809
  }
1557
1810
  }, 60 * 1000 + 300);
1558
1811
  }
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "matterbridge-example-dynamic-platform",
3
- "version": "2.0.4",
3
+ "version": "2.0.5-dev-20260125-f72d187",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "matterbridge-example-dynamic-platform",
9
- "version": "2.0.4",
9
+ "version": "2.0.5-dev-20260125-f72d187",
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": "2.0.4",
3
+ "version": "2.0.5-dev-20260125-f72d187",
4
4
  "description": "Matterbridge dynamic plugin",
5
5
  "author": "https://github.com/Luligu",
6
6
  "license": "Apache-2.0",