diff --git a/homeassistant/components/yeelight/__init__.py b/homeassistant/components/yeelight/__init__.py index 6a0e99dbd78331..7bda6da845cc4f 100644 --- a/homeassistant/components/yeelight/__init__.py +++ b/homeassistant/components/yeelight/__init__.py @@ -18,6 +18,7 @@ from homeassistant.helpers import discovery from homeassistant.helpers.discovery import load_platform import homeassistant.helpers.config_validation as cv +from homeassistant.helpers.event import async_track_time_interval REQUIREMENTS = ['yeelight==0.4.3'] @@ -36,15 +37,6 @@ CONF_FLOW_PARAMS = 'flow_params' CONF_CUSTOM_EFFECTS = 'custom_effects' -LEGACY_DEVICE_TYPE_MAP = { - 'color1': 'rgb', - 'mono1': 'white', - 'strip1': 'strip', - 'bslamp1': 'bedside', - 'ceiling1': 'ceiling', -} - - ATTR_MODE = 'mode' ATTR_COUNT = 'count' ATTR_ACTION = 'action' @@ -54,6 +46,9 @@ ACTION_STAY = 'stay' ACTION_OFF = 'off' +MODE_MOONLIGHT = 'moonlight' +MODE_DAYLIGHT = 'normal' + SCAN_INTERVAL = timedelta(seconds=30) YEELIGHT_RGB_TRANSITION = 'RGBTransition' @@ -130,10 +125,8 @@ def device_discovered(service, info): _LOGGER.debug("Adding autodetected %s", info[ATTR_HOSTNAME]) device_type = info[ATTR_DEVICE_TYPE] - legacy_device_type = \ - LEGACY_DEVICE_TYPE_MAP.get(device_type, device_type) - name = "yeelight_%s_%s" % (legacy_device_type, + name = "yeelight_%s_%s" % (device_type, info[ATTR_PROPERTIES]['mac']) ipaddr = info[CONF_HOST] device_config = DEVICE_SCHEMA({ @@ -145,6 +138,14 @@ def device_discovered(service, info): discovery.listen(hass, SERVICE_YEELIGHT, device_discovered) + def async_update(event): + for device in hass.data[DATA_YEELIGHT][CONF_DEVICES].values(): + device.update() + + async_track_time_interval( + hass, async_update, conf[CONF_SCAN_INTERVAL] + ) + for ipaddr, device_config in conf[CONF_DEVICES].items(): _LOGGER.debug("Adding configured %s", device_config[CONF_NAME]) _setup_device(hass, config, ipaddr, device_config) @@ -159,6 +160,7 @@ def _setup_device(hass, hass_config, ipaddr, device_config): return device = YeelightDevice(ipaddr, device_config) + devices[ipaddr] = device platform_config = device_config.copy() @@ -182,6 +184,15 @@ def __init__(self, ipaddr, config): self._name = config.get(CONF_NAME) self._model = config.get(CONF_MODEL) self._bulb_device = None + self.device_updated_cbs = [] + + def register_device_updated_cb(self, device_updated_cb): + """Register device updated callback.""" + self.device_updated_cbs.append(device_updated_cb) + + def unregister_device_updated_cb(self, device_updated_cb): + """Unregister device updated callback.""" + self.device_updated_cbs.remove(device_updated_cb) @property def bulb(self): @@ -192,7 +203,7 @@ def bulb(self): self._bulb_device = yeelight.Bulb(self._ipaddr, model=self._model) # force init for type - self._bulb_device.get_properties(UPDATE_REQUEST_PROPERTIES) + self._update_properties() except yeelight.BulbException as ex: _LOGGER.error("Failed to connect to bulb %s, %s: %s", @@ -211,14 +222,57 @@ def config(self): return self._config @property - def is_nightlight_supported(self): + def is_nightlight_supported(self) -> bool: """Return true / false if nightlight is supported""" return self._model in NIGHTLIGHT_SUPPORTED_MODELS + @property + def is_nightlight_enabled(self) -> bool: + """Return true / false if nightlight is currently enabled""" + return self.bulb.last_properties.get('active_mode') == '1' + + def turn_on(self, duration=DEFAULT_TRANSITION): + """Turn on device""" + import yeelight + try: + self._bulb_device.turn_on(duration=duration) + except yeelight.BulbException as ex: + _LOGGER.error("Unable to turn the bulb on: %s", ex) + return + + self.update() + + def turn_off(self, duration=DEFAULT_TRANSITION): + """Turn off device""" + import yeelight + try: + self._bulb_device.turn_off(duration=duration) + except yeelight.BulbException as ex: + _LOGGER.error("Unable to turn the bulb on: %s", ex) + return + + self.update() + + def set_mode(self, mode: str): + """Set a power mode.""" + import yeelight + try: + self.bulb.set_power_mode(yeelight.enums.PowerMode[mode.upper()]) + except yeelight.BulbException as ex: + _LOGGER.error("Unable to set the power mode: %s", ex) + + self.update() + def update(self): """Read new properties from the device""" - if self.bulb: - return self.bulb.get_properties(UPDATE_REQUEST_PROPERTIES) + if not self.bulb: + return + + self._update_properties() + + for device_updated_cb in self.device_updated_cbs: + device_updated_cb(self) - return {} + def _update_properties(self): + self._bulb_device.get_properties(UPDATE_REQUEST_PROPERTIES) diff --git a/homeassistant/components/yeelight/light.py b/homeassistant/components/yeelight/light.py index 9b245a7d88e5ba..84feacedc6e971 100644 --- a/homeassistant/components/yeelight/light.py +++ b/homeassistant/components/yeelight/light.py @@ -7,6 +7,7 @@ color_temperature_mired_to_kelvin as mired_to_kelvin, color_temperature_kelvin_to_mired as kelvin_to_mired) from homeassistant.const import CONF_NAME, CONF_DEVICES, CONF_LIGHTS, CONF_HOST +from homeassistant.core import callback from homeassistant.components.light import ( ATTR_BRIGHTNESS, ATTR_HS_COLOR, ATTR_TRANSITION, ATTR_COLOR_TEMP, ATTR_FLASH, FLASH_SHORT, FLASH_LONG, ATTR_EFFECT, SUPPORT_BRIGHTNESS, @@ -179,6 +180,21 @@ def __init__(self, device, custom_effects=None): else: self._custom_effects = {} + self.register_callbacks() + + @callback + def register_callbacks(self): + """Register callbacks to update hass after device was changed.""" + def after_update_callback(device): + """Call after device was updated.""" + self.async_schedule_update_ha_state(True) + self._device.register_device_updated_cb(after_update_callback) + + @property + def should_poll(self): + """No polling needed""" + return False + @property def available(self) -> bool: """Return if bulb is available.""" @@ -290,12 +306,13 @@ def update(self) -> None: """Update properties from the bulb.""" import yeelight try: - self._device.update() - if self._bulb.bulb_type == yeelight.BulbType.Color: self._supported_features = SUPPORT_YEELIGHT_RGB elif self._bulb.bulb_type == yeelight.BulbType.WhiteTemp: - self._supported_features = SUPPORT_YEELIGHT_WHITE_TEMP + if self._device.is_nightlight_enabled: + self._supported_features = SUPPORT_YEELIGHT + else: + self._supported_features = SUPPORT_YEELIGHT_WHITE_TEMP if self._min_mireds is None: model_specs = self._bulb.get_model_specs() @@ -306,7 +323,11 @@ def update(self) -> None: self._is_on = self._properties.get('power') == 'on' - bright = self._properties.get('bright', None) + if self._device.is_nightlight_enabled: + bright = self._properties.get('nl_br', None) + else: + bright = self._properties.get('bright', None) + if bright: self._brightness = round(255 * (int(bright) / 100)) @@ -446,11 +467,7 @@ def turn_on(self, **kwargs) -> None: if ATTR_TRANSITION in kwargs: # passed kwarg overrides config duration = int(kwargs.get(ATTR_TRANSITION) * 1000) # kwarg in s - try: - self._bulb.turn_on(duration=duration) - except yeelight.BulbException as ex: - _LOGGER.error("Unable to turn the bulb on: %s", ex) - return + self._device.turn_on(duration=duration) if self.config[CONF_MODE_MUSIC] and not self._bulb.music_mode: try: @@ -482,23 +499,11 @@ def turn_on(self, **kwargs) -> None: def turn_off(self, **kwargs) -> None: """Turn off.""" - import yeelight duration = int(self.config[CONF_TRANSITION]) # in ms if ATTR_TRANSITION in kwargs: # passed kwarg overrides config duration = int(kwargs.get(ATTR_TRANSITION) * 1000) # kwarg in s - try: - self._bulb.turn_off(duration=duration) - except yeelight.BulbException as ex: - _LOGGER.error("Unable to turn the bulb off: %s", ex) - def set_mode(self, mode: str): - """Set a power mode.""" - import yeelight - try: - self._bulb.set_power_mode(yeelight.enums.PowerMode[mode.upper()]) - self.async_schedule_update_ha_state(True) - except yeelight.BulbException as ex: - _LOGGER.error("Unable to set the power mode: %s", ex) + self._device.turn_off(duration=duration) def start_flow(self, transitions, count=0, action=ACTION_RECOVER): """Start flow.""" diff --git a/homeassistant/components/yeelight/switch.py b/homeassistant/components/yeelight/switch.py index 1419d60687699e..cf3ae70bff5dc0 100644 --- a/homeassistant/components/yeelight/switch.py +++ b/homeassistant/components/yeelight/switch.py @@ -4,14 +4,15 @@ from netdisco.const import ATTR_HOST from homeassistant.const import CONF_DEVICES +from homeassistant.core import callback from homeassistant.helpers.entity import ToggleEntity -from homeassistant.components.yeelight import DATA_YEELIGHT +from homeassistant.components.yeelight import DATA_YEELIGHT, MODE_MOONLIGHT, \ + MODE_DAYLIGHT DEPENDENCIES = ['yeelight'] _LOGGER = logging.getLogger(__name__) - def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Yeelight switches.""" @@ -31,10 +32,24 @@ class YeelightPowerModeSwitch(ToggleEntity): def __init__(self, device): self._device = device + self.register_callbacks() + + @callback + def register_callbacks(self): + """Register callbacks to update hass after device was changed.""" + def after_update_callback(device): + """Call after device was updated.""" + self.async_schedule_update_ha_state(True) + self._device.register_device_updated_cb(after_update_callback) + + @property + def should_poll(self): + """No polling needed""" + return False @property def is_on(self) -> bool: - return self._bulb.last_properties.get('active_mode') == '1' + return self._device.is_nightlight_enabled @property def name(self): @@ -50,15 +65,7 @@ def icon(self): return 'mdi:weather-night' def turn_on(self, **kwargs) -> None: - import yeelight - - self._bulb.set_power_mode(yeelight.enums.PowerMode.MOONLIGHT) - self._device.update() - self.async_schedule_update_ha_state(True) + self._device.set_mode(MODE_MOONLIGHT) def turn_off(self, **kwargs) -> None: - import yeelight - - self._bulb.set_power_mode(yeelight.enums.PowerMode.NORMAL) - self._device.update() - self.async_schedule_update_ha_state(True) + self._device.set_mode(MODE_DAYLIGHT)