homebridge-broadlink-rm-pro
Version:
Broadlink RM plugin (including the mini and pro) for homebridge with AC Pro and TV features
270 lines (215 loc) • 8.57 kB
JavaScript
const { assert } = require('chai');
const ServiceManagerTypes = require('../helpers/serviceManagerTypes');
const delayForDuration = require('../helpers/delayForDuration');
const catchDelayCancelError = require('../helpers/catchDelayCancelError')
const SwitchAccessory = require('./switch');
class LightAccessory extends SwitchAccessory {
setDefaults () {
super.setDefaults();
const { config } = this;
config.onDelay = config.onDelay || 0.1;
config.defaultBrightness = config.defaultBrightness || 100;
}
reset () {
super.reset();
// Clear existing timeouts
if (this.onDelayTimeoutPromise) {
this.onDelayTimeoutPromise.cancel();
this.onDelayTimeoutPromise = undefined
}
}
async updateAccessories (accessories) {
const { config, name, log, logLevel } = this;
const { exclusives } = config;
//console.log('updateAccessories: %s', this.name);
if (exclusives) {
exclusives.forEach(exname => {
const exAccessory = accessories.find(x => x.name === exname);
//console.log(exAccessory.name);
if (exAccessory && exAccessory.config.type === 'light') {
if (!this.exclusives) {this.exclusives = [];}
if (!this.exclusives.find(x => x === exAccessory)) {
this.exclusives.push(exAccessory);
}
if (!exAccessory.exclusives) {exAccessory.exclusives = [];}
if (!exAccessory.exclusives.find(x => x === this)) {
exAccessory.exclusives.push(this);
}
} else {
log(`${name}: No light accessory could be found with the name "${exname}". Please update the "exclusives" value or add matching light accessories.`);
}
});
}
}
async setSwitchState (hexData, previousValue) {
const { config, data, host, log, name, state, logLevel, serviceManager } = this;
let { defaultBrightness, useLastKnownBrightness } = config;
this.reset();
if (state.switchState) {
if (this.exclusives) {
this.exclusives.forEach(x => {
if (x.state.switchState) {
log(`${name} setSwitchState: (${x.name} is configured to be turned off)`);
x.reset();
x.state.switchState = false;
x.lastBrightness = undefined;
x.serviceManager.updateCharacteristic(Characteristic.On,x.state.switchState);
}
});
}
const brightness = (useLastKnownBrightness && state.brightness > 0) ? state.brightness : defaultBrightness;
if (brightness !== state.brightness || previousValue !== state.switchState) {
log(`${name} setSwitchState: (brightness: ${brightness})`);
state.switchState = false;
serviceManager.setCharacteristic(Characteristic.Brightness, brightness);
} else {
if (hexData) {await this.performSend(hexData);}
this.checkAutoOnOff();
}
} else {
this.lastBrightness = undefined;
if (hexData) {await this.performSend(hexData);}
this.checkAutoOnOff();
}
}
async setSaturation () {
}
async setHue () {
await catchDelayCancelError(async () => {
const { config, data, host, log, name, state, logLevel, serviceManager} = this;
const { onDelay } = config;
const { off, on } = data;
this.reset();
if (!state.switchState) {
state.switchState = true;
serviceManager.updateCharacteristic(Characteristic.On,state.switchState);
if (on) {
log(`${name} setHue: (turn on, wait ${onDelay}s)`);
await this.performSend(on);
log(`${name} setHue: (wait ${onDelay}s then send data)`);
this.onDelayTimeoutPromise = delayForDuration(onDelay);
await this.onDelayTimeoutPromise;
}
}
// Find hue closest to the one requested
const foundValues = this.dataKeys('hue');
const closest = foundValues.reduce((prev, curr) => Math.abs(curr - state.hue) < Math.abs(prev - state.hue) ? curr : prev);
var hexData = "";
// If saturation is less than 10, choose white
if (state.saturation < 10 && data.white) {
hexData = data.white;
log(`${name} setHue: (closest: white)`);
} else {
hexData = data[`hue${closest}`];
log(`${name} setHue: (closest: hue${closest})`);
}
await this.performSend(hexData);
});
}
async setBrightness () {
await catchDelayCancelError(async () => {
const { config, data, host, log, name, state, logLevel, serviceManager } = this;
const { off, on } = data;
let { onDelay } = config;
if (this.lastBrightness === state.brightness) {
if (state.brightness > 0) {
state.switchState = true;
}
await this.checkAutoOnOff();
return;
}
this.lastBrightness = state.brightness;
this.reset();
if (state.brightness > 0) {
if (!state.switchState) {
state.switchState = true;
serviceManager.refreshCharacteristicUI(Characteristic.On);
if (on) {
log(`${name} setBrightness: (turn on, wait ${onDelay}s)`);
await this.performSend(on);
log(`${name} setHue: (wait ${onDelay}s then send data)`);
this.onDelayTimeoutPromise = delayForDuration(onDelay);
await this.onDelayTimeoutPromise;
}
}
// Find brightness closest to the one requested
const foundValues = this.dataKeys('brightness')
assert(foundValues.length > 0, `\x1b[31m[CONFIG ERROR] \x1b[33mbrightness\x1b[0m keys need to ne set. See the config-sample.json file for an example.`);
const closest = foundValues.reduce((prev, curr) => Math.abs(curr - state.brightness) < Math.abs(prev - state.brightness) ? curr : prev);
const hexData = data[`brightness${closest}`];
log(`${name} setBrightness: (closest: ${closest})`);
await this.performSend(hexData);
} else {
log(`${name} setBrightness: (off)`);
await this.performSend(off);
}
await this.checkAutoOnOff();
});
}
dataKeys (filter) {
const { data } = this;
const allHexKeys = Object.keys(data || {});
if (!filter) {return allHexKeys;}
// Create an array of value specified in the data config
const foundValues = [];
allHexKeys.forEach((key) => {
const parts = key.split(filter);
if (parts.length !== 2) {return;}
foundValues.push(parts[1]);
})
return foundValues
}
setupServiceManager () {
const { data, name, config, serviceManagerType } = this;
const { on, off } = data || { };
this.serviceManager = new ServiceManagerTypes[serviceManagerType](name, Service.Lightbulb, this.log);
this.serviceManager.addToggleCharacteristic({
name: 'switchState',
type: Characteristic.On,
getMethod: this.getCharacteristicValue,
setMethod: this.setCharacteristicValue,
bind: this,
props: {
onData: on,
offData: off,
setValuePromise: this.setSwitchState.bind(this)
}
});
this.serviceManager.addToggleCharacteristic({
name: 'brightness',
type: Characteristic.Brightness,
getMethod: this.getCharacteristicValue,
setMethod: this.setCharacteristicValue,
bind: this,
props: {
setValuePromise: this.setBrightness.bind(this),
ignorePreviousValue: true // TODO: Check what this does and test it
}
});
if (this.dataKeys('hue').length > 0) {
this.serviceManager.addToggleCharacteristic({
name: 'hue',
type: Characteristic.Hue,
getMethod: this.getCharacteristicValue,
setMethod: this.setCharacteristicValue,
bind: this,
props: {
setValuePromise: this.setHue.bind(this),
ignorePreviousValue: true // TODO: Check what this does and test it
}
});
this.serviceManager.addToggleCharacteristic({
name: 'saturation',
type: Characteristic.Saturation,
getMethod: this.getCharacteristicValue,
setMethod: this.setCharacteristicValue,
bind: this,
props: {
setValuePromise: this.setSaturation.bind(this),
ignorePreviousValue: true // TODO: Check what this does and test it
}
});
}
}
}
module.exports = LightAccessory;