"""Support for the Vallox ventilation unit fan.""" import logging from homeassistant.components.fan import FanEntity from homeassistant.core import callback from homeassistant.helpers.dispatcher import async_dispatcher_connect from . import ( DOMAIN, METRIC_KEY_MODE, METRIC_KEY_PROFILE_FAN_SPEED_AWAY, METRIC_KEY_PROFILE_FAN_SPEED_BOOST, METRIC_KEY_PROFILE_FAN_SPEED_HOME, SIGNAL_VALLOX_STATE_UPDATE) _LOGGER = logging.getLogger(__name__) # Device attributes ATTR_PROFILE_FAN_SPEED_HOME = { 'description': 'fan_speed_home', 'metric_key': METRIC_KEY_PROFILE_FAN_SPEED_HOME } ATTR_PROFILE_FAN_SPEED_AWAY = { 'description': 'fan_speed_away', 'metric_key': METRIC_KEY_PROFILE_FAN_SPEED_AWAY } ATTR_PROFILE_FAN_SPEED_BOOST = { 'description': 'fan_speed_boost', 'metric_key': METRIC_KEY_PROFILE_FAN_SPEED_BOOST } async def async_setup_platform(hass, config, async_add_entities, discovery_info=None): """Set up the fan device.""" if discovery_info is None: return client = hass.data[DOMAIN]['client'] client.set_settable_address(METRIC_KEY_MODE, int) device = ValloxFan(hass.data[DOMAIN]['name'], client, hass.data[DOMAIN]['state_proxy']) async_add_entities([device], update_before_add=True) class ValloxFan(FanEntity): """Representation of the fan.""" def __init__(self, name, client, state_proxy): """Initialize the fan.""" self._name = name self._client = client self._state_proxy = state_proxy self._available = False self._state = None self._fan_speed_home = None self._fan_speed_away = None self._fan_speed_boost = None @property def should_poll(self): """Do not poll the device.""" return False @property def name(self): """Return the name of the device.""" return self._name @property def available(self): """Return if state is known.""" return self._available @property def is_on(self): """Return if device is on.""" return self._state @property def device_state_attributes(self): """Return device specific state attributes.""" return { ATTR_PROFILE_FAN_SPEED_HOME['description']: self._fan_speed_home, ATTR_PROFILE_FAN_SPEED_AWAY['description']: self._fan_speed_away, ATTR_PROFILE_FAN_SPEED_BOOST['description']: self._fan_speed_boost, } async def async_added_to_hass(self): """Call to update.""" async_dispatcher_connect(self.hass, SIGNAL_VALLOX_STATE_UPDATE, self._update_callback) @callback def _update_callback(self): """Call update method.""" self.async_schedule_update_ha_state(True) async def async_update(self): """Fetch state from the device.""" try: # Fetch if the whole device is in regular operation state. mode = self._state_proxy.fetch_metric(METRIC_KEY_MODE) if mode == 0: self._state = True else: self._state = False # Fetch the profile fan speeds. self._fan_speed_home = int(self._state_proxy.fetch_metric( ATTR_PROFILE_FAN_SPEED_HOME['metric_key'])) self._fan_speed_away = int(self._state_proxy.fetch_metric( ATTR_PROFILE_FAN_SPEED_AWAY['metric_key'])) self._fan_speed_boost = int(self._state_proxy.fetch_metric( ATTR_PROFILE_FAN_SPEED_BOOST['metric_key'])) self._available = True except (OSError, KeyError) as err: self._available = False _LOGGER.error("Error updating fan: %s", err) async def async_turn_on(self, speed: str = None, **kwargs) -> None: """Turn the device on.""" _LOGGER.debug("Turn on: %s", speed) # Only the case speed == None equals the GUI toggle switch being # activated. if speed is not None: return if self._state is False: try: await self._client.set_values({METRIC_KEY_MODE: 0}) # This state change affects other entities like sensors. Force # an immediate update that can be observed by all parties # involved. await self._state_proxy.async_update(None) except OSError as err: self._available = False _LOGGER.error("Error turning on: %s", err) else: _LOGGER.error("Already on") async def async_turn_off(self, **kwargs) -> None: """Turn the device off.""" if self._state is True: try: await self._client.set_values({METRIC_KEY_MODE: 5}) # Same as for turn_on method. await self._state_proxy.async_update(None) except OSError as err: self._available = False _LOGGER.error("Error turning off: %s", err) else: _LOGGER.error("Already off")