278 lines
10 KiB
Python
278 lines
10 KiB
Python
"""Provides number entities for Home Connect."""
|
|
|
|
import logging
|
|
from typing import cast
|
|
|
|
from aiohomeconnect.model import GetSetting, OptionKey, SettingKey
|
|
from aiohomeconnect.model.error import HomeConnectError
|
|
|
|
from homeassistant.components.number import (
|
|
NumberDeviceClass,
|
|
NumberEntity,
|
|
NumberEntityDescription,
|
|
)
|
|
from homeassistant.const import PERCENTAGE
|
|
from homeassistant.core import HomeAssistant
|
|
from homeassistant.exceptions import HomeAssistantError
|
|
from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback
|
|
|
|
from .common import setup_home_connect_entry
|
|
from .const import DOMAIN, UNIT_MAP
|
|
from .coordinator import HomeConnectApplianceData, HomeConnectConfigEntry
|
|
from .entity import HomeConnectEntity, HomeConnectOptionEntity, constraint_fetcher
|
|
from .utils import get_dict_from_home_connect_error
|
|
|
|
_LOGGER = logging.getLogger(__name__)
|
|
|
|
PARALLEL_UPDATES = 1
|
|
|
|
NUMBERS = (
|
|
NumberEntityDescription(
|
|
key=SettingKey.BSH_COMMON_ALARM_CLOCK,
|
|
device_class=NumberDeviceClass.DURATION,
|
|
translation_key="alarm_clock",
|
|
),
|
|
NumberEntityDescription(
|
|
key=SettingKey.REFRIGERATION_FRIDGE_FREEZER_SETPOINT_TEMPERATURE_REFRIGERATOR,
|
|
device_class=NumberDeviceClass.TEMPERATURE,
|
|
translation_key="refrigerator_setpoint_temperature",
|
|
),
|
|
NumberEntityDescription(
|
|
key=SettingKey.REFRIGERATION_FRIDGE_FREEZER_SETPOINT_TEMPERATURE_FREEZER,
|
|
device_class=NumberDeviceClass.TEMPERATURE,
|
|
translation_key="freezer_setpoint_temperature",
|
|
),
|
|
NumberEntityDescription(
|
|
key=SettingKey.REFRIGERATION_COMMON_BOTTLE_COOLER_SETPOINT_TEMPERATURE,
|
|
device_class=NumberDeviceClass.TEMPERATURE,
|
|
translation_key="bottle_cooler_setpoint_temperature",
|
|
),
|
|
NumberEntityDescription(
|
|
key=SettingKey.REFRIGERATION_COMMON_CHILLER_LEFT_SETPOINT_TEMPERATURE,
|
|
device_class=NumberDeviceClass.TEMPERATURE,
|
|
translation_key="chiller_left_setpoint_temperature",
|
|
),
|
|
NumberEntityDescription(
|
|
key=SettingKey.REFRIGERATION_COMMON_CHILLER_COMMON_SETPOINT_TEMPERATURE,
|
|
device_class=NumberDeviceClass.TEMPERATURE,
|
|
translation_key="chiller_setpoint_temperature",
|
|
),
|
|
NumberEntityDescription(
|
|
key=SettingKey.REFRIGERATION_COMMON_CHILLER_RIGHT_SETPOINT_TEMPERATURE,
|
|
device_class=NumberDeviceClass.TEMPERATURE,
|
|
translation_key="chiller_right_setpoint_temperature",
|
|
),
|
|
NumberEntityDescription(
|
|
key=SettingKey.REFRIGERATION_COMMON_WINE_COMPARTMENT_SETPOINT_TEMPERATURE,
|
|
device_class=NumberDeviceClass.TEMPERATURE,
|
|
translation_key="wine_compartment_setpoint_temperature",
|
|
),
|
|
NumberEntityDescription(
|
|
key=SettingKey.REFRIGERATION_COMMON_WINE_COMPARTMENT_2_SETPOINT_TEMPERATURE,
|
|
device_class=NumberDeviceClass.TEMPERATURE,
|
|
translation_key="wine_compartment_2_setpoint_temperature",
|
|
),
|
|
NumberEntityDescription(
|
|
key=SettingKey.REFRIGERATION_COMMON_WINE_COMPARTMENT_3_SETPOINT_TEMPERATURE,
|
|
device_class=NumberDeviceClass.TEMPERATURE,
|
|
translation_key="wine_compartment_3_setpoint_temperature",
|
|
),
|
|
NumberEntityDescription(
|
|
key=SettingKey.COOKING_HOOD_COLOR_TEMPERATURE_PERCENT,
|
|
translation_key="color_temperature_percent",
|
|
native_unit_of_measurement=PERCENTAGE,
|
|
),
|
|
NumberEntityDescription(
|
|
key=SettingKey.LAUNDRY_CARE_WASHER_I_DOS_1_BASE_LEVEL,
|
|
device_class=NumberDeviceClass.VOLUME,
|
|
translation_key="washer_i_dos_1_base_level",
|
|
),
|
|
NumberEntityDescription(
|
|
key=SettingKey.LAUNDRY_CARE_WASHER_I_DOS_2_BASE_LEVEL,
|
|
device_class=NumberDeviceClass.VOLUME,
|
|
translation_key="washer_i_dos_2_base_level",
|
|
),
|
|
)
|
|
|
|
NUMBER_OPTIONS = (
|
|
NumberEntityDescription(
|
|
key=OptionKey.BSH_COMMON_DURATION,
|
|
translation_key="duration",
|
|
),
|
|
NumberEntityDescription(
|
|
key=OptionKey.BSH_COMMON_FINISH_IN_RELATIVE,
|
|
translation_key="finish_in_relative",
|
|
),
|
|
NumberEntityDescription(
|
|
key=OptionKey.BSH_COMMON_START_IN_RELATIVE,
|
|
translation_key="start_in_relative",
|
|
),
|
|
NumberEntityDescription(
|
|
key=OptionKey.CONSUMER_PRODUCTS_COFFEE_MAKER_FILL_QUANTITY,
|
|
translation_key="fill_quantity",
|
|
device_class=NumberDeviceClass.VOLUME,
|
|
native_step=1,
|
|
),
|
|
NumberEntityDescription(
|
|
key=OptionKey.COOKING_OVEN_SETPOINT_TEMPERATURE,
|
|
translation_key="setpoint_temperature",
|
|
device_class=NumberDeviceClass.TEMPERATURE,
|
|
),
|
|
)
|
|
|
|
|
|
def _get_entities_for_appliance(
|
|
entry: HomeConnectConfigEntry,
|
|
appliance: HomeConnectApplianceData,
|
|
) -> list[HomeConnectEntity]:
|
|
"""Get a list of entities."""
|
|
return [
|
|
HomeConnectNumberEntity(entry.runtime_data, appliance, description)
|
|
for description in NUMBERS
|
|
if description.key in appliance.settings
|
|
]
|
|
|
|
|
|
def _get_option_entities_for_appliance(
|
|
entry: HomeConnectConfigEntry,
|
|
appliance: HomeConnectApplianceData,
|
|
) -> list[HomeConnectOptionEntity]:
|
|
"""Get a list of currently available option entities."""
|
|
return [
|
|
HomeConnectOptionNumberEntity(entry.runtime_data, appliance, description)
|
|
for description in NUMBER_OPTIONS
|
|
if description.key in appliance.options
|
|
]
|
|
|
|
|
|
async def async_setup_entry(
|
|
hass: HomeAssistant,
|
|
entry: HomeConnectConfigEntry,
|
|
async_add_entities: AddConfigEntryEntitiesCallback,
|
|
) -> None:
|
|
"""Set up the Home Connect number."""
|
|
setup_home_connect_entry(
|
|
entry,
|
|
_get_entities_for_appliance,
|
|
async_add_entities,
|
|
_get_option_entities_for_appliance,
|
|
)
|
|
|
|
|
|
class HomeConnectNumberEntity(HomeConnectEntity, NumberEntity):
|
|
"""Number setting class for Home Connect."""
|
|
|
|
async def async_set_native_value(self, value: float) -> None:
|
|
"""Set the native value of the entity."""
|
|
_LOGGER.debug(
|
|
"Tried to set value %s to %s for %s",
|
|
value,
|
|
self.bsh_key,
|
|
self.entity_id,
|
|
)
|
|
try:
|
|
await self.coordinator.client.set_setting(
|
|
self.appliance.info.ha_id,
|
|
setting_key=SettingKey(self.bsh_key),
|
|
value=value,
|
|
)
|
|
except HomeConnectError as err:
|
|
raise HomeAssistantError(
|
|
translation_domain=DOMAIN,
|
|
translation_key="set_setting_entity",
|
|
translation_placeholders={
|
|
**get_dict_from_home_connect_error(err),
|
|
"entity_id": self.entity_id,
|
|
"key": self.bsh_key,
|
|
"value": str(value),
|
|
},
|
|
) from err
|
|
|
|
@constraint_fetcher
|
|
async def async_fetch_constraints(self) -> None:
|
|
"""Fetch the max and min values and step for the number entity."""
|
|
setting_key = cast(SettingKey, self.bsh_key)
|
|
data = self.appliance.settings.get(setting_key)
|
|
if not data or not data.unit or not data.constraints:
|
|
data = await self.coordinator.client.get_setting(
|
|
self.appliance.info.ha_id, setting_key=setting_key
|
|
)
|
|
if data.unit:
|
|
self._attr_native_unit_of_measurement = data.unit
|
|
self.set_constraints(data)
|
|
|
|
def set_constraints(self, setting: GetSetting) -> None:
|
|
"""Set constraints for the number entity."""
|
|
if setting.unit:
|
|
self._attr_native_unit_of_measurement = UNIT_MAP.get(
|
|
setting.unit, setting.unit
|
|
)
|
|
if not (constraints := setting.constraints):
|
|
return
|
|
if constraints.max:
|
|
self._attr_native_max_value = constraints.max
|
|
if constraints.min:
|
|
self._attr_native_min_value = constraints.min
|
|
if constraints.step_size:
|
|
self._attr_native_step = constraints.step_size
|
|
else:
|
|
self._attr_native_step = 0.1 if setting.type == "Double" else 1
|
|
|
|
def update_native_value(self) -> None:
|
|
"""Update status when an event for the entity is received."""
|
|
data = self.appliance.settings[cast(SettingKey, self.bsh_key)]
|
|
self._attr_native_value = cast(float, data.value)
|
|
|
|
async def async_added_to_hass(self) -> None:
|
|
"""When entity is added to hass."""
|
|
await super().async_added_to_hass()
|
|
data = self.appliance.settings[cast(SettingKey, self.bsh_key)]
|
|
self.set_constraints(data)
|
|
if (
|
|
not hasattr(self, "_attr_native_unit_of_measurement")
|
|
or not hasattr(self, "_attr_native_min_value")
|
|
or not hasattr(self, "_attr_native_max_value")
|
|
or not hasattr(self, "_attr_native_step")
|
|
):
|
|
await self.async_fetch_constraints()
|
|
|
|
|
|
class HomeConnectOptionNumberEntity(HomeConnectOptionEntity, NumberEntity):
|
|
"""Number option class for Home Connect."""
|
|
|
|
async def async_set_native_value(self, value: float) -> None:
|
|
"""Set the native value of the entity."""
|
|
await self.async_set_option(value)
|
|
|
|
def update_native_value(self) -> None:
|
|
"""Set the value of the entity."""
|
|
self._attr_native_value = cast(float | None, self.option_value)
|
|
option_definition = self.appliance.options.get(self.bsh_key)
|
|
if option_definition:
|
|
if option_definition.unit:
|
|
candidate_unit = UNIT_MAP.get(
|
|
option_definition.unit, option_definition.unit
|
|
)
|
|
if (
|
|
not hasattr(self, "_attr_native_unit_of_measurement")
|
|
or candidate_unit != self._attr_native_unit_of_measurement
|
|
):
|
|
self._attr_native_unit_of_measurement = candidate_unit
|
|
option_constraints = option_definition.constraints
|
|
if option_constraints:
|
|
if (
|
|
not hasattr(self, "_attr_native_min_value")
|
|
or self._attr_native_min_value != option_constraints.min
|
|
) and option_constraints.min:
|
|
self._attr_native_min_value = option_constraints.min
|
|
if (
|
|
not hasattr(self, "_attr_native_max_value")
|
|
or self._attr_native_max_value != option_constraints.max
|
|
) and option_constraints.max:
|
|
self._attr_native_max_value = option_constraints.max
|
|
if (
|
|
not hasattr(self, "_attr_native_step")
|
|
or self._attr_native_step != option_constraints.step_size
|
|
) and option_constraints.step_size:
|
|
self._attr_native_step = option_constraints.step_size
|