Skip to content

Commit

Permalink
Rewrite opentherm_gw to a component (#17133)
Browse files Browse the repository at this point in the history
* 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 5711dc4.

* Revert "Add OpenTherm Gateway sensor platform."

This reverts commit b3505ed.

* 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.
  • Loading branch information
mvn23 authored and MartinHjelmare committed Oct 9, 2018
1 parent 2aeb0ef commit fc67f5e
Show file tree
Hide file tree
Showing 4 changed files with 111 additions and 50 deletions.
4 changes: 3 additions & 1 deletion .coveragerc
Expand Up @@ -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

Expand Down Expand Up @@ -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
Expand Down
81 changes: 33 additions & 48 deletions 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__)
Expand All @@ -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)
Expand All @@ -63,52 +50,50 @@ 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:
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.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
Expand Down Expand Up @@ -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()

Expand Down
74 changes: 74 additions & 0 deletions 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)
2 changes: 1 addition & 1 deletion requirements_all.txt
Expand Up @@ -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
Expand Down

0 comments on commit fc67f5e

Please sign in to comment.