mirror of
https://github.com/home-assistant/core
synced 2024-09-06 10:29:55 +02:00
![Davide Varricchio](/assets/img/avatar_default.png)
* First commit * First working release, but there's a lot to do * Added support for preset_modes * Refined logic * Added translations for config_flow * Updated translations * modified: homeassistant/components/hisense_aehw4a1/climate.py * modified: climate.py * Updated library to latest version * Small changes * Null states when AC off * Minor fixes * Latest updates for TOX * First commit * First working release, but there's a lot to do * new file: requirements_test_all.txt * Added support for preset_modes * Refined logic * Added translations for config_flow * Updated translations * modified: homeassistant/components/hisense_aehw4a1/climate.py * modified: climate.py * Updated library to latest version * Small changes * Null states when AC off * Minor fixes * Latest updates for TOX * new file: requirements_test_all.txt * Fighting with tox * vs Tox round 2 * Isort and updated requirements_test_all.txt * Fighting with lint * Implemented available state * Changed exception type after Travis-ci pylint fails * Support entry in configuration.yaml * Removed commented code * Switched to async * Minor changes * Updated library and fixed pylint errors * Code optimization * Implemented static ip addresses in configuration.yaml * Reverted to existing constant * Corrected pylint wrong-import-order * Recovery from nuke event (messing all while rebase) * Resolved Ci error * Changes for PR * Corrected temp scale for frontend * Added test for config entry from configuration.yaml * Updated dependency * Check on manual config * Imported custom exceptions and modified import config * Optimized * Change based on PR revision * Added logging for failure event on manual config * Tests added but to be corrected * Edited tests * Tests updated to ensure no I/O * Working on tests * Cheanges based on revision for PR * Setting librey exception as direct side_effect in test * Final changes for PR * Redundand on command solved * Improved AC logic
439 lines
13 KiB
Python
439 lines
13 KiB
Python
"""Pyaehw4a1 platform to control of Hisense AEH-W4A1 Climate Devices."""
|
|
|
|
import logging
|
|
|
|
from pyaehw4a1.aehw4a1 import AehW4a1
|
|
import pyaehw4a1.exceptions
|
|
|
|
from homeassistant.components.climate import ClimateDevice
|
|
from homeassistant.components.climate.const import (
|
|
FAN_AUTO,
|
|
FAN_HIGH,
|
|
FAN_LOW,
|
|
FAN_MEDIUM,
|
|
HVAC_MODE_COOL,
|
|
HVAC_MODE_DRY,
|
|
HVAC_MODE_FAN_ONLY,
|
|
HVAC_MODE_HEAT,
|
|
HVAC_MODE_OFF,
|
|
PRESET_BOOST,
|
|
PRESET_ECO,
|
|
PRESET_NONE,
|
|
PRESET_SLEEP,
|
|
SUPPORT_FAN_MODE,
|
|
SUPPORT_PRESET_MODE,
|
|
SUPPORT_SWING_MODE,
|
|
SUPPORT_TARGET_TEMPERATURE,
|
|
SWING_BOTH,
|
|
SWING_HORIZONTAL,
|
|
SWING_OFF,
|
|
SWING_VERTICAL,
|
|
)
|
|
from homeassistant.const import (
|
|
ATTR_TEMPERATURE,
|
|
PRECISION_WHOLE,
|
|
TEMP_CELSIUS,
|
|
TEMP_FAHRENHEIT,
|
|
)
|
|
|
|
from . import CONF_IP_ADDRESS, DOMAIN
|
|
|
|
SUPPORT_FLAGS = (
|
|
SUPPORT_TARGET_TEMPERATURE
|
|
| SUPPORT_FAN_MODE
|
|
| SUPPORT_SWING_MODE
|
|
| SUPPORT_PRESET_MODE
|
|
)
|
|
|
|
MIN_TEMP_C = 16
|
|
MAX_TEMP_C = 32
|
|
|
|
MIN_TEMP_F = 61
|
|
MAX_TEMP_F = 90
|
|
|
|
HVAC_MODES = [
|
|
HVAC_MODE_OFF,
|
|
HVAC_MODE_HEAT,
|
|
HVAC_MODE_COOL,
|
|
HVAC_MODE_DRY,
|
|
HVAC_MODE_FAN_ONLY,
|
|
]
|
|
|
|
FAN_MODES = [
|
|
"mute",
|
|
FAN_LOW,
|
|
FAN_MEDIUM,
|
|
FAN_HIGH,
|
|
FAN_AUTO,
|
|
]
|
|
|
|
SWING_MODES = [
|
|
SWING_OFF,
|
|
SWING_VERTICAL,
|
|
SWING_HORIZONTAL,
|
|
SWING_BOTH,
|
|
]
|
|
|
|
PRESET_MODES = [
|
|
PRESET_NONE,
|
|
PRESET_ECO,
|
|
PRESET_BOOST,
|
|
PRESET_SLEEP,
|
|
"sleep_2",
|
|
"sleep_3",
|
|
"sleep_4",
|
|
]
|
|
|
|
AC_TO_HA_STATE = {
|
|
"0001": HVAC_MODE_HEAT,
|
|
"0010": HVAC_MODE_COOL,
|
|
"0011": HVAC_MODE_DRY,
|
|
"0000": HVAC_MODE_FAN_ONLY,
|
|
}
|
|
|
|
HA_STATE_TO_AC = {
|
|
HVAC_MODE_OFF: "off",
|
|
HVAC_MODE_HEAT: "mode_heat",
|
|
HVAC_MODE_COOL: "mode_cool",
|
|
HVAC_MODE_DRY: "mode_dry",
|
|
HVAC_MODE_FAN_ONLY: "mode_fan",
|
|
}
|
|
|
|
AC_TO_HA_FAN_MODES = {
|
|
"00000000": FAN_AUTO, # fan value for heat mode
|
|
"00000001": FAN_AUTO,
|
|
"00000010": "mute",
|
|
"00000100": FAN_LOW,
|
|
"00000110": FAN_MEDIUM,
|
|
"00001000": FAN_HIGH,
|
|
}
|
|
|
|
HA_FAN_MODES_TO_AC = {
|
|
"mute": "speed_mute",
|
|
FAN_LOW: "speed_low",
|
|
FAN_MEDIUM: "speed_med",
|
|
FAN_HIGH: "speed_max",
|
|
FAN_AUTO: "speed_auto",
|
|
}
|
|
|
|
AC_TO_HA_SWING = {
|
|
"00": SWING_OFF,
|
|
"10": SWING_VERTICAL,
|
|
"01": SWING_HORIZONTAL,
|
|
"11": SWING_BOTH,
|
|
}
|
|
|
|
_LOGGER = logging.getLogger(__name__)
|
|
|
|
|
|
def _build_entity(device):
|
|
_LOGGER.debug("Found device at %s", device)
|
|
return ClimateAehW4a1(device)
|
|
|
|
|
|
async def async_setup_entry(hass, config_entry, async_add_entities):
|
|
"""Set up the AEH-W4A1 climate platform."""
|
|
# Priority 1: manual config
|
|
if hass.data[DOMAIN].get(CONF_IP_ADDRESS):
|
|
devices = hass.data[DOMAIN][CONF_IP_ADDRESS]
|
|
else:
|
|
# Priority 2: scanned interfaces
|
|
devices = await AehW4a1().discovery()
|
|
|
|
entities = [_build_entity(device) for device in devices]
|
|
async_add_entities(entities, True)
|
|
|
|
|
|
class ClimateAehW4a1(ClimateDevice):
|
|
"""Representation of a Hisense AEH-W4A1 module for climate device."""
|
|
|
|
def __init__(self, device):
|
|
"""Initialize the climate device."""
|
|
self._unique_id = device
|
|
self._device = AehW4a1(device)
|
|
self._hvac_modes = HVAC_MODES
|
|
self._fan_modes = FAN_MODES
|
|
self._swing_modes = SWING_MODES
|
|
self._preset_modes = PRESET_MODES
|
|
self._available = None
|
|
self._on = None
|
|
self._temperature_unit = None
|
|
self._current_temperature = None
|
|
self._target_temperature = None
|
|
self._hvac_mode = None
|
|
self._fan_mode = None
|
|
self._swing_mode = None
|
|
self._preset_mode = None
|
|
self._previous_state = None
|
|
|
|
async def async_update(self):
|
|
"""Pull state from AEH-W4A1."""
|
|
try:
|
|
status = await self._device.command("status_102_0")
|
|
except pyaehw4a1.exceptions.ConnectionError as library_error:
|
|
_LOGGER.warning(
|
|
"Unexpected error of %s: %s", self._unique_id, library_error
|
|
)
|
|
self._available = False
|
|
return
|
|
|
|
self._available = True
|
|
|
|
self._on = status["run_status"]
|
|
|
|
if status["temperature_Fahrenheit"] == "0":
|
|
self._temperature_unit = TEMP_CELSIUS
|
|
else:
|
|
self._temperature_unit = TEMP_FAHRENHEIT
|
|
|
|
self._current_temperature = int(status["indoor_temperature_status"], 2)
|
|
|
|
if self._on == "1":
|
|
device_mode = status["mode_status"]
|
|
self._hvac_mode = AC_TO_HA_STATE[device_mode]
|
|
|
|
fan_mode = status["wind_status"]
|
|
self._fan_mode = AC_TO_HA_FAN_MODES[fan_mode]
|
|
|
|
swing_mode = f'{status["up_down"]}{status["left_right"]}'
|
|
self._swing_mode = AC_TO_HA_SWING[swing_mode]
|
|
|
|
if self._hvac_mode in (HVAC_MODE_COOL, HVAC_MODE_HEAT):
|
|
self._target_temperature = int(status["indoor_temperature_setting"], 2)
|
|
else:
|
|
self._target_temperature = None
|
|
|
|
if status["efficient"] == "1":
|
|
self._preset_mode = PRESET_BOOST
|
|
elif status["low_electricity"] == "1":
|
|
self._preset_mode = PRESET_ECO
|
|
elif status["sleep_status"] == "0000001":
|
|
self._preset_mode = PRESET_SLEEP
|
|
elif status["sleep_status"] == "0000010":
|
|
self._preset_mode = "sleep_2"
|
|
elif status["sleep_status"] == "0000011":
|
|
self._preset_mode = "sleep_3"
|
|
elif status["sleep_status"] == "0000100":
|
|
self._preset_mode = "sleep_4"
|
|
else:
|
|
self._preset_mode = PRESET_NONE
|
|
else:
|
|
self._hvac_mode = HVAC_MODE_OFF
|
|
self._fan_mode = None
|
|
self._swing_mode = None
|
|
self._target_temperature = None
|
|
self._preset_mode = None
|
|
|
|
@property
|
|
def available(self):
|
|
"""Return True if entity is available."""
|
|
return self._available
|
|
|
|
@property
|
|
def name(self):
|
|
"""Return the name of the climate device."""
|
|
return self._unique_id
|
|
|
|
@property
|
|
def temperature_unit(self):
|
|
"""Return the unit of measurement."""
|
|
return self._temperature_unit
|
|
|
|
@property
|
|
def current_temperature(self):
|
|
"""Return the current temperature."""
|
|
return self._current_temperature
|
|
|
|
@property
|
|
def target_temperature(self):
|
|
"""Return the temperature we are trying to reach."""
|
|
return self._target_temperature
|
|
|
|
@property
|
|
def hvac_mode(self):
|
|
"""Return hvac target hvac state."""
|
|
return self._hvac_mode
|
|
|
|
@property
|
|
def hvac_modes(self):
|
|
"""Return the list of available operation modes."""
|
|
return self._hvac_modes
|
|
|
|
@property
|
|
def fan_mode(self):
|
|
"""Return the fan setting."""
|
|
return self._fan_mode
|
|
|
|
@property
|
|
def fan_modes(self):
|
|
"""Return the list of available fan modes."""
|
|
return self._fan_modes
|
|
|
|
@property
|
|
def preset_mode(self):
|
|
"""Return the preset mode if on."""
|
|
return self._preset_mode
|
|
|
|
@property
|
|
def preset_modes(self):
|
|
"""Return the list of available preset modes."""
|
|
return self._preset_modes
|
|
|
|
@property
|
|
def swing_mode(self):
|
|
"""Return swing operation."""
|
|
return self._swing_mode
|
|
|
|
@property
|
|
def swing_modes(self):
|
|
"""Return the list of available fan modes."""
|
|
return self._swing_modes
|
|
|
|
@property
|
|
def min_temp(self):
|
|
"""Return the minimum temperature."""
|
|
if self._temperature_unit == TEMP_CELSIUS:
|
|
return MIN_TEMP_C
|
|
return MIN_TEMP_F
|
|
|
|
@property
|
|
def max_temp(self):
|
|
"""Return the maximum temperature."""
|
|
if self._temperature_unit == TEMP_CELSIUS:
|
|
return MAX_TEMP_C
|
|
return MAX_TEMP_F
|
|
|
|
@property
|
|
def precision(self):
|
|
"""Return the precision of the system."""
|
|
return PRECISION_WHOLE
|
|
|
|
@property
|
|
def target_temperature_step(self):
|
|
"""Return the supported step of target temperature."""
|
|
return 1
|
|
|
|
@property
|
|
def supported_features(self):
|
|
"""Return the list of supported features."""
|
|
return SUPPORT_FLAGS
|
|
|
|
async def async_set_temperature(self, **kwargs):
|
|
"""Set new target temperatures."""
|
|
if self._on != "1":
|
|
_LOGGER.warning(
|
|
"AC at %s is off, could not set temperature", self._unique_id
|
|
)
|
|
return
|
|
temp = kwargs.get(ATTR_TEMPERATURE)
|
|
if temp is not None:
|
|
_LOGGER.debug("Setting temp of %s to %s", self._unique_id, temp)
|
|
if self._preset_mode != PRESET_NONE:
|
|
await self.async_set_preset_mode(PRESET_NONE)
|
|
if self._temperature_unit == TEMP_CELSIUS:
|
|
await self._device.command(f"temp_{int(temp)}_C")
|
|
else:
|
|
await self._device.command(f"temp_{int(temp)}_F")
|
|
|
|
async def async_set_fan_mode(self, fan_mode):
|
|
"""Set new fan mode."""
|
|
if self._on != "1":
|
|
_LOGGER.warning("AC at %s is off, could not set fan mode", self._unique_id)
|
|
return
|
|
if self._hvac_mode in (HVAC_MODE_COOL, HVAC_MODE_FAN_ONLY) and (
|
|
self._hvac_mode != HVAC_MODE_FAN_ONLY or fan_mode != FAN_AUTO
|
|
):
|
|
_LOGGER.debug("Setting fan mode of %s to %s", self._unique_id, fan_mode)
|
|
await self._device.command(HA_FAN_MODES_TO_AC[fan_mode])
|
|
|
|
async def async_set_swing_mode(self, swing_mode):
|
|
"""Set new target swing operation."""
|
|
if self._on != "1":
|
|
_LOGGER.warning(
|
|
"AC at %s is off, could not set swing mode", self._unique_id
|
|
)
|
|
return
|
|
|
|
_LOGGER.debug("Setting swing mode of %s to %s", self._unique_id, swing_mode)
|
|
swing_act = self._swing_mode
|
|
|
|
if swing_mode == SWING_OFF and swing_act != SWING_OFF:
|
|
if swing_act in (SWING_HORIZONTAL, SWING_BOTH):
|
|
await self._device.command("hor_dir")
|
|
if swing_act in (SWING_VERTICAL, SWING_BOTH):
|
|
await self._device.command("vert_dir")
|
|
|
|
if swing_mode == SWING_BOTH and swing_act != SWING_BOTH:
|
|
if swing_act in (SWING_OFF, SWING_HORIZONTAL):
|
|
await self._device.command("vert_swing")
|
|
if swing_act in (SWING_OFF, SWING_VERTICAL):
|
|
await self._device.command("hor_swing")
|
|
|
|
if swing_mode == SWING_VERTICAL and swing_act != SWING_VERTICAL:
|
|
if swing_act in (SWING_OFF, SWING_HORIZONTAL):
|
|
await self._device.command("vert_swing")
|
|
if swing_act in (SWING_BOTH, SWING_HORIZONTAL):
|
|
await self._device.command("hor_dir")
|
|
|
|
if swing_mode == SWING_HORIZONTAL and swing_act != SWING_HORIZONTAL:
|
|
if swing_act in (SWING_BOTH, SWING_VERTICAL):
|
|
await self._device.command("vert_dir")
|
|
if swing_act in (SWING_OFF, SWING_VERTICAL):
|
|
await self._device.command("hor_swing")
|
|
|
|
async def async_set_preset_mode(self, preset_mode):
|
|
"""Set new preset mode."""
|
|
if self._on != "1":
|
|
if preset_mode == PRESET_NONE:
|
|
return
|
|
await self.async_turn_on()
|
|
|
|
_LOGGER.debug("Setting preset mode of %s to %s", self._unique_id, preset_mode)
|
|
|
|
if preset_mode == PRESET_ECO:
|
|
await self._device.command("energysave_on")
|
|
self._previous_state = preset_mode
|
|
elif preset_mode == PRESET_BOOST:
|
|
await self._device.command("turbo_on")
|
|
self._previous_state = preset_mode
|
|
elif preset_mode == PRESET_SLEEP:
|
|
await self._device.command("sleep_1")
|
|
self._previous_state = self._hvac_mode
|
|
elif preset_mode == "sleep_2":
|
|
await self._device.command("sleep_2")
|
|
self._previous_state = self._hvac_mode
|
|
elif preset_mode == "sleep_3":
|
|
await self._device.command("sleep_3")
|
|
self._previous_state = self._hvac_mode
|
|
elif preset_mode == "sleep_4":
|
|
await self._device.command("sleep_4")
|
|
self._previous_state = self._hvac_mode
|
|
elif self._previous_state is not None:
|
|
if self._previous_state == PRESET_ECO:
|
|
await self._device.command("energysave_off")
|
|
elif self._previous_state == PRESET_BOOST:
|
|
await self._device.command("turbo_off")
|
|
elif self._previous_state in HA_STATE_TO_AC:
|
|
await self._device.command(HA_STATE_TO_AC[self._previous_state])
|
|
self._previous_state = None
|
|
|
|
async def async_set_hvac_mode(self, hvac_mode):
|
|
"""Set new operation mode."""
|
|
_LOGGER.debug("Setting operation mode of %s to %s", self._unique_id, hvac_mode)
|
|
if hvac_mode == HVAC_MODE_OFF:
|
|
await self.async_turn_off()
|
|
else:
|
|
await self._device.command(HA_STATE_TO_AC[hvac_mode])
|
|
if self._on != "1":
|
|
await self.async_turn_on()
|
|
|
|
async def async_turn_on(self):
|
|
"""Turn on."""
|
|
_LOGGER.debug("Turning %s on", self._unique_id)
|
|
await self._device.command("on")
|
|
|
|
async def async_turn_off(self):
|
|
"""Turn off."""
|
|
_LOGGER.debug("Turning %s off", self._unique_id)
|
|
await self._device.command("off")
|