From 0515a90cc5815b02f2b03c3bdd2a87b66ac2ac23 Mon Sep 17 00:00:00 2001 From: mvn23 Date: Sat, 2 Nov 2019 23:39:20 +0100 Subject: [PATCH 1/4] Add opentherm_gw device support --- .../components/opentherm_gw/__init__.py | 4 +++- .../components/opentherm_gw/binary_sensor.py | 17 +++++++++++++++++ .../components/opentherm_gw/climate.py | 12 ++++++++++++ homeassistant/components/opentherm_gw/sensor.py | 17 +++++++++++++++++ 4 files changed, 49 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/opentherm_gw/__init__.py b/homeassistant/components/opentherm_gw/__init__.py index 643f80ae8f9db5..314f16b9046f28 100644 --- a/homeassistant/components/opentherm_gw/__init__.py +++ b/homeassistant/components/opentherm_gw/__init__.py @@ -358,11 +358,13 @@ def __init__(self, hass, config_entry): 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 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.""" diff --git a/homeassistant/components/opentherm_gw/binary_sensor.py b/homeassistant/components/opentherm_gw/binary_sensor.py index 36867feda61e52..c8b13032b49af9 100644 --- a/homeassistant/components/opentherm_gw/binary_sensor.py +++ b/homeassistant/components/opentherm_gw/binary_sensor.py @@ -7,6 +7,7 @@ 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 @@ -63,6 +64,22 @@ def name(self): """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}-binary-sensor-{self._var}" + @property def is_on(self): """Return true if the binary sensor is on.""" diff --git a/homeassistant/components/opentherm_gw/climate.py b/homeassistant/components/opentherm_gw/climate.py index 44f143d64da24a..cd0135dc862454 100644 --- a/homeassistant/components/opentherm_gw/climate.py +++ b/homeassistant/components/opentherm_gw/climate.py @@ -26,6 +26,7 @@ from homeassistant.core import callback from homeassistant.helpers.dispatcher import async_dispatcher_connect +from . import DOMAIN from .const import CONF_FLOOR_TEMP, CONF_PRECISION, DATA_GATEWAYS, DATA_OPENTHERM_GW @@ -136,6 +137,17 @@ def name(self): """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.""" diff --git a/homeassistant/components/opentherm_gw/sensor.py b/homeassistant/components/opentherm_gw/sensor.py index c77a73cd18032b..becfd6b87e694f 100644 --- a/homeassistant/components/opentherm_gw/sensor.py +++ b/homeassistant/components/opentherm_gw/sensor.py @@ -7,6 +7,7 @@ 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 @@ -69,6 +70,22 @@ def name(self): """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}-sensor-{self._var}" + @property def device_class(self): """Return the device class.""" From d85ea9da1fafac8d6f1e38851881db0e27183ac4 Mon Sep 17 00:00:00 2001 From: mvn23 Date: Mon, 4 Nov 2019 15:55:21 +0100 Subject: [PATCH 2/4] Add support for enabling/disabling of opentherm_gw entities --- .../components/opentherm_gw/__init__.py | 26 ++++++++++++++----- .../components/opentherm_gw/binary_sensor.py | 10 ++++++- .../components/opentherm_gw/climate.py | 12 +++++++-- .../components/opentherm_gw/sensor.py | 8 +++++- 4 files changed, 46 insertions(+), 10 deletions(-) diff --git a/homeassistant/components/opentherm_gw/__init__.py b/homeassistant/components/opentherm_gw/__init__.py index 314f16b9046f28..3a1255e3697866 100644 --- a/homeassistant/components/opentherm_gw/__init__.py +++ b/homeassistant/components/opentherm_gw/__init__.py @@ -1,4 +1,5 @@ """Support for OpenTherm Gateway devices.""" +import asyncio import logging from datetime import datetime, date @@ -344,6 +345,18 @@ async def set_setback_temp(call): ) +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.""" @@ -360,18 +373,19 @@ def __init__(self, hass, config_entry): 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.""" 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.""" diff --git a/homeassistant/components/opentherm_gw/binary_sensor.py b/homeassistant/components/opentherm_gw/binary_sensor.py index c8b13032b49af9..f2ea83fa406c51 100644 --- a/homeassistant/components/opentherm_gw/binary_sensor.py +++ b/homeassistant/components/opentherm_gw/binary_sensor.py @@ -45,14 +45,22 @@ def __init__(self, gw_dev, var, device_class, friendly_name_format): 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() + @callback def receive_report(self, status): """Handle status updates from the component.""" diff --git a/homeassistant/components/opentherm_gw/climate.py b/homeassistant/components/opentherm_gw/climate.py index cd0135dc862454..bcffc5288636a4 100644 --- a/homeassistant/components/opentherm_gw/climate.py +++ b/homeassistant/components/opentherm_gw/climate.py @@ -66,6 +66,8 @@ def __init__(self, gw_dev, options): 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): @@ -77,13 +79,19 @@ def update_options(self, entry): 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.""" diff --git a/homeassistant/components/opentherm_gw/sensor.py b/homeassistant/components/opentherm_gw/sensor.py index becfd6b87e694f..95354fe4568bf2 100644 --- a/homeassistant/components/opentherm_gw/sensor.py +++ b/homeassistant/components/opentherm_gw/sensor.py @@ -48,14 +48,20 @@ def __init__(self, gw_dev, var, device_class, unit, friendly_name_format): 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() + @callback def receive_report(self, status): """Handle status updates from the component.""" From 99374e98bd1928c0c64145a686d96d7cba091b81 Mon Sep 17 00:00:00 2001 From: mvn23 Date: Sun, 10 Nov 2019 16:23:16 +0100 Subject: [PATCH 3/4] Disable sensors by default, base climate entity_id on gw_id instead of friendly_name to guarantee uniqueness --- homeassistant/components/opentherm_gw/binary_sensor.py | 5 +++++ homeassistant/components/opentherm_gw/climate.py | 6 +++++- homeassistant/components/opentherm_gw/sensor.py | 5 +++++ 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/homeassistant/components/opentherm_gw/binary_sensor.py b/homeassistant/components/opentherm_gw/binary_sensor.py index f2ea83fa406c51..00f1cc46b900ff 100644 --- a/homeassistant/components/opentherm_gw/binary_sensor.py +++ b/homeassistant/components/opentherm_gw/binary_sensor.py @@ -61,6 +61,11 @@ async def async_will_remove_from_hass(self): ) 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.""" diff --git a/homeassistant/components/opentherm_gw/climate.py b/homeassistant/components/opentherm_gw/climate.py index bcffc5288636a4..c8bb6e86036b31 100644 --- a/homeassistant/components/opentherm_gw/climate.py +++ b/homeassistant/components/opentherm_gw/climate.py @@ -3,7 +3,7 @@ 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,6 +25,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 CONF_FLOOR_TEMP, CONF_PRECISION, DATA_GATEWAYS, DATA_OPENTHERM_GW @@ -54,6 +55,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[CONF_FLOOR_TEMP] self.temp_precision = options.get(CONF_PRECISION) diff --git a/homeassistant/components/opentherm_gw/sensor.py b/homeassistant/components/opentherm_gw/sensor.py index 95354fe4568bf2..b2d621e10b499e 100644 --- a/homeassistant/components/opentherm_gw/sensor.py +++ b/homeassistant/components/opentherm_gw/sensor.py @@ -62,6 +62,11 @@ async def async_will_remove_from_hass(self): _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.""" From f79753221ce0999284a0b6429013f884403a54e8 Mon Sep 17 00:00:00 2001 From: mvn23 Date: Thu, 14 Nov 2019 11:39:43 +0100 Subject: [PATCH 4/4] Remove platform name from unique_id --- homeassistant/components/opentherm_gw/binary_sensor.py | 2 +- homeassistant/components/opentherm_gw/sensor.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/homeassistant/components/opentherm_gw/binary_sensor.py b/homeassistant/components/opentherm_gw/binary_sensor.py index 00f1cc46b900ff..39fd78f5fe8f23 100644 --- a/homeassistant/components/opentherm_gw/binary_sensor.py +++ b/homeassistant/components/opentherm_gw/binary_sensor.py @@ -91,7 +91,7 @@ def device_info(self): @property def unique_id(self): """Return a unique ID.""" - return f"{self._gateway.gw_id}-binary-sensor-{self._var}" + return f"{self._gateway.gw_id}-{self._var}" @property def is_on(self): diff --git a/homeassistant/components/opentherm_gw/sensor.py b/homeassistant/components/opentherm_gw/sensor.py index b2d621e10b499e..cd9ce9fb095a35 100644 --- a/homeassistant/components/opentherm_gw/sensor.py +++ b/homeassistant/components/opentherm_gw/sensor.py @@ -95,7 +95,7 @@ def device_info(self): @property def unique_id(self): """Return a unique ID.""" - return f"{self._gateway.gw_id}-sensor-{self._var}" + return f"{self._gateway.gw_id}-{self._var}" @property def device_class(self):