1 """Support for IntesisHome and airconwithme Smart AC Controllers."""
3 from __future__
import annotations
6 from random
import randrange
7 from typing
import Any, NamedTuple
9 from pyintesishome
import IHAuthenticationError, IHConnectionError, IntesisHome
10 import voluptuous
as vol
14 PLATFORM_SCHEMA
as CLIMATE_PLATFORM_SCHEMA,
41 _LOGGER = logging.getLogger(__name__)
43 IH_DEVICE_INTESISHOME =
"IntesisHome"
44 IH_DEVICE_AIRCONWITHME =
"airconwithme"
45 IH_DEVICE_ANYWAIR =
"anywair"
47 PLATFORM_SCHEMA = CLIMATE_PLATFORM_SCHEMA.extend(
49 vol.Required(CONF_USERNAME): cv.string,
50 vol.Required(CONF_PASSWORD): cv.string,
51 vol.Optional(CONF_DEVICE, default=IH_DEVICE_INTESISHOME): vol.In(
52 [IH_DEVICE_AIRCONWITHME, IH_DEVICE_ANYWAIR, IH_DEVICE_INTESISHOME]
59 """Settings for swing mode."""
65 MAP_IH_TO_HVAC_MODE = {
66 "auto": HVACMode.HEAT_COOL,
67 "cool": HVACMode.COOL,
69 "fan": HVACMode.FAN_ONLY,
70 "heat": HVACMode.HEAT,
73 MAP_HVAC_MODE_TO_IH = {v: k
for k, v
in MAP_IH_TO_HVAC_MODE.items()}
75 MAP_IH_TO_PRESET_MODE = {
77 "comfort": PRESET_COMFORT,
78 "powerful": PRESET_BOOST,
80 MAP_PRESET_MODE_TO_IH = {v: k
for k, v
in MAP_IH_TO_PRESET_MODE.items()}
82 IH_SWING_STOP =
"auto/stop"
83 IH_SWING_SWING =
"swing"
85 SWING_OFF:
SwingSettings(vvane=IH_SWING_STOP, hvane=IH_SWING_STOP),
86 SWING_BOTH:
SwingSettings(vvane=IH_SWING_SWING, hvane=IH_SWING_SWING),
87 SWING_HORIZONTAL:
SwingSettings(vvane=IH_SWING_STOP, hvane=IH_SWING_SWING),
88 SWING_VERTICAL:
SwingSettings(vvane=IH_SWING_SWING, hvane=IH_SWING_STOP),
93 HVACMode.COOL:
"mdi:snowflake",
94 HVACMode.DRY:
"mdi:water-off",
95 HVACMode.FAN_ONLY:
"mdi:fan",
96 HVACMode.HEAT:
"mdi:white-balance-sunny",
97 HVACMode.HEAT_COOL:
"mdi:cached",
104 async_add_entities: AddEntitiesCallback,
105 discovery_info: DiscoveryInfoType |
None =
None,
107 """Create the IntesisHome climate devices."""
108 ih_user = config[CONF_USERNAME]
109 ih_pass = config[CONF_PASSWORD]
110 device_type = config[CONF_DEVICE]
112 controller = IntesisHome(
117 device_type=device_type,
120 await controller.poll_status()
121 except IHAuthenticationError:
122 _LOGGER.error(
"Invalid username or password")
124 except IHConnectionError
as ex:
125 _LOGGER.error(
"Error connecting to the %s server", device_type)
126 raise PlatformNotReady
from ex
128 if ih_devices := controller.get_devices():
131 IntesisAC(ih_device_id, device, controller)
132 for ih_device_id, device
in ih_devices.items()
138 "Error getting device list from %s API: %s",
140 controller.error_message,
142 await controller.stop()
146 """Represents an Intesishome air conditioning device."""
148 _attr_should_poll =
False
149 _attr_temperature_unit = UnitOfTemperature.CELSIUS
150 _enable_turn_on_off_backwards_compatibility =
False
152 def __init__(self, ih_device_id, ih_device, controller):
153 """Initialize the thermostat."""
169 self.
_preset_list_preset_list = [PRESET_ECO, PRESET_COMFORT, PRESET_BOOST]
181 if controller.has_setpoint_control(ih_device_id):
182 self._attr_supported_features |= ClimateEntityFeature.TARGET_TEMPERATURE
185 if controller.has_vertical_swing(ih_device_id):
187 if controller.has_horizontal_swing(ih_device_id):
188 self.
_swing_list_swing_list.append(SWING_HORIZONTAL)
192 self._attr_supported_features |= ClimateEntityFeature.SWING_MODE
195 self.
_fan_modes_fan_modes = controller.get_fan_speed_list(ih_device_id)
197 self._attr_supported_features |= ClimateEntityFeature.FAN_MODE
200 if ih_device.get(
"climate_working_mode"):
201 self._attr_supported_features |= ClimateEntityFeature.PRESET_MODE
204 if modes := controller.get_mode_list(ih_device_id):
205 mode_list = [MAP_IH_TO_HVAC_MODE[mode]
for mode
in modes]
210 self._attr_supported_features |= (
211 ClimateEntityFeature.TURN_OFF | ClimateEntityFeature.TURN_ON
215 """Subscribe to event updates."""
216 _LOGGER.debug(
"Added climate device with state: %s", repr(self.
_ih_device_ih_device))
220 except IHConnectionError
as ex:
221 _LOGGER.error(
"Exception connecting to IntesisHome: %s", ex)
222 raise PlatformNotReady
from ex
226 """Return the name of the AC device."""
231 """Return the device specific state attributes."""
236 attrs[
"power_consumption_heat_kw"] = round(
240 attrs[
"power_consumption_cool_kw"] = round(
248 """Return unique ID for this device."""
253 """Return whether setpoint should be whole or half degree precision."""
258 """Return a list of HVAC preset modes."""
263 """Return the current preset mode."""
267 """Set new target temperature."""
268 if hvac_mode := kwargs.get(ATTR_HVAC_MODE):
271 if temperature := kwargs.get(ATTR_TEMPERATURE):
272 _LOGGER.debug(
"Setting %s to %s degrees", self.
_device_type_device_type, temperature)
280 """Set operation mode."""
281 _LOGGER.debug(
"Setting %s to %s mode", self.
_device_type_device_type, hvac_mode)
282 if hvac_mode == HVACMode.OFF:
295 await self.
_controller_controller.set_mode(self.
_device_id_device_id, MAP_HVAC_MODE_TO_IH[hvac_mode])
306 """Set fan mode (from quiet, low, medium, high, auto)."""
314 """Set preset mode."""
315 ih_preset_mode = MAP_PRESET_MODE_TO_IH.get(preset_mode)
319 """Set the vertical vane."""
320 if swing_settings := MAP_SWING_TO_IH.get(swing_mode):
321 await self.
_controller_controller.set_vertical_vane(
322 self.
_device_id_device_id, swing_settings.vvane
324 await self.
_controller_controller.set_horizontal_vane(
325 self.
_device_id_device_id, swing_settings.hvane
329 """Copy values from controller dictionary to climate device."""
344 self.
_hvac_mode_hvac_mode = MAP_IH_TO_HVAC_MODE.get(mode)
348 self.
_preset_preset = MAP_IH_TO_PRESET_MODE.get(preset)
364 """Shutdown the controller when the device is being removed."""
369 """Return the icon for the current state."""
372 icon = MAP_STATE_ICONS.get(self.
_hvac_mode_hvac_mode)
376 """Let HA know there has been an update from the controller."""
381 reconnect_minutes = 1 + randrange(10)
383 "Connection to %s API was lost. Reconnecting in %i minutes",
397 _LOGGER.debug(
"Connection to %s API was restored", self.
_device_type_device_type)
399 if not device_id
or self.
_device_id_device_id == device_id:
402 "%s API sent a status update for device %s",
410 """Return the minimum temperature for the current mode of operation."""
415 """Return the maximum temperature for the current mode of operation."""
420 """Return whether the fan is on."""
425 """Return current swing mode."""
426 if self.
_vvane_vvane == IH_SWING_SWING
and self.
_hvane_hvane == IH_SWING_SWING:
428 elif self.
_vvane_vvane == IH_SWING_SWING:
429 swing = SWING_VERTICAL
430 elif self.
_hvane_hvane == IH_SWING_SWING:
431 swing = SWING_HORIZONTAL
438 """List of available fan modes."""
443 """List of available swing positions."""
448 """If the device hasn't been able to connect, mark as unavailable."""
453 """Return the current temperature."""
458 """Return the current mode of operation if unit is on."""
465 """Return the current setpoint temperature if unit is on."""
None set_temperature(self, **Any kwargs)
None async_set_hvac_mode(self, HVACMode hvac_mode)
None set_preset_mode(self, str preset_mode)
list[HVACMode] hvac_modes(self)
def target_temperature(self)
None async_set_hvac_mode(self, HVACMode hvac_mode)
None async_will_remove_from_hass(self)
def async_update_callback(self, device_id=None)
float target_temperature_step(self)
None async_set_preset_mode(self, str preset_mode)
def extra_state_attributes(self)
def current_temperature(self)
None async_set_swing_mode(self, str swing_mode)
None async_set_temperature(self, **Any kwargs)
None async_added_to_hass(self)
None async_set_fan_mode(self, str fan_mode)
def __init__(self, ih_device_id, ih_device, controller)
None async_schedule_update_ha_state(self, bool force_refresh=False)
None async_write_ha_state(self)
bool is_on(HomeAssistant hass, str entity_id)
None async_setup_platform(HomeAssistant hass, ConfigType config, AddEntitiesCallback async_add_entities, DiscoveryInfoType|None discovery_info=None)
float|None get_temperature(MadVRCoordinator coordinator, str key)
bool try_connect(HomeAssistant hass, ConfGatewayType gateway_type, dict[str, Any] user_input)
aiohttp.ClientSession async_get_clientsession(HomeAssistant hass, bool verify_ssl=True, socket.AddressFamily family=socket.AF_UNSPEC, ssl_util.SSLCipherList ssl_cipher=ssl_util.SSLCipherList.PYTHON_DEFAULT)
CALLBACK_TYPE async_call_later(HomeAssistant hass, float|timedelta delay, HassJob[[datetime], Coroutine[Any, Any, None]|None]|Callable[[datetime], Coroutine[Any, Any, None]|None] action)