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 +2 -1
- package/dist/evse.js +85 -0
- package/dist/index.js +3 -1
- package/dist/matterbridgeBehaviors.js +12 -99
- package/dist/matterbridgeEndpoint.js +41 -3
- package/dist/roboticVacuumCleaner.js +84 -2
- package/dist/waterHeater.js +35 -3
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -1
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-
|
|
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
|
|
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 {
|
|
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
|
|
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(`
|
|
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(`
|
|
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 {
|
|
4
|
-
import {
|
|
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
|
+
}
|
package/dist/waterHeater.js
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
import { MatterbridgeEndpoint } from './matterbridgeEndpoint.js';
|
|
2
2
|
import { waterHeater } from './matterbridgeDeviceTypes.js';
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
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,
|
|
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
|
+
}
|
package/npm-shrinkwrap.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "matterbridge",
|
|
3
|
-
"version": "3.0.5-dev-20250607-
|
|
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-
|
|
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