Add incomfort sensor and binary_sensor (#23812)

* Initial commit - add sensors to incomfort

* improve temp heuristics

* remove self._hass

* device_state_attributes shoudln't be None

* bump client

* refactor to reduce duplication of attributes

* refactor binary_sensor to simplify

* refactor binary_sensor to simplify 2

* delint

* fix rebase regression

* small refactor

* delint

* remove DEVICE_CLASS for CV pressure

* tidy up exception handling

* delint

* fix exception handling

* use differnt icon for boiler temp
This commit is contained in:
David Bonnes 2019-06-19 17:03:18 +01:00 committed by Paulus Schoutsen
parent d63c44f778
commit c1d441b0ac
5 changed files with 190 additions and 16 deletions

View file

@ -1,6 +1,7 @@
"""Support for an Intergas boiler via an InComfort/Intouch Lan2RF gateway.""" """Support for an Intergas boiler via an InComfort/Intouch Lan2RF gateway."""
import logging import logging
from aiohttp import ClientResponseError
import voluptuous as vol import voluptuous as vol
from incomfortclient import Gateway as InComfortGateway from incomfortclient import Gateway as InComfortGateway
@ -30,21 +31,20 @@ async def async_setup(hass, hass_config):
credentials = dict(hass_config[DOMAIN]) credentials = dict(hass_config[DOMAIN])
hostname = credentials.pop(CONF_HOST) hostname = credentials.pop(CONF_HOST)
client = incomfort_data['client'] = InComfortGateway(
hostname, **credentials, session=async_get_clientsession(hass)
)
try: try:
client = incomfort_data['client'] = InComfortGateway(
hostname, **credentials, session=async_get_clientsession(hass)
)
heater = incomfort_data['heater'] = list(await client.heaters)[0] heater = incomfort_data['heater'] = list(await client.heaters)[0]
await heater.update() except ClientResponseError as err:
except AssertionError: # assert response.status == HTTP_OK
_LOGGER.warning( _LOGGER.warning(
"Setup failed, check your configuration.", "Setup failed, check your configuration, message is: %s", err)
exc_info=True)
return False return False
for platform in ['water_heater', 'climate']: await heater.update()
for platform in ['water_heater', 'binary_sensor', 'sensor', 'climate']:
hass.async_create_task(async_load_platform( hass.async_create_task(async_load_platform(
hass, platform, DOMAIN, {}, hass_config)) hass, platform, DOMAIN, {}, hass_config))

View file

@ -0,0 +1,52 @@
"""Support for an Intergas boiler via an InComfort/InTouch Lan2RF gateway."""
from homeassistant.components.binary_sensor import BinarySensorDevice
from homeassistant.core import callback
from homeassistant.helpers.dispatcher import async_dispatcher_connect
from . import DOMAIN
async def async_setup_platform(hass, config, async_add_entities,
discovery_info=None):
"""Set up an InComfort/InTouch binary_sensor device."""
async_add_entities([
IncomfortFailed(hass.data[DOMAIN]['client'],
hass.data[DOMAIN]['heater'])
])
class IncomfortFailed(BinarySensorDevice):
"""Representation of an InComfort Failed sensor."""
def __init__(self, client, boiler):
"""Initialize the binary sensor."""
self._client = client
self._boiler = boiler
async def async_added_to_hass(self):
"""Set up a listener when this entity is added to HA."""
async_dispatcher_connect(self.hass, DOMAIN, self._refresh)
@callback
def _refresh(self):
self.async_schedule_update_ha_state(force_refresh=True)
@property
def name(self):
"""Return the name of the sensor."""
return 'Fault state'
@property
def is_on(self):
"""Return the status of the sensor."""
return self._boiler.status['is_failed']
@property
def device_state_attributes(self):
"""Return the device state attributes."""
return {'fault_code': self._boiler.status['fault_code']}
@property
def should_poll(self) -> bool:
"""Return False as this device should never be polled."""
return False

View file

@ -0,0 +1,110 @@
"""Support for an Intergas boiler via an InComfort/InTouch Lan2RF gateway."""
from homeassistant.const import (
PRESSURE_BAR, TEMP_CELSIUS, DEVICE_CLASS_TEMPERATURE)
from homeassistant.core import callback
from homeassistant.helpers.dispatcher import async_dispatcher_connect
from homeassistant.helpers.entity import Entity
from . import DOMAIN
INTOUCH_HEATER_TEMP = 'CV Temp'
INTOUCH_PRESSURE = 'CV Pressure'
INTOUCH_TAP_TEMP = 'Tap Temp'
INTOUCH_MAP_ATTRS = {
INTOUCH_HEATER_TEMP: ['heater_temp', 'is_pumping'],
INTOUCH_TAP_TEMP: ['tap_temp', 'is_tapping'],
}
async def async_setup_platform(hass, config, async_add_entities,
discovery_info=None):
"""Set up an InComfort/InTouch sensor device."""
client = hass.data[DOMAIN]['client']
heater = hass.data[DOMAIN]['heater']
async_add_entities([
IncomfortPressure(client, heater, INTOUCH_PRESSURE),
IncomfortTemperature(client, heater, INTOUCH_HEATER_TEMP),
IncomfortTemperature(client, heater, INTOUCH_TAP_TEMP)
])
class IncomfortSensor(Entity):
"""Representation of an InComfort/InTouch sensor device."""
def __init__(self, client, boiler):
"""Initialize the sensor."""
self._client = client
self._boiler = boiler
self._name = None
self._device_class = None
self._unit_of_measurement = None
async def async_added_to_hass(self):
"""Set up a listener when this entity is added to HA."""
async_dispatcher_connect(self.hass, DOMAIN, self._refresh)
@callback
def _refresh(self):
self.async_schedule_update_ha_state(force_refresh=True)
@property
def name(self):
"""Return the name of the sensor."""
return self._name
@property
def device_class(self):
"""Return the device class of the sensor."""
return self._device_class
@property
def unit_of_measurement(self):
"""Return the unit of measurement of the sensor."""
return self._unit_of_measurement
@property
def should_poll(self) -> bool:
"""Return False as this device should never be polled."""
return False
class IncomfortPressure(IncomfortSensor):
"""Representation of an InTouch CV Pressure sensor."""
def __init__(self, client, boiler, name):
"""Initialize the sensor."""
super().__init__(client, boiler)
self._name = name
self._unit_of_measurement = PRESSURE_BAR
@property
def state(self):
"""Return the state/value of the sensor."""
return self._boiler.status['pressure']
class IncomfortTemperature(IncomfortSensor):
"""Representation of an InTouch Temperature sensor."""
def __init__(self, client, boiler, name):
"""Initialize the signal strength sensor."""
super().__init__(client, boiler)
self._name = name
self._device_class = DEVICE_CLASS_TEMPERATURE
self._unit_of_measurement = TEMP_CELSIUS
@property
def state(self):
"""Return the state of the sensor."""
return self._boiler.status[INTOUCH_MAP_ATTRS[self._name][0]]
@property
def device_state_attributes(self):
"""Return the device state attributes."""
key = INTOUCH_MAP_ATTRS[self._name][1]
return {key: self._boiler.status[key]}

View file

@ -2,8 +2,10 @@
import asyncio import asyncio
import logging import logging
from aiohttp import ClientResponseError
from homeassistant.components.water_heater import WaterHeaterDevice from homeassistant.components.water_heater import WaterHeaterDevice
from homeassistant.const import TEMP_CELSIUS from homeassistant.const import TEMP_CELSIUS
from homeassistant.helpers.dispatcher import async_dispatcher_send
from . import DOMAIN from . import DOMAIN
@ -16,8 +18,8 @@ HEATER_MIN_TEMP = 30.0
HEATER_NAME = 'Boiler' HEATER_NAME = 'Boiler'
HEATER_ATTRS = [ HEATER_ATTRS = [
'display_code', 'display_text', 'fault_code', 'is_burning', 'is_failed', 'display_code', 'display_text', 'is_burning',
'is_pumping', 'is_tapping', 'heater_temp', 'tap_temp', 'pressure'] 'rf_message_rssi', 'nodenr', 'rfstatus_cntr']
async def async_setup_platform(hass, hass_config, async_add_entities, async def async_setup_platform(hass, hass_config, async_add_entities,
@ -43,6 +45,11 @@ class IncomfortWaterHeater(WaterHeaterDevice):
"""Return the name of the water_heater device.""" """Return the name of the water_heater device."""
return HEATER_NAME return HEATER_NAME
@property
def icon(self):
"""Return the icon of the water_heater device."""
return "mdi:oil-temperature"
@property @property
def device_state_attributes(self): def device_state_attributes(self):
"""Return the device state attributes.""" """Return the device state attributes."""
@ -55,7 +62,9 @@ class IncomfortWaterHeater(WaterHeaterDevice):
"""Return the current temperature.""" """Return the current temperature."""
if self._heater.is_tapping: if self._heater.is_tapping:
return self._heater.tap_temp return self._heater.tap_temp
return self._heater.heater_temp if self._heater.is_pumping:
return self._heater.heater_temp
return max(self._heater.heater_temp, self._heater.tap_temp)
@property @property
def min_temp(self): def min_temp(self):
@ -81,7 +90,7 @@ class IncomfortWaterHeater(WaterHeaterDevice):
def current_operation(self): def current_operation(self):
"""Return the current operation mode.""" """Return the current operation mode."""
if self._heater.is_failed: if self._heater.is_failed:
return "Failed ({})".format(self._heater.fault_code) return "Fault code: {}".format(self._heater.fault_code)
return self._heater.display_text return self._heater.display_text
@ -90,5 +99,7 @@ class IncomfortWaterHeater(WaterHeaterDevice):
try: try:
await self._heater.update() await self._heater.update()
except (AssertionError, asyncio.TimeoutError) as err: except (ClientResponseError, asyncio.TimeoutError) as err:
_LOGGER.warning("Update failed, message: %s", err) _LOGGER.warning("Update failed, message is: %s", err)
async_dispatcher_send(self.hass, DOMAIN)

View file

@ -346,6 +346,7 @@ LENGTH_MILES = 'mi' # type: str
# Pressure units # Pressure units
PRESSURE_PA = 'Pa' # type: str PRESSURE_PA = 'Pa' # type: str
PRESSURE_HPA = 'hPa' # type: str PRESSURE_HPA = 'hPa' # type: str
PRESSURE_BAR = 'bar' # type: str
PRESSURE_MBAR = 'mbar' # type: str PRESSURE_MBAR = 'mbar' # type: str
PRESSURE_INHG = 'inHg' # type: str PRESSURE_INHG = 'inHg' # type: str
PRESSURE_PSI = 'psi' # type: str PRESSURE_PSI = 'psi' # type: str