homebridge-virtual-accessories
Version:
Virtual HomeKit accessories for Homebridge.
222 lines • 10.6 kB
JavaScript
/* eslint-disable brace-style */
import { Accessory } from './accessory.js';
import { Colors } from '../utils/colorUtils.js';
import { Utils } from '../utils/utils.js';
/**
* Lightbulb - Accessory implementation
*/
export class Lightbulb extends Accessory {
static ACCESSORY_TYPE_NAME = 'Lightbulb';
static ON = true;
static OFF = false;
static WHITE = 'white';
static AMBIANCE = 'ambiance';
static COLOR = 'color';
stateStorageKey = 'LightbulbState';
brightnessStorageKey = 'LightbulbBrightness';
colorTemperatureStorageKey = 'LightbulbColorTemperature';
hueStorageKey = 'LightbulbHue';
saturationStorageKey = 'LightbulbSaturation';
type = Lightbulb.WHITE;
states = {
LightbulbState: Lightbulb.OFF,
LightbulbBrightness: 0,
LightbulbColorTemperature: 2700, // Kelvin
LightbulbHue: 0,
LightbulbSaturation: 0,
};
constructor(platform, accessory, accessoryConfiguration) {
super(platform, accessory, accessoryConfiguration);
this.type = this.accessoryConfiguration.lightbulb.type;
// First configure the device based on the accessory details
this.defaultState = this.accessoryConfiguration.lightbulb.defaultState === 'on' ? Lightbulb.ON : Lightbulb.OFF;
const brightness = this.accessoryConfiguration.lightbulb.brightness;
const colorTemperatureKelvin = this.accessoryConfiguration.lightbulb.colorTemperatureKelvin;
const colorHex = this.accessoryConfiguration.lightbulb.colorHex;
this.states.LightbulbState = this.defaultState;
this.states.LightbulbBrightness = brightness;
if (this.type === Lightbulb.WHITE) {
this.states.LightbulbBrightness = brightness;
}
else if (this.type === Lightbulb.AMBIANCE) {
this.states.LightbulbBrightness = brightness;
this.states.LightbulbColorTemperature = colorTemperatureKelvin;
}
else if (this.type === Lightbulb.COLOR) {
const hsl = Colors.HexToHSL(colorHex);
this.states.LightbulbHue = hsl.hue;
this.states.LightbulbSaturation = hsl.saturation;
this.states.LightbulbBrightness = hsl.luminance;
}
// If the accessory is stateful retrieve stored state
if (this.accessoryConfiguration.accessoryIsStateful) {
const accessoryState = this.loadAccessoryState(this.storagePath);
const cachedState = accessoryState[this.stateStorageKey];
const cachedBrightness = accessoryState[this.brightnessStorageKey];
const cachedColorTemperature = accessoryState[this.colorTemperatureStorageKey];
const cachedHue = accessoryState[this.hueStorageKey];
const cachedSaturation = accessoryState[this.saturationStorageKey];
if (cachedState !== undefined) {
this.states.LightbulbState = cachedState;
}
if (cachedBrightness !== undefined) {
this.states.LightbulbBrightness = cachedBrightness;
}
if (this.type === Lightbulb.AMBIANCE && cachedColorTemperature !== undefined) {
this.states.LightbulbColorTemperature = cachedColorTemperature;
}
if (this.type === Lightbulb.COLOR) {
if (cachedHue !== undefined) {
this.states.LightbulbHue = cachedHue;
}
if (cachedSaturation !== undefined) {
this.states.LightbulbSaturation = cachedSaturation;
}
}
}
this.service = this.accessory.getService(this.platform.Service.Lightbulb) || this.accessory.addService(this.platform.Service.Lightbulb);
this.service.setCharacteristic(this.platform.Characteristic.Name, this.accessoryConfiguration.accessoryName);
// Update the initial state of the accessory
this.log.debug(`[${this.accessoryName}] Setting Lightbulb Current State: ${Lightbulb.getStateName(this.states.LightbulbState)}`);
this.service.updateCharacteristic(this.platform.Characteristic.On, (this.states.LightbulbState));
this.service.updateCharacteristic(this.platform.Characteristic.Brightness, (this.states.LightbulbBrightness));
// register handlers
this.service.getCharacteristic(this.platform.Characteristic.On)
.onSet(this.setOn.bind(this))
.onGet(this.getOn.bind(this));
this.service.getCharacteristic(this.platform.Characteristic.Brightness)
.onSet(Utils.debounce(this.setBrightness.bind(this)))
.onGet(this.getBrightness.bind(this));
switch (this.type) {
case Lightbulb.AMBIANCE:
// register handlers for the ColorTemperature Characteristic
this.service.getCharacteristic(this.platform.Characteristic.ColorTemperature)
.onSet(Utils.debounce(this.setColorTemperature.bind(this)))
.onGet(this.getColorTemperature.bind(this));
break;
case Lightbulb.COLOR:
this.service.getCharacteristic(this.platform.Characteristic.Hue)
.onSet(Utils.debounce(this.setHue.bind(this)))
.onGet(this.getHue.bind(this));
this.service.getCharacteristic(this.platform.Characteristic.Saturation)
.onSet(Utils.debounce(this.setSaturation.bind(this)))
.onGet(this.getSaturation.bind(this));
break;
case Lightbulb.WHITE:
// No additional characteristics
break;
}
}
// Handlers
async setOn(value) {
this.states.LightbulbState = value;
// If brightness is 0% or 100%, ON = 100%, OFF = 0%
if ((this.states.LightbulbState === Lightbulb.ON) && (this.states.LightbulbBrightness === 0)) {
this.states.LightbulbBrightness = 100;
this.service?.updateCharacteristic(this.platform.Characteristic.Brightness, (this.states.LightbulbBrightness));
}
else if ((this.states.LightbulbState === Lightbulb.OFF) && (this.states.LightbulbBrightness === 100)) {
this.states.LightbulbBrightness = 0;
this.service?.updateCharacteristic(this.platform.Characteristic.Brightness, (this.states.LightbulbBrightness));
}
this.storeState();
this.log.info(`[${this.accessoryName}] Setting State: ${Lightbulb.getStateName(this.states.LightbulbState)}`);
}
async getOn() {
const lightbulbState = this.states.LightbulbState;
this.log.debug(`[${this.accessoryName}] Getting State: ${Lightbulb.getStateName(lightbulbState)}`);
return lightbulbState;
}
async setBrightness(value) {
this.states.LightbulbBrightness = value;
// Setting the brightness to 0 turns lightbulb OFF
if ((this.states.LightbulbBrightness === 0) && (this.states.LightbulbState === Lightbulb.ON)) {
this.states.LightbulbState = Lightbulb.OFF;
this.service?.updateCharacteristic(this.platform.Characteristic.On, (this.states.LightbulbState));
}
this.storeState();
this.log.info(`[${this.accessoryName}] Setting Brightness: ${this.states.LightbulbBrightness}%`);
}
async getBrightness() {
const lightbulbBrightness = this.states.LightbulbBrightness;
this.log.debug(`[${this.accessoryName}] Getting Brightness: ${lightbulbBrightness}%`);
return lightbulbBrightness;
}
async setColorTemperature(miredValue) {
this.states.LightbulbColorTemperature = this.miredToKelvin(miredValue);
this.storeState();
this.log.debug(`[${this.accessoryName}] Setting Color Temperature: ${this.states.LightbulbColorTemperature}K (${miredValue} Mired)`);
}
async getColorTemperature() {
const miredValue = this.kelvinToMired(this.states.LightbulbColorTemperature);
this.log.debug(`[${this.accessoryName}] Getting Color Temperature: ${this.states.LightbulbColorTemperature}K (${miredValue} Mired)`);
return miredValue;
}
async setHue(value) {
this.states.LightbulbHue = value;
this.storeState();
this.log.info(`[${this.accessoryName}] Setting Hue: ${this.states.LightbulbHue}º`);
}
async getHue() {
const lightbulbHue = this.states.LightbulbHue;
this.log.debug(`[${this.accessoryName}] Getting Hue: ${lightbulbHue}º`);
return lightbulbHue;
}
async setSaturation(value) {
this.states.LightbulbSaturation = value;
this.storeState();
this.log.info(`[${this.accessoryName}] Setting Saturation: ${this.states.LightbulbSaturation}º`);
}
async getSaturation() {
const lightbulbSaturation = this.states.LightbulbSaturation;
this.log.debug(`[${this.accessoryName}] Getting Saturation: ${lightbulbSaturation}º`);
return lightbulbSaturation;
}
getJsonState() {
const jsonState = {
[this.stateStorageKey]: this.states.LightbulbState,
[this.brightnessStorageKey]: this.states.LightbulbBrightness,
};
if (this.type === Lightbulb.AMBIANCE) {
Object.assign(jsonState, { [this.colorTemperatureStorageKey]: this.states.LightbulbColorTemperature });
}
if (this.type === Lightbulb.COLOR) {
Object.assign(jsonState, { [this.hueStorageKey]: this.states.LightbulbHue });
Object.assign(jsonState, { [this.saturationStorageKey]: this.states.LightbulbSaturation });
}
const json = JSON.stringify(jsonState);
return json;
}
getAccessoryTypeName() {
return Lightbulb.ACCESSORY_TYPE_NAME;
}
static getStateName(state) {
let stateName;
switch (state) {
case undefined: {
stateName = 'undefined';
break;
}
case Lightbulb.ON: {
stateName = 'ON';
break;
}
case Lightbulb.OFF: {
stateName = 'OFF';
break;
}
default: {
stateName = state.toString();
}
}
return stateName;
}
// micro-reciprocal degrees (mired): 1,000,000 divided by the color temperature in kelvins
kelvinToMired(kelvin) {
return Math.round(1000000 / kelvin);
}
miredToKelvin(mired) {
return Math.round(1000000 / mired);
}
}
//# sourceMappingURL=virtualAccessoryLightbulb.js.map