From 1ab7103aea51344675764bcca3ee17f917013b00 Mon Sep 17 00:00:00 2001 From: Anders Melchiorsen Date: Fri, 12 May 2017 18:19:51 +0200 Subject: [PATCH] LIFX: add lifx_set_state service call (#7552) * Move service helpers to LifxManager * Add lifx_set_color This is a synonym for light.turn_on except it does not actually turn on the light unless asked to. Thus, turn_on can be implemented just by asking. * Rename set_color to set_state * Support power=False with lifx_set_state --- .../components/light/lifx/__init__.py | 102 ++++++++++++++---- .../components/light/lifx/effects.py | 18 +--- .../components/light/lifx/services.yaml | 20 ++++ 3 files changed, 105 insertions(+), 35 deletions(-) diff --git a/homeassistant/components/light/lifx/__init__.py b/homeassistant/components/light/lifx/__init__.py index f13934011e9..17512f5dd3b 100644 --- a/homeassistant/components/light/lifx/__init__.py +++ b/homeassistant/components/light/lifx/__init__.py @@ -9,6 +9,7 @@ import logging import asyncio import sys import math +from os import path from functools import partial from datetime import timedelta import async_timeout @@ -16,15 +17,18 @@ import async_timeout import voluptuous as vol from homeassistant.components.light import ( - Light, PLATFORM_SCHEMA, ATTR_BRIGHTNESS, ATTR_COLOR_NAME, ATTR_RGB_COLOR, + Light, DOMAIN, PLATFORM_SCHEMA, LIGHT_TURN_ON_SCHEMA, + ATTR_BRIGHTNESS, ATTR_COLOR_NAME, ATTR_RGB_COLOR, ATTR_XY_COLOR, ATTR_COLOR_TEMP, ATTR_TRANSITION, ATTR_EFFECT, SUPPORT_BRIGHTNESS, SUPPORT_COLOR_TEMP, SUPPORT_RGB_COLOR, SUPPORT_XY_COLOR, SUPPORT_TRANSITION, SUPPORT_EFFECT) +from homeassistant.config import load_yaml_config_file from homeassistant.util.color import ( color_temperature_mired_to_kelvin, color_temperature_kelvin_to_mired) from homeassistant import util from homeassistant.core import callback from homeassistant.helpers.event import async_track_point_in_utc_time +from homeassistant.helpers.service import extract_entity_ids import homeassistant.helpers.config_validation as cv import homeassistant.util.color as color_util @@ -41,7 +45,10 @@ BULB_LATENCY = 500 CONF_SERVER = 'server' +SERVICE_LIFX_SET_STATE = 'lifx_set_state' + ATTR_HSBK = 'hsbk' +ATTR_POWER = 'power' BYTE_MAX = 255 SHORT_MAX = 65535 @@ -53,6 +60,10 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ vol.Optional(CONF_SERVER, default='0.0.0.0'): cv.string, }) +LIFX_SET_STATE_SCHEMA = LIGHT_TURN_ON_SCHEMA.extend({ + ATTR_POWER: cv.boolean, +}) + @asyncio.coroutine def async_setup_platform(hass, config, async_add_devices, discovery_info=None): @@ -87,6 +98,41 @@ class LIFXManager(object): self.hass = hass self.async_add_devices = async_add_devices + @asyncio.coroutine + def async_service_handle(service): + """Apply a service.""" + tasks = [] + for light in self.service_to_entities(service): + if service.service == SERVICE_LIFX_SET_STATE: + task = light.async_set_state(**service.data) + tasks.append(hass.async_add_job(task)) + if tasks: + yield from asyncio.wait(tasks, loop=hass.loop) + + descriptions = self.get_descriptions() + + hass.services.async_register( + DOMAIN, SERVICE_LIFX_SET_STATE, async_service_handle, + descriptions.get(SERVICE_LIFX_SET_STATE), + schema=LIFX_SET_STATE_SCHEMA) + + @staticmethod + def get_descriptions(): + """Load and return descriptions for our own service calls.""" + return load_yaml_config_file( + path.join(path.dirname(__file__), 'services.yaml')) + + def service_to_entities(self, service): + """Return the known devices that a service call mentions.""" + entity_ids = extract_entity_ids(self.hass, service) + if entity_ids: + entities = [entity for entity in self.entities.values() + if entity.entity_id in entity_ids] + else: + entities = list(self.entities.values()) + + return entities + @callback def register(self, device): """Handle for newly detected bulb.""" @@ -298,6 +344,18 @@ class LIFXLight(Light): @asyncio.coroutine def async_turn_on(self, **kwargs): """Turn the device on.""" + kwargs[ATTR_POWER] = True + yield from self.async_set_state(**kwargs) + + @asyncio.coroutine + def async_turn_off(self, **kwargs): + """Turn the device off.""" + kwargs[ATTR_POWER] = False + yield from self.async_set_state(**kwargs) + + @asyncio.coroutine + def async_set_state(self, **kwargs): + """Set a color on the light and turn it on/off.""" yield from self.stop_effect() if ATTR_EFFECT in kwargs: @@ -309,39 +367,41 @@ class LIFXLight(Light): else: fade = 0 + # These are both False if ATTR_POWER is not set + power_on = kwargs.get(ATTR_POWER, False) + power_off = not kwargs.get(ATTR_POWER, True) + hsbk, changed_color = self.find_hsbk(**kwargs) _LOGGER.debug("turn_on: %s (%d) %d %d %d %d %d", self.who, self._power, fade, *hsbk) if self._power == 0: + if power_off: + self.device.set_power(False, None, 0) if changed_color: self.device.set_color(hsbk, None, 0) - self.device.set_power(True, None, fade) + if power_on: + self.device.set_power(True, None, fade) else: - self.device.set_power(True, None, 0) # racing for power status + if power_on: + self.device.set_power(True, None, 0) if changed_color: self.device.set_color(hsbk, None, fade) + if power_off: + self.device.set_power(False, None, fade) - self.update_later(0) - if fade < BULB_LATENCY: - self.set_power(1) - self.set_color(*hsbk) - - @asyncio.coroutine - def async_turn_off(self, **kwargs): - """Turn the device off.""" - yield from self.stop_effect() - - if ATTR_TRANSITION in kwargs: - fade = int(kwargs[ATTR_TRANSITION] * 1000) + if power_on: + self.update_later(0) else: - fade = 0 + self.update_later(fade) - self.device.set_power(False, None, fade) - - self.update_later(fade) - if fade < BULB_LATENCY: - self.set_power(0) + if fade <= BULB_LATENCY: + if power_on: + self.set_power(1) + if power_off: + self.set_power(0) + if changed_color: + self.set_color(*hsbk) @asyncio.coroutine def async_update(self): diff --git a/homeassistant/components/light/lifx/effects.py b/homeassistant/components/light/lifx/effects.py index 85de663a817..315759738f9 100644 --- a/homeassistant/components/light/lifx/effects.py +++ b/homeassistant/components/light/lifx/effects.py @@ -2,16 +2,13 @@ import logging import asyncio import random -from os import path import voluptuous as vol from homeassistant.components.light import ( DOMAIN, ATTR_BRIGHTNESS, ATTR_COLOR_NAME, ATTR_RGB_COLOR, ATTR_EFFECT, ATTR_TRANSITION) -from homeassistant.config import load_yaml_config_file from homeassistant.const import (ATTR_ENTITY_ID) -from homeassistant.helpers.service import extract_entity_ids import homeassistant.helpers.config_validation as cv _LOGGER = logging.getLogger(__name__) @@ -73,19 +70,12 @@ def setup(hass, lifx_manager): @asyncio.coroutine def async_service_handle(service): """Apply a service.""" - entity_ids = extract_entity_ids(hass, service) - if entity_ids: - devices = [entity for entity in lifx_manager.entities.values() - if entity.entity_id in entity_ids] - else: - devices = list(lifx_manager.entities.values()) - - if devices: - yield from start_effect(hass, devices, + entities = lifx_manager.service_to_entities(service) + if entities: + yield from start_effect(hass, entities, service.service, **service.data) - descriptions = load_yaml_config_file( - path.join(path.dirname(__file__), 'services.yaml')) + descriptions = lifx_manager.get_descriptions() hass.services.async_register( DOMAIN, SERVICE_EFFECT_BREATHE, async_service_handle, diff --git a/homeassistant/components/light/lifx/services.yaml b/homeassistant/components/light/lifx/services.yaml index d939e1432bc..a907a665753 100644 --- a/homeassistant/components/light/lifx/services.yaml +++ b/homeassistant/components/light/lifx/services.yaml @@ -1,3 +1,23 @@ +lifx_set_state: + description: Set a color/brightness and possibliy turn the light on/off + + fields: + entity_id: + description: Name(s) of entities to set a state on + example: 'light.garage' + + '...': + description: All turn_on parameters can be used to specify a color + + transition: + description: Duration in seconds it takes to get to the final state + example: 10 + + power: + description: Turn the light on (True) or off (False). Leave out to keep the power as it is. + example: True + + lifx_effect_breathe: description: Run a breathe effect by fading to a color and back.