mirror of
https://github.com/home-assistant/core.git
synced 2025-07-23 21:27:38 +00:00
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:
parent
12ae8b025f
commit
fdf0793fdd
@ -1,4 +1,5 @@
|
|||||||
"""Support for OpenTherm Gateway devices."""
|
"""Support for OpenTherm Gateway devices."""
|
||||||
|
import asyncio
|
||||||
import logging
|
import logging
|
||||||
from datetime import datetime, date
|
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:
|
class OpenThermGatewayDevice:
|
||||||
"""OpenTherm Gateway device class."""
|
"""OpenTherm Gateway device class."""
|
||||||
|
|
||||||
@ -358,18 +371,21 @@ class OpenThermGatewayDevice:
|
|||||||
self.update_signal = f"{DATA_OPENTHERM_GW}_{self.gw_id}_update"
|
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.options_update_signal = f"{DATA_OPENTHERM_GW}_{self.gw_id}_options_update"
|
||||||
self.gateway = pyotgw.pyotgw()
|
self.gateway = pyotgw.pyotgw()
|
||||||
|
self.gw_version = None
|
||||||
|
|
||||||
async def connect_and_subscribe(self):
|
async def cleanup(self, event=None):
|
||||||
"""Connect to serial device and subscribe report handler."""
|
|
||||||
await self.gateway.connect(self.hass.loop, self.device_path)
|
|
||||||
_LOGGER.debug("Connected to OpenTherm Gateway at %s", self.device_path)
|
|
||||||
|
|
||||||
async def cleanup(event):
|
|
||||||
"""Reset overrides on the gateway."""
|
"""Reset overrides on the gateway."""
|
||||||
await self.gateway.set_control_setpoint(0)
|
await self.gateway.set_control_setpoint(0)
|
||||||
await self.gateway.set_max_relative_mod("-")
|
await self.gateway.set_max_relative_mod("-")
|
||||||
|
await self.gateway.disconnect()
|
||||||
|
|
||||||
self.hass.bus.async_listen(EVENT_HOMEASSISTANT_STOP, cleanup)
|
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)
|
||||||
|
|
||||||
|
self.hass.bus.async_listen(EVENT_HOMEASSISTANT_STOP, self.cleanup)
|
||||||
|
|
||||||
async def handle_report(status):
|
async def handle_report(status):
|
||||||
"""Handle reports from the OpenTherm Gateway."""
|
"""Handle reports from the OpenTherm Gateway."""
|
||||||
|
@ -7,6 +7,7 @@ from homeassistant.core import callback
|
|||||||
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
||||||
from homeassistant.helpers.entity import async_generate_entity_id
|
from homeassistant.helpers.entity import async_generate_entity_id
|
||||||
|
|
||||||
|
from . import DOMAIN
|
||||||
from .const import BINARY_SENSOR_INFO, DATA_GATEWAYS, DATA_OPENTHERM_GW
|
from .const import BINARY_SENSOR_INFO, DATA_GATEWAYS, DATA_OPENTHERM_GW
|
||||||
|
|
||||||
|
|
||||||
@ -44,14 +45,27 @@ class OpenThermBinarySensor(BinarySensorDevice):
|
|||||||
self._state = None
|
self._state = None
|
||||||
self._device_class = device_class
|
self._device_class = device_class
|
||||||
self._friendly_name = friendly_name_format.format(gw_dev.name)
|
self._friendly_name = friendly_name_format.format(gw_dev.name)
|
||||||
|
self._unsub_updates = None
|
||||||
|
|
||||||
async def async_added_to_hass(self):
|
async def async_added_to_hass(self):
|
||||||
"""Subscribe to updates from the component."""
|
"""Subscribe to updates from the component."""
|
||||||
_LOGGER.debug("Added OpenTherm Gateway binary sensor %s", self._friendly_name)
|
_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
|
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
|
@callback
|
||||||
def receive_report(self, status):
|
def receive_report(self, status):
|
||||||
"""Handle status updates from the component."""
|
"""Handle status updates from the component."""
|
||||||
@ -63,6 +77,22 @@ class OpenThermBinarySensor(BinarySensorDevice):
|
|||||||
"""Return the friendly name."""
|
"""Return the friendly name."""
|
||||||
return self._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
|
@property
|
||||||
def is_on(self):
|
def is_on(self):
|
||||||
"""Return true if the binary sensor is on."""
|
"""Return true if the binary sensor is on."""
|
||||||
|
@ -3,7 +3,7 @@ import logging
|
|||||||
|
|
||||||
from pyotgw import vars as gw_vars
|
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 (
|
from homeassistant.components.climate.const import (
|
||||||
CURRENT_HVAC_COOL,
|
CURRENT_HVAC_COOL,
|
||||||
CURRENT_HVAC_HEAT,
|
CURRENT_HVAC_HEAT,
|
||||||
@ -25,7 +25,9 @@ from homeassistant.const import (
|
|||||||
)
|
)
|
||||||
from homeassistant.core import callback
|
from homeassistant.core import callback
|
||||||
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
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
|
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):
|
def __init__(self, gw_dev, options):
|
||||||
"""Initialize the device."""
|
"""Initialize the device."""
|
||||||
self._gateway = gw_dev
|
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.friendly_name = gw_dev.name
|
||||||
self.floor_temp = options.get(CONF_FLOOR_TEMP, DEFAULT_FLOOR_TEMP)
|
self.floor_temp = options.get(CONF_FLOOR_TEMP, DEFAULT_FLOOR_TEMP)
|
||||||
self.temp_precision = options.get(CONF_PRECISION, DEFAULT_PRECISION)
|
self.temp_precision = options.get(CONF_PRECISION, DEFAULT_PRECISION)
|
||||||
@ -68,6 +73,8 @@ class OpenThermClimate(ClimateDevice):
|
|||||||
self._away_mode_b = None
|
self._away_mode_b = None
|
||||||
self._away_state_a = False
|
self._away_state_a = False
|
||||||
self._away_state_b = False
|
self._away_state_b = False
|
||||||
|
self._unsub_options = None
|
||||||
|
self._unsub_updates = None
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def update_options(self, entry):
|
def update_options(self, entry):
|
||||||
@ -79,13 +86,19 @@ class OpenThermClimate(ClimateDevice):
|
|||||||
async def async_added_to_hass(self):
|
async def async_added_to_hass(self):
|
||||||
"""Connect to the OpenTherm Gateway device."""
|
"""Connect to the OpenTherm Gateway device."""
|
||||||
_LOGGER.debug("Added OpenTherm Gateway climate device %s", self.friendly_name)
|
_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
|
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
|
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
|
@callback
|
||||||
def receive_report(self, status):
|
def receive_report(self, status):
|
||||||
"""Receive and handle a new report from the Gateway."""
|
"""Receive and handle a new report from the Gateway."""
|
||||||
@ -139,6 +152,17 @@ class OpenThermClimate(ClimateDevice):
|
|||||||
"""Return the friendly name."""
|
"""Return the friendly name."""
|
||||||
return self.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
|
@property
|
||||||
def unique_id(self):
|
def unique_id(self):
|
||||||
"""Return a unique ID."""
|
"""Return a unique ID."""
|
||||||
|
@ -7,6 +7,7 @@ from homeassistant.core import callback
|
|||||||
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
||||||
from homeassistant.helpers.entity import Entity, async_generate_entity_id
|
from homeassistant.helpers.entity import Entity, async_generate_entity_id
|
||||||
|
|
||||||
|
from . import DOMAIN
|
||||||
from .const import DATA_GATEWAYS, DATA_OPENTHERM_GW, SENSOR_INFO
|
from .const import DATA_GATEWAYS, DATA_OPENTHERM_GW, SENSOR_INFO
|
||||||
|
|
||||||
|
|
||||||
@ -47,14 +48,25 @@ class OpenThermSensor(Entity):
|
|||||||
self._device_class = device_class
|
self._device_class = device_class
|
||||||
self._unit = unit
|
self._unit = unit
|
||||||
self._friendly_name = friendly_name_format.format(gw_dev.name)
|
self._friendly_name = friendly_name_format.format(gw_dev.name)
|
||||||
|
self._unsub_updates = None
|
||||||
|
|
||||||
async def async_added_to_hass(self):
|
async def async_added_to_hass(self):
|
||||||
"""Subscribe to updates from the component."""
|
"""Subscribe to updates from the component."""
|
||||||
_LOGGER.debug("Added OpenTherm Gateway sensor %s", self._friendly_name)
|
_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
|
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
|
@callback
|
||||||
def receive_report(self, status):
|
def receive_report(self, status):
|
||||||
"""Handle status updates from the component."""
|
"""Handle status updates from the component."""
|
||||||
@ -69,6 +81,22 @@ class OpenThermSensor(Entity):
|
|||||||
"""Return the friendly name of the sensor."""
|
"""Return the friendly name of the sensor."""
|
||||||
return self._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
|
@property
|
||||||
def device_class(self):
|
def device_class(self):
|
||||||
"""Return the device class."""
|
"""Return the device class."""
|
||||||
|
Loading…
x
Reference in New Issue
Block a user