Created
June 28, 2018 19:51
-
-
Save karlkar/60c0b65ab6b83a0dd9c56e4322ff1dc6 to your computer and use it in GitHub Desktop.
Climate component for controling LG Standard AC using scripts that send IRDA commands
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
""" | |
Adds support for AC being controlled via IRDA device. | |
""" | |
import asyncio | |
import logging | |
import voluptuous as vol | |
from homeassistant.components.climate import ( | |
STATE_FAN_ONLY, STATE_DRY, STATE_COOL, STATE_IDLE, STATE_AUTO, STATE_PERFORMANCE, ClimateDevice, | |
SUPPORT_FAN_MODE, ATTR_SWING_MODE, ATTR_FAN_MODE, STATE_HEAT, | |
SUPPORT_SWING_MODE, SUPPORT_OPERATION_MODE, | |
SUPPORT_TARGET_TEMPERATURE, PLATFORM_SCHEMA) | |
from homeassistant.const import ( | |
STATE_OFF, ATTR_TEMPERATURE, ATTR_UNIT_OF_MEASUREMENT, | |
CONF_NAME, PRECISION_WHOLE) | |
import homeassistant.helpers.config_validation as cv | |
from homeassistant.helpers.event import (async_track_state_change) | |
from homeassistant.helpers.restore_state import async_get_last_state | |
_LOGGER = logging.getLogger(__name__) | |
DEPENDENCIES = ['sensor'] | |
DEFAULT_NAME = 'External Climate' | |
DEFAULT_TEMP = 22.0 | |
DEFAULT_SENSOR = "" | |
CONF_MIN_TEMP = 'min_temp' | |
CONF_MAX_TEMP = 'max_temp' | |
CONF_SENSOR = 'target_sensor' | |
SUPPORT_FLAGS = (SUPPORT_TARGET_TEMPERATURE | | |
SUPPORT_OPERATION_MODE | | |
SUPPORT_FAN_MODE | | |
SUPPORT_SWING_MODE) | |
OPERATION_LIST = [STATE_COOL, STATE_AUTO, STATE_DRY, STATE_HEAT, STATE_FAN_ONLY, STATE_PERFORMANCE, STATE_OFF] | |
FAN_AUTO = "auto" | |
FAN_1 = "1" | |
FAN_2 = "2" | |
FAN_3 = "3" | |
FAN_4 = "4" | |
FAN_5 = "5" | |
FAN_LIST = [FAN_AUTO, FAN_1, FAN_2, FAN_3, FAN_4, FAN_5] | |
SWING_AUTO = "auto" | |
SWING_HIGH = "high" | |
SWING_MEDIUM = "medium" | |
SWING_LOW = "low" | |
SWING_LIST = [SWING_AUTO, SWING_HIGH, SWING_MEDIUM, SWING_LOW] | |
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ | |
vol.Optional(CONF_SENSOR): cv.entity_id, | |
vol.Optional(CONF_MAX_TEMP): vol.Coerce(float), | |
vol.Optional(CONF_MIN_TEMP): vol.Coerce(float), | |
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string, | |
}) | |
@asyncio.coroutine | |
def async_setup_platform(hass, config, async_add_devices, discovery_info=None): | |
"""Set up the generic thermostat platform.""" | |
name = config.get(CONF_NAME) | |
sensor_entity_id = config.get(CONF_SENSOR, None) | |
min_temp = config.get(CONF_MIN_TEMP) | |
max_temp = config.get(CONF_MAX_TEMP) | |
async_add_devices([ExternalClimate( | |
hass, name, sensor_entity_id, min_temp, max_temp)]) | |
class ExternalClimate(ClimateDevice): | |
"""Representation of a Generic Thermostat device.""" | |
def __init__(self, hass, name, sensor_entity_id, min_temp, max_temp): | |
"""Initialize the thermostat.""" | |
self.hass = hass | |
self._name = name | |
self._sensor_entity_id = sensor_entity_id | |
self._active = False | |
self._cur_temp = None | |
self._min_temp = min_temp | |
self._max_temp = max_temp | |
self._target_temp = None | |
self._unit = hass.config.units.temperature_unit | |
self._current_fan = FAN_AUTO | |
self._current_swing = SWING_AUTO | |
self._current_operation = STATE_OFF | |
if self._sensor_entity_id: | |
async_track_state_change( | |
hass, sensor_entity_id, self._async_sensor_changed) | |
@asyncio.coroutine | |
def async_added_to_hass(self): | |
"""Run when entity about to be added.""" | |
# Check If we have an old state | |
old_state = yield from async_get_last_state(self.hass, | |
self.entity_id) | |
if old_state is not None: | |
# If we have a previously saved temperature | |
if old_state.attributes.get(ATTR_TEMPERATURE) is None: | |
self._target_temp = DEFAULT_TEMP | |
_LOGGER.warning("Undefined target temperature," | |
"falling back to %s", self._target_temp) | |
else: | |
self._target_temp = float( | |
old_state.attributes[ATTR_TEMPERATURE]) | |
if old_state.attributes.get(ATTR_SWING_MODE) is None: | |
self._current_swing = SWING_AUTO | |
else: | |
self._current_swing = old_state.attributes.get(ATTR_SWING_MODE) | |
if old_state.attributes.get(ATTR_FAN_MODE) is None: | |
self._current_fan = FAN_AUTO | |
else: | |
self._current_fan = old_state.attributes.get(ATTR_FAN_MODE) | |
else: | |
# No previous state, try and restore defaults | |
if self._target_temp is None: | |
self._target_temp = DEFAULT_TEMP | |
_LOGGER.warning("No previously saved temperature, setting to %s", | |
self._target_temp) | |
@property | |
def state(self): | |
"""Return the current state.""" | |
if self._is_device_active: | |
return self.current_operation | |
return STATE_IDLE | |
@property | |
def should_poll(self): | |
"""Return the polling state.""" | |
return False | |
@property | |
def name(self): | |
"""Return the name of the thermostat.""" | |
return self._name | |
@property | |
def temperature_unit(self): | |
"""Return the unit of measurement.""" | |
return self._unit | |
@property | |
def current_temperature(self): | |
"""Return the sensor temperature.""" | |
return self._cur_temp | |
@property | |
def current_operation(self): | |
"""Return current operation.""" | |
return self._current_operation | |
@property | |
def target_temperature(self): | |
"""Return the temperature we try to reach.""" | |
return self._target_temp | |
@property | |
def operation_list(self): | |
"""List of available operation modes.""" | |
return OPERATION_LIST | |
async def async_set_operation_mode(self, operation_mode): | |
"""Set operation mode.""" | |
self._current_operation = operation_mode | |
self._active = self._current_operation != STATE_OFF | |
script = self._get_proper_script() | |
self.hass.async_add_job( | |
self.hass.services.async_call("script", script)) | |
self.hass.async_add_job( | |
self.hass.services.async_call("script", "remote_ac_lg_swing_" + self._current_swing)) | |
# Ensure we update the current operation after changing the mode | |
self.schedule_update_ha_state() | |
@asyncio.coroutine | |
def async_set_temperature(self, **kwargs): | |
"""Set new target temperature.""" | |
temperature = kwargs.get(ATTR_TEMPERATURE) | |
if temperature is None: | |
return | |
self._target_temp = temperature | |
if not self._active: | |
return | |
script = self._get_proper_script() | |
self.hass.async_add_job( | |
self.hass.services.async_call("script", script)) | |
yield from self.async_update_ha_state() | |
@asyncio.coroutine | |
def async_set_fan_mode(self, fan_mode): | |
"""Set new target fan mode. | |
This method must be run in the event loop and returns a coroutine. | |
""" | |
self._current_fan = fan_mode | |
if not self._active: | |
return | |
script = self._get_proper_script() | |
self.hass.async_add_job( | |
self.hass.services.async_call("script", script)) | |
yield from self.async_update_ha_state() | |
@asyncio.coroutine | |
def async_set_swing_mode(self, swing_mode): | |
"""Set new target swing operation. | |
This method must be run in the event loop and returns a coroutine. | |
""" | |
self._current_swing = swing_mode | |
if not self._active: | |
return | |
self.hass.async_add_job( | |
self.hass.services.async_call("script", "remote_ac_lg_swing_" + swing_mode)) | |
yield from self.async_update_ha_state() | |
def _get_proper_script(self): | |
"""This method returns the state which should be returned""" | |
if self._current_operation in [STATE_AUTO, STATE_HEAT, STATE_COOL]: | |
return "remote_ac_lg_power_" + self._current_operation + "_" + \ | |
str(int(self._target_temp)) + "_fan_" + str(self._current_fan) | |
if self._current_operation == STATE_DRY: | |
return "remote_ac_lg_power_dry_fan_" + str(self._current_fan) | |
if self._current_operation == STATE_FAN_ONLY: | |
return "remote_ac_lg_power_fan_" + str(self._current_fan) | |
if self._current_operation == STATE_PERFORMANCE: | |
return "remote_ac_lg_jet_mode_on" | |
if self._current_operation == STATE_OFF: | |
return "remote_ac_lg_power_off" | |
@property | |
def target_temperature_step(self): | |
"""Return the supported step of target temperature.""" | |
return PRECISION_WHOLE | |
@property | |
def min_temp(self): | |
"""Return the minimum temperature.""" | |
if self._min_temp: | |
return self._min_temp | |
# get default temp from super class | |
return super().min_temp | |
@property | |
def max_temp(self): | |
"""Return the maximum temperature.""" | |
if self._max_temp: | |
return self._max_temp | |
# Get default temp from super class | |
return super().max_temp | |
@asyncio.coroutine | |
def _async_sensor_changed(self, entity_id, old_state, new_state): | |
"""Handle temperature changes.""" | |
if new_state is None: | |
return | |
unit = new_state.attributes.get(ATTR_UNIT_OF_MEASUREMENT) | |
try: | |
self._cur_temp = self.hass.config.units.temperature( | |
float(new_state.state), unit) | |
except ValueError as ex: | |
_LOGGER.error("Unable to update from sensor: %s", ex) | |
yield from self.async_update_ha_state() | |
@property | |
def target_temperature_high(self): | |
"""Return the highbound target temperature we try to reach.""" | |
if self._max_temp: | |
return self._max_temp | |
# Get default temp from super class | |
return super().target_temperature_high | |
@property | |
def target_temperature_low(self): | |
"""Return the lowbound target temperature we try to reach.""" | |
if self._min_temp: | |
return self._min_temp | |
# get default temp from super class | |
return super().target_temperature_low | |
@property | |
def _is_device_active(self): | |
"""If the toggleable device is currently active.""" | |
return self._active | |
@property | |
def supported_features(self): | |
"""Return the list of supported features.""" | |
return SUPPORT_FLAGS | |
@property | |
def is_away_mode_on(self): | |
"""Return true if away mode is on.""" | |
return False | |
@property | |
def current_fan_mode(self): | |
"""Return the fan setting.""" | |
return self._current_fan | |
@property | |
def fan_list(self): | |
"""Return the list of available fan modes.""" | |
return FAN_LIST | |
@property | |
def current_swing_mode(self): | |
"""Return the fan setting.""" | |
return self._current_swing | |
@property | |
def swing_list(self): | |
"""Return the list of available swing modes.""" | |
return SWING_LIST |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment