Created
January 4, 2019 19:27
-
-
Save rubenverhoef/3d7ba6b52f03e55a04d212640a4413af to your computer and use it in GitHub Desktop.
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
""" | |
Support for OpenTherm Gateway climate devices. | |
For more details about this platform, please refer to the documentation at | |
http://home-assistant.io/components/climate.opentherm_gw/ | |
""" | |
import logging | |
from homeassistant.components.climate import (ClimateDevice, | |
STATE_HEAT, STATE_COOL, | |
STATE_IDLE, | |
SUPPORT_TARGET_TEMPERATURE, | |
SUPPORT_OPERATION_MODE) | |
from homeassistant.components.opentherm_gw import ( | |
CONF_FLOOR_TEMP, CONF_PRECISION, DATA_DEVICE, DATA_GW_VARS, | |
DATA_OPENTHERM_GW, SIGNAL_OPENTHERM_GW_UPDATE) | |
from homeassistant.const import (ATTR_TEMPERATURE, CONF_NAME, PRECISION_HALVES, | |
PRECISION_TENTHS, PRECISION_WHOLE, | |
TEMP_CELSIUS) | |
from homeassistant.helpers.dispatcher import async_dispatcher_connect | |
DEPENDENCIES = ['opentherm_gw'] | |
SUPPORT_FLAGS = (SUPPORT_TARGET_TEMPERATURE|SUPPORT_OPERATION_MODE) | |
CanCool = False # OpenTherm system can also cool | |
if CanCool == True: | |
OPERATION_MODES = [STATE_HEAT, STATE_COOL] | |
else: | |
OPERATION_MODES = [STATE_HEAT] | |
_LOGGER = logging.getLogger(__name__) | |
async def async_setup_platform(hass, config, async_add_entities, | |
discovery_info=None): | |
"""Set up the opentherm_gw device.""" | |
gateway = OpenThermGateway(hass, discovery_info) | |
async_add_entities([gateway]) | |
class OpenThermGateway(ClimateDevice): | |
"""Representation of a climate device.""" | |
def __init__(self, hass, config): | |
"""Initialize the device.""" | |
self._gateway = hass.data[DATA_OPENTHERM_GW][DATA_DEVICE] | |
self._gw_vars = hass.data[DATA_OPENTHERM_GW][DATA_GW_VARS] | |
self.friendly_name = config.get(CONF_NAME) | |
self.floor_temp = config.get(CONF_FLOOR_TEMP) | |
self.temp_precision = config.get(CONF_PRECISION) | |
self._state = STATE_IDLE | |
self._prevstate = STATE_HEAT | |
self._current_temperature = 0.0 | |
self._target_temperature = 0.0 | |
self._away_mode_a = None | |
self._away_mode_b = None | |
self._away_state_a = False | |
self._away_state_b = False | |
async def async_added_to_hass(self): | |
"""Connect to the OpenTherm Gateway device.""" | |
_LOGGER.debug("Added device %s", self.friendly_name) | |
async_dispatcher_connect(self.hass, SIGNAL_OPENTHERM_GW_UPDATE, | |
self.receive_report) | |
async def receive_report(self, status): | |
"""Receive and handle a new report from the Gateway.""" | |
ch_active = status.get(self._gw_vars.DATA_SLAVE_CH_ACTIVE) | |
flame_on = status.get(self._gw_vars.DATA_SLAVE_FLAME_ON) | |
cooling_active = status.get(self._gw_vars.DATA_SLAVE_COOLING_ACTIVE) | |
if self._state != STATE_IDLE: | |
self._prevstate = self._state | |
if ch_active and flame_on: | |
self._state = STATE_HEAT | |
elif cooling_active and CanCool == True: | |
self._state = STATE_COOL | |
else: | |
self._state = STATE_IDLE | |
self._current_temperature = status.get(self._gw_vars.DATA_ROOM_TEMP) | |
temp = status.get(self._gw_vars.DATA_ROOM_SETPOINT_OVRD) | |
if temp is None: | |
temp = status.get(self._gw_vars.DATA_ROOM_SETPOINT) | |
self._target_temperature = temp | |
# GPIO mode 5: 0 == Away | |
# GPIO mode 6: 1 == Away | |
gpio_a_state = status.get(self._gw_vars.OTGW_GPIO_A) | |
if gpio_a_state == 5: | |
self._away_mode_a = 0 | |
elif gpio_a_state == 6: | |
self._away_mode_a = 1 | |
else: | |
self._away_mode_a = None | |
gpio_b_state = status.get(self._gw_vars.OTGW_GPIO_B) | |
if gpio_b_state == 5: | |
self._away_mode_b = 0 | |
elif gpio_b_state == 6: | |
self._away_mode_b = 1 | |
else: | |
self._away_mode_b = None | |
if self._away_mode_a is not None: | |
self._away_state_a = (status.get( | |
self._gw_vars.OTGW_GPIO_A_STATE) == self._away_mode_a) | |
if self._away_mode_b is not None: | |
self._away_state_b = (status.get( | |
self._gw_vars.OTGW_GPIO_B_STATE) == self._away_mode_b) | |
self.async_schedule_update_ha_state() | |
@property | |
def state(self): | |
"""Return the state of the system.""" | |
return self._state | |
@property | |
def name(self): | |
"""Return the friendly name.""" | |
return self.friendly_name | |
@property | |
def precision(self): | |
"""Return the precision of the system.""" | |
if self.temp_precision is not None: | |
return self.temp_precision | |
if self.hass.config.units.temperature_unit == TEMP_CELSIUS: | |
return PRECISION_HALVES | |
return PRECISION_WHOLE | |
@property | |
def should_poll(self): | |
"""Disable polling for this entity.""" | |
return False | |
@property | |
def temperature_unit(self): | |
"""Return the unit of measurement used by the platform.""" | |
return TEMP_CELSIUS | |
@property | |
def operation_list(self): | |
"""List of available operation modes.""" | |
return OPERATION_MODES | |
@property | |
def current_operation(self): | |
"""Return current operation ie. heat, cool.""" | |
if self._state in OPERATION_MODES: | |
return self._state | |
return self._prevstate | |
@property | |
def current_temperature(self): | |
"""Return the current temperature.""" | |
if self.floor_temp is True: | |
if self.temp_precision == PRECISION_HALVES: | |
return int(2 * self._current_temperature) / 2 | |
if self.temp_precision == PRECISION_TENTHS: | |
return int(10 * self._current_temperature) / 10 | |
return int(self._current_temperature) | |
return self._current_temperature | |
@property | |
def target_temperature(self): | |
"""Return the temperature we try to reach.""" | |
return self._target_temperature | |
@property | |
def target_temperature_step(self): | |
"""Return the supported step of target temperature.""" | |
return self.temp_precision | |
@property | |
def is_away_mode_on(self): | |
"""Return true if away mode is on.""" | |
return self._away_state_a or self._away_state_b | |
async def async_set_temperature(self, **kwargs): | |
"""Set new target temperature.""" | |
if ATTR_TEMPERATURE in kwargs: | |
temp = float(kwargs[ATTR_TEMPERATURE]) | |
self._target_temperature = await self._gateway.set_target_temp( | |
temp) | |
self.async_schedule_update_ha_state() | |
@property | |
def supported_features(self): | |
"""Return the list of supported features.""" | |
return SUPPORT_FLAGS | |
@property | |
def min_temp(self): | |
"""Return the minimum temperature.""" | |
return 1 | |
@property | |
def max_temp(self): | |
"""Return the maximum temperature.""" | |
return 30 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
2019-01-19 08:19:04 ERROR (MainThread) [homeassistant.core] Error doing job: Task exception was never retrieved
Traceback (most recent call last):
File "/usr/lib/python3.5/asyncio/tasks.py", line 239, in _step
result = coro.send(None)
File "/usr/local/lib/python3.5/dist-packages/homeassistant/helpers/entity.py", line 239, in async_update_ha_state
attr = self.state_attributes or {}
File "/usr/local/lib/python3.5/dist-packages/homeassistant/components/climate/init.py", line 225, in state_attributes
self.hass, self.current_temperature, self.temperature_unit,
File "/usr/local/lib/python3.5/dist-packages/homeassistant/components/climate/opentherm_gw.py", line 133, in current_temperature
return int(2 * self._current_temperature) / 2
issue: got a few of these errors at startup,
reason: _current_temperature is not set (yet) and you cannot calculate with it (yet)
solution: insert before line 156:
if self._current_temperature is None:
return 0