Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add light specs for yeelight #1163

Merged
merged 11 commits into from
Oct 22, 2021
21 changes: 19 additions & 2 deletions miio/integrations/yeelight/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from miio.exceptions import DeviceException
from miio.utils import int_to_rgb, rgb_to_int

SUPPORTED_MODELS = ["yeelink.light.color1"]
from .spec_helper import YeelightSpecHelper


class YeelightException(DeviceException):
Expand Down Expand Up @@ -259,7 +259,24 @@ class Yeelight(Device):
which however requires enabling the developer mode on the bulbs.
"""

_supported_models = SUPPORTED_MODELS
_supported_models: List[str] = []
_spec_helper = None

def __init__(
self,
ip: str = None,
token: str = None,
start_id: int = 0,
debug: int = 0,
lazy_discover: bool = True,
model: str = None,
) -> None:
super().__init__(ip, token, start_id, debug, lazy_discover, model=model)
if Yeelight._spec_helper is None:
Yeelight._spec_helper = YeelightSpecHelper()
Yeelight._supported_models = Yeelight._spec_helper.supported_models

self._model_info = Yeelight._spec_helper.get_model_info(self.model)

@command(default_output=format_output("", "{result.cli_format}"))
def status(self) -> YeelightStatus:
Expand Down
74 changes: 74 additions & 0 deletions miio/integrations/yeelight/spec_helper.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import logging
import os
from typing import Dict, NamedTuple

import attr
import yaml

_LOGGER = logging.getLogger(__name__)

GENERIC_MODEL_SPEC_DICT = {
"color_temp": (1700, 6500),
"night_light": False,
"background_light": False,
"supports_color": False,
}
rytilahti marked this conversation as resolved.
Show resolved Hide resolved


class ColorTempRange(NamedTuple):
"""Color temperature range."""

min: int
max: int


@attr.s(auto_attribs=True)
class YeelightModelInfo:
rytilahti marked this conversation as resolved.
Show resolved Hide resolved
model: str
color_temp: ColorTempRange
night_light: bool
background_light: bool
supports_color: bool


class YeelightSpecHelper:
_models: Dict[str, YeelightModelInfo] = {}

def __init__(self):
if len(YeelightSpecHelper._models) == 0:
rytilahti marked this conversation as resolved.
Show resolved Hide resolved
self._parse_specs_yaml()

def _parse_specs_yaml(self):
generic_info = YeelightModelInfo(
"generic",
ColorTempRange(*GENERIC_MODEL_SPEC_DICT["color_temp"]),
GENERIC_MODEL_SPEC_DICT["night_light"],
GENERIC_MODEL_SPEC_DICT["background_light"],
GENERIC_MODEL_SPEC_DICT["supports_color"],
rytilahti marked this conversation as resolved.
Show resolved Hide resolved
)
YeelightSpecHelper._models["generic"] = generic_info
# read the yaml file to populate the internal model cache
with open(os.path.dirname(__file__) + "/specs.yaml") as filedata:
models = yaml.safe_load(filedata)
for key, value in models.items():
info = YeelightModelInfo(
key,
ColorTempRange(*value["color_temp"]),
value["night_light"],
value["background_light"],
value["supports_color"],
)
YeelightSpecHelper._models[key] = info
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Even when improbable that someone adds a duplicate entry to the file, I'd check here if the key already exists and raise an exception if so.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did this but as I understand yaml.safe_load ignore key duplicates. I tried to add duplicate and wouldnt catch exception :(

Copy link
Owner

@rytilahti rytilahti Oct 22, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, it's a dictionary already so new values will override the existing one. Let's do the following: remove the duplicate handling (no need to check for the key & raise the exception) and let's merge this then.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This-Russian-artists-comics-will-make-you-win-your-day-60338d74658b9__700


@property
def supported_models(self):
return self._models.keys()

def get_model_info(self, model) -> YeelightModelInfo:
if model not in self._models:
_LOGGER.warning(
"Unknown model %s, please open an issue and supply features for this light. Returning generic information.",
model,
)
return self._models["generic"]
return self._models[model]
180 changes: 180 additions & 0 deletions miio/integrations/yeelight/specs.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
yeelink.light.bslamp1:
color_temp: [1700, 6500]
night_light: False
background_light: False
supports_color: True
yeelink.light.bslamp2:
color_temp: [1700, 6500]
night_light: True
background_light: False
supports_color: True
yeelink.light.bslamp3:
color_temp: [1700, 6500]
night_light: True
background_light: False
supports_color: True
yeelink.light.ceil26:
color_temp: [2700, 6500]
night_light: True
background_light: False
supports_color: False
yeelink.light.ceila:
color_temp: [2700, 6500]
night_light: True
background_light: False
supports_color: False
yeelink.light.ceiling1:
color_temp: [2700, 6500]
night_light: True
background_light: False
supports_color: False
yeelink.light.ceiling2:
color_temp: [2700, 6500]
night_light: True
background_light: False
supports_color: False
yeelink.light.ceiling3:
color_temp: [2700, 6500]
night_light: True
background_light: False
supports_color: False
yeelink.light.ceiling4:
color_temp: [2700, 6500]
night_light: True
background_light: True
supports_color: False
yeelink.light.ceiling5:
color_temp: [2700, 6500]
night_light: True
background_light: False
supports_color: False
yeelink.light.ceiling6:
color_temp: [2700, 6500]
night_light: True
background_light: False
supports_color: False
yeelink.light.ceiling10:
color_temp: [2700, 6500]
night_light: True
background_light: True
supports_color: False
yeelink.light.ceiling13:
color_temp: [2700, 6500]
night_light: True
background_light: False
supports_color: False
yeelink.light.ceiling15:
color_temp: [2700, 6500]
night_light: True
background_light: False
supports_color: False
yeelink.light.ceiling18:
color_temp: [2700, 6500]
night_light: True
background_light: False
supports_color: False
yeelink.light.ceiling19:
color_temp: [2700, 6500]
night_light: True
background_light: True
supports_color: False
yeelink.light.ceiling20:
color_temp: [2700, 6500]
night_light: True
background_light: True
supports_color: False
yeelink.light.ceiling22:
color_temp: [2700, 6500]
night_light: True
background_light: False
supports_color: False
yeelink.light.ceiling24:
color_temp: [2700, 6500]
night_light: True
background_light: False
supports_color: False
yeelink.light.color1:
color_temp: [1700, 6500]
night_light: False
background_light: False
supports_color: True
yeelink.light.color2:
color_temp: [2700, 6500]
night_light: False
background_light: False
supports_color: True
yeelink.light.color4:
color_temp: [1700, 6500]
night_light: False
background_light: False
supports_color: True
yeelink.light.colorc:
color_temp: [2700, 6500]
night_light: False
background_light: False
supports_color: True
yeelink.light.color:
color_temp: [1700, 6500]
night_light: False
background_light: False
supports_color: True
yeelink.light.ct_bulb:
color_temp: [2700, 6500]
night_light: False
background_light: False
supports_color: False
yeelink.light.ct2:
color_temp: [2700, 6500]
night_light: False
background_light: False
supports_color: False
yeelink.light.lamp1:
color_temp: [2700, 5000]
night_light: False
background_light: False
supports_color: False
yeelink.light.lamp4:
color_temp: [2600, 5000]
night_light: False
background_light: False
supports_color: False
yeelink.light.lamp15:
color_temp: [2700, 6500]
night_light: False
background_light: True
supports_color: False
yeelink.light.mono1:
color_temp: [2700, 2700]
night_light: False
background_light: False
supports_color: False
yeelink.light.mono5:
color_temp: [2700, 2700]
night_light: False
background_light: False
supports_color: False
yeelink.light.mono:
color_temp: [2700, 2700]
night_light: False
background_light: False
supports_color: False
yeelink.light.monob:
color_temp: [2700, 2700]
night_light: False
background_light: False
supports_color: False
yeelink.light.strip1:
color_temp: [1700, 6500]
night_light: False
background_light: False
supports_color: True
yeelink.light.strip2:
color_temp: [2700, 6500]
night_light: False
background_light: False
supports_color: True
yeelink.light.strip4:
color_temp: [2700, 6500]
night_light: False
background_light: False
supports_color: True
21 changes: 21 additions & 0 deletions miio/integrations/yeelight/tests/test_yeelight_spec_helper.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
from ..spec_helper import ColorTempRange, YeelightSpecHelper


def test_get_model_info():
spec_helper = YeelightSpecHelper()
model_info = spec_helper.get_model_info("yeelink.light.bslamp1")
assert model_info.model == "yeelink.light.bslamp1"
assert model_info.color_temp == ColorTempRange(1700, 6500)
assert model_info.night_light is False
assert model_info.background_light is False
assert model_info.supports_color is True


def test_get_unknown_model_info():
spec_helper = YeelightSpecHelper()
model_info = spec_helper.get_model_info("notreal")
assert model_info.model == "generic"
assert model_info.color_temp == ColorTempRange(1700, 6500)
assert model_info.night_light is False
assert model_info.background_light is False
assert model_info.supports_color is False