"""Number platform for La Marzocco espresso machines.""" from collections.abc import Callable, Coroutine from dataclasses import dataclass from typing import Any, cast from pylamarzocco import LaMarzoccoMachine from pylamarzocco.const import ModelName, PreExtractionMode, WidgetType from pylamarzocco.exceptions import RequestNotSuccessful from pylamarzocco.models import CoffeeBoiler, PreBrewing from homeassistant.components.number import ( NumberDeviceClass, NumberEntity, NumberEntityDescription, ) from homeassistant.const import ( PRECISION_TENTHS, PRECISION_WHOLE, EntityCategory, UnitOfTemperature, UnitOfTime, ) from homeassistant.core import HomeAssistant from homeassistant.exceptions import HomeAssistantError from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback from .const import DOMAIN from .coordinator import LaMarzoccoConfigEntry from .entity import LaMarzoccoEntity, LaMarzoccoEntityDescription PARALLEL_UPDATES = 1 @dataclass(frozen=True, kw_only=True) class LaMarzoccoNumberEntityDescription( LaMarzoccoEntityDescription, NumberEntityDescription, ): """Description of a La Marzocco number entity.""" native_value_fn: Callable[[LaMarzoccoMachine], float | int] set_value_fn: Callable[[LaMarzoccoMachine, float | int], Coroutine[Any, Any, bool]] ENTITIES: tuple[LaMarzoccoNumberEntityDescription, ...] = ( LaMarzoccoNumberEntityDescription( key="coffee_temp", translation_key="coffee_temp", device_class=NumberDeviceClass.TEMPERATURE, native_unit_of_measurement=UnitOfTemperature.CELSIUS, native_step=PRECISION_TENTHS, native_min_value=85, native_max_value=104, set_value_fn=lambda machine, temp: machine.set_coffee_target_temperature(temp), native_value_fn=( lambda machine: cast( CoffeeBoiler, machine.dashboard.config[WidgetType.CM_COFFEE_BOILER] ).target_temperature ), ), LaMarzoccoNumberEntityDescription( key="smart_standby_time", translation_key="smart_standby_time", device_class=NumberDeviceClass.DURATION, native_unit_of_measurement=UnitOfTime.MINUTES, native_step=PRECISION_WHOLE, native_min_value=0, native_max_value=240, entity_category=EntityCategory.CONFIG, set_value_fn=( lambda machine, value: machine.set_smart_standby( enabled=machine.schedule.smart_wake_up_sleep.smart_stand_by_enabled, mode=machine.schedule.smart_wake_up_sleep.smart_stand_by_after, minutes=int(value), ) ), native_value_fn=lambda machine: machine.schedule.smart_wake_up_sleep.smart_stand_by_minutes, ), LaMarzoccoNumberEntityDescription( key="preinfusion_off", translation_key="preinfusion_time", device_class=NumberDeviceClass.DURATION, native_unit_of_measurement=UnitOfTime.SECONDS, native_step=PRECISION_TENTHS, native_min_value=0, native_max_value=10, entity_category=EntityCategory.CONFIG, set_value_fn=( lambda machine, value: machine.set_pre_extraction_times( seconds_on=0, seconds_off=float(value), ) ), native_value_fn=( lambda machine: cast( PreBrewing, machine.dashboard.config[WidgetType.CM_PRE_BREWING] ) .times.pre_infusion[0] .seconds.seconds_out ), available_fn=( lambda coordinator: cast( PreBrewing, coordinator.device.dashboard.config[WidgetType.CM_PRE_BREWING], ).mode is PreExtractionMode.PREINFUSION ), supported_fn=( lambda coordinator: coordinator.device.dashboard.model_name in ( ModelName.LINEA_MICRA, ModelName.LINEA_MINI, ModelName.LINEA_MINI_R, ) ), ), LaMarzoccoNumberEntityDescription( key="prebrew_on", translation_key="prebrew_time_on", device_class=NumberDeviceClass.DURATION, native_unit_of_measurement=UnitOfTime.SECONDS, native_step=PRECISION_TENTHS, native_min_value=0, native_max_value=10, entity_category=EntityCategory.CONFIG, set_value_fn=( lambda machine, value: machine.set_pre_extraction_times( seconds_on=float(value), seconds_off=cast( PreBrewing, machine.dashboard.config[WidgetType.CM_PRE_BREWING] ) .times.pre_brewing[0] .seconds.seconds_out, ) ), native_value_fn=( lambda machine: cast( PreBrewing, machine.dashboard.config[WidgetType.CM_PRE_BREWING] ) .times.pre_brewing[0] .seconds.seconds_in ), available_fn=lambda coordinator: cast( PreBrewing, coordinator.device.dashboard.config[WidgetType.CM_PRE_BREWING] ).mode is PreExtractionMode.PREBREWING, supported_fn=( lambda coordinator: coordinator.device.dashboard.model_name in ( ModelName.LINEA_MICRA, ModelName.LINEA_MINI, ModelName.LINEA_MINI_R, ) ), ), LaMarzoccoNumberEntityDescription( key="prebrew_off", translation_key="prebrew_time_off", device_class=NumberDeviceClass.DURATION, native_unit_of_measurement=UnitOfTime.SECONDS, native_step=PRECISION_TENTHS, native_min_value=0, native_max_value=10, entity_category=EntityCategory.CONFIG, set_value_fn=( lambda machine, value: machine.set_pre_extraction_times( seconds_on=cast( PreBrewing, machine.dashboard.config[WidgetType.CM_PRE_BREWING] ) .times.pre_brewing[0] .seconds.seconds_in, seconds_off=float(value), ) ), native_value_fn=( lambda machine: cast( PreBrewing, machine.dashboard.config[WidgetType.CM_PRE_BREWING] ) .times.pre_brewing[0] .seconds.seconds_out ), available_fn=( lambda coordinator: cast( PreBrewing, coordinator.device.dashboard.config[WidgetType.CM_PRE_BREWING], ).mode is PreExtractionMode.PREBREWING ), supported_fn=( lambda coordinator: coordinator.device.dashboard.model_name in ( ModelName.LINEA_MICRA, ModelName.LINEA_MINI, ModelName.LINEA_MINI_R, ) ), ), ) async def async_setup_entry( hass: HomeAssistant, entry: LaMarzoccoConfigEntry, async_add_entities: AddConfigEntryEntitiesCallback, ) -> None: """Set up number entities.""" coordinator = entry.runtime_data.config_coordinator entities: list[NumberEntity] = [ LaMarzoccoNumberEntity(coordinator, description) for description in ENTITIES if description.supported_fn(coordinator) ] async_add_entities(entities) class LaMarzoccoNumberEntity(LaMarzoccoEntity, NumberEntity): """La Marzocco number entity.""" entity_description: LaMarzoccoNumberEntityDescription @property def native_value(self) -> float | int: """Return the current value.""" return self.entity_description.native_value_fn(self.coordinator.device) async def async_set_native_value(self, value: float) -> None: """Set the value.""" if value != self.native_value: try: await self.entity_description.set_value_fn( self.coordinator.device, value ) except RequestNotSuccessful as exc: raise HomeAssistantError( translation_domain=DOMAIN, translation_key="number_exception", translation_placeholders={ "key": self.entity_description.key, "value": str(value), }, ) from exc self.async_write_ha_state()