diff --git a/homeassistant/components/light/services.yaml b/homeassistant/components/light/services.yaml index 23814f16598..0bcf6933e68 100644 --- a/homeassistant/components/light/services.yaml +++ b/homeassistant/components/light/services.yaml @@ -159,3 +159,13 @@ lifx_effect_stop: entity_id: description: Name(s) of entities to stop effects on. Leave out to stop effects everywhere. example: 'light.bedroom' + +xiaomi_miio_set_scene: + description: Set a fixed scene. + fields: + entity_id: + description: Name of the light entity. + example: 'light.xiaomi_miio' + scene: + description: Number of the fixed scene, between 1 and 4. + example: 1 diff --git a/homeassistant/components/light/xiaomi_miio.py b/homeassistant/components/light/xiaomi_miio.py index 9812933b7d6..cd3ea9f0f39 100644 --- a/homeassistant/components/light/xiaomi_miio.py +++ b/homeassistant/components/light/xiaomi_miio.py @@ -13,7 +13,7 @@ import voluptuous as vol import homeassistant.helpers.config_validation as cv from homeassistant.components.light import ( PLATFORM_SCHEMA, ATTR_BRIGHTNESS, SUPPORT_BRIGHTNESS, - ATTR_COLOR_TEMP, SUPPORT_COLOR_TEMP, Light, ) + ATTR_COLOR_TEMP, SUPPORT_COLOR_TEMP, Light, ATTR_ENTITY_ID, DOMAIN, ) from homeassistant.const import (CONF_NAME, CONF_HOST, CONF_TOKEN, ) from homeassistant.exceptions import PlatformNotReady @@ -21,6 +21,7 @@ from homeassistant.exceptions import PlatformNotReady _LOGGER = logging.getLogger(__name__) DEFAULT_NAME = 'Xiaomi Philips Light' +PLATFORM = 'xiaomi_miio' PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({ vol.Required(CONF_HOST): cv.string, @@ -36,6 +37,24 @@ CCT_MAX = 100 SUCCESS = ['ok'] ATTR_MODEL = 'model' +ATTR_SCENE = 'scene' + +SERVICE_SET_SCENE = 'xiaomi_miio_set_scene' + +XIAOMI_MIIO_SERVICE_SCHEMA = vol.Schema({ + vol.Optional(ATTR_ENTITY_ID): cv.entity_ids, +}) + +SERVICE_SCHEMA_SCENE = XIAOMI_MIIO_SERVICE_SCHEMA.extend({ + vol.Required(ATTR_SCENE): + vol.All(vol.Coerce(int), vol.Clamp(min=1, max=4)) +}) + +SERVICE_TO_METHOD = { + SERVICE_SET_SCENE: { + 'method': 'async_set_scene', + 'schema': SERVICE_SCHEMA_SCENE} +} # pylint: disable=unused-argument @@ -43,6 +62,8 @@ ATTR_MODEL = 'model' def async_setup_platform(hass, config, async_add_devices, discovery_info=None): """Set up the light from config.""" from miio import Device, DeviceException + if PLATFORM not in hass.data: + hass.data[PLATFORM] = {} host = config.get(CONF_HOST) name = config.get(CONF_NAME) @@ -50,7 +71,6 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None): _LOGGER.info("Initializing with host %s (token %s...)", host, token[:5]) - devices = [] try: light = Device(host, token) device_info = light.info() @@ -63,27 +83,53 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None): from miio import PhilipsEyecare light = PhilipsEyecare(host, token) device = XiaomiPhilipsEyecareLamp(name, light, device_info) - devices.append(device) elif device_info.model == 'philips.light.ceiling': from miio import Ceil light = Ceil(host, token) device = XiaomiPhilipsCeilingLamp(name, light, device_info) - devices.append(device) elif device_info.model == 'philips.light.bulb': from miio import PhilipsBulb light = PhilipsBulb(host, token) device = XiaomiPhilipsLightBall(name, light, device_info) - devices.append(device) else: _LOGGER.error( 'Unsupported device found! Please create an issue at ' 'https://github.com/rytilahti/python-miio/issues ' 'and provide the following data: %s', device_info.model) + return False except DeviceException: raise PlatformNotReady - async_add_devices(devices, update_before_add=True) + hass.data[PLATFORM][host] = device + async_add_devices([device], update_before_add=True) + + @asyncio.coroutine + def async_service_handler(service): + """Map services to methods on Xiaomi Philips Lights.""" + method = SERVICE_TO_METHOD.get(service.service) + params = {key: value for key, value in service.data.items() + if key != ATTR_ENTITY_ID} + entity_ids = service.data.get(ATTR_ENTITY_ID) + if entity_ids: + target_devices = [dev for dev in hass.data[PLATFORM].values() + if dev.entity_id in entity_ids] + else: + target_devices = hass.data[PLATFORM].values() + + update_tasks = [] + for target_device in target_devices: + yield from getattr(target_device, method['method'])(**params) + update_tasks.append(target_device.async_update_ha_state(True)) + + if update_tasks: + yield from asyncio.wait(update_tasks, loop=hass.loop) + + for xiaomi_miio_service in SERVICE_TO_METHOD: + schema = SERVICE_TO_METHOD[xiaomi_miio_service].get( + 'schema', XIAOMI_MIIO_SERVICE_SCHEMA) + hass.services.async_register( + DOMAIN, xiaomi_miio_service, async_service_handler, schema=schema) class XiaomiPhilipsGenericLight(Light): @@ -194,6 +240,13 @@ class XiaomiPhilipsGenericLight(Light): except DeviceException as ex: _LOGGER.error("Got exception while fetching the state: %s", ex) + @asyncio.coroutine + def async_set_scene(self, scene: int=1): + """Set the fixed scene.""" + yield from self._try_command( + "Setting a fixed scene failed.", + self._light.set_scene, scene) + @staticmethod def translate(value, left_min, left_max, right_min, right_max): """Map a value from left span to right span."""