matterbridge-example-dynamic-platform 1.2.2 → 1.2.3-rc.1
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 +17 -1
- package/README.md +3 -2
- package/dist/appliances.js +13 -18
- package/dist/platform.js +113 -79
- package/npm-shrinkwrap.json +2 -2
- package/package.json +6 -2
package/CHANGELOG.md
CHANGED
@@ -8,10 +8,26 @@ 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.
|
11
|
+
## [1.2.3] - 2025-05-25
|
12
|
+
|
13
|
+
### Added
|
14
|
+
|
15
|
+
- [platform]: Added a cover device with both lift and tilt (supported by the Home app).
|
12
16
|
|
13
17
|
### Changed
|
14
18
|
|
19
|
+
- [package]: Require matterbridge 3.0.4.
|
20
|
+
- [package]: Updated package.
|
21
|
+
- [package]: Updated dependencies.
|
22
|
+
|
23
|
+
<a href="https://www.buymeacoffee.com/luligugithub">
|
24
|
+
<img src="bmc-button.svg" alt="Buy me a coffee" width="80">
|
25
|
+
</a>
|
26
|
+
|
27
|
+
## [1.2.2] - 2025-05-19
|
28
|
+
|
29
|
+
### Added
|
30
|
+
|
15
31
|
- [package]: Added waterHeater device type (not supported by Alexa and Apple Home)
|
16
32
|
|
17
33
|
### Changed
|
package/README.md
CHANGED
@@ -16,7 +16,7 @@
|
|
16
16
|
|
17
17
|
Matterbridge dynamic platform example plugin is a template to develop your own plugin using the dynamic platform.
|
18
18
|
|
19
|
-
It exposes
|
19
|
+
It exposes 40 virtual devices:
|
20
20
|
|
21
21
|
- a switch with onOff cluster
|
22
22
|
- a light with onOff
|
@@ -26,7 +26,8 @@ It exposes 38 devices:
|
|
26
26
|
- a light with onOff, levelControl and colorControl (with XY and CT) clusters
|
27
27
|
- a light with onOff, levelControl and colorControl (with CT only) clusters
|
28
28
|
- an outlet (plug) with onOff cluster
|
29
|
-
- a cover with windowCovering cluster
|
29
|
+
- a cover with windowCovering cluster and lift feature
|
30
|
+
- a cover with windowCovering cluster and both lift and tilt features
|
30
31
|
- a lock with doorLock cluster
|
31
32
|
- a thermo autoMode (i.e. with Auto Heat and Cool features) with thermostat cluster and 3 sub endpoints with flowMeasurement cluster, temperatureMeasurement cluster
|
32
33
|
and relativeHumidityMeasurement cluster (to show how to create a composed device with sub endpoints)
|
package/dist/appliances.js
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
import { MatterbridgeEndpoint, MatterbridgeServer, MatterbridgeOnOffServer, laundryWasher, laundryDryer, dishwasher, refrigerator, temperatureControlledCabinetCooler, oven, temperatureControlledCabinetHeater, microwaveOven, extractorHood, cooktop, cookSurface, powerSource, } from 'matterbridge';
|
2
|
-
import {
|
3
|
-
import { OperationalState, TemperatureControl, DishwasherMode, LaundryWasherControls, LaundryWasherMode, LaundryDryerControls, OvenMode, ModeBase, RefrigeratorAndTemperatureControlledCabinetMode, MicrowaveOvenMode, MicrowaveOvenControl,
|
4
|
-
import { DishwasherAlarmServer, LaundryDryerControlsServer, LaundryWasherControlsServer, MicrowaveOvenControlBehavior, MicrowaveOvenModeServer, TemperatureControlBehavior, } from 'matterbridge/matter/behaviors';
|
2
|
+
import { PositionTag, RefrigeratorTag } from 'matterbridge/matter';
|
3
|
+
import { OperationalState, TemperatureControl, DishwasherMode, LaundryWasherControls, LaundryWasherMode, LaundryDryerControls, OvenMode, ModeBase, RefrigeratorAndTemperatureControlledCabinetMode, MicrowaveOvenMode, MicrowaveOvenControl, } from 'matterbridge/matter/clusters';
|
4
|
+
import { DishwasherAlarmServer, DishwasherModeBehavior, LaundryDryerControlsServer, LaundryWasherControlsServer, LaundryWasherModeBehavior, MicrowaveOvenControlBehavior, MicrowaveOvenModeServer, OvenCavityOperationalStateBehavior, OvenModeBehavior, RefrigeratorAndTemperatureControlledCabinetModeBehavior, TemperatureControlBehavior, } from 'matterbridge/matter/behaviors';
|
5
5
|
export class Appliances extends MatterbridgeEndpoint {
|
6
6
|
constructor(deviceType, name, serial) {
|
7
7
|
super([deviceType, powerSource], { uniqueStorageKey: `${name}-${serial}` }, true);
|
@@ -112,7 +112,7 @@ export class Appliances extends MatterbridgeEndpoint {
|
|
112
112
|
}
|
113
113
|
}
|
114
114
|
createDefaultOvenCavityOperationalStateClusterServer(operationalState = OperationalState.OperationalStateEnum.Stopped) {
|
115
|
-
this.behaviors.require(
|
115
|
+
this.behaviors.require(MatterbridgeOvenCavityOperationalStateServer, {
|
116
116
|
phaseList: [],
|
117
117
|
currentPhase: null,
|
118
118
|
operationalStateList: [
|
@@ -126,7 +126,7 @@ export class Appliances extends MatterbridgeEndpoint {
|
|
126
126
|
return this;
|
127
127
|
}
|
128
128
|
static createDefaultRefrigeratorAndTemperatureControlledCabinetModeClusterServer(endpoint, currentMode) {
|
129
|
-
endpoint.behaviors.require(
|
129
|
+
endpoint.behaviors.require(MatterbridgeRefrigeratorAndTemperatureControlledCabinetModeServer, {
|
130
130
|
supportedModes: [
|
131
131
|
{ label: 'Auto', mode: 0, modeTags: [{ value: RefrigeratorAndTemperatureControlledCabinetMode.ModeTag.Auto }] },
|
132
132
|
{ label: 'RapidCool', mode: 1, modeTags: [{ value: RefrigeratorAndTemperatureControlledCabinetMode.ModeTag.RapidCool }] },
|
@@ -137,7 +137,7 @@ export class Appliances extends MatterbridgeEndpoint {
|
|
137
137
|
return endpoint;
|
138
138
|
}
|
139
139
|
static createDefaultOvenModeClusterServer(endpoint, currentMode) {
|
140
|
-
endpoint.behaviors.require(
|
140
|
+
endpoint.behaviors.require(MatterbridgeOvenModeServer, {
|
141
141
|
supportedModes: [
|
142
142
|
{ label: 'Bake', mode: 1, modeTags: [{ value: OvenMode.ModeTag.Bake }] },
|
143
143
|
{ label: 'Convection', mode: 2, modeTags: [{ value: OvenMode.ModeTag.Convection }] },
|
@@ -155,7 +155,7 @@ export class Appliances extends MatterbridgeEndpoint {
|
|
155
155
|
return endpoint;
|
156
156
|
}
|
157
157
|
createDefaultDishwasherModeClusterServer(currentMode) {
|
158
|
-
this.behaviors.require(
|
158
|
+
this.behaviors.require(MatterbridgeDishwasherModeServer, {
|
159
159
|
supportedModes: [
|
160
160
|
{ label: 'Light', mode: 1, modeTags: [{ value: DishwasherMode.ModeTag.Light }] },
|
161
161
|
{ label: 'Normal', mode: 2, modeTags: [{ value: DishwasherMode.ModeTag.Normal }] },
|
@@ -166,7 +166,7 @@ export class Appliances extends MatterbridgeEndpoint {
|
|
166
166
|
return this;
|
167
167
|
}
|
168
168
|
createDefaultLaundryWasherModeClusterServer(currentMode) {
|
169
|
-
this.behaviors.require(
|
169
|
+
this.behaviors.require(MatterbridgeLaundryWasherModeServer, {
|
170
170
|
supportedModes: [
|
171
171
|
{ label: 'Delicate', mode: 1, modeTags: [{ value: LaundryWasherMode.ModeTag.Delicate }] },
|
172
172
|
{ label: 'Normal', mode: 2, modeTags: [{ value: LaundryWasherMode.ModeTag.Normal }] },
|
@@ -350,8 +350,7 @@ class MatterbridgeMicrowaveOvenControlServer extends MicrowaveOvenControlBehavio
|
|
350
350
|
}
|
351
351
|
}
|
352
352
|
}
|
353
|
-
export
|
354
|
-
export class OvenCavityOperationalStateServer extends OvenCavityOperationalStateBehavior {
|
353
|
+
export class MatterbridgeOvenCavityOperationalStateServer extends OvenCavityOperationalStateBehavior {
|
355
354
|
initialize() {
|
356
355
|
const device = this.endpoint.stateOf(MatterbridgeServer).deviceCommand;
|
357
356
|
device.log.info('OvenCavityOperationalStateServer initialized: setting operational state to Stopped and operational error to No error');
|
@@ -377,8 +376,7 @@ export class OvenCavityOperationalStateServer extends OvenCavityOperationalState
|
|
377
376
|
};
|
378
377
|
}
|
379
378
|
}
|
380
|
-
|
381
|
-
class RefrigeratorAndTemperatureControlledCabinetModeServer extends RefrigeratorAndTemperatureControlledCabinetModeBehavior {
|
379
|
+
class MatterbridgeRefrigeratorAndTemperatureControlledCabinetModeServer extends RefrigeratorAndTemperatureControlledCabinetModeBehavior {
|
382
380
|
initialize() {
|
383
381
|
const device = this.endpoint.stateOf(MatterbridgeServer).deviceCommand;
|
384
382
|
device.log.info('MatterbridgeRefrigeratorAndTemperatureControlledCabinetModeServer initialized: setting currentMode to 1');
|
@@ -398,8 +396,7 @@ class RefrigeratorAndTemperatureControlledCabinetModeServer extends Refrigerator
|
|
398
396
|
}
|
399
397
|
}
|
400
398
|
}
|
401
|
-
|
402
|
-
class OvenModeServer extends OvenModeBehavior {
|
399
|
+
class MatterbridgeOvenModeServer extends OvenModeBehavior {
|
403
400
|
initialize() {
|
404
401
|
const device = this.endpoint.stateOf(MatterbridgeServer).deviceCommand;
|
405
402
|
device.log.info('OvenModeServer initialized: setting currentMode to 3');
|
@@ -419,8 +416,7 @@ class OvenModeServer extends OvenModeBehavior {
|
|
419
416
|
}
|
420
417
|
}
|
421
418
|
}
|
422
|
-
|
423
|
-
class DishwasherModeServer extends DishwasherModeBehavior {
|
419
|
+
class MatterbridgeDishwasherModeServer extends DishwasherModeBehavior {
|
424
420
|
initialize() {
|
425
421
|
const device = this.endpoint.stateOf(MatterbridgeServer).deviceCommand;
|
426
422
|
device.log.info('DishwasherModeServer initialized: setting currentMode to 3');
|
@@ -448,8 +444,7 @@ class DishwasherModeServer extends DishwasherModeBehavior {
|
|
448
444
|
}
|
449
445
|
}
|
450
446
|
}
|
451
|
-
|
452
|
-
class LaundryWasherModeServer extends LaundryWasherModeBehavior {
|
447
|
+
class MatterbridgeLaundryWasherModeServer extends LaundryWasherModeBehavior {
|
453
448
|
initialize() {
|
454
449
|
const device = this.endpoint.stateOf(MatterbridgeServer).deviceCommand;
|
455
450
|
device.log.info('LaundryWasherModeServer initialized: setting currentMode to 3');
|
package/dist/platform.js
CHANGED
@@ -14,7 +14,8 @@ export class ExampleMatterbridgeDynamicPlatform extends MatterbridgeDynamicPlatf
|
|
14
14
|
lightHS;
|
15
15
|
lightCT;
|
16
16
|
outlet;
|
17
|
-
|
17
|
+
coverLift;
|
18
|
+
coverLiftTilt;
|
18
19
|
lock;
|
19
20
|
thermoAuto;
|
20
21
|
thermoHeat;
|
@@ -57,8 +58,8 @@ export class ExampleMatterbridgeDynamicPlatform extends MatterbridgeDynamicPlatf
|
|
57
58
|
fanModeLookup = ['Off', 'Low', 'Medium', 'High', 'On', 'Auto', 'Smart'];
|
58
59
|
constructor(matterbridge, log, config) {
|
59
60
|
super(matterbridge, log, config);
|
60
|
-
if (this.verifyMatterbridgeVersion === undefined || typeof this.verifyMatterbridgeVersion !== 'function' || !this.verifyMatterbridgeVersion('3.0.
|
61
|
-
throw new Error(`This plugin requires Matterbridge version >= "3.0.
|
61
|
+
if (this.verifyMatterbridgeVersion === undefined || typeof this.verifyMatterbridgeVersion !== 'function' || !this.verifyMatterbridgeVersion('3.0.4')) {
|
62
|
+
throw new Error(`This plugin requires Matterbridge version >= "3.0.4". Please update Matterbridge from ${this.matterbridge.matterbridgeVersion} to the latest version in the frontend.`);
|
62
63
|
}
|
63
64
|
this.log.info('Initializing platform:', this.config.name);
|
64
65
|
if (config.whiteList === undefined)
|
@@ -432,41 +433,76 @@ export class ExampleMatterbridgeDynamicPlatform extends MatterbridgeDynamicPlatf
|
|
432
433
|
await this.outlet?.setAttribute(OnOff.Cluster.id, 'onOff', false, this.outlet?.log);
|
433
434
|
this.outlet?.log.info('Command off called');
|
434
435
|
});
|
435
|
-
this.
|
436
|
+
this.coverLift = new MatterbridgeEndpoint([coverDevice, bridgedNode, powerSource], { uniqueStorageKey: 'CoverLift' }, this.config.debug)
|
436
437
|
.createDefaultIdentifyClusterServer()
|
437
438
|
.createDefaultGroupsClusterServer()
|
438
|
-
.createDefaultBridgedDeviceBasicInformationClusterServer('Cover', '
|
439
|
+
.createDefaultBridgedDeviceBasicInformationClusterServer('Cover lift', 'CL01020564', 0xfff1, 'Matterbridge', 'Matterbridge Cover', parseInt(this.version.replace(/\D/g, '')), this.version === '' ? 'Unknown' : this.version, parseInt(this.matterbridge.matterbridgeVersion.replace(/\D/g, '')), this.matterbridge.matterbridgeVersion)
|
439
440
|
.createDefaultWindowCoveringClusterServer()
|
440
441
|
.createDefaultPowerSourceRechargeableBatteryClusterServer(86);
|
441
|
-
this.setSelectDevice(this.
|
442
|
-
if (this.validateDevice(this.
|
443
|
-
await this.registerDevice(this.
|
444
|
-
this.bridgedDevices.set(this.
|
442
|
+
this.setSelectDevice(this.coverLift.serialNumber ?? '', this.coverLift.deviceName ?? '', undefined, 'hub');
|
443
|
+
if (this.validateDevice(this.coverLift.deviceName ?? '')) {
|
444
|
+
await this.registerDevice(this.coverLift);
|
445
|
+
this.bridgedDevices.set(this.coverLift.deviceName ?? '', this.coverLift);
|
445
446
|
}
|
446
447
|
else {
|
447
|
-
this.
|
448
|
+
this.coverLift = undefined;
|
448
449
|
}
|
449
|
-
this.
|
450
|
-
this.
|
451
|
-
}, this.cover.log);
|
452
|
-
this.cover?.addCommandHandler('identify', async ({ request: { identifyTime } }) => {
|
453
|
-
this.cover?.log.info(`Command identify called identifyTime:${identifyTime}`);
|
450
|
+
this.coverLift?.addCommandHandler('identify', async ({ request: { identifyTime } }) => {
|
451
|
+
this.coverLift?.log.info(`Command identify called identifyTime:${identifyTime}`);
|
454
452
|
});
|
455
|
-
this.
|
456
|
-
await this.
|
457
|
-
this.
|
453
|
+
this.coverLift?.addCommandHandler('stopMotion', async () => {
|
454
|
+
await this.coverLift?.setWindowCoveringTargetAsCurrentAndStopped();
|
455
|
+
this.coverLift?.log.info(`Command stopMotion called`);
|
458
456
|
});
|
459
|
-
this.
|
460
|
-
await this.
|
461
|
-
this.
|
457
|
+
this.coverLift?.addCommandHandler('downOrClose', async () => {
|
458
|
+
await this.coverLift?.setWindowCoveringCurrentTargetStatus(10000, 10000, WindowCovering.MovementStatus.Stopped);
|
459
|
+
this.coverLift?.log.info(`Command downOrClose called`);
|
462
460
|
});
|
463
|
-
this.
|
464
|
-
await this.
|
465
|
-
this.
|
461
|
+
this.coverLift?.addCommandHandler('upOrOpen', async () => {
|
462
|
+
await this.coverLift?.setWindowCoveringCurrentTargetStatus(0, 0, WindowCovering.MovementStatus.Stopped);
|
463
|
+
this.coverLift?.log.info(`Command upOrOpen called`);
|
466
464
|
});
|
467
|
-
this.
|
468
|
-
await this.
|
469
|
-
this.
|
465
|
+
this.coverLift?.addCommandHandler('goToLiftPercentage', async ({ request: { liftPercent100thsValue } }) => {
|
466
|
+
await this.coverLift?.setWindowCoveringCurrentTargetStatus(liftPercent100thsValue, liftPercent100thsValue, WindowCovering.MovementStatus.Stopped);
|
467
|
+
this.coverLift?.log.info(`Command goToLiftPercentage ${liftPercent100thsValue} called`);
|
468
|
+
});
|
469
|
+
this.coverLiftTilt = new MatterbridgeEndpoint([coverDevice, bridgedNode, powerSource], { uniqueStorageKey: 'CoverLiftTilt' }, this.config.debug)
|
470
|
+
.createDefaultIdentifyClusterServer()
|
471
|
+
.createDefaultGroupsClusterServer()
|
472
|
+
.createDefaultBridgedDeviceBasicInformationClusterServer('Cover lift and tilt', 'CLT01020554', 0xfff1, 'Matterbridge', 'Matterbridge Cover', parseInt(this.version.replace(/\D/g, '')), this.version === '' ? 'Unknown' : this.version, parseInt(this.matterbridge.matterbridgeVersion.replace(/\D/g, '')), this.matterbridge.matterbridgeVersion)
|
473
|
+
.createDefaultLiftTiltWindowCoveringClusterServer()
|
474
|
+
.createDefaultPowerSourceRechargeableBatteryClusterServer(86);
|
475
|
+
this.setSelectDevice(this.coverLiftTilt.serialNumber ?? '', this.coverLiftTilt.deviceName ?? '', undefined, 'hub');
|
476
|
+
if (this.validateDevice(this.coverLiftTilt.deviceName ?? '')) {
|
477
|
+
await this.registerDevice(this.coverLiftTilt);
|
478
|
+
this.bridgedDevices.set(this.coverLiftTilt.deviceName ?? '', this.coverLiftTilt);
|
479
|
+
}
|
480
|
+
else {
|
481
|
+
this.coverLiftTilt = undefined;
|
482
|
+
}
|
483
|
+
this.coverLiftTilt?.addCommandHandler('identify', async ({ request: { identifyTime } }) => {
|
484
|
+
this.coverLiftTilt?.log.info(`Command identify called identifyTime:${identifyTime}`);
|
485
|
+
});
|
486
|
+
this.coverLiftTilt?.addCommandHandler('stopMotion', async () => {
|
487
|
+
await this.coverLiftTilt?.setWindowCoveringTargetAsCurrentAndStopped();
|
488
|
+
this.coverLiftTilt?.log.info(`Command stopMotion called`);
|
489
|
+
});
|
490
|
+
this.coverLiftTilt?.addCommandHandler('downOrClose', async () => {
|
491
|
+
await this.coverLiftTilt?.setWindowCoveringCurrentTargetStatus(10000, 10000, WindowCovering.MovementStatus.Stopped);
|
492
|
+
this.coverLiftTilt?.log.info(`Command downOrClose called`);
|
493
|
+
});
|
494
|
+
this.coverLiftTilt?.addCommandHandler('upOrOpen', async () => {
|
495
|
+
await this.coverLiftTilt?.setWindowCoveringCurrentTargetStatus(0, 0, WindowCovering.MovementStatus.Stopped);
|
496
|
+
this.coverLiftTilt?.log.info(`Command upOrOpen called`);
|
497
|
+
});
|
498
|
+
this.coverLiftTilt?.addCommandHandler('goToLiftPercentage', async ({ request: { liftPercent100thsValue } }) => {
|
499
|
+
await this.coverLiftTilt?.setWindowCoveringCurrentTargetStatus(liftPercent100thsValue, liftPercent100thsValue, WindowCovering.MovementStatus.Stopped);
|
500
|
+
this.coverLiftTilt?.log.info(`Command goToLiftPercentage ${liftPercent100thsValue} called`);
|
501
|
+
});
|
502
|
+
this.coverLiftTilt?.addCommandHandler('goToTiltPercentage', async ({ request: { tiltPercent100thsValue } }) => {
|
503
|
+
const position = this.coverLiftTilt?.getAttribute(WindowCovering.Cluster.id, 'currentPositionLiftPercent100ths', this.coverLiftTilt?.log);
|
504
|
+
await this.coverLiftTilt?.setWindowCoveringTargetAndCurrentPosition(position, tiltPercent100thsValue);
|
505
|
+
this.coverLiftTilt?.log.info(`Command goToTiltPercentage ${tiltPercent100thsValue} called`);
|
470
506
|
});
|
471
507
|
this.lock = new MatterbridgeEndpoint([doorLockDevice, bridgedNode, powerSource], { uniqueStorageKey: 'Lock' }, this.config.debug)
|
472
508
|
.createDefaultIdentifyClusterServer()
|
@@ -538,14 +574,14 @@ export class ExampleMatterbridgeDynamicPlatform extends MatterbridgeDynamicPlatf
|
|
538
574
|
this.thermoAuto?.log.info('Set occupiedCoolingSetpoint:', setpoint);
|
539
575
|
}
|
540
576
|
});
|
541
|
-
this.thermoAuto?.subscribeAttribute(ThermostatCluster.id, 'systemMode',
|
577
|
+
await this.thermoAuto?.subscribeAttribute(ThermostatCluster.id, 'systemMode', (value) => {
|
542
578
|
const lookupSystemMode = ['Off', 'Auto', '', 'Cool', 'Heat', 'EmergencyHeat', 'Precooling', 'FanOnly', 'Dry', 'Sleep'];
|
543
579
|
this.thermoAuto?.log.info('Subscribe systemMode called with:', lookupSystemMode[value]);
|
544
580
|
}, this.thermoAuto.log);
|
545
|
-
this.thermoAuto?.subscribeAttribute(ThermostatCluster.id, 'occupiedHeatingSetpoint',
|
581
|
+
await this.thermoAuto?.subscribeAttribute(ThermostatCluster.id, 'occupiedHeatingSetpoint', (value) => {
|
546
582
|
this.thermoAuto?.log.info('Subscribe occupiedHeatingSetpoint called with:', value / 100);
|
547
583
|
}, this.thermoAuto.log);
|
548
|
-
this.thermoAuto?.subscribeAttribute(ThermostatCluster.id, 'occupiedCoolingSetpoint',
|
584
|
+
await this.thermoAuto?.subscribeAttribute(ThermostatCluster.id, 'occupiedCoolingSetpoint', (value) => {
|
549
585
|
this.thermoAuto?.log.info('Subscribe occupiedCoolingSetpoint called with:', value / 100);
|
550
586
|
}, this.thermoAuto.log);
|
551
587
|
this.thermoHeat = new MatterbridgeEndpoint([thermostatDevice, bridgedNode, powerSource], { uniqueStorageKey: 'Thermostat (Heat)' }, this.config.debug)
|
@@ -580,11 +616,11 @@ export class ExampleMatterbridgeDynamicPlatform extends MatterbridgeDynamicPlatf
|
|
580
616
|
this.thermoHeat?.addCommandHandler('triggerEffect', async ({ request: { effectIdentifier, effectVariant } }) => {
|
581
617
|
this.thermoHeat?.log.info(`Command identify called effectIdentifier ${effectIdentifier} effectVariant ${effectVariant}`);
|
582
618
|
});
|
583
|
-
this.thermoHeat?.subscribeAttribute(ThermostatCluster.id, 'systemMode',
|
619
|
+
await this.thermoHeat?.subscribeAttribute(ThermostatCluster.id, 'systemMode', (value) => {
|
584
620
|
const lookupSystemMode = ['Off', 'Auto', '', 'Cool', 'Heat', 'EmergencyHeat', 'Precooling', 'FanOnly', 'Dry', 'Sleep'];
|
585
621
|
this.thermoHeat?.log.info('Subscribe systemMode called with:', lookupSystemMode[value]);
|
586
622
|
}, this.thermoHeat.log);
|
587
|
-
this.thermoHeat?.subscribeAttribute(ThermostatCluster.id, 'occupiedHeatingSetpoint',
|
623
|
+
await this.thermoHeat?.subscribeAttribute(ThermostatCluster.id, 'occupiedHeatingSetpoint', (value) => {
|
588
624
|
this.thermoHeat?.log.info('Subscribe occupiedHeatingSetpoint called with:', value / 100);
|
589
625
|
}, this.thermoHeat.log);
|
590
626
|
this.thermoCool = new MatterbridgeEndpoint([thermostatDevice, bridgedNode, powerSource], { uniqueStorageKey: 'Thermostat (Cool)' }, this.config.debug)
|
@@ -607,11 +643,11 @@ export class ExampleMatterbridgeDynamicPlatform extends MatterbridgeDynamicPlatf
|
|
607
643
|
this.thermoCool?.addCommandHandler('triggerEffect', async ({ request: { effectIdentifier, effectVariant } }) => {
|
608
644
|
this.thermoCool?.log.info(`Command identify called effectIdentifier ${effectIdentifier} effectVariant ${effectVariant}`);
|
609
645
|
});
|
610
|
-
this.thermoCool?.subscribeAttribute(ThermostatCluster.id, 'systemMode',
|
646
|
+
await this.thermoCool?.subscribeAttribute(ThermostatCluster.id, 'systemMode', (value) => {
|
611
647
|
const lookupSystemMode = ['Off', 'Auto', '', 'Cool', 'Heat', 'EmergencyHeat', 'Precooling', 'FanOnly', 'Dry', 'Sleep'];
|
612
648
|
this.thermoCool?.log.info('Subscribe systemMode called with:', lookupSystemMode[value]);
|
613
649
|
}, this.thermoCool.log);
|
614
|
-
this.thermoCool?.subscribeAttribute(ThermostatCluster.id, 'occupiedCoolingSetpoint',
|
650
|
+
await this.thermoCool?.subscribeAttribute(ThermostatCluster.id, 'occupiedCoolingSetpoint', (value) => {
|
615
651
|
this.thermoCool?.log.info('Subscribe occupiedCoolingSetpoint called with:', value / 100);
|
616
652
|
}, this.thermoCool.log);
|
617
653
|
this.airPurifier = new MatterbridgeEndpoint([airPurifier, temperatureSensor, humiditySensor, bridgedNode, powerSource], { uniqueStorageKey: 'Air purifier' }, this.config.debug)
|
@@ -634,31 +670,28 @@ export class ExampleMatterbridgeDynamicPlatform extends MatterbridgeDynamicPlatf
|
|
634
670
|
this.airPurifier?.addCommandHandler('identify', async ({ request: { identifyTime } }) => {
|
635
671
|
this.airPurifier?.log.info(`Command identify called identifyTime:${identifyTime}`);
|
636
672
|
});
|
637
|
-
this.airPurifier?.subscribeAttribute(FanControl.Cluster.id, 'fanMode',
|
638
|
-
this.
|
673
|
+
await this.airPurifier?.subscribeAttribute(FanControl.Cluster.id, 'fanMode', (newValue, oldValue, context) => {
|
674
|
+
this.airPurifier?.log.info(`Fan mode changed from ${this.fanModeLookup[oldValue]} to ${this.fanModeLookup[newValue]} context: ${context.offline === true ? 'offline' : 'online'}`);
|
639
675
|
if (newValue === FanControl.FanMode.Off) {
|
640
|
-
|
676
|
+
this.airPurifier?.setAttribute(FanControl.Cluster.id, 'percentCurrent', 0, this.airPurifier?.log);
|
641
677
|
}
|
642
678
|
else if (newValue === FanControl.FanMode.Low) {
|
643
|
-
|
679
|
+
this.airPurifier?.setAttribute(FanControl.Cluster.id, 'percentCurrent', 33, this.airPurifier?.log);
|
644
680
|
}
|
645
681
|
else if (newValue === FanControl.FanMode.Medium) {
|
646
|
-
|
682
|
+
this.airPurifier?.setAttribute(FanControl.Cluster.id, 'percentCurrent', 66, this.airPurifier?.log);
|
647
683
|
}
|
648
684
|
else if (newValue === FanControl.FanMode.High) {
|
649
|
-
|
685
|
+
this.airPurifier?.setAttribute(FanControl.Cluster.id, 'percentCurrent', 100, this.airPurifier?.log);
|
650
686
|
}
|
651
687
|
else if (newValue === FanControl.FanMode.On) {
|
652
|
-
|
653
|
-
}
|
654
|
-
else if (newValue === FanControl.FanMode.Auto) {
|
655
|
-
await this.airPurifier?.setAttribute(FanControl.Cluster.id, 'percentCurrent', 50, this.airPurifier?.log);
|
688
|
+
this.airPurifier?.setAttribute(FanControl.Cluster.id, 'percentCurrent', 100, this.airPurifier?.log);
|
656
689
|
}
|
657
690
|
}, this.airPurifier.log);
|
658
|
-
this.airPurifier?.subscribeAttribute(FanControl.Cluster.id, 'percentSetting',
|
659
|
-
this.
|
691
|
+
await this.airPurifier?.subscribeAttribute(FanControl.Cluster.id, 'percentSetting', (newValue, oldValue, context) => {
|
692
|
+
this.airPurifier?.log.info(`Percent setting changed from ${oldValue} to ${newValue} context: ${context.offline === true ? 'offline' : 'online'}`);
|
660
693
|
if (isValidNumber(newValue, 0, 100))
|
661
|
-
|
694
|
+
this.airPurifier?.setAttribute(FanControl.Cluster.id, 'percentCurrent', newValue, this.airPurifier?.log);
|
662
695
|
}, this.airPurifier.log);
|
663
696
|
this.airConditioner = new MatterbridgeEndpoint([airConditioner, bridgedNode, powerSource], { uniqueStorageKey: 'Air Conditioner' }, this.config.debug)
|
664
697
|
.createDefaultBridgedDeviceBasicInformationClusterServer('Air Conditioner', '0x96382864AC', 0xfff1, 'Matterbridge', 'Matterbridge Air Conditioner', parseInt(this.version.replace(/\D/g, '')), this.version === '' ? 'Unknown' : this.version, parseInt(this.matterbridge.matterbridgeVersion.replace(/\D/g, '')), this.matterbridge.matterbridgeVersion)
|
@@ -756,42 +789,42 @@ export class ExampleMatterbridgeDynamicPlatform extends MatterbridgeDynamicPlatf
|
|
756
789
|
else {
|
757
790
|
this.fan = undefined;
|
758
791
|
}
|
759
|
-
this.fan?.subscribeAttribute(FanControl.Cluster.id, 'fanMode',
|
760
|
-
this.fan?.log.info(`Fan mode changed from ${this.fanModeLookup[oldValue]} to ${this.fanModeLookup[newValue]}`);
|
792
|
+
await this.fan?.subscribeAttribute(FanControl.Cluster.id, 'fanMode', (newValue, oldValue, context) => {
|
793
|
+
this.fan?.log.info(`Fan mode changed from ${this.fanModeLookup[oldValue]} to ${this.fanModeLookup[newValue]} context: ${context.offline === true ? 'offline' : 'online'}`);
|
761
794
|
if (newValue === FanControl.FanMode.Off) {
|
762
|
-
|
763
|
-
|
795
|
+
this.fan?.setAttribute(FanControl.Cluster.id, 'percentSetting', 0, this.fan?.log);
|
796
|
+
this.fan?.setAttribute(FanControl.Cluster.id, 'percentCurrent', 0, this.fan?.log);
|
764
797
|
}
|
765
798
|
else if (newValue === FanControl.FanMode.Low) {
|
766
|
-
|
767
|
-
|
799
|
+
this.fan?.setAttribute(FanControl.Cluster.id, 'percentSetting', 33, this.fan?.log);
|
800
|
+
this.fan?.setAttribute(FanControl.Cluster.id, 'percentCurrent', 33, this.fan?.log);
|
768
801
|
}
|
769
802
|
else if (newValue === FanControl.FanMode.Medium) {
|
770
|
-
|
771
|
-
|
803
|
+
this.fan?.setAttribute(FanControl.Cluster.id, 'percentSetting', 66, this.fan?.log);
|
804
|
+
this.fan?.setAttribute(FanControl.Cluster.id, 'percentCurrent', 66, this.fan?.log);
|
772
805
|
}
|
773
806
|
else if (newValue === FanControl.FanMode.High) {
|
774
|
-
|
775
|
-
|
807
|
+
this.fan?.setAttribute(FanControl.Cluster.id, 'percentSetting', 100, this.fan?.log);
|
808
|
+
this.fan?.setAttribute(FanControl.Cluster.id, 'percentCurrent', 100, this.fan?.log);
|
776
809
|
}
|
777
810
|
else if (newValue === FanControl.FanMode.On) {
|
778
|
-
|
779
|
-
|
811
|
+
this.fan?.setAttribute(FanControl.Cluster.id, 'percentSetting', 100, this.fan?.log);
|
812
|
+
this.fan?.setAttribute(FanControl.Cluster.id, 'percentCurrent', 100, this.fan?.log);
|
780
813
|
}
|
781
814
|
else if (newValue === FanControl.FanMode.Auto) {
|
782
|
-
|
783
|
-
|
815
|
+
this.fan?.setAttribute(FanControl.Cluster.id, 'percentSetting', 50, this.fan?.log);
|
816
|
+
this.fan?.setAttribute(FanControl.Cluster.id, 'percentCurrent', 50, this.fan?.log);
|
784
817
|
}
|
785
818
|
}, this.fan.log);
|
786
|
-
this.fan?.subscribeAttribute(FanControl.Cluster.id, 'percentSetting',
|
787
|
-
this.fan?.log.info(`Percent setting changed from ${oldValue} to ${newValue}`);
|
819
|
+
await this.fan?.subscribeAttribute(FanControl.Cluster.id, 'percentSetting', (newValue, oldValue, context) => {
|
820
|
+
this.fan?.log.info(`Percent setting changed from ${oldValue} to ${newValue} context: ${context.offline === true ? 'offline' : 'online'}`);
|
788
821
|
if (isValidNumber(newValue, 0, 100))
|
789
|
-
|
822
|
+
this.fan?.setAttribute(FanControl.Cluster.id, 'percentCurrent', newValue, this.fan?.log);
|
790
823
|
}, this.fan.log);
|
791
|
-
this.fan?.subscribeAttribute(FanControl.Cluster.id, 'speedSetting',
|
792
|
-
this.fan?.log.info(`Speed setting changed from ${oldValue} to ${newValue}`);
|
824
|
+
await this.fan?.subscribeAttribute(FanControl.Cluster.id, 'speedSetting', (newValue, oldValue, context) => {
|
825
|
+
this.fan?.log.info(`Speed setting changed from ${oldValue} to ${newValue} context: ${context.offline === true ? 'offline' : 'online'}`);
|
793
826
|
if (isValidNumber(newValue, 0, 100))
|
794
|
-
|
827
|
+
this.fan?.setAttribute(FanControl.Cluster.id, 'speedCurrent', newValue, this.fan?.log);
|
795
828
|
}, this.fan.log);
|
796
829
|
this.waterLeak = new MatterbridgeEndpoint([waterLeakDetector, bridgedNode, powerSource], { uniqueStorageKey: 'Water leak detector' }, this.config.debug)
|
797
830
|
.createDefaultBridgedDeviceBasicInformationClusterServer('Water leak detector', 'serial_98745631222', 0xfff1, 'Matterbridge', 'Matterbridge WaterLeakDetector', parseInt(this.version.replace(/\D/g, '')), this.version === '' ? 'Unknown' : this.version, parseInt(this.matterbridge.matterbridgeVersion.replace(/\D/g, '')), this.matterbridge.matterbridgeVersion)
|
@@ -1091,17 +1124,17 @@ export class ExampleMatterbridgeDynamicPlatform extends MatterbridgeDynamicPlatf
|
|
1091
1124
|
}
|
1092
1125
|
}, 60 * 1000 + 300);
|
1093
1126
|
}
|
1094
|
-
await this.
|
1095
|
-
this.
|
1127
|
+
await this.coverLift?.setWindowCoveringTargetAsCurrentAndStopped();
|
1128
|
+
this.coverLift?.log.info('Set cover initial targetPositionLiftPercent100ths = currentPositionLiftPercent100ths and operationalStatus to Stopped.');
|
1096
1129
|
if (this.config.useInterval) {
|
1097
1130
|
this.coverInterval = setInterval(async () => {
|
1098
|
-
let position = this.
|
1131
|
+
let position = this.coverLift?.getAttribute(WindowCovering.Cluster.id, 'currentPositionLiftPercent100ths', this.coverLift.log);
|
1099
1132
|
if (isValidNumber(position, 0, 10000)) {
|
1100
1133
|
position = position > 9000 ? 0 : position + 1000;
|
1101
|
-
await this.
|
1102
|
-
await this.
|
1103
|
-
await this.
|
1104
|
-
this.
|
1134
|
+
await this.coverLift?.setAttribute(WindowCovering.Cluster.id, 'targetPositionLiftPercent100ths', position, this.coverLift.log);
|
1135
|
+
await this.coverLift?.setAttribute(WindowCovering.Cluster.id, 'currentPositionLiftPercent100ths', position, this.coverLift.log);
|
1136
|
+
await this.coverLift?.setAttribute(WindowCovering.Cluster.id, 'operationalStatus', { global: WindowCovering.MovementStatus.Stopped, lift: WindowCovering.MovementStatus.Stopped, tilt: WindowCovering.MovementStatus.Stopped }, this.coverLift.log);
|
1137
|
+
this.coverLift?.log.info(`Set cover current and target positionLiftPercent100ths to ${position} and operationalStatus to Stopped`);
|
1105
1138
|
}
|
1106
1139
|
}, 60 * 1000 + 400);
|
1107
1140
|
}
|
@@ -1276,26 +1309,27 @@ export class ExampleMatterbridgeDynamicPlatform extends MatterbridgeDynamicPlatf
|
|
1276
1309
|
await surface2?.setAttribute(OnOff.Cluster.id, 'onOff', true, surface2.log);
|
1277
1310
|
surface2?.log.info(`Set Surface 2 onOff only OnOff to on`);
|
1278
1311
|
if (this.config.useInterval) {
|
1312
|
+
this.genericSwitchLastEvent = 'Release';
|
1279
1313
|
this.genericSwitchInterval = setInterval(async () => {
|
1280
1314
|
if (this.genericSwitchLastEvent === 'Release') {
|
1281
|
-
await this.momentarySwitch?.triggerSwitchEvent('Single', this.momentarySwitch?.log);
|
1282
1315
|
this.genericSwitchLastEvent = 'Single';
|
1316
|
+
await this.momentarySwitch?.triggerSwitchEvent('Single', this.momentarySwitch?.log);
|
1283
1317
|
}
|
1284
1318
|
else if (this.genericSwitchLastEvent === 'Single') {
|
1285
|
-
await this.momentarySwitch?.triggerSwitchEvent('Double', this.momentarySwitch?.log);
|
1286
1319
|
this.genericSwitchLastEvent = 'Double';
|
1320
|
+
await this.momentarySwitch?.triggerSwitchEvent('Double', this.momentarySwitch?.log);
|
1287
1321
|
}
|
1288
1322
|
else if (this.genericSwitchLastEvent === 'Double') {
|
1289
|
-
await this.momentarySwitch?.triggerSwitchEvent('Long', this.momentarySwitch?.log);
|
1290
1323
|
this.genericSwitchLastEvent = 'Long';
|
1324
|
+
await this.momentarySwitch?.triggerSwitchEvent('Long', this.momentarySwitch?.log);
|
1291
1325
|
}
|
1292
1326
|
else if (this.genericSwitchLastEvent === 'Long') {
|
1293
|
-
await this.latchingSwitch?.triggerSwitchEvent('Press', this.latchingSwitch?.log);
|
1294
1327
|
this.genericSwitchLastEvent = 'Press';
|
1328
|
+
await this.latchingSwitch?.triggerSwitchEvent('Press', this.latchingSwitch?.log);
|
1295
1329
|
}
|
1296
1330
|
else if (this.genericSwitchLastEvent === 'Press') {
|
1297
|
-
await this.latchingSwitch?.triggerSwitchEvent('Release', this.latchingSwitch?.log);
|
1298
1331
|
this.genericSwitchLastEvent = 'Release';
|
1332
|
+
await this.latchingSwitch?.triggerSwitchEvent('Release', this.latchingSwitch?.log);
|
1299
1333
|
}
|
1300
1334
|
}, 60 * 1000 + 1900);
|
1301
1335
|
}
|
package/npm-shrinkwrap.json
CHANGED
@@ -1,12 +1,12 @@
|
|
1
1
|
{
|
2
2
|
"name": "matterbridge-example-dynamic-platform",
|
3
|
-
"version": "1.2.
|
3
|
+
"version": "1.2.3-rc.1",
|
4
4
|
"lockfileVersion": 3,
|
5
5
|
"requires": true,
|
6
6
|
"packages": {
|
7
7
|
"": {
|
8
8
|
"name": "matterbridge-example-dynamic-platform",
|
9
|
-
"version": "1.2.
|
9
|
+
"version": "1.2.3-rc.1",
|
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.
|
3
|
+
"version": "1.2.3-rc.1",
|
4
4
|
"description": "Matterbridge dynamic plugin",
|
5
5
|
"author": "https://github.com/Luligu",
|
6
6
|
"license": "MIT",
|
@@ -24,7 +24,11 @@
|
|
24
24
|
"matter",
|
25
25
|
"matter.js",
|
26
26
|
"example",
|
27
|
-
"plugin"
|
27
|
+
"plugin",
|
28
|
+
"dynamic",
|
29
|
+
"platform",
|
30
|
+
"virtual device",
|
31
|
+
"virtual devices"
|
28
32
|
],
|
29
33
|
"engines": {
|
30
34
|
"node": ">=18.0.0 <19.0.0 || >=20.0.0 <21.0.0 || >=22.0.0"
|