mirror of
https://github.com/home-assistant/core.git
synced 2025-07-19 11:17:21 +00:00
Add Philips Moonlight Bedside Lamp support (#18496)
* Add Philips Moonlight Bedside Lamp support * Update comment * Make hound happy * Wrap the call that could raise the exception only * Remote blank line * Use updated python-miio API
This commit is contained in:
parent
ffe83d9ab1
commit
2134331e2b
@ -1,5 +1,7 @@
|
|||||||
"""
|
"""
|
||||||
Support for Xiaomi Philips Lights (LED Ball & Ceiling Lamp, Eyecare Lamp 2).
|
Support for Xiaomi Philips Lights.
|
||||||
|
|
||||||
|
LED Ball, Candle, Downlight, Ceiling, Eyecare 2, Bedside & Desklamp Lamp.
|
||||||
|
|
||||||
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_miio/
|
https://home-assistant.io/components/light.xiaomi_miio/
|
||||||
@ -19,7 +21,7 @@ from homeassistant.components.light import (
|
|||||||
from homeassistant.const import CONF_HOST, CONF_NAME, CONF_TOKEN
|
from homeassistant.const import CONF_HOST, CONF_NAME, CONF_TOKEN
|
||||||
from homeassistant.exceptions import PlatformNotReady
|
from homeassistant.exceptions import PlatformNotReady
|
||||||
import homeassistant.helpers.config_validation as cv
|
import homeassistant.helpers.config_validation as cv
|
||||||
from homeassistant.util import dt
|
from homeassistant.util import color, dt
|
||||||
|
|
||||||
REQUIREMENTS = ['python-miio==0.4.4', 'construct==2.9.45']
|
REQUIREMENTS = ['python-miio==0.4.4', 'construct==2.9.45']
|
||||||
|
|
||||||
@ -38,6 +40,7 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
|
|||||||
['philips.light.sread1',
|
['philips.light.sread1',
|
||||||
'philips.light.ceiling',
|
'philips.light.ceiling',
|
||||||
'philips.light.zyceiling',
|
'philips.light.zyceiling',
|
||||||
|
'philips.light.moonlight',
|
||||||
'philips.light.bulb',
|
'philips.light.bulb',
|
||||||
'philips.light.candle',
|
'philips.light.candle',
|
||||||
'philips.light.candle2',
|
'philips.light.candle2',
|
||||||
@ -63,6 +66,13 @@ ATTR_AUTOMATIC_COLOR_TEMPERATURE = 'automatic_color_temperature'
|
|||||||
ATTR_REMINDER = 'reminder'
|
ATTR_REMINDER = 'reminder'
|
||||||
ATTR_EYECARE_MODE = 'eyecare_mode'
|
ATTR_EYECARE_MODE = 'eyecare_mode'
|
||||||
|
|
||||||
|
# Moonlight
|
||||||
|
ATTR_SLEEP_ASSISTANT = 'sleep_assistant'
|
||||||
|
ATTR_SLEEP_OFF_TIME = 'sleep_off_time'
|
||||||
|
ATTR_TOTAL_ASSISTANT_SLEEP_TIME = 'total_assistant_sleep_time'
|
||||||
|
ATTR_BRAND_SLEEP = 'brand_sleep'
|
||||||
|
ATTR_BRAND = 'brand'
|
||||||
|
|
||||||
SERVICE_SET_SCENE = 'xiaomi_miio_set_scene'
|
SERVICE_SET_SCENE = 'xiaomi_miio_set_scene'
|
||||||
SERVICE_SET_DELAYED_TURN_OFF = 'xiaomi_miio_set_delayed_turn_off'
|
SERVICE_SET_DELAYED_TURN_OFF = 'xiaomi_miio_set_delayed_turn_off'
|
||||||
SERVICE_REMINDER_ON = 'xiaomi_miio_reminder_on'
|
SERVICE_REMINDER_ON = 'xiaomi_miio_reminder_on'
|
||||||
@ -151,6 +161,12 @@ async def async_setup_platform(hass, config, async_add_entities,
|
|||||||
device = XiaomiPhilipsCeilingLamp(name, light, model, unique_id)
|
device = XiaomiPhilipsCeilingLamp(name, light, model, unique_id)
|
||||||
devices.append(device)
|
devices.append(device)
|
||||||
hass.data[DATA_KEY][host] = device
|
hass.data[DATA_KEY][host] = device
|
||||||
|
elif model == 'philips.light.moonlight':
|
||||||
|
from miio import PhilipsMoonlight
|
||||||
|
light = PhilipsMoonlight(host, token)
|
||||||
|
device = XiaomiPhilipsMoonlightLamp(name, light, model, unique_id)
|
||||||
|
devices.append(device)
|
||||||
|
hass.data[DATA_KEY][host] = device
|
||||||
elif model in ['philips.light.bulb',
|
elif model in ['philips.light.bulb',
|
||||||
'philips.light.candle',
|
'philips.light.candle',
|
||||||
'philips.light.candle2',
|
'philips.light.candle2',
|
||||||
@ -307,15 +323,15 @@ class XiaomiPhilipsAbstractLight(Light):
|
|||||||
from miio import DeviceException
|
from miio import DeviceException
|
||||||
try:
|
try:
|
||||||
state = await self.hass.async_add_executor_job(self._light.status)
|
state = await self.hass.async_add_executor_job(self._light.status)
|
||||||
_LOGGER.debug("Got new state: %s", state)
|
|
||||||
|
|
||||||
self._available = True
|
|
||||||
self._state = state.is_on
|
|
||||||
self._brightness = ceil((255 / 100.0) * state.brightness)
|
|
||||||
|
|
||||||
except DeviceException as ex:
|
except DeviceException as ex:
|
||||||
self._available = False
|
self._available = False
|
||||||
_LOGGER.error("Got exception while fetching the state: %s", ex)
|
_LOGGER.error("Got exception while fetching the state: %s", ex)
|
||||||
|
return
|
||||||
|
|
||||||
|
_LOGGER.debug("Got new state: %s", state)
|
||||||
|
self._available = True
|
||||||
|
self._state = state.is_on
|
||||||
|
self._brightness = ceil((255 / 100.0) * state.brightness)
|
||||||
|
|
||||||
|
|
||||||
class XiaomiPhilipsGenericLight(XiaomiPhilipsAbstractLight):
|
class XiaomiPhilipsGenericLight(XiaomiPhilipsAbstractLight):
|
||||||
@ -335,25 +351,25 @@ class XiaomiPhilipsGenericLight(XiaomiPhilipsAbstractLight):
|
|||||||
from miio import DeviceException
|
from miio import DeviceException
|
||||||
try:
|
try:
|
||||||
state = await self.hass.async_add_executor_job(self._light.status)
|
state = await self.hass.async_add_executor_job(self._light.status)
|
||||||
_LOGGER.debug("Got new state: %s", state)
|
|
||||||
|
|
||||||
self._available = True
|
|
||||||
self._state = state.is_on
|
|
||||||
self._brightness = ceil((255 / 100.0) * state.brightness)
|
|
||||||
|
|
||||||
delayed_turn_off = self.delayed_turn_off_timestamp(
|
|
||||||
state.delay_off_countdown,
|
|
||||||
dt.utcnow(),
|
|
||||||
self._state_attrs[ATTR_DELAYED_TURN_OFF])
|
|
||||||
|
|
||||||
self._state_attrs.update({
|
|
||||||
ATTR_SCENE: state.scene,
|
|
||||||
ATTR_DELAYED_TURN_OFF: delayed_turn_off,
|
|
||||||
})
|
|
||||||
|
|
||||||
except DeviceException as ex:
|
except DeviceException as ex:
|
||||||
self._available = False
|
self._available = False
|
||||||
_LOGGER.error("Got exception while fetching the state: %s", ex)
|
_LOGGER.error("Got exception while fetching the state: %s", ex)
|
||||||
|
return
|
||||||
|
|
||||||
|
_LOGGER.debug("Got new state: %s", state)
|
||||||
|
self._available = True
|
||||||
|
self._state = state.is_on
|
||||||
|
self._brightness = ceil((255 / 100.0) * state.brightness)
|
||||||
|
|
||||||
|
delayed_turn_off = self.delayed_turn_off_timestamp(
|
||||||
|
state.delay_off_countdown,
|
||||||
|
dt.utcnow(),
|
||||||
|
self._state_attrs[ATTR_DELAYED_TURN_OFF])
|
||||||
|
|
||||||
|
self._state_attrs.update({
|
||||||
|
ATTR_SCENE: state.scene,
|
||||||
|
ATTR_DELAYED_TURN_OFF: delayed_turn_off,
|
||||||
|
})
|
||||||
|
|
||||||
async def async_set_scene(self, scene: int = 1):
|
async def async_set_scene(self, scene: int = 1):
|
||||||
"""Set the fixed scene."""
|
"""Set the fixed scene."""
|
||||||
@ -485,29 +501,29 @@ class XiaomiPhilipsBulb(XiaomiPhilipsGenericLight):
|
|||||||
from miio import DeviceException
|
from miio import DeviceException
|
||||||
try:
|
try:
|
||||||
state = await self.hass.async_add_executor_job(self._light.status)
|
state = await self.hass.async_add_executor_job(self._light.status)
|
||||||
_LOGGER.debug("Got new state: %s", state)
|
|
||||||
|
|
||||||
self._available = True
|
|
||||||
self._state = state.is_on
|
|
||||||
self._brightness = ceil((255 / 100.0) * state.brightness)
|
|
||||||
self._color_temp = self.translate(
|
|
||||||
state.color_temperature,
|
|
||||||
CCT_MIN, CCT_MAX,
|
|
||||||
self.max_mireds, self.min_mireds)
|
|
||||||
|
|
||||||
delayed_turn_off = self.delayed_turn_off_timestamp(
|
|
||||||
state.delay_off_countdown,
|
|
||||||
dt.utcnow(),
|
|
||||||
self._state_attrs[ATTR_DELAYED_TURN_OFF])
|
|
||||||
|
|
||||||
self._state_attrs.update({
|
|
||||||
ATTR_SCENE: state.scene,
|
|
||||||
ATTR_DELAYED_TURN_OFF: delayed_turn_off,
|
|
||||||
})
|
|
||||||
|
|
||||||
except DeviceException as ex:
|
except DeviceException as ex:
|
||||||
self._available = False
|
self._available = False
|
||||||
_LOGGER.error("Got exception while fetching the state: %s", ex)
|
_LOGGER.error("Got exception while fetching the state: %s", ex)
|
||||||
|
return
|
||||||
|
|
||||||
|
_LOGGER.debug("Got new state: %s", state)
|
||||||
|
self._available = True
|
||||||
|
self._state = state.is_on
|
||||||
|
self._brightness = ceil((255 / 100.0) * state.brightness)
|
||||||
|
self._color_temp = self.translate(
|
||||||
|
state.color_temperature,
|
||||||
|
CCT_MIN, CCT_MAX,
|
||||||
|
self.max_mireds, self.min_mireds)
|
||||||
|
|
||||||
|
delayed_turn_off = self.delayed_turn_off_timestamp(
|
||||||
|
state.delay_off_countdown,
|
||||||
|
dt.utcnow(),
|
||||||
|
self._state_attrs[ATTR_DELAYED_TURN_OFF])
|
||||||
|
|
||||||
|
self._state_attrs.update({
|
||||||
|
ATTR_SCENE: state.scene,
|
||||||
|
ATTR_DELAYED_TURN_OFF: delayed_turn_off,
|
||||||
|
})
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def translate(value, left_min, left_max, right_min, right_max):
|
def translate(value, left_min, left_max, right_min, right_max):
|
||||||
@ -545,32 +561,32 @@ class XiaomiPhilipsCeilingLamp(XiaomiPhilipsBulb):
|
|||||||
from miio import DeviceException
|
from miio import DeviceException
|
||||||
try:
|
try:
|
||||||
state = await self.hass.async_add_executor_job(self._light.status)
|
state = await self.hass.async_add_executor_job(self._light.status)
|
||||||
_LOGGER.debug("Got new state: %s", state)
|
|
||||||
|
|
||||||
self._available = True
|
|
||||||
self._state = state.is_on
|
|
||||||
self._brightness = ceil((255 / 100.0) * state.brightness)
|
|
||||||
self._color_temp = self.translate(
|
|
||||||
state.color_temperature,
|
|
||||||
CCT_MIN, CCT_MAX,
|
|
||||||
self.max_mireds, self.min_mireds)
|
|
||||||
|
|
||||||
delayed_turn_off = self.delayed_turn_off_timestamp(
|
|
||||||
state.delay_off_countdown,
|
|
||||||
dt.utcnow(),
|
|
||||||
self._state_attrs[ATTR_DELAYED_TURN_OFF])
|
|
||||||
|
|
||||||
self._state_attrs.update({
|
|
||||||
ATTR_SCENE: state.scene,
|
|
||||||
ATTR_DELAYED_TURN_OFF: delayed_turn_off,
|
|
||||||
ATTR_NIGHT_LIGHT_MODE: state.smart_night_light,
|
|
||||||
ATTR_AUTOMATIC_COLOR_TEMPERATURE:
|
|
||||||
state.automatic_color_temperature,
|
|
||||||
})
|
|
||||||
|
|
||||||
except DeviceException as ex:
|
except DeviceException as ex:
|
||||||
self._available = False
|
self._available = False
|
||||||
_LOGGER.error("Got exception while fetching the state: %s", ex)
|
_LOGGER.error("Got exception while fetching the state: %s", ex)
|
||||||
|
return
|
||||||
|
|
||||||
|
_LOGGER.debug("Got new state: %s", state)
|
||||||
|
self._available = True
|
||||||
|
self._state = state.is_on
|
||||||
|
self._brightness = ceil((255 / 100.0) * state.brightness)
|
||||||
|
self._color_temp = self.translate(
|
||||||
|
state.color_temperature,
|
||||||
|
CCT_MIN, CCT_MAX,
|
||||||
|
self.max_mireds, self.min_mireds)
|
||||||
|
|
||||||
|
delayed_turn_off = self.delayed_turn_off_timestamp(
|
||||||
|
state.delay_off_countdown,
|
||||||
|
dt.utcnow(),
|
||||||
|
self._state_attrs[ATTR_DELAYED_TURN_OFF])
|
||||||
|
|
||||||
|
self._state_attrs.update({
|
||||||
|
ATTR_SCENE: state.scene,
|
||||||
|
ATTR_DELAYED_TURN_OFF: delayed_turn_off,
|
||||||
|
ATTR_NIGHT_LIGHT_MODE: state.smart_night_light,
|
||||||
|
ATTR_AUTOMATIC_COLOR_TEMPERATURE:
|
||||||
|
state.automatic_color_temperature,
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
class XiaomiPhilipsEyecareLamp(XiaomiPhilipsGenericLight):
|
class XiaomiPhilipsEyecareLamp(XiaomiPhilipsGenericLight):
|
||||||
@ -591,28 +607,28 @@ class XiaomiPhilipsEyecareLamp(XiaomiPhilipsGenericLight):
|
|||||||
from miio import DeviceException
|
from miio import DeviceException
|
||||||
try:
|
try:
|
||||||
state = await self.hass.async_add_executor_job(self._light.status)
|
state = await self.hass.async_add_executor_job(self._light.status)
|
||||||
_LOGGER.debug("Got new state: %s", state)
|
|
||||||
|
|
||||||
self._available = True
|
|
||||||
self._state = state.is_on
|
|
||||||
self._brightness = ceil((255 / 100.0) * state.brightness)
|
|
||||||
|
|
||||||
delayed_turn_off = self.delayed_turn_off_timestamp(
|
|
||||||
state.delay_off_countdown,
|
|
||||||
dt.utcnow(),
|
|
||||||
self._state_attrs[ATTR_DELAYED_TURN_OFF])
|
|
||||||
|
|
||||||
self._state_attrs.update({
|
|
||||||
ATTR_SCENE: state.scene,
|
|
||||||
ATTR_DELAYED_TURN_OFF: delayed_turn_off,
|
|
||||||
ATTR_REMINDER: state.reminder,
|
|
||||||
ATTR_NIGHT_LIGHT_MODE: state.smart_night_light,
|
|
||||||
ATTR_EYECARE_MODE: state.eyecare,
|
|
||||||
})
|
|
||||||
|
|
||||||
except DeviceException as ex:
|
except DeviceException as ex:
|
||||||
self._available = False
|
self._available = False
|
||||||
_LOGGER.error("Got exception while fetching the state: %s", ex)
|
_LOGGER.error("Got exception while fetching the state: %s", ex)
|
||||||
|
return
|
||||||
|
|
||||||
|
_LOGGER.debug("Got new state: %s", state)
|
||||||
|
self._available = True
|
||||||
|
self._state = state.is_on
|
||||||
|
self._brightness = ceil((255 / 100.0) * state.brightness)
|
||||||
|
|
||||||
|
delayed_turn_off = self.delayed_turn_off_timestamp(
|
||||||
|
state.delay_off_countdown,
|
||||||
|
dt.utcnow(),
|
||||||
|
self._state_attrs[ATTR_DELAYED_TURN_OFF])
|
||||||
|
|
||||||
|
self._state_attrs.update({
|
||||||
|
ATTR_SCENE: state.scene,
|
||||||
|
ATTR_DELAYED_TURN_OFF: delayed_turn_off,
|
||||||
|
ATTR_REMINDER: state.reminder,
|
||||||
|
ATTR_NIGHT_LIGHT_MODE: state.smart_night_light,
|
||||||
|
ATTR_EYECARE_MODE: state.eyecare,
|
||||||
|
})
|
||||||
|
|
||||||
async def async_set_delayed_turn_off(self, time_period: timedelta):
|
async def async_set_delayed_turn_off(self, time_period: timedelta):
|
||||||
"""Set delayed turn off."""
|
"""Set delayed turn off."""
|
||||||
@ -719,12 +735,84 @@ class XiaomiPhilipsEyecareLampAmbientLight(XiaomiPhilipsAbstractLight):
|
|||||||
from miio import DeviceException
|
from miio import DeviceException
|
||||||
try:
|
try:
|
||||||
state = await self.hass.async_add_executor_job(self._light.status)
|
state = await self.hass.async_add_executor_job(self._light.status)
|
||||||
_LOGGER.debug("Got new state: %s", state)
|
|
||||||
|
|
||||||
self._available = True
|
|
||||||
self._state = state.ambient
|
|
||||||
self._brightness = ceil((255 / 100.0) * state.ambient_brightness)
|
|
||||||
|
|
||||||
except DeviceException as ex:
|
except DeviceException as ex:
|
||||||
self._available = False
|
self._available = False
|
||||||
_LOGGER.error("Got exception while fetching the state: %s", ex)
|
_LOGGER.error("Got exception while fetching the state: %s", ex)
|
||||||
|
return
|
||||||
|
|
||||||
|
_LOGGER.debug("Got new state: %s", state)
|
||||||
|
self._available = True
|
||||||
|
self._state = state.ambient
|
||||||
|
self._brightness = ceil((255 / 100.0) * state.ambient_brightness)
|
||||||
|
|
||||||
|
|
||||||
|
class XiaomiPhilipsMoonlightLamp(XiaomiPhilipsBulb):
|
||||||
|
"""Representation of a Xiaomi Philips Zhirui Bedside Lamp."""
|
||||||
|
|
||||||
|
def __init__(self, name, light, model, unique_id):
|
||||||
|
"""Initialize the light device."""
|
||||||
|
super().__init__(name, light, model, unique_id)
|
||||||
|
|
||||||
|
self._hs_color = None
|
||||||
|
self._state_attrs.pop(ATTR_DELAYED_TURN_OFF)
|
||||||
|
self._state_attrs.update({
|
||||||
|
ATTR_SLEEP_ASSISTANT: None,
|
||||||
|
ATTR_SLEEP_OFF_TIME: None,
|
||||||
|
ATTR_TOTAL_ASSISTANT_SLEEP_TIME: None,
|
||||||
|
ATTR_BRAND_SLEEP: None,
|
||||||
|
ATTR_BRAND: None,
|
||||||
|
})
|
||||||
|
|
||||||
|
@property
|
||||||
|
def min_mireds(self):
|
||||||
|
"""Return the coldest color_temp that this light supports."""
|
||||||
|
return 153
|
||||||
|
|
||||||
|
@property
|
||||||
|
def max_mireds(self):
|
||||||
|
"""Return the warmest color_temp that this light supports."""
|
||||||
|
return 588
|
||||||
|
|
||||||
|
@property
|
||||||
|
def hs_color(self) -> tuple:
|
||||||
|
"""Return the hs color value."""
|
||||||
|
return self._hs_color
|
||||||
|
|
||||||
|
@property
|
||||||
|
def supported_features(self):
|
||||||
|
"""Return the supported features."""
|
||||||
|
return SUPPORT_BRIGHTNESS | SUPPORT_COLOR_TEMP
|
||||||
|
|
||||||
|
async def async_update(self):
|
||||||
|
"""Fetch state from the device."""
|
||||||
|
from miio import DeviceException
|
||||||
|
try:
|
||||||
|
state = await self.hass.async_add_executor_job(self._light.status)
|
||||||
|
except DeviceException as ex:
|
||||||
|
self._available = False
|
||||||
|
_LOGGER.error("Got exception while fetching the state: %s", ex)
|
||||||
|
return
|
||||||
|
|
||||||
|
_LOGGER.debug("Got new state: %s", state)
|
||||||
|
self._available = True
|
||||||
|
self._state = state.is_on
|
||||||
|
self._brightness = ceil((255 / 100.0) * state.brightness)
|
||||||
|
self._color_temp = self.translate(
|
||||||
|
state.color_temperature,
|
||||||
|
CCT_MIN, CCT_MAX,
|
||||||
|
self.max_mireds, self.min_mireds)
|
||||||
|
self._hs_color = color.color_RGB_to_hs(*state.rgb)
|
||||||
|
|
||||||
|
self._state_attrs.update({
|
||||||
|
ATTR_SCENE: state.scene,
|
||||||
|
ATTR_SLEEP_ASSISTANT: state.sleep_assistant,
|
||||||
|
ATTR_SLEEP_OFF_TIME: state.sleep_off_time,
|
||||||
|
ATTR_TOTAL_ASSISTANT_SLEEP_TIME:
|
||||||
|
state.total_assistant_sleep_time,
|
||||||
|
ATTR_BRAND_SLEEP: state.brand_sleep,
|
||||||
|
ATTR_BRAND: state.brand,
|
||||||
|
})
|
||||||
|
|
||||||
|
async def async_set_delayed_turn_off(self, time_period: timedelta):
|
||||||
|
"""Set delayed turn off. Unsupported."""
|
||||||
|
return
|
||||||
|
Loading…
x
Reference in New Issue
Block a user