feat: add DknClimateEntity stub
This commit is contained in:
@@ -0,0 +1,175 @@
|
||||
"""Climate entity for DKN Cloud NA."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
from homeassistant.components.climate import (
|
||||
ClimateEntity,
|
||||
ClimateEntityFeature,
|
||||
HVACMode,
|
||||
)
|
||||
from homeassistant.const import ATTR_TEMPERATURE, PRECISION_WHOLE, UnitOfTemperature
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
|
||||
from .const import (
|
||||
DEVICE_MODE_AUTO,
|
||||
DEVICE_MODE_COOL,
|
||||
DEVICE_MODE_DRY,
|
||||
DEVICE_MODE_FAN,
|
||||
DEVICE_MODE_HEAT,
|
||||
DOMAIN,
|
||||
LOGGER,
|
||||
SPEED_20,
|
||||
SPEED_40,
|
||||
SPEED_60,
|
||||
SPEED_80,
|
||||
SPEED_100,
|
||||
SPEED_AUTO,
|
||||
)
|
||||
from .coordinator import DknCoordinator
|
||||
from .entity import DknEntity
|
||||
|
||||
# Map DKN device mode integers to HA HVACMode
|
||||
_MODE_TO_HVAC: dict[int, HVACMode] = {
|
||||
DEVICE_MODE_AUTO: HVACMode.AUTO,
|
||||
DEVICE_MODE_COOL: HVACMode.COOL,
|
||||
DEVICE_MODE_HEAT: HVACMode.HEAT,
|
||||
DEVICE_MODE_FAN: HVACMode.FAN_ONLY,
|
||||
DEVICE_MODE_DRY: HVACMode.DRY,
|
||||
}
|
||||
_HVAC_TO_MODE: dict[HVACMode, int] = {v: k for k, v in _MODE_TO_HVAC.items()}
|
||||
|
||||
# Fan speed labels
|
||||
_FAN_MODES = ["auto", "20%", "40%", "60%", "80%", "100%"]
|
||||
_SPEED_TO_FAN: dict[int, str] = {
|
||||
SPEED_AUTO: "auto",
|
||||
SPEED_20: "20%",
|
||||
SPEED_40: "40%",
|
||||
SPEED_60: "60%",
|
||||
SPEED_80: "80%",
|
||||
SPEED_100: "100%",
|
||||
}
|
||||
_FAN_TO_SPEED: dict[str, int] = {v: k for k, v in _SPEED_TO_FAN.items()}
|
||||
|
||||
# Modes where a temperature target makes no sense
|
||||
_NO_TARGET_TEMP_MODES = {HVACMode.FAN_ONLY, HVACMode.DRY, HVACMode.OFF}
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant,
|
||||
entry: ConfigEntry,
|
||||
async_add_entities: AddEntitiesCallback,
|
||||
) -> None:
|
||||
"""Set up climate entities from a config entry."""
|
||||
coordinator: DknCoordinator = hass.data[DOMAIN][entry.entry_id]["coordinator"]
|
||||
async_add_entities(
|
||||
DknClimateEntity(coordinator, mac)
|
||||
for mac in (coordinator.data or {})
|
||||
)
|
||||
|
||||
|
||||
class DknClimateEntity(DknEntity, ClimateEntity):
|
||||
"""Climate entity representing one DKN Cloud NA AC unit."""
|
||||
|
||||
_attr_name = None # entity name = device name
|
||||
_attr_temperature_unit = UnitOfTemperature.CELSIUS
|
||||
_attr_precision = PRECISION_WHOLE
|
||||
_attr_hvac_modes = [
|
||||
HVACMode.OFF,
|
||||
HVACMode.AUTO,
|
||||
HVACMode.COOL,
|
||||
HVACMode.HEAT,
|
||||
HVACMode.DRY,
|
||||
HVACMode.FAN_ONLY,
|
||||
]
|
||||
_attr_fan_modes = _FAN_MODES
|
||||
_attr_swing_modes = ["off", "swing"]
|
||||
_attr_min_temp = 16
|
||||
_attr_max_temp = 32
|
||||
_attr_target_temperature_step = 1
|
||||
|
||||
def __init__(self, coordinator: DknCoordinator, mac: str) -> None:
|
||||
super().__init__(coordinator, mac)
|
||||
self._attr_unique_id = f"{DOMAIN}_{mac}"
|
||||
|
||||
@property
|
||||
def supported_features(self) -> ClimateEntityFeature:
|
||||
"""Return feature flags appropriate for the current HVAC mode."""
|
||||
features = ClimateEntityFeature.TURN_ON | ClimateEntityFeature.TURN_OFF
|
||||
mode = self.hvac_mode
|
||||
if mode not in _NO_TARGET_TEMP_MODES:
|
||||
features |= ClimateEntityFeature.TARGET_TEMPERATURE
|
||||
features |= ClimateEntityFeature.FAN_MODE
|
||||
features |= ClimateEntityFeature.SWING_MODE
|
||||
return features
|
||||
|
||||
@property
|
||||
def hvac_mode(self) -> HVACMode:
|
||||
data = self._device_data
|
||||
if not data.get("power", False):
|
||||
return HVACMode.OFF
|
||||
mode_int = data.get("real_mode") or data.get("mode", DEVICE_MODE_AUTO)
|
||||
return _MODE_TO_HVAC.get(int(mode_int), HVACMode.AUTO)
|
||||
|
||||
@property
|
||||
def current_temperature(self) -> float | None:
|
||||
return self._device_data.get("work_temp")
|
||||
|
||||
@property
|
||||
def target_temperature(self) -> float | None:
|
||||
if self.hvac_mode in _NO_TARGET_TEMP_MODES:
|
||||
return None
|
||||
data = self._device_data
|
||||
mode = data.get("real_mode") or data.get("mode", DEVICE_MODE_AUTO)
|
||||
if int(mode) == DEVICE_MODE_HEAT:
|
||||
return self._optimistic_get("target_temp", data.get("setpoint_air_heat"))
|
||||
if int(mode) == DEVICE_MODE_COOL:
|
||||
return self._optimistic_get("target_temp", data.get("setpoint_air_cool"))
|
||||
return self._optimistic_get("target_temp", data.get("setpoint_air_auto"))
|
||||
|
||||
@property
|
||||
def target_temperature_high(self) -> float | None:
|
||||
return None # no range mode
|
||||
|
||||
@property
|
||||
def target_temperature_low(self) -> float | None:
|
||||
return None
|
||||
|
||||
@property
|
||||
def fan_mode(self) -> str | None:
|
||||
speed = self._device_data.get("speed_state", SPEED_AUTO)
|
||||
return self._optimistic_get("fan_mode", _SPEED_TO_FAN.get(int(speed), "auto"))
|
||||
|
||||
@property
|
||||
def swing_mode(self) -> str | None:
|
||||
slat = self._device_data.get("slats_vertical_1", 0)
|
||||
return self._optimistic_get("swing_mode", "swing" if int(slat) == 9 else "off")
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Service handlers — all stub (raise NotImplementedError for now)
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
async def async_set_hvac_mode(self, hvac_mode: HVACMode) -> None:
|
||||
LOGGER.debug("set_hvac_mode(%s) stub for %s", hvac_mode, self._mac)
|
||||
raise NotImplementedError("Device control not yet implemented")
|
||||
|
||||
async def async_set_temperature(self, **kwargs: Any) -> None:
|
||||
LOGGER.debug("set_temperature(%s) stub for %s", kwargs, self._mac)
|
||||
raise NotImplementedError("Device control not yet implemented")
|
||||
|
||||
async def async_set_fan_mode(self, fan_mode: str) -> None:
|
||||
LOGGER.debug("set_fan_mode(%s) stub for %s", fan_mode, self._mac)
|
||||
raise NotImplementedError("Device control not yet implemented")
|
||||
|
||||
async def async_set_swing_mode(self, swing_mode: str) -> None:
|
||||
LOGGER.debug("set_swing_mode(%s) stub for %s", swing_mode, self._mac)
|
||||
raise NotImplementedError("Device control not yet implemented")
|
||||
|
||||
async def async_turn_on(self) -> None:
|
||||
await self.async_set_hvac_mode(HVACMode.AUTO)
|
||||
|
||||
async def async_turn_off(self) -> None:
|
||||
await self.async_set_hvac_mode(HVACMode.OFF)
|
||||
Reference in New Issue
Block a user