Multiple devices support for opentherm_gw (#22932)
* Breaking change: Rewrite opentherm_gw to add support for more than one OpenTherm Gateway. Breaks config layout and child entity ids and adds a required parameter to all service calls (gateway_id). * Add schema and parameter description for service opentherm_gw.reset_gateway. * Add optional name attribute in config to be used for friendly names. Fix bugs in binary_sensor and climate platforms. * pylint fixes * Remove unused variables. * Update manifest.json, remove REQUIREMENTS from .py file * Update CODEOWNERS * Address issues that were brought up (requested changes): - Move imports to module level - Change certain functions from async to sync - Move constants to const.py (new file) - Call gateway setup from outside of __init__() - Move validation of monitored_variables to config schema * Address requested changes: - Make module imports relative - Move more functions from async to sync, decorate with @callback where necessary - Remove monitored_variables option, add all sensors by default
This commit is contained in:
parent
d9f2a406f6
commit
43a6be6471
8 changed files with 486 additions and 519 deletions
|
@ -1,17 +1,21 @@
|
|||
"""Support for OpenTherm Gateway climate devices."""
|
||||
import logging
|
||||
|
||||
from homeassistant.components.climate import ClimateDevice
|
||||
from pyotgw import vars as gw_vars
|
||||
|
||||
from homeassistant.components.climate import ClimateDevice, ENTITY_ID_FORMAT
|
||||
from homeassistant.components.climate.const import (
|
||||
STATE_COOL, STATE_HEAT, STATE_IDLE, SUPPORT_TARGET_TEMPERATURE)
|
||||
from homeassistant.const import (
|
||||
ATTR_TEMPERATURE, CONF_NAME, PRECISION_HALVES, PRECISION_TENTHS,
|
||||
PRECISION_WHOLE, TEMP_CELSIUS)
|
||||
ATTR_TEMPERATURE, PRECISION_HALVES, PRECISION_TENTHS, PRECISION_WHOLE,
|
||||
TEMP_CELSIUS)
|
||||
from homeassistant.core import callback
|
||||
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
||||
from homeassistant.helpers.entity import async_generate_entity_id
|
||||
|
||||
from .const import (
|
||||
CONF_FLOOR_TEMP, CONF_PRECISION, DATA_GATEWAYS, DATA_OPENTHERM_GW)
|
||||
|
||||
from . import (
|
||||
CONF_FLOOR_TEMP, CONF_PRECISION, DATA_DEVICE, DATA_GW_VARS,
|
||||
DATA_OPENTHERM_GW, SIGNAL_OPENTHERM_GW_UPDATE)
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
@ -21,20 +25,22 @@ SUPPORT_FLAGS = SUPPORT_TARGET_TEMPERATURE
|
|||
async def async_setup_platform(
|
||||
hass, config, async_add_entities, discovery_info=None):
|
||||
"""Set up the opentherm_gw device."""
|
||||
gateway = OpenThermGateway(hass, discovery_info)
|
||||
gw_dev = hass.data[DATA_OPENTHERM_GW][DATA_GATEWAYS][discovery_info]
|
||||
gateway = OpenThermClimate(gw_dev)
|
||||
async_add_entities([gateway])
|
||||
|
||||
|
||||
class OpenThermGateway(ClimateDevice):
|
||||
class OpenThermClimate(ClimateDevice):
|
||||
"""Representation of a climate device."""
|
||||
|
||||
def __init__(self, hass, config):
|
||||
def __init__(self, gw_dev):
|
||||
"""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._gateway = gw_dev
|
||||
self.entity_id = async_generate_entity_id(
|
||||
ENTITY_ID_FORMAT, gw_dev.gw_id, hass=gw_dev.hass)
|
||||
self.friendly_name = gw_dev.name
|
||||
self.floor_temp = gw_dev.climate_config[CONF_FLOOR_TEMP]
|
||||
self.temp_precision = gw_dev.climate_config.get(CONF_PRECISION)
|
||||
self._current_operation = STATE_IDLE
|
||||
self._current_temperature = None
|
||||
self._new_target_temperature = None
|
||||
|
@ -47,36 +53,37 @@ class OpenThermGateway(ClimateDevice):
|
|||
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,
|
||||
async_dispatcher_connect(self.hass, self._gateway.update_signal,
|
||||
self.receive_report)
|
||||
|
||||
async def receive_report(self, status):
|
||||
@callback
|
||||
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)
|
||||
ch_active = status.get(gw_vars.DATA_SLAVE_CH_ACTIVE)
|
||||
flame_on = status.get(gw_vars.DATA_SLAVE_FLAME_ON)
|
||||
cooling_active = status.get(gw_vars.DATA_SLAVE_COOLING_ACTIVE)
|
||||
if ch_active and flame_on:
|
||||
self._current_operation = STATE_HEAT
|
||||
elif cooling_active:
|
||||
self._current_operation = STATE_COOL
|
||||
else:
|
||||
self._current_operation = STATE_IDLE
|
||||
self._current_temperature = status.get(self._gw_vars.DATA_ROOM_TEMP)
|
||||
temp_upd = status.get(self._gw_vars.DATA_ROOM_SETPOINT)
|
||||
self._current_temperature = status.get(gw_vars.DATA_ROOM_TEMP)
|
||||
temp_upd = status.get(gw_vars.DATA_ROOM_SETPOINT)
|
||||
if self._target_temperature != temp_upd:
|
||||
self._new_target_temperature = None
|
||||
self._target_temperature = temp_upd
|
||||
|
||||
# GPIO mode 5: 0 == Away
|
||||
# GPIO mode 6: 1 == Away
|
||||
gpio_a_state = status.get(self._gw_vars.OTGW_GPIO_A)
|
||||
gpio_a_state = status.get(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)
|
||||
gpio_b_state = status.get(gw_vars.OTGW_GPIO_B)
|
||||
if gpio_b_state == 5:
|
||||
self._away_mode_b = 0
|
||||
elif gpio_b_state == 6:
|
||||
|
@ -85,10 +92,10 @@ class OpenThermGateway(ClimateDevice):
|
|||
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)
|
||||
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)
|
||||
gw_vars.OTGW_GPIO_B_STATE) == self._away_mode_b)
|
||||
self.async_schedule_update_ha_state()
|
||||
|
||||
@property
|
||||
|
@ -126,9 +133,9 @@ class OpenThermGateway(ClimateDevice):
|
|||
if self._current_temperature is None:
|
||||
return
|
||||
if self.floor_temp is True:
|
||||
if self.temp_precision == PRECISION_HALVES:
|
||||
if self.precision == PRECISION_HALVES:
|
||||
return int(2 * self._current_temperature) / 2
|
||||
if self.temp_precision == PRECISION_TENTHS:
|
||||
if self.precision == PRECISION_TENTHS:
|
||||
return int(10 * self._current_temperature) / 10
|
||||
return int(self._current_temperature)
|
||||
return self._current_temperature
|
||||
|
@ -141,7 +148,7 @@ class OpenThermGateway(ClimateDevice):
|
|||
@property
|
||||
def target_temperature_step(self):
|
||||
"""Return the supported step of target temperature."""
|
||||
return self.temp_precision
|
||||
return self.precision
|
||||
|
||||
@property
|
||||
def is_away_mode_on(self):
|
||||
|
@ -154,8 +161,8 @@ class OpenThermGateway(ClimateDevice):
|
|||
temp = float(kwargs[ATTR_TEMPERATURE])
|
||||
if temp == self.target_temperature:
|
||||
return
|
||||
self._new_target_temperature = await self._gateway.set_target_temp(
|
||||
temp)
|
||||
self._new_target_temperature = (
|
||||
await self._gateway.gateway.set_target_temp(temp))
|
||||
self.async_schedule_update_ha_state()
|
||||
|
||||
@property
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue