"""Support for lights from FHEM""" import voluptuous as vol import logging from homeassistant.components.light import ATTR_BRIGHTNESS, PLATFORM_SCHEMA, SUPPORT_BRIGHTNESS, Light, ATTR_TRANSITION from homeassistant.const import CONF_NAME import homeassistant.helpers.config_validation as cv from . import DATA_FHEM, device_error_reporting, CONF_FHEM_IDS CONF_DIMMER = 'dimmer' _LOGGER = logging.getLogger(__name__) PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ vol.Required(CONF_FHEM_IDS): vol.All(cv.ensure_list, [cv.string]), vol.Optional(CONF_DIMMER, default=False): cv.boolean, vol.Required(CONF_NAME): cv.string, }) async def async_setup_platform(hass, config, async_add_entities, discovery_info=None): """Set up lights for KNX platform.""" connection = hass.data[DATA_FHEM] light = FhemLight(connection, config[CONF_NAME], config[CONF_FHEM_IDS], dimmer=config[CONF_DIMMER]) for dev_id in config[CONF_FHEM_IDS]: connection.register_device(dev_id, light) async_add_entities([light]) class FhemLight(Light): def __init__(self, connection, name, ids, dimmer=False): self._brightness = None self.connection = connection self._dimmer = dimmer self._ids = ids self._name = name self._available = True @property def name(self): return self._name @property def should_poll(self): """No polling needed.""" return False @property def available(self) -> bool: return self._available and self.connection.connected @property def supported_features(self): """Flag supported features.""" flags = 0 if self._dimmer: flags |= SUPPORT_BRIGHTNESS return flags @property def brightness(self): return self._brightness @property def is_on(self): """Return true if light is on.""" if self._brightness is not None: return self._brightness > 0 else: return None def refresh(self): self.connection.fhem_set(self._ids[0], 'statusRequest') async def async_turn_on(self, **kwargs): brightness = kwargs.get(ATTR_BRIGHTNESS, None) transition_time = kwargs.get(ATTR_TRANSITION, 0.5) if brightness is None: brightness = 255 if self._dimmer: # zero in the middle is the time until light is switched off, # which is disabled here when passing 0 self.connection.fhem_set(self._ids[0], int(brightness / 255 * 100), 0, transition_time) else: self.connection.fhem_set(self._ids[0], 'on') self._brightness = brightness await self.async_update_ha_state() async def async_turn_off(self, **kwargs): self.connection.fhem_set(self._ids[0], 'off') async def line_received(self, line): if line.startswith('dim:'): self._available = True _, new_dim_state, new_level = line.split(':') new_level = new_level.strip().lower() new_dim_state = new_dim_state.strip().lower() assert new_dim_state == 'stop' or new_dim_state == 'up' or new_dim_state == 'down' if new_dim_state == 'stop': if new_level == 'on': self._brightness = 255 elif new_level == 'off': self._brightness = 0 else: new_level = int(float(new_level)) # first convert from string to floating point then truncate assert 0 <= new_level <= 100 self._brightness = int(new_level / 100 * 255) await self.async_update_ha_state() elif line.startswith('level:') and not self._dimmer: self._available = True _, new_level = line.split(':') new_level = new_level.strip().lower() if new_level == 'on': self._brightness = 255 elif new_level == 'off': self._brightness = 0 else: try: new_level = int(float(new_level)) # first convert from string to floating point then truncate assert 0 <= new_level <= 100 self._brightness = int(new_level / 100 * 255) except ValueError: pass await self.async_update_ha_state() elif line.startswith('ResndFail') or line.startswith('MISSING ACK'): _LOGGER.warning(f"FHEM light {self.name} became unavailable: '{line}'") self._available = False await self.async_update_ha_state() else: device_error_reporting(self.hass, line, component_type="Light", component_name=self.entity_id)