2023-04-26 21:57:44 +00:00
|
|
|
import logging
|
2023-05-28 05:50:59 +00:00
|
|
|
from dataclasses import dataclass
|
2023-04-26 21:57:44 +00:00
|
|
|
|
|
|
|
from homeassistant.components.climate import (
|
|
|
|
ClimateEntity,
|
|
|
|
ClimateEntityDescription,
|
|
|
|
)
|
|
|
|
from homeassistant.components.climate.const import (
|
|
|
|
FAN_OFF,
|
|
|
|
SWING_OFF,
|
|
|
|
SWING_BOTH,
|
|
|
|
SWING_VERTICAL,
|
|
|
|
SWING_HORIZONTAL,
|
|
|
|
ClimateEntityFeature,
|
|
|
|
HVACMode,
|
|
|
|
)
|
|
|
|
from homeassistant.config_entries import ConfigEntry
|
|
|
|
from homeassistant.const import (
|
|
|
|
ATTR_TEMPERATURE,
|
|
|
|
TEMP_CELSIUS,
|
|
|
|
)
|
|
|
|
from homeassistant.core import callback
|
2023-06-08 17:59:43 +00:00
|
|
|
from pyhon.appliance import HonAppliance
|
|
|
|
|
2023-06-21 17:52:32 +00:00
|
|
|
from .const import HON_HVAC_MODE, HON_FAN, DOMAIN
|
2023-05-24 23:30:33 +00:00
|
|
|
from .hon import HonEntity
|
2023-04-26 21:57:44 +00:00
|
|
|
|
|
|
|
_LOGGER = logging.getLogger(__name__)
|
|
|
|
|
2023-05-28 05:50:59 +00:00
|
|
|
|
|
|
|
@dataclass
|
|
|
|
class HonACClimateEntityDescription(ClimateEntityDescription):
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
@dataclass
|
2023-05-28 15:38:56 +00:00
|
|
|
class HonClimateEntityDescription(ClimateEntityDescription):
|
|
|
|
mode: HVACMode = "auto"
|
2023-05-28 05:50:59 +00:00
|
|
|
|
|
|
|
|
2023-04-26 21:57:44 +00:00
|
|
|
CLIMATES = {
|
2023-05-10 16:13:05 +00:00
|
|
|
"AC": (
|
2023-05-28 05:50:59 +00:00
|
|
|
HonACClimateEntityDescription(
|
2023-05-10 16:13:05 +00:00
|
|
|
key="settings",
|
|
|
|
name="Air Conditioner",
|
|
|
|
icon="mdi:air-conditioner",
|
|
|
|
translation_key="air_conditioner",
|
|
|
|
),
|
|
|
|
),
|
2023-05-28 05:50:59 +00:00
|
|
|
"REF": (
|
2023-05-28 15:38:56 +00:00
|
|
|
HonClimateEntityDescription(
|
2023-05-28 05:50:59 +00:00
|
|
|
key="settings.tempSelZ1",
|
2023-05-28 15:38:56 +00:00
|
|
|
mode=HVACMode.COOL,
|
2023-05-28 05:50:59 +00:00
|
|
|
name="Fridge",
|
|
|
|
icon="mdi:thermometer",
|
|
|
|
translation_key="fridge",
|
|
|
|
),
|
2023-05-28 15:38:56 +00:00
|
|
|
HonClimateEntityDescription(
|
2023-05-28 05:50:59 +00:00
|
|
|
key="settings.tempSelZ2",
|
2023-05-28 15:38:56 +00:00
|
|
|
mode=HVACMode.COOL,
|
2023-05-28 05:50:59 +00:00
|
|
|
name="Freezer",
|
|
|
|
icon="mdi:snowflake-thermometer",
|
|
|
|
translation_key="freezer",
|
|
|
|
),
|
|
|
|
),
|
2023-05-28 15:38:56 +00:00
|
|
|
"OV": (
|
|
|
|
HonClimateEntityDescription(
|
|
|
|
key="settings.tempSel",
|
|
|
|
mode=HVACMode.HEAT,
|
|
|
|
name="Oven",
|
|
|
|
icon="mdi:thermometer",
|
|
|
|
translation_key="oven",
|
|
|
|
),
|
|
|
|
),
|
2023-06-10 04:44:19 +00:00
|
|
|
"WC": (
|
|
|
|
HonClimateEntityDescription(
|
|
|
|
key="settings.tempSel",
|
|
|
|
mode=HVACMode.COOL,
|
|
|
|
name="Wine Cellar",
|
|
|
|
icon="mdi:thermometer",
|
|
|
|
translation_key="wine",
|
|
|
|
),
|
|
|
|
HonClimateEntityDescription(
|
|
|
|
key="settings.tempSelZ2",
|
|
|
|
mode=HVACMode.COOL,
|
|
|
|
name="Wine Cellar",
|
|
|
|
icon="mdi:thermometer",
|
|
|
|
translation_key="wine",
|
|
|
|
),
|
|
|
|
),
|
2023-04-26 21:57:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
async def async_setup_entry(hass, entry: ConfigEntry, async_add_entities) -> None:
|
2023-05-24 23:30:33 +00:00
|
|
|
entities = []
|
|
|
|
for device in hass.data[DOMAIN][entry.unique_id].appliances:
|
|
|
|
for description in CLIMATES.get(device.appliance_type, []):
|
2023-05-28 05:50:59 +00:00
|
|
|
if isinstance(description, HonACClimateEntityDescription):
|
|
|
|
if description.key not in list(device.commands):
|
|
|
|
continue
|
|
|
|
entity = HonACClimateEntity(hass, entry, device, description)
|
2023-05-28 15:38:56 +00:00
|
|
|
elif isinstance(description, HonClimateEntityDescription):
|
2023-05-28 05:50:59 +00:00
|
|
|
if description.key not in device.available_settings:
|
|
|
|
continue
|
2023-05-28 15:38:56 +00:00
|
|
|
entity = HonClimateEntity(hass, entry, device, description)
|
2023-05-28 05:50:59 +00:00
|
|
|
else:
|
2023-05-24 23:30:33 +00:00
|
|
|
continue
|
|
|
|
await entity.coordinator.async_config_entry_first_refresh()
|
|
|
|
entities.append(entity)
|
|
|
|
async_add_entities(entities)
|
2023-04-26 21:57:44 +00:00
|
|
|
|
|
|
|
|
2023-05-28 05:50:59 +00:00
|
|
|
class HonACClimateEntity(HonEntity, ClimateEntity):
|
2023-05-24 23:30:33 +00:00
|
|
|
def __init__(self, hass, entry, device: HonAppliance, description) -> None:
|
2023-05-27 22:30:08 +00:00
|
|
|
super().__init__(hass, entry, device, description)
|
2023-04-26 21:57:44 +00:00
|
|
|
|
|
|
|
self._attr_temperature_unit = TEMP_CELSIUS
|
2023-06-21 17:52:32 +00:00
|
|
|
self._set_temperature_bound()
|
2023-04-26 21:57:44 +00:00
|
|
|
|
2023-06-08 17:59:43 +00:00
|
|
|
self._attr_hvac_modes = [HVACMode.OFF]
|
|
|
|
for mode in device.settings["settings.machMode"].values:
|
2023-06-12 22:14:51 +00:00
|
|
|
self._attr_hvac_modes.append(HON_HVAC_MODE[int(mode)])
|
2023-06-21 17:52:32 +00:00
|
|
|
self._attr_preset_modes = []
|
|
|
|
for mode in device.settings["startProgram.program"].values:
|
|
|
|
self._attr_preset_modes.append(mode)
|
2023-06-08 17:59:43 +00:00
|
|
|
self._attr_fan_modes = [FAN_OFF]
|
|
|
|
for mode in device.settings["settings.windSpeed"].values:
|
2023-06-12 22:14:51 +00:00
|
|
|
self._attr_fan_modes.append(HON_FAN[int(mode)])
|
2023-04-26 21:57:44 +00:00
|
|
|
self._attr_swing_modes = [
|
|
|
|
SWING_OFF,
|
|
|
|
SWING_VERTICAL,
|
|
|
|
SWING_HORIZONTAL,
|
|
|
|
SWING_BOTH,
|
|
|
|
]
|
|
|
|
self._attr_supported_features = (
|
|
|
|
ClimateEntityFeature.TARGET_TEMPERATURE
|
|
|
|
| ClimateEntityFeature.FAN_MODE
|
|
|
|
| ClimateEntityFeature.SWING_MODE
|
2023-06-21 17:52:32 +00:00
|
|
|
| ClimateEntityFeature.PRESET_MODE
|
2023-04-26 21:57:44 +00:00
|
|
|
)
|
|
|
|
|
2023-05-18 23:27:44 +00:00
|
|
|
self._handle_coordinator_update(update=False)
|
2023-05-08 00:05:04 +00:00
|
|
|
|
2023-06-21 17:52:32 +00:00
|
|
|
def _set_temperature_bound(self) -> None:
|
|
|
|
self._attr_target_temperature_step = self._device.settings[
|
|
|
|
"settings.tempSel"
|
|
|
|
].step
|
|
|
|
self._attr_max_temp = self._device.settings["settings.tempSel"].max
|
|
|
|
self._attr_min_temp = self._device.settings["settings.tempSel"].min
|
|
|
|
|
2023-06-08 17:59:43 +00:00
|
|
|
@property
|
|
|
|
def target_temperature(self) -> int | None:
|
|
|
|
"""Return the temperature we try to reach."""
|
2023-06-12 22:14:51 +00:00
|
|
|
return self._device.get("tempSel")
|
2023-06-08 17:59:43 +00:00
|
|
|
|
|
|
|
@property
|
|
|
|
def current_temperature(self) -> float | None:
|
|
|
|
"""Return the current temperature."""
|
2023-06-12 22:14:51 +00:00
|
|
|
return self._device.get("tempIndoor")
|
2023-06-08 17:59:43 +00:00
|
|
|
|
|
|
|
async def async_set_temperature(self, **kwargs):
|
|
|
|
if (temperature := kwargs.get(ATTR_TEMPERATURE)) is None:
|
|
|
|
return False
|
|
|
|
self._device.settings["settings.tempSel"].value = str(int(temperature))
|
|
|
|
await self._device.commands["settings"].send()
|
|
|
|
self.async_write_ha_state()
|
|
|
|
|
2023-05-28 15:38:56 +00:00
|
|
|
@property
|
|
|
|
def hvac_mode(self) -> HVACMode | str | None:
|
2023-06-12 22:14:51 +00:00
|
|
|
if self._device.get("onOffStatus") == 0:
|
2023-05-28 15:38:56 +00:00
|
|
|
return HVACMode.OFF
|
2023-05-28 05:50:59 +00:00
|
|
|
else:
|
2023-05-28 15:38:56 +00:00
|
|
|
return HON_HVAC_MODE[self._device.get("machMode")]
|
|
|
|
|
|
|
|
async def async_set_hvac_mode(self, hvac_mode):
|
2023-06-08 17:59:43 +00:00
|
|
|
self._attr_hvac_mode = hvac_mode
|
2023-04-26 21:57:44 +00:00
|
|
|
if hvac_mode == HVACMode.OFF:
|
2023-06-21 17:52:32 +00:00
|
|
|
await self._device.commands["stopProgram"].send()
|
|
|
|
self._device.sync_command("stopProgram", "settings")
|
2023-04-26 21:57:44 +00:00
|
|
|
else:
|
2023-06-21 17:52:32 +00:00
|
|
|
self._device.settings["settings.onOffStatus"].value = "1"
|
|
|
|
setting = self._device.settings["settings.machMode"]
|
2023-06-22 11:18:45 +00:00
|
|
|
modes = {HON_HVAC_MODE[int(number)]: number for number in setting.values}
|
2023-06-21 17:52:32 +00:00
|
|
|
setting.value = modes[hvac_mode]
|
|
|
|
await self._device.commands["settings"].send()
|
|
|
|
self.async_write_ha_state()
|
|
|
|
|
|
|
|
@property
|
|
|
|
def preset_mode(self) -> str | None:
|
|
|
|
"""Return the current Preset for this channel."""
|
|
|
|
return None
|
|
|
|
|
|
|
|
async def async_set_preset_mode(self, preset_mode: str) -> None:
|
|
|
|
"""Set the new preset mode."""
|
|
|
|
if program := self._device.settings.get(f"startProgram.program"):
|
|
|
|
program.value = preset_mode
|
|
|
|
self._device.sync_command("startProgram", "settings")
|
|
|
|
self._set_temperature_bound()
|
|
|
|
self._handle_coordinator_update(update=False)
|
|
|
|
await self.coordinator.async_refresh()
|
|
|
|
self._attr_preset_mode = preset_mode
|
|
|
|
await self._device.commands["startProgram"].send()
|
2023-05-16 22:01:33 +00:00
|
|
|
self.async_write_ha_state()
|
2023-04-26 21:57:44 +00:00
|
|
|
|
2023-06-08 17:59:43 +00:00
|
|
|
@property
|
|
|
|
def fan_mode(self) -> str | None:
|
|
|
|
"""Return the fan setting."""
|
|
|
|
return HON_FAN[self._device.get("windSpeed")]
|
|
|
|
|
2023-04-26 21:57:44 +00:00
|
|
|
async def async_set_fan_mode(self, fan_mode):
|
|
|
|
mode_number = list(HON_FAN.values()).index(fan_mode)
|
2023-06-08 17:59:43 +00:00
|
|
|
mode = list(HON_FAN.keys())[mode_number]
|
|
|
|
self._device.settings["settings.windSpeed"].value = mode
|
|
|
|
self._attr_fan_mode = fan_mode
|
2023-05-08 00:05:04 +00:00
|
|
|
await self._device.commands["settings"].send()
|
2023-05-16 22:01:33 +00:00
|
|
|
self.async_write_ha_state()
|
2023-04-26 21:57:44 +00:00
|
|
|
|
2023-06-08 17:59:43 +00:00
|
|
|
@property
|
|
|
|
def swing_mode(self) -> str | None:
|
|
|
|
"""Return the swing setting."""
|
|
|
|
horizontal = self._device.get("windDirectionHorizontal")
|
|
|
|
vertical = self._device.get("windDirectionVertical")
|
2023-06-12 22:14:51 +00:00
|
|
|
if horizontal == 7 and vertical == 8:
|
2023-06-08 17:59:43 +00:00
|
|
|
return SWING_BOTH
|
2023-06-12 22:14:51 +00:00
|
|
|
elif horizontal == 7:
|
2023-06-08 17:59:43 +00:00
|
|
|
return SWING_HORIZONTAL
|
2023-06-12 22:14:51 +00:00
|
|
|
elif vertical == 8:
|
2023-06-08 17:59:43 +00:00
|
|
|
return SWING_VERTICAL
|
|
|
|
else:
|
|
|
|
return SWING_OFF
|
|
|
|
|
2023-04-26 21:57:44 +00:00
|
|
|
async def async_set_swing_mode(self, swing_mode):
|
2023-05-08 00:05:04 +00:00
|
|
|
horizontal = self._device.settings["settings.windDirectionHorizontal"]
|
|
|
|
vertical = self._device.settings["settings.windDirectionVertical"]
|
2023-04-26 21:57:44 +00:00
|
|
|
if swing_mode in [SWING_BOTH, SWING_HORIZONTAL]:
|
|
|
|
horizontal.value = "7"
|
|
|
|
if swing_mode in [SWING_BOTH, SWING_VERTICAL]:
|
|
|
|
vertical.value = "8"
|
|
|
|
if swing_mode in [SWING_OFF, SWING_HORIZONTAL] and vertical.value == "8":
|
|
|
|
vertical.value = "5"
|
|
|
|
if swing_mode in [SWING_OFF, SWING_VERTICAL] and horizontal.value == "7":
|
|
|
|
horizontal.value = "0"
|
|
|
|
self._attr_swing_mode = swing_mode
|
2023-05-08 00:05:04 +00:00
|
|
|
await self._device.commands["settings"].send()
|
2023-05-16 22:01:33 +00:00
|
|
|
self.async_write_ha_state()
|
2023-04-26 21:57:44 +00:00
|
|
|
|
|
|
|
@callback
|
|
|
|
def _handle_coordinator_update(self, update=True) -> None:
|
2023-06-08 17:59:43 +00:00
|
|
|
self._attr_target_temperature = self.target_temperature
|
|
|
|
self._attr_current_temperature = self.current_temperature
|
|
|
|
self._attr_hvac_mode = self.hvac_mode
|
|
|
|
self._attr_fan_mode = self.fan_mode
|
|
|
|
self._attr_swing_mode = self.swing_mode
|
2023-05-18 23:27:44 +00:00
|
|
|
if update:
|
|
|
|
self.async_write_ha_state()
|
2023-05-28 05:50:59 +00:00
|
|
|
|
|
|
|
|
2023-05-28 15:38:56 +00:00
|
|
|
class HonClimateEntity(HonEntity, ClimateEntity):
|
2023-06-09 03:56:52 +00:00
|
|
|
entity_description: HonClimateEntityDescription
|
2023-05-28 15:38:56 +00:00
|
|
|
|
2023-05-28 05:50:59 +00:00
|
|
|
def __init__(self, hass, entry, device: HonAppliance, description) -> None:
|
|
|
|
super().__init__(hass, entry, device, description)
|
|
|
|
|
|
|
|
self._attr_temperature_unit = TEMP_CELSIUS
|
2023-05-28 15:38:56 +00:00
|
|
|
self._set_temperature_bound()
|
2023-05-28 05:50:59 +00:00
|
|
|
|
|
|
|
self._attr_supported_features = (
|
|
|
|
ClimateEntityFeature.TARGET_TEMPERATURE | ClimateEntityFeature.PRESET_MODE
|
|
|
|
)
|
|
|
|
|
2023-05-28 15:38:56 +00:00
|
|
|
self._attr_hvac_modes = [description.mode]
|
2023-06-10 04:44:19 +00:00
|
|
|
if "stopProgram" in device.commands:
|
2023-05-28 15:38:56 +00:00
|
|
|
self._attr_hvac_modes += [HVACMode.OFF]
|
|
|
|
modes = []
|
|
|
|
else:
|
|
|
|
modes = ["no_mode"]
|
2023-05-28 05:50:59 +00:00
|
|
|
|
|
|
|
for mode, data in device.commands["startProgram"].categories.items():
|
2023-05-28 15:38:56 +00:00
|
|
|
if mode not in data.parameters["program"].values:
|
|
|
|
continue
|
2023-05-28 05:50:59 +00:00
|
|
|
if zone := data.parameters.get("zone"):
|
|
|
|
if self.entity_description.name.lower() in zone.values:
|
|
|
|
modes.append(mode)
|
2023-05-28 15:38:56 +00:00
|
|
|
else:
|
|
|
|
modes.append(mode)
|
2023-05-28 05:50:59 +00:00
|
|
|
self._attr_preset_modes = modes
|
|
|
|
|
2023-05-28 15:38:56 +00:00
|
|
|
self._handle_coordinator_update(update=False)
|
|
|
|
|
2023-05-28 05:50:59 +00:00
|
|
|
@property
|
2023-06-10 04:44:19 +00:00
|
|
|
def target_temperature(self) -> float | None:
|
2023-05-28 05:50:59 +00:00
|
|
|
"""Return the temperature we try to reach."""
|
2023-06-12 22:14:51 +00:00
|
|
|
return self._device.get(self.entity_description.key)
|
2023-05-28 05:50:59 +00:00
|
|
|
|
|
|
|
@property
|
2023-06-10 04:44:19 +00:00
|
|
|
def current_temperature(self) -> float | None:
|
2023-05-28 05:50:59 +00:00
|
|
|
"""Return the current temperature."""
|
|
|
|
temp_key = self.entity_description.key.split(".")[-1].replace("Sel", "")
|
2023-06-12 22:14:51 +00:00
|
|
|
return self._device.get(temp_key)
|
2023-05-28 05:50:59 +00:00
|
|
|
|
|
|
|
async def async_set_temperature(self, **kwargs):
|
|
|
|
if (temperature := kwargs.get(ATTR_TEMPERATURE)) is None:
|
|
|
|
return False
|
|
|
|
self._device.settings[self.entity_description.key].value = str(int(temperature))
|
|
|
|
await self._device.commands["settings"].send()
|
|
|
|
self.async_write_ha_state()
|
|
|
|
|
2023-05-28 15:38:56 +00:00
|
|
|
@property
|
|
|
|
def hvac_mode(self) -> HVACMode | str | None:
|
2023-06-12 22:14:51 +00:00
|
|
|
if self._device.get("onOffStatus") == 0:
|
2023-05-28 15:38:56 +00:00
|
|
|
return HVACMode.OFF
|
|
|
|
else:
|
|
|
|
return self.entity_description.mode
|
|
|
|
|
|
|
|
async def async_set_hvac_mode(self, hvac_mode):
|
|
|
|
if len(self.hvac_modes) <= 1:
|
|
|
|
return
|
|
|
|
if hvac_mode == HVACMode.OFF:
|
|
|
|
await self._device.commands["stopProgram"].send()
|
|
|
|
else:
|
|
|
|
await self._device.commands["startProgram"].send()
|
|
|
|
self._attr_hvac_mode = hvac_mode
|
|
|
|
self.async_write_ha_state()
|
|
|
|
|
2023-05-28 05:50:59 +00:00
|
|
|
@property
|
|
|
|
def preset_mode(self) -> str | None:
|
|
|
|
"""Return the current Preset for this channel."""
|
2023-05-28 15:38:56 +00:00
|
|
|
if self._device.get("onOffStatus") is not None:
|
|
|
|
return self._device.get("programName", "")
|
|
|
|
else:
|
|
|
|
return self._device.get(
|
|
|
|
f"mode{self.entity_description.key[-2:]}", "no_mode"
|
|
|
|
)
|
2023-05-28 05:50:59 +00:00
|
|
|
|
|
|
|
async def async_set_preset_mode(self, preset_mode: str) -> None:
|
|
|
|
"""Set the new preset mode."""
|
2023-05-28 15:38:56 +00:00
|
|
|
command = "stopProgram" if preset_mode == "no_mode" else "startProgram"
|
|
|
|
if program := self._device.settings.get(f"{command}.program"):
|
|
|
|
program.value = preset_mode
|
|
|
|
if zone := self._device.settings.get(f"{command}.zone"):
|
|
|
|
zone.value = self.entity_description.name.lower()
|
|
|
|
self._device.sync_command(command, "settings")
|
|
|
|
self._set_temperature_bound()
|
|
|
|
await self.coordinator.async_refresh()
|
|
|
|
await self._device.commands[command].send()
|
|
|
|
self._attr_preset_mode = preset_mode
|
2023-05-28 05:50:59 +00:00
|
|
|
self.async_write_ha_state()
|
|
|
|
|
2023-05-28 15:38:56 +00:00
|
|
|
def _set_temperature_bound(self):
|
|
|
|
self._attr_target_temperature_step = self._device.settings[
|
|
|
|
self.entity_description.key
|
|
|
|
].step
|
|
|
|
self._attr_max_temp = self._device.settings[self.entity_description.key].max
|
|
|
|
self._attr_min_temp = self._device.settings[self.entity_description.key].min
|
|
|
|
|
2023-05-28 05:50:59 +00:00
|
|
|
@callback
|
|
|
|
def _handle_coordinator_update(self, update=True) -> None:
|
2023-05-28 15:38:56 +00:00
|
|
|
self._attr_target_temperature = self.target_temperature
|
|
|
|
self._attr_current_temperature = self.current_temperature
|
|
|
|
self._attr_hvac_mode = self.hvac_mode
|
|
|
|
self._attr_preset_mode = self.preset_mode
|
2023-05-28 05:50:59 +00:00
|
|
|
if update:
|
|
|
|
self.async_write_ha_state()
|