core/homeassistant/components/comelit/climate.py

208 lines
5.8 KiB
Python

"""Support for climates."""
from __future__ import annotations
from enum import StrEnum
from typing import Any
from aiocomelit import ComelitSerialBridgeObject
from aiocomelit.const import CLIMATE
from homeassistant.components.climate import (
ClimateEntity,
ClimateEntityFeature,
HVACAction,
HVACMode,
UnitOfTemperature,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import ATTR_TEMPERATURE, PRECISION_TENTHS
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.update_coordinator import CoordinatorEntity
from .const import DOMAIN
from .coordinator import ComelitSerialBridge
class ClimaComelitMode(StrEnum):
"""Serial Bridge clima modes."""
AUTO = "A"
OFF = "O"
LOWER = "L"
UPPER = "U"
class ClimaComelitCommand(StrEnum):
"""Serial Bridge clima commands."""
OFF = "off"
ON = "on"
MANUAL = "man"
SET = "set"
AUTO = "auto"
API_STATUS: dict[str, dict[str, Any]] = {
ClimaComelitMode.OFF: {
"action": "off",
"hvac_mode": HVACMode.OFF,
"hvac_action": HVACAction.OFF,
},
ClimaComelitMode.LOWER: {
"action": "lower",
"hvac_mode": HVACMode.COOL,
"hvac_action": HVACAction.COOLING,
},
ClimaComelitMode.UPPER: {
"action": "upper",
"hvac_mode": HVACMode.HEAT,
"hvac_action": HVACAction.HEATING,
},
}
MODE_TO_ACTION: dict[HVACMode, ClimaComelitCommand] = {
HVACMode.OFF: ClimaComelitCommand.OFF,
HVACMode.AUTO: ClimaComelitCommand.AUTO,
HVACMode.COOL: ClimaComelitCommand.MANUAL,
HVACMode.HEAT: ClimaComelitCommand.MANUAL,
}
async def async_setup_entry(
hass: HomeAssistant,
config_entry: ConfigEntry,
async_add_entities: AddEntitiesCallback,
) -> None:
"""Set up Comelit climates."""
coordinator: ComelitSerialBridge = hass.data[DOMAIN][config_entry.entry_id]
async_add_entities(
ComelitClimateEntity(coordinator, device, config_entry.entry_id)
for device in coordinator.data[CLIMATE].values()
)
class ComelitClimateEntity(CoordinatorEntity[ComelitSerialBridge], ClimateEntity):
"""Climate device."""
_attr_hvac_modes = [HVACMode.AUTO, HVACMode.COOL, HVACMode.HEAT, HVACMode.OFF]
_attr_max_temp = 30
_attr_min_temp = 5
_attr_supported_features = (
ClimateEntityFeature.TARGET_TEMPERATURE
| ClimateEntityFeature.TURN_OFF
| ClimateEntityFeature.TURN_ON
)
_attr_target_temperature_step = PRECISION_TENTHS
_attr_temperature_unit = UnitOfTemperature.CELSIUS
_attr_has_entity_name = True
_attr_name = None
_enable_turn_on_off_backwards_compatibility = False
def __init__(
self,
coordinator: ComelitSerialBridge,
device: ComelitSerialBridgeObject,
config_entry_entry_id: str,
) -> None:
"""Init light entity."""
self._api = coordinator.api
self._device = device
super().__init__(coordinator)
# Use config_entry.entry_id as base for unique_id
# because no serial number or mac is available
self._attr_unique_id = f"{config_entry_entry_id}-{device.index}"
self._attr_device_info = coordinator.platform_device_info(device, device.type)
@property
def _clima(self) -> list[Any]:
"""Return clima device data."""
# CLIMATE has a 2 item tuple:
# - first for Clima
# - second for Humidifier
return self.coordinator.data[CLIMATE][self._device.index].val[0]
@property
def _api_mode(self) -> str:
"""Return device mode."""
# Values from API: "O", "L", "U"
return self._clima[2]
@property
def _api_active(self) -> bool:
"Return device active/idle."
return self._clima[1]
@property
def _api_automatic(self) -> bool:
"""Return device in automatic/manual mode."""
return self._clima[3] == ClimaComelitMode.AUTO
@property
def target_temperature(self) -> float:
"""Set target temperature."""
return self._clima[4] / 10
@property
def current_temperature(self) -> float:
"""Return current temperature."""
return self._clima[0] / 10
@property
def hvac_mode(self) -> HVACMode | None:
"""HVAC current mode."""
if self._api_mode == ClimaComelitMode.OFF:
return HVACMode.OFF
if self._api_automatic:
return HVACMode.AUTO
if self._api_mode in API_STATUS:
return API_STATUS[self._api_mode]["hvac_mode"]
return None
@property
def hvac_action(self) -> HVACAction | None:
"""HVAC current action."""
if self._api_mode == ClimaComelitMode.OFF:
return HVACAction.OFF
if not self._api_active:
return HVACAction.IDLE
if self._api_mode in API_STATUS:
return API_STATUS[self._api_mode]["hvac_action"]
return None
async def async_set_temperature(self, **kwargs: Any) -> None:
"""Set new target temperature."""
if (
target_temp := kwargs.get(ATTR_TEMPERATURE)
) is None or self.hvac_mode == HVACMode.OFF:
return
await self.coordinator.api.set_clima_status(
self._device.index, ClimaComelitCommand.MANUAL
)
await self.coordinator.api.set_clima_status(
self._device.index, ClimaComelitCommand.SET, target_temp
)
async def async_set_hvac_mode(self, hvac_mode: HVACMode) -> None:
"""Set hvac mode."""
if hvac_mode != HVACMode.OFF:
await self.coordinator.api.set_clima_status(
self._device.index, ClimaComelitCommand.ON
)
await self.coordinator.api.set_clima_status(
self._device.index, MODE_TO_ACTION[hvac_mode]
)