UNPKG

matterbridge

Version:
376 lines • 22.9 kB
/** * This file contains the class MatterbridgeEndpoint that extends the Endpoint class from the Matter.js library. * * @file matterbridgeBehaviors.ts * @author Luca Liguori * @date 2024-11-07 * @version 1.0.0 * * Copyright 2024, 2025, 2026 Luca Liguori. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ /* eslint-disable @typescript-eslint/no-namespace */ /* eslint-disable @typescript-eslint/no-unused-vars */ /* eslint-disable @typescript-eslint/no-explicit-any */ // @matter import { Behavior } from '@matter/main'; // @matter clusters import { BooleanStateConfiguration } from '@matter/main/clusters/boolean-state-configuration'; import { ColorControl } from '@matter/main/clusters/color-control'; import { FanControl } from '@matter/main/clusters/fan-control'; import { WindowCovering } from '@matter/main/clusters/window-covering'; import { Thermostat } from '@matter/main/clusters/thermostat'; import { ValveConfigurationAndControl } from '@matter/main/clusters/valve-configuration-and-control'; import { SmokeCoAlarm } from '@matter/main/clusters/smoke-co-alarm'; import { BooleanStateConfigurationServer } from '@matter/main/behaviors/boolean-state-configuration'; // @matter behaviors import { IdentifyServer } from '@matter/main/behaviors/identify'; import { OnOffServer } from '@matter/main/behaviors/on-off'; import { LevelControlServer } from '@matter/main/behaviors/level-control'; import { ColorControlServer } from '@matter/main/behaviors/color-control'; import { WindowCoveringServer } from '@matter/main/behaviors/window-covering'; import { DoorLockServer } from '@matter/main/behaviors/door-lock'; import { FanControlServer } from '@matter/main/behaviors/fan-control'; import { ThermostatServer } from '@matter/main/behaviors/thermostat'; import { ValveConfigurationAndControlServer } from '@matter/main/behaviors/valve-configuration-and-control'; import { ModeSelectServer } from '@matter/main/behaviors/mode-select'; import { SmokeCoAlarmServer } from '@matter/main/behaviors/smoke-co-alarm'; import { SwitchServer } from '@matter/main/behaviors/switch'; export class MatterbridgeBehaviorDevice { log; commandHandler; device; // Will be a plugin device endpointId = undefined; endpointNumber = undefined; constructor(log, commandHandler, device) { this.log = log; this.commandHandler = commandHandler; this.device = device; } setEndpointId(endpointId) { this.endpointId = endpointId; } setEndpointNumber(endpointNumber) { this.endpointNumber = endpointNumber; } identify({ identifyTime }) { this.log.info(`Identifying device for ${identifyTime} seconds`); this.commandHandler.executeHandler('identify', { request: { identifyTime }, attributes: {}, endpoint: { number: this.endpointNumber, uniqueStorageKey: this.endpointId } }); } triggerEffect({ effectIdentifier, effectVariant }) { this.log.info(`Triggering effect ${effectIdentifier} variant ${effectVariant}`); this.commandHandler.executeHandler('triggerEffect', { request: { effectIdentifier, effectVariant }, attributes: {}, endpoint: { number: this.endpointNumber, uniqueStorageKey: this.endpointId } }); } on() { this.log.info(`Switching device on (endpoint ${this.endpointId}.${this.endpointNumber})`); this.commandHandler.executeHandler('on', { request: {}, attributes: {}, endpoint: { number: this.endpointNumber, uniqueStorageKey: this.endpointId } }); } off() { this.log.info(`Switching device off (endpoint ${this.endpointId}.${this.endpointNumber})`); this.commandHandler.executeHandler('off', { request: {}, attributes: {}, endpoint: { number: this.endpointNumber, uniqueStorageKey: this.endpointId } }); } toggle() { this.log.info(`Toggle device on/off (endpoint ${this.endpointId}.${this.endpointNumber})`); this.commandHandler.executeHandler('toggle', { request: {}, attributes: {}, endpoint: { number: this.endpointNumber, uniqueStorageKey: this.endpointId } }); } moveToLevel({ level, transitionTime, optionsMask, optionsOverride }) { this.log.info(`Setting level to ${level} with transitionTime ${transitionTime} (endpoint ${this.endpointId}.${this.endpointNumber})`); this.commandHandler.executeHandler('moveToLevel', { request: { level, transitionTime, optionsMask, optionsOverride }, attributes: {}, endpoint: { number: this.endpointNumber, uniqueStorageKey: this.endpointId } }); } moveToLevelWithOnOff({ level, transitionTime, optionsMask, optionsOverride }) { this.log.info(`Setting level to ${level} with transitionTime ${transitionTime} (endpoint ${this.endpointId}.${this.endpointNumber})`); this.commandHandler.executeHandler('moveToLevelWithOnOff', { request: { level, transitionTime, optionsMask, optionsOverride }, attributes: {}, endpoint: { number: this.endpointNumber, uniqueStorageKey: this.endpointId } }); } moveToHue({ optionsMask, optionsOverride, hue, direction, transitionTime }) { this.log.info(`Setting hue to ${hue} with transitionTime ${transitionTime} (endpoint ${this.endpointId}.${this.endpointNumber})`); this.commandHandler.executeHandler('moveToHue', { request: { optionsMask, optionsOverride, hue, direction, transitionTime }, attributes: {}, endpoint: { number: this.endpointNumber, uniqueStorageKey: this.endpointId } }); } moveToSaturation({ optionsMask, optionsOverride, saturation, transitionTime }) { this.log.info(`Setting saturation to ${saturation} with transitionTime ${transitionTime} (endpoint ${this.endpointId}.${this.endpointNumber})`); this.commandHandler.executeHandler('moveToSaturation', { request: { optionsMask, optionsOverride, saturation, transitionTime }, attributes: {}, endpoint: { number: this.endpointNumber, uniqueStorageKey: this.endpointId } }); } moveToHueAndSaturation({ optionsOverride, optionsMask, saturation, hue, transitionTime }) { this.log.info(`Setting hue to ${hue} and saturation to ${saturation} with transitionTime ${transitionTime} (endpoint ${this.endpointId}.${this.endpointNumber})`); this.commandHandler.executeHandler('moveToHueAndSaturation', { request: { optionsOverride, optionsMask, saturation, hue, transitionTime }, attributes: {}, endpoint: { number: this.endpointNumber, uniqueStorageKey: this.endpointId } }); } moveToColor({ optionsMask, optionsOverride, colorX, colorY, transitionTime }) { this.log.info(`Setting color to ${colorX}, ${colorY} with transitionTime ${transitionTime} (endpoint ${this.endpointId}.${this.endpointNumber})`); this.commandHandler.executeHandler('moveToColor', { request: { optionsMask, optionsOverride, colorX, colorY, transitionTime }, attributes: {}, endpoint: { number: this.endpointNumber, uniqueStorageKey: this.endpointId } }); } moveToColorTemperature({ optionsOverride, optionsMask, colorTemperatureMireds, transitionTime }) { this.log.info(`Setting color temperature to ${colorTemperatureMireds} with transitionTime ${transitionTime} (endpoint ${this.endpointId}.${this.endpointNumber})`); this.commandHandler.executeHandler('moveToColorTemperature', { request: { optionsOverride, optionsMask, colorTemperatureMireds, transitionTime }, attributes: {}, endpoint: { number: this.endpointNumber, uniqueStorageKey: this.endpointId }, }); } upOrOpen() { this.log.info(`Opening cover (endpoint ${this.endpointId}.${this.endpointNumber})`); this.commandHandler.executeHandler(`upOrOpen`, { request: {}, attributes: {}, endpoint: { number: this.endpointNumber, uniqueStorageKey: this.endpointId } }); } downOrClose() { this.log.info(`Closing cover (endpoint ${this.endpointId}.${this.endpointNumber})`); this.commandHandler.executeHandler(`downOrClose`, { request: {}, attributes: {}, endpoint: { number: this.endpointNumber, uniqueStorageKey: this.endpointId } }); } stopMotion() { this.log.info(`Stopping cover (endpoint ${this.endpointId}.${this.endpointNumber})`); this.commandHandler.executeHandler('stopMotion', { request: {}, attributes: {}, endpoint: { number: this.endpointNumber, uniqueStorageKey: this.endpointId } }); } goToLiftPercentage({ liftPercent100thsValue }) { this.log.info(`Setting cover lift percentage to ${liftPercent100thsValue} (endpoint ${this.endpointId}.${this.endpointNumber})`); this.commandHandler.executeHandler('goToLiftPercentage', { request: { liftPercent100thsValue }, attributes: {}, endpoint: { number: this.endpointNumber, uniqueStorageKey: this.endpointId } }); } lockDoor() { this.log.info(`Locking door (endpoint ${this.endpointId}.${this.endpointNumber})`); this.commandHandler.executeHandler('lockDoor', { request: {}, attributes: {}, endpoint: { number: this.endpointNumber, uniqueStorageKey: this.endpointId } }); } unlockDoor() { this.log.info(`Unlocking door (endpoint ${this.endpointId}.${this.endpointNumber})`); this.commandHandler.executeHandler('unlockDoor', { request: {}, attributes: {}, endpoint: { number: this.endpointNumber, uniqueStorageKey: this.endpointId } }); } step({ direction, wrap, lowestOff }) { this.log.info(`Stepping fan with direction ${direction} (endpoint ${this.endpointId}.${this.endpointNumber})`); this.commandHandler.executeHandler('step', { request: { direction, wrap, lowestOff }, attributes: {}, endpoint: { number: this.endpointNumber, uniqueStorageKey: this.endpointId } }); } setpointRaiseLower({ mode, amount }) { this.log.info(`Setting setpoint to ${amount} in mode ${mode} (endpoint ${this.endpointId}.${this.endpointNumber})`); this.commandHandler.executeHandler('setpointRaiseLower', { request: { mode, amount }, attributes: {}, endpoint: { number: this.endpointNumber, uniqueStorageKey: this.endpointId } }); } open({ openDuration, targetLevel }) { this.log.info(`Opening valve (endpoint ${this.endpointId}.${this.endpointNumber})`); this.commandHandler.executeHandler('open', { request: { openDuration, targetLevel }, attributes: {}, endpoint: { number: this.endpointNumber, uniqueStorageKey: this.endpointId } }); } close() { this.log.info(`Closing valve (endpoint ${this.endpointId}.${this.endpointNumber})`); this.commandHandler.executeHandler('close', { request: {}, attributes: {}, endpoint: { number: this.endpointNumber, uniqueStorageKey: this.endpointId } }); } changeToMode({ newMode }) { this.log.info(`Changing mode to ${newMode}`); this.commandHandler.executeHandler('changeToMode', { request: { newMode }, attributes: {}, endpoint: { number: this.endpointNumber, uniqueStorageKey: this.endpointId } }); } selfTestRequest() { this.log.info(`Testing SmokeCOAlarm (endpoint ${this.endpointId}.${this.endpointNumber})`); this.commandHandler.executeHandler('selfTestRequest', { request: {}, attributes: {}, endpoint: { number: this.endpointNumber, uniqueStorageKey: this.endpointId } }); } enableDisableAlarm({ alarmsToEnableDisable }) { this.log.info(`Enabling/disabling alarm ${alarmsToEnableDisable}`); this.commandHandler.executeHandler('enableDisableAlarm', { request: { alarmsToEnableDisable }, attributes: {}, endpoint: { number: this.endpointNumber, uniqueStorageKey: this.endpointId } }); } } export class MatterbridgeBehavior extends Behavior { static id = 'matterbridge'; } (function (MatterbridgeBehavior) { class State { deviceCommand; } MatterbridgeBehavior.State = State; })(MatterbridgeBehavior || (MatterbridgeBehavior = {})); export class MatterbridgeIdentifyServer extends IdentifyServer { initialize() { const device = this.agent.get(MatterbridgeBehavior).state.deviceCommand; device.setEndpointId(this.endpoint.maybeId); device.setEndpointNumber(this.endpoint.maybeNumber); super.initialize(); } identify({ identifyTime }) { const device = this.agent.get(MatterbridgeBehavior).state.deviceCommand; device.identify({ identifyTime }); super.identify({ identifyTime }); } triggerEffect({ effectIdentifier, effectVariant }) { const device = this.agent.get(MatterbridgeBehavior).state.deviceCommand; device.triggerEffect({ effectIdentifier, effectVariant }); super.triggerEffect({ effectIdentifier, effectVariant }); } } export class MatterbridgeOnOffServer extends OnOffServer { async on() { const device = this.agent.get(MatterbridgeBehavior).state.deviceCommand; device.on(); super.on(); } async off() { const device = this.agent.get(MatterbridgeBehavior).state.deviceCommand; device.off(); super.off(); } async toggle() { const device = this.agent.get(MatterbridgeBehavior).state.deviceCommand; device.toggle(); super.toggle(); } } export class MatterbridgeLevelControlServer extends LevelControlServer { async moveToLevel({ level, transitionTime, optionsMask, optionsOverride }) { const device = this.agent.get(MatterbridgeBehavior).state.deviceCommand; device.moveToLevel({ level, transitionTime, optionsMask, optionsOverride }); super.moveToLevel({ level, transitionTime, optionsMask, optionsOverride }); } async moveToLevelWithOnOff({ level, transitionTime, optionsMask, optionsOverride }) { const device = this.agent.get(MatterbridgeBehavior).state.deviceCommand; device.moveToLevelWithOnOff({ level, transitionTime, optionsMask, optionsOverride }); super.moveToLevelWithOnOff({ level, transitionTime, optionsMask, optionsOverride }); } } export class MatterbridgeColorControlServer extends ColorControlServer.with(ColorControl.Feature.HueSaturation, ColorControl.Feature.Xy, ColorControl.Feature.ColorTemperature) { async moveToHue({ optionsMask, optionsOverride, hue, direction, transitionTime }) { const device = this.agent.get(MatterbridgeBehavior).state.deviceCommand; device.moveToHue({ optionsMask, optionsOverride, hue, direction, transitionTime }); super.moveToHue({ optionsMask, optionsOverride, hue, direction, transitionTime }); } async moveToSaturation({ optionsMask, optionsOverride, saturation, transitionTime }) { const device = this.agent.get(MatterbridgeBehavior).state.deviceCommand; device.moveToSaturation({ optionsMask, optionsOverride, saturation, transitionTime }); super.moveToSaturation({ optionsMask, optionsOverride, saturation, transitionTime }); } async moveToHueAndSaturation({ optionsOverride, optionsMask, saturation, hue, transitionTime }) { const device = this.agent.get(MatterbridgeBehavior).state.deviceCommand; device.moveToHueAndSaturation({ optionsOverride, optionsMask, saturation, hue, transitionTime }); super.moveToHueAndSaturation({ optionsOverride, optionsMask, saturation, hue, transitionTime }); } async moveToColor({ optionsMask, optionsOverride, colorX, colorY, transitionTime }) { const device = this.agent.get(MatterbridgeBehavior).state.deviceCommand; device.moveToColor({ optionsMask, optionsOverride, colorX, colorY, transitionTime }); super.moveToColor({ optionsMask, optionsOverride, colorX, colorY, transitionTime }); } async moveToColorTemperature({ optionsOverride, optionsMask, colorTemperatureMireds, transitionTime }) { const device = this.agent.get(MatterbridgeBehavior).state.deviceCommand; device.moveToColorTemperature({ optionsOverride, optionsMask, colorTemperatureMireds, transitionTime }); super.moveToColorTemperature({ optionsOverride, optionsMask, colorTemperatureMireds, transitionTime }); } } export class MatterbridgeWindowCoveringServer extends WindowCoveringServer.with(WindowCovering.Feature.Lift, WindowCovering.Feature.PositionAwareLift) { async upOrOpen() { const device = this.agent.get(MatterbridgeBehavior).state.deviceCommand; device.upOrOpen(); super.upOrOpen(); } async downOrClose() { const device = this.agent.get(MatterbridgeBehavior).state.deviceCommand; device.downOrClose(); super.downOrClose(); } stopMotion() { const device = this.agent.get(MatterbridgeBehavior).state.deviceCommand; device.stopMotion(); super.stopMotion(); } goToLiftPercentage({ liftPercent100thsValue }) { const device = this.agent.get(MatterbridgeBehavior).state.deviceCommand; device.goToLiftPercentage({ liftPercent100thsValue }); super.goToLiftPercentage({ liftPercent100thsValue }); } async handleMovement(type, reversed, direction, targetPercent100ths) { // Do nothing here, as the device will handle the movement } } export class MatterbridgeDoorLockServer extends DoorLockServer { async lockDoor() { const device = this.agent.get(MatterbridgeBehavior).state.deviceCommand; device.lockDoor(); super.lockDoor(); } async unlockDoor() { const device = this.agent.get(MatterbridgeBehavior).state.deviceCommand; device.unlockDoor(); super.unlockDoor(); } } export class MatterbridgeModeSelectServer extends ModeSelectServer { async changeToMode({ newMode }) { const device = this.agent.get(MatterbridgeBehavior).state.deviceCommand; device.changeToMode({ newMode }); super.changeToMode({ newMode }); } } export class MatterbridgeFanControlServer extends FanControlServer.with(FanControl.Feature.MultiSpeed, FanControl.Feature.Auto, FanControl.Feature.Step) { async step({ direction, wrap, lowestOff }) { const device = this.agent.get(MatterbridgeBehavior).state.deviceCommand; device.step({ direction, wrap, lowestOff }); const lookupStepDirection = ['Increase', 'Decrease']; device.log.debug(`Command step called with direction: ${lookupStepDirection[direction]} wrap: ${wrap} lowestOff: ${lowestOff}`); device.log.debug(`- current percentCurrent: ${this.state.percentCurrent}`); if (direction === FanControl.StepDirection.Increase) { if (wrap && this.state.percentCurrent === 100) { this.state.percentCurrent = lowestOff ? 0 : 10; } else this.state.percentCurrent = Math.min(this.state.percentCurrent + 10, 100); } else if (direction === FanControl.StepDirection.Decrease) { if (wrap && this.state.percentCurrent === (lowestOff ? 0 : 10)) { this.state.percentCurrent = 100; } else this.state.percentCurrent = Math.max(this.state.percentCurrent - 10, lowestOff ? 0 : 10); } device.log.debug('Set percentCurrent to:', this.state.percentCurrent); super.step({ direction, wrap, lowestOff }); } } export class MatterbridgeThermostatServer extends ThermostatServer.with(Thermostat.Feature.Cooling, Thermostat.Feature.Heating, Thermostat.Feature.AutoMode) { async setpointRaiseLower({ mode, amount }) { const device = this.agent.get(MatterbridgeBehavior).state.deviceCommand; device.setpointRaiseLower({ mode, amount }); const lookupSetpointAdjustMode = ['Heat', 'Cool', 'Both']; device.log.debug(`Command setpointRaiseLower called with mode: ${lookupSetpointAdjustMode[mode]} amount: ${amount / 10}`); device.log.debug(`- current occupiedHeatingSetpoint: ${this.state.occupiedHeatingSetpoint / 100}`); device.log.debug(`- current occupiedCoolingSetpoint: ${this.state.occupiedCoolingSetpoint / 100}`); if ((mode === Thermostat.SetpointRaiseLowerMode.Heat || mode === Thermostat.SetpointRaiseLowerMode.Both) && this.state.occupiedHeatingSetpoint !== undefined) { const setpoint = this.state.occupiedHeatingSetpoint / 100 + amount / 10; this.state.occupiedHeatingSetpoint = setpoint * 100; device.log.debug('Set occupiedHeatingSetpoint to:', setpoint); } if ((mode === Thermostat.SetpointRaiseLowerMode.Cool || mode === Thermostat.SetpointRaiseLowerMode.Both) && this.state.occupiedCoolingSetpoint !== undefined) { const setpoint = this.state.occupiedCoolingSetpoint / 100 + amount / 10; this.state.occupiedCoolingSetpoint = setpoint * 100; device.log.debug('Set occupiedCoolingSetpoint to:', setpoint); } super.setpointRaiseLower({ mode, amount }); } } export class MatterbridgeValveConfigurationAndControlServer extends ValveConfigurationAndControlServer.with(ValveConfigurationAndControl.Feature.Level) { async open({ openDuration, targetLevel }) { const device = this.agent.get(MatterbridgeBehavior).state.deviceCommand; device.open({ openDuration, targetLevel }); super.open({ openDuration, targetLevel }); } async close() { const device = this.agent.get(MatterbridgeBehavior).state.deviceCommand; device.close(); super.close(); } } export class MatterbridgeSmokeCoAlarmServer extends SmokeCoAlarmServer.with(SmokeCoAlarm.Feature.SmokeAlarm, SmokeCoAlarm.Feature.CoAlarm) { async selfTestRequest() { const device = this.agent.get(MatterbridgeBehavior).state.deviceCommand; device.selfTestRequest(); super.selfTestRequest(); } } export class MatterbridgeBooleanStateConfigurationServer extends BooleanStateConfigurationServer.with(BooleanStateConfiguration.Feature.Visual, BooleanStateConfiguration.Feature.Audible, BooleanStateConfiguration.Feature.SensitivityLevel) { async enableDisableAlarm({ alarmsToEnableDisable }) { const device = this.agent.get(MatterbridgeBehavior).state.deviceCommand; device.enableDisableAlarm({ alarmsToEnableDisable }); super.enableDisableAlarm({ alarmsToEnableDisable }); } } export class MatterbridgeSwitchServer extends SwitchServer { initialize() { // Do nothing here, as the device will handle the switch logic } } //# sourceMappingURL=matterbridgeBehaviors.js.map