matterbridge 3.0.5-dev-20250607-86b467e → 3.0.5-dev-20250607-ef2d8e1

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
@@ -8,11 +8,12 @@ If you like this project and find it useful, please consider giving it a star on
8
8
  <img src="bmc-button.svg" alt="Buy me a coffee" width="120">
9
9
  </a>
10
10
 
11
- ## [3.0.5] - 2025-05-??
11
+ ## [3.0.5] - 2025-06-07
12
12
 
13
13
  ### Added
14
14
 
15
15
  - [cli]: Added takeHeapSnapshot() and triggerGarbageCollection().
16
+ - [LaundryWasher]: Added Evse class and Jest test. Thanks Ludovic BOUÉ.
16
17
  - [LaundryWasher]: Added LaundryWasher class and Jest test.
17
18
  - [WaterHeater]: Added WaterHeater class and Jest test. Thanks Ludovic BOUÉ.
18
19
  - [nginx]: Added new example configurations for [nginx](README-NGINX.md).
package/dist/evse.js ADDED
@@ -0,0 +1,85 @@
1
+ import { EnergyEvseServer } from '@matter/main/behaviors/energy-evse';
2
+ import { EnergyEvseModeServer } from '@matter/main/behaviors/energy-evse-mode';
3
+ import { EnergyEvse, EnergyEvseMode } from '@matter/main/clusters';
4
+ import { ModeBase } from '@matter/main/clusters/mode-base';
5
+ import { MatterbridgeEndpoint } from './matterbridgeEndpoint.js';
6
+ import { MatterbridgeServer } from './matterbridgeBehaviors.js';
7
+ import { deviceEnergyManagement, electricalSensor, evse, powerSource } from './matterbridgeDeviceTypes.js';
8
+ export class Evse extends MatterbridgeEndpoint {
9
+ constructor(name, serial, state, supplyState, faultState) {
10
+ super([evse, powerSource, electricalSensor, deviceEnergyManagement], { uniqueStorageKey: `${name.replaceAll(' ', '')}-${serial.replaceAll(' ', '')}` }, true);
11
+ this.createDefaultIdentifyClusterServer()
12
+ .createDefaultBasicInformationClusterServer(name, serial, 0xfff1, 'Matterbridge', 0x8000, 'Matterbridge EVSE')
13
+ .createDefaultPowerSourceWiredClusterServer()
14
+ .createDefaultPowerTopologyClusterServer()
15
+ .createDefaultElectricalPowerMeasurementClusterServer()
16
+ .createDefaultElectricalEnergyMeasurementClusterServer()
17
+ .createDefaultDeviceEnergyManagementCluster()
18
+ .createDefaultEnergyEvseClusterServer(state, supplyState, faultState)
19
+ .createDefaultEnergyEvseModeClusterServer()
20
+ .addRequiredClusterServers();
21
+ }
22
+ createDefaultEnergyEvseClusterServer(state, supplyState, faultState) {
23
+ this.behaviors.require(MatterbridgeEnergyEvseServer, {
24
+ state: state ?? EnergyEvse.State.NotPluggedIn,
25
+ supplyState: supplyState ?? EnergyEvse.SupplyState.Disabled,
26
+ faultState: faultState ?? EnergyEvse.FaultState.NoError,
27
+ chargingEnabledUntil: 0,
28
+ circuitCapacity: 0,
29
+ minimumChargeCurrent: 6000,
30
+ maximumChargeCurrent: 0,
31
+ sessionId: null,
32
+ sessionDuration: 0,
33
+ sessionEnergyCharged: 0,
34
+ });
35
+ return this;
36
+ }
37
+ createDefaultEnergyEvseModeClusterServer(currentMode, supportedModes) {
38
+ this.behaviors.require(MatterbridgeEnergyEvseModeServer, {
39
+ supportedModes: supportedModes ?? [
40
+ { label: 'Auto', mode: 1, modeTags: [{ value: EnergyEvseMode.ModeTag.Auto }] },
41
+ { label: 'Quick', mode: 2, modeTags: [{ value: EnergyEvseMode.ModeTag.Quick }] },
42
+ { label: 'Quiet', mode: 3, modeTags: [{ value: EnergyEvseMode.ModeTag.Quiet }] },
43
+ { label: 'LowNoise', mode: 4, modeTags: [{ value: EnergyEvseMode.ModeTag.LowNoise }] },
44
+ { label: 'LowEnergy', mode: 5, modeTags: [{ value: EnergyEvseMode.ModeTag.LowEnergy }] },
45
+ { label: 'Vacation', mode: 6, modeTags: [{ value: EnergyEvseMode.ModeTag.Vacation }] },
46
+ { label: 'Min', mode: 7, modeTags: [{ value: EnergyEvseMode.ModeTag.Min }] },
47
+ { label: 'Max', mode: 8, modeTags: [{ value: EnergyEvseMode.ModeTag.Max }] },
48
+ { label: 'Night', mode: 9, modeTags: [{ value: EnergyEvseMode.ModeTag.Night }] },
49
+ { label: 'Day', mode: 10, modeTags: [{ value: EnergyEvseMode.ModeTag.Day }] },
50
+ { label: 'Manual', mode: 11, modeTags: [{ value: EnergyEvseMode.ModeTag.Manual }] },
51
+ { label: 'TimeOfUse', mode: 12, modeTags: [{ value: EnergyEvseMode.ModeTag.TimeOfUse }] },
52
+ { label: 'SolarCharging', mode: 13, modeTags: [{ value: EnergyEvseMode.ModeTag.SolarCharging }] },
53
+ { label: 'V2X', mode: 14, modeTags: [{ value: EnergyEvseMode.ModeTag.V2X }] },
54
+ ],
55
+ currentMode: currentMode ?? 1,
56
+ });
57
+ return this;
58
+ }
59
+ }
60
+ export class MatterbridgeEnergyEvseServer extends EnergyEvseServer {
61
+ disable() {
62
+ const device = this.endpoint.stateOf(MatterbridgeServer).deviceCommand;
63
+ device.disable();
64
+ device.log.info(`MatterbridgeEnergyEvseServer disable called`);
65
+ }
66
+ enableCharging() {
67
+ const device = this.endpoint.stateOf(MatterbridgeServer).deviceCommand;
68
+ device.enableCharging();
69
+ device.log.info(`MatterbridgeEnergyEvseServer enableCharging called`);
70
+ }
71
+ }
72
+ export class MatterbridgeEnergyEvseModeServer extends EnergyEvseModeServer {
73
+ changeToMode({ newMode }) {
74
+ const device = this.endpoint.stateOf(MatterbridgeServer).deviceCommand;
75
+ const supported = this.state.supportedModes.find((mode) => mode.mode === newMode);
76
+ if (!supported) {
77
+ device.log.error(`MatterbridgeEnergyEvseModeServer changeToMode called with unsupported newMode: ${newMode}`);
78
+ return { status: ModeBase.ModeChangeStatus.UnsupportedMode, statusText: 'Unsupported mode' };
79
+ }
80
+ device.changeToMode({ newMode });
81
+ this.state.currentMode = newMode;
82
+ device.log.info(`MatterbridgeEnergyEvseModeServer changeToMode called with newMode ${newMode} => ${supported.label}`);
83
+ return { status: ModeBase.ModeChangeStatus.Success, statusText: 'Success' };
84
+ }
85
+ }
package/dist/index.js CHANGED
@@ -10,9 +10,11 @@ export * from './matterbridgeDeviceTypes.js';
10
10
  export * from './matterbridgePlatform.js';
11
11
  export * from './matterbridgeAccessoryPlatform.js';
12
12
  export * from './matterbridgeDynamicPlatform.js';
13
+ export { addVirtualDevice } from './helpers.js';
13
14
  export * from './roboticVacuumCleaner.js';
15
+ export * from './laundryWasher.js';
14
16
  export * from './waterHeater.js';
15
- export { addVirtualDevice } from './helpers.js';
17
+ export * from './evse.js';
16
18
  const log = new AnsiLogger({ logName: 'Main', logTimestampFormat: 4, logLevel: hasParameter('debug') ? "debug" : "info" });
17
19
  async function main() {
18
20
  log.debug('***Matterbridge.loadInstance() called');
@@ -9,10 +9,7 @@ import { SmokeCoAlarm } from '@matter/main/clusters/smoke-co-alarm';
9
9
  import { BooleanStateConfigurationServer } from '@matter/main/behaviors/boolean-state-configuration';
10
10
  import { OperationalState } from '@matter/main/clusters/operational-state';
11
11
  import { ModeBase } from '@matter/main/clusters/mode-base';
12
- import { RvcRunMode } from '@matter/main/clusters/rvc-run-mode';
13
- import { RvcOperationalState } from '@matter/main/clusters/rvc-operational-state';
14
12
  import { ServiceArea } from '@matter/main/clusters/service-area';
15
- import { WaterHeaterManagement } from '@matter/main/clusters/water-heater-management';
16
13
  import { IdentifyServer } from '@matter/main/behaviors/identify';
17
14
  import { OnOffServer } from '@matter/main/behaviors/on-off';
18
15
  import { LevelControlServer } from '@matter/main/behaviors/level-control';
@@ -26,12 +23,8 @@ import { ModeSelectServer } from '@matter/main/behaviors/mode-select';
26
23
  import { SmokeCoAlarmServer } from '@matter/main/behaviors/smoke-co-alarm';
27
24
  import { SwitchServer } from '@matter/main/behaviors/switch';
28
25
  import { OperationalStateServer } from '@matter/main/behaviors/operational-state';
29
- import { RvcRunModeServer } from '@matter/main/behaviors/rvc-run-mode';
30
- import { RvcCleanModeServer } from '@matter/main/behaviors/rvc-clean-mode';
31
- import { RvcOperationalStateServer } from '@matter/main/behaviors/rvc-operational-state';
32
26
  import { ServiceAreaServer } from '@matter/main/behaviors/service-area';
33
- import { WaterHeaterModeServer } from '@matter/main/behaviors/water-heater-mode';
34
- import { WaterHeaterManagementServer } from '@matter/main/behaviors/water-heater-management';
27
+ import { DeviceEnergyManagementModeServer } from '@matter/main/behaviors/device-energy-management-mode';
35
28
  export class MatterbridgeServerDevice {
36
29
  log;
37
30
  commandHandler;
@@ -189,6 +182,14 @@ export class MatterbridgeServerDevice {
189
182
  this.log.info(`Cancel boost (endpoint ${this.endpointId}.${this.endpointNumber})`);
190
183
  this.commandHandler.executeHandler('cancelBoost', { request: {}, attributes: {}, endpoint: { number: this.endpointNumber, uniqueStorageKey: this.endpointId } });
191
184
  }
185
+ enableCharging() {
186
+ this.log.info(`EnableCharging (endpoint ${this.endpointId}.${this.endpointNumber})`);
187
+ this.commandHandler.executeHandler('enableCharging', { request: {}, attributes: {}, endpoint: { number: this.endpointNumber, uniqueStorageKey: this.endpointId } });
188
+ }
189
+ disable() {
190
+ this.log.info(`Disable charging (endpoint ${this.endpointId}.${this.endpointNumber})`);
191
+ this.commandHandler.executeHandler('disable', { request: {}, attributes: {}, endpoint: { number: this.endpointNumber, uniqueStorageKey: this.endpointId } });
192
+ }
192
193
  }
193
194
  export class MatterbridgeServer extends Behavior {
194
195
  static id = 'matterbridge';
@@ -495,80 +496,6 @@ export class MatterbridgeOperationalStateServer extends OperationalStateServer {
495
496
  };
496
497
  }
497
498
  }
498
- export class MatterbridgeRvcRunModeServer extends RvcRunModeServer {
499
- changeToMode({ newMode }) {
500
- const device = this.endpoint.stateOf(MatterbridgeServer).deviceCommand;
501
- const supported = this.state.supportedModes.find((mode) => mode.mode === newMode);
502
- if (!supported) {
503
- device.log.error(`MatterbridgeRvcRunModeServer changeToMode called with unsupported newMode: ${newMode}`);
504
- return { status: ModeBase.ModeChangeStatus.UnsupportedMode, statusText: 'Unsupported mode' };
505
- }
506
- device.changeToMode({ newMode });
507
- this.state.currentMode = newMode;
508
- if (supported.modeTags.find((tag) => tag.value === RvcRunMode.ModeTag.Cleaning)) {
509
- device.log.info('MatterbridgeRvcRunModeServer changeToMode called with newMode Cleaning => Running');
510
- this.agent.get(MatterbridgeRvcOperationalStateServer).state.operationalState = RvcOperationalState.OperationalState.Running;
511
- return { status: ModeBase.ModeChangeStatus.Success, statusText: 'Running' };
512
- }
513
- else if (supported.modeTags.find((tag) => tag.value === RvcRunMode.ModeTag.Idle)) {
514
- device.log.info('MatterbridgeRvcRunModeServer changeToMode called with newMode Idle => Docked');
515
- this.agent.get(MatterbridgeRvcOperationalStateServer).state.operationalState = RvcOperationalState.OperationalState.Docked;
516
- return { status: ModeBase.ModeChangeStatus.Success, statusText: 'Docked' };
517
- }
518
- device.log.info(`MatterbridgeRvcRunModeServer changeToMode called with newMode ${newMode} => ${supported.label}`);
519
- this.agent.get(MatterbridgeRvcOperationalStateServer).state.operationalState = RvcOperationalState.OperationalState.Running;
520
- return { status: ModeBase.ModeChangeStatus.Success, statusText: 'Success' };
521
- }
522
- }
523
- export class MatterbridgeRvcCleanModeServer extends RvcCleanModeServer {
524
- changeToMode({ newMode }) {
525
- const device = this.endpoint.stateOf(MatterbridgeServer).deviceCommand;
526
- const supported = this.state.supportedModes.find((mode) => mode.mode === newMode);
527
- if (!supported) {
528
- device.log.error(`MatterbridgeRvcCleanModeServer changeToMode called with unsupported newMode: ${newMode}`);
529
- return { status: ModeBase.ModeChangeStatus.UnsupportedMode, statusText: 'Unsupported mode' };
530
- }
531
- device.changeToMode({ newMode });
532
- this.state.currentMode = newMode;
533
- device.log.info(`MatterbridgeRvcCleanModeServer changeToMode called with newMode ${newMode} => ${supported.label}`);
534
- return { status: ModeBase.ModeChangeStatus.Success, statusText: 'Success' };
535
- }
536
- }
537
- export class MatterbridgeRvcOperationalStateServer extends RvcOperationalStateServer {
538
- pause() {
539
- const device = this.endpoint.stateOf(MatterbridgeServer).deviceCommand;
540
- device.log.info('MatterbridgeRvcOperationalStateServer: pause called setting operational state to Paused and currentMode to Idle');
541
- device.pause();
542
- this.agent.get(MatterbridgeRvcRunModeServer).state.currentMode = 1;
543
- this.state.operationalState = RvcOperationalState.OperationalState.Paused;
544
- this.state.operationalError = { errorStateId: RvcOperationalState.ErrorState.NoError, errorStateLabel: 'No Error', errorStateDetails: 'Fully operational' };
545
- return {
546
- commandResponseState: { errorStateId: OperationalState.ErrorState.NoError, errorStateLabel: 'No error', errorStateDetails: 'Fully operational' },
547
- };
548
- }
549
- resume() {
550
- const device = this.endpoint.stateOf(MatterbridgeServer).deviceCommand;
551
- device.log.info('MatterbridgeRvcOperationalStateServer: resume called setting operational state to Running and currentMode to Cleaning');
552
- device.resume();
553
- this.agent.get(MatterbridgeRvcRunModeServer).state.currentMode = 2;
554
- this.state.operationalState = RvcOperationalState.OperationalState.Running;
555
- this.state.operationalError = { errorStateId: RvcOperationalState.ErrorState.NoError, errorStateLabel: 'No Error', errorStateDetails: 'Fully operational' };
556
- return {
557
- commandResponseState: { errorStateId: OperationalState.ErrorState.NoError, errorStateLabel: 'No error', errorStateDetails: 'Fully operational' },
558
- };
559
- }
560
- goHome() {
561
- const device = this.endpoint.stateOf(MatterbridgeServer).deviceCommand;
562
- device.log.info('MatterbridgeRvcOperationalStateServer: goHome called setting operational state to Docked and currentMode to Idle');
563
- device.goHome();
564
- this.agent.get(MatterbridgeRvcRunModeServer).state.currentMode = 1;
565
- this.state.operationalState = RvcOperationalState.OperationalState.Docked;
566
- this.state.operationalError = { errorStateId: RvcOperationalState.ErrorState.NoError, errorStateLabel: 'No Error', errorStateDetails: 'Fully operational' };
567
- return {
568
- commandResponseState: { errorStateId: OperationalState.ErrorState.NoError, errorStateLabel: 'No error', errorStateDetails: 'Fully operational' },
569
- };
570
- }
571
- }
572
499
  export class MatterbridgeServiceAreaServer extends ServiceAreaServer {
573
500
  selectAreas({ newAreas }) {
574
501
  const device = this.endpoint.stateOf(MatterbridgeServer).deviceCommand;
@@ -585,31 +512,17 @@ export class MatterbridgeServiceAreaServer extends ServiceAreaServer {
585
512
  return { status: ServiceArea.SelectAreasStatus.Success, statusText: 'Succesfully selected new areas' };
586
513
  }
587
514
  }
588
- export class MatterbridgeWaterHeaterManagementServer extends WaterHeaterManagementServer {
589
- boost({ boostInfo }) {
590
- const device = this.endpoint.stateOf(MatterbridgeServer).deviceCommand;
591
- device.boost({ boostInfo });
592
- device.log.info(`MatterbridgeWaterHeaterManagementServer boost called with: ${JSON.stringify(boostInfo)}`);
593
- this.state.boostState = WaterHeaterManagement.BoostState.Active;
594
- }
595
- cancelBoost() {
596
- const device = this.endpoint.stateOf(MatterbridgeServer).deviceCommand;
597
- device.cancelBoost();
598
- device.log.info(`MatterbridgeWaterHeaterManagementServer cancelBoost called`);
599
- this.state.boostState = WaterHeaterManagement.BoostState.Inactive;
600
- }
601
- }
602
- export class MatterbridgeWaterHeaterModeServer extends WaterHeaterModeServer {
515
+ export class MatterbridgeDeviceEnergyManagementModeServer extends DeviceEnergyManagementModeServer {
603
516
  changeToMode({ newMode }) {
604
517
  const device = this.endpoint.stateOf(MatterbridgeServer).deviceCommand;
605
518
  const supported = this.state.supportedModes.find((mode) => mode.mode === newMode);
606
519
  if (!supported) {
607
- device.log.error(`MatterbridgeWaterHeaterModeServer changeToMode called with unsupported newMode: ${newMode}`);
520
+ device.log.error(`MatterbridgeDeviceEnergyManagementModeServer changeToMode called with unsupported newMode: ${newMode}`);
608
521
  return { status: ModeBase.ModeChangeStatus.UnsupportedMode, statusText: 'Unsupported mode' };
609
522
  }
610
523
  device.changeToMode({ newMode });
611
524
  this.state.currentMode = newMode;
612
- device.log.info(`MatterbridgeWaterHeaterModeServer changeToMode called with newMode ${newMode} => ${supported.label}`);
525
+ device.log.info(`MatterbridgeDeviceEnergyManagementModeServer changeToMode called with newMode ${newMode} => ${supported.label}`);
613
526
  return { status: ModeBase.ModeChangeStatus.Success, statusText: 'Success' };
614
527
  }
615
528
  }
@@ -1,7 +1,7 @@
1
1
  import { AnsiLogger, CYAN, YELLOW, db, debugStringify, hk, or, zb } from './logger/export.js';
2
2
  import { bridgedNode } from './matterbridgeDeviceTypes.js';
3
3
  import { isValidNumber, isValidObject, isValidString } from './utils/export.js';
4
- import { MatterbridgeServer, MatterbridgeServerDevice, MatterbridgeIdentifyServer, MatterbridgeOnOffServer, MatterbridgeLevelControlServer, MatterbridgeColorControlServer, MatterbridgeLiftWindowCoveringServer, MatterbridgeLiftTiltWindowCoveringServer, MatterbridgeThermostatServer, MatterbridgeFanControlServer, MatterbridgeDoorLockServer, MatterbridgeModeSelectServer, MatterbridgeValveConfigurationAndControlServer, MatterbridgeSmokeCoAlarmServer, MatterbridgeBooleanStateConfigurationServer, MatterbridgeSwitchServer, MatterbridgeOperationalStateServer, } from './matterbridgeBehaviors.js';
4
+ import { MatterbridgeServer, MatterbridgeServerDevice, MatterbridgeIdentifyServer, MatterbridgeOnOffServer, MatterbridgeLevelControlServer, MatterbridgeColorControlServer, MatterbridgeLiftWindowCoveringServer, MatterbridgeLiftTiltWindowCoveringServer, MatterbridgeThermostatServer, MatterbridgeFanControlServer, MatterbridgeDoorLockServer, MatterbridgeModeSelectServer, MatterbridgeValveConfigurationAndControlServer, MatterbridgeSmokeCoAlarmServer, MatterbridgeBooleanStateConfigurationServer, MatterbridgeSwitchServer, MatterbridgeOperationalStateServer, MatterbridgeDeviceEnergyManagementModeServer, } from './matterbridgeBehaviors.js';
5
5
  import { addClusterServers, addFixedLabel, addOptionalClusterServers, addRequiredClusterServers, addUserLabel, createUniqueId, getBehavior, getBehaviourTypesFromClusterClientIds, getBehaviourTypesFromClusterServerIds, getDefaultOperationalStateClusterServer, getDefaultFlowMeasurementClusterServer, getDefaultIlluminanceMeasurementClusterServer, getDefaultPressureMeasurementClusterServer, getDefaultRelativeHumidityMeasurementClusterServer, getDefaultTemperatureMeasurementClusterServer, getDefaultOccupancySensingClusterServer, lowercaseFirstLetter, updateAttribute, getClusterId, getAttributeId, setAttribute, getAttribute, checkNotLatinCharacters, generateUniqueId, subscribeAttribute, invokeBehaviorCommand, triggerEvent, } from './matterbridgeEndpointHelpers.js';
6
6
  import { Endpoint, Lifecycle, MutableEndpoint, NamedHandler, SupportedBehaviors, UINT16_MAX, UINT32_MAX, VendorId } from '@matter/main';
7
7
  import { getClusterNameById, MeasurementType } from '@matter/main/types';
@@ -29,6 +29,8 @@ import { ConcentrationMeasurement } from '@matter/main/clusters/concentration-me
29
29
  import { OccupancySensing } from '@matter/main/clusters/occupancy-sensing';
30
30
  import { ThermostatUserInterfaceConfiguration } from '@matter/main/clusters/thermostat-user-interface-configuration';
31
31
  import { OperationalState } from '@matter/main/clusters/operational-state';
32
+ import { DeviceEnergyManagement } from '@matter/main/clusters/device-energy-management';
33
+ import { DeviceEnergyManagementMode } from '@matter/main/clusters/device-energy-management-mode';
32
34
  import { DescriptorServer } from '@matter/main/behaviors/descriptor';
33
35
  import { PowerSourceServer } from '@matter/main/behaviors/power-source';
34
36
  import { BridgedDeviceBasicInformationServer } from '@matter/main/behaviors/bridged-device-basic-information';
@@ -62,6 +64,7 @@ import { ResourceMonitoring } from '@matter/main/clusters/resource-monitoring';
62
64
  import { HepaFilterMonitoringServer } from '@matter/main/behaviors/hepa-filter-monitoring';
63
65
  import { ActivatedCarbonFilterMonitoringServer } from '@matter/main/behaviors/activated-carbon-filter-monitoring';
64
66
  import { ThermostatUserInterfaceConfigurationServer } from '@matter/main/behaviors/thermostat-user-interface-configuration';
67
+ import { DeviceEnergyManagementServer } from '@matter/main/behaviors/device-energy-management';
65
68
  export class MatterbridgeEndpoint extends Endpoint {
66
69
  static bridgeMode = '';
67
70
  static logLevel = "info";
@@ -139,8 +142,7 @@ export class MatterbridgeEndpoint extends Endpoint {
139
142
  else
140
143
  this.deviceTypes.set(firstDefinition.code, firstDefinition);
141
144
  this.log = new AnsiLogger({ logName: options.uniqueStorageKey ?? 'MatterbridgeEndpoint', logTimestampFormat: 4, logLevel: debug === true ? "debug" : MatterbridgeEndpoint.logLevel });
142
- this.log.debug(`${YELLOW}new${db} MatterbridgeEndpoint: ${zb}${'0x' + firstDefinition.code.toString(16).padStart(4, '0')}${db}-${zb}${firstDefinition.name}${db} ` +
143
- `id: ${CYAN}${options.uniqueStorageKey}${db} number: ${CYAN}${options.endpointId}${db} taglist: ${CYAN}${options.tagList ? debugStringify(options.tagList) : 'undefined'}${db}`);
145
+ this.log.debug(`${YELLOW}new${db} MatterbridgeEndpoint: ${zb}${'0x' + firstDefinition.code.toString(16).padStart(4, '0')}${db}-${zb}${firstDefinition.name}${db} id: ${CYAN}${options.uniqueStorageKey}${db} number: ${CYAN}${options.endpointId}${db} taglist: ${CYAN}${options.tagList ? debugStringify(options.tagList) : 'undefined'}${db}`);
144
146
  this.behaviors.require(MatterbridgeServer, { deviceCommand: new MatterbridgeServerDevice(this.log, this.commandHandler, undefined) });
145
147
  }
146
148
  static async loadInstance(definition, options = {}, debug = false) {
@@ -1041,6 +1043,42 @@ export class MatterbridgeEndpoint extends Endpoint {
1041
1043
  });
1042
1044
  return this;
1043
1045
  }
1046
+ createDefaultDeviceEnergyManagementCluster() {
1047
+ this.behaviors.require(DeviceEnergyManagementServer.with(DeviceEnergyManagement.Feature.PowerForecastReporting), {
1048
+ forecast: null,
1049
+ esaType: DeviceEnergyManagement.EsaType.Other,
1050
+ esaCanGenerate: false,
1051
+ esaState: DeviceEnergyManagement.EsaState.Offline,
1052
+ absMinPower: 0,
1053
+ absMaxPower: 0,
1054
+ });
1055
+ return this;
1056
+ }
1057
+ createDefaultDeviceEnergyManagementModeCluster(currentMode, supportedModes) {
1058
+ this.behaviors.require(MatterbridgeDeviceEnergyManagementModeServer, {
1059
+ supportedModes: supportedModes ?? [
1060
+ { label: 'No Energy Management (Forecast reporting only)', mode: 1, modeTags: [{ value: DeviceEnergyManagementMode.ModeTag.NoOptimization }] },
1061
+ {
1062
+ label: 'Device Energy Management',
1063
+ mode: 2,
1064
+ modeTags: [{ value: DeviceEnergyManagementMode.ModeTag.DeviceOptimization }, { value: DeviceEnergyManagementMode.ModeTag.LocalOptimization }],
1065
+ },
1066
+ {
1067
+ label: 'Home Energy Management',
1068
+ mode: 3,
1069
+ modeTags: [{ value: DeviceEnergyManagementMode.ModeTag.GridOptimization }, { value: DeviceEnergyManagementMode.ModeTag.LocalOptimization }],
1070
+ },
1071
+ { label: 'Grid Energy Managemen', mode: 4, modeTags: [{ value: DeviceEnergyManagementMode.ModeTag.GridOptimization }] },
1072
+ {
1073
+ label: 'Full Energy Management',
1074
+ mode: 5,
1075
+ modeTags: [{ value: DeviceEnergyManagementMode.ModeTag.DeviceOptimization }, { value: DeviceEnergyManagementMode.ModeTag.LocalOptimization }, { value: DeviceEnergyManagementMode.ModeTag.GridOptimization }],
1076
+ },
1077
+ ],
1078
+ currentMode: currentMode ?? 1,
1079
+ });
1080
+ return this;
1081
+ }
1044
1082
  createDefaultPowerTopologyClusterServer() {
1045
1083
  this.behaviors.require(PowerTopologyServer.with(PowerTopology.Feature.TreeTopology));
1046
1084
  return this;
@@ -1,7 +1,15 @@
1
1
  import { MatterbridgeEndpoint } from './matterbridgeEndpoint.js';
2
2
  import { roboticVacuumCleaner } from './matterbridgeDeviceTypes.js';
3
- import { MatterbridgeRvcCleanModeServer, MatterbridgeRvcOperationalStateServer, MatterbridgeRvcRunModeServer, MatterbridgeServiceAreaServer } from './matterbridgeBehaviors.js';
4
- import { PowerSource, RvcRunMode, RvcCleanMode, RvcOperationalState } from '@matter/main/clusters';
3
+ import { MatterbridgeServer, MatterbridgeServiceAreaServer } from './matterbridgeBehaviors.js';
4
+ import { RvcRunModeServer } from '@matter/main/behaviors/rvc-run-mode';
5
+ import { RvcOperationalStateServer } from '@matter/main/behaviors/rvc-operational-state';
6
+ import { RvcCleanModeServer } from '@matter/main/behaviors/rvc-clean-mode';
7
+ import { PowerSource } from '@matter/main/clusters/power-source';
8
+ import { RvcRunMode } from '@matter/main/clusters/rvc-run-mode';
9
+ import { RvcCleanMode } from '@matter/main/clusters/rvc-clean-mode';
10
+ import { RvcOperationalState } from '@matter/main/clusters/rvc-operational-state';
11
+ import { ModeBase } from '@matter/main/clusters/mode-base';
12
+ import { OperationalState } from '@matter/main/clusters/operational-state';
5
13
  export class RoboticVacuumCleaner extends MatterbridgeEndpoint {
6
14
  constructor(name, serial, currentRunMode, supportedRunModes, currentCleanMode, supportedCleanModes, currentPhase = null, phaseList = null, operationalState, operationalStateList, supportedAreas, selectedAreas, currentArea) {
7
15
  super(roboticVacuumCleaner, { uniqueStorageKey: `${name.replaceAll(' ', '')}-${serial.replaceAll(' ', '')}` }, true);
@@ -85,3 +93,77 @@ export class RoboticVacuumCleaner extends MatterbridgeEndpoint {
85
93
  return this;
86
94
  }
87
95
  }
96
+ export class MatterbridgeRvcRunModeServer extends RvcRunModeServer {
97
+ changeToMode({ newMode }) {
98
+ const device = this.endpoint.stateOf(MatterbridgeServer).deviceCommand;
99
+ const supported = this.state.supportedModes.find((mode) => mode.mode === newMode);
100
+ if (!supported) {
101
+ device.log.error(`MatterbridgeRvcRunModeServer changeToMode called with unsupported newMode: ${newMode}`);
102
+ return { status: ModeBase.ModeChangeStatus.UnsupportedMode, statusText: 'Unsupported mode' };
103
+ }
104
+ device.changeToMode({ newMode });
105
+ this.state.currentMode = newMode;
106
+ if (supported.modeTags.find((tag) => tag.value === RvcRunMode.ModeTag.Cleaning)) {
107
+ device.log.info('MatterbridgeRvcRunModeServer changeToMode called with newMode Cleaning => Running');
108
+ this.agent.get(MatterbridgeRvcOperationalStateServer).state.operationalState = RvcOperationalState.OperationalState.Running;
109
+ return { status: ModeBase.ModeChangeStatus.Success, statusText: 'Running' };
110
+ }
111
+ else if (supported.modeTags.find((tag) => tag.value === RvcRunMode.ModeTag.Idle)) {
112
+ device.log.info('MatterbridgeRvcRunModeServer changeToMode called with newMode Idle => Docked');
113
+ this.agent.get(MatterbridgeRvcOperationalStateServer).state.operationalState = RvcOperationalState.OperationalState.Docked;
114
+ return { status: ModeBase.ModeChangeStatus.Success, statusText: 'Docked' };
115
+ }
116
+ device.log.info(`MatterbridgeRvcRunModeServer changeToMode called with newMode ${newMode} => ${supported.label}`);
117
+ this.agent.get(MatterbridgeRvcOperationalStateServer).state.operationalState = RvcOperationalState.OperationalState.Running;
118
+ return { status: ModeBase.ModeChangeStatus.Success, statusText: 'Success' };
119
+ }
120
+ }
121
+ export class MatterbridgeRvcCleanModeServer extends RvcCleanModeServer {
122
+ changeToMode({ newMode }) {
123
+ const device = this.endpoint.stateOf(MatterbridgeServer).deviceCommand;
124
+ const supported = this.state.supportedModes.find((mode) => mode.mode === newMode);
125
+ if (!supported) {
126
+ device.log.error(`MatterbridgeRvcCleanModeServer changeToMode called with unsupported newMode: ${newMode}`);
127
+ return { status: ModeBase.ModeChangeStatus.UnsupportedMode, statusText: 'Unsupported mode' };
128
+ }
129
+ device.changeToMode({ newMode });
130
+ this.state.currentMode = newMode;
131
+ device.log.info(`MatterbridgeRvcCleanModeServer changeToMode called with newMode ${newMode} => ${supported.label}`);
132
+ return { status: ModeBase.ModeChangeStatus.Success, statusText: 'Success' };
133
+ }
134
+ }
135
+ export class MatterbridgeRvcOperationalStateServer extends RvcOperationalStateServer {
136
+ pause() {
137
+ const device = this.endpoint.stateOf(MatterbridgeServer).deviceCommand;
138
+ device.log.info('MatterbridgeRvcOperationalStateServer: pause called setting operational state to Paused and currentMode to Idle');
139
+ device.pause();
140
+ this.agent.get(MatterbridgeRvcRunModeServer).state.currentMode = 1;
141
+ this.state.operationalState = RvcOperationalState.OperationalState.Paused;
142
+ this.state.operationalError = { errorStateId: RvcOperationalState.ErrorState.NoError, errorStateLabel: 'No Error', errorStateDetails: 'Fully operational' };
143
+ return {
144
+ commandResponseState: { errorStateId: OperationalState.ErrorState.NoError, errorStateLabel: 'No error', errorStateDetails: 'Fully operational' },
145
+ };
146
+ }
147
+ resume() {
148
+ const device = this.endpoint.stateOf(MatterbridgeServer).deviceCommand;
149
+ device.log.info('MatterbridgeRvcOperationalStateServer: resume called setting operational state to Running and currentMode to Cleaning');
150
+ device.resume();
151
+ this.agent.get(MatterbridgeRvcRunModeServer).state.currentMode = 2;
152
+ this.state.operationalState = RvcOperationalState.OperationalState.Running;
153
+ this.state.operationalError = { errorStateId: RvcOperationalState.ErrorState.NoError, errorStateLabel: 'No Error', errorStateDetails: 'Fully operational' };
154
+ return {
155
+ commandResponseState: { errorStateId: OperationalState.ErrorState.NoError, errorStateLabel: 'No error', errorStateDetails: 'Fully operational' },
156
+ };
157
+ }
158
+ goHome() {
159
+ const device = this.endpoint.stateOf(MatterbridgeServer).deviceCommand;
160
+ device.log.info('MatterbridgeRvcOperationalStateServer: goHome called setting operational state to Docked and currentMode to Idle');
161
+ device.goHome();
162
+ this.agent.get(MatterbridgeRvcRunModeServer).state.currentMode = 1;
163
+ this.state.operationalState = RvcOperationalState.OperationalState.Docked;
164
+ this.state.operationalError = { errorStateId: RvcOperationalState.ErrorState.NoError, errorStateLabel: 'No Error', errorStateDetails: 'Fully operational' };
165
+ return {
166
+ commandResponseState: { errorStateId: OperationalState.ErrorState.NoError, errorStateLabel: 'No error', errorStateDetails: 'Fully operational' },
167
+ };
168
+ }
169
+ }
@@ -1,7 +1,11 @@
1
1
  import { MatterbridgeEndpoint } from './matterbridgeEndpoint.js';
2
2
  import { waterHeater } from './matterbridgeDeviceTypes.js';
3
- import { MatterbridgeWaterHeaterManagementServer, MatterbridgeWaterHeaterModeServer } from './matterbridgeBehaviors.js';
4
- import { WaterHeaterManagement, WaterHeaterMode } from '@matter/main/clusters';
3
+ import { MatterbridgeServer } from './matterbridgeBehaviors.js';
4
+ import { ModeBase } from '@matter/main/clusters/mode-base';
5
+ import { WaterHeaterManagement } from '@matter/main/clusters/water-heater-management';
6
+ import { WaterHeaterMode } from '@matter/main/clusters/water-heater-mode';
7
+ import { WaterHeaterManagementServer } from '@matter/main/behaviors/water-heater-management';
8
+ import { WaterHeaterModeServer } from '@matter/main/behaviors/water-heater-mode';
5
9
  export class WaterHeater extends MatterbridgeEndpoint {
6
10
  constructor(name, serial, waterTemperature = 50, targetWaterTemperature = 55, minHeatSetpointLimit = 20, maxHeatSetpointLimit = 80, heaterTypes = { immersionElement1: true }, tankPercentage = 90) {
7
11
  super(waterHeater, { uniqueStorageKey: `${name.replaceAll(' ', '')}-${serial.replaceAll(' ', '')}` }, true);
@@ -9,7 +13,7 @@ export class WaterHeater extends MatterbridgeEndpoint {
9
13
  .createDefaultBasicInformationClusterServer(name, serial, 0xfff1, 'Matterbridge', 0x8000, 'Matterbridge Water Heater')
10
14
  .createDefaultPowerSourceWiredClusterServer()
11
15
  .createDefaultHeatingThermostatClusterServer(waterTemperature, targetWaterTemperature, minHeatSetpointLimit, maxHeatSetpointLimit)
12
- .createDefaultWaterHeaterManagementClusterServer(heaterTypes, heaterTypes, tankPercentage)
16
+ .createDefaultWaterHeaterManagementClusterServer(heaterTypes, {}, tankPercentage)
13
17
  .createDefaultWaterHeaterModeClusterServer();
14
18
  }
15
19
  createDefaultWaterHeaterManagementClusterServer(heaterTypes, heatDemand, tankPercentage, boostState) {
@@ -43,3 +47,31 @@ export class WaterHeater extends MatterbridgeEndpoint {
43
47
  return this;
44
48
  }
45
49
  }
50
+ export class MatterbridgeWaterHeaterManagementServer extends WaterHeaterManagementServer {
51
+ boost({ boostInfo }) {
52
+ const device = this.endpoint.stateOf(MatterbridgeServer).deviceCommand;
53
+ device.boost({ boostInfo });
54
+ device.log.info(`MatterbridgeWaterHeaterManagementServer boost called with: ${JSON.stringify(boostInfo)}`);
55
+ this.state.boostState = WaterHeaterManagement.BoostState.Active;
56
+ }
57
+ cancelBoost() {
58
+ const device = this.endpoint.stateOf(MatterbridgeServer).deviceCommand;
59
+ device.cancelBoost();
60
+ device.log.info(`MatterbridgeWaterHeaterManagementServer cancelBoost called`);
61
+ this.state.boostState = WaterHeaterManagement.BoostState.Inactive;
62
+ }
63
+ }
64
+ export class MatterbridgeWaterHeaterModeServer extends WaterHeaterModeServer {
65
+ changeToMode({ newMode }) {
66
+ const device = this.endpoint.stateOf(MatterbridgeServer).deviceCommand;
67
+ const supported = this.state.supportedModes.find((mode) => mode.mode === newMode);
68
+ if (!supported) {
69
+ device.log.error(`MatterbridgeWaterHeaterModeServer changeToMode called with unsupported newMode: ${newMode}`);
70
+ return { status: ModeBase.ModeChangeStatus.UnsupportedMode, statusText: 'Unsupported mode' };
71
+ }
72
+ device.changeToMode({ newMode });
73
+ this.state.currentMode = newMode;
74
+ device.log.info(`MatterbridgeWaterHeaterModeServer changeToMode called with newMode ${newMode} => ${supported.label}`);
75
+ return { status: ModeBase.ModeChangeStatus.Success, statusText: 'Success' };
76
+ }
77
+ }
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "matterbridge",
3
- "version": "3.0.5-dev-20250607-86b467e",
3
+ "version": "3.0.5-dev-20250607-ef2d8e1",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "matterbridge",
9
- "version": "3.0.5-dev-20250607-86b467e",
9
+ "version": "3.0.5-dev-20250607-ef2d8e1",
10
10
  "license": "Apache-2.0",
11
11
  "dependencies": {
12
12
  "@matter/main": "0.14.0",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "matterbridge",
3
- "version": "3.0.5-dev-20250607-86b467e",
3
+ "version": "3.0.5-dev-20250607-ef2d8e1",
4
4
  "description": "Matterbridge plugin manager for Matter",
5
5
  "author": "https://github.com/Luligu",
6
6
  "license": "Apache-2.0",