From fc67f5eef3c69a2e1fe693d0c519c1a1c3e991ce Mon Sep 17 00:00:00 2001 From: mvn23 Date: Tue, 9 Oct 2018 21:06:24 +0200 Subject: [PATCH] Rewrite opentherm_gw to a component (#17133) * Rewrite opentherm_gw to a component which loads the opentherm_gw climate platform. * Add OpenTherm Gateway sensor platform. * Remove library imports from platforms (use hass.data instead) * Update .coveragerc * Update docstrings to use new component documentation url * Add OpenTherm Gateway binary sensor support. Fix houndci findings. * Revert "Add OpenTherm Gateway binary sensor support." This reverts commit 5711dc4c25edc7352ba8bdf14875c79f0bf51d11. * Revert "Add OpenTherm Gateway sensor platform." This reverts commit b3505ed5611d52842c1ea623cd04a13ef2bb9a88. * Remove import from platform, use hass.data instead. Update .coveragerc Update docstrings Update requirements_all.txt General code cleanup * Fix review findings. Avoid using hass.data within connect_and_subscribe. --- .coveragerc | 4 +- .../components/climate/opentherm_gw.py | 81 ++++++++----------- homeassistant/components/opentherm_gw.py | 74 +++++++++++++++++ requirements_all.txt | 2 +- 4 files changed, 111 insertions(+), 50 deletions(-) create mode 100644 homeassistant/components/opentherm_gw.py diff --git a/.coveragerc b/.coveragerc index 801932b19fba6c..459b59f3d39a46 100644 --- a/.coveragerc +++ b/.coveragerc @@ -248,6 +248,9 @@ omit = homeassistant/components/opencv.py homeassistant/components/*/opencv.py + homeassistant/components/opentherm_gw.py + homeassistant/components/climate/opentherm_gw.py + homeassistant/components/openuv/__init__.py homeassistant/components/*/openuv.py @@ -438,7 +441,6 @@ omit = homeassistant/components/climate/honeywell.py homeassistant/components/climate/knx.py homeassistant/components/climate/oem.py - homeassistant/components/climate/opentherm_gw.py homeassistant/components/climate/proliphix.py homeassistant/components/climate/radiotherm.py homeassistant/components/climate/sensibo.py diff --git a/homeassistant/components/climate/opentherm_gw.py b/homeassistant/components/climate/opentherm_gw.py index 00049d26b7f994..6dc52e6acc7f18 100644 --- a/homeassistant/components/climate/opentherm_gw.py +++ b/homeassistant/components/climate/opentherm_gw.py @@ -1,34 +1,23 @@ """ -Support for OpenTherm Gateway devices. +Support for OpenTherm Gateway climate devices. -For more details about this component, please refer to the documentation at +For more details about this platform, please refer to the documentation at http://home-assistant.io/components/climate.opentherm_gw/ """ import logging -import voluptuous as vol - -from homeassistant.components.climate import (ClimateDevice, PLATFORM_SCHEMA, - STATE_IDLE, STATE_HEAT, - STATE_COOL, +from homeassistant.components.climate import (ClimateDevice, STATE_IDLE, + STATE_HEAT, STATE_COOL, SUPPORT_TARGET_TEMPERATURE) -from homeassistant.const import (ATTR_TEMPERATURE, CONF_DEVICE, CONF_NAME, - PRECISION_HALVES, PRECISION_TENTHS, - TEMP_CELSIUS, PRECISION_WHOLE) -import homeassistant.helpers.config_validation as cv - -REQUIREMENTS = ['pyotgw==0.1b0'] - -CONF_FLOOR_TEMP = "floor_temperature" -CONF_PRECISION = 'precision' +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 -PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ - vol.Required(CONF_DEVICE): cv.string, - vol.Optional(CONF_NAME, default="OpenTherm Gateway"): cv.string, - vol.Optional(CONF_PRECISION): vol.In([PRECISION_TENTHS, PRECISION_HALVES, - PRECISION_WHOLE]), - vol.Optional(CONF_FLOOR_TEMP, default=False): cv.boolean, -}) +DEPENDENCIES = ['opentherm_gw'] SUPPORT_FLAGS = (SUPPORT_TARGET_TEMPERATURE) _LOGGER = logging.getLogger(__name__) @@ -37,19 +26,17 @@ async def async_setup_platform(hass, config, async_add_entities, discovery_info=None): """Set up the opentherm_gw device.""" - gateway = OpenThermGateway(config) + gateway = OpenThermGateway(hass, discovery_info) async_add_entities([gateway]) class OpenThermGateway(ClimateDevice): """Representation of a climate device.""" - def __init__(self, config): - """Initialize the sensor.""" - import pyotgw - self.pyotgw = pyotgw - self.gateway = self.pyotgw.pyotgw() - self._device = config[CONF_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) @@ -63,40 +50,38 @@ def __init__(self, config): async def async_added_to_hass(self): """Connect to the OpenTherm Gateway device.""" - await self.gateway.connect(self.hass.loop, self._device) - self.gateway.subscribe(self.receive_report) - _LOGGER.debug("Connected to %s on %s", self.friendly_name, - self._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.""" - _LOGGER.debug("Received report: %s", status) - ch_active = status.get(self.pyotgw.DATA_SLAVE_CH_ACTIVE) - flame_on = status.get(self.pyotgw.DATA_SLAVE_FLAME_ON) - cooling_active = status.get(self.pyotgw.DATA_SLAVE_COOLING_ACTIVE) + 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 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.pyotgw.DATA_ROOM_TEMP) + self._current_temperature = status.get(self._gw_vars.DATA_ROOM_TEMP) - temp = status.get(self.pyotgw.DATA_ROOM_SETPOINT_OVRD) + temp = status.get(self._gw_vars.DATA_ROOM_SETPOINT_OVRD) if temp is None: - temp = status.get(self.pyotgw.DATA_ROOM_SETPOINT) + 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.pyotgw.OTGW_GPIO_A) + 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.pyotgw.OTGW_GPIO_B) + 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: @@ -104,11 +89,11 @@ async def receive_report(self, status): else: self._away_mode_b = None if self._away_mode_a is not None: - self._away_state_a = (status.get(self.pyotgw.OTGW_GPIO_A_STATE) == - self._away_mode_a) + 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.pyotgw.OTGW_GPIO_B_STATE) == - self._away_mode_b) + self._away_state_b = (status.get( + self._gw_vars.OTGW_GPIO_B_STATE) == self._away_mode_b) self.async_schedule_update_ha_state() @property @@ -170,7 +155,7 @@ 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( + self._target_temperature = await self._gateway.set_target_temp( temp) self.async_schedule_update_ha_state() diff --git a/homeassistant/components/opentherm_gw.py b/homeassistant/components/opentherm_gw.py new file mode 100644 index 00000000000000..7bc2bbeaa8a46e --- /dev/null +++ b/homeassistant/components/opentherm_gw.py @@ -0,0 +1,74 @@ +""" +Support for OpenTherm Gateway devices. + +For more details about this component, please refer to the documentation at +http://home-assistant.io/components/opentherm_gw/ +""" +import logging + +import voluptuous as vol + +from homeassistant.const import (CONF_DEVICE, CONF_NAME, PRECISION_HALVES, + PRECISION_TENTHS, PRECISION_WHOLE) +from homeassistant.helpers.discovery import async_load_platform +from homeassistant.helpers.dispatcher import async_dispatcher_send + +import homeassistant.helpers.config_validation as cv + +DOMAIN = 'opentherm_gw' + +CONF_CLIMATE = 'climate' +CONF_FLOOR_TEMP = 'floor_temperature' +CONF_PRECISION = 'precision' + +DATA_DEVICE = 'device' +DATA_GW_VARS = 'gw_vars' +DATA_OPENTHERM_GW = 'opentherm_gw' + +SIGNAL_OPENTHERM_GW_UPDATE = 'opentherm_gw_update' + +CLIMATE_SCHEMA = vol.Schema({ + vol.Optional(CONF_NAME, default="OpenTherm Gateway"): cv.string, + vol.Optional(CONF_PRECISION): vol.In([PRECISION_TENTHS, PRECISION_HALVES, + PRECISION_WHOLE]), + vol.Optional(CONF_FLOOR_TEMP, default=False): cv.boolean, +}) + +CONFIG_SCHEMA = vol.Schema({ + DOMAIN: vol.Schema({ + vol.Required(CONF_DEVICE): cv.string, + vol.Optional(CONF_CLIMATE, default={}): CLIMATE_SCHEMA, + }), +}, extra=vol.ALLOW_EXTRA) + +REQUIREMENTS = ['pyotgw==0.1b0'] + +_LOGGER = logging.getLogger(__name__) + + +async def async_setup(hass, config): + """Set up the OpenTherm Gateway component.""" + import pyotgw + conf = config[DOMAIN] + gateway = pyotgw.pyotgw() + hass.data[DATA_OPENTHERM_GW] = { + DATA_DEVICE: gateway, + DATA_GW_VARS: pyotgw.vars, + } + hass.async_create_task(connect_and_subscribe( + hass, conf[CONF_DEVICE], gateway)) + hass.async_create_task(async_load_platform( + hass, 'climate', DOMAIN, conf.get(CONF_CLIMATE))) + return True + + +async def connect_and_subscribe(hass, device_path, gateway): + """Connect to serial device and subscribe report handler.""" + await gateway.connect(hass.loop, device_path) + _LOGGER.debug("Connected to OpenTherm Gateway at %s", device_path) + + async def handle_report(status): + """Handle reports from the OpenTherm Gateway.""" + _LOGGER.debug("Received report: %s", status) + async_dispatcher_send(hass, SIGNAL_OPENTHERM_GW_UPDATE, status) + gateway.subscribe(handle_report) diff --git a/requirements_all.txt b/requirements_all.txt index 3581eb3b9e5cb4..9804d3022d4d54 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -1027,7 +1027,7 @@ pyoppleio==1.0.5 # homeassistant.components.iota pyota==2.0.5 -# homeassistant.components.climate.opentherm_gw +# homeassistant.components.opentherm_gw pyotgw==0.1b0 # homeassistant.auth.mfa_modules.notify