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

Rework light supported features #511

Merged
merged 3 commits into from
Jun 23, 2022
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
2 changes: 1 addition & 1 deletion examples/example_color.py
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ async def run():
light = None
# Find a bulb that can set color
for dev in lights:
if dev.light_control.can_set_color:
if dev.light_control.lights[0].supports_hsb_xy_color:
light = dev
break

Expand Down
7 changes: 4 additions & 3 deletions pytradfri/color.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,16 +46,16 @@ def supported_features(data: LightResponse) -> int:
"""Return supported features."""
supported_color_features = 0

if data.dimmer:
if data.dimmer is not None:
ggravlingen marked this conversation as resolved.
Show resolved Hide resolved
supported_color_features = supported_color_features + SUPPORT_BRIGHTNESS

if data.color_hex:
Copy link
Member

Choose a reason for hiding this comment

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

Color_hex is a string and thus truthy here, ie we don't need to check for None?

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'm assuming that an empty string would be an error/incorrect. But we can change to check for None if we want to allow an empty string.

Copy link
Member

Choose a reason for hiding this comment

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

Hmm, I'm not sure here. If I remember correctly, the gateway has a set of permitted hex colors. If one of these colors is set, color_hex will contain a hex value. If a color that does not have a string representation is set, then color_hex takes some other value (might be "0" as seen here

"5706": "0",
).

I'm inclined to believe that any color bulb would support hex but again, I'm not sure.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ok. I think we can keep it like this for now. If we know better in the future we can update it.

Copy link
Member

Choose a reason for hiding this comment

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

OK, sounds good.

supported_color_features = supported_color_features + SUPPORT_HEX_COLOR

if data.color_mireds:
if data.color_mireds is not None:
supported_color_features = supported_color_features + SUPPORT_COLOR_TEMP

if data.color_xy_x and data.color_xy_y:
if None not in (data.color_xy_x, data.color_xy_y):
supported_color_features = supported_color_features + SUPPORT_XY_COLOR

if (
Expand All @@ -64,6 +64,7 @@ def supported_features(data: LightResponse) -> int:
and data.color_xy_y is not None
and data.color_saturation is not None
and data.color_hue is not None
and data.dimmer is not None
):
supported_color_features = supported_color_features + SUPPORT_RGB_COLOR

Expand Down
46 changes: 31 additions & 15 deletions pytradfri/device/light.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
SUPPORT_BRIGHTNESS,
SUPPORT_COLOR_TEMP,
SUPPORT_HEX_COLOR,
SUPPORT_RGB_COLOR,
SUPPORT_XY_COLOR,
)
from ..resource import BaseResponse
Expand All @@ -36,7 +37,7 @@ class LightResponse(BaseResponse):
color_xy_y: Optional[int] = Field(alias=ATTR_LIGHT_COLOR_Y)
color_hue: Optional[int] = Field(alias=ATTR_LIGHT_COLOR_HUE)
color_saturation: Optional[int] = Field(alias=ATTR_LIGHT_COLOR_SATURATION)
dimmer: int = Field(alias=ATTR_LIGHT_DIMMER)
dimmer: Optional[int] = Field(alias=ATTR_LIGHT_DIMMER)
state: int = Field(alias=ATTR_DEVICE_STATE)


Expand All @@ -57,6 +58,31 @@ def supported_features(self) -> int:
"""Return supported features."""
return supported_features(self.raw)

@property
def supports_dimmer(self) -> bool:
"""Return True if light supports dimmer."""
return bool(self.supported_features & SUPPORT_BRIGHTNESS)

@property
def supports_color_temp(self) -> bool:
"""Return True if light supports color temperature."""
return bool(self.supported_features & SUPPORT_COLOR_TEMP)

@property
def supports_hex_color(self) -> bool:
"""Return True if light supports hex color."""
return bool(self.supported_features & SUPPORT_HEX_COLOR)

@property
def supports_xy_color(self) -> bool:
"""Return True if light supports xy color."""
return bool(self.supported_features & SUPPORT_XY_COLOR)

@property
def supports_hsb_xy_color(self) -> bool:
"""Return True if light supports hsb xy color."""
return bool(self.supported_features & SUPPORT_RGB_COLOR)

@property
def state(self) -> bool:
"""Return device state."""
Expand All @@ -65,32 +91,22 @@ def state(self) -> bool:
@property
def dimmer(self) -> int | None:
"""Return dimmer if present."""
if self.supported_features & SUPPORT_BRIGHTNESS:
return self.raw.dimmer
return None
return self.raw.dimmer

@property
def color_temp(self) -> int | None:
"""Return color temperature."""
if self.supported_features & SUPPORT_COLOR_TEMP and self.raw.color_mireds:
return self.raw.color_mireds
return None
return self.raw.color_mireds

@property
def hex_color(self) -> str | None:
"""Return hex color."""
if self.supported_features & SUPPORT_HEX_COLOR:
return self.raw.color_hex
return None
return self.raw.color_hex

@property
def xy_color(self) -> tuple[int, int] | None:
"""Return xy color."""
if (
self.supported_features & SUPPORT_XY_COLOR
and self.raw.color_xy_x is not None
and self.raw.color_xy_y is not None
):
if self.raw.color_xy_x is not None and self.raw.color_xy_y is not None:
return (self.raw.color_xy_x, self.raw.color_xy_y)
return None

Expand Down
16 changes: 0 additions & 16 deletions pytradfri/device/light_control.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,24 +40,8 @@ def __init__(self, device: Device) -> None:
"""Create object of class."""
super().__init__(device)

self.can_set_dimmer: bool = False
self.can_set_temp: bool = False
self.can_set_xy: bool = False
self.can_set_color: bool = False
self.can_combine_commands: bool = False

if ATTR_LIGHT_DIMMER in self.raw[0].dict():
self.can_set_dimmer = True

if ATTR_LIGHT_MIREDS in self.raw[0].dict():
self.can_set_temp = True

if ATTR_LIGHT_COLOR_X in self.raw[0].dict():
self.can_set_xy = True

if ATTR_LIGHT_COLOR_HUE in self.raw[0].dict():
self.can_set_color = True

# Currently uncertain which bulbs are capable of setting
# multiple values simultaneously. As of gateway firmware
# 1.3.14 1st party bulbs do not seem to support this properly,
Expand Down
29 changes: 27 additions & 2 deletions tests/test_light.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,11 @@ def test_white_bulb():

assert bulb.hex_color is None
assert bulb.xy_color is None
assert bulb.supports_dimmer
assert not bulb.supports_color_temp
assert not bulb.supports_hex_color
assert not bulb.supports_xy_color
assert not bulb.supports_hsb_xy_color


def test_spectrum_bulb():
Expand All @@ -33,6 +38,11 @@ def test_spectrum_bulb():
assert bulb.hex_color == "0"
assert bulb.xy_color == (31103, 27007)
assert bulb.color_temp == 400
assert bulb.supports_dimmer
assert bulb.supports_color_temp
assert bulb.supports_hex_color
assert bulb.supports_xy_color
assert not bulb.supports_hsb_xy_color


def test_spectrum_bulb_custom_color():
Expand All @@ -41,22 +51,37 @@ def test_spectrum_bulb_custom_color():

assert bulb.hex_color == "0"
assert bulb.xy_color == (32228, 27203)
assert bulb.supports_dimmer
assert bulb.supports_color_temp
assert bulb.supports_hex_color
assert bulb.supports_xy_color
assert not bulb.supports_hsb_xy_color


def test_color_bulb():
"""Test color."""
bulb = light(LIGHT_CWS)

assert bulb.hex_color == "f1e0b5"
# assert bulb.xy_color == (32768, 15729) # temporarily disable
assert bulb.xy_color == (30015, 26870)
assert bulb.supports_dimmer
assert not bulb.supports_color_temp
assert bulb.supports_hex_color
assert bulb.supports_xy_color
assert bulb.supports_hsb_xy_color


def test_color_bulb_custom_color():
"""Test custom color."""
bulb = light(LIGHT_CWS_CUSTOM_COLOR)

assert bulb.hex_color == "0"
# assert bulb.xy_color == (23327, 33940) # temporarily disable
assert bulb.xy_color == (23327, 33940)
assert bulb.supports_dimmer
assert not bulb.supports_color_temp
assert bulb.supports_hex_color
assert bulb.supports_xy_color
assert bulb.supports_hsb_xy_color


def test_setters():
Expand Down