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

Moonlight: Provide property rgb as tuple #414

Merged
merged 7 commits into from
Nov 18, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 12 additions & 9 deletions miio/philips_moonlight.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import logging
from collections import defaultdict
from typing import Any, Dict
from typing import Any, Dict, Tuple

import click

from .click_common import command, format_output
from .device import Device, DeviceException
from .utils import int_to_rgb, rgb_to_int

_LOGGER = logging.getLogger(__name__)

Expand Down Expand Up @@ -43,8 +44,9 @@ def color_temperature(self) -> int:
return self.data["cct"]

@property
def rgb(self) -> int:
return self.data["rgb"]
def rgb(self) -> Tuple[int, int, int]:
"""Return color in RGB."""
return int_to_rgb(int(self.data["rgb"]))

@property
def scene(self) -> int:
Expand Down Expand Up @@ -175,15 +177,16 @@ def off(self):
return self.send("set_power", ["off"])

@command(
click.argument("rgb", type=int),
click.argument("rgb", default=[255] * 3, type=click.Tuple([int, int, int])),
default_output=format_output("Setting color to {rgb}")
)
def set_rgb(self, rgb):
"""Set color in encoded RGB."""
if rgb < 0 or rgb > 16777215:
raise PhilipsMoonlightException("Invalid color: %s" % rgb)
def set_rgb(self, rgb: Tuple[int, int, int]):
"""Set color in RGB."""
for color in rgb:
if color < 0 or color > 255:
raise PhilipsMoonlightException("Invalid color: %s" % color)

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

@command(
click.argument("level", type=int),
Expand Down
37 changes: 25 additions & 12 deletions miio/tests/test_philips_moonlight.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

from miio import PhilipsMoonlight
from miio.philips_moonlight import PhilipsMoonlightStatus, PhilipsMoonlightException
from miio.utils import int_to_rgb
from .dummies import DummyDevice


Expand Down Expand Up @@ -79,7 +80,7 @@ def test_status(self):
assert self.is_on() is True
assert self.state().brightness == self.device.start_state["bri"]
assert self.state().color_temperature == self.device.start_state["cct"]
assert self.state().rgb == self.device.start_state["rgb"]
assert self.state().rgb == int_to_rgb(int(self.device.start_state["rgb"]))
assert self.state().scene == self.device.start_state["snm"]

def test_set_brightness(self):
Expand All @@ -105,18 +106,30 @@ def test_set_rgb(self):
def rgb():
return self.device.status().rgb

self.device.set_rgb(1)
assert rgb() == 1
self.device.set_rgb(16711680)
assert rgb() == 16711680
self.device.set_rgb(16777215)
assert rgb() == 16777215
self.device.set_rgb((0, 0, 1))
assert rgb() == (0, 0, 1)
self.device.set_rgb((255, 255, 0))
assert rgb() == (255, 255, 0)
self.device.set_rgb((255, 255, 255))
assert rgb() == (255, 255, 255)

with pytest.raises(PhilipsMoonlightException):
self.device.set_rgb(-1)
self.device.set_rgb((-1, 0, 0))

with pytest.raises(PhilipsMoonlightException):
self.device.set_rgb(16777216)
self.device.set_rgb((256, 0, 0))

with pytest.raises(PhilipsMoonlightException):
self.device.set_rgb((0, -1, 0))

with pytest.raises(PhilipsMoonlightException):
self.device.set_rgb((0, 256, 0))

with pytest.raises(PhilipsMoonlightException):
self.device.set_rgb((0, 0, -1))

with pytest.raises(PhilipsMoonlightException):
self.device.set_rgb((0, 0, 256))

def test_set_color_temperature(self):
def color_temperature():
Expand Down Expand Up @@ -181,13 +194,13 @@ def rgb():

self.device.set_brightness_and_rgb(20, 0)
assert brightness() == 20
assert rgb() == 0
assert rgb() == (0, 0, 0)
self.device.set_brightness_and_rgb(31, 16711680)
assert brightness() == 31
assert rgb() == 16711680
assert rgb() == (255, 0, 0)
self.device.set_brightness_and_rgb(100, 16777215)
assert brightness() == 100
assert rgb() == 16777215
assert rgb() == (255, 255, 255)

with pytest.raises(PhilipsMoonlightException):
self.device.set_brightness_and_rgb(-1, 10)
Expand Down
36 changes: 31 additions & 5 deletions miio/tests/test_yeelight.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ def test_status(self):
assert status.brightness == 100
assert status.color_temp == 3584
assert status.color_mode == YeelightMode.ColorTemperature
assert status.rgb is None
assert status.developer_mode is True
assert status.save_state_on_change is True

Expand Down Expand Up @@ -124,14 +125,39 @@ def color_temp():
with pytest.raises(YeelightException):
self.device.set_color_temp(7000)

@pytest.mark.skip("rgb is not properly implemented")
def test_set_rgb(self):
def rgb():
return self.device.status().rgb

self.device._reset_state()
assert self.device.status().rgb == 16711680
self.device._set_state('color_mode', [1])

assert rgb() == (255, 0, 0)

self.device.set_rgb((0, 0, 1))
assert rgb() == (0, 0, 1)
self.device.set_rgb((255, 255, 0))
assert rgb() == (255, 255, 0)
self.device.set_rgb((255, 255, 255))
assert rgb() == (255, 255, 255)

with pytest.raises(YeelightException):
self.device.set_rgb((-1, 0, 0))

with pytest.raises(YeelightException):
self.device.set_rgb((256, 0, 0))

NEW_RGB = 16712222
self.set_rgb(NEW_RGB)
assert self.device.status().rgb == NEW_RGB
with pytest.raises(YeelightException):
self.device.set_rgb((0, -1, 0))

with pytest.raises(YeelightException):
self.device.set_rgb((0, 256, 0))

with pytest.raises(YeelightException):
self.device.set_rgb((0, 0, -1))

with pytest.raises(YeelightException):
self.device.set_rgb((0, 0, 256))

@pytest.mark.skip("hsv is not properly implemented")
def test_set_hsv(self):
Expand Down
14 changes: 14 additions & 0 deletions miio/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import inspect
import warnings
from datetime import timedelta, datetime
from typing import Tuple


def deprecated(reason):
Expand Down Expand Up @@ -88,3 +89,16 @@ def pretty_seconds(x: float) -> timedelta:
def pretty_time(x: float) -> datetime:
"""Return a datetime object from unix timestamp."""
return datetime.fromtimestamp(x)


def int_to_rgb(x: int) -> Tuple[int, int, int]:
"""Return a RGB tuple from integer."""
red = (x >> 16) & 0xff
green = (x >> 8) & 0xff
blue = x & 0xff
return red, green, blue


def rgb_to_int(x: Tuple[int, int, int]) -> int:
"""Return an integer from RGB tuple."""
return int(x[0] << 16 | x[1] << 8 | x[2])
21 changes: 13 additions & 8 deletions miio/yeelight.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

from .click_common import command, format_output
from .device import Device, DeviceException
from .utils import int_to_rgb, rgb_to_int


class YeelightException(DeviceException):
Expand Down Expand Up @@ -37,11 +38,7 @@ def brightness(self) -> int:
def rgb(self) -> Optional[Tuple[int, int, int]]:
"""Return color in RGB if RGB mode is active."""
if self.color_mode == YeelightMode.RGB:
rgb = int(self.data["rgb"])
blue = rgb & 0xff
green = (rgb >> 8) & 0xff
red = (rgb >> 16) & 0xff
return red, green, blue
return int_to_rgb(int(self.data["rgb"]))
return None

@property
Expand Down Expand Up @@ -204,9 +201,17 @@ def set_color_temp(self, level, transition=500):
else:
return self.send("set_ct_abx", [level])

def set_rgb(self, rgb):
"""Set color in encoded RGB."""
return self.send("set_rgb", [rgb])
@command(
click.argument("rgb", default=[255] * 3, type=click.Tuple([int, int, int])),
default_output=format_output("Setting color to {rgb}")
)
def set_rgb(self, rgb: Tuple[int, int, int]):
"""Set color in RGB."""
for color in rgb:
if color < 0 or color > 255:
raise YeelightException("Invalid color: %s" % color)

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

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