homebridge-homeassistant-mbmccormick
Version:
Homebridge plugin for Home Assistant: https://home-assistant.io
210 lines (178 loc) • 6.69 kB
JavaScript
'use strict';
let Service;
let Characteristic;
let communicationError;
class HomeAssistantCover {
constructor(log, data, client, firmware) {
this.client = client;
this.log = log;
// device info
this.domain = 'cover';
this.data = data;
this.entity_id = data.entity_id;
this.uuid_base = data.entity_id;
this.firmware = firmware;
if (data.attributes && data.attributes.friendly_name) {
this.name = data.attributes.friendly_name;
} else {
this.name = data.entity_id.split('.').pop().replace(/_/g, ' ');
}
if (data.attributes && data.attributes.homebridge_manufacturer) {
this.manufacturer = String(data.attributes.homebridge_manufacturer);
} else {
this.manufacturer = 'Home Assistant';
}
if (data.attributes && data.attributes.homebridge_serial) {
this.serial = String(data.attributes.homebridge_serial);
} else {
this.serial = data.entity_id;
}
}
onEvent(oldState, newState) {
if (newState.state) {
const state = this.transformData(newState);
this.service.getCharacteristic(this.stateCharacteristic)
.setValue(state, null, 'internal');
this.service.getCharacteristic(this.targetCharacteristic)
.setValue(state, null, 'internal');
}
}
getState(callback) {
this.client.fetchState(this.entity_id, (data) => {
if (data) {
callback(null, this.transformData(data));
} else {
callback(communicationError);
}
});
}
getServices() {
const informationService = new Service.AccessoryInformation();
informationService
.setCharacteristic(Characteristic.Manufacturer, this.manufacturer)
.setCharacteristic(Characteristic.Model, this.model)
.setCharacteristic(Characteristic.SerialNumber, this.serial)
.setCharacteristic(Characteristic.FirmwareRevision, this.firmware);
this.service
.getCharacteristic(this.stateCharacteristic)
.on('get', this.getState.bind(this));
this.service
.getCharacteristic(this.targetCharacteristic)
.on('get', this.getState.bind(this))
.on('set', this.setTargetState.bind(this));
return [informationService, this.service];
}
doChangeState(service, callback) {
const serviceData = {
entity_id: this.entity_id,
};
this.log(`Calling service ${service} on ${this.name}`);
this.client.callService(this.domain, service, serviceData, (data) => {
if (data) {
callback();
} else {
callback(communicationError);
}
});
}
}
class HomeAssistantGarageDoor extends HomeAssistantCover {
constructor(log, data, client, firmware) {
super(log, data, client, firmware);
if (data.attributes && data.attributes.homebridge_model) {
this.model = String(data.attributes.homebridge_model);
} else {
this.model = 'Garage Door';
}
this.service = new Service.GarageDoorOpener();
this.stateCharacteristic = Characteristic.CurrentDoorState;
this.targetCharacteristic = Characteristic.TargetDoorState;
}
transformData(data) {
return data.state === 'closed' ? this.stateCharacteristic.CLOSED : this.stateCharacteristic.OPEN;
}
setTargetState(targetState, callback, context) {
if (context === 'internal') {
callback();
return;
}
this.doChangeState(targetState === Characteristic.TargetDoorState.CLOSED ? 'close_cover' : 'open_cover', callback);
}
}
class HomeAssistantRollershutter extends HomeAssistantCover {
constructor(log, data, client, firmware) {
super(log, data, client, firmware);
if (data.attributes && data.attributes.homebridge_model) {
this.model = String(data.attributes.homebridge_model);
} else {
this.model = 'Rollershutter';
}
this.service = new Service.WindowCovering();
this.stateCharacteristic = Characteristic.CurrentPosition;
this.targetCharacteristic = Characteristic.TargetPosition;
}
transformData(data) {
return (data && data.attributes) ? data.attributes.current_position : null;
}
setTargetState(position, callback, context) {
if (context === 'internal') {
callback();
return;
}
const payload = {
entity_id: this.entity_id,
position,
};
this.log(`Setting the state of the ${this.name} to ${payload.position}`);
this.client.callService(this.domain, 'set_cover_position', payload, (data) => {
if (data) {
callback();
} else {
callback(communicationError);
}
});
}
}
class HomeAssistantRollershutterBinary extends HomeAssistantRollershutter {
transformData(data) {
return (data && data.state) ? ((data.state === 'open') * 100) : null;
}
setTargetState(position, callback, context) {
if (context === 'internal') {
callback();
return;
}
if (!(position === 100 || position === 0)) {
this.log('Cannot set this cover to positions other than 0 or 100');
callback(communicationError); // TODO
} else {
this.doChangeState(position === 100 ? 'open_cover' : 'close_cover', callback);
}
}
}
function HomeAssistantCoverFactory(log, data, client, firmware) {
if (!data.attributes) {
return null;
}
if (data.attributes.homebridge_cover_type === 'garage_door') {
return new HomeAssistantGarageDoor(log, data, client, firmware);
} else if (data.attributes.homebridge_cover_type === 'rollershutter') {
if (data.attributes.current_position !== undefined) {
return new HomeAssistantRollershutter(log, data, client, firmware);
}
return new HomeAssistantRollershutterBinary(log, data, client, firmware);
}
log.error(`'${data.entity_id}' is a cover but does not have a 'homebridge_cover_type' property set. ` +
'You must set it to either \'rollershutter\' or \'garage_door\' in the customize section ' +
'of your Home Assistant configuration. It will not be available to Homebridge until you do. ' +
'See the README.md for more information. ' +
'The attributes that were found are:', JSON.stringify(data.attributes));
}
function HomeAssistantCoverPlatform(oService, oCharacteristic, oCommunicationError) {
Service = oService;
Characteristic = oCharacteristic;
communicationError = oCommunicationError;
return HomeAssistantCoverFactory;
}
module.exports = HomeAssistantCoverPlatform;
module.exports.HomeAssistantCoverFactory = HomeAssistantCoverFactory;