UNPKG

matterbridge-example-dynamic-platform

Version:
693 lines 111 kB
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, cooktop, microwaveOven, oven, refrigerator, onOffMountedSwitch, dimmableMountedSwitch, extendedColorLight, } from 'matterbridge'; import { RoboticVacuumCleaner, LaundryWasher, WaterHeater, Evse, SolarPower, BatteryStorage, LaundryDryer, HeatPump, Dishwasher, ExtractorHood } from 'matterbridge/devices'; import { isValidBoolean, isValidNumber, isValidString } from 'matterbridge/utils'; import { debugStringify } from 'matterbridge/logger'; import { AreaNamespaceTag, LocationTag, NumberTag, PositionTag, SwitchesTag, UINT16_MAX, UINT32_MAX } from 'matterbridge/matter'; 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, EnergyEvseMode, EnergyEvse, RvcRunMode, RvcCleanMode, ConcentrationMeasurement, Descriptor, BridgedDeviceBasicInformation, } from 'matterbridge/matter/clusters'; import { Appliances } from './appliances.js'; export class ExampleMatterbridgeDynamicPlatform extends MatterbridgeDynamicPlatform { switch; mountedOnOffSwitch; mountedDimmerSwitch; lightOnOff; dimmer; light; lightXY; lightHS; lightCT; outlet; coverLift; coverLiftTilt; lock; thermoAuto; thermoHeat; thermoCool; fanBase; fanOnHigh; fanDefault; fanComplete; waterLeak; waterFreeze; rain; smokeCo; smokeOnly; coOnly; airQuality; airConditioner; airPurifier; pump; valve; momentarySwitch; latchingSwitch; vacuum; roboticVacuum; waterHeater; evse; laundryWasher; laundryDryer; dishwasher; extractorHood; solarPower; batteryStorage; heatPump; switchInterval; lightInterval; outletInterval; coverInterval; lockInterval; thermoInterval; fanInterval; waterLeakInterval; waterFreezeInterval; rainInterval; smokeInterval; airQualityInterval; airConditionerInterval; genericSwitchInterval; genericSwitchLastEvent = 'Release'; intervalOnOff = false; intervalLevel = 0; intervalColorTemperature = 147; bridgedDevices = new Map(); fanModeLookup = ['Off', 'Low', 'Medium', 'High', 'On', 'Auto', 'Smart']; fanDirectionLookup = ['Forward', 'Reverse']; constructor(matterbridge, log, config) { super(matterbridge, log, config); if (this.verifyMatterbridgeVersion === undefined || typeof this.verifyMatterbridgeVersion !== 'function' || !this.verifyMatterbridgeVersion('3.1.7')) { throw new Error(`This plugin requires Matterbridge version >= "3.1.7". Please update Matterbridge from ${this.matterbridge.matterbridgeVersion} to the latest version in the frontend.`); } this.log.info('Initializing platform:', this.config.name); if (config.whiteList === undefined) config.whiteList = []; if (config.blackList === undefined) config.blackList = []; if (config.enableRVC !== undefined) delete config.enableRVC; if (config.enableServerRvc === undefined) config.enableServerRvc = true; } async onStart(reason) { this.log.info('onStart called with reason:', reason ?? 'none'); await this.ready; await this.clearSelect(); this.switch = new MatterbridgeEndpoint([onOffSwitch, bridgedNode, powerSource], { uniqueStorageKey: 'Switch' }, this.config.debug) .createDefaultIdentifyClusterServer() .createDefaultGroupsClusterServer() .createDefaultBridgedDeviceBasicInformationClusterServer('Switch', '0x23452164', 0xfff1, 'Matterbridge', 'Matterbridge Switch') .createDefaultOnOffClusterServer() .createDefaultPowerSourceRechargeableBatteryClusterServer(70); this.switch = await this.addDevice(this.switch); this.switch?.addCommandHandler('identify', async ({ request: { identifyTime } }) => { this.log.info(`Command identify called identifyTime:${identifyTime}`); }); this.switch?.addCommandHandler('on', async () => { await this.switch?.setAttribute(OnOff.Cluster.id, 'onOff', true, this.switch.log); this.switch?.log.info('Command on called'); }); this.switch?.addCommandHandler('off', async () => { await this.switch?.setAttribute(OnOff.Cluster.id, 'onOff', false, this.switch.log); this.switch?.log.info('Command off called'); }); this.mountedOnOffSwitch = new MatterbridgeEndpoint([onOffMountedSwitch, bridgedNode, powerSource], { uniqueStorageKey: 'OnOffMountedSwitch' }, this.config.debug) .createDefaultIdentifyClusterServer() .createDefaultGroupsClusterServer() .createDefaultBridgedDeviceBasicInformationClusterServer('OnOff Mounted Switch', '0x298242164', 0xfff1, 'Matterbridge', 'Matterbridge OnOff Mounted Switch') .createDefaultOnOffClusterServer() .createDefaultPowerSourceRechargeableBatteryClusterServer(70); this.mountedOnOffSwitch = await this.addDevice(this.mountedOnOffSwitch); this.mountedOnOffSwitch?.addCommandHandler('identify', async ({ request: { identifyTime } }) => { this.mountedOnOffSwitch?.log.info(`Command identify called identifyTime:${identifyTime}`); }); this.mountedOnOffSwitch?.addCommandHandler('on', async () => { await this.mountedOnOffSwitch?.setAttribute(OnOff.Cluster.id, 'onOff', true, this.mountedOnOffSwitch.log); this.mountedOnOffSwitch?.log.info('Command on called'); }); this.mountedOnOffSwitch?.addCommandHandler('off', async () => { await this.mountedOnOffSwitch?.setAttribute(OnOff.Cluster.id, 'onOff', false, this.mountedOnOffSwitch.log); this.mountedOnOffSwitch?.log.info('Command off called'); }); this.mountedDimmerSwitch = new MatterbridgeEndpoint([dimmableMountedSwitch, bridgedNode, powerSource], { uniqueStorageKey: 'DimmerMountedSwitch' }, this.config.debug) .createDefaultIdentifyClusterServer() .createDefaultGroupsClusterServer() .createDefaultBridgedDeviceBasicInformationClusterServer('Dimmer Mounted Switch', '0x22145578864', 0xfff1, 'Matterbridge', 'Matterbridge Dimmer Mounted Switch') .createDefaultOnOffClusterServer() .createDefaultLevelControlClusterServer() .createDefaultPowerSourceWiredClusterServer() .addRequiredClusterServers(); this.mountedDimmerSwitch = await this.addDevice(this.mountedDimmerSwitch); this.mountedDimmerSwitch?.addCommandHandler('identify', async ({ request: { identifyTime } }) => { this.mountedDimmerSwitch?.log.info(`Command identify called identifyTime:${identifyTime}`); }); this.mountedDimmerSwitch?.addCommandHandler('on', async () => { await this.mountedDimmerSwitch?.setAttribute(OnOff.Cluster.id, 'onOff', true, this.mountedDimmerSwitch.log); this.mountedDimmerSwitch?.log.info('Command on called'); }); this.mountedDimmerSwitch?.addCommandHandler('off', async () => { await this.mountedDimmerSwitch?.setAttribute(OnOff.Cluster.id, 'onOff', false, this.mountedDimmerSwitch.log); this.mountedDimmerSwitch?.log.info('Command off called'); }); this.mountedDimmerSwitch?.addCommandHandler('moveToLevel', async ({ request: { level } }) => { await this.mountedDimmerSwitch?.setAttribute(LevelControl.Cluster.id, 'currentLevel', level, this.mountedDimmerSwitch.log); this.mountedDimmerSwitch?.log.debug(`Command moveToLevel called request: ${level}`); }); this.mountedDimmerSwitch?.addCommandHandler('moveToLevelWithOnOff', async ({ request: { level } }) => { await this.mountedDimmerSwitch?.setAttribute(LevelControl.Cluster.id, 'currentLevel', level, this.mountedDimmerSwitch.log); this.mountedDimmerSwitch?.log.debug(`Command moveToLevelWithOnOff called request: ${level}`); }); this.lightOnOff = new MatterbridgeEndpoint([onOffLight, bridgedNode, powerSource], { uniqueStorageKey: 'Light (on/off)' }, this.config.debug) .createDefaultIdentifyClusterServer() .createDefaultGroupsClusterServer() .createDefaultBridgedDeviceBasicInformationClusterServer('Light (on/off)', '0x2342375564', 0xfff1, 'Matterbridge', 'Matterbridge Light on/off') .createDefaultOnOffClusterServer() .createDefaultPowerSourceWiredClusterServer(); this.lightOnOff = await this.addDevice(this.lightOnOff); this.lightOnOff?.addCommandHandler('identify', async ({ request: { identifyTime } }) => { this.lightOnOff?.log.info(`Command identify called identifyTime:${identifyTime}`); }); this.lightOnOff?.addCommandHandler('on', async () => { await this.lightOnOff?.setAttribute(OnOff.Cluster.id, 'onOff', true, this.lightOnOff?.log); this.lightOnOff?.log.info('Command on called'); }); this.lightOnOff?.addCommandHandler('off', async () => { await this.lightOnOff?.setAttribute(OnOff.Cluster.id, 'onOff', false, this.lightOnOff?.log); this.lightOnOff?.log.info('Command off called'); }); this.dimmer = new MatterbridgeEndpoint([dimmableLight, bridgedNode, powerSource], { uniqueStorageKey: 'Dimmer' }, this.config.debug) .createDefaultIdentifyClusterServer() .createDefaultGroupsClusterServer() .createDefaultBridgedDeviceBasicInformationClusterServer('Dimmer', '0x234554564', 0xfff1, 'Matterbridge', 'Matterbridge Dimmer') .createDefaultOnOffClusterServer() .createDefaultLevelControlClusterServer() .createDefaultPowerSourceReplaceableBatteryClusterServer(70, PowerSource.BatChargeLevel.Ok, 2990, '2 x AA', 2); this.dimmer = await this.addDevice(this.dimmer); this.dimmer?.addCommandHandler('identify', async ({ request: { identifyTime } }) => { this.dimmer?.log.info(`Command identify called identifyTime:${identifyTime}`); }); this.dimmer?.addCommandHandler('on', async () => { await this.dimmer?.setAttribute(OnOff.Cluster.id, 'onOff', true, this.dimmer.log); this.dimmer?.log.info('Command on called'); }); this.dimmer?.addCommandHandler('off', async () => { await this.dimmer?.setAttribute(OnOff.Cluster.id, 'onOff', false, this.dimmer.log); this.dimmer?.log.info('Command off called'); }); this.dimmer?.addCommandHandler('moveToLevel', async ({ request: { level } }) => { await this.dimmer?.setAttribute(LevelControl.Cluster.id, 'currentLevel', level, this.dimmer.log); this.dimmer?.log.debug(`Command moveToLevel called request: ${level}`); }); this.dimmer?.addCommandHandler('moveToLevelWithOnOff', async ({ request: { level } }) => { await this.dimmer?.setAttribute(LevelControl.Cluster.id, 'currentLevel', level, this.dimmer.log); this.dimmer?.log.debug(`Command moveToLevelWithOnOff called request: ${level}`); }); this.light = new MatterbridgeEndpoint([extendedColorLight, bridgedNode, powerSource], { uniqueStorageKey: 'Light (XY, HS and CT)' }, this.config.debug) .createDefaultIdentifyClusterServer() .createDefaultGroupsClusterServer() .createDefaultBridgedDeviceBasicInformationClusterServer('Light (XY, HS and CT)', '0x23480564', 0xfff1, 'Matterbridge', 'Matterbridge Light') .createDefaultOnOffClusterServer() .createDefaultLevelControlClusterServer() .createDefaultColorControlClusterServer() .createDefaultPowerSourceReplaceableBatteryClusterServer(70); this.light = await this.addDevice(this.light); this.light?.addCommandHandler('identify', async ({ request: { identifyTime } }) => { this.light?.log.info(`Command identify called identifyTime:${identifyTime}`); }); this.light?.addCommandHandler('on', async () => { await this.light?.setAttribute(OnOff.Cluster.id, 'onOff', true, this.light?.log); this.light?.log.info('Command on called'); }); this.light?.addCommandHandler('off', async () => { await this.light?.setAttribute(OnOff.Cluster.id, 'onOff', false, this.light?.log); this.light?.log.info('Command off called'); }); this.light?.addCommandHandler('moveToLevel', async ({ request: { level } }) => { await this.light?.setAttribute(LevelControl.Cluster.id, 'currentLevel', level, this.light?.log); this.light?.log.debug(`Command moveToLevel called request: ${level}`); }); this.light?.addCommandHandler('moveToLevelWithOnOff', async ({ request: { level } }) => { await this.light?.setAttribute(LevelControl.Cluster.id, 'currentLevel', level, this.light?.log); this.light?.log.debug(`Command moveToLevelWithOnOff called request: ${level}`); }); this.light?.addCommandHandler('moveToColor', async ({ request: { colorX, colorY } }) => { await this.light?.setAttribute(ColorControl.Cluster.id, 'currentX', colorX, this.light?.log); await this.light?.setAttribute(ColorControl.Cluster.id, 'currentY', colorY, this.light?.log); this.light?.log.debug(`Command moveToColor called request: X ${colorX / 65536} Y ${colorY / 65536}`); }); this.light?.addCommandHandler('moveToHueAndSaturation', async ({ request: { hue, saturation } }) => { await this.light?.setAttribute(ColorControl.Cluster.id, 'currentHue', hue, this.light?.log); await this.light?.setAttribute(ColorControl.Cluster.id, 'currentSaturation', saturation, this.light?.log); this.light?.log.debug(`Command moveToHueAndSaturation called request: hue ${hue} saturation ${saturation}`); }); this.light?.addCommandHandler('moveToHue', async ({ request: { hue } }) => { await this.light?.setAttribute(ColorControl.Cluster.id, 'currentHue', hue, this.light?.log); this.light?.log.debug(`Command moveToHue called request: hue ${hue}`); }); this.light?.addCommandHandler('moveToSaturation', async ({ request: { saturation } }) => { await this.light?.setAttribute(ColorControl.Cluster.id, 'currentSaturation', saturation, this.light?.log); this.light?.log.debug(`Command moveToSaturation called request: saturation ${saturation}}`); }); this.light?.addCommandHandler('moveToColorTemperature', async ({ request: { colorTemperatureMireds } }) => { await this.light?.setAttribute(ColorControl.Cluster.id, 'colorTemperatureMireds', colorTemperatureMireds, this.light?.log); this.light?.log.debug(`Command moveToColorTemperature called request: ${colorTemperatureMireds}`); }); this.lightHS = new MatterbridgeEndpoint([colorTemperatureLight, bridgedNode, powerSource], { uniqueStorageKey: 'Light (HS, CT)' }, this.config.debug) .createDefaultIdentifyClusterServer() .createDefaultGroupsClusterServer() .createDefaultBridgedDeviceBasicInformationClusterServer('Light (HS, CT)', '0x25097564', 0xfff1, 'Matterbridge', 'Matterbridge Light') .createDefaultOnOffClusterServer() .createDefaultLevelControlClusterServer() .createHsColorControlClusterServer() .createDefaultPowerSourceWiredClusterServer(); this.lightHS = await this.addDevice(this.lightHS); this.lightHS?.addCommandHandler('identify', async ({ request: { identifyTime } }) => { this.lightHS?.log.info(`Command identify called identifyTime:${identifyTime}`); }); this.lightHS?.addCommandHandler('on', async () => { await this.lightHS?.setAttribute(OnOff.Cluster.id, 'onOff', true, this.lightHS?.log); this.lightHS?.log.info('Command on called'); }); this.lightHS?.addCommandHandler('off', async () => { await this.lightHS?.setAttribute(OnOff.Cluster.id, 'onOff', false, this.lightHS?.log); this.lightHS?.log.info('Command off called'); }); this.lightHS?.addCommandHandler('moveToLevel', async ({ request: { level } }) => { await this.lightHS?.setAttribute(LevelControl.Cluster.id, 'currentLevel', level, this.lightHS?.log); this.lightHS?.log.debug(`Command moveToLevel called request: ${level}`); }); this.lightHS?.addCommandHandler('moveToLevelWithOnOff', async ({ request: { level } }) => { await this.lightHS?.setAttribute(LevelControl.Cluster.id, 'currentLevel', level, this.lightHS?.log); this.lightHS?.log.debug(`Command moveToLevelWithOnOff called request: ${level}`); }); this.lightHS?.addCommandHandler('moveToHueAndSaturation', async ({ request: { hue, saturation } }) => { await this.lightHS?.setAttribute(ColorControl.Cluster.id, 'currentHue', hue, this.lightHS?.log); await this.lightHS?.setAttribute(ColorControl.Cluster.id, 'currentSaturation', saturation, this.lightHS?.log); this.lightHS?.log.debug(`Command moveToHueAndSaturation called request: hue ${hue} saturation ${saturation}}`); }); this.lightHS?.addCommandHandler('moveToHue', async ({ request: { hue } }) => { await this.lightHS?.setAttribute(ColorControl.Cluster.id, 'currentHue', hue, this.lightHS?.log); this.lightHS?.log.debug(`Command moveToHue called request: hue ${hue}`); }); this.lightHS?.addCommandHandler('moveToSaturation', async ({ request: { saturation } }) => { await this.lightHS?.setAttribute(ColorControl.Cluster.id, 'currentSaturation', saturation, this.lightHS?.log); this.lightHS?.log.debug(`Command moveToSaturation called request: saturation ${saturation}`); }); this.lightHS?.addCommandHandler('moveToColorTemperature', async ({ request: { colorTemperatureMireds } }) => { await this.lightHS?.setAttribute(ColorControl.Cluster.id, 'colorTemperatureMireds', colorTemperatureMireds, this.lightHS?.log); this.lightHS?.log.debug(`Command moveToColorTemperature called request: ${colorTemperatureMireds}`); }); this.lightXY = new MatterbridgeEndpoint([extendedColorLight, bridgedNode, powerSource], { uniqueStorageKey: 'Light (XY, CT)' }, this.config.debug) .createDefaultIdentifyClusterServer() .createDefaultGroupsClusterServer() .createDefaultBridgedDeviceBasicInformationClusterServer('Light (XY, CT)', '0x23497564', 0xfff1, 'Matterbridge', 'Matterbridge Light') .createDefaultOnOffClusterServer() .createDefaultLevelControlClusterServer() .createXyColorControlClusterServer() .createDefaultPowerSourceWiredClusterServer(); this.lightXY = await this.addDevice(this.lightXY); this.lightXY?.addCommandHandler('identify', async ({ request: { identifyTime } }) => { this.lightXY?.log.info(`Command identify called identifyTime:${identifyTime}`); }); this.lightXY?.addCommandHandler('on', async () => { await this.lightXY?.setAttribute(OnOff.Cluster.id, 'onOff', true, this.lightXY?.log); this.lightXY?.log.info('Command on called'); }); this.lightXY?.addCommandHandler('off', async () => { await this.lightXY?.setAttribute(OnOff.Cluster.id, 'onOff', false, this.lightXY?.log); this.lightXY?.log.info('Command off called'); }); this.lightXY?.addCommandHandler('moveToLevel', async ({ request: { level } }) => { await this.lightXY?.setAttribute(LevelControl.Cluster.id, 'currentLevel', level, this.lightXY?.log); this.lightXY?.log.debug(`Command moveToLevel called request: ${level}`); }); this.lightXY?.addCommandHandler('moveToLevelWithOnOff', async ({ request: { level } }) => { await this.lightXY?.setAttribute(LevelControl.Cluster.id, 'currentLevel', level, this.lightXY?.log); this.lightXY?.log.debug(`Command moveToLevelWithOnOff called request: ${level}`); }); this.lightXY?.addCommandHandler('moveToColor', async ({ request: { colorX, colorY } }) => { await this.lightXY?.setAttribute(ColorControl.Cluster.id, 'currentX', colorX, this.lightXY?.log); await this.lightXY?.setAttribute(ColorControl.Cluster.id, 'currentY', colorY, this.lightXY?.log); this.lightXY?.log.debug(`Command moveToColor called request: X ${colorX / 65536} Y ${colorY / 65536}`); }); this.lightXY?.addCommandHandler('moveToColorTemperature', async ({ request: { colorTemperatureMireds } }) => { await this.lightXY?.setAttribute(ColorControl.Cluster.id, 'colorTemperatureMireds', colorTemperatureMireds, this.lightXY?.log); this.lightXY?.log.debug(`Command moveToColorTemperature called request: ${colorTemperatureMireds}`); }); this.lightCT = new MatterbridgeEndpoint([colorTemperatureLight, bridgedNode, powerSource], { uniqueStorageKey: 'Light (CT)' }, this.config.debug) .createDefaultIdentifyClusterServer() .createDefaultGroupsClusterServer() .createDefaultBridgedDeviceBasicInformationClusterServer('Light (CT)', '0x23480749', 0xfff1, 'Matterbridge', 'Matterbridge Light') .createDefaultOnOffClusterServer() .createDefaultLevelControlClusterServer() .createCtColorControlClusterServer() .createDefaultPowerSourceReplaceableBatteryClusterServer(70); this.lightCT = await this.addDevice(this.lightCT); this.lightCT?.addCommandHandler('identify', async ({ request: { identifyTime } }) => { this.lightCT?.log.info(`Command identify called identifyTime:${identifyTime}`); }); this.lightCT?.addCommandHandler('on', async () => { await this.lightCT?.setAttribute(OnOff.Cluster.id, 'onOff', true, this.lightCT?.log); this.lightCT?.log.info('Command on called'); }); this.lightCT?.addCommandHandler('off', async () => { await this.lightCT?.setAttribute(OnOff.Cluster.id, 'onOff', false, this.lightCT?.log); this.lightCT?.log.info('Command off called'); }); this.lightCT?.addCommandHandler('moveToLevel', async ({ request: { level } }) => { await this.lightCT?.setAttribute(LevelControl.Cluster.id, 'currentLevel', level, this.lightCT?.log); this.lightCT?.log.debug(`Command moveToLevel called request: ${level}`); }); this.lightCT?.addCommandHandler('moveToLevelWithOnOff', async ({ request: { level } }) => { await this.lightCT?.setAttribute(LevelControl.Cluster.id, 'currentLevel', level, this.lightCT?.log); this.lightCT?.log.debug(`Command moveToLevelWithOnOff called request: ${level}`); }); this.lightCT?.addCommandHandler('moveToColorTemperature', async ({ request: { colorTemperatureMireds } }) => { await this.lightCT?.setAttribute(ColorControl.Cluster.id, 'colorTemperatureMireds', colorTemperatureMireds, this.lightCT?.log); this.lightCT?.log.debug(`Command moveToColorTemperature called request: ${colorTemperatureMireds}`); }); this.outlet = new MatterbridgeEndpoint([onOffOutlet, bridgedNode, powerSource], { uniqueStorageKey: 'Outlet' }, this.config.debug) .createDefaultIdentifyClusterServer() .createDefaultGroupsClusterServer() .createDefaultBridgedDeviceBasicInformationClusterServer('Outlet', '0x29252164', 0xfff1, 'Matterbridge', 'Matterbridge Outlet') .createDefaultOnOffClusterServer() .createDefaultPowerSourceWiredClusterServer(); this.outlet = await this.addDevice(this.outlet); this.outlet?.addCommandHandler('identify', async ({ request: { identifyTime } }) => { this.outlet?.log.info(`Command identify called identifyTime:${identifyTime}`); }); this.outlet?.addCommandHandler('on', async () => { await this.outlet?.setAttribute(OnOff.Cluster.id, 'onOff', true, this.outlet?.log); this.outlet?.log.info('Command on called'); }); this.outlet?.addCommandHandler('off', async () => { await this.outlet?.setAttribute(OnOff.Cluster.id, 'onOff', false, this.outlet?.log); this.outlet?.log.info('Command off called'); }); this.coverLift = new MatterbridgeEndpoint([coverDevice, bridgedNode, powerSource], { uniqueStorageKey: 'CoverLift' }, this.config.debug) .createDefaultIdentifyClusterServer() .createDefaultGroupsClusterServer() .createDefaultBridgedDeviceBasicInformationClusterServer('Cover lift', 'CL01020564', 0xfff1, 'Matterbridge', 'Matterbridge Cover') .createDefaultWindowCoveringClusterServer() .createDefaultPowerSourceRechargeableBatteryClusterServer(86); this.coverLift = await this.addDevice(this.coverLift); this.coverLift?.addCommandHandler('identify', async ({ request: { identifyTime } }) => { this.coverLift?.log.info(`Command identify called identifyTime:${identifyTime}`); }); this.coverLift?.addCommandHandler('stopMotion', async () => { await this.coverLift?.setWindowCoveringTargetAsCurrentAndStopped(); this.coverLift?.log.info(`Command stopMotion called`); }); this.coverLift?.addCommandHandler('downOrClose', async () => { await this.coverLift?.setWindowCoveringCurrentTargetStatus(10000, 10000, WindowCovering.MovementStatus.Stopped); this.coverLift?.log.info(`Command downOrClose called`); }); this.coverLift?.addCommandHandler('upOrOpen', async () => { await this.coverLift?.setWindowCoveringCurrentTargetStatus(0, 0, WindowCovering.MovementStatus.Stopped); this.coverLift?.log.info(`Command upOrOpen called`); }); this.coverLift?.addCommandHandler('goToLiftPercentage', async ({ request: { liftPercent100thsValue } }) => { await this.coverLift?.setWindowCoveringCurrentTargetStatus(liftPercent100thsValue, liftPercent100thsValue, WindowCovering.MovementStatus.Stopped); this.coverLift?.log.info(`Command goToLiftPercentage ${liftPercent100thsValue} called`); }); this.coverLiftTilt = new MatterbridgeEndpoint([coverDevice, bridgedNode, powerSource], { uniqueStorageKey: 'CoverLiftTilt' }, this.config.debug) .createDefaultIdentifyClusterServer() .createDefaultGroupsClusterServer() .createDefaultBridgedDeviceBasicInformationClusterServer('Cover lift and tilt', 'CLT01020554', 0xfff1, 'Matterbridge', 'Matterbridge Cover') .createDefaultLiftTiltWindowCoveringClusterServer() .createDefaultPowerSourceRechargeableBatteryClusterServer(86); this.coverLiftTilt = await this.addDevice(this.coverLiftTilt); this.coverLiftTilt?.addCommandHandler('identify', async ({ request: { identifyTime } }) => { this.coverLiftTilt?.log.info(`Command identify called identifyTime:${identifyTime}`); }); this.coverLiftTilt?.addCommandHandler('stopMotion', async () => { await this.coverLiftTilt?.setWindowCoveringTargetAsCurrentAndStopped(); this.coverLiftTilt?.log.info(`Command stopMotion called`); }); this.coverLiftTilt?.addCommandHandler('downOrClose', async () => { await this.coverLiftTilt?.setWindowCoveringCurrentTargetStatus(10000, 10000, WindowCovering.MovementStatus.Stopped); this.coverLiftTilt?.log.info(`Command downOrClose called`); }); this.coverLiftTilt?.addCommandHandler('upOrOpen', async () => { await this.coverLiftTilt?.setWindowCoveringCurrentTargetStatus(0, 0, WindowCovering.MovementStatus.Stopped); this.coverLiftTilt?.log.info(`Command upOrOpen called`); }); this.coverLiftTilt?.addCommandHandler('goToLiftPercentage', async ({ request: { liftPercent100thsValue } }) => { await this.coverLiftTilt?.setWindowCoveringCurrentTargetStatus(liftPercent100thsValue, liftPercent100thsValue, WindowCovering.MovementStatus.Stopped); this.coverLiftTilt?.log.info(`Command goToLiftPercentage ${liftPercent100thsValue} called`); }); this.coverLiftTilt?.addCommandHandler('goToTiltPercentage', async ({ request: { tiltPercent100thsValue } }) => { const position = this.coverLiftTilt?.getAttribute(WindowCovering.Cluster.id, 'currentPositionLiftPercent100ths', this.coverLiftTilt?.log); await this.coverLiftTilt?.setWindowCoveringTargetAndCurrentPosition(position, tiltPercent100thsValue); this.coverLiftTilt?.log.info(`Command goToTiltPercentage ${tiltPercent100thsValue} called`); }); this.lock = new MatterbridgeEndpoint([doorLockDevice, bridgedNode, powerSource], { uniqueStorageKey: 'Lock' }, this.config.debug) .createDefaultIdentifyClusterServer() .createDefaultBridgedDeviceBasicInformationClusterServer('Lock', '0x96352164', 0xfff1, 'Matterbridge', 'Matterbridge Lock') .createDefaultDoorLockClusterServer() .createDefaultPowerSourceRechargeableBatteryClusterServer(30); this.lock = await this.addDevice(this.lock); this.lock?.addCommandHandler('identify', async ({ request: { identifyTime } }) => { this.lock?.log.info(`Command identify called identifyTime:${identifyTime}`); }); this.lock?.addCommandHandler('lockDoor', async () => { await this.lock?.setAttribute(DoorLock.Cluster.id, 'lockState', DoorLock.LockState.Locked, this.lock?.log); this.lock?.log.info('Command lockDoor called'); }); this.lock?.addCommandHandler('unlockDoor', async () => { await this.lock?.setAttribute(DoorLock.Cluster.id, 'lockState', DoorLock.LockState.Unlocked, this.lock?.log); this.lock?.log.info('Command unlockDoor called'); }); this.thermoAuto = new MatterbridgeEndpoint([thermostatDevice, bridgedNode, powerSource], { uniqueStorageKey: 'Thermostat (AutoMode)' }, this.config.debug) .createDefaultIdentifyClusterServer() .createDefaultGroupsClusterServer() .createDefaultBridgedDeviceBasicInformationClusterServer('Thermostat (AutoMode)', '0x96382164A', 0xfff1, 'Matterbridge', 'Matterbridge Thermostat') .createDefaultThermostatClusterServer(20, 18, 22) .createDefaultPowerSourceRechargeableBatteryClusterServer(70, PowerSource.BatChargeLevel.Ok, 4700); this.thermoAuto .addChildDeviceType('Flow', flowSensor) .createDefaultFlowMeasurementClusterServer(1 * 10) .addRequiredClusterServers(); this.thermoAuto .addChildDeviceType('Temperature', temperatureSensor) .createDefaultTemperatureMeasurementClusterServer(21 * 100) .addRequiredClusterServers(); this.thermoAuto .addChildDeviceType('Humidity', humiditySensor) .createDefaultRelativeHumidityMeasurementClusterServer(50 * 100) .addRequiredClusterServers(); this.thermoAuto = await this.addDevice(this.thermoAuto); this.thermoAuto?.addCommandHandler('identify', async ({ request: { identifyTime } }) => { this.thermoAuto?.log.info(`Command identify called identifyTime ${identifyTime}`); }); this.thermoAuto?.addCommandHandler('triggerEffect', async ({ request: { effectIdentifier, effectVariant } }) => { this.thermoAuto?.log.info(`Command identify called effectIdentifier ${effectIdentifier} effectVariant ${effectVariant}`); }); this.thermoAuto?.addCommandHandler('setpointRaiseLower', async ({ request: { mode, amount } }) => { const lookupSetpointAdjustMode = ['Heat', 'Cool', 'Both']; this.thermoAuto?.log.info(`Command setpointRaiseLower called with mode: ${lookupSetpointAdjustMode[mode]} amount: ${amount / 10}`); if (mode === Thermostat.SetpointRaiseLowerMode.Heat || mode === Thermostat.SetpointRaiseLowerMode.Both) { const setpoint = this.thermoAuto?.getAttribute(ThermostatCluster.id, 'occupiedHeatingSetpoint', this.thermoAuto?.log) / 100 + amount / 10; await this.thermoAuto?.setAttribute(ThermostatCluster.id, 'occupiedHeatingSetpoint', setpoint * 100, this.thermoAuto?.log); this.thermoAuto?.log.info('Set occupiedHeatingSetpoint:', setpoint); } if (mode === Thermostat.SetpointRaiseLowerMode.Cool || mode === Thermostat.SetpointRaiseLowerMode.Both) { const setpoint = this.thermoAuto?.getAttribute(ThermostatCluster.id, 'occupiedCoolingSetpoint', this.thermoAuto?.log) / 100 + amount / 10; await this.thermoAuto?.setAttribute(ThermostatCluster.id, 'occupiedCoolingSetpoint', setpoint * 100, this.thermoAuto?.log); this.thermoAuto?.log.info('Set occupiedCoolingSetpoint:', setpoint); } }); await this.thermoAuto?.subscribeAttribute(ThermostatCluster.id, 'systemMode', (value) => { const lookupSystemMode = ['Off', 'Auto', '', 'Cool', 'Heat', 'EmergencyHeat', 'Precooling', 'FanOnly', 'Dry', 'Sleep']; this.thermoAuto?.log.info('Subscribe systemMode called with:', lookupSystemMode[value]); }, this.thermoAuto.log); await this.thermoAuto?.subscribeAttribute(ThermostatCluster.id, 'occupiedHeatingSetpoint', (value) => { this.thermoAuto?.log.info('Subscribe occupiedHeatingSetpoint called with:', value / 100); }, this.thermoAuto.log); await this.thermoAuto?.subscribeAttribute(ThermostatCluster.id, 'occupiedCoolingSetpoint', (value) => { this.thermoAuto?.log.info('Subscribe occupiedCoolingSetpoint called with:', value / 100); }, this.thermoAuto.log); this.thermoHeat = new MatterbridgeEndpoint([thermostatDevice, bridgedNode, powerSource], { uniqueStorageKey: 'Thermostat (Heat)' }, this.config.debug) .createDefaultIdentifyClusterServer() .createDefaultGroupsClusterServer() .createDefaultBridgedDeviceBasicInformationClusterServer('Thermostat (Heat)', '0x96382164H', 0xfff1, 'Matterbridge', 'Matterbridge Thermostat') .createDefaultHeatingThermostatClusterServer(20, 18, 5, 35) .createDefaultPowerSourceReplaceableBatteryClusterServer(70, PowerSource.BatChargeLevel.Ok, 6010, 'AA 1.5V', 4); this.thermoHeat .addChildDeviceType('TemperatureIN', [temperatureSensor], { tagList: [ { mfgCode: null, namespaceId: LocationTag.Indoor.namespaceId, tag: LocationTag.Indoor.tag, label: null }, { mfgCode: null, namespaceId: NumberTag.One.namespaceId, tag: NumberTag.One.tag, label: null }, ], }) .createDefaultIdentifyClusterServer() .createDefaultTemperatureMeasurementClusterServer(21 * 100); this.thermoHeat .addChildDeviceType('TemperatureOUT', [temperatureSensor], { tagList: [ { mfgCode: null, namespaceId: LocationTag.Outdoor.namespaceId, tag: LocationTag.Outdoor.tag, label: null }, { mfgCode: null, namespaceId: NumberTag.Two.namespaceId, tag: NumberTag.Two.tag, label: null }, ], }) .createDefaultIdentifyClusterServer() .createDefaultTemperatureMeasurementClusterServer(15 * 100); this.thermoHeat = await this.addDevice(this.thermoHeat); this.thermoHeat?.addCommandHandler('identify', async ({ request: { identifyTime } }) => { this.thermoHeat?.log.info(`Command identify called identifyTime ${identifyTime}`); }); this.thermoHeat?.addCommandHandler('triggerEffect', async ({ request: { effectIdentifier, effectVariant } }) => { this.thermoHeat?.log.info(`Command identify called effectIdentifier ${effectIdentifier} effectVariant ${effectVariant}`); }); await this.thermoHeat?.subscribeAttribute(ThermostatCluster.id, 'systemMode', (value) => { const lookupSystemMode = ['Off', 'Auto', '', 'Cool', 'Heat', 'EmergencyHeat', 'Precooling', 'FanOnly', 'Dry', 'Sleep']; this.thermoHeat?.log.info('Subscribe systemMode called with:', lookupSystemMode[value]); }, this.thermoHeat.log); await this.thermoHeat?.subscribeAttribute(ThermostatCluster.id, 'occupiedHeatingSetpoint', (value) => { this.thermoHeat?.log.info('Subscribe occupiedHeatingSetpoint called with:', value / 100); }, this.thermoHeat.log); this.thermoCool = new MatterbridgeEndpoint([thermostatDevice, bridgedNode, powerSource], { uniqueStorageKey: 'Thermostat (Cool)' }, this.config.debug) .createDefaultIdentifyClusterServer() .createDefaultGroupsClusterServer() .createDefaultBridgedDeviceBasicInformationClusterServer('Thermostat (Cool)', '0x96382164C', 0xfff1, 'Matterbridge', 'Matterbridge Thermostat') .createDefaultCoolingThermostatClusterServer(20, 18, 5, 35) .createDefaultPowerSourceReplaceableBatteryClusterServer(40, PowerSource.BatChargeLevel.Ok, 5080, 'AA 1.5V', 4); this.thermoCool = await this.addDevice(this.thermoCool); this.thermoCool?.addCommandHandler('identify', async ({ request: { identifyTime } }) => { this.thermoCool?.log.info(`Command identify called identifyTime ${identifyTime}`); }); this.thermoCool?.addCommandHandler('triggerEffect', async ({ request: { effectIdentifier, effectVariant } }) => { this.thermoCool?.log.info(`Command identify called effectIdentifier ${effectIdentifier} effectVariant ${effectVariant}`); }); await this.thermoCool?.subscribeAttribute(ThermostatCluster.id, 'systemMode', (value) => { const lookupSystemMode = ['Off', 'Auto', '', 'Cool', 'Heat', 'EmergencyHeat', 'Precooling', 'FanOnly', 'Dry', 'Sleep']; this.thermoCool?.log.info('Subscribe systemMode called with:', lookupSystemMode[value]); }, this.thermoCool.log); await this.thermoCool?.subscribeAttribute(ThermostatCluster.id, 'occupiedCoolingSetpoint', (value) => { this.thermoCool?.log.info('Subscribe occupiedCoolingSetpoint called with:', value / 100); }, this.thermoCool.log); this.airPurifier = new MatterbridgeEndpoint([airPurifier, temperatureSensor, humiditySensor, bridgedNode, powerSource], { uniqueStorageKey: 'Air purifier' }, this.config.debug) .createDefaultBridgedDeviceBasicInformationClusterServer('Air purifier', '0x96584864AP', 0xfff1, 'Matterbridge', 'Matterbridge Air purifier') .createDefaultIdentifyClusterServer() .createDefaultFanControlClusterServer() .createDefaultTemperatureMeasurementClusterServer(20 * 100) .createDefaultRelativeHumidityMeasurementClusterServer(50 * 100) .createDefaultPowerSourceWiredClusterServer() .createDefaultActivatedCarbonFilterMonitoringClusterServer() .createDefaultHepaFilterMonitoringClusterServer(); this.airPurifier = await this.addDevice(this.airPurifier); this.airPurifier?.addCommandHandler('identify', async ({ request: { identifyTime } }) => { this.airPurifier?.log.info(`Command identify called identifyTime:${identifyTime}`); }); await this.airPurifier?.subscribeAttribute(FanControl.Cluster.id, 'fanMode', (newValue, oldValue, context) => { this.airPurifier?.log.info(`Fan mode changed from ${this.fanModeLookup[oldValue]} to ${this.fanModeLookup[newValue]} context: ${context.offline === true ? 'offline' : 'online'}`); if (context.offline === true) return; if (newValue === FanControl.FanMode.Off) { this.airPurifier?.setAttribute(FanControl.Cluster.id, 'percentSettings', 0, this.airPurifier?.log); this.airPurifier?.setAttribute(FanControl.Cluster.id, 'percentCurrent', 0, this.airPurifier?.log); } else if (newValue === FanControl.FanMode.Low) { this.airPurifier?.setAttribute(FanControl.Cluster.id, 'percentSettings', 33, this.airPurifier?.log); this.airPurifier?.setAttribute(FanControl.Cluster.id, 'percentCurrent', 33, this.airPurifier?.log); } else if (newValue === FanControl.FanMode.Medium) { this.airPurifier?.setAttribute(FanControl.Cluster.id, 'percentSettings', 66, this.airPurifier?.log); this.airPurifier?.setAttribute(FanControl.Cluster.id, 'percentCurrent', 66, this.airPurifier?.log); } else if (newValue === FanControl.FanMode.High) { this.airPurifier?.setAttribute(FanControl.Cluster.id, 'percentSettings', 100, this.airPurifier?.log); this.airPurifier?.setAttribute(FanControl.Cluster.id, 'percentCurrent', 100, this.airPurifier?.log); } else if (newValue === FanControl.FanMode.On) { this.airPurifier?.setAttribute(FanControl.Cluster.id, 'percentSettings', 100, this.airPurifier?.log); this.airPurifier?.setAttribute(FanControl.Cluster.id, 'percentCurrent', 100, this.airPurifier?.log); } }, this.airPurifier.log); await this.airPurifier?.subscribeAttribute(FanControl.Cluster.id, 'percentSetting', (newValue, oldValue, context) => { this.airPurifier?.log.info(`Percent setting changed from ${oldValue} to ${newValue} context: ${context.offline === true ? 'offline' : 'online'}`); if (context.offline === true) return; if (isValidNumber(newValue, 0, 100)) this.airPurifier?.setAttribute(FanControl.Cluster.id, 'percentCurrent', newValue, this.airPurifier?.log); }, this.airPurifier.log); this.airConditioner = new MatterbridgeEndpoint([airConditioner, bridgedNode, powerSource], { uniqueStorageKey: 'Air Conditioner' }, this.config.debug) .createDefaultBridgedDeviceBasicInformationClusterServer('Air Conditioner', '0x96382864AC', 0xfff1, 'Matterbridge', 'Matterbridge Air Conditioner') .createDefaultIdentifyClusterServer() .createDeadFrontOnOffClusterServer(true) .createDefaultThermostatClusterServer(20, 18, 22) .createDefaultThermostatUserInterfaceConfigurationClusterServer() .createDefaultFanControlClusterServer(FanControl.FanMode.Auto) .createDefaultTemperatureMeasurementClusterServer(20 * 100) .createDefaultRelativeHumidityMeasurementClusterServer(50 * 100) .createDefaultPowerSourceWiredClusterServer() .addRequiredClusterServers(); this.airConditioner = await this.addDevice(this.airConditioner); this.airConditioner?.addCommandHandler('identify', async ({ request: { identifyTime } }) => { this.airConditioner?.log.info(`Command identify called identifyTime:${identifyTime}`); }); this.airConditioner?.addCommandHandler('on', async () => { this.airConditioner?.log.info('Command on called'); await this.airConditioner?.setAttribute(ThermostatCluster.id, 'localTemperature', 20 * 100, this.airConditioner?.log); await this.airConditioner?.setAttribute(TemperatureMeasurement.Cluster.id, 'measuredValue', 20 * 100, this.airConditioner?.log); await this.airConditioner?.setAttribute(RelativeHumidityMeasurementCluster.id, 'measuredValue', 50 * 100, this.airConditioner?.log); await this.airConditioner?.setAttribute(FanControl.Cluster.id, 'percentSetting', 50, this.airConditioner?.log); }); this.airConditioner?.addCommandHandler('off', async () => { this.airConditioner?.log.info('Command off called'); await this.airConditioner?.setAttribute(ThermostatCluster.id, 'localTemperature', null, this.airConditioner?.log); await this.airConditioner?.setAttribute(TemperatureMeasurement.Cluster.id, 'measuredValue', null, this.airConditioner?.log); await this.airConditioner?.setAttribute(RelativeHumidityMeasurementCluster.id, 'measuredValue', null, this.airConditioner?.log); await this.airConditioner?.setAttribute(FanControl.Cluster.id, 'percentSetting', null, this.airConditioner?.log); }); this.pump = new MatterbridgeEndpoint([pumpDevice, bridgedNode, powerSource], { uniqueStorageKey: 'Pump' }, this.config.debug) .createDefaultBridgedDeviceBasicInformationClusterServer('Pump', '0x96382864PUMP', 0xfff1, 'Matterbridge', 'Matterbridge Pump') .createDefaultIdentifyClusterServer() .createOnOffClusterServer() .createLevelControlClusterServer() .createDefaultPumpConfigurationAndControlClusterServer() .createDefaultPowerSourceWiredClusterServer(); this.pump = await this.addDevice(this.pump); this.pump?.addCommandHandler('identify', async ({ request: { identifyTime } }) => { this.pump?.log.info(`Command identify called identifyTime:${identifyTime}`); }); this.pump?.addCommandHandler('on', async () => { this.pump?.log.info('Command on called'); }); this.pump?.addCommandHandler('off', async () => { this.pump?.log.info('Command off called'); }); this.pump?.addCommandHandler('moveToLevel', async ({ request: { level } }) => { this.pump?.log.info(`Command moveToLevel called request: ${level}`); }); this.pump?.addCommandHandler('moveToLevelWithOnOff', async ({ request: { level } }) => { this.pump?.log.info(`Command moveToLevelWithOnOff called request: ${level}`); }); this.valve = new MatterbridgeEndpoint([waterValve, bridgedNode, powerSource], { uniqueStorageKey: 'Water valve' }, this.config.debug) .createDefaultBridgedDeviceBasicInformationClusterServer('Water valve', '0x96382864WV', 0xfff1, 'Matterbridge', 'Matterbridge Water valve') .createDefaultIdentifyClusterServer() .createDefaultValveConfigurationAndControlClusterServer() .createDefaultPowerSourceWiredClusterServer(); this.valve = await this.addDevice(this.valve); this.valve?.addCommandHandler('identify', async ({ request: { identifyTime } }) => { this.valve?.log.info(`Command identify called identifyTime:${identifyTime}`); }); this.fanDefault = new MatterbridgeEndpoint([fanDevice, bridgedNode, powerSource], { uniqueStorageKey: 'Fan off low medium high auto' }, this.config.debug) .createDefaultBridgedDeviceBasicInformationClusterServer('Fan', 'FAN_980545631228', 0xfff1, 'Matterbridge', 'Matterbridge Fan') .createDefaultPowerSourceWiredClusterServer() .addRequiredClusterServers(); this.fanDefault = await this.addDevice(this.fanDefault); await this.fanDefault?.subscribeAttribute(FanControl.Cluster.id, 'fanMode', (newValue, oldValue, context) => { this.fanDefault?.log.info(`Fan mode changed from ${this.fanModeLookup[oldValue]} to ${this.fanModeLookup[newValue]} context: ${context.offline === true ? 'offline' : 'online'}`); if (context.offline === true) return; if (newValue === FanControl.FanMode.Off) { this.fanDefault?.setAttribute(FanControl.Cluster.id, 'percentSetting', 0, this.fanDefault?.log); this.fanDefault?.setAttribute(FanControl.Cluster.id, 'percentCurrent', 0, this.fanDefault?.log); } else if (newValue === FanControl.FanMode.Low) { this.fanDefault?.setAttribute(FanControl.Cluster.id, 'percentSetting', 33, this.fanDefault?.log); this.fanDefault?.setAttribute(FanControl.Cluster.id, 'percentCurrent', 33, this.fanDefault?.log); } else if (newValue === FanControl.FanMode.Medium) { this.fanDefault?.setAttribute(FanControl.Cluster.id, 'percentSetting', 66, this.fanDefault?.log); this.fanDefault?.setAttribute(FanControl.Cluster.id, 'percentCurrent', 66, this.fanDefault?.log); } else if (newValue === FanControl.FanMode.High) { this.fanDefault?.setAttr