From c3822d653d44c32f25a10bad5909216c5fa968ae Mon Sep 17 00:00:00 2001 From: Xavier Moreno Date: Sat, 1 Jan 2022 19:12:39 +0100 Subject: [PATCH] feat(light_controller): add "hold_toggle_direction_init" attribute to select init state related to #388 --- apps/controllerx/cx_core/stepper/__init__.py | 7 ++++-- .../cx_core/stepper/index_loop_stepper.py | 6 ++--- .../cx_core/type/light_controller.py | 15 +++++++++++-- docs/start/type-configuration.md | 1 + .../hold_toggle_previous_dir_init/config.yaml | 10 +++++++++ .../hold_toggle_test.yaml | 22 +++++++++++++++++++ 6 files changed, 54 insertions(+), 7 deletions(-) create mode 100644 tests/integ_tests/hold_toggle_previous_dir_init/config.yaml create mode 100644 tests/integ_tests/hold_toggle_previous_dir_init/hold_toggle_test.yaml diff --git a/apps/controllerx/cx_core/stepper/__init__.py b/apps/controllerx/cx_core/stepper/__init__.py index eaa527af..13385d5f 100644 --- a/apps/controllerx/cx_core/stepper/__init__.py +++ b/apps/controllerx/cx_core/stepper/__init__.py @@ -54,7 +54,7 @@ def exceeded(self) -> bool: class Stepper(abc.ABC): sign_mapping = {StepperDir.UP: 1, StepperDir.DOWN: -1} - previous_direction: str = StepperDir.DOWN + previous_direction: str min_max: MinMax steps: Number @@ -66,9 +66,12 @@ def invert_direction(direction: str) -> str: def sign(direction: str) -> int: return Stepper.sign_mapping[direction] - def __init__(self, min_max: MinMax, steps: Number) -> None: + def __init__( + self, min_max: MinMax, steps: Number, previous_direction: str = StepperDir.DOWN + ) -> None: self.min_max = min_max self.steps = steps + self.previous_direction = previous_direction def get_direction(self, value: Number, direction: str) -> str: if direction == StepperDir.TOGGLE: diff --git a/apps/controllerx/cx_core/stepper/index_loop_stepper.py b/apps/controllerx/cx_core/stepper/index_loop_stepper.py index 2492c9bf..e4770092 100644 --- a/apps/controllerx/cx_core/stepper/index_loop_stepper.py +++ b/apps/controllerx/cx_core/stepper/index_loop_stepper.py @@ -1,10 +1,10 @@ from cx_const import Number -from cx_core.stepper import MinMax, Stepper, StepperOutput +from cx_core.stepper import MinMax, Stepper, StepperDir, StepperOutput class IndexLoopStepper(Stepper): - def __init__(self, size: int) -> None: - super().__init__(MinMax(0, size - 1), size) + def __init__(self, size: int, previous_direction: str = StepperDir.DOWN) -> None: + super().__init__(MinMax(0, size - 1), size, previous_direction) def step(self, value: Number, direction: str) -> StepperOutput: value = self.min_max.clip(value) diff --git a/apps/controllerx/cx_core/type/light_controller.py b/apps/controllerx/cx_core/type/light_controller.py index 8fa19a03..f2a004b9 100644 --- a/apps/controllerx/cx_core/type/light_controller.py +++ b/apps/controllerx/cx_core/type/light_controller.py @@ -27,6 +27,7 @@ DEFAULT_TRANSITION = 300 DEFAULT_ADD_TRANSITION = True DEFAULT_TRANSITION_TURN_TOGGLE = False +DEFAULT_HOLD_TOGGLE_DIRECTION_INIT = "up" ColorMode = str # Once the minimum supported version of Python is 3.8, @@ -136,6 +137,13 @@ async def init(self) -> None: self.add_transition_turn_toggle = self.args.get( "add_transition_turn_toggle", DEFAULT_TRANSITION_TURN_TOGGLE ) + self.hold_toggle_direction_init = self.get_option( + self.args.get( + "hold_toggle_direction_init", DEFAULT_HOLD_TOGGLE_DIRECTION_INIT + ), + [StepperDir.UP, StepperDir.DOWN], + "`hold_toggle_direction_init`", + ) await super().init() def _get_entity_type(self) -> Type[LightEntity]: @@ -568,14 +576,17 @@ async def is_colortemp_supported(self) -> bool: @lru_cache(maxsize=None) def get_stepper(self, attribute: str, steps: Number, mode: str) -> Stepper: + previous_direction = Stepper.invert_direction(self.hold_toggle_direction_init) if attribute == LightController.ATTRIBUTE_XY_COLOR: - return IndexLoopStepper(len(self.color_wheel)) + return IndexLoopStepper(len(self.color_wheel), previous_direction) if mode not in STEPPER_MODES: raise ValueError( f"`{mode}` mode is not available. Options are: {list(STEPPER_MODES.keys())}" ) stepper_cls = STEPPER_MODES[mode] - return stepper_cls(self.min_max_attributes[attribute], steps) + return stepper_cls( + self.min_max_attributes[attribute], steps, previous_direction + ) async def get_attribute(self, attribute: str) -> str: if attribute == LightController.ATTRIBUTE_COLOR: diff --git a/docs/start/type-configuration.md b/docs/start/type-configuration.md index 57985d35..8ae7a685 100644 --- a/docs/start/type-configuration.md +++ b/docs/start/type-configuration.md @@ -39,6 +39,7 @@ This controller allows the devices to control light or group of lights. This all | `supported_features` | int | `0b101100` or `44` | See [below](#supported_features-field) for the explanation. | | `supported_color_modes` | list | `["xy", "rgb"]` | It overrides the `supported_color_modes` that can be found in light attributes. Values can be `color_temp`, `hs`, `xy`, `rgb`, `rgbw` and `rgbww`. | | `update_supported_features` | boolean | False | If `true`, it will check the supported features field everytime before calling any call service action. Useful in case the supported features of the device entity changes over the time. | +| `hold_toggle_direction_init` | string | `up` | It indicates the first direction of the hold toggle actions (`up` or `down`). | _\* Required fields_ diff --git a/tests/integ_tests/hold_toggle_previous_dir_init/config.yaml b/tests/integ_tests/hold_toggle_previous_dir_init/config.yaml new file mode 100644 index 00000000..7b3597c7 --- /dev/null +++ b/tests/integ_tests/hold_toggle_previous_dir_init/config.yaml @@ -0,0 +1,10 @@ +livingroom_controller: + module: controllerx + class: E1810Controller + controller: sensor.livingroom_controller_action + integration: z2m + light: light.livingroom + hold_toggle_direction_init: "down" + merge_mapping: + brightness_up_hold: hold_brightness_toggle + diff --git a/tests/integ_tests/hold_toggle_previous_dir_init/hold_toggle_test.yaml b/tests/integ_tests/hold_toggle_previous_dir_init/hold_toggle_test.yaml new file mode 100644 index 00000000..b79106f6 --- /dev/null +++ b/tests/integ_tests/hold_toggle_previous_dir_init/hold_toggle_test.yaml @@ -0,0 +1,22 @@ +# This test is testing that the @lru_cache decorator from light_controller.py::get_stepper +# is working properly, and that the returned stepper is the same everytime. +entity_state_attributes: + brightness: 60 +entity_state: "on" +fired_actions: [brightness_up_hold, 0.450, brightness_up_release, 0.3, brightness_up_hold, 0.05, brightness_up_release] +expected_calls: +- service: light/turn_on + data: + entity_id: light.livingroom + transition: 0.35 + brightness: 34.6 +- service: light/turn_on + data: + entity_id: light.livingroom + transition: 0.35 + brightness: 9.2 +- service: light/turn_on + data: + entity_id: light.livingroom + transition: 0.35 + brightness: 85.4