homebridge-broadlink-rm-enhanced
Version:
Broadlink RM plugin (including the mini and pro) for homebridge: https://github.com/nfarina/homebridge
281 lines (233 loc) • 9.4 kB
JavaScript
const ServiceManagerTypes = require('../helpers/serviceManagerTypes');
const SwitchAccessory = require('./switch');
class FanAccessory extends SwitchAccessory {
async setSwitchState (hexData, previousValue) {
const { config, state, serviceManager } = this;
const requestedValue = state.active // state is already set by main handler before this subhandler is called
const { showRotationDirection, showSwingMode, availableFanSpeed } = config
if (!state.active) {
this.lastFanSpeed = undefined;
}
// Reset the fan speed back to the default speed when turned off
if (state.active === Characteristic.Active.INACTIVE
&& config && config.alwaysResetToDefaults) {
this.setDefaults();
if (showSwingMode) {
serviceManager.service.updateCharacteristic(Characteristic.SwingMode, state.swingMode)
}
if (availableFanSpeed) {
serviceManager.service.updateCharacteristic(Characteristic.RotationSpeed, state.fanSpeed)
}
if (showRotationDirection) {
serviceManager.service.updateCharacteristic(Characteristic.RotationDirection, state.rotationDirection)
}
}
// If fanSpeeds are available and the 'on' hex code does not automatically set the last
// known state, then find the right hexCode to turn on the fan to last known state.
if (!config.alwaysResetToDefaults && !config.powerOnMemoryAvailable
&& requestedValue === Characteristic.Active.ACTIVE
&& config.availableFanSpeed) {
await this.setFanSpeed(hexData)
return
}
super.setSwitchState(hexData, previousValue);
}
didDefineFanSpeed() {
const { config } = this
const { data } = config
// Create an array of speeds specified in the data config
let foundFanSpeed = false;
const allHexKeys = Object.keys(data || {});
for (let index in allHexKeys) {
const key = allHexKeys[index]
const parts = key.split('fanSpeed');
if (parts.length === 2) {
foundFanSpeed = true
break
}
}
return foundFanSpeed
}
setDefaults () {
super.setDefaults();
let { config, state } = this;
// Reset the fan speed back to the default speed when turned off
// This will also be called whenever homebridge is restarted
if (config && config.alwaysResetToDefaults) {
state.fanSpeed = (config.defaultFanSpeed !== undefined) ? config.defaultFanSpeed : 100
state.swingMode = config.defaultSwingMode === "on" ? Characteristic.SwingMode.SWING_ENABLED : Characteristic.SwingMode.SWING_DISABLED
if (config.defaultRotationDirection === undefined || config.defaultRotationDirection === "clockwise") {
state.rotationDirection = Characteristic.RotationDirection.CLOCKWISE
} else {
state.rotationDirection = Characteristic.RotationDirection.COUNTER_CLOCKWISE
}
}
}
async setFanSpeed (hexData) {
const { data, host, log, state, name, debug} = this;
this.reset();
// Create an array of speeds specified in the data config
const foundSpeeds = [];
const allHexKeys = Object.keys(data || {});
allHexKeys.forEach((key) => {
const parts = key.split('fanSpeed');
if (parts.length !== 2) return;
foundSpeeds.push(parts[1])
})
if (foundSpeeds.length === 0) {
return log(`${name} setFanSpeed: No fan speed hex codes provided.`)
}
// Find speed closest to the one requested
const closest = foundSpeeds.reduce((prev, curr) => Math.abs(curr - state.fanSpeed) < Math.abs(prev - state.fanSpeed) ? curr : prev);
log(`${name} setFanSpeed: (closest: ${closest})`);
if (this.lastFanSpeed === closest) {
return;
}
this.lastFanSpeed = closest;
// Get the closest speed's hex data
hexData = data[`fanSpeed${closest}`];
// Check if the device is configured with combined fan and swing mode hex codes
// e.g. fanSpeedX : { swingOn/swingOff: }
if (typeof hexData === 'object' && hexData !== null) {
log('Determining hex code by decoding fanSpeed object');
if ('swingMode' in state) {
log(`Using swingMode of ${state['swingMode']}`);
hexData = state['swingMode'] === Characteristic.SwingMode.SWING_ENABLED ? hexData['swingOn'] : hexData['swingOff'];
} else {
log(`Using swingMode of 0`);
hexData = hexData['swingOff'];
}
}
if (!hexData) {
log('Could not find hex codes for fanSpeed, please check the config.json file')
return;
}
await this.performSend(hexData);
this.checkAutoOnOff();
}
// Determine the hex codes for the requested swing mode and send the
// IR signals to execute it.
// hexData: hex codes for swing modes from the config file
//
// NOTE: This handler is designed to keep the plugin backwards compatible
// while also allowing for combined fan speed and swing mode hex codes.
// This handler function is called by setCharacteristicValue which
// is the main handler registered with homebridge. setCharacteristicValue()
// calls this handler with hex code from config. We need to know if homebridge
// requested to turn on/off swing so we can decipher the hex code acccordingly.
// If values in config for swingOn, swingOff are set as "on" and "off"
// respectively the handler will decipher the hex codes for swing mode from
// the fanSpeed object.
async setSwingMode (hexData) {
const { data, log, state } = this;
const currSpeed = state['fanSpeed'];
const fanSpeedObj = currSpeed ? data["fanSpeed" + currSpeed] : undefined;
// config.swingOn: "on" and config.swingOff: "off" to support combined
// speed + swing values
if (hexData === "off") { // requested value off
if (fanSpeedObj['swingOff']) {
hexData = fanSpeedObj['swingOff'];
} else {
log(`Could not locate value for fanSpeed${currSpeed}.swingOff in config.json`);
return;
}
}
if (hexData === "on") {
if (fanSpeedObj['swingOn']) {
hexData = fanSpeedObj['swingOn'];
} else {
log(`Could not locate value for fanSpeed${currSpeed}.swingOn in config.json`);
return;
}
}
await this.performSend(hexData);
this.checkAutoOnOff();
}
// Read "stepSize" from the config file if present else
// set it to a default value.
getFanSpeedStepSize() {
const step = this.data['stepSize']
// since fan speed is % based in the plugin the default
// step size is set to 1.
return isNaN(step) || step > 100 || step < 1 ? 1 : step
}
correctReloadedState (state) {
const { config } = this
const { showRotationDirection, showSwingMode } = config
state.active = state.active || Characteristic.Active.INACTIVE
if (showSwingMode) {
state.swingMode = state.swingMode || Characteristic.SwingMode.SWING_DISABLED
}
if (showRotationDirection) {
state.rotationDirection = state.RotationDirection || Characteristic.RotationDirection.CLOCKWISE
}
if (state.fanSpeed === undefined) {
state.fanSpeed = 100
}
}
setupServiceManager () {
const { config, data, name, serviceManagerType } = this;
let { showSwingMode, showRotationDirection, hideSwingMode, hideRotationDirection } = config;
const { on, off, clockwise, counterClockwise, swingToggle, swingOn, swingOff } = data || {};
config.availableFanSpeed = this.didDefineFanSpeed()
// Defaults
if (showSwingMode !== false && hideSwingMode !== true) showSwingMode = true
if (showRotationDirection !== false && hideRotationDirection !== true) showRotationDirection = true
this.serviceManager = new ServiceManagerTypes[serviceManagerType](name, Service.Fanv2, this.log);
this.serviceManager.addToggleCharacteristic({
name: 'active',
type: Characteristic.Active,
getMethod: this.getCharacteristicValue,
setMethod: this.setCharacteristicValue,
bind: this,
props: {
onData: on,
offData: off,
setValuePromise: this.setSwitchState.bind(this)
}
});
if (showSwingMode) {
this.serviceManager.addToggleCharacteristic({
name: 'swingMode',
type: Characteristic.SwingMode,
getMethod: this.getCharacteristicValue,
setMethod: this.setCharacteristicValue,
bind: this,
props: {
onData: swingOn || swingToggle,
offData: swingOff || swingToggle,
setValuePromise: this.setSwingMode.bind(this)
}
});
}
this.serviceManager.addToggleCharacteristic({
name: 'fanSpeed',
type: Characteristic.RotationSpeed,
getMethod: this.getCharacteristicValue,
setMethod: this.setCharacteristicValue,
bind: this,
props: {
setValuePromise: this.setFanSpeed.bind(this)
},
characteristicProps: {
minStep: this.getFanSpeedStepSize(),
minValue: 0,
maxVlue: 100
}
});
if (showRotationDirection) {
this.serviceManager.addToggleCharacteristic({
name: 'rotationDirection',
type: Characteristic.RotationDirection,
getMethod: this.getCharacteristicValue,
setMethod: this.setCharacteristicValue,
bind: this,
props: {
onData: counterClockwise,
offData: clockwise
}
});
}
}
}
module.exports = FanAccessory;