mirror of
https://github.com/home-assistant/core.git
synced 2025-07-23 13:17:32 +00:00
Split opentherm_gw entities between different devices (#124869)
* * 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
This commit is contained in:
parent
12336f5c15
commit
2f7a396778
@ -4,7 +4,7 @@ import asyncio
|
||||
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
|
||||
@ -59,6 +59,8 @@ from .const import (
|
||||
SERVICE_SET_MAX_MOD,
|
||||
SERVICE_SET_OAT,
|
||||
SERVICE_SET_SB_TEMP,
|
||||
OpenThermDataSource,
|
||||
OpenThermDeviceIdentifier,
|
||||
)
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
@ -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:
|
||||
@ -427,10 +446,9 @@ class OpenThermGatewayHub:
|
||||
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:
|
||||
@ -441,11 +459,11 @@ class OpenThermGatewayHub:
|
||||
|
||||
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
|
||||
@ -453,22 +471,69 @@ class OpenThermGatewayHub:
|
||||
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
|
||||
|
@ -5,281 +5,387 @@ from dataclasses import dataclass
|
||||
from pyotgw import vars as gw_vars
|
||||
|
||||
from homeassistant.components.binary_sensor import (
|
||||
ENTITY_ID_FORMAT,
|
||||
BinarySensorDeviceClass,
|
||||
BinarySensorEntity,
|
||||
BinarySensorEntityDescription,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import CONF_ID
|
||||
from homeassistant.const import CONF_ID, EntityCategory
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.helpers.entity import async_generate_entity_id
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
|
||||
from . import OpenThermGatewayHub
|
||||
from .const import DATA_GATEWAYS, DATA_OPENTHERM_GW
|
||||
from .const import (
|
||||
BOILER_DEVICE_DESCRIPTION,
|
||||
DATA_GATEWAYS,
|
||||
DATA_OPENTHERM_GW,
|
||||
GATEWAY_DEVICE_DESCRIPTION,
|
||||
THERMOSTAT_DEVICE_DESCRIPTION,
|
||||
OpenThermDataSource,
|
||||
)
|
||||
from .entity import OpenThermEntity, OpenThermEntityDescription
|
||||
|
||||
|
||||
@dataclass(frozen=True, kw_only=True)
|
||||
class OpenThermBinarySensorEntityDescription(
|
||||
BinarySensorEntityDescription, OpenThermEntityDescription
|
||||
OpenThermEntityDescription, BinarySensorEntityDescription
|
||||
):
|
||||
"""Describes opentherm_gw binary sensor entity."""
|
||||
|
||||
|
||||
BINARY_SENSOR_INFO: tuple[
|
||||
tuple[list[str], OpenThermBinarySensorEntityDescription], ...
|
||||
] = (
|
||||
(
|
||||
[gw_vars.BOILER, gw_vars.THERMOSTAT],
|
||||
OpenThermBinarySensorEntityDescription(
|
||||
key=gw_vars.DATA_MASTER_CH_ENABLED,
|
||||
friendly_name_format="Thermostat Central Heating {}",
|
||||
),
|
||||
BINARY_SENSOR_DESCRIPTIONS: tuple[OpenThermBinarySensorEntityDescription, ...] = (
|
||||
OpenThermBinarySensorEntityDescription(
|
||||
key=gw_vars.DATA_SLAVE_FAULT_IND,
|
||||
translation_key="fault_indication",
|
||||
device_class=BinarySensorDeviceClass.PROBLEM,
|
||||
device_description=BOILER_DEVICE_DESCRIPTION,
|
||||
),
|
||||
(
|
||||
[gw_vars.BOILER, gw_vars.THERMOSTAT],
|
||||
OpenThermBinarySensorEntityDescription(
|
||||
key=gw_vars.DATA_MASTER_DHW_ENABLED,
|
||||
friendly_name_format="Thermostat Hot Water {}",
|
||||
),
|
||||
OpenThermBinarySensorEntityDescription(
|
||||
key=gw_vars.DATA_SLAVE_CH_ACTIVE,
|
||||
translation_key="central_heating_n",
|
||||
translation_placeholders={"circuit_number": "1"},
|
||||
device_class=BinarySensorDeviceClass.RUNNING,
|
||||
device_description=BOILER_DEVICE_DESCRIPTION,
|
||||
),
|
||||
(
|
||||
[gw_vars.BOILER, gw_vars.THERMOSTAT],
|
||||
OpenThermBinarySensorEntityDescription(
|
||||
key=gw_vars.DATA_MASTER_COOLING_ENABLED,
|
||||
friendly_name_format="Thermostat Cooling {}",
|
||||
),
|
||||
OpenThermBinarySensorEntityDescription(
|
||||
key=gw_vars.DATA_SLAVE_CH2_ACTIVE,
|
||||
translation_key="central_heating_n",
|
||||
translation_placeholders={"circuit_number": "2"},
|
||||
device_class=BinarySensorDeviceClass.RUNNING,
|
||||
device_description=BOILER_DEVICE_DESCRIPTION,
|
||||
),
|
||||
(
|
||||
[gw_vars.BOILER, gw_vars.THERMOSTAT],
|
||||
OpenThermBinarySensorEntityDescription(
|
||||
key=gw_vars.DATA_MASTER_OTC_ENABLED,
|
||||
friendly_name_format="Thermostat Outside Temperature Correction {}",
|
||||
),
|
||||
OpenThermBinarySensorEntityDescription(
|
||||
key=gw_vars.DATA_SLAVE_DHW_ACTIVE,
|
||||
translation_key="hot_water",
|
||||
device_class=BinarySensorDeviceClass.RUNNING,
|
||||
device_description=BOILER_DEVICE_DESCRIPTION,
|
||||
),
|
||||
(
|
||||
[gw_vars.BOILER, gw_vars.THERMOSTAT],
|
||||
OpenThermBinarySensorEntityDescription(
|
||||
key=gw_vars.DATA_MASTER_CH2_ENABLED,
|
||||
friendly_name_format="Thermostat Central Heating 2 {}",
|
||||
),
|
||||
OpenThermBinarySensorEntityDescription(
|
||||
key=gw_vars.DATA_SLAVE_FLAME_ON,
|
||||
translation_key="flame",
|
||||
device_class=BinarySensorDeviceClass.HEAT,
|
||||
device_description=BOILER_DEVICE_DESCRIPTION,
|
||||
),
|
||||
(
|
||||
[gw_vars.BOILER, gw_vars.THERMOSTAT],
|
||||
OpenThermBinarySensorEntityDescription(
|
||||
key=gw_vars.DATA_SLAVE_FAULT_IND,
|
||||
friendly_name_format="Boiler Fault {}",
|
||||
device_class=BinarySensorDeviceClass.PROBLEM,
|
||||
),
|
||||
OpenThermBinarySensorEntityDescription(
|
||||
key=gw_vars.DATA_SLAVE_COOLING_ACTIVE,
|
||||
translation_key="cooling",
|
||||
device_class=BinarySensorDeviceClass.RUNNING,
|
||||
device_description=BOILER_DEVICE_DESCRIPTION,
|
||||
),
|
||||
(
|
||||
[gw_vars.BOILER, gw_vars.THERMOSTAT],
|
||||
OpenThermBinarySensorEntityDescription(
|
||||
key=gw_vars.DATA_SLAVE_CH_ACTIVE,
|
||||
friendly_name_format="Boiler Central Heating {}",
|
||||
device_class=BinarySensorDeviceClass.HEAT,
|
||||
),
|
||||
OpenThermBinarySensorEntityDescription(
|
||||
key=gw_vars.DATA_SLAVE_DIAG_IND,
|
||||
translation_key="diagnostic_indication",
|
||||
device_class=BinarySensorDeviceClass.PROBLEM,
|
||||
device_description=BOILER_DEVICE_DESCRIPTION,
|
||||
),
|
||||
(
|
||||
[gw_vars.BOILER, gw_vars.THERMOSTAT],
|
||||
OpenThermBinarySensorEntityDescription(
|
||||
key=gw_vars.DATA_SLAVE_DHW_ACTIVE,
|
||||
friendly_name_format="Boiler Hot Water {}",
|
||||
device_class=BinarySensorDeviceClass.HEAT,
|
||||
),
|
||||
OpenThermBinarySensorEntityDescription(
|
||||
key=gw_vars.DATA_SLAVE_DHW_PRESENT,
|
||||
translation_key="supports_hot_water",
|
||||
device_description=BOILER_DEVICE_DESCRIPTION,
|
||||
),
|
||||
(
|
||||
[gw_vars.BOILER, gw_vars.THERMOSTAT],
|
||||
OpenThermBinarySensorEntityDescription(
|
||||
key=gw_vars.DATA_SLAVE_FLAME_ON,
|
||||
friendly_name_format="Boiler Flame {}",
|
||||
device_class=BinarySensorDeviceClass.HEAT,
|
||||
),
|
||||
OpenThermBinarySensorEntityDescription(
|
||||
key=gw_vars.DATA_SLAVE_CONTROL_TYPE,
|
||||
translation_key="control_type",
|
||||
device_description=BOILER_DEVICE_DESCRIPTION,
|
||||
),
|
||||
(
|
||||
[gw_vars.BOILER, gw_vars.THERMOSTAT],
|
||||
OpenThermBinarySensorEntityDescription(
|
||||
key=gw_vars.DATA_SLAVE_COOLING_ACTIVE,
|
||||
friendly_name_format="Boiler Cooling {}",
|
||||
device_class=BinarySensorDeviceClass.COLD,
|
||||
),
|
||||
OpenThermBinarySensorEntityDescription(
|
||||
key=gw_vars.DATA_SLAVE_COOLING_SUPPORTED,
|
||||
translation_key="supports_cooling",
|
||||
device_description=BOILER_DEVICE_DESCRIPTION,
|
||||
),
|
||||
(
|
||||
[gw_vars.BOILER, gw_vars.THERMOSTAT],
|
||||
OpenThermBinarySensorEntityDescription(
|
||||
key=gw_vars.DATA_SLAVE_CH2_ACTIVE,
|
||||
friendly_name_format="Boiler Central Heating 2 {}",
|
||||
device_class=BinarySensorDeviceClass.HEAT,
|
||||
),
|
||||
OpenThermBinarySensorEntityDescription(
|
||||
key=gw_vars.DATA_SLAVE_DHW_CONFIG,
|
||||
translation_key="hot_water_config",
|
||||
device_description=BOILER_DEVICE_DESCRIPTION,
|
||||
),
|
||||
(
|
||||
[gw_vars.BOILER, gw_vars.THERMOSTAT],
|
||||
OpenThermBinarySensorEntityDescription(
|
||||
key=gw_vars.DATA_SLAVE_DIAG_IND,
|
||||
friendly_name_format="Boiler Diagnostics {}",
|
||||
device_class=BinarySensorDeviceClass.PROBLEM,
|
||||
),
|
||||
OpenThermBinarySensorEntityDescription(
|
||||
key=gw_vars.DATA_SLAVE_MASTER_LOW_OFF_PUMP,
|
||||
translation_key="supports_pump_control",
|
||||
device_description=BOILER_DEVICE_DESCRIPTION,
|
||||
),
|
||||
(
|
||||
[gw_vars.BOILER, gw_vars.THERMOSTAT],
|
||||
OpenThermBinarySensorEntityDescription(
|
||||
key=gw_vars.DATA_SLAVE_DHW_PRESENT,
|
||||
friendly_name_format="Boiler Hot Water Present {}",
|
||||
),
|
||||
OpenThermBinarySensorEntityDescription(
|
||||
key=gw_vars.DATA_SLAVE_CH2_PRESENT,
|
||||
translation_key="supports_ch_2",
|
||||
device_description=BOILER_DEVICE_DESCRIPTION,
|
||||
),
|
||||
(
|
||||
[gw_vars.BOILER, gw_vars.THERMOSTAT],
|
||||
OpenThermBinarySensorEntityDescription(
|
||||
key=gw_vars.DATA_SLAVE_CONTROL_TYPE,
|
||||
friendly_name_format="Boiler Control Type {}",
|
||||
),
|
||||
OpenThermBinarySensorEntityDescription(
|
||||
key=gw_vars.DATA_SLAVE_SERVICE_REQ,
|
||||
translation_key="service_required",
|
||||
device_class=BinarySensorDeviceClass.PROBLEM,
|
||||
device_description=BOILER_DEVICE_DESCRIPTION,
|
||||
),
|
||||
(
|
||||
[gw_vars.BOILER, gw_vars.THERMOSTAT],
|
||||
OpenThermBinarySensorEntityDescription(
|
||||
key=gw_vars.DATA_SLAVE_COOLING_SUPPORTED,
|
||||
friendly_name_format="Boiler Cooling Support {}",
|
||||
),
|
||||
OpenThermBinarySensorEntityDescription(
|
||||
key=gw_vars.DATA_SLAVE_REMOTE_RESET,
|
||||
translation_key="supports_remote_reset",
|
||||
device_description=BOILER_DEVICE_DESCRIPTION,
|
||||
),
|
||||
(
|
||||
[gw_vars.BOILER, gw_vars.THERMOSTAT],
|
||||
OpenThermBinarySensorEntityDescription(
|
||||
key=gw_vars.DATA_SLAVE_DHW_CONFIG,
|
||||
friendly_name_format="Boiler Hot Water Configuration {}",
|
||||
),
|
||||
OpenThermBinarySensorEntityDescription(
|
||||
key=gw_vars.DATA_SLAVE_LOW_WATER_PRESS,
|
||||
translation_key="low_water_pressure",
|
||||
device_class=BinarySensorDeviceClass.PROBLEM,
|
||||
device_description=BOILER_DEVICE_DESCRIPTION,
|
||||
),
|
||||
(
|
||||
[gw_vars.BOILER, gw_vars.THERMOSTAT],
|
||||
OpenThermBinarySensorEntityDescription(
|
||||
key=gw_vars.DATA_SLAVE_MASTER_LOW_OFF_PUMP,
|
||||
friendly_name_format="Boiler Pump Commands Support {}",
|
||||
),
|
||||
OpenThermBinarySensorEntityDescription(
|
||||
key=gw_vars.DATA_SLAVE_GAS_FAULT,
|
||||
translation_key="gas_fault",
|
||||
device_class=BinarySensorDeviceClass.PROBLEM,
|
||||
device_description=BOILER_DEVICE_DESCRIPTION,
|
||||
),
|
||||
(
|
||||
[gw_vars.BOILER, gw_vars.THERMOSTAT],
|
||||
OpenThermBinarySensorEntityDescription(
|
||||
key=gw_vars.DATA_SLAVE_CH2_PRESENT,
|
||||
friendly_name_format="Boiler Central Heating 2 Present {}",
|
||||
),
|
||||
OpenThermBinarySensorEntityDescription(
|
||||
key=gw_vars.DATA_SLAVE_AIR_PRESS_FAULT,
|
||||
translation_key="air_pressure_fault",
|
||||
device_class=BinarySensorDeviceClass.PROBLEM,
|
||||
device_description=BOILER_DEVICE_DESCRIPTION,
|
||||
),
|
||||
(
|
||||
[gw_vars.BOILER, gw_vars.THERMOSTAT],
|
||||
OpenThermBinarySensorEntityDescription(
|
||||
key=gw_vars.DATA_SLAVE_SERVICE_REQ,
|
||||
friendly_name_format="Boiler Service Required {}",
|
||||
device_class=BinarySensorDeviceClass.PROBLEM,
|
||||
),
|
||||
OpenThermBinarySensorEntityDescription(
|
||||
key=gw_vars.DATA_SLAVE_WATER_OVERTEMP,
|
||||
translation_key="water_overtemperature",
|
||||
device_class=BinarySensorDeviceClass.PROBLEM,
|
||||
device_description=BOILER_DEVICE_DESCRIPTION,
|
||||
),
|
||||
(
|
||||
[gw_vars.BOILER, gw_vars.THERMOSTAT],
|
||||
OpenThermBinarySensorEntityDescription(
|
||||
key=gw_vars.DATA_SLAVE_REMOTE_RESET,
|
||||
friendly_name_format="Boiler Remote Reset Support {}",
|
||||
),
|
||||
OpenThermBinarySensorEntityDescription(
|
||||
key=gw_vars.DATA_REMOTE_TRANSFER_MAX_CH,
|
||||
translation_key="supports_central_heating_setpoint_transfer",
|
||||
device_description=BOILER_DEVICE_DESCRIPTION,
|
||||
),
|
||||
(
|
||||
[gw_vars.BOILER, gw_vars.THERMOSTAT],
|
||||
OpenThermBinarySensorEntityDescription(
|
||||
key=gw_vars.DATA_SLAVE_LOW_WATER_PRESS,
|
||||
friendly_name_format="Boiler Low Water Pressure {}",
|
||||
device_class=BinarySensorDeviceClass.PROBLEM,
|
||||
),
|
||||
OpenThermBinarySensorEntityDescription(
|
||||
key=gw_vars.DATA_REMOTE_RW_MAX_CH,
|
||||
translation_key="supports_central_heating_setpoint_writing",
|
||||
device_description=BOILER_DEVICE_DESCRIPTION,
|
||||
),
|
||||
(
|
||||
[gw_vars.BOILER, gw_vars.THERMOSTAT],
|
||||
OpenThermBinarySensorEntityDescription(
|
||||
key=gw_vars.DATA_SLAVE_GAS_FAULT,
|
||||
friendly_name_format="Boiler Gas Fault {}",
|
||||
device_class=BinarySensorDeviceClass.PROBLEM,
|
||||
),
|
||||
OpenThermBinarySensorEntityDescription(
|
||||
key=gw_vars.DATA_REMOTE_TRANSFER_DHW,
|
||||
translation_key="supports_hot_water_setpoint_transfer",
|
||||
device_description=BOILER_DEVICE_DESCRIPTION,
|
||||
),
|
||||
(
|
||||
[gw_vars.BOILER, gw_vars.THERMOSTAT],
|
||||
OpenThermBinarySensorEntityDescription(
|
||||
key=gw_vars.DATA_SLAVE_AIR_PRESS_FAULT,
|
||||
friendly_name_format="Boiler Air Pressure Fault {}",
|
||||
device_class=BinarySensorDeviceClass.PROBLEM,
|
||||
),
|
||||
OpenThermBinarySensorEntityDescription(
|
||||
key=gw_vars.DATA_REMOTE_RW_DHW,
|
||||
translation_key="supports_hot_water_setpoint_writing",
|
||||
device_description=BOILER_DEVICE_DESCRIPTION,
|
||||
),
|
||||
(
|
||||
[gw_vars.BOILER, gw_vars.THERMOSTAT],
|
||||
OpenThermBinarySensorEntityDescription(
|
||||
key=gw_vars.DATA_SLAVE_WATER_OVERTEMP,
|
||||
friendly_name_format="Boiler Water Overtemperature {}",
|
||||
device_class=BinarySensorDeviceClass.PROBLEM,
|
||||
),
|
||||
OpenThermBinarySensorEntityDescription(
|
||||
key=gw_vars.OTGW_GPIO_A_STATE,
|
||||
translation_key="gpio_state_n",
|
||||
translation_placeholders={"gpio_id": "A"},
|
||||
device_description=GATEWAY_DEVICE_DESCRIPTION,
|
||||
),
|
||||
(
|
||||
[gw_vars.BOILER, gw_vars.THERMOSTAT],
|
||||
OpenThermBinarySensorEntityDescription(
|
||||
key=gw_vars.DATA_REMOTE_TRANSFER_DHW,
|
||||
friendly_name_format="Remote Hot Water Setpoint Transfer Support {}",
|
||||
),
|
||||
OpenThermBinarySensorEntityDescription(
|
||||
key=gw_vars.OTGW_GPIO_B_STATE,
|
||||
translation_key="gpio_state_n",
|
||||
translation_placeholders={"gpio_id": "B"},
|
||||
device_description=GATEWAY_DEVICE_DESCRIPTION,
|
||||
),
|
||||
(
|
||||
[gw_vars.BOILER, gw_vars.THERMOSTAT],
|
||||
OpenThermBinarySensorEntityDescription(
|
||||
key=gw_vars.DATA_REMOTE_TRANSFER_MAX_CH,
|
||||
friendly_name_format="Remote Maximum Central Heating Setpoint Write Support {}",
|
||||
),
|
||||
OpenThermBinarySensorEntityDescription(
|
||||
key=gw_vars.OTGW_IGNORE_TRANSITIONS,
|
||||
translation_key="ignore_transitions",
|
||||
device_description=GATEWAY_DEVICE_DESCRIPTION,
|
||||
),
|
||||
(
|
||||
[gw_vars.BOILER, gw_vars.THERMOSTAT],
|
||||
OpenThermBinarySensorEntityDescription(
|
||||
key=gw_vars.DATA_REMOTE_RW_DHW,
|
||||
friendly_name_format="Remote Hot Water Setpoint Write Support {}",
|
||||
),
|
||||
OpenThermBinarySensorEntityDescription(
|
||||
key=gw_vars.OTGW_OVRD_HB,
|
||||
translation_key="override_high_byte",
|
||||
device_description=GATEWAY_DEVICE_DESCRIPTION,
|
||||
),
|
||||
(
|
||||
[gw_vars.BOILER, gw_vars.THERMOSTAT],
|
||||
OpenThermBinarySensorEntityDescription(
|
||||
key=gw_vars.DATA_REMOTE_RW_MAX_CH,
|
||||
friendly_name_format="Remote Central Heating Setpoint Write Support {}",
|
||||
),
|
||||
OpenThermBinarySensorEntityDescription(
|
||||
key=gw_vars.DATA_MASTER_CH_ENABLED,
|
||||
translation_key="central_heating_n",
|
||||
translation_placeholders={"circuit_number": "1"},
|
||||
device_description=THERMOSTAT_DEVICE_DESCRIPTION,
|
||||
),
|
||||
(
|
||||
[gw_vars.BOILER, gw_vars.THERMOSTAT],
|
||||
OpenThermBinarySensorEntityDescription(
|
||||
key=gw_vars.DATA_ROVRD_MAN_PRIO,
|
||||
friendly_name_format="Remote Override Manual Change Priority {}",
|
||||
),
|
||||
OpenThermBinarySensorEntityDescription(
|
||||
key=gw_vars.DATA_MASTER_CH2_ENABLED,
|
||||
translation_key="central_heating_n",
|
||||
translation_placeholders={"circuit_number": "2"},
|
||||
device_description=THERMOSTAT_DEVICE_DESCRIPTION,
|
||||
),
|
||||
(
|
||||
[gw_vars.BOILER, gw_vars.THERMOSTAT],
|
||||
OpenThermBinarySensorEntityDescription(
|
||||
key=gw_vars.DATA_ROVRD_AUTO_PRIO,
|
||||
friendly_name_format="Remote Override Program Change Priority {}",
|
||||
),
|
||||
OpenThermBinarySensorEntityDescription(
|
||||
key=gw_vars.DATA_MASTER_DHW_ENABLED,
|
||||
translation_key="hot_water",
|
||||
device_description=THERMOSTAT_DEVICE_DESCRIPTION,
|
||||
),
|
||||
(
|
||||
[gw_vars.OTGW],
|
||||
OpenThermBinarySensorEntityDescription(
|
||||
key=gw_vars.OTGW_GPIO_A_STATE,
|
||||
friendly_name_format="Gateway GPIO A {}",
|
||||
),
|
||||
OpenThermBinarySensorEntityDescription(
|
||||
key=gw_vars.DATA_MASTER_COOLING_ENABLED,
|
||||
translation_key="cooling",
|
||||
device_description=THERMOSTAT_DEVICE_DESCRIPTION,
|
||||
),
|
||||
(
|
||||
[gw_vars.OTGW],
|
||||
OpenThermBinarySensorEntityDescription(
|
||||
key=gw_vars.OTGW_GPIO_B_STATE,
|
||||
friendly_name_format="Gateway GPIO B {}",
|
||||
),
|
||||
OpenThermBinarySensorEntityDescription(
|
||||
key=gw_vars.DATA_MASTER_OTC_ENABLED,
|
||||
translation_key="outside_temp_correction",
|
||||
device_description=THERMOSTAT_DEVICE_DESCRIPTION,
|
||||
),
|
||||
(
|
||||
[gw_vars.OTGW],
|
||||
OpenThermBinarySensorEntityDescription(
|
||||
key=gw_vars.OTGW_IGNORE_TRANSITIONS,
|
||||
friendly_name_format="Gateway Ignore Transitions {}",
|
||||
),
|
||||
OpenThermBinarySensorEntityDescription(
|
||||
key=gw_vars.DATA_ROVRD_MAN_PRIO,
|
||||
translation_key="override_manual_change_prio",
|
||||
device_description=THERMOSTAT_DEVICE_DESCRIPTION,
|
||||
),
|
||||
(
|
||||
[gw_vars.OTGW],
|
||||
OpenThermBinarySensorEntityDescription(
|
||||
key=gw_vars.OTGW_OVRD_HB,
|
||||
friendly_name_format="Gateway Override High Byte {}",
|
||||
),
|
||||
OpenThermBinarySensorEntityDescription(
|
||||
key=gw_vars.DATA_ROVRD_AUTO_PRIO,
|
||||
translation_key="override_program_change_prio",
|
||||
device_description=THERMOSTAT_DEVICE_DESCRIPTION,
|
||||
),
|
||||
OpenThermBinarySensorEntityDescription(
|
||||
key=gw_vars.DATA_SLAVE_FAULT_IND,
|
||||
translation_key="fault_indication",
|
||||
device_class=BinarySensorDeviceClass.PROBLEM,
|
||||
device_description=THERMOSTAT_DEVICE_DESCRIPTION,
|
||||
),
|
||||
OpenThermBinarySensorEntityDescription(
|
||||
key=gw_vars.DATA_SLAVE_CH_ACTIVE,
|
||||
translation_key="central_heating_n",
|
||||
translation_placeholders={"circuit_number": "1"},
|
||||
device_class=BinarySensorDeviceClass.RUNNING,
|
||||
device_description=THERMOSTAT_DEVICE_DESCRIPTION,
|
||||
),
|
||||
OpenThermBinarySensorEntityDescription(
|
||||
key=gw_vars.DATA_SLAVE_CH2_ACTIVE,
|
||||
translation_key="central_heating_n",
|
||||
translation_placeholders={"circuit_number": "2"},
|
||||
device_class=BinarySensorDeviceClass.RUNNING,
|
||||
device_description=THERMOSTAT_DEVICE_DESCRIPTION,
|
||||
),
|
||||
OpenThermBinarySensorEntityDescription(
|
||||
key=gw_vars.DATA_SLAVE_DHW_ACTIVE,
|
||||
translation_key="hot_water",
|
||||
device_class=BinarySensorDeviceClass.RUNNING,
|
||||
device_description=THERMOSTAT_DEVICE_DESCRIPTION,
|
||||
),
|
||||
OpenThermBinarySensorEntityDescription(
|
||||
key=gw_vars.DATA_SLAVE_FLAME_ON,
|
||||
translation_key="flame",
|
||||
device_class=BinarySensorDeviceClass.HEAT,
|
||||
device_description=THERMOSTAT_DEVICE_DESCRIPTION,
|
||||
),
|
||||
OpenThermBinarySensorEntityDescription(
|
||||
key=gw_vars.DATA_SLAVE_COOLING_ACTIVE,
|
||||
translation_key="cooling",
|
||||
device_class=BinarySensorDeviceClass.RUNNING,
|
||||
device_description=THERMOSTAT_DEVICE_DESCRIPTION,
|
||||
),
|
||||
OpenThermBinarySensorEntityDescription(
|
||||
key=gw_vars.DATA_SLAVE_DIAG_IND,
|
||||
translation_key="diagnostic_indication",
|
||||
device_class=BinarySensorDeviceClass.PROBLEM,
|
||||
device_description=THERMOSTAT_DEVICE_DESCRIPTION,
|
||||
),
|
||||
OpenThermBinarySensorEntityDescription(
|
||||
key=gw_vars.DATA_SLAVE_DHW_PRESENT,
|
||||
translation_key="supports_hot_water",
|
||||
device_description=THERMOSTAT_DEVICE_DESCRIPTION,
|
||||
),
|
||||
OpenThermBinarySensorEntityDescription(
|
||||
key=gw_vars.DATA_SLAVE_CONTROL_TYPE,
|
||||
translation_key="control_type",
|
||||
device_description=THERMOSTAT_DEVICE_DESCRIPTION,
|
||||
),
|
||||
OpenThermBinarySensorEntityDescription(
|
||||
key=gw_vars.DATA_SLAVE_COOLING_SUPPORTED,
|
||||
translation_key="supports_cooling",
|
||||
device_description=THERMOSTAT_DEVICE_DESCRIPTION,
|
||||
),
|
||||
OpenThermBinarySensorEntityDescription(
|
||||
key=gw_vars.DATA_SLAVE_DHW_CONFIG,
|
||||
translation_key="hot_water_config",
|
||||
device_description=THERMOSTAT_DEVICE_DESCRIPTION,
|
||||
),
|
||||
OpenThermBinarySensorEntityDescription(
|
||||
key=gw_vars.DATA_SLAVE_MASTER_LOW_OFF_PUMP,
|
||||
translation_key="supports_pump_control",
|
||||
device_description=THERMOSTAT_DEVICE_DESCRIPTION,
|
||||
),
|
||||
OpenThermBinarySensorEntityDescription(
|
||||
key=gw_vars.DATA_SLAVE_CH2_PRESENT,
|
||||
translation_key="supports_ch_2",
|
||||
device_description=THERMOSTAT_DEVICE_DESCRIPTION,
|
||||
),
|
||||
OpenThermBinarySensorEntityDescription(
|
||||
key=gw_vars.DATA_SLAVE_SERVICE_REQ,
|
||||
translation_key="service_required",
|
||||
device_class=BinarySensorDeviceClass.PROBLEM,
|
||||
device_description=THERMOSTAT_DEVICE_DESCRIPTION,
|
||||
),
|
||||
OpenThermBinarySensorEntityDescription(
|
||||
key=gw_vars.DATA_SLAVE_REMOTE_RESET,
|
||||
translation_key="supports_remote_reset",
|
||||
device_description=THERMOSTAT_DEVICE_DESCRIPTION,
|
||||
),
|
||||
OpenThermBinarySensorEntityDescription(
|
||||
key=gw_vars.DATA_SLAVE_LOW_WATER_PRESS,
|
||||
translation_key="low_water_pressure",
|
||||
device_class=BinarySensorDeviceClass.PROBLEM,
|
||||
device_description=THERMOSTAT_DEVICE_DESCRIPTION,
|
||||
),
|
||||
OpenThermBinarySensorEntityDescription(
|
||||
key=gw_vars.DATA_SLAVE_GAS_FAULT,
|
||||
translation_key="gas_fault",
|
||||
device_class=BinarySensorDeviceClass.PROBLEM,
|
||||
device_description=THERMOSTAT_DEVICE_DESCRIPTION,
|
||||
),
|
||||
OpenThermBinarySensorEntityDescription(
|
||||
key=gw_vars.DATA_SLAVE_AIR_PRESS_FAULT,
|
||||
translation_key="air_pressure_fault",
|
||||
device_class=BinarySensorDeviceClass.PROBLEM,
|
||||
device_description=THERMOSTAT_DEVICE_DESCRIPTION,
|
||||
),
|
||||
OpenThermBinarySensorEntityDescription(
|
||||
key=gw_vars.DATA_SLAVE_WATER_OVERTEMP,
|
||||
translation_key="water_overtemperature",
|
||||
device_class=BinarySensorDeviceClass.PROBLEM,
|
||||
device_description=THERMOSTAT_DEVICE_DESCRIPTION,
|
||||
),
|
||||
OpenThermBinarySensorEntityDescription(
|
||||
key=gw_vars.DATA_REMOTE_TRANSFER_MAX_CH,
|
||||
translation_key="supports_central_heating_setpoint_transfer",
|
||||
device_description=THERMOSTAT_DEVICE_DESCRIPTION,
|
||||
),
|
||||
OpenThermBinarySensorEntityDescription(
|
||||
key=gw_vars.DATA_REMOTE_RW_MAX_CH,
|
||||
translation_key="supports_central_heating_setpoint_writing",
|
||||
device_description=THERMOSTAT_DEVICE_DESCRIPTION,
|
||||
),
|
||||
OpenThermBinarySensorEntityDescription(
|
||||
key=gw_vars.DATA_REMOTE_TRANSFER_DHW,
|
||||
translation_key="supports_hot_water_setpoint_transfer",
|
||||
device_description=THERMOSTAT_DEVICE_DESCRIPTION,
|
||||
),
|
||||
OpenThermBinarySensorEntityDescription(
|
||||
key=gw_vars.DATA_REMOTE_RW_DHW,
|
||||
translation_key="supports_hot_water_setpoint_writing",
|
||||
device_description=THERMOSTAT_DEVICE_DESCRIPTION,
|
||||
),
|
||||
OpenThermBinarySensorEntityDescription(
|
||||
key=gw_vars.DATA_MASTER_CH_ENABLED,
|
||||
translation_key="central_heating_n",
|
||||
translation_placeholders={"circuit_number": "1"},
|
||||
device_description=BOILER_DEVICE_DESCRIPTION,
|
||||
),
|
||||
OpenThermBinarySensorEntityDescription(
|
||||
key=gw_vars.DATA_MASTER_CH2_ENABLED,
|
||||
translation_key="central_heating_n",
|
||||
translation_placeholders={"circuit_number": "2"},
|
||||
device_description=BOILER_DEVICE_DESCRIPTION,
|
||||
),
|
||||
OpenThermBinarySensorEntityDescription(
|
||||
key=gw_vars.DATA_MASTER_DHW_ENABLED,
|
||||
translation_key="hot_water",
|
||||
device_description=BOILER_DEVICE_DESCRIPTION,
|
||||
),
|
||||
OpenThermBinarySensorEntityDescription(
|
||||
key=gw_vars.DATA_MASTER_COOLING_ENABLED,
|
||||
translation_key="cooling",
|
||||
device_description=BOILER_DEVICE_DESCRIPTION,
|
||||
),
|
||||
OpenThermBinarySensorEntityDescription(
|
||||
key=gw_vars.DATA_MASTER_OTC_ENABLED,
|
||||
translation_key="outside_temp_correction",
|
||||
device_description=BOILER_DEVICE_DESCRIPTION,
|
||||
),
|
||||
OpenThermBinarySensorEntityDescription(
|
||||
key=gw_vars.DATA_ROVRD_MAN_PRIO,
|
||||
translation_key="override_manual_change_prio",
|
||||
device_description=BOILER_DEVICE_DESCRIPTION,
|
||||
),
|
||||
OpenThermBinarySensorEntityDescription(
|
||||
key=gw_vars.DATA_ROVRD_AUTO_PRIO,
|
||||
translation_key="override_program_change_prio",
|
||||
device_description=BOILER_DEVICE_DESCRIPTION,
|
||||
),
|
||||
)
|
||||
|
||||
@ -293,35 +399,22 @@ async def async_setup_entry(
|
||||
gw_hub = hass.data[DATA_OPENTHERM_GW][DATA_GATEWAYS][config_entry.data[CONF_ID]]
|
||||
|
||||
async_add_entities(
|
||||
OpenThermBinarySensor(gw_hub, source, description)
|
||||
for sources, description in BINARY_SENSOR_INFO
|
||||
for source in sources
|
||||
OpenThermBinarySensor(gw_hub, description)
|
||||
for description in BINARY_SENSOR_DESCRIPTIONS
|
||||
)
|
||||
|
||||
|
||||
class OpenThermBinarySensor(OpenThermEntity, BinarySensorEntity):
|
||||
"""Represent an OpenTherm Gateway binary sensor."""
|
||||
|
||||
_attr_entity_category = EntityCategory.DIAGNOSTIC
|
||||
entity_description: OpenThermBinarySensorEntityDescription
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
gw_hub: OpenThermGatewayHub,
|
||||
source: str,
|
||||
description: OpenThermBinarySensorEntityDescription,
|
||||
) -> None:
|
||||
"""Initialize the binary sensor."""
|
||||
self.entity_id = async_generate_entity_id(
|
||||
ENTITY_ID_FORMAT,
|
||||
f"{description.key}_{source}_{gw_hub.hub_id}",
|
||||
hass=gw_hub.hass,
|
||||
)
|
||||
super().__init__(gw_hub, source, description)
|
||||
|
||||
@callback
|
||||
def receive_report(self, status: dict[str, dict]) -> None:
|
||||
def receive_report(self, status: dict[OpenThermDataSource, dict]) -> None:
|
||||
"""Handle status updates from the component."""
|
||||
self._attr_available = self._gateway.connected
|
||||
state = status[self._source].get(self.entity_description.key)
|
||||
state = status[self.entity_description.device_description.data_source].get(
|
||||
self.entity_description.key
|
||||
)
|
||||
self._attr_is_on = None if state is None else bool(state)
|
||||
self.async_write_ha_state()
|
||||
|
@ -2,50 +2,52 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass
|
||||
import logging
|
||||
from types import MappingProxyType
|
||||
from typing import Any
|
||||
|
||||
from pyotgw import vars as gw_vars
|
||||
|
||||
from homeassistant.components.climate import (
|
||||
ENTITY_ID_FORMAT,
|
||||
PRESET_AWAY,
|
||||
PRESET_NONE,
|
||||
ClimateEntity,
|
||||
ClimateEntityDescription,
|
||||
ClimateEntityFeature,
|
||||
HVACAction,
|
||||
HVACMode,
|
||||
)
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import (
|
||||
ATTR_TEMPERATURE,
|
||||
CONF_ID,
|
||||
PRECISION_HALVES,
|
||||
PRECISION_TENTHS,
|
||||
PRECISION_WHOLE,
|
||||
UnitOfTemperature,
|
||||
)
|
||||
from homeassistant.const import ATTR_TEMPERATURE, CONF_ID, UnitOfTemperature
|
||||
from homeassistant.core import HomeAssistant, callback
|
||||
from homeassistant.helpers.device_registry import DeviceInfo
|
||||
from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
||||
from homeassistant.helpers.entity import async_generate_entity_id
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
|
||||
from . import DOMAIN
|
||||
from . import OpenThermGatewayHub
|
||||
from .const import (
|
||||
CONF_FLOOR_TEMP,
|
||||
CONF_READ_PRECISION,
|
||||
CONF_SET_PRECISION,
|
||||
CONF_TEMPORARY_OVRD_MODE,
|
||||
DATA_GATEWAYS,
|
||||
DATA_OPENTHERM_GW,
|
||||
THERMOSTAT_DEVICE_DESCRIPTION,
|
||||
OpenThermDataSource,
|
||||
)
|
||||
from .entity import OpenThermEntity, OpenThermEntityDescription
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
DEFAULT_FLOOR_TEMP = False
|
||||
|
||||
|
||||
@dataclass(frozen=True, kw_only=True)
|
||||
class OpenThermClimateEntityDescription(
|
||||
ClimateEntityDescription, OpenThermEntityDescription
|
||||
):
|
||||
"""Describes an opentherm_gw climate entity."""
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
config_entry: ConfigEntry,
|
||||
@ -56,6 +58,10 @@ async def async_setup_entry(
|
||||
ents.append(
|
||||
OpenThermClimate(
|
||||
hass.data[DATA_OPENTHERM_GW][DATA_GATEWAYS][config_entry.data[CONF_ID]],
|
||||
OpenThermClimateEntityDescription(
|
||||
key="thermostat_entity",
|
||||
device_description=THERMOSTAT_DEVICE_DESCRIPTION,
|
||||
),
|
||||
config_entry.options,
|
||||
)
|
||||
)
|
||||
@ -63,98 +69,82 @@ async def async_setup_entry(
|
||||
async_add_entities(ents)
|
||||
|
||||
|
||||
class OpenThermClimate(ClimateEntity):
|
||||
class OpenThermClimate(OpenThermEntity, ClimateEntity):
|
||||
"""Representation of a climate device."""
|
||||
|
||||
_attr_should_poll = False
|
||||
_attr_supported_features = (
|
||||
ClimateEntityFeature.TARGET_TEMPERATURE | ClimateEntityFeature.PRESET_MODE
|
||||
)
|
||||
_attr_temperature_unit = UnitOfTemperature.CELSIUS
|
||||
_attr_available = False
|
||||
_attr_hvac_modes = []
|
||||
_attr_name = None
|
||||
_attr_preset_modes = []
|
||||
_attr_min_temp = 1
|
||||
_attr_max_temp = 30
|
||||
_hvac_mode = HVACMode.HEAT
|
||||
_current_temperature: float | None = None
|
||||
_new_target_temperature: float | None = None
|
||||
_target_temperature: float | None = None
|
||||
_attr_hvac_mode = HVACMode.HEAT
|
||||
_away_mode_a: int | None = None
|
||||
_away_mode_b: int | None = None
|
||||
_away_state_a = False
|
||||
_away_state_b = False
|
||||
_current_operation: HVACAction | None = None
|
||||
_enable_turn_on_off_backwards_compatibility = False
|
||||
_target_temperature: float | None = None
|
||||
_new_target_temperature: float | None = None
|
||||
entity_description: OpenThermClimateEntityDescription
|
||||
|
||||
def __init__(self, gw_hub, options):
|
||||
"""Initialize the device."""
|
||||
self._gateway = gw_hub
|
||||
self.entity_id = async_generate_entity_id(
|
||||
ENTITY_ID_FORMAT, gw_hub.hub_id, hass=gw_hub.hass
|
||||
)
|
||||
self.friendly_name = gw_hub.name
|
||||
self._attr_name = self.friendly_name
|
||||
self.floor_temp = options.get(CONF_FLOOR_TEMP, DEFAULT_FLOOR_TEMP)
|
||||
self.temp_read_precision = options.get(CONF_READ_PRECISION)
|
||||
self.temp_set_precision = options.get(CONF_SET_PRECISION)
|
||||
def __init__(
|
||||
self,
|
||||
gw_hub: OpenThermGatewayHub,
|
||||
description: OpenThermClimateEntityDescription,
|
||||
options: MappingProxyType[str, Any],
|
||||
) -> None:
|
||||
"""Initialize the entity."""
|
||||
super().__init__(gw_hub, description)
|
||||
if CONF_READ_PRECISION in options:
|
||||
self._attr_precision = options[CONF_READ_PRECISION]
|
||||
self._attr_target_temperature_step = options.get(CONF_SET_PRECISION)
|
||||
self.temporary_ovrd_mode = options.get(CONF_TEMPORARY_OVRD_MODE, True)
|
||||
self._unsub_options = None
|
||||
self._unsub_updates = None
|
||||
self._attr_device_info = DeviceInfo(
|
||||
identifiers={(DOMAIN, gw_hub.hub_id)},
|
||||
manufacturer="Schelte Bron",
|
||||
model="OpenTherm Gateway",
|
||||
name=gw_hub.name,
|
||||
sw_version=gw_hub.gw_version,
|
||||
)
|
||||
self._attr_unique_id = gw_hub.hub_id
|
||||
|
||||
@callback
|
||||
def update_options(self, entry):
|
||||
"""Update climate entity options."""
|
||||
self.floor_temp = entry.options[CONF_FLOOR_TEMP]
|
||||
self.temp_read_precision = entry.options[CONF_READ_PRECISION]
|
||||
self.temp_set_precision = entry.options[CONF_SET_PRECISION]
|
||||
self._attr_precision = entry.options[CONF_READ_PRECISION]
|
||||
self._attr_target_temperature_step = entry.options[CONF_SET_PRECISION]
|
||||
self.temporary_ovrd_mode = entry.options[CONF_TEMPORARY_OVRD_MODE]
|
||||
self.async_write_ha_state()
|
||||
|
||||
async def async_added_to_hass(self) -> None:
|
||||
"""Connect to the OpenTherm Gateway device."""
|
||||
_LOGGER.debug("Added OpenTherm Gateway climate device %s", self.friendly_name)
|
||||
self._unsub_updates = async_dispatcher_connect(
|
||||
self.hass, self._gateway.update_signal, self.receive_report
|
||||
await super().async_added_to_hass()
|
||||
self.async_on_remove(
|
||||
async_dispatcher_connect(
|
||||
self.hass, self._gateway.options_update_signal, self.update_options
|
||||
)
|
||||
)
|
||||
self._unsub_options = async_dispatcher_connect(
|
||||
self.hass, self._gateway.options_update_signal, self.update_options
|
||||
)
|
||||
|
||||
async def async_will_remove_from_hass(self) -> None:
|
||||
"""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):
|
||||
def receive_report(self, status: dict[OpenThermDataSource, dict]):
|
||||
"""Receive and handle a new report from the Gateway."""
|
||||
self._attr_available = self._gateway.connected
|
||||
ch_active = status[gw_vars.BOILER].get(gw_vars.DATA_SLAVE_CH_ACTIVE)
|
||||
flame_on = status[gw_vars.BOILER].get(gw_vars.DATA_SLAVE_FLAME_ON)
|
||||
cooling_active = status[gw_vars.BOILER].get(gw_vars.DATA_SLAVE_COOLING_ACTIVE)
|
||||
ch_active = status[OpenThermDataSource.BOILER].get(gw_vars.DATA_SLAVE_CH_ACTIVE)
|
||||
flame_on = status[OpenThermDataSource.BOILER].get(gw_vars.DATA_SLAVE_FLAME_ON)
|
||||
cooling_active = status[OpenThermDataSource.BOILER].get(
|
||||
gw_vars.DATA_SLAVE_COOLING_ACTIVE
|
||||
)
|
||||
if ch_active and flame_on:
|
||||
self._current_operation = HVACAction.HEATING
|
||||
self._hvac_mode = HVACMode.HEAT
|
||||
self._attr_hvac_action = HVACAction.HEATING
|
||||
self._attr_hvac_mode = HVACMode.HEAT
|
||||
elif cooling_active:
|
||||
self._current_operation = HVACAction.COOLING
|
||||
self._hvac_mode = HVACMode.COOL
|
||||
self._attr_hvac_action = HVACAction.COOLING
|
||||
self._attr_hvac_mode = HVACMode.COOL
|
||||
else:
|
||||
self._current_operation = HVACAction.IDLE
|
||||
self._attr_hvac_action = HVACAction.IDLE
|
||||
|
||||
self._current_temperature = status[gw_vars.THERMOSTAT].get(
|
||||
self._attr_current_temperature = status[OpenThermDataSource.THERMOSTAT].get(
|
||||
gw_vars.DATA_ROOM_TEMP
|
||||
)
|
||||
temp_upd = status[gw_vars.THERMOSTAT].get(gw_vars.DATA_ROOM_SETPOINT)
|
||||
temp_upd = status[OpenThermDataSource.THERMOSTAT].get(
|
||||
gw_vars.DATA_ROOM_SETPOINT
|
||||
)
|
||||
|
||||
if self._target_temperature != temp_upd:
|
||||
self._new_target_temperature = None
|
||||
@ -162,82 +152,35 @@ class OpenThermClimate(ClimateEntity):
|
||||
|
||||
# GPIO mode 5: 0 == Away
|
||||
# GPIO mode 6: 1 == Away
|
||||
gpio_a_state = status[gw_vars.OTGW].get(gw_vars.OTGW_GPIO_A)
|
||||
if gpio_a_state == 5:
|
||||
self._away_mode_a = 0
|
||||
elif gpio_a_state == 6:
|
||||
self._away_mode_a = 1
|
||||
else:
|
||||
self._away_mode_a = None
|
||||
gpio_b_state = status[gw_vars.OTGW].get(gw_vars.OTGW_GPIO_B)
|
||||
if gpio_b_state == 5:
|
||||
self._away_mode_b = 0
|
||||
elif gpio_b_state == 6:
|
||||
self._away_mode_b = 1
|
||||
else:
|
||||
self._away_mode_b = None
|
||||
if self._away_mode_a is not None:
|
||||
self._away_state_a = (
|
||||
status[gw_vars.OTGW].get(gw_vars.OTGW_GPIO_A_STATE) == self._away_mode_a
|
||||
gpio_a_state = status[OpenThermDataSource.GATEWAY].get(gw_vars.OTGW_GPIO_A)
|
||||
gpio_b_state = status[OpenThermDataSource.GATEWAY].get(gw_vars.OTGW_GPIO_B)
|
||||
self._away_mode_a = gpio_a_state - 5 if gpio_a_state in (5, 6) else None
|
||||
self._away_mode_b = gpio_b_state - 5 if gpio_b_state in (5, 6) else None
|
||||
self._away_state_a = (
|
||||
(
|
||||
status[OpenThermDataSource.GATEWAY].get(gw_vars.OTGW_GPIO_A_STATE)
|
||||
== self._away_mode_a
|
||||
)
|
||||
if self._away_mode_b is not None:
|
||||
self._away_state_b = (
|
||||
status[gw_vars.OTGW].get(gw_vars.OTGW_GPIO_B_STATE) == self._away_mode_b
|
||||
if self._away_mode_a is not None
|
||||
else False
|
||||
)
|
||||
self._away_state_b = (
|
||||
(
|
||||
status[OpenThermDataSource.GATEWAY].get(gw_vars.OTGW_GPIO_B_STATE)
|
||||
== self._away_mode_b
|
||||
)
|
||||
if self._away_mode_b is not None
|
||||
else False
|
||||
)
|
||||
self.async_write_ha_state()
|
||||
|
||||
@property
|
||||
def precision(self):
|
||||
"""Return the precision of the system."""
|
||||
if self.temp_read_precision:
|
||||
return self.temp_read_precision
|
||||
if self.hass.config.units.temperature_unit == UnitOfTemperature.CELSIUS:
|
||||
return PRECISION_HALVES
|
||||
return PRECISION_WHOLE
|
||||
|
||||
@property
|
||||
def hvac_action(self) -> HVACAction | None:
|
||||
"""Return current HVAC operation."""
|
||||
return self._current_operation
|
||||
|
||||
@property
|
||||
def hvac_mode(self) -> HVACMode:
|
||||
"""Return current HVAC mode."""
|
||||
return self._hvac_mode
|
||||
|
||||
def set_hvac_mode(self, hvac_mode: HVACMode) -> None:
|
||||
"""Set the HVAC mode."""
|
||||
_LOGGER.warning("Changing HVAC mode is not supported")
|
||||
|
||||
@property
|
||||
def current_temperature(self):
|
||||
"""Return the current temperature."""
|
||||
if self._current_temperature is None:
|
||||
return None
|
||||
if self.floor_temp is True:
|
||||
if self.precision == PRECISION_HALVES:
|
||||
return int(2 * self._current_temperature) / 2
|
||||
if self.precision == PRECISION_TENTHS:
|
||||
return int(10 * self._current_temperature) / 10
|
||||
return int(self._current_temperature)
|
||||
return self._current_temperature
|
||||
|
||||
@property
|
||||
def target_temperature(self):
|
||||
def target_temperature(self) -> float | None:
|
||||
"""Return the temperature we try to reach."""
|
||||
return self._new_target_temperature or self._target_temperature
|
||||
|
||||
@property
|
||||
def target_temperature_step(self):
|
||||
"""Return the supported step of target temperature."""
|
||||
if self.temp_set_precision:
|
||||
return self.temp_set_precision
|
||||
if self.hass.config.units.temperature_unit == UnitOfTemperature.CELSIUS:
|
||||
return PRECISION_HALVES
|
||||
return PRECISION_WHOLE
|
||||
|
||||
@property
|
||||
def preset_mode(self):
|
||||
def preset_mode(self) -> str:
|
||||
"""Return current preset mode."""
|
||||
if self._away_state_a or self._away_state_b:
|
||||
return PRESET_AWAY
|
||||
|
@ -34,6 +34,7 @@ from .const import (
|
||||
CONF_SET_PRECISION,
|
||||
CONF_TEMPORARY_OVRD_MODE,
|
||||
CONNECTION_TIMEOUT,
|
||||
OpenThermDataSource,
|
||||
)
|
||||
|
||||
|
||||
@ -74,7 +75,7 @@ class OpenThermGwConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
await otgw.disconnect()
|
||||
if not status:
|
||||
raise ConnectionError
|
||||
return status[gw_vars.OTGW].get(gw_vars.OTGW_ABOUT)
|
||||
return status[OpenThermDataSource.GATEWAY].get(gw_vars.OTGW_ABOUT)
|
||||
|
||||
try:
|
||||
async with asyncio.timeout(CONNECTION_TIMEOUT):
|
||||
|
@ -1,5 +1,10 @@
|
||||
"""Constants for the opentherm_gw integration."""
|
||||
|
||||
from dataclasses import dataclass
|
||||
from enum import StrEnum
|
||||
|
||||
from pyotgw import vars as gw_vars
|
||||
|
||||
ATTR_GW_ID = "gateway_id"
|
||||
ATTR_LEVEL = "level"
|
||||
ATTR_DHW_OVRD = "dhw_override"
|
||||
@ -33,3 +38,41 @@ SERVICE_SET_MAX_MOD = "set_max_modulation"
|
||||
SERVICE_SET_OAT = "set_outside_temperature"
|
||||
SERVICE_SET_SB_TEMP = "set_setback_temperature"
|
||||
SERVICE_SEND_TRANSP_CMD = "send_transparent_command"
|
||||
|
||||
|
||||
class OpenThermDataSource(StrEnum):
|
||||
"""List valid OpenTherm data sources."""
|
||||
|
||||
BOILER = gw_vars.BOILER
|
||||
GATEWAY = gw_vars.OTGW
|
||||
THERMOSTAT = gw_vars.THERMOSTAT
|
||||
|
||||
|
||||
class OpenThermDeviceIdentifier(StrEnum):
|
||||
"""List valid OpenTherm device identifiers."""
|
||||
|
||||
BOILER = "boiler"
|
||||
GATEWAY = "gateway"
|
||||
THERMOSTAT = "thermostat"
|
||||
|
||||
|
||||
@dataclass(frozen=True, kw_only=True)
|
||||
class OpenThermDeviceDescription:
|
||||
"""Describe OpenTherm device properties."""
|
||||
|
||||
data_source: OpenThermDataSource
|
||||
device_identifier: OpenThermDeviceIdentifier
|
||||
|
||||
|
||||
BOILER_DEVICE_DESCRIPTION = OpenThermDeviceDescription(
|
||||
data_source=OpenThermDataSource.BOILER,
|
||||
device_identifier=OpenThermDeviceIdentifier.BOILER,
|
||||
)
|
||||
GATEWAY_DEVICE_DESCRIPTION = OpenThermDeviceDescription(
|
||||
data_source=OpenThermDataSource.GATEWAY,
|
||||
device_identifier=OpenThermDeviceIdentifier.GATEWAY,
|
||||
)
|
||||
THERMOSTAT_DEVICE_DESCRIPTION = OpenThermDeviceDescription(
|
||||
data_source=OpenThermDataSource.THERMOSTAT,
|
||||
device_identifier=OpenThermDeviceIdentifier.THERMOSTAT,
|
||||
)
|
||||
|
@ -10,7 +10,7 @@ from homeassistant.helpers.dispatcher import async_dispatcher_connect
|
||||
from homeassistant.helpers.entity import Entity, EntityDescription
|
||||
|
||||
from . import OpenThermGatewayHub
|
||||
from .const import DOMAIN
|
||||
from .const import DOMAIN, OpenThermDataSource, OpenThermDeviceDescription
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
@ -24,53 +24,49 @@ TRANSLATE_SOURCE = {
|
||||
class OpenThermEntityDescription(EntityDescription):
|
||||
"""Describe common opentherm_gw entity properties."""
|
||||
|
||||
friendly_name_format: str
|
||||
device_description: OpenThermDeviceDescription
|
||||
|
||||
|
||||
class OpenThermEntity(Entity):
|
||||
"""Represent an OpenTherm Gateway entity."""
|
||||
"""Represent an OpenTherm entity."""
|
||||
|
||||
_attr_has_entity_name = True
|
||||
_attr_should_poll = False
|
||||
_attr_entity_registry_enabled_default = False
|
||||
_attr_available = False
|
||||
entity_description: OpenThermEntityDescription
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
gw_hub: OpenThermGatewayHub,
|
||||
source: str,
|
||||
description: OpenThermEntityDescription,
|
||||
) -> None:
|
||||
"""Initialize the entity."""
|
||||
self.entity_description = description
|
||||
self._gateway = gw_hub
|
||||
self._source = source
|
||||
friendly_name_format = (
|
||||
f"{description.friendly_name_format} ({TRANSLATE_SOURCE[source]})"
|
||||
if TRANSLATE_SOURCE[source] is not None
|
||||
else description.friendly_name_format
|
||||
)
|
||||
self._attr_name = friendly_name_format.format(gw_hub.name)
|
||||
self._attr_unique_id = f"{gw_hub.hub_id}-{source}-{description.key}"
|
||||
self._attr_unique_id = f"{gw_hub.hub_id}-{description.device_description.device_identifier}-{description.key}"
|
||||
self._attr_device_info = DeviceInfo(
|
||||
identifiers={(DOMAIN, gw_hub.hub_id)},
|
||||
manufacturer="Schelte Bron",
|
||||
model="OpenTherm Gateway",
|
||||
name=gw_hub.name,
|
||||
sw_version=gw_hub.gw_version,
|
||||
identifiers={
|
||||
(
|
||||
DOMAIN,
|
||||
f"{gw_hub.hub_id}-{description.device_description.device_identifier}",
|
||||
)
|
||||
},
|
||||
)
|
||||
|
||||
async def async_added_to_hass(self) -> None:
|
||||
"""Subscribe to updates from the component."""
|
||||
_LOGGER.debug("Added OpenTherm Gateway entity %s", self._attr_name)
|
||||
self.async_on_remove(
|
||||
async_dispatcher_connect(
|
||||
self.hass, self._gateway.update_signal, self.receive_report
|
||||
)
|
||||
)
|
||||
|
||||
@property
|
||||
def available(self) -> bool:
|
||||
"""Return connection status of the hub to indicate availability."""
|
||||
return self._gateway.connected
|
||||
|
||||
@callback
|
||||
def receive_report(self, status: dict[str, dict]) -> None:
|
||||
def receive_report(self, status: dict[OpenThermDataSource, dict]) -> None:
|
||||
"""Handle status updates from the component."""
|
||||
# Must be implemented at the platform level.
|
||||
raise NotImplementedError
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,4 +1,8 @@
|
||||
{
|
||||
"common": {
|
||||
"state_not_supported": "Not supported",
|
||||
"state_supported": "Supported"
|
||||
},
|
||||
"config": {
|
||||
"step": {
|
||||
"init": {
|
||||
@ -16,6 +20,297 @@
|
||||
"timeout_connect": "[%key:common::config_flow::error::timeout_connect%]"
|
||||
}
|
||||
},
|
||||
"device": {
|
||||
"boiler_device": {
|
||||
"name": "OpenTherm Boiler"
|
||||
},
|
||||
"gateway_device": {
|
||||
"name": "OpenTherm Gateway"
|
||||
},
|
||||
"thermostat_device": {
|
||||
"name": "OpenTherm Thermostat"
|
||||
}
|
||||
},
|
||||
"entity": {
|
||||
"binary_sensor": {
|
||||
"fault_indication": {
|
||||
"name": "Fault indication"
|
||||
},
|
||||
"central_heating_n": {
|
||||
"name": "Central heating {circuit_number}"
|
||||
},
|
||||
"cooling": {
|
||||
"name": "Cooling"
|
||||
},
|
||||
"flame": {
|
||||
"name": "Flame"
|
||||
},
|
||||
"hot_water": {
|
||||
"name": "Hot water"
|
||||
},
|
||||
"diagnostic_indication": {
|
||||
"name": "Diagnostic indication"
|
||||
},
|
||||
"supports_hot_water": {
|
||||
"name": "Hot water support",
|
||||
"state": {
|
||||
"off": "[%key:component::opentherm_gw::common::state_not_supported%]",
|
||||
"on": "[%key:component::opentherm_gw::common::state_supported%]"
|
||||
}
|
||||
},
|
||||
"control_type": {
|
||||
"name": "Control type"
|
||||
},
|
||||
"supports_cooling": {
|
||||
"name": "Cooling support",
|
||||
"state": {
|
||||
"off": "[%key:component::opentherm_gw::common::state_not_supported%]",
|
||||
"on": "[%key:component::opentherm_gw::common::state_supported%]"
|
||||
}
|
||||
},
|
||||
"hot_water_config": {
|
||||
"name": "Hot water system type",
|
||||
"state": {
|
||||
"off": "Instantaneous or unspecified",
|
||||
"on": "Storage tank"
|
||||
}
|
||||
},
|
||||
"supports_pump_control": {
|
||||
"name": "Pump control support",
|
||||
"state": {
|
||||
"off": "[%key:component::opentherm_gw::common::state_not_supported%]",
|
||||
"on": "[%key:component::opentherm_gw::common::state_supported%]"
|
||||
}
|
||||
},
|
||||
"supports_ch_2": {
|
||||
"name": "Central heating 2 support",
|
||||
"state": {
|
||||
"off": "[%key:component::opentherm_gw::common::state_not_supported%]",
|
||||
"on": "[%key:component::opentherm_gw::common::state_supported%]"
|
||||
}
|
||||
},
|
||||
"service_required": {
|
||||
"name": "Service required"
|
||||
},
|
||||
"supports_remote_reset": {
|
||||
"name": "Remote reset support",
|
||||
"state": {
|
||||
"off": "[%key:component::opentherm_gw::common::state_not_supported%]",
|
||||
"on": "[%key:component::opentherm_gw::common::state_supported%]"
|
||||
}
|
||||
},
|
||||
"low_water_pressure": {
|
||||
"name": "Low water pressure"
|
||||
},
|
||||
"gas_fault": {
|
||||
"name": "Gas fault"
|
||||
},
|
||||
"air_pressure_fault": {
|
||||
"name": "Air pressure fault"
|
||||
},
|
||||
"water_overtemperature": {
|
||||
"name": "Water overtemperature"
|
||||
},
|
||||
"supports_central_heating_setpoint_transfer": {
|
||||
"name": "Central heating setpoint transfer support",
|
||||
"state": {
|
||||
"off": "[%key:component::opentherm_gw::common::state_not_supported%]",
|
||||
"on": "[%key:component::opentherm_gw::common::state_supported%]"
|
||||
}
|
||||
},
|
||||
"supports_central_heating_setpoint_writing": {
|
||||
"name": "Central heating setpoint write support",
|
||||
"state": {
|
||||
"off": "[%key:component::opentherm_gw::common::state_not_supported%]",
|
||||
"on": "[%key:component::opentherm_gw::common::state_supported%]"
|
||||
}
|
||||
},
|
||||
"supports_hot_water_setpoint_transfer": {
|
||||
"name": "Hot water setpoint transfer support",
|
||||
"state": {
|
||||
"off": "[%key:component::opentherm_gw::common::state_not_supported%]",
|
||||
"on": "[%key:component::opentherm_gw::common::state_supported%]"
|
||||
}
|
||||
},
|
||||
"supports_hot_water_setpoint_writing": {
|
||||
"name": "Hot water setpoint write support",
|
||||
"state": {
|
||||
"off": "[%key:component::opentherm_gw::common::state_not_supported%]",
|
||||
"on": "[%key:component::opentherm_gw::common::state_supported%]"
|
||||
}
|
||||
},
|
||||
"gpio_state_n": {
|
||||
"name": "GPIO {gpio_id} state"
|
||||
},
|
||||
"ignore_transitions": {
|
||||
"name": "Ignore transitions"
|
||||
},
|
||||
"override_high_byte": {
|
||||
"name": "Override high byte"
|
||||
},
|
||||
"outside_temp_correction": {
|
||||
"name": "Outside temperature correction"
|
||||
},
|
||||
"override_manual_change_prio": {
|
||||
"name": "Manual change has priority over override"
|
||||
},
|
||||
"override_program_change_prio": {
|
||||
"name": "Programmed change has priority over override"
|
||||
}
|
||||
},
|
||||
"sensor": {
|
||||
"control_setpoint_n": {
|
||||
"name": "Control setpoint {circuit_number}"
|
||||
},
|
||||
"manufacturer_id": {
|
||||
"name": "Manufacturer ID"
|
||||
},
|
||||
"oem_fault_code": {
|
||||
"name": "Manufacturer-specific fault code"
|
||||
},
|
||||
"cooling_control": {
|
||||
"name": "Cooling control signal"
|
||||
},
|
||||
"max_relative_mod_level": {
|
||||
"name": "Maximum relative modulation level"
|
||||
},
|
||||
"max_capacity": {
|
||||
"name": "Maximum capacity"
|
||||
},
|
||||
"min_mod_level": {
|
||||
"name": "Minimum modulation level"
|
||||
},
|
||||
"relative_mod_level": {
|
||||
"name": "Relative modulation level"
|
||||
},
|
||||
"central_heating_pressure": {
|
||||
"name": "Central heating water pressure"
|
||||
},
|
||||
"hot_water_flow_rate": {
|
||||
"name": "Hot water flow rate"
|
||||
},
|
||||
"central_heating_temperature_n": {
|
||||
"name": "Central heating {circuit_number} water temperature"
|
||||
},
|
||||
"hot_water_temperature_n": {
|
||||
"name": "Hot water {circuit_number} temperature"
|
||||
},
|
||||
"return_water_temperature": {
|
||||
"name": "Return water temperature"
|
||||
},
|
||||
"solar_storage_temperature": {
|
||||
"name": "Solar storage temperature"
|
||||
},
|
||||
"solar_collector_temperature": {
|
||||
"name": "Solar collector temperature"
|
||||
},
|
||||
"exhaust_temperature": {
|
||||
"name": "Exhaust temperature"
|
||||
},
|
||||
"max_hot_water_setpoint_upper": {
|
||||
"name": "Maximum hot water setpoint upper bound"
|
||||
},
|
||||
"max_hot_water_setpoint_lower": {
|
||||
"name": "Maximum hot water setpoint lower bound"
|
||||
},
|
||||
"max_central_heating_setpoint_upper": {
|
||||
"name": "Maximum central heating setpoint upper bound"
|
||||
},
|
||||
"max_central_heating_setpoint_lower": {
|
||||
"name": "Maximum central heating setpoint lower bound"
|
||||
},
|
||||
"hot_water_setpoint": {
|
||||
"name": "Hot water setpoint"
|
||||
},
|
||||
"max_central_heating_setpoint": {
|
||||
"name": "Maximum central heating setpoint"
|
||||
},
|
||||
"oem_diagnostic_code": {
|
||||
"name": "Manufacturer-specific diagnostic code"
|
||||
},
|
||||
"total_burner_starts": {
|
||||
"name": "Burner start count"
|
||||
},
|
||||
"central_heating_pump_starts": {
|
||||
"name": "Central heating pump start count"
|
||||
},
|
||||
"hot_water_pump_starts": {
|
||||
"name": "Hot water pump start count"
|
||||
},
|
||||
"hot_water_burner_starts": {
|
||||
"name": "Hot water burner start count"
|
||||
},
|
||||
"total_burner_hours": {
|
||||
"name": "Burner running time"
|
||||
},
|
||||
"central_heating_pump_hours": {
|
||||
"name": "Central heating pump running time"
|
||||
},
|
||||
"hot_water_pump_hours": {
|
||||
"name": "Hot water pump running time"
|
||||
},
|
||||
"hot_water_burner_hours": {
|
||||
"name": "Hot water burner running time"
|
||||
},
|
||||
"opentherm_version": {
|
||||
"name": "OpenTherm protocol version"
|
||||
},
|
||||
"product_type": {
|
||||
"name": "Product type"
|
||||
},
|
||||
"product_version": {
|
||||
"name": "Product version"
|
||||
},
|
||||
"operating_mode": {
|
||||
"name": "Operating mode"
|
||||
},
|
||||
"hot_water_override_mode": {
|
||||
"name": "Hot water override mode"
|
||||
},
|
||||
"firmware_version": {
|
||||
"name": "Firmware version"
|
||||
},
|
||||
"firmware_build": {
|
||||
"name": "Firmware build"
|
||||
},
|
||||
"clock_speed": {
|
||||
"name": "Clock speed"
|
||||
},
|
||||
"led_mode_n": {
|
||||
"name": "LED {led_id} mode"
|
||||
},
|
||||
"gpio_mode_n": {
|
||||
"name": "GPIO {gpio_id} mode"
|
||||
},
|
||||
"setback_temperature": {
|
||||
"name": "Setback temperature"
|
||||
},
|
||||
"room_setpoint_override_mode": {
|
||||
"name": "Room setpoint override mode"
|
||||
},
|
||||
"smart_power_mode": {
|
||||
"name": "Smart power mode"
|
||||
},
|
||||
"thermostat_detection_mode": {
|
||||
"name": "Thermostat detection mode"
|
||||
},
|
||||
"reference_voltage": {
|
||||
"name": "Reference voltage setting"
|
||||
},
|
||||
"room_setpoint_override": {
|
||||
"name": "Room setpoint override"
|
||||
},
|
||||
"room_setpoint_n": {
|
||||
"name": "Room setpoint {setpoint_id}"
|
||||
},
|
||||
"room_temperature": {
|
||||
"name": "Room temperature"
|
||||
},
|
||||
"outside_temperature": {
|
||||
"name": "Outside temperature"
|
||||
}
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
"step": {
|
||||
"init": {
|
||||
|
@ -1,12 +1,15 @@
|
||||
"""Test Opentherm Gateway init."""
|
||||
|
||||
from unittest.mock import patch
|
||||
from unittest.mock import AsyncMock, MagicMock, patch
|
||||
|
||||
from pyotgw.vars import OTGW, OTGW_ABOUT
|
||||
import pytest
|
||||
|
||||
from homeassistant import setup
|
||||
from homeassistant.components.opentherm_gw.const import DOMAIN
|
||||
from homeassistant.components.opentherm_gw.const import (
|
||||
DOMAIN,
|
||||
OpenThermDeviceIdentifier,
|
||||
)
|
||||
from homeassistant.const import CONF_DEVICE, CONF_ID, CONF_NAME
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.helpers import device_registry as dr
|
||||
@ -49,7 +52,9 @@ async def test_device_registry_insert(
|
||||
|
||||
await hass.async_block_till_done()
|
||||
|
||||
gw_dev = device_registry.async_get_device(identifiers={(DOMAIN, MOCK_GATEWAY_ID)})
|
||||
gw_dev = device_registry.async_get_device(
|
||||
identifiers={(DOMAIN, f"{MOCK_GATEWAY_ID}-{OpenThermDeviceIdentifier.GATEWAY}")}
|
||||
)
|
||||
assert gw_dev.sw_version == VERSION_OLD
|
||||
|
||||
|
||||
@ -63,7 +68,9 @@ async def test_device_registry_update(
|
||||
|
||||
device_registry.async_get_or_create(
|
||||
config_entry_id=MOCK_CONFIG_ENTRY.entry_id,
|
||||
identifiers={(DOMAIN, MOCK_GATEWAY_ID)},
|
||||
identifiers={
|
||||
(DOMAIN, f"{MOCK_GATEWAY_ID}-{OpenThermDeviceIdentifier.GATEWAY}")
|
||||
},
|
||||
name="Mock Gateway",
|
||||
manufacturer="Schelte Bron",
|
||||
model="OpenTherm Gateway",
|
||||
@ -80,5 +87,70 @@ async def test_device_registry_update(
|
||||
await setup.async_setup_component(hass, DOMAIN, {})
|
||||
|
||||
await hass.async_block_till_done()
|
||||
gw_dev = device_registry.async_get_device(identifiers={(DOMAIN, MOCK_GATEWAY_ID)})
|
||||
gw_dev = device_registry.async_get_device(
|
||||
identifiers={(DOMAIN, f"{MOCK_GATEWAY_ID}-{OpenThermDeviceIdentifier.GATEWAY}")}
|
||||
)
|
||||
assert gw_dev is not None
|
||||
assert gw_dev.sw_version == VERSION_NEW
|
||||
|
||||
|
||||
# Device migration test can be removed in 2025.4.0
|
||||
async def test_device_migration(
|
||||
hass: HomeAssistant, device_registry: dr.DeviceRegistry
|
||||
) -> None:
|
||||
"""Test that the device registry is updated correctly."""
|
||||
MOCK_CONFIG_ENTRY.add_to_hass(hass)
|
||||
|
||||
device_registry.async_get_or_create(
|
||||
config_entry_id=MOCK_CONFIG_ENTRY.entry_id,
|
||||
identifiers={
|
||||
(DOMAIN, MOCK_GATEWAY_ID),
|
||||
},
|
||||
name="Mock Gateway",
|
||||
manufacturer="Schelte Bron",
|
||||
model="OpenTherm Gateway",
|
||||
sw_version=VERSION_OLD,
|
||||
)
|
||||
|
||||
with (
|
||||
patch(
|
||||
"homeassistant.components.opentherm_gw.OpenThermGateway",
|
||||
return_value=MagicMock(
|
||||
connect=AsyncMock(return_value=MINIMAL_STATUS_UPD),
|
||||
set_control_setpoint=AsyncMock(),
|
||||
set_max_relative_mod=AsyncMock(),
|
||||
disconnect=AsyncMock(),
|
||||
),
|
||||
),
|
||||
):
|
||||
await setup.async_setup_component(hass, DOMAIN, {})
|
||||
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert (
|
||||
device_registry.async_get_device(identifiers={(DOMAIN, MOCK_GATEWAY_ID)})
|
||||
is None
|
||||
)
|
||||
|
||||
gw_dev = device_registry.async_get_device(
|
||||
identifiers={(DOMAIN, f"{MOCK_GATEWAY_ID}-{OpenThermDeviceIdentifier.GATEWAY}")}
|
||||
)
|
||||
assert gw_dev is not None
|
||||
|
||||
assert (
|
||||
device_registry.async_get_device(
|
||||
identifiers={
|
||||
(DOMAIN, f"{MOCK_GATEWAY_ID}-{OpenThermDeviceIdentifier.BOILER}")
|
||||
}
|
||||
)
|
||||
is not None
|
||||
)
|
||||
|
||||
assert (
|
||||
device_registry.async_get_device(
|
||||
identifiers={
|
||||
(DOMAIN, f"{MOCK_GATEWAY_ID}-{OpenThermDeviceIdentifier.THERMOSTAT}")
|
||||
}
|
||||
)
|
||||
is not None
|
||||
)
|
||||
|
Loading…
x
Reference in New Issue
Block a user