mirror of
https://github.com/home-assistant/core.git
synced 2025-07-08 05:47:10 +00:00
Xiaomi MiIO Light: Philips Eyecare Lamp 2 support (#10007)
* Xiaomi Philips Eyecare Lamp 2 support introduced. * Code clean-up. * Make hound happy again (idents). * Revert "Code clean-up." This reverts commit ea637602ff02451bcdff1b4ddf40498559419b90. * Unused platform constant removed. * Nice compromise of the code clean-up implemented.
This commit is contained in:
parent
248d974ded
commit
fe9b45c964
@ -1,5 +1,5 @@
|
|||||||
"""
|
"""
|
||||||
Support for Xiaomi Philips Lights (LED Ball & Ceil).
|
Support for Xiaomi Philips Lights (LED Ball & Ceiling Lamp, Eyecare Lamp 2).
|
||||||
|
|
||||||
For more details about this platform, please refer to the documentation
|
For more details about this platform, please refer to the documentation
|
||||||
https://home-assistant.io/components/light.xiaomi_philipslight/
|
https://home-assistant.io/components/light.xiaomi_philipslight/
|
||||||
@ -21,7 +21,7 @@ from homeassistant.exceptions import PlatformNotReady
|
|||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
DEFAULT_NAME = 'Xiaomi Philips Light'
|
DEFAULT_NAME = 'Xiaomi Philips Light'
|
||||||
PLATFORM = 'xiaomi_miio'
|
|
||||||
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
|
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
|
||||||
vol.Required(CONF_HOST): cv.string,
|
vol.Required(CONF_HOST): cv.string,
|
||||||
vol.Required(CONF_TOKEN): vol.All(cv.string, vol.Length(min=32, max=32)),
|
vol.Required(CONF_TOKEN): vol.All(cv.string, vol.Length(min=32, max=32)),
|
||||||
@ -42,9 +42,7 @@ ATTR_MODEL = 'model'
|
|||||||
@asyncio.coroutine
|
@asyncio.coroutine
|
||||||
def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
|
def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
|
||||||
"""Set up the light from config."""
|
"""Set up the light from config."""
|
||||||
from mirobo import Ceil, DeviceException
|
from miio import Device, DeviceException
|
||||||
if PLATFORM not in hass.data:
|
|
||||||
hass.data[PLATFORM] = {}
|
|
||||||
|
|
||||||
host = config.get(CONF_HOST)
|
host = config.get(CONF_HOST)
|
||||||
name = config.get(CONF_NAME)
|
name = config.get(CONF_NAME)
|
||||||
@ -52,23 +50,43 @@ def async_setup_platform(hass, config, async_add_devices, discovery_info=None):
|
|||||||
|
|
||||||
_LOGGER.info("Initializing with host %s (token %s...)", host, token[:5])
|
_LOGGER.info("Initializing with host %s (token %s...)", host, token[:5])
|
||||||
|
|
||||||
|
devices = []
|
||||||
try:
|
try:
|
||||||
light = Ceil(host, token)
|
light = Device(host, token)
|
||||||
device_info = light.info()
|
device_info = light.info()
|
||||||
_LOGGER.info("%s %s %s initialized",
|
_LOGGER.info("%s %s %s initialized",
|
||||||
device_info.raw['model'],
|
device_info.model,
|
||||||
device_info.raw['fw_ver'],
|
device_info.firmware_version,
|
||||||
device_info.raw['hw_ver'])
|
device_info.hardware_version)
|
||||||
|
|
||||||
|
if device_info.model == 'philips.light.sread1':
|
||||||
|
from miio import PhilipsEyecare
|
||||||
|
light = PhilipsEyecare(host, token)
|
||||||
|
device = XiaomiPhilipsEyecareLamp(name, light, device_info)
|
||||||
|
devices.append(device)
|
||||||
|
elif device_info.model == 'philips.light.ceil':
|
||||||
|
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 Ceil
|
||||||
|
light = Ceil(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)
|
||||||
|
|
||||||
philips_light = XiaomiPhilipsLight(name, light, device_info)
|
|
||||||
hass.data[PLATFORM][host] = philips_light
|
|
||||||
except DeviceException:
|
except DeviceException:
|
||||||
raise PlatformNotReady
|
raise PlatformNotReady
|
||||||
|
|
||||||
async_add_devices([philips_light], update_before_add=True)
|
async_add_devices(devices, update_before_add=True)
|
||||||
|
|
||||||
|
|
||||||
class XiaomiPhilipsLight(Light):
|
class XiaomiPhilipsGenericLight(Light):
|
||||||
"""Representation of a Xiaomi Philips Light."""
|
"""Representation of a Xiaomi Philips Light."""
|
||||||
|
|
||||||
def __init__(self, name, light, device_info):
|
def __init__(self, name, light, device_info):
|
||||||
@ -82,7 +100,7 @@ class XiaomiPhilipsLight(Light):
|
|||||||
self._light = light
|
self._light = light
|
||||||
self._state = None
|
self._state = None
|
||||||
self._state_attrs = {
|
self._state_attrs = {
|
||||||
ATTR_MODEL: self._device_info.raw['model'],
|
ATTR_MODEL: self._device_info.model,
|
||||||
}
|
}
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@ -115,30 +133,15 @@ class XiaomiPhilipsLight(Light):
|
|||||||
"""Return the brightness of this light between 0..255."""
|
"""Return the brightness of this light between 0..255."""
|
||||||
return self._brightness
|
return self._brightness
|
||||||
|
|
||||||
@property
|
|
||||||
def color_temp(self):
|
|
||||||
"""Return the color temperature."""
|
|
||||||
return self._color_temp
|
|
||||||
|
|
||||||
@property
|
|
||||||
def min_mireds(self):
|
|
||||||
"""Return the coldest color_temp that this light supports."""
|
|
||||||
return 175
|
|
||||||
|
|
||||||
@property
|
|
||||||
def max_mireds(self):
|
|
||||||
"""Return the warmest color_temp that this light supports."""
|
|
||||||
return 333
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def supported_features(self):
|
def supported_features(self):
|
||||||
"""Return the supported features."""
|
"""Return the supported features."""
|
||||||
return SUPPORT_BRIGHTNESS | SUPPORT_COLOR_TEMP
|
return SUPPORT_BRIGHTNESS
|
||||||
|
|
||||||
@asyncio.coroutine
|
@asyncio.coroutine
|
||||||
def _try_command(self, mask_error, func, *args, **kwargs):
|
def _try_command(self, mask_error, func, *args, **kwargs):
|
||||||
"""Call a light command handling error messages."""
|
"""Call a light command handling error messages."""
|
||||||
from mirobo import DeviceException
|
from miio import DeviceException
|
||||||
try:
|
try:
|
||||||
result = yield from self.hass.async_add_job(
|
result = yield from self.hass.async_add_job(
|
||||||
partial(func, *args, **kwargs))
|
partial(func, *args, **kwargs))
|
||||||
@ -168,6 +171,68 @@ class XiaomiPhilipsLight(Light):
|
|||||||
if result:
|
if result:
|
||||||
self._brightness = brightness
|
self._brightness = brightness
|
||||||
|
|
||||||
|
self._state = yield from self._try_command(
|
||||||
|
"Turning the light on failed.", self._light.on)
|
||||||
|
|
||||||
|
@asyncio.coroutine
|
||||||
|
def async_turn_off(self, **kwargs):
|
||||||
|
"""Turn the light off."""
|
||||||
|
self._state = yield from self._try_command(
|
||||||
|
"Turning the light off failed.", self._light.off)
|
||||||
|
|
||||||
|
@asyncio.coroutine
|
||||||
|
def async_update(self):
|
||||||
|
"""Fetch state from the device."""
|
||||||
|
from miio import DeviceException
|
||||||
|
try:
|
||||||
|
state = yield from self.hass.async_add_job(self._light.status)
|
||||||
|
_LOGGER.debug("Got new state: %s", state)
|
||||||
|
|
||||||
|
self._state = state.is_on
|
||||||
|
self._brightness = int(255 * 0.01 * state.brightness)
|
||||||
|
|
||||||
|
except DeviceException as ex:
|
||||||
|
_LOGGER.error("Got exception while fetching the state: %s", ex)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def translate(value, left_min, left_max, right_min, right_max):
|
||||||
|
"""Map a value from left span to right span."""
|
||||||
|
left_span = left_max - left_min
|
||||||
|
right_span = right_max - right_min
|
||||||
|
value_scaled = float(value - left_min) / float(left_span)
|
||||||
|
return int(right_min + (value_scaled * right_span))
|
||||||
|
|
||||||
|
|
||||||
|
class XiaomiPhilipsLightBall(XiaomiPhilipsGenericLight, Light):
|
||||||
|
"""Representation of a Xiaomi Philips Light Ball."""
|
||||||
|
|
||||||
|
def __init__(self, name, light, device_info):
|
||||||
|
"""Initialize the light device."""
|
||||||
|
super().__init__(name, light, device_info)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def color_temp(self):
|
||||||
|
"""Return the color temperature."""
|
||||||
|
return self._color_temp
|
||||||
|
|
||||||
|
@property
|
||||||
|
def min_mireds(self):
|
||||||
|
"""Return the coldest color_temp that this light supports."""
|
||||||
|
return 175
|
||||||
|
|
||||||
|
@property
|
||||||
|
def max_mireds(self):
|
||||||
|
"""Return the warmest color_temp that this light supports."""
|
||||||
|
return 333
|
||||||
|
|
||||||
|
@property
|
||||||
|
def supported_features(self):
|
||||||
|
"""Return the supported features."""
|
||||||
|
return SUPPORT_BRIGHTNESS | SUPPORT_COLOR_TEMP
|
||||||
|
|
||||||
|
@asyncio.coroutine
|
||||||
|
def async_turn_on(self, **kwargs):
|
||||||
|
"""Turn the light on."""
|
||||||
if ATTR_COLOR_TEMP in kwargs:
|
if ATTR_COLOR_TEMP in kwargs:
|
||||||
color_temp = kwargs[ATTR_COLOR_TEMP]
|
color_temp = kwargs[ATTR_COLOR_TEMP]
|
||||||
percent_color_temp = self.translate(
|
percent_color_temp = self.translate(
|
||||||
@ -186,42 +251,64 @@ class XiaomiPhilipsLight(Light):
|
|||||||
if result:
|
if result:
|
||||||
self._color_temp = color_temp
|
self._color_temp = color_temp
|
||||||
|
|
||||||
result = yield from self._try_command(
|
if ATTR_BRIGHTNESS in kwargs:
|
||||||
|
brightness = kwargs[ATTR_BRIGHTNESS]
|
||||||
|
percent_brightness = int(100 * brightness / 255)
|
||||||
|
|
||||||
|
_LOGGER.debug(
|
||||||
|
"Setting brightness: %s %s%%",
|
||||||
|
self.brightness, percent_brightness)
|
||||||
|
|
||||||
|
result = yield from self._try_command(
|
||||||
|
"Setting brightness failed: %s",
|
||||||
|
self._light.set_brightness, percent_brightness)
|
||||||
|
|
||||||
|
if result:
|
||||||
|
self._brightness = brightness
|
||||||
|
|
||||||
|
self._state = yield from self._try_command(
|
||||||
"Turning the light on failed.", self._light.on)
|
"Turning the light on failed.", self._light.on)
|
||||||
|
|
||||||
if result:
|
|
||||||
self._state = True
|
|
||||||
|
|
||||||
@asyncio.coroutine
|
|
||||||
def async_turn_off(self, **kwargs):
|
|
||||||
"""Turn the light off."""
|
|
||||||
result = yield from self._try_command(
|
|
||||||
"Turning the light off failed.", self._light.off)
|
|
||||||
|
|
||||||
if result:
|
|
||||||
self._state = True
|
|
||||||
|
|
||||||
@asyncio.coroutine
|
@asyncio.coroutine
|
||||||
def async_update(self):
|
def async_update(self):
|
||||||
"""Fetch state from the device."""
|
"""Fetch state from the device."""
|
||||||
from mirobo import DeviceException
|
from miio import DeviceException
|
||||||
try:
|
try:
|
||||||
state = yield from self.hass.async_add_job(self._light.status)
|
state = yield from self.hass.async_add_job(self._light.status)
|
||||||
_LOGGER.debug("Got new state: %s", state)
|
_LOGGER.debug("Got new state: %s", state)
|
||||||
|
|
||||||
self._state = state.is_on
|
self._state = state.is_on
|
||||||
self._brightness = int(255 * 0.01 * state.brightness)
|
self._brightness = int(255 * 0.01 * state.brightness)
|
||||||
self._color_temp = self.translate(state.color_temperature,
|
self._color_temp = self.translate(
|
||||||
CCT_MIN, CCT_MAX,
|
state.color_temperature,
|
||||||
self.max_mireds, self.min_mireds)
|
CCT_MIN, CCT_MAX,
|
||||||
|
self.max_mireds, self.min_mireds)
|
||||||
|
|
||||||
except DeviceException as ex:
|
except DeviceException as ex:
|
||||||
_LOGGER.error("Got exception while fetching the state: %s", ex)
|
_LOGGER.error("Got exception while fetching the state: %s", ex)
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def translate(value, left_min, left_max, right_min, right_max):
|
class XiaomiPhilipsCeilingLamp(XiaomiPhilipsLightBall, Light):
|
||||||
"""Map a value from left span to right span."""
|
"""Representation of a Xiaomi Philips Ceiling Lamp."""
|
||||||
left_span = left_max - left_min
|
|
||||||
right_span = right_max - right_min
|
def __init__(self, name, light, device_info):
|
||||||
value_scaled = float(value - left_min) / float(left_span)
|
"""Initialize the light device."""
|
||||||
return int(right_min + (value_scaled * right_span))
|
super().__init__(name, light, device_info)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def min_mireds(self):
|
||||||
|
"""Return the coldest color_temp that this light supports."""
|
||||||
|
return 175
|
||||||
|
|
||||||
|
@property
|
||||||
|
def max_mireds(self):
|
||||||
|
"""Return the warmest color_temp that this light supports."""
|
||||||
|
return 370
|
||||||
|
|
||||||
|
|
||||||
|
class XiaomiPhilipsEyecareLamp(XiaomiPhilipsGenericLight, Light):
|
||||||
|
"""Representation of a Xiaomi Philips Eyecare Lamp 2."""
|
||||||
|
|
||||||
|
def __init__(self, name, light, device_info):
|
||||||
|
"""Initialize the light device."""
|
||||||
|
super().__init__(name, light, device_info)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user