matterbridge-example-dynamic-platform 1.2.1 → 1.2.2

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,6 +8,23 @@ 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
+ ## [1.2.2] - 2025-05-19
12
+
13
+ ### Changed
14
+
15
+ - [package]: Added waterHeater device type (not supported by Alexa and Apple Home)
16
+
17
+ ### Changed
18
+
19
+ - [package]: Changed the RVC from local implementation to the new RoboticVacuumCleaner class from matterbridge.
20
+ - [package]: Require matterbridge 3.0.3.
21
+ - [package]: Updated package.
22
+ - [package]: Updated dependencies.
23
+
24
+ <a href="https://www.buymeacoffee.com/luligugithub">
25
+ <img src="bmc-button.svg" alt="Buy me a coffee" width="80">
26
+ </a>
27
+
11
28
  ## [1.2.1] - 2025-05-15
12
29
 
13
30
  ### Changed
package/README.md CHANGED
@@ -57,6 +57,7 @@ It exposes 38 devices:
57
57
  - a microwave Oven device (supported by SmartThings, Alexa and Home Assistant)
58
58
  - an extractor Hood device (supported by SmartThings, Alexa and Home Assistant)
59
59
  - a cooktop device (supported by SmartThings, Alexa and Home Assistant)
60
+ - a water heater device (supported by SmartThings and Home Assistant)
60
61
 
61
62
  All these devices continuously change state and position. The plugin also shows how to use all the command handlers (you can control all the devices), how to subscribe to attributes and how to trigger events.
62
63
 
package/dist/platform.js CHANGED
@@ -1,9 +1,8 @@
1
- import { MatterbridgeEndpoint, MatterbridgeDynamicPlatform, airQualitySensor, bridgedNode, colorTemperatureLight, coverDevice, dimmableLight, doorLockDevice, fanDevice, flowSensor, humiditySensor, onOffLight, onOffOutlet, onOffSwitch, powerSource, rainSensor, smokeCoAlarm, temperatureSensor, thermostatDevice, waterFreezeDetector, waterLeakDetector, airPurifier, pumpDevice, waterValve, genericSwitch, airConditioner, laundryWasher, cooktop, extractorHood, microwaveOven, oven, refrigerator, dishwasher, laundryDryer, onOffMountedSwitch, dimmableMountedSwitch, extendedColorLight, } 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, airConditioner, laundryWasher, cooktop, extractorHood, microwaveOven, oven, refrigerator, dishwasher, laundryDryer, onOffMountedSwitch, dimmableMountedSwitch, extendedColorLight, RoboticVacuumCleaner, WaterHeater, } from 'matterbridge';
2
2
  import { isValidBoolean, isValidNumber } from 'matterbridge/utils';
3
3
  import { LocationTag } from 'matterbridge/matter';
4
4
  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, } from 'matterbridge/matter/clusters';
5
5
  import { Appliances } from './appliances.js';
6
- import { Robot } from './robot.js';
7
6
  export class ExampleMatterbridgeDynamicPlatform extends MatterbridgeDynamicPlatform {
8
7
  switch;
9
8
  mountedOnOffSwitch;
@@ -35,6 +34,7 @@ export class ExampleMatterbridgeDynamicPlatform extends MatterbridgeDynamicPlatf
35
34
  momentarySwitch;
36
35
  latchingSwitch;
37
36
  vacuum;
37
+ heater;
38
38
  switchInterval;
39
39
  lightInterval;
40
40
  outletInterval;
@@ -57,8 +57,8 @@ export class ExampleMatterbridgeDynamicPlatform extends MatterbridgeDynamicPlatf
57
57
  fanModeLookup = ['Off', 'Low', 'Medium', 'High', 'On', 'Auto', 'Smart'];
58
58
  constructor(matterbridge, log, config) {
59
59
  super(matterbridge, log, config);
60
- if (this.verifyMatterbridgeVersion === undefined || typeof this.verifyMatterbridgeVersion !== 'function' || !this.verifyMatterbridgeVersion('3.0.1')) {
61
- throw new Error(`This plugin requires Matterbridge version >= "3.0.1". Please update Matterbridge from ${this.matterbridge.matterbridgeVersion} to the latest version in the frontend.`);
60
+ if (this.verifyMatterbridgeVersion === undefined || typeof this.verifyMatterbridgeVersion !== 'function' || !this.verifyMatterbridgeVersion('3.0.3')) {
61
+ throw new Error(`This plugin requires Matterbridge version >= "3.0.3". Please update Matterbridge from ${this.matterbridge.matterbridgeVersion} to the latest version in the frontend.`);
62
62
  }
63
63
  this.log.info('Initializing platform:', this.config.name);
64
64
  if (config.whiteList === undefined)
@@ -718,11 +718,15 @@ export class ExampleMatterbridgeDynamicPlatform extends MatterbridgeDynamicPlatf
718
718
  });
719
719
  this.pump?.addCommandHandler('on', async () => {
720
720
  this.pump?.log.info('Command on called');
721
- await this.pump?.setAttribute(OnOff.Cluster.id, 'onOff', true, this.pump?.log);
722
721
  });
723
722
  this.pump?.addCommandHandler('off', async () => {
724
723
  this.pump?.log.info('Command off called');
725
- await this.pump?.setAttribute(OnOff.Cluster.id, 'onOff', false, this.pump?.log);
724
+ });
725
+ this.pump?.addCommandHandler('moveToLevel', async ({ request: { level } }) => {
726
+ this.pump?.log.info(`Command moveToLevel called request: ${level}`);
727
+ });
728
+ this.pump?.addCommandHandler('moveToLevelWithOnOff', async ({ request: { level } }) => {
729
+ this.pump?.log.info(`Command moveToLevelWithOnOff called request: ${level}`);
726
730
  });
727
731
  this.valve = new MatterbridgeEndpoint([waterValve, bridgedNode, powerSource], { uniqueStorageKey: 'Water valve' }, this.config.debug)
728
732
  .createDefaultBridgedDeviceBasicInformationClusterServer('Water valve', '0x96382864WV', 0xfff1, 'Matterbridge', 'Matterbridge Water valve', parseInt(this.version.replace(/\D/g, '')), this.version === '' ? 'Unknown' : this.version, parseInt(this.matterbridge.matterbridgeVersion.replace(/\D/g, '')), this.matterbridge.matterbridgeVersion)
@@ -922,13 +926,19 @@ export class ExampleMatterbridgeDynamicPlatform extends MatterbridgeDynamicPlatf
922
926
  this.latchingSwitch = undefined;
923
927
  }
924
928
  if (this.config.enableRVC === true) {
925
- const robot = new Robot('Robot Vacuum', '1238777820');
929
+ const robot = new RoboticVacuumCleaner('Robot Vacuum', '1238777820');
926
930
  this.setSelectDevice(robot.serialNumber ?? '', robot.deviceName ?? '', undefined, 'hub');
927
931
  if (this.validateDevice(robot.deviceName ?? '')) {
928
932
  await this.registerDevice(robot);
929
933
  this.bridgedDevices.set(robot.deviceName ?? '', robot);
930
934
  }
931
935
  }
936
+ const heater = new WaterHeater('Water Heater', '3456177820');
937
+ this.setSelectDevice(heater.serialNumber ?? '', heater.deviceName ?? '', undefined, 'hub');
938
+ if (this.validateDevice(heater.deviceName ?? '')) {
939
+ await this.registerDevice(heater);
940
+ this.bridgedDevices.set(heater.deviceName ?? '', heater);
941
+ }
932
942
  const laundryWasherDevice = new Appliances(laundryWasher, 'Laundry Washer', '1234567890');
933
943
  this.setSelectDevice(laundryWasherDevice.serialNumber ?? '', laundryWasherDevice.deviceName ?? '', undefined, 'hub');
934
944
  if (this.validateDevice(laundryWasherDevice.deviceName ?? '')) {
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "matterbridge-example-dynamic-platform",
3
- "version": "1.2.1",
3
+ "version": "1.2.2",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "matterbridge-example-dynamic-platform",
9
- "version": "1.2.1",
9
+ "version": "1.2.2",
10
10
  "license": "MIT",
11
11
  "dependencies": {
12
12
  "node-ansi-logger": "3.0.1",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "matterbridge-example-dynamic-platform",
3
- "version": "1.2.1",
3
+ "version": "1.2.2",
4
4
  "description": "Matterbridge dynamic plugin",
5
5
  "author": "https://github.com/Luligu",
6
6
  "license": "MIT",
package/dist/robot.js DELETED
@@ -1,210 +0,0 @@
1
- import { Matterbridge, MatterbridgeServer, MatterbridgeEndpoint, roboticVacuumCleaner, dishwasher } from 'matterbridge';
2
- import { LogLevel as MatterLogLevel, LogFormat as MatterLogFormat, DeviceTypeId, VendorId } from 'matterbridge/matter';
3
- import { ModeBase, OperationalState, PowerSource, RvcRunMode, RvcCleanMode, RvcOperationalState, ServiceArea } from 'matterbridge/matter/clusters';
4
- import { ActionsServer, RvcCleanModeBehavior, RvcOperationalStateBehavior, RvcRunModeBehavior, ServiceAreaBehavior } from 'matterbridge/matter/behaviors';
5
- import { AnsiLogger } from 'matterbridge/logger';
6
- export class Robot extends MatterbridgeEndpoint {
7
- constructor(name, serial) {
8
- super(roboticVacuumCleaner, { uniqueStorageKey: `${name}-${serial}` }, true);
9
- this.createDefaultIdentifyClusterServer()
10
- .createDefaultBasicInformationClusterServer(name, serial, 0xfff1, 'Matterbridge', 0x8000, 'Matterbridge Robot Vacuum Cleaner')
11
- .createDefaultRvcRunModeClusterServer()
12
- .createDefaultRvcOperationalStateClusterServer()
13
- .createDefaultRvcCleanModeClusterServer()
14
- .createDefaultServiceAreaClusterServer()
15
- .createDefaultPowerSourceRechargeableBatteryClusterServer(80, PowerSource.BatChargeLevel.Ok, 5900);
16
- }
17
- createDefaultRvcRunModeClusterServer(currentMode, supportedModes) {
18
- this.behaviors.require(MatterbridgeRvcRunModeServer, {
19
- supportedModes: supportedModes ?? [
20
- { label: 'Idle', mode: 1, modeTags: [{ value: RvcRunMode.ModeTag.Idle }] },
21
- { label: 'Cleaning', mode: 2, modeTags: [{ value: RvcRunMode.ModeTag.Cleaning }] },
22
- { label: 'Mapping', mode: 3, modeTags: [{ value: RvcRunMode.ModeTag.Mapping }] },
23
- { label: 'SpotCleaning', mode: 4, modeTags: [{ value: RvcRunMode.ModeTag.Cleaning }, { value: RvcRunMode.ModeTag.Max }] },
24
- ],
25
- currentMode: currentMode ?? 1,
26
- });
27
- return this;
28
- }
29
- createDefaultRvcCleanModeClusterServer(currentMode, supportedModes) {
30
- this.behaviors.require(MatterbridgeRvcCleanModeServer, {
31
- supportedModes: supportedModes ?? [
32
- { label: 'Vacuum', mode: 1, modeTags: [{ value: RvcCleanMode.ModeTag.Vacuum }] },
33
- { label: 'Mop', mode: 2, modeTags: [{ value: RvcCleanMode.ModeTag.Mop }] },
34
- { label: 'Clean', mode: 3, modeTags: [{ value: RvcCleanMode.ModeTag.DeepClean }] },
35
- ],
36
- currentMode: currentMode ?? 1,
37
- });
38
- return this;
39
- }
40
- createDefaultServiceAreaClusterServer(supportedAreas, selectedAreas) {
41
- this.behaviors.require(MatterbridgeServiceAreaServer, {
42
- supportedAreas: supportedAreas ?? [
43
- {
44
- areaId: 1,
45
- mapId: null,
46
- areaInfo: { locationInfo: { locationName: 'Living', floorNumber: null, areaType: null }, landmarkInfo: null },
47
- },
48
- {
49
- areaId: 2,
50
- mapId: null,
51
- areaInfo: { locationInfo: { locationName: 'Kitchen', floorNumber: null, areaType: null }, landmarkInfo: null },
52
- },
53
- {
54
- areaId: 3,
55
- mapId: null,
56
- areaInfo: { locationInfo: { locationName: 'Bedroom', floorNumber: null, areaType: null }, landmarkInfo: null },
57
- },
58
- {
59
- areaId: 4,
60
- mapId: null,
61
- areaInfo: { locationInfo: { locationName: 'Bathroom', floorNumber: null, areaType: null }, landmarkInfo: null },
62
- },
63
- ],
64
- selectedAreas: selectedAreas ?? [],
65
- currentArea: 1,
66
- estimatedEndTime: null,
67
- });
68
- return this;
69
- }
70
- createDefaultRvcOperationalStateClusterServer(phaseList = null, currentPhase = null, operationalStateList, operationalState, operationalError) {
71
- this.behaviors.require(MatterbridgeRvcOperationalStateServer, {
72
- phaseList,
73
- currentPhase,
74
- operationalStateList: operationalStateList ?? [
75
- { operationalStateId: RvcOperationalState.OperationalState.Stopped, operationalStateLabel: 'Stopped' },
76
- { operationalStateId: RvcOperationalState.OperationalState.Running, operationalStateLabel: 'Running' },
77
- { operationalStateId: RvcOperationalState.OperationalState.Paused, operationalStateLabel: 'Paused' },
78
- { operationalStateId: RvcOperationalState.OperationalState.Error, operationalStateLabel: 'Error' },
79
- { operationalStateId: RvcOperationalState.OperationalState.SeekingCharger, operationalStateLabel: 'SeekingCharger' },
80
- { operationalStateId: RvcOperationalState.OperationalState.Charging, operationalStateLabel: 'Charging' },
81
- { operationalStateId: RvcOperationalState.OperationalState.Docked, operationalStateLabel: 'Docked' },
82
- ],
83
- operationalState: operationalState ?? RvcOperationalState.OperationalState.Docked,
84
- operationalError: operationalError ?? { errorStateId: RvcOperationalState.ErrorState.NoError, errorStateLabel: 'No Error', errorStateDetails: 'Fully operational' },
85
- });
86
- return this;
87
- }
88
- }
89
- export class MatterbridgeServiceAreaServer extends ServiceAreaBehavior {
90
- initialize() {
91
- }
92
- selectAreas({ newAreas }) {
93
- const device = this.endpoint.stateOf(MatterbridgeServer).deviceCommand;
94
- for (const area of newAreas) {
95
- const supportedArea = this.state.supportedAreas.find((supportedArea) => supportedArea.areaId === area);
96
- if (!supportedArea) {
97
- device.log.error('MatterbridgeServiceAreaServer selectAreas called with unsupported area:', area);
98
- return { status: ServiceArea.SelectAreasStatus.UnsupportedArea, statusText: 'Unsupported areas' };
99
- }
100
- }
101
- this.state.selectedAreas = newAreas;
102
- this.state.currentArea = newAreas[0];
103
- device.log.info(`***MatterbridgeServiceAreaServer selectAreas called with: ${newAreas.map((area) => area.toString()).join(', ')}`);
104
- return { status: ServiceArea.SelectAreasStatus.Success, statusText: 'Succesfully selected new areas' };
105
- }
106
- }
107
- export class MatterbridgeRvcRunModeServer extends RvcRunModeBehavior {
108
- initialize() {
109
- this.state.currentMode = 1;
110
- }
111
- changeToMode({ newMode }) {
112
- const device = this.endpoint.stateOf(MatterbridgeServer).deviceCommand;
113
- const changedMode = this.state.supportedModes.find((mode) => mode.mode === newMode);
114
- if (!changedMode) {
115
- device.log.error('MatterbridgeRvcRunModeServer changeToMode called with unsupported newMode:', newMode);
116
- return { status: ModeBase.ModeChangeStatus.InvalidInMode, statusText: 'Invalid mode' };
117
- }
118
- device.changeToMode({ newMode });
119
- this.state.currentMode = newMode;
120
- if (changedMode.modeTags.find((tag) => tag.value === RvcRunMode.ModeTag.Cleaning)) {
121
- device.log.info('***MatterbridgeRvcRunModeServer changeToMode called with newMode Cleaning => Running');
122
- this.agent.get(MatterbridgeRvcOperationalStateServer).state.operationalState = RvcOperationalState.OperationalState.Running;
123
- return { status: ModeBase.ModeChangeStatus.Success, statusText: 'Running' };
124
- }
125
- else if (changedMode.modeTags.find((tag) => tag.value === RvcRunMode.ModeTag.Idle)) {
126
- device.log.info('***MatterbridgeRvcRunModeServer changeToMode called with newMode Idle => Docked');
127
- this.agent.get(MatterbridgeRvcOperationalStateServer).state.operationalState = RvcOperationalState.OperationalState.Docked;
128
- return { status: ModeBase.ModeChangeStatus.Success, statusText: 'Docked' };
129
- }
130
- device.log.info(`***MatterbridgeRvcRunModeServer changeToMode called with newMode ${newMode} => ${changedMode.label}`);
131
- this.agent.get(MatterbridgeRvcOperationalStateServer).state.operationalState = RvcOperationalState.OperationalState.Running;
132
- return { status: ModeBase.ModeChangeStatus.Success, statusText: 'Success' };
133
- }
134
- }
135
- export class MatterbridgeRvcCleanModeServer extends RvcCleanModeBehavior {
136
- initialize() {
137
- this.state.currentMode = 1;
138
- }
139
- changeToMode({ newMode }) {
140
- const device = this.endpoint.stateOf(MatterbridgeServer).deviceCommand;
141
- const supported = this.state.supportedModes.find((mode) => mode.mode === newMode);
142
- if (!supported) {
143
- device.log.error('***MatterbridgeRvcCleanModeServer changeToMode called with unsupported newMode:', newMode);
144
- return { status: ModeBase.ModeChangeStatus.InvalidInMode, statusText: 'Invalid mode' };
145
- }
146
- device.changeToMode({ newMode });
147
- this.state.currentMode = newMode;
148
- device.log.info(`***MatterbridgeRvcCleanModeServer changeToMode called with newMode ${newMode} => ${supported.label}`);
149
- return { status: ModeBase.ModeChangeStatus.Success, statusText: 'Success' };
150
- }
151
- }
152
- export class MatterbridgeRvcOperationalStateServer extends RvcOperationalStateBehavior {
153
- initialize() {
154
- const device = this.endpoint.stateOf(MatterbridgeServer).deviceCommand;
155
- device.log.info('***MatterbridgeRvcOperationalStateServer initialized: setting operational state to Docked');
156
- this.state.operationalState = RvcOperationalState.OperationalState.Docked;
157
- this.state.operationalError = { errorStateId: RvcOperationalState.ErrorState.NoError, errorStateLabel: 'No Error', errorStateDetails: 'Fully operational' };
158
- }
159
- pause() {
160
- const device = this.endpoint.stateOf(MatterbridgeServer).deviceCommand;
161
- device.log.info('MatterbridgeRvcOperationalStateServer: pause called setting operational state to Paused and currentMode to Idle');
162
- this.agent.get(MatterbridgeRvcRunModeServer).state.currentMode = 1;
163
- this.state.operationalState = RvcOperationalState.OperationalState.Paused;
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
- resume() {
170
- const device = this.endpoint.stateOf(MatterbridgeServer).deviceCommand;
171
- device.log.info('MatterbridgeRvcOperationalStateServer: resume called setting operational state to Running and currentMode to Cleaning');
172
- this.agent.get(MatterbridgeRvcRunModeServer).state.currentMode = 2;
173
- this.state.operationalState = RvcOperationalState.OperationalState.Running;
174
- this.state.operationalError = { errorStateId: RvcOperationalState.ErrorState.NoError, errorStateLabel: 'No Error', errorStateDetails: 'Fully operational' };
175
- return {
176
- commandResponseState: { errorStateId: OperationalState.ErrorState.NoError, errorStateLabel: 'No error', errorStateDetails: 'Fully operational' },
177
- };
178
- }
179
- goHome() {
180
- const device = this.endpoint.stateOf(MatterbridgeServer).deviceCommand;
181
- device.log.info('MatterbridgeRvcOperationalStateServer: goHome called setting operational state to Docked and currentMode to Idle');
182
- this.agent.get(MatterbridgeRvcRunModeServer).state.currentMode = 1;
183
- this.state.operationalState = RvcOperationalState.OperationalState.Docked;
184
- this.state.operationalError = { errorStateId: RvcOperationalState.ErrorState.NoError, errorStateLabel: 'No Error', errorStateDetails: 'Fully operational' };
185
- return {
186
- commandResponseState: { errorStateId: OperationalState.ErrorState.NoError, errorStateLabel: 'No error', errorStateDetails: 'Fully operational' },
187
- };
188
- }
189
- }
190
- function createEndpointActionsClusterServer(endpoint, endpointLists) {
191
- endpoint.behaviors.require(ActionsServer, {
192
- actionList: [],
193
- endpointLists,
194
- });
195
- return endpoint;
196
- }
197
- if (process.argv.includes('-testRobot')) {
198
- const matterbridge = await Matterbridge.loadInstance(false);
199
- matterbridge.log = new AnsiLogger({ logName: 'Matterbridge', logTimestampFormat: 4, logLevel: "debug" });
200
- matterbridge.environment.vars.set('log.level', MatterLogLevel.DEBUG);
201
- matterbridge.environment.vars.set('log.format', MatterLogFormat.ANSI);
202
- matterbridge.environment.vars.set('path.root', 'matterstorage');
203
- matterbridge.environment.vars.set('runtime.signals', true);
204
- matterbridge.environment.vars.set('runtime.exitcode', true);
205
- matterbridge.environment.vars.set('mdns.networkInterface', 'Wi-Fi');
206
- await matterbridge.startMatterStorage();
207
- const deviceType = dishwasher;
208
- const context = await matterbridge.createServerNodeContext('Matterbridge', deviceType.name, DeviceTypeId(deviceType.code), VendorId(0xfff1), 'Matterbridge', 0x8000, 'Matterbridge device');
209
- const server = (await matterbridge.createServerNode(context));
210
- }