homebridge-homeassistant-mbmccormick
Version:
Homebridge plugin for Home Assistant: https://home-assistant.io
370 lines (331 loc) • 12.2 kB
JavaScript
var Service;
var Characteristic;
var communicationError;
function fahrenheitToCelsius(temperature) {
return (temperature - 32) / 1.8;
}
function celsiusToFahrenheit(temperature) {
return Math.round((temperature * 1.8) + 32);
}
function getTempUnits(data) {
// determine HomeAssistant temp. units (celsius vs. fahrenheit)
// defaults to celsius
return (data.attributes && data.attributes.unit_of_measurement && data.attributes.unit_of_measurement === '°F') ? 'FAHRENHEIT' : 'CELSIUS';
}
function HomeAssistantClimate(log, data, client, firmware) {
// device info
this.domain = 'climate';
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_model) {
this.model = String(data.attributes.homebridge_model);
} else {
this.model = 'Climate';
}
if (data.attributes && data.attributes.homebridge_serial) {
this.serial = String(data.attributes.homebridge_serial);
} else {
this.serial = data.entity_id;
}
this.client = client;
this.log = log;
var fanList = data.attributes.fan_list;
if (fanList) {
this.maxFanRotationValue = fanList.length - 1;
} else {
this.maxFanRotationValue = 100;
}
}
HomeAssistantClimate.prototype = {
onEvent: function (oldState, newState) {
if (newState.state) {
const list = {
idle: 0, heat: 1, cool: 2, auto: 3, off: 0
};
this.ThermostatService.getCharacteristic(Characteristic.CurrentTemperature)
.setValue(newState.attributes.current_temperature || newState.attributes.temperature, null, 'internal');
this.ThermostatService.getCharacteristic(Characteristic.TargetTemperature)
.setValue(newState.attributes.temperature, null, 'internal');
this.ThermostatService.getCharacteristic(Characteristic.TargetHeatingCoolingState)
.setValue(list[newState.state], null, 'internal');
}
},
getCurrentTemp: function (callback) {
this.client.fetchState(this.entity_id, function (data) {
if (data) {
if (getTempUnits(data) === 'FAHRENHEIT') {
callback(null, fahrenheitToCelsius(data.attributes.current_temperature));
} else {
callback(null, data.attributes.current_temperature);
}
} else {
callback(communicationError);
}
});
},
getTargetTemp: function (callback) {
this.client.fetchState(this.entity_id, function (data) {
if (data) {
if (getTempUnits(data) === 'FAHRENHEIT') {
callback(null, fahrenheitToCelsius(data.attributes.temperature));
} else {
callback(null, data.attributes.temperature);
}
} else {
callback(communicationError);
}
});
},
setTargetTemp: function (value, callback, context) {
if (context === 'internal') {
callback();
return;
}
var that = this;
var serviceData = {};
serviceData.entity_id = this.entity_id;
serviceData.temperature = value;
if (getTempUnits(this.data) === 'FAHRENHEIT') {
serviceData.temperature = celsiusToFahrenheit(serviceData.temperature);
}
this.log(`Setting temperature on the '${this.name}' to ${serviceData.temperature}`);
this.client.callService(this.domain, 'set_temperature', serviceData, function (data) {
if (data) {
that.log(`Successfully set temperature of '${that.name}'`);
callback();
} else {
callback(communicationError);
}
});
},
getTargetHeatingCoolingState: function (callback) {
this.log('fetching Current Heating Cooling state for: ' + this.name);
this.client.fetchState(this.entity_id, function (data) {
if (data && data.attributes && data.attributes.operation_mode) {
var state;
switch (data.attributes.operation_mode) {
case 'auto':
state = Characteristic.TargetHeatingCoolingState.AUTO;
break;
case 'cool':
state = Characteristic.TargetHeatingCoolingState.COOL;
break;
case 'heat':
state = Characteristic.TargetHeatingCoolingState.HEAT;
break;
case 'off':
default:
state = Characteristic.TargetHeatingCoolingState.OFF;
break;
}
callback(null, state);
} else {
callback(communicationError);
}
});
},
setTargetHeatingCoolingState: function (value, callback, context) {
if (context === 'internal') {
callback();
return;
}
var serviceData = {};
serviceData.entity_id = this.entity_id;
var mode = '';
switch (value) {
case Characteristic.TargetHeatingCoolingState.AUTO:
mode = 'auto';
break;
case Characteristic.TargetHeatingCoolingState.COOL:
mode = 'cool';
break;
case Characteristic.TargetHeatingCoolingState.HEAT:
mode = 'heat';
break;
case Characteristic.TargetHeatingCoolingState.OFF:
default:
mode = 'off';
break;
}
serviceData.operation_mode = mode;
this.log(`Setting Current Heating Cooling state on the '${this.name}' to ${mode}`);
var that = this;
if (mode === 'idle') {
this.fanService.getCharacteristic(Characteristic.On)
.setValue(false, null, 'internal');
} else {
this.fanService.getCharacteristic(Characteristic.On)
.setValue(true, null, 'internal');
}
this.client.callService(this.domain, 'set_operation_mode', serviceData, function (data) {
if (data) {
that.log(`Successfully set current heating cooling state of '${that.name}'`);
callback();
} else {
callback(communicationError);
}
});
},
getRotationSpeed(callback) {
this.client.fetchState(this.entity_id, (data) => {
if (data) {
if (data.attributes.operation_mode === 'idle') {
callback(null, 0);
} else {
var fanList = data.attributes.fan_list;
if (fanList) {
if (fanList.length > 2) {
var index = fanList.indexOf(data.attributes.current_fan_mode);
callback(null, index);
}
} else {
switch (data.attributes.current_fan_mode) {
case 'low':
callback(null, 25);
break;
case 'mid':
callback(null, 50);
break;
case 'high':
callback(null, 75);
break;
case 'highest':
callback(null, 100);
break;
default:
callback(null, 0);
}
}
}
} else {
callback(communicationError);
}
});
},
setRotationSpeed(speed, callback, context) {
if (context === 'internal') {
callback();
return;
}
const that = this;
const serviceData = {};
serviceData.entity_id = this.entity_id;
this.client.fetchState(this.entity_id, (data) => {
if (data) {
var fanList = data.attributes.fan_list;
if (fanList) {
for (var index = 0; index < fanList.length - 1; index += 1) {
if (speed === index) {
serviceData.fan_mode = fanList[index];
break;
}
}
if (!serviceData.fan_mode) {
serviceData.fan_mode = fanList[fanList.length - 1];
}
} else if (speed <= 25) {
serviceData.fan_mode = 'low';
} else if (speed <= 50) {
serviceData.fan_mode = 'medium';
} else if (speed <= 75) {
serviceData.fan_mode = 'high';
} else if (speed <= 100) {
serviceData.fan_mode = 'highest';
}
this.log(`Setting fan mode on the '${this.name}' to ${serviceData.fan_mode}`);
this.client.callService(this.domain, 'set_fan_mode', serviceData, (data2) => {
if (data2) {
that.log(`Successfully set fan mode on the '${that.name}' to ${serviceData.fan_mode}`);
callback();
} else {
callback(communicationError);
}
});
} else {
callback(communicationError);
}
});
},
getServices: function () {
this.ThermostatService = new Service.Thermostat();
var 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);
// get our unit var -- default to celsius
var units = (getTempUnits(this.data) === 'FAHRENHEIT') ? Characteristic.TemperatureDisplayUnits.FAHRENHEIT : Characteristic.TemperatureDisplayUnits.CELSIUS;
this.ThermostatService
.getCharacteristic(Characteristic.CurrentTemperature)
.on('get', this.getCurrentTemp.bind(this));
// default min/max/step for temperature
var minTemp = 7.0;
var maxTemp = 35.0;
var tempStep = 0.5;
if (units === Characteristic.TemperatureDisplayUnits.FAHRENHEIT) {
if (this.data && this.data.attributes) {
if (this.data.attributes.min_temp) {
minTemp = fahrenheitToCelsius(this.data.attributes.min_temp);
}
if (this.data.attributes.max_temp) {
maxTemp = fahrenheitToCelsius(this.data.attributes.max_temp);
}
if (this.data.attributes.target_temp_step) {
tempStep = this.data.attributes.target_temp_step;
}
}
} else if (this.data && this.data.attributes) {
if (this.data.attributes.min_temp) {
minTemp = this.data.attributes.min_temp;
}
if (this.data.attributes.max_temp) {
maxTemp = this.data.attributes.max_temp;
}
if (this.data.attributes.target_temp_step) {
tempStep = this.data.attributes.target_temp_step;
}
}
this.ThermostatService
.getCharacteristic(Characteristic.TargetTemperature)
.setProps({ minValue: minTemp, maxValue: maxTemp, minStep: tempStep })
.on('get', this.getTargetTemp.bind(this))
.on('set', this.setTargetTemp.bind(this));
this.ThermostatService
.getCharacteristic(Characteristic.TargetHeatingCoolingState)
.on('get', this.getTargetHeatingCoolingState.bind(this))
.on('set', this.setTargetHeatingCoolingState.bind(this));
this.ThermostatService.setCharacteristic(Characteristic.TemperatureDisplayUnits, units);
this.fanService = new Service.Fan();
this.fanService
.getCharacteristic(Characteristic.RotationSpeed)
.setProps({
minValue: 0,
maxValue: this.maxFanRotationValue,
minStep: 1
})
.on('get', this.getRotationSpeed.bind(this))
.on('set', this.setRotationSpeed.bind(this));
return [informationService, this.ThermostatService, this.fanService];
}
};
function HomeAssistantClimatePlatform(oService, oCharacteristic, oCommunicationError) {
Service = oService;
Characteristic = oCharacteristic;
communicationError = oCommunicationError;
return HomeAssistantClimate;
}
module.exports = HomeAssistantClimatePlatform;
module.exports.HomeAssistantClimate = HomeAssistantClimate;