Skip to content

Commit

Permalink
Merge branch 'dev'
Browse files Browse the repository at this point in the history
  • Loading branch information
xaviml committed Mar 14, 2020
2 parents 4c1e838 + 5750d51 commit 5239ca5
Show file tree
Hide file tree
Showing 5 changed files with 120 additions and 28 deletions.
27 changes: 27 additions & 0 deletions apps/controllerx/core/light_features.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
SUPPORT_BRIGHTNESS = 1
SUPPORT_COLOR_TEMP = 2
SUPPORT_EFFECT = 4
SUPPORT_FLASH = 8
SUPPORT_COLOR = 16
SUPPORT_TRANSITION = 32
SUPPORT_WHITE_VALUE = 128

FEATURES = [
SUPPORT_BRIGHTNESS,
SUPPORT_COLOR_TEMP,
SUPPORT_EFFECT,
SUPPORT_FLASH,
SUPPORT_COLOR,
SUPPORT_TRANSITION,
SUPPORT_WHITE_VALUE,
]


def decode(number):
number = int(number)
supported = []
for feature in FEATURES:
n = number & feature
if n != 0:
supported.append(n)
return supported
29 changes: 21 additions & 8 deletions apps/controllerx/core/type/light_controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

from const import Light
from core.controller import ReleaseHoldController, action
from core import light_features
from core.stepper import Stepper
from core.stepper.circular_stepper import CircularStepper
from core.stepper.minmax_stepper import MinMaxStepper
Expand Down Expand Up @@ -250,8 +251,8 @@ def get_light(self, light):
return {"name": light["name"], "color_mode": "auto"}

@action
async def on(self, add_transition=True, **attributes):
if add_transition and "transition" not in attributes:
async def on(self, **attributes):
if "transition" not in attributes:
attributes["transition"] = self.transition / 1000
self.call_service(
"homeassistant/turn_on", entity_id=self.light["name"], **attributes,
Expand Down Expand Up @@ -288,18 +289,30 @@ async def on_min(self, attribute):

@action
async def sync(self):
await self.on_full(LightController.ATTRIBUTE_BRIGHTNESS)
await self.on(brightness=self.max_brightness, transition=0)
attributes = {}
try:
color_attribute = await self.get_attribute(LightController.ATTRIBUTE_COLOR)
if color_attribute == LightController.ATTRIBUTE_COLOR_TEMP:
attributes[color_attribute] = 370 # 2700K light
else:
self.index_color = 12 # white colour
attributes[color_attribute] = self.colors[self.index_color]
except:
self.log("sync action will only change brightness", level="DEBUG")
if attributes != {}:
await self.on(**attributes)

async def get_attribute(self, attribute):
if attribute == LightController.ATTRIBUTE_COLOR:
entity_states = await self.get_entity_state(
self.light["name"], attribute="all"
bitfield = await self.get_entity_state(
self.light["name"], attribute="supported_features"
)
entity_attributes = entity_states["attributes"]
supported_features = light_features.decode(bitfield)
if self.light["color_mode"] == "auto":
if LightController.ATTRIBUTE_XY_COLOR in entity_attributes:
if light_features.SUPPORT_COLOR in supported_features:
return LightController.ATTRIBUTE_XY_COLOR
elif LightController.ATTRIBUTE_COLOR_TEMP in entity_attributes:
elif light_features.SUPPORT_COLOR_TEMP in supported_features:
return LightController.ATTRIBUTE_COLOR_TEMP
else:
raise ValueError(
Expand Down
2 changes: 1 addition & 1 deletion docs/others/custom-controllers.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ This controller lets you map controller events with predefined light actions. Th
| `on_min_color_temp` | It puts the color temp to the minimum value |
| `set_half_brightness` | It sets the brightness to 50% |
| `set_half_color_temp` | It sets the color temp to 50% |
| `sync` | It syncs the light(s) to full brightness |
| `sync` | It syncs the light(s) to full brightness and white colour or 2700K (370 mireds) |
| `click_brightness_up` | It brights up accordingly with the `manual_steps` attribute |
| `click_brightness_down` | It brights down accordingly with the `manual_steps` attribute |
| `click_color_up` | It turns the color up accordingly with the `manual_steps` attribute |
Expand Down
33 changes: 33 additions & 0 deletions tests/core/light_feature_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import pytest

from core import light_features


@pytest.mark.parametrize(
"number, expected_supported_features",
[
(
"57",
{
light_features.SUPPORT_BRIGHTNESS,
light_features.SUPPORT_FLASH,
light_features.SUPPORT_COLOR,
light_features.SUPPORT_TRANSITION,
},
),
(
"149",
{
light_features.SUPPORT_BRIGHTNESS,
light_features.SUPPORT_EFFECT,
light_features.SUPPORT_COLOR,
light_features.SUPPORT_WHITE_VALUE,
},
),
(0, set()),
("0", set()),
],
)
def test_decode(number, expected_supported_features):
light_support = light_features.decode(number)
assert set(light_support) == expected_supported_features
57 changes: 38 additions & 19 deletions tests/core/type/light_controller_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,16 +43,16 @@ def test_initialize_and_get_light(sut, mocker, light_input, light_output):


@pytest.mark.parametrize(
"attribute_input, color_mode, light_attributes, attribute_expected, throws_error",
"attribute_input, color_mode, supported_features, attribute_expected, throws_error",
[
("color", "auto", ("xy_color",), "xy_color", False),
("color", "auto", ("color_temp",), "color_temp", False),
("color", "auto", ("color_temp", "xy_color"), "xy_color", False),
("brightness", "auto", (), "brightness", False),
("brightness", "auto", ("xy_color",), "brightness", False),
("color", "color_temp", ("color_temp", "xy_color"), "color_temp", False),
("color", "xy_color", ("color_temp", "xy_color"), "xy_color", False),
("color", "auto", (), "not_important", True),
("color", "auto", "16", "xy_color", False), # 16 = xy_color
("color", "auto", "2", "color_temp", False), # 2 = color_temp
("color", "auto", "18", "xy_color", False), # 18 = xy_color + color_temp
("brightness", "auto", "0", "brightness", False),
("brightness", "auto", "16", "brightness", False),
("color", "color_temp", "18", "color_temp", False),
("color", "xy_color", "18", "xy_color", False),
("color", "auto", "0", "not_important", True),
],
)
@pytest.mark.asyncio
Expand All @@ -61,12 +61,13 @@ async def test_get_attribute(
monkeypatch,
attribute_input,
color_mode,
light_attributes,
supported_features,
attribute_expected,
throws_error,
):
async def fake_get_entity_state(entity, attribute=None):
return {"attributes": set(light_attributes)}

return supported_features

sut.light = {"name": "light", "color_mode": color_mode}
monkeypatch.setattr(sut, "get_entity_state", fake_get_entity_state)
Expand Down Expand Up @@ -268,30 +269,48 @@ async def test_on_min(sut, mocker):


@pytest.mark.parametrize(
"max_brightness, expected_attributes",
"max_brightness, color_attribute, expected_color_attributes",
[
(255, {"brightness": 255}),
(255, {"brightness": 255}),
(120, {"brightness": 120}),
(255, "color_temp", {"color_temp": 370}),
(255, "xy_color", {"xy_color": (0.323, 0.329)}),
(120, "error", {}),
],
)
@pytest.mark.asyncio
async def test_sync(sut, monkeypatch, mocker, max_brightness, expected_attributes):
async def test_sync(
sut, monkeypatch, mocker, max_brightness, color_attribute, expected_color_attributes
):
sut.max_brightness = max_brightness
sut.automatic_steppers = {"brightness": MinMaxStepper(1, max_brightness, 10)}
sut.light = {"name": "test_light"}
sut.transition = 300

async def fake_get_attribute(*args, **kwargs):
if color_attribute == "error":
raise ValueError()
return color_attribute

monkeypatch.setattr(sut, "get_attribute", fake_get_attribute)
called_service_patch = mocker.patch.object(sut, "call_service")

await sut.sync()

called_service_patch.assert_called_once_with(
called_service_patch.assert_any_call(
"homeassistant/turn_on",
entity_id="test_light",
**{"transition": 0.3, **expected_attributes}
brightness=max_brightness,
transition=0,
)

if color_attribute == "error":
assert called_service_patch.call_count == 1
else:
assert called_service_patch.call_count == 2
called_service_patch.assert_any_call(
"homeassistant/turn_on",
entity_id="test_light",
**{"transition": 0.3, **expected_color_attributes}
)


@pytest.mark.parametrize(
"attribute_input, direction_input, light_state, smooth_power_on, expected_calls",
Expand Down

0 comments on commit 5239ca5

Please sign in to comment.