Add opentherm_gw device support (#28722)

* Add opentherm_gw device support

* Add support for enabling/disabling of opentherm_gw entities

* Disable sensors by default, base climate entity_id on gw_id instead of friendly_name to guarantee uniqueness

* Remove platform name from unique_id
This commit is contained in:
mvn23 2019-11-27 14:14:41 +01:00 committed by Charles Garwood
parent 12ae8b025f
commit fdf0793fdd
4 changed files with 110 additions and 12 deletions

View File

@ -1,4 +1,5 @@
"""Support for OpenTherm Gateway devices."""
import asyncio
import logging
from datetime import datetime, date
@ -344,6 +345,18 @@ def register_services(hass):
)
async def async_unload_entry(hass, entry):
"""Cleanup and disconnect from gateway."""
await asyncio.gather(
hass.config_entries.async_forward_entry_unload(entry, COMP_BINARY_SENSOR),
hass.config_entries.async_forward_entry_unload(entry, COMP_CLIMATE),
hass.config_entries.async_forward_entry_unload(entry, COMP_SENSOR),
)
gateway = hass.data[DATA_OPENTHERM_GW][DATA_GATEWAYS][entry.data[CONF_ID]]
await gateway.cleanup()
return True
class OpenThermGatewayDevice:
"""OpenTherm Gateway device class."""
@ -358,18 +371,21 @@ class OpenThermGatewayDevice:
self.update_signal = f"{DATA_OPENTHERM_GW}_{self.gw_id}_update"
self.options_update_signal = f"{DATA_OPENTHERM_GW}_{self.gw_id}_options_update"
self.gateway = pyotgw.pyotgw()
self.gw_version = None
async def cleanup(self, event=None):
"""Reset overrides on the gateway."""
await self.gateway.set_control_setpoint(0)
await self.gateway.set_max_relative_mod("-")
await self.gateway.disconnect()
async def connect_and_subscribe(self):
"""Connect to serial device and subscribe report handler."""
await self.gateway.connect(self.hass.loop, self.device_path)
self.status = await self.gateway.connect(self.hass.loop, self.device_path)
_LOGGER.debug("Connected to OpenTherm Gateway at %s", self.device_path)
self.gw_version = self.status.get(gw_vars.OTGW_BUILD)
async def cleanup(event):
"""Reset overrides on the gateway."""
await self.gateway.set_control_setpoint(0)
await self.gateway.set_max_relative_mod("-")
self.hass.bus.async_listen(EVENT_HOMEASSISTANT_STOP, cleanup)
self.hass.bus.async_listen(EVENT_HOMEASSISTANT_STOP, self.cleanup)
async def handle_report(status):
"""Handle reports from the OpenTherm Gateway."""

View File

@ -7,6 +7,7 @@ from homeassistant.core import callback
from homeassistant.helpers.dispatcher import async_dispatcher_connect
from homeassistant.helpers.entity import async_generate_entity_id
from . import DOMAIN
from .const import BINARY_SENSOR_INFO, DATA_GATEWAYS, DATA_OPENTHERM_GW
@ -44,14 +45,27 @@ class OpenThermBinarySensor(BinarySensorDevice):
self._state = None
self._device_class = device_class
self._friendly_name = friendly_name_format.format(gw_dev.name)
self._unsub_updates = None
async def async_added_to_hass(self):
"""Subscribe to updates from the component."""
_LOGGER.debug("Added OpenTherm Gateway binary sensor %s", self._friendly_name)
async_dispatcher_connect(
self._unsub_updates = async_dispatcher_connect(
self.hass, self._gateway.update_signal, self.receive_report
)
async def async_will_remove_from_hass(self):
"""Unsubscribe from updates from the component."""
_LOGGER.debug(
"Removing OpenTherm Gateway binary sensor %s", self._friendly_name
)
self._unsub_updates()
@property
def entity_registry_enabled_default(self):
"""Disable binary_sensors by default."""
return False
@callback
def receive_report(self, status):
"""Handle status updates from the component."""
@ -63,6 +77,22 @@ class OpenThermBinarySensor(BinarySensorDevice):
"""Return the friendly name."""
return self._friendly_name
@property
def device_info(self):
"""Return device info."""
return {
"identifiers": {(DOMAIN, self._gateway.gw_id)},
"name": self._gateway.name,
"manufacturer": "Schelte Bron",
"model": "OpenTherm Gateway",
"sw_version": self._gateway.gw_version,
}
@property
def unique_id(self):
"""Return a unique ID."""
return f"{self._gateway.gw_id}-{self._var}"
@property
def is_on(self):
"""Return true if the binary sensor is on."""

View File

@ -3,7 +3,7 @@ import logging
from pyotgw import vars as gw_vars
from homeassistant.components.climate import ClimateDevice
from homeassistant.components.climate import ClimateDevice, ENTITY_ID_FORMAT
from homeassistant.components.climate.const import (
CURRENT_HVAC_COOL,
CURRENT_HVAC_HEAT,
@ -25,7 +25,9 @@ from homeassistant.const import (
)
from homeassistant.core import callback
from homeassistant.helpers.dispatcher import async_dispatcher_connect
from homeassistant.helpers.entity import async_generate_entity_id
from . import DOMAIN
from .const import CONF_FLOOR_TEMP, CONF_PRECISION, DATA_GATEWAYS, DATA_OPENTHERM_GW
@ -56,6 +58,9 @@ class OpenThermClimate(ClimateDevice):
def __init__(self, gw_dev, options):
"""Initialize the device."""
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 = options.get(CONF_FLOOR_TEMP, DEFAULT_FLOOR_TEMP)
self.temp_precision = options.get(CONF_PRECISION, DEFAULT_PRECISION)
@ -68,6 +73,8 @@ class OpenThermClimate(ClimateDevice):
self._away_mode_b = None
self._away_state_a = False
self._away_state_b = False
self._unsub_options = None
self._unsub_updates = None
@callback
def update_options(self, entry):
@ -79,13 +86,19 @@ class OpenThermClimate(ClimateDevice):
async def async_added_to_hass(self):
"""Connect to the OpenTherm Gateway device."""
_LOGGER.debug("Added OpenTherm Gateway climate device %s", self.friendly_name)
async_dispatcher_connect(
self._unsub_updates = async_dispatcher_connect(
self.hass, self._gateway.update_signal, self.receive_report
)
async_dispatcher_connect(
self._unsub_options = async_dispatcher_connect(
self.hass, self._gateway.options_update_signal, self.update_options
)
async def async_will_remove_from_hass(self):
"""Unsubscribe from updates from the component."""
_LOGGER.debug("Removing OpenTherm Gateway climate %s", self.friendly_name)
self._unsub_options()
self._unsub_updates()
@callback
def receive_report(self, status):
"""Receive and handle a new report from the Gateway."""
@ -139,6 +152,17 @@ class OpenThermClimate(ClimateDevice):
"""Return the friendly name."""
return self.friendly_name
@property
def device_info(self):
"""Return device info."""
return {
"identifiers": {(DOMAIN, self._gateway.gw_id)},
"name": self._gateway.name,
"manufacturer": "Schelte Bron",
"model": "OpenTherm Gateway",
"sw_version": self._gateway.gw_version,
}
@property
def unique_id(self):
"""Return a unique ID."""

View File

@ -7,6 +7,7 @@ from homeassistant.core import callback
from homeassistant.helpers.dispatcher import async_dispatcher_connect
from homeassistant.helpers.entity import Entity, async_generate_entity_id
from . import DOMAIN
from .const import DATA_GATEWAYS, DATA_OPENTHERM_GW, SENSOR_INFO
@ -47,14 +48,25 @@ class OpenThermSensor(Entity):
self._device_class = device_class
self._unit = unit
self._friendly_name = friendly_name_format.format(gw_dev.name)
self._unsub_updates = None
async def async_added_to_hass(self):
"""Subscribe to updates from the component."""
_LOGGER.debug("Added OpenTherm Gateway sensor %s", self._friendly_name)
async_dispatcher_connect(
self._unsub_updates = async_dispatcher_connect(
self.hass, self._gateway.update_signal, self.receive_report
)
async def async_will_remove_from_hass(self):
"""Unsubscribe from updates from the component."""
_LOGGER.debug("Removing OpenTherm Gateway sensor %s", self._friendly_name)
self._unsub_updates()
@property
def entity_registry_enabled_default(self):
"""Disable sensors by default."""
return False
@callback
def receive_report(self, status):
"""Handle status updates from the component."""
@ -69,6 +81,22 @@ class OpenThermSensor(Entity):
"""Return the friendly name of the sensor."""
return self._friendly_name
@property
def device_info(self):
"""Return device info."""
return {
"identifiers": {(DOMAIN, self._gateway.gw_id)},
"name": self._gateway.name,
"manufacturer": "Schelte Bron",
"model": "OpenTherm Gateway",
"sw_version": self._gateway.gw_version,
}
@property
def unique_id(self):
"""Return a unique ID."""
return f"{self._gateway.gw_id}-{self._var}"
@property
def device_class(self):
"""Return the device class."""