From b4a6ccfbc8f9107e160cf88d3e8227d4f0324060 Mon Sep 17 00:00:00 2001 From: Matrix Date: Fri, 10 Jun 2022 20:18:46 +0800 Subject: [PATCH] Add yolink thermostat support (#73243) * Add yolink thermostat support * suggest and bugs fix * fix suggest and bugs --- .coveragerc | 1 + homeassistant/components/yolink/__init__.py | 1 + homeassistant/components/yolink/climate.py | 135 ++++++++++++++++++++ homeassistant/components/yolink/const.py | 1 + 4 files changed, 138 insertions(+) create mode 100644 homeassistant/components/yolink/climate.py diff --git a/.coveragerc b/.coveragerc index ea2c41f7e46..ef1a2adb712 100644 --- a/.coveragerc +++ b/.coveragerc @@ -1496,6 +1496,7 @@ omit = homeassistant/components/yolink/__init__.py homeassistant/components/yolink/api.py homeassistant/components/yolink/binary_sensor.py + homeassistant/components/yolink/climate.py homeassistant/components/yolink/const.py homeassistant/components/yolink/coordinator.py homeassistant/components/yolink/entity.py diff --git a/homeassistant/components/yolink/__init__.py b/homeassistant/components/yolink/__init__.py index 21d36d33a30..92068d1e26e 100644 --- a/homeassistant/components/yolink/__init__.py +++ b/homeassistant/components/yolink/__init__.py @@ -26,6 +26,7 @@ SCAN_INTERVAL = timedelta(minutes=5) PLATFORMS = [ Platform.BINARY_SENSOR, + Platform.CLIMATE, Platform.LOCK, Platform.SENSOR, Platform.SIREN, diff --git a/homeassistant/components/yolink/climate.py b/homeassistant/components/yolink/climate.py new file mode 100644 index 00000000000..1f877571d94 --- /dev/null +++ b/homeassistant/components/yolink/climate.py @@ -0,0 +1,135 @@ +"""YoLink Thermostat.""" +from __future__ import annotations + +from typing import Any + +from homeassistant.components.climate import ClimateEntity, ClimateEntityFeature +from homeassistant.components.climate.const import ( + ATTR_TARGET_TEMP_HIGH, + ATTR_TARGET_TEMP_LOW, + FAN_AUTO, + FAN_ON, + PRESET_ECO, + PRESET_NONE, + HVACAction, + HVACMode, +) +from homeassistant.config_entries import ConfigEntry +from homeassistant.const import TEMP_CELSIUS +from homeassistant.core import HomeAssistant, callback +from homeassistant.helpers.entity_platform import AddEntitiesCallback + +from .const import ATTR_COORDINATORS, ATTR_DEVICE_THERMOSTAT, DOMAIN +from .coordinator import YoLinkCoordinator +from .entity import YoLinkEntity + +YOLINK_MODEL_2_HA = { + "cool": HVACMode.COOL, + "heat": HVACMode.HEAT, + "auto": HVACMode.AUTO, + "off": HVACMode.OFF, +} + +HA_MODEL_2_YOLINK = {v: k for k, v in YOLINK_MODEL_2_HA.items()} + +YOLINK_ACTION_2_HA = { + "cool": HVACAction.COOLING, + "heat": HVACAction.HEATING, + "idle": HVACAction.IDLE, +} + + +async def async_setup_entry( + hass: HomeAssistant, + config_entry: ConfigEntry, + async_add_entities: AddEntitiesCallback, +) -> None: + """Set up YoLink Thermostat from a config entry.""" + device_coordinators = hass.data[DOMAIN][config_entry.entry_id][ATTR_COORDINATORS] + entities = [ + YoLinkClimateEntity(config_entry, device_coordinator) + for device_coordinator in device_coordinators.values() + if device_coordinator.device.device_type == ATTR_DEVICE_THERMOSTAT + ] + async_add_entities(entities) + + +class YoLinkClimateEntity(YoLinkEntity, ClimateEntity): + """YoLink Climate Entity.""" + + def __init__( + self, + config_entry: ConfigEntry, + coordinator: YoLinkCoordinator, + ) -> None: + """Init YoLink Thermostat.""" + super().__init__(config_entry, coordinator) + self._attr_unique_id = f"{coordinator.device.device_id}_climate" + self._attr_name = f"{coordinator.device.device_name} (Thermostat)" + self._attr_temperature_unit = TEMP_CELSIUS + self._attr_fan_modes = [FAN_ON, FAN_AUTO] + self._attr_min_temp = -10 + self._attr_max_temp = 50 + self._attr_hvac_modes = [ + HVACMode.COOL, + HVACMode.HEAT, + HVACMode.AUTO, + HVACMode.OFF, + ] + self._attr_preset_modes = [PRESET_NONE, PRESET_ECO] + self._attr_supported_features = ( + ClimateEntityFeature.FAN_MODE + | ClimateEntityFeature.PRESET_MODE + | ClimateEntityFeature.TARGET_TEMPERATURE_RANGE + ) + + @callback + def update_entity_state(self, state: dict[str, Any]) -> None: + """Update HA Entity State.""" + normal_state = state.get("state") + if normal_state is not None: + self._attr_current_temperature = normal_state.get("temperature") + self._attr_current_humidity = normal_state.get("humidity") + self._attr_target_temperature_low = normal_state.get("lowTemp") + self._attr_target_temperature_high = normal_state.get("highTemp") + self._attr_fan_mode = normal_state.get("fan") + self._attr_hvac_mode = YOLINK_MODEL_2_HA.get(normal_state.get("mode")) + self._attr_hvac_action = YOLINK_ACTION_2_HA.get(normal_state.get("running")) + eco_setting = state.get("eco") + if eco_setting is not None: + self._attr_preset_mode = ( + PRESET_NONE if eco_setting.get("mode") == "on" else PRESET_ECO + ) + self.async_write_ha_state() + + async def async_set_hvac_mode(self, hvac_mode: HVACMode) -> None: + """Set new target hvac mode.""" + if (hvac_mode_id := HA_MODEL_2_YOLINK.get(hvac_mode)) is None: + raise ValueError(f"Received an invalid hvac mode: {hvac_mode}") + await self.call_device_api("setState", {"mode": hvac_mode_id}) + await self.coordinator.async_refresh() + + async def async_set_fan_mode(self, fan_mode: str) -> None: + """Set fan mode.""" + await self.call_device_api("setState", {"fan": fan_mode}) + self._attr_fan_mode = fan_mode + self.async_write_ha_state() + + async def async_set_temperature(self, **kwargs) -> None: + """Set temperature.""" + target_temp_low = kwargs.get(ATTR_TARGET_TEMP_LOW) + target_temp_high = kwargs.get(ATTR_TARGET_TEMP_HIGH) + if target_temp_low is not None: + await self.call_device_api("setState", {"lowTemp": target_temp_low}) + self._attr_target_temperature_low = target_temp_low + if target_temp_high is not None: + await self.call_device_api("setState", {"highTemp": target_temp_high}) + self._attr_target_temperature_high = target_temp_high + await self.coordinator.async_refresh() + + async def async_set_preset_mode(self, preset_mode: str) -> None: + """Set preset mode.""" + eco_params = "on" if preset_mode == PRESET_ECO else "off" + await self.call_device_api("setECO", {"mode": eco_params}) + self._attr_preset_mode = PRESET_ECO if eco_params == "on" else PRESET_NONE + self.async_write_ha_state() diff --git a/homeassistant/components/yolink/const.py b/homeassistant/components/yolink/const.py index dba0a0ee221..f6add984dc2 100644 --- a/homeassistant/components/yolink/const.py +++ b/homeassistant/components/yolink/const.py @@ -24,3 +24,4 @@ ATTR_DEVICE_LOCK = "Lock" ATTR_DEVICE_MANIPULATOR = "Manipulator" ATTR_DEVICE_CO_SMOKE_SENSOR = "COSmokeSensor" ATTR_DEVICE_SWITCH = "Switch" +ATTR_DEVICE_THERMOSTAT = "Thermostat"