from homeassistant.components.climate import * from homeassistant.components.climate.const import * from homeassistant.const import ( Platform, TEMP_CELSIUS, PRECISION_WHOLE, PRECISION_HALVES, ATTR_TEMPERATURE, CONF_DEVICE_ID, CONF_SWITCHES ) from .const import ( DOMAIN, DEVICES, ) from .midea.devices.ac.device import DeviceAttributes as ACAttributes from .midea.devices.c3.device import DeviceAttributes as C3Attributes from .midea.devices.cc.device import DeviceAttributes as CCAttributes from .midea.devices.cf.device import DeviceAttributes as CFAttributes from .midea.devices.fb.device import DeviceAttributes as FBAttributes from .midea_devices import MIDEA_DEVICES from .midea_entity import MideaEntity _LOGGER = logging.getLogger(__name__) TEMPERATURE_MAX = 30 TEMPERATURE_MIN = 17 FAN_SILENT = "Silent" FAN_FULL_SPEED = "Full" async def async_setup_entry(hass, config_entry, async_add_entities): device_id = config_entry.data.get(CONF_DEVICE_ID) device = hass.data[DOMAIN][DEVICES].get(device_id) extra_switches = config_entry.options.get( CONF_SWITCHES, [] ) devs = [] for entity_key, config in MIDEA_DEVICES[device.device_type]["entities"].items(): if config["type"] == Platform.CLIMATE and (config.get("default") or entity_key in extra_switches): if device.device_type == 0xAC: devs.append(MideaACClimate(device, entity_key)) elif device.device_type == 0xCC: devs.append(MideaCCClimate(device, entity_key)) elif device.device_type == 0xCF: devs.append(MideaCFClimate(device, entity_key)) elif device.device_type == 0xC3: devs.append(MideaC3Climate(device, entity_key, config["zone"])) elif device.device_type == 0xFB: devs.append(MideaFBClimate(device, entity_key)) async_add_entities(devs) class MideaClimate(MideaEntity, ClimateEntity): def __init__(self, device, entity_key): super().__init__(device, entity_key) @property def state(self): return self.hvac_mode @property def supported_features(self): return ClimateEntityFeature.TARGET_TEMPERATURE | \ ClimateEntityFeature.FAN_MODE | \ ClimateEntityFeature.PRESET_MODE | \ ClimateEntityFeature.SWING_MODE | \ ClimateEntityFeature.AUX_HEAT @property def min_temp(self): return TEMPERATURE_MIN @property def max_temp(self): return TEMPERATURE_MAX @property def temperature_unit(self): return TEMP_CELSIUS @property def target_temperature_low(self): return TEMPERATURE_MIN @property def target_temperature_high(self): return TEMPERATURE_MAX @property def hvac_modes(self): return self._modes @property def swing_modes(self): return self._swing_modes @property def is_on(self) -> bool: return self.hvac_mode != HVACMode.OFF @property def hvac_mode(self) -> str: if self._device.get_attribute("power"): return self._modes[self._device.get_attribute("mode")] else: return HVACMode.OFF @property def target_temperature(self): return self._device.get_attribute("target_temperature") @property def current_temperature(self): return self._device.get_attribute("indoor_temperature") @property def is_aux_heat(self): return self._device.get_attribute("aux_heating") @property def preset_modes(self): return self._preset_modes @property def preset_mode(self): if self._device.get_attribute("comfort_mode"): mode = PRESET_COMFORT elif self._device.get_attribute("eco_mode"): mode = PRESET_ECO elif self._device.get_attribute("boost_mode"): mode = PRESET_BOOST elif self._device.get_attribute("sleep_mode"): mode = PRESET_SLEEP elif self._device.get_attribute("frost_protect"): mode = PRESET_AWAY else: mode = PRESET_NONE return mode @property def extra_state_attributes(self) -> dict: return self._device.attributes def turn_on(self): self._device.set_attribute(attr="power", value=True) def turn_off(self): self._device.set_attribute(attr="power", value=False) def set_temperature(self, **kwargs) -> None: if ATTR_TEMPERATURE not in kwargs: return temperature = float(int((float(kwargs.get(ATTR_TEMPERATURE)) * 2) + 0.5)) / 2 hvac_mode = kwargs.get(ATTR_HVAC_MODE) if hvac_mode == HVACMode.OFF: self.turn_off() else: try: mode = self._modes.index(hvac_mode.lower()) if hvac_mode else None self._device.set_target_temperature( target_temperature=temperature, mode=mode) except ValueError as e: _LOGGER.error(f"set_temperature {e}, kwargs = {kwargs}") def set_hvac_mode(self, hvac_mode: str) -> None: hvac_mode = hvac_mode.lower() if hvac_mode == HVACMode.OFF: self.turn_off() else: self._device.set_attribute(attr="mode", value=self._modes.index(hvac_mode)) def set_preset_mode(self, preset_mode: str) -> None: old_mode = self.preset_mode preset_mode = preset_mode.lower() if preset_mode == PRESET_AWAY: self._device.set_attribute(attr="frost_protect", value=True) elif preset_mode == PRESET_COMFORT: self._device.set_attribute(attr="comfort_mode", value=True) elif preset_mode == PRESET_SLEEP: self._device.set_attribute(attr="sleep_mode", value=True) elif preset_mode == PRESET_ECO: self._device.set_attribute(attr="eco_mode", value=True) elif preset_mode == PRESET_BOOST: self._device.set_attribute(attr="boost_mode", value=True) elif old_mode == PRESET_AWAY: self._device.set_attribute(attr="frost_protect", value=False) elif old_mode == PRESET_COMFORT: self._device.set_attribute(attr="comfort_mode", value=False) elif old_mode == PRESET_SLEEP: self._device.set_attribute(attr="sleep_mode", value=False) elif old_mode == PRESET_ECO: self._device.set_attribute(attr="eco_mode", value=False) elif old_mode == PRESET_BOOST: self._device.set_attribute(attr="boost_mode", value=False) def update_state(self, status): try: self.schedule_update_ha_state() except Exception as e: _LOGGER.debug(f"Entity {self.entity_id} update_state {repr(e)}, status = {status}") def turn_aux_heat_on(self) -> None: self._device.set_attribute(attr="aux_heating", value=True) def turn_aux_heat_off(self) -> None: self._device.set_attribute(attr="aux_heating", value=False) class MideaACClimate(MideaClimate): def __init__(self, device, entity_key): super().__init__(device, entity_key) self._modes = [HVACMode.OFF, HVACMode.AUTO, HVACMode.COOL, HVACMode.DRY, HVACMode.HEAT, HVACMode.FAN_ONLY] self._fan_speeds = { FAN_SILENT.capitalize(): 20, FAN_LOW.capitalize(): 40, FAN_MEDIUM.capitalize(): 60, FAN_HIGH.capitalize(): 80, FAN_FULL_SPEED.capitalize(): 100, FAN_AUTO.capitalize(): 102 } self._swing_modes = [ SWING_OFF.capitalize(), SWING_VERTICAL.capitalize(), SWING_HORIZONTAL.capitalize(), SWING_BOTH.capitalize() ] self._preset_modes = [PRESET_NONE, PRESET_COMFORT, PRESET_ECO, PRESET_BOOST, PRESET_SLEEP, PRESET_AWAY] @property def fan_modes(self): return list(self._fan_speeds.keys()) @property def fan_mode(self) -> str: fan_speed = self._device.get_attribute(ACAttributes.fan_speed) if fan_speed > 100: return FAN_AUTO.capitalize() elif fan_speed > 80: return FAN_FULL_SPEED.capitalize() elif fan_speed > 60: return FAN_HIGH.capitalize() elif fan_speed > 40: return FAN_MEDIUM.capitalize() elif fan_speed > 20: return FAN_LOW.capitalize() else: return FAN_SILENT.capitalize() @property def target_temperature_step(self): return PRECISION_WHOLE if self._device.temperature_step == 1 else PRECISION_HALVES @property def swing_mode(self): swing_mode = (1 if self._device.get_attribute(ACAttributes.swing_vertical) else 0) + \ (2 if self._device.get_attribute(ACAttributes.swing_horizontal) else 0) return self._swing_modes[swing_mode] @property def outdoor_temperature(self): return self._device.get_attribute(ACAttributes.outdoor_temperature) def set_fan_mode(self, fan_mode: str) -> None: fan_speed = self._fan_speeds.get(fan_mode.capitalize()) if fan_speed: self._device.set_attribute(attr=ACAttributes.fan_speed, value=fan_speed) def set_swing_mode(self, swing_mode: str) -> None: swing = self._swing_modes.index(swing_mode.capitalize()) swing_vertical = swing & 1 > 0 swing_horizontal = swing & 2 > 0 self._device.set_swing(swing_vertical=swing_vertical, swing_horizontal=swing_horizontal) class MideaCCClimate(MideaClimate): def __init__(self, device, entity_key): super().__init__(device, entity_key) self._modes = [HVACMode.OFF, HVACMode.FAN_ONLY, HVACMode.DRY, HVACMode.HEAT, HVACMode.COOL, HVACMode.AUTO] self._swing_modes = [ SWING_OFF.capitalize(), SWING_ON.capitalize() ] self._preset_modes = [PRESET_NONE, PRESET_SLEEP, PRESET_ECO] @property def fan_modes(self): return self._device.fan_modes @property def fan_mode(self) -> str: return self._device.get_attribute(CCAttributes.fan_speed) @property def target_temperature_step(self): return self._device.get_attribute(CCAttributes.temperature_precision) @property def swing_mode(self): return SWING_ON.capitalize() if self._device.get_attribute(CCAttributes.swing) else SWING_OFF.capitalize() def set_fan_mode(self, fan_mode: str) -> None: self._device.set_attribute(attr=CCAttributes.fan_speed, value=fan_mode) def set_swing_mode(self, swing_mode: str) -> None: self._device.set_attribute( attr=CCAttributes.swing, value=swing_mode.capitalize() == SWING_ON.capitalize() ) class MideaCFClimate(MideaClimate): def __init__(self, device, entity_key): super().__init__(device, entity_key) self._modes = [HVACMode.OFF, HVACMode.AUTO, HVACMode.COOL, HVACMode.HEAT] @property def supported_features(self): return ClimateEntityFeature.TARGET_TEMPERATURE | ClimateEntityFeature.AUX_HEAT @property def target_temperature_step(self): return PRECISION_WHOLE @property def min_temp(self): return self._device.get_attribute(CFAttributes.min_temperature) @property def max_temp(self): return self._device.get_attribute(CFAttributes.max_temperature) @property def target_temperature_low(self): return self._device.get_attribute(CFAttributes.min_temperature) @property def target_temperature_high(self): return self._device.get_attribute(CFAttributes.max_temperature) @property def current_temperature(self): return self._device.get_attribute(CFAttributes.current_temperature) class MideaC3Climate(MideaClimate): _powers = [ C3Attributes.zone1_power, C3Attributes.zone2_power, ] def __init__(self, device, entity_key, zone): super().__init__(device, entity_key) self._zone = zone self._modes = [HVACMode.OFF, HVACMode.AUTO, HVACMode.COOL, HVACMode.HEAT] self._power_attr = MideaC3Climate._powers[self._zone] @property def supported_features(self): return ClimateEntityFeature.TARGET_TEMPERATURE @property def target_temperature_step(self): return PRECISION_WHOLE if \ self._device.get_attribute(C3Attributes.zone_temp_type)[self._zone] else PRECISION_HALVES @property def min_temp(self): return self._device.get_attribute(C3Attributes.temperature_min)[self._zone] @property def max_temp(self): return self._device.get_attribute(C3Attributes.temperature_max)[self._zone] @property def target_temperature_low(self): return self._device.get_attribute(C3Attributes.temperature_min)[self._zone] @property def target_temperature_high(self): return self._device.get_attribute(C3Attributes.temperature_max)[self._zone] def turn_on(self): self._device.set_attribute(attr=self._power_attr, value=True) def turn_off(self): self._device.set_attribute(attr=self._power_attr, value=False) @property def hvac_mode(self) -> str: if self._device.get_attribute(self._power_attr): return self._modes[self._device.get_attribute(C3Attributes.mode)] else: return HVACMode.OFF @property def target_temperature(self): return self._device.get_attribute(C3Attributes.target_temperature)[self._zone] @property def current_temperature(self): return None def set_temperature(self, **kwargs) -> None: if ATTR_TEMPERATURE not in kwargs: return temperature = float(int((float(kwargs.get(ATTR_TEMPERATURE)) * 2) + 0.5)) / 2 hvac_mode = kwargs.get(ATTR_HVAC_MODE) if hvac_mode == HVACMode.OFF: self.turn_off() else: try: mode = self._modes.index(hvac_mode.lower()) if hvac_mode else None self._device.set_target_temperature( zone=self._zone, target_temperature=temperature, mode=mode) except ValueError as e: _LOGGER.error(f"set_temperature {e}, kwargs = {kwargs}") def set_hvac_mode(self, hvac_mode: str) -> None: hvac_mode = hvac_mode.lower() if hvac_mode == HVACMode.OFF: self.turn_off() else: self._device.set_mode(self._zone, self._modes.index(hvac_mode)) class MideaFBClimate(MideaClimate): def __init__(self, device, entity_key): super().__init__(device, entity_key) self._modes = [HVACMode.OFF, HVACMode.HEAT] self._preset_modes = self._device.modes @property def supported_features(self): return ClimateEntityFeature.TARGET_TEMPERATURE | ClimateEntityFeature.PRESET_MODE @property def target_temperature_step(self): return PRECISION_WHOLE @property def preset_modes(self): return self._preset_modes @property def preset_mode(self): return self._device.get_attribute(attr=FBAttributes.mode) @property def min_temp(self): return 5 @property def max_temp(self): return 35 @property def target_temperature_low(self): return 5 @property def target_temperature_high(self): return 35 @property def hvac_mode(self) -> str: return HVACMode.HEAT if self._device.get_attribute(attr=FBAttributes.power) else HVACMode.OFF @property def current_temperature(self): return self._device.get_attribute(FBAttributes.current_temperature) def set_temperature(self, **kwargs) -> None: if ATTR_TEMPERATURE not in kwargs: return temperature = float(int((float(kwargs.get(ATTR_TEMPERATURE)) * 2) + 0.5)) / 2 hvac_mode = kwargs.get(ATTR_HVAC_MODE) if hvac_mode == HVACMode.OFF: self.turn_off() else: self._device.set_attribute(attr=FBAttributes.target_temperature, value=temperature) def set_hvac_mode(self, hvac_mode: str) -> None: hvac_mode = hvac_mode.lower() if hvac_mode == HVACMode.OFF: self.turn_off() else: self.turn_on() def set_preset_mode(self, preset_mode: str) -> None: self._device.set_attribute(attr=FBAttributes.mode,value=preset_mode)