Refactor yeelight code (#22547)

* Separate yeelight light classes

* Removed not used variable

* Allow to create device right away, when model is declared

* Lint fixes

* Use correct brightness, when nightlight mode is on

* Pylint fix

* Add power property

* Fix imports

* Update homeassistant/components/yeelight/light.py

Co-Authored-By: Teemu R. <tpr@iki.fi>

* Small PR fixes

* Simplify device to yeelight class mapping

* Simplify device initialization code

* Fix comment
This commit is contained in:
zewelor 2019-06-13 18:42:47 +02:00 committed by Teemu R
parent 3d03a86b13
commit 6d3c3ce449
2 changed files with 185 additions and 128 deletions

View file

@ -80,6 +80,19 @@ YEELIGHT_EFFECT_LIST = [
EFFECT_TWITTER,
EFFECT_STOP]
MODEL_TO_DEVICE_TYPE = {
'mono': BulbType.White,
'mono1': BulbType.White,
'color': BulbType.Color,
'color1': BulbType.Color,
'color2': BulbType.Color,
'strip1': BulbType.Color,
'bslamp1': BulbType.Color,
'ceiling1': BulbType.WhiteTemp,
'ceiling2': BulbType.WhiteTemp,
'ceiling3': BulbType.WhiteTemp,
'ceiling4': BulbType.WhiteTempMood}
def _transitions_config_parser(transitions):
"""Parse transitions config into initialized objects."""
@ -137,11 +150,28 @@ def setup_platform(hass, config, add_entities, discovery_info=None):
custom_effects = _parse_custom_effects(discovery_info[CONF_CUSTOM_EFFECTS])
lights = [YeelightLight(device, custom_effects=custom_effects)]
lights = []
if device.is_ambilight_supported:
lights.append(
YeelightAmbientLight(device, custom_effects=custom_effects))
if device.model:
device_type = MODEL_TO_DEVICE_TYPE.get(device.model, None)
else:
device_type = device.type
def _lights_setup_helper(klass):
lights.append(klass(device, custom_effects=custom_effects))
if device_type == BulbType.White:
_lights_setup_helper(YeelightGenericLight)
elif device_type == BulbType.Color:
_lights_setup_helper(YeelightColorLight)
elif device_type == BulbType.WhiteTemp:
_lights_setup_helper(YeelightWhiteTempLight)
elif device_type == BulbType.WhiteTempMood:
_lights_setup_helper(YeelightWithAmbientLight)
_lights_setup_helper(YeelightAmbientLight)
else:
_LOGGER.error("Cannot determine device type for %s, %s",
device.ipaddr, device.name)
hass.data[data_key] += lights
add_entities(lights, True)
@ -179,23 +209,21 @@ def setup_platform(hass, config, add_entities, discovery_info=None):
schema=service_schema_start_flow)
class YeelightLight(Light):
"""Representation of a Yeelight light."""
class YeelightGenericLight(Light):
"""Representation of a Yeelight generic light."""
def __init__(self, device, custom_effects=None):
"""Initialize the Yeelight light."""
self.config = device.config
self._device = device
self._supported_features = SUPPORT_YEELIGHT
self._brightness = None
self._color_temp = None
self._is_on = None
self._hs = None
self._min_mireds = None
self._max_mireds = None
model_specs = self._bulb.get_model_specs()
self._min_mireds = kelvin_to_mired(model_specs['color_temp']['max'])
self._max_mireds = kelvin_to_mired(model_specs['color_temp']['min'])
self._light_type = LightType.Main
@ -229,7 +257,7 @@ class YeelightLight(Light):
@property
def supported_features(self) -> int:
"""Flag supported features."""
return self._supported_features
return SUPPORT_YEELIGHT
@property
def effect_list(self):
@ -239,7 +267,10 @@ class YeelightLight(Light):
@property
def color_temp(self) -> int:
"""Return the color temperature."""
return self._color_temp
temp = self._get_property('ct')
if temp:
self._color_temp = temp
return kelvin_to_mired(int(self._color_temp))
@property
def name(self) -> str:
@ -249,12 +280,15 @@ class YeelightLight(Light):
@property
def is_on(self) -> bool:
"""Return true if device is on."""
return self._is_on
return self._get_property(self._power_property) == 'on'
@property
def brightness(self) -> int:
"""Return the brightness of this light between 1..255."""
return self._brightness
temp = self._get_property(self._brightness_property)
if temp:
self._brightness = temp
return round(255 * (int(self._brightness) / 100))
@property
def min_mireds(self):
@ -281,6 +315,42 @@ class YeelightLight(Light):
"""Return light type."""
return self._light_type
@property
def hs_color(self) -> tuple:
"""Return the color property."""
return self._hs
# F821: https://github.com/PyCQA/pyflakes/issues/373
@property
def _bulb(self) -> 'Bulb': # noqa: F821
return self.device.bulb
@property
def _properties(self) -> dict:
if self._bulb is None:
return {}
return self._bulb.last_properties
def _get_property(self, prop, default=None):
return self._properties.get(prop, default)
@property
def _brightness_property(self):
return 'bright'
@property
def _power_property(self):
return 'power'
@property
def device(self):
"""Return yeelight device."""
return self._device
def update(self):
"""Update light properties."""
self._hs = self._get_hs_from_properties()
def _get_hs_from_properties(self):
rgb = self._get_property('rgb')
color_mode = self._get_property('color_mode')
@ -290,7 +360,7 @@ class YeelightLight(Light):
color_mode = int(color_mode)
if color_mode == 2: # color temperature
temp_in_k = mired_to_kelvin(self._color_temp)
temp_in_k = mired_to_kelvin(self.color_temp)
return color_util.color_temperature_to_hs(temp_in_k)
if color_mode == 3: # hsv
hue = int(self._get_property('hue'))
@ -305,34 +375,6 @@ class YeelightLight(Light):
return color_util.color_RGB_to_hs(red, green, blue)
@property
def hs_color(self) -> tuple:
"""Return the color property."""
return self._hs
@property
def _properties(self) -> dict:
if self._bulb is None:
return {}
return self._bulb.last_properties
def _get_property(self, prop, default=None):
return self._properties.get(prop, default)
@property
def device(self):
"""Return yeelight device."""
return self._device
@property
def _is_nightlight_enabled(self):
return self.device.is_nightlight_enabled
# F821: https://github.com/PyCQA/pyflakes/issues/373
@property
def _bulb(self) -> 'yeelight.Bulb': # noqa: F821
return self.device.bulb
def set_music_mode(self, mode) -> None:
"""Set the music mode on or off."""
if mode:
@ -340,47 +382,6 @@ class YeelightLight(Light):
else:
self._bulb.stop_music()
def update(self) -> None:
"""Update properties from the bulb."""
bulb_type = self._bulb.bulb_type
if bulb_type == BulbType.Color:
self._supported_features = SUPPORT_YEELIGHT_RGB
elif self.light_type == LightType.Ambient:
self._supported_features = SUPPORT_YEELIGHT_RGB
elif bulb_type in (BulbType.WhiteTemp, BulbType.WhiteTempMood):
if self._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()
self._min_mireds = \
kelvin_to_mired(model_specs['color_temp']['max'])
self._max_mireds = \
kelvin_to_mired(model_specs['color_temp']['min'])
if bulb_type == BulbType.WhiteTempMood:
self._is_on = self._get_property('main_power') == 'on'
else:
self._is_on = self._get_property('power') == 'on'
if self._is_nightlight_enabled:
bright = self._get_property('nl_br')
else:
bright = self._get_property('bright')
if bright:
self._brightness = round(255 * (int(bright) / 100))
temp_in_k = self._get_property('ct')
if temp_in_k:
self._color_temp = kelvin_to_mired(int(temp_in_k))
self._hs = self._get_hs_from_properties()
@_cmd
def set_brightness(self, brightness, duration) -> None:
"""Set bulb brightness."""
@ -566,12 +567,41 @@ class YeelightLight(Light):
_LOGGER.error("Unable to set effect: %s", ex)
class YeelightAmbientLight(YeelightLight):
class YeelightColorLight(YeelightGenericLight):
"""Representation of a Color Yeelight light."""
@property
def supported_features(self) -> int:
"""Flag supported features."""
return SUPPORT_YEELIGHT_RGB
class YeelightWhiteTempLight(YeelightGenericLight):
"""Representation of a Color Yeelight light."""
@property
def supported_features(self) -> int:
"""Flag supported features."""
return SUPPORT_YEELIGHT_WHITE_TEMP
@property
def _brightness_property(self):
return 'current_brightness'
class YeelightWithAmbientLight(YeelightWhiteTempLight):
"""Representation of a Yeelight which has ambilight support."""
@ property
def _power_property(self):
return 'main_power'
class YeelightAmbientLight(YeelightColorLight):
"""Representation of a Yeelight ambient light."""
PROPERTIES_MAPPING = {
"color_mode": "bg_lmode",
"main_power": "bg_power",
}
def __init__(self, *args, **kwargs):
@ -587,14 +617,10 @@ class YeelightAmbientLight(YeelightLight):
"""Return the name of the device if any."""
return "{} ambilight".format(self.device.name)
@property
def _is_nightlight_enabled(self):
return False
def _get_property(self, prop, default=None):
bg_prop = self.PROPERTIES_MAPPING.get(prop)
if not bg_prop:
bg_prop = "bg_" + prop
return self._properties.get(bg_prop, default)
return super()._get_property(bg_prop, default)