mirror of https://github.com/home-assistant/core
142 lines
4.6 KiB
Python
142 lines
4.6 KiB
Python
"""Support for TP-Link thermostats."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import logging
|
|
from typing import Any, cast
|
|
|
|
from kasa import Device, DeviceType
|
|
from kasa.smart.modules.temperaturecontrol import ThermostatState
|
|
|
|
from homeassistant.components.climate import (
|
|
ATTR_TEMPERATURE,
|
|
ClimateEntity,
|
|
ClimateEntityFeature,
|
|
HVACAction,
|
|
HVACMode,
|
|
)
|
|
from homeassistant.const import PRECISION_TENTHS
|
|
from homeassistant.core import HomeAssistant, callback
|
|
from homeassistant.exceptions import ServiceValidationError
|
|
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
|
|
|
from . import TPLinkConfigEntry
|
|
from .const import UNIT_MAPPING
|
|
from .coordinator import TPLinkDataUpdateCoordinator
|
|
from .entity import CoordinatedTPLinkEntity, async_refresh_after
|
|
|
|
# Upstream state to HVACAction
|
|
STATE_TO_ACTION = {
|
|
ThermostatState.Idle: HVACAction.IDLE,
|
|
ThermostatState.Heating: HVACAction.HEATING,
|
|
ThermostatState.Off: HVACAction.OFF,
|
|
}
|
|
|
|
|
|
_LOGGER = logging.getLogger(__name__)
|
|
|
|
|
|
async def async_setup_entry(
|
|
hass: HomeAssistant,
|
|
config_entry: TPLinkConfigEntry,
|
|
async_add_entities: AddEntitiesCallback,
|
|
) -> None:
|
|
"""Set up climate entities."""
|
|
data = config_entry.runtime_data
|
|
parent_coordinator = data.parent_coordinator
|
|
device = parent_coordinator.device
|
|
|
|
# As there are no standalone thermostats, we just iterate over the children.
|
|
async_add_entities(
|
|
TPLinkClimateEntity(child, parent_coordinator, parent=device)
|
|
for child in device.children
|
|
if child.device_type is DeviceType.Thermostat
|
|
)
|
|
|
|
|
|
class TPLinkClimateEntity(CoordinatedTPLinkEntity, ClimateEntity):
|
|
"""Representation of a TPLink thermostat."""
|
|
|
|
_attr_name = None
|
|
_attr_supported_features = (
|
|
ClimateEntityFeature.TARGET_TEMPERATURE
|
|
| ClimateEntityFeature.TURN_OFF
|
|
| ClimateEntityFeature.TURN_ON
|
|
)
|
|
_attr_hvac_modes = [HVACMode.HEAT, HVACMode.OFF]
|
|
_attr_precision = PRECISION_TENTHS
|
|
|
|
# This disables the warning for async_turn_{on,off}, can be removed later.
|
|
_enable_turn_on_off_backwards_compatibility = False
|
|
|
|
def __init__(
|
|
self,
|
|
device: Device,
|
|
coordinator: TPLinkDataUpdateCoordinator,
|
|
*,
|
|
parent: Device,
|
|
) -> None:
|
|
"""Initialize the climate entity."""
|
|
self._state_feature = device.features["state"]
|
|
self._mode_feature = device.features["thermostat_mode"]
|
|
self._temp_feature = device.features["temperature"]
|
|
self._target_feature = device.features["target_temperature"]
|
|
|
|
self._attr_min_temp = self._target_feature.minimum_value
|
|
self._attr_max_temp = self._target_feature.maximum_value
|
|
self._attr_temperature_unit = UNIT_MAPPING[cast(str, self._temp_feature.unit)]
|
|
|
|
super().__init__(device, coordinator, parent=parent)
|
|
|
|
@async_refresh_after
|
|
async def async_set_temperature(self, **kwargs: Any) -> None:
|
|
"""Set target temperature."""
|
|
await self._target_feature.set_value(int(kwargs[ATTR_TEMPERATURE]))
|
|
|
|
@async_refresh_after
|
|
async def async_set_hvac_mode(self, hvac_mode: HVACMode) -> None:
|
|
"""Set hvac mode (heat/off)."""
|
|
if hvac_mode is HVACMode.HEAT:
|
|
await self._state_feature.set_value(True)
|
|
elif hvac_mode is HVACMode.OFF:
|
|
await self._state_feature.set_value(False)
|
|
else:
|
|
raise ServiceValidationError(f"Tried to set unsupported mode: {hvac_mode}")
|
|
|
|
@async_refresh_after
|
|
async def async_turn_on(self) -> None:
|
|
"""Turn heating on."""
|
|
await self._state_feature.set_value(True)
|
|
|
|
@async_refresh_after
|
|
async def async_turn_off(self) -> None:
|
|
"""Turn heating off."""
|
|
await self._state_feature.set_value(False)
|
|
|
|
@callback
|
|
def _async_update_attrs(self) -> None:
|
|
"""Update the entity's attributes."""
|
|
self._attr_current_temperature = self._temp_feature.value
|
|
self._attr_target_temperature = self._target_feature.value
|
|
|
|
self._attr_hvac_mode = (
|
|
HVACMode.HEAT if self._state_feature.value else HVACMode.OFF
|
|
)
|
|
|
|
if (
|
|
self._mode_feature.value not in STATE_TO_ACTION
|
|
and self._attr_hvac_action is not HVACAction.OFF
|
|
):
|
|
_LOGGER.warning(
|
|
"Unknown thermostat state, defaulting to OFF: %s",
|
|
self._mode_feature.value,
|
|
)
|
|
self._attr_hvac_action = HVACAction.OFF
|
|
return
|
|
|
|
self._attr_hvac_action = STATE_TO_ACTION[self._mode_feature.value]
|
|
|
|
def _get_unique_id(self) -> str:
|
|
"""Return unique id."""
|
|
return f"{self._device.device_id}_climate"
|