Skip to content

Commit

Permalink
Add standard identifiers for lights (#1739)
Browse files Browse the repository at this point in the history
Add standard identifiers for lights based on what is used by miotspec.
Also convert yeelight integration to use them.
  • Loading branch information
rytilahti authored Feb 20, 2023
1 parent c8a3f4b commit e0511d9
Show file tree
Hide file tree
Showing 2 changed files with 73 additions and 22 deletions.
10 changes: 10 additions & 0 deletions miio/identifiers.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
"""Compat layer for homeassistant."""
from enum import Enum


Expand Down Expand Up @@ -27,3 +28,12 @@ class VacuumId(StandardIdentifier):
State = "vacuum:status"
ErrorMessage = "vacuum:fault"
Battery = "battery:level"


class LightId(StandardIdentifier):
"""Standard identifiers for lights."""

On = "light:on"
Brightness = "light:brightness"
ColorTemperature = "light:color-temperature"
Color = "light:color"
85 changes: 63 additions & 22 deletions miio/integrations/yeelight/light/yeelight.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,19 @@
import logging
from enum import IntEnum
from typing import List, Optional, Tuple
from typing import Dict, List, Optional, Tuple

import click

from miio import LightInterface
from miio.click_common import command, format_output
from miio.descriptors import ValidSettingRange
from miio.descriptors import (
NumberSettingDescriptor,
SettingDescriptor,
ValidSettingRange,
)
from miio.device import Device, DeviceStatus
from miio.devicestatus import sensor, setting
from miio.identifiers import LightId
from miio.utils import int_to_rgb, rgb_to_int

from .spec_helper import YeelightSpecHelper, YeelightSubLightType
Expand Down Expand Up @@ -87,9 +92,18 @@ def brightness(self) -> int:
@property
def rgb(self) -> Optional[Tuple[int, int, int]]:
"""Return color in RGB if RGB mode is active."""
rgb_int = self.rgb_int
if rgb_int is not None:
return int_to_rgb(rgb_int)

return None

@property
def rgb_int(self) -> Optional[int]:
"""Return color as single integer RGB if RGB mode is active."""
rgb = self.data[self.get_prop_name("rgb")]
if self.color_mode == YeelightMode.RGB and rgb:
return int_to_rgb(int(rgb))
return int(rgb)
return None

@property
Expand Down Expand Up @@ -144,7 +158,7 @@ def __init__(self, data):
self.data = data

@property
@setting("Power", setter_name="set_power", id="light:on")
@setting("Power", setter_name="set_power", id=LightId.On)
def is_on(self) -> bool:
"""Return whether the light is on or off."""
return self.lights[0].is_on
Expand All @@ -155,22 +169,23 @@ def is_on(self) -> bool:
unit="%",
setter_name="set_brightness",
max_value=100,
id="light:brightness",
id=LightId.Brightness,
)
def brightness(self) -> int:
"""Return current brightness."""
return self.lights[0].brightness

@property
@sensor(
"RGB", setter_name="set_rgb"
) # TODO: we need to extend @setting to support tuples to fix this
def rgb(self) -> Optional[Tuple[int, int, int]]:
"""Return color in RGB if RGB mode is active."""
return self.lights[0].rgb

@property
@sensor("Color mode")
def rgb_int(self) -> Optional[int]:
"""Return color as single integer if RGB mode is active."""
return self.lights[0].rgb_int

@property
def color_mode(self) -> Optional[YeelightMode]:
"""Return current color mode."""
return self.lights[0].color_mode
Expand All @@ -184,13 +199,6 @@ def hsv(self) -> Optional[Tuple[int, int, int]]:
return self.lights[0].hsv

@property
@setting(
"Color temperature",
setter_name="set_color_temperature",
range_attribute="color_temperature_range",
id="light:color-temp",
unit="kelvin",
)
def color_temp(self) -> Optional[int]:
"""Return current color temperature, if applicable."""
return self.lights[0].color_temp
Expand Down Expand Up @@ -347,11 +355,40 @@ def status(self) -> YeelightStatus:

return YeelightStatus(dict(zip(properties, values)))

@property
def valid_temperature_range(self) -> ValidSettingRange:
"""Return supported color temperature range."""
_LOGGER.warning("Deprecated, use color_temperature_range instead")
return self.color_temperature_range
def settings(self) -> Dict[str, SettingDescriptor]:
"""Return settings based on supported features.
This extends the decorated settings with color temperature and color, if
supported by the device.
"""
# TODO: unclear semantics on settings, as making changes here will affect other instances of the class...
settings = super().settings().copy()
ct = self._light_info.color_temp
if ct.min != ct.max:
_LOGGER.info("Got ct for %s: %s", self.model, ct)
settings[LightId.ColorTemperature.value] = NumberSettingDescriptor(
name="Color temperature",
id=LightId.ColorTemperature.value,
property="color_temp",
setter=self.set_color_temperature,
min_value=self.color_temperature_range.min_value,
max_value=self.color_temperature_range.max_value,
step=1,
unit="kelvin",
)
if self._light_info.supports_color:
_LOGGER.info("Got color for %s", self.model)
settings[LightId.Color.value] = NumberSettingDescriptor(
name="Color",
id=LightId.Color.value,
property="rgb_int",
setter=self.set_rgb_int,
min_value=1,
max_value=0xFFFFFF,
step=1,
)

return settings

@property
def color_temperature_range(self) -> ValidSettingRange:
Expand Down Expand Up @@ -448,7 +485,11 @@ def set_rgb(self, rgb: Tuple[int, int, int]):
if color < 0 or color > 255:
raise ValueError("Invalid color: %s" % color)

return self.send("set_rgb", [rgb_to_int(rgb)])
return self.set_rgb_int(rgb_to_int(rgb))

def set_rgb_int(self, rgb: int):
"""Set color from single RGB integer."""
return self.send("set_rgb", [rgb])

def set_hsv(self, hsv):
"""Set color in HSV."""
Expand Down

0 comments on commit e0511d9

Please sign in to comment.