Skip to content

Commit

Permalink
Split opentherm_gw entities between different devices (#124869)
Browse files Browse the repository at this point in the history
* * Add migration from single device to multiple devices, removing all old entities
* Create new devices for Boiler and Thermostat
* Add classes for new entities based on the new devices

* Split binary_sensor entities into devices

* Split sensor entities into different devices

* Move climate entity to thermostat device

* Fix climate entity away mode

* Fix translation placeholders

* Allow sensor values with capital letters

* * Add EntityCategory
* Update and add device_classes

* Fix translation keys

* Fix climate entity category

* Update tests

* Handle `available` property in `entity.py`

* Improve GPIO state binary_sensor translations

* Fix: Updates are already subscribed to in the base entity

* Remove entity_id generation from sensor and binary_sensor entities

* * Use _attr_name on climate class instead of through entity_description
* Add type hints

* Rewrite to derive entities for all OpenTherm devices from a single base class

* Improve type annotations

* Use OpenThermDataSource to access status dict

* Move entity_category from entity_description to _attr_entity_category

* Move entity descriptions with the same translation_key closer together

* Update tests

* Add device migration test

* * Add missing sensors and binary_sensors back
* Improve migration, do not delete old entities from registry

* Add comments for migration period

* Use single lists for entity descriptions

* Avoid changing sensor values, remove translations

* * Import only required class from pyotgw
* Update tests
  • Loading branch information
mvn23 authored Sep 1, 2024
1 parent 12336f5 commit 2f7a396
Show file tree
Hide file tree
Showing 9 changed files with 1,799 additions and 1,052 deletions.
83 changes: 74 additions & 9 deletions homeassistant/components/opentherm_gw/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from datetime import date, datetime
import logging

import pyotgw
from pyotgw import OpenThermGateway
import pyotgw.vars as gw_vars
from serial import SerialException
import voluptuous as vol
Expand Down Expand Up @@ -59,6 +59,8 @@
SERVICE_SET_MAX_MOD,
SERVICE_SET_OAT,
SERVICE_SET_SB_TEMP,
OpenThermDataSource,
OpenThermDeviceIdentifier,
)

_LOGGER = logging.getLogger(__name__)
Expand Down Expand Up @@ -113,6 +115,23 @@ async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> b
del migrate_options[CONF_PRECISION]
hass.config_entries.async_update_entry(config_entry, options=migrate_options)

# Migration can be removed in 2025.4.0
dev_reg = dr.async_get(hass)
if (
migrate_device := dev_reg.async_get_device(
{(DOMAIN, config_entry.data[CONF_ID])}
)
) is not None:
dev_reg.async_update_device(
migrate_device.id,
new_identifiers={
(
DOMAIN,
f"{config_entry.data[CONF_ID]}-{OpenThermDeviceIdentifier.GATEWAY}",
)
},
)

config_entry.add_update_listener(options_updated)

try:
Expand Down Expand Up @@ -427,10 +446,9 @@ def __init__(self, hass: HomeAssistant, config_entry: ConfigEntry) -> None:
self.name = config_entry.data[CONF_NAME]
self.climate_config = config_entry.options
self.config_entry_id = config_entry.entry_id
self.status = gw_vars.DEFAULT_STATUS
self.update_signal = f"{DATA_OPENTHERM_GW}_{self.hub_id}_update"
self.options_update_signal = f"{DATA_OPENTHERM_GW}_{self.hub_id}_options_update"
self.gateway = pyotgw.OpenThermGateway()
self.gateway = OpenThermGateway()
self.gw_version = None

async def cleanup(self, event=None) -> None:
Expand All @@ -441,34 +459,81 @@ async def cleanup(self, event=None) -> None:

async def connect_and_subscribe(self) -> None:
"""Connect to serial device and subscribe report handler."""
self.status = await self.gateway.connect(self.device_path)
if not self.status:
status = await self.gateway.connect(self.device_path)
if not status:
await self.cleanup()
raise ConnectionError
version_string = self.status[gw_vars.OTGW].get(gw_vars.OTGW_ABOUT)
version_string = status[OpenThermDataSource.GATEWAY].get(gw_vars.OTGW_ABOUT)
self.gw_version = version_string[18:] if version_string else None
_LOGGER.debug(
"Connected to OpenTherm Gateway %s at %s", self.gw_version, self.device_path
)
dev_reg = dr.async_get(self.hass)
gw_dev = dev_reg.async_get_or_create(
config_entry_id=self.config_entry_id,
identifiers={(DOMAIN, self.hub_id)},
name=self.name,
identifiers={
(DOMAIN, f"{self.hub_id}-{OpenThermDeviceIdentifier.GATEWAY}")
},
manufacturer="Schelte Bron",
model="OpenTherm Gateway",
translation_key="gateway_device",
sw_version=self.gw_version,
)
if gw_dev.sw_version != self.gw_version:
dev_reg.async_update_device(gw_dev.id, sw_version=self.gw_version)

boiler_device = dev_reg.async_get_or_create(
config_entry_id=self.config_entry_id,
identifiers={(DOMAIN, f"{self.hub_id}-{OpenThermDeviceIdentifier.BOILER}")},
translation_key="boiler_device",
)
thermostat_device = dev_reg.async_get_or_create(
config_entry_id=self.config_entry_id,
identifiers={
(DOMAIN, f"{self.hub_id}-{OpenThermDeviceIdentifier.THERMOSTAT}")
},
translation_key="thermostat_device",
)

self.hass.bus.async_listen(EVENT_HOMEASSISTANT_STOP, self.cleanup)

async def handle_report(status):
"""Handle reports from the OpenTherm Gateway."""
_LOGGER.debug("Received report: %s", status)
self.status = status
async_dispatcher_send(self.hass, self.update_signal, status)

dev_reg.async_update_device(
boiler_device.id,
manufacturer=status[OpenThermDataSource.BOILER].get(
gw_vars.DATA_SLAVE_MEMBERID
),
model_id=status[OpenThermDataSource.BOILER].get(
gw_vars.DATA_SLAVE_PRODUCT_TYPE
),
hw_version=status[OpenThermDataSource.BOILER].get(
gw_vars.DATA_SLAVE_PRODUCT_VERSION
),
sw_version=status[OpenThermDataSource.BOILER].get(
gw_vars.DATA_SLAVE_OT_VERSION
),
)

dev_reg.async_update_device(
thermostat_device.id,
manufacturer=status[OpenThermDataSource.THERMOSTAT].get(
gw_vars.DATA_MASTER_MEMBERID
),
model_id=status[OpenThermDataSource.THERMOSTAT].get(
gw_vars.DATA_MASTER_PRODUCT_TYPE
),
hw_version=status[OpenThermDataSource.THERMOSTAT].get(
gw_vars.DATA_MASTER_PRODUCT_VERSION
),
sw_version=status[OpenThermDataSource.THERMOSTAT].get(
gw_vars.DATA_MASTER_OT_VERSION
),
)

self.gateway.subscribe(handle_report)

@property
Expand Down
Loading

0 comments on commit 2f7a396

Please sign in to comment.