matterbridge-example-dynamic-platform 3.0.2-dev-20260808-db970b3 → 3.0.2-dev-20260809-1aeac01

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
@@ -31,14 +31,16 @@ If you like this project and find it useful, please consider giving it a star on
31
31
 
32
32
  ### Breaking changes
33
33
 
34
- - [matterbridge]: Require matterbridge v.3.10.3 with matter v.1.6.0.
34
+ - [matterbridge]: Require matterbridge v.3.10.5 with matter v.1.6.0.
35
35
 
36
36
  ### Added
37
37
 
38
38
  - [chip]: Add chip-test toolchain agents instruction and chip-test runner.
39
+ - [chip]: Add `chipTests.json`/`chipTests.md` with the generic device basic composition, conformance and default warnings tests, and document known CHIP test suite gaps.
39
40
  - [frontend]: Add plugin-frontend agents instructions.
40
- - [platform]: Add `closureGarageDoor` device.
41
- - [platform]: Add `closureVenetianBlind` device with Lift and Tilt panels.
41
+ - [platform]: Add `closureGarageDoor` device. Thanks Ludovic BOUÉ.
42
+ - [platform]: Add `closureVenetianBlind` device with Lift and Tilt panels. Thanks Ludovic BOUÉ.
43
+ - [platform]: Add a thermostat auto mode with schedules (Weekdays and Weekend schedules) including 2 sub endpoints with temperature and humidity sensors, using `createDefaultSchedulesThermostatClusterServer()`. Thanks Ludovic BOUÉ.
42
44
 
43
45
  ### Changed
44
46
 
package/README.md CHANGED
@@ -23,7 +23,7 @@
23
23
 
24
24
  Matterbridge dynamic platform example plugin is a template to develop your own plugin using the dynamic platform.
25
25
 
26
- It exposes 72 virtual devices:
26
+ It exposes 73 virtual devices:
27
27
 
28
28
  - a door contact sensor
29
29
  - a motion sensor
@@ -53,6 +53,7 @@ It exposes 72 virtual devices:
53
53
  and relativeHumidityMeasurement cluster (to show how to create a composed device with sub endpoints)
54
54
  - a thermostat with auto mode (i.e. with Auto Heat and Cool features), occupancy and outdoorTemperature
55
55
  - a thermostat auto mode with presets (Home, Away, Sleep, Wake, Vacation and GoingToSleep modes) including 3 sub endpoints with flow, temperature and humidity sensors
56
+ - a thermostat auto mode with schedules (Weekdays and Weekend schedules) including 2 sub endpoints with temperature and humidity sensors
56
57
  - a thermostat heat only with two external temperature sensors (tagged like Indoor and Outdoor)
57
58
  - a thermostat cool only
58
59
  - a fan with Off High presets
@@ -100,6 +101,8 @@ It exposes 72 virtual devices:
100
101
 
101
102
  All these devices continuously change their state and position. The plugin also shows how to use all the command handlers (so you can control all the devices), how to subscribe to attributes, and how to trigger events.
102
103
 
104
+ All devices pass the generic Matter conformance test suite (`TC_DeviceBasicComposition.py`, `TC_DeviceConformance.py` and `TC_DefaultWarnings.py`) run via the CHIP test harness, with one known upstream test-suite gap for the Closure devices — see [chipTests.md](./chipTests.md) for details and how to run the suite against this plugin.
105
+
103
106
  If you want to write your own plugin, the easiest way to get started is to clone the [Matterbridge Plugin Template](https://github.com/Luligu/matterbridge-plugin-template). It has **Dev Container support for an instant development environment**, with all tools and extensions (like Node.js, npm, TypeScript, ESLint, Prettier, Jest, and Vitest) already loaded and configured.
104
107
 
105
108
  If you like this project and find it useful, please consider giving it a star on [GitHub](https://github.com/Luligu/matterbridge-example-dynamic-platform) and sponsoring it.
package/dist/module.js CHANGED
@@ -61,6 +61,7 @@ export class ExampleMatterbridgeDynamicPlatform extends MatterbridgeDynamicPlatf
61
61
  thermoAuto;
62
62
  thermoAutoOccupancy;
63
63
  thermoAutoPresets;
64
+ thermoAutoSchedules;
64
65
  thermoHeat;
65
66
  thermoCool;
66
67
  fanBase;
@@ -108,8 +109,8 @@ export class ExampleMatterbridgeDynamicPlatform extends MatterbridgeDynamicPlatf
108
109
  constructor(matterbridge, log, config) {
109
110
  super(matterbridge, log, config);
110
111
  this.config = config;
111
- if (typeof this.verifyMatterbridgeVersion !== 'function' || !this.verifyMatterbridgeVersion('3.10.3')) {
112
- throw new Error(`This plugin requires Matterbridge version >= "3.10.3". Please update Matterbridge from ${this.matterbridge.matterbridgeVersion} to the latest version in the frontend.`);
112
+ if (typeof this.verifyMatterbridgeVersion !== 'function' || !this.verifyMatterbridgeVersion('3.10.5')) {
113
+ throw new Error(`This plugin requires Matterbridge version >= "3.10.5". Please update Matterbridge from ${this.matterbridge.matterbridgeVersion} to the latest version in the frontend.`);
113
114
  }
114
115
  this.log.info('Initializing platform:', this.config.name);
115
116
  }
@@ -168,7 +169,7 @@ export class ExampleMatterbridgeDynamicPlatform extends MatterbridgeDynamicPlatf
168
169
  this.flow = await this.addDevice(this.flow);
169
170
  this.soil = new SoilSensor('Soil Sensor', 'SOI000067', { soilMoistureMeasuredValue: 45, temperatureMeasuredValue: 3500, batteryPowered: true });
170
171
  this.soil = await this.addDevice(this.soil);
171
- this.irrigation = new IrrigationSystem('Irrigation System', 'IRR000068', { singleZone: true, batteryPowered: true, flowMeasuredValue: 15 });
172
+ this.irrigation = new IrrigationSystem('Irrigation System', 'IRR000068', { batteryPowered: true, flowMeasuredValue: 15 }).addZone(CommonNumberTag.One);
172
173
  this.irrigation = (await this.addDevice(this.irrigation));
173
174
  this.irrigationSystem = new IrrigationSystem('Irrigation System 4 zones', 'IRR000069', { flowMeasuredValue: 60 })
174
175
  .addZone(CommonNumberTag.One)
@@ -217,8 +218,8 @@ export class ExampleMatterbridgeDynamicPlatform extends MatterbridgeDynamicPlatf
217
218
  this.closureVenetianBlind = new Closure('Venetian Blind', 'VEN000072', {
218
219
  tagList: [getSemtag(ClosureTag.Covering), getSemtag(ClosureCoveringTag.Venetian)],
219
220
  });
220
- const closureVenetianBlindLift = this.closureVenetianBlind.addPanel('Lift', [getSemtag(ClosurePanelTag.Lift)]);
221
- const closureVenetianBlindTilt = this.closureVenetianBlind.addPanel('Tilt', [getSemtag(ClosurePanelTag.Tilt)]);
221
+ const closureVenetianBlindLift = this.closureVenetianBlind.addPanel('Lift', [getSemtag(ClosurePanelTag.Lift)], 'lift');
222
+ const closureVenetianBlindTilt = this.closureVenetianBlind.addPanel('Tilt', [getSemtag(ClosurePanelTag.Tilt)], 'tilt');
222
223
  this.closureVenetianBlind = (await this.addDevice(this.closureVenetianBlind));
223
224
  const closurePanelTargetPercent = (position) => {
224
225
  if (position === ClosureControl.TargetPosition.MoveToFullyOpen)
@@ -325,9 +326,11 @@ export class ExampleMatterbridgeDynamicPlatform extends MatterbridgeDynamicPlatf
325
326
  this.switch = new MatterbridgeEndpoint([onOffLightSwitch, bridgedNode, powerSource], { id: 'Switch' }, this.config.debug)
326
327
  .createDefaultIdentifyClusterServer()
327
328
  .createDefaultBridgedDeviceBasicInformationClusterServer('Switch', 'SWI00010', 0xfff1, 'Matterbridge', 'Matterbridge Switch')
328
- .createDefaultOnOffClusterServer()
329
329
  .createDefaultPowerSourceWiredClusterServer()
330
330
  .addRequiredClusters();
331
+ if (process.env.MATTERBRIDGE_CHIP_TEST !== '1') {
332
+ this.switch.createDefaultOnOffClusterServer();
333
+ }
331
334
  this.switch = await this.addDevice(this.switch);
332
335
  this.switch?.addCommandHandler('identify', ({ request: { identifyTime } }) => {
333
336
  this.log.info(`Command identify called identifyTime:${identifyTime}`);
@@ -1075,6 +1078,83 @@ export class ExampleMatterbridgeDynamicPlatform extends MatterbridgeDynamicPlatf
1075
1078
  this.thermoAutoPresets?.subscribeAttribute(Thermostat.id, 'presets', (newValue, oldValue) => {
1076
1079
  this.thermoAutoPresets?.log.info(`Subscribe presets called with: ${debugStringify(newValue)} (old value: ${debugStringify(oldValue)})`);
1077
1080
  }, this.thermoAutoPresets.log);
1081
+ const schedules_List = [
1082
+ {
1083
+ scheduleHandle: new Uint8Array([0]),
1084
+ systemMode: Thermostat.SystemMode.Auto,
1085
+ name: 'Weekdays',
1086
+ transitions: [
1087
+ { dayOfWeek: { monday: true, tuesday: true, wednesday: true, thursday: true, friday: true }, transitionTime: 360, heatingSetpoint: 2000, coolingSetpoint: 2400 },
1088
+ { dayOfWeek: { monday: true, tuesday: true, wednesday: true, thursday: true, friday: true }, transitionTime: 750, heatingSetpoint: 1900, coolingSetpoint: 2500 },
1089
+ { dayOfWeek: { monday: true, tuesday: true, wednesday: true, thursday: true, friday: true }, transitionTime: 810, heatingSetpoint: 2000, coolingSetpoint: 2400 },
1090
+ { dayOfWeek: { monday: true, tuesday: true, wednesday: true, thursday: true, friday: true }, transitionTime: 1320, heatingSetpoint: 1800, coolingSetpoint: 2600 },
1091
+ ],
1092
+ builtIn: true,
1093
+ },
1094
+ {
1095
+ scheduleHandle: new Uint8Array([1]),
1096
+ systemMode: Thermostat.SystemMode.Auto,
1097
+ name: 'Weekend',
1098
+ transitions: [
1099
+ { dayOfWeek: { saturday: true, sunday: true }, transitionTime: 480, heatingSetpoint: 2100, coolingSetpoint: 2300 },
1100
+ { dayOfWeek: { saturday: true, sunday: true }, transitionTime: 1380, heatingSetpoint: 1900, coolingSetpoint: 2500 },
1101
+ ],
1102
+ builtIn: true,
1103
+ },
1104
+ ];
1105
+ const scheduleTypeDefinitions = [
1106
+ {
1107
+ systemMode: Thermostat.SystemMode.Auto,
1108
+ numberOfSchedules: 10,
1109
+ scheduleTypeFeatures: { supportsSetpoints: true, supportsNames: true },
1110
+ },
1111
+ ];
1112
+ this.thermoAutoSchedules = new MatterbridgeEndpoint([thermostat, bridgedNode, powerSource], { id: 'Thermostat (AutoModeSchedules)' }, this.config.debug)
1113
+ .createDefaultIdentifyClusterServer()
1114
+ .createDefaultBridgedDeviceBasicInformationClusterServer('Thermostat (AutoModeSchedules)', 'TAS00059', 0xfff1, 'Matterbridge', 'Matterbridge Thermostat With Schedules')
1115
+ .createDefaultSchedulesThermostatClusterServer(20, 18, 22, 1, 0, 35, 15, 50, undefined, undefined, undefined, undefined, null, schedules_List, scheduleTypeDefinitions, 10, null)
1116
+ .createDefaultPowerSourceWiredClusterServer()
1117
+ .addRequiredClusterServers();
1118
+ if (this.thermoAutoSchedules) {
1119
+ this.thermoAutoSchedules
1120
+ .addChildDeviceType('Temperature', temperatureSensor)
1121
+ .createDefaultTemperatureMeasurementClusterServer(21 * 100)
1122
+ .addRequiredClusterServers();
1123
+ this.thermoAutoSchedules
1124
+ .addChildDeviceType('Humidity', humiditySensor)
1125
+ .createDefaultRelativeHumidityMeasurementClusterServer(50 * 100)
1126
+ .addRequiredClusterServers();
1127
+ this.thermoAutoSchedules = await this.addDevice(this.thermoAutoSchedules);
1128
+ }
1129
+ this.thermoAutoSchedules?.addCommandHandler('identify', ({ request: { identifyTime } }) => {
1130
+ this.thermoAutoSchedules?.log.info(`Command identify called identifyTime ${identifyTime}`);
1131
+ });
1132
+ this.thermoAutoSchedules?.addCommandHandler('triggerEffect', ({ request: { effectIdentifier, effectVariant } }) => {
1133
+ this.thermoAutoSchedules?.log.info(`Command identify called effectIdentifier ${effectIdentifier} effectVariant ${effectVariant}`);
1134
+ });
1135
+ this.thermoAutoSchedules?.addCommandHandler('setpointRaiseLower', ({ request: { mode, amount } }) => {
1136
+ const lookupSetpointAdjustMode = ['Heat', 'Cool', 'Both'];
1137
+ this.thermoAutoSchedules?.log.info(`Command setpointRaiseLower called with mode: ${lookupSetpointAdjustMode[mode]} amount: ${amount / 10}`);
1138
+ });
1139
+ this.thermoAutoSchedules?.addCommandHandler('setActiveScheduleRequest', ({ request: { scheduleHandle } }) => {
1140
+ this.thermoAutoSchedules?.log.info(`Command setActiveScheduleRequest called with scheduleHandle: ${scheduleHandle ? `0x${Buffer.from(scheduleHandle).toString('hex')}` : 'null'}`);
1141
+ });
1142
+ this.thermoAutoSchedules?.subscribeAttribute(Thermostat, 'systemMode', (newValue, oldValue) => {
1143
+ const lookupSystemMode = ['Off', 'Auto', '', 'Cool', 'Heat', 'EmergencyHeat', 'Precooling', 'FanOnly', 'Dry', 'Sleep'];
1144
+ this.thermoAutoSchedules?.log.info(`Subscribe systemMode called with: ${lookupSystemMode[newValue]} (old value: ${lookupSystemMode[oldValue]})`);
1145
+ }, this.thermoAutoSchedules.log);
1146
+ this.thermoAutoSchedules?.subscribeAttribute(Thermostat.id, 'occupiedHeatingSetpoint', (newValue, oldValue) => {
1147
+ this.thermoAutoSchedules?.log.info(`Subscribe occupiedHeatingSetpoint called with: ${newValue / 100} (old value: ${oldValue / 100})`);
1148
+ }, this.thermoAutoSchedules.log);
1149
+ this.thermoAutoSchedules?.subscribeAttribute(Thermostat.id, 'occupiedCoolingSetpoint', (newValue, oldValue) => {
1150
+ this.thermoAutoSchedules?.log.info(`Subscribe occupiedCoolingSetpoint called with: ${newValue / 100} (old value: ${oldValue / 100})`);
1151
+ }, this.thermoAutoSchedules.log);
1152
+ this.thermoAutoSchedules?.subscribeAttribute(Thermostat.id, 'activeScheduleHandle', (newValue, oldValue) => {
1153
+ this.thermoAutoSchedules?.log.info(`Subscribe activeScheduleHandle called with: ${newValue ? `0x${Buffer.from(newValue).toString('hex')}` : 'null'} (old value: ${oldValue ? `0x${Buffer.from(oldValue).toString('hex')}` : 'null'})`);
1154
+ }, this.thermoAutoSchedules.log);
1155
+ this.thermoAutoSchedules?.subscribeAttribute(Thermostat.id, 'schedules', (newValue, oldValue) => {
1156
+ this.thermoAutoSchedules?.log.info(`Subscribe schedules called with: ${debugStringify(newValue)} (old value: ${debugStringify(oldValue)})`);
1157
+ }, this.thermoAutoSchedules.log);
1078
1158
  this.thermoHeat = new MatterbridgeEndpoint([thermostat, bridgedNode, powerSource], { id: 'Thermostat (Heat)' }, this.config.debug)
1079
1159
  .createDefaultIdentifyClusterServer()
1080
1160
  .createDefaultBridgedDeviceBasicInformationClusterServer('Thermostat (Heat)', 'THE00024', 0xfff1, 'Matterbridge', 'Matterbridge Thermostat')
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "matterbridge-example-dynamic-platform",
3
- "version": "3.0.2-dev-20260808-db970b3",
3
+ "version": "3.0.2-dev-20260809-1aeac01",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "matterbridge-example-dynamic-platform",
9
- "version": "3.0.2-dev-20260808-db970b3",
9
+ "version": "3.0.2-dev-20260809-1aeac01",
10
10
  "license": "Apache-2.0",
11
11
  "dependencies": {
12
12
  "node-ansi-logger": "3.3.0",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "matterbridge-example-dynamic-platform",
3
- "version": "3.0.2-dev-20260808-db970b3",
3
+ "version": "3.0.2-dev-20260809-1aeac01",
4
4
  "description": "Matterbridge dynamic plugin",
5
5
  "author": "https://github.com/Luligu",
6
6
  "license": "Apache-2.0",