Skip to content

Commit

Permalink
fix(light-controller): add add_transition_turn_toggle attribute
Browse files Browse the repository at this point in the history
Fixes #79
  • Loading branch information
xaviml committed May 17, 2020
1 parent 1c607b4 commit 3bca712
Show file tree
Hide file tree
Showing 5 changed files with 130 additions and 52 deletions.
8 changes: 4 additions & 4 deletions Pipfile
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,15 @@ verify_ssl = true

[dev-packages]
black = "==19.10b0"
pytest = "==5.4.1"
pytest = "==5.4.2"
pytest-asyncio = "==0.12.0"
pytest-cov = "==2.8.1"
pytest-mock = "==3.1.0"
mock = "==4.0.2"
pre-commit = "==2.3.0"
commitizen = "==1.19.3"
pre-commit = "==2.4.0"
commitizen = "==1.22.0"
mypy = "==0.770"
flake8 = "==3.7.9"
flake8 = "==3.8.1"
controllerx = {path = ".",editable = true}

[packages]
Expand Down
44 changes: 30 additions & 14 deletions apps/controllerx/core/type/light_controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,9 @@ async def initialize(self) -> None:
"smooth_power_on", self.supports_smooth_power_on()
)
self.add_transition = self.args.get("add_transition", True)
self.add_transition_turn_toggle = self.args.get(
"add_transition_turn_toggle", True
)

bitfield = await self.get_entity_state(
self.light["name"], attribute="supported_features"
Expand Down Expand Up @@ -264,49 +267,62 @@ def get_light(self, light: Union[str, dict]) -> LightEntity:
f"Type {type(light)} is not supported for `light` attribute"
)

async def call_light_service(self, service: str, **attributes) -> None:
async def call_light_service(
self, service: str, turned_toggle: bool, **attributes
) -> None:

if "transition" not in attributes:
attributes["transition"] = self.transition / 1000
if (
self.supported_features.not_supported(LightSupport.TRANSITION)
or not self.add_transition
or (turned_toggle and not self.add_transition_turn_toggle)
):
del attributes["transition"]
await self.call_service(service, entity_id=self.light["name"], **attributes)

@action
async def on(self, **attributes) -> None:
await self.call_light_service("light/turn_on", **attributes)
async def on(self, light_on: bool = None, **attributes) -> None:
if light_on is None:
light_state = await self.get_entity_state(self.light["name"])
light_on = light_state == "on"
await self.call_light_service(
"light/turn_on", turned_toggle=not light_on, **attributes
)

@action
async def off(self, **attributes) -> None:
await self.call_light_service("light/turn_off", **attributes)
await self.call_light_service(
"light/turn_off", turned_toggle=True, **attributes
)

@action
async def toggle(self, **attributes) -> None:
await self.call_light_service("light/toggle", **attributes)
await self.call_light_service("light/toggle", turned_toggle=True, **attributes)

@action
async def set_value(self, attribute: str, fraction: float) -> None:
async def set_value(
self, attribute: str, fraction: float, light_on: bool = None
) -> None:
fraction = max(0, min(fraction, 1))
stepper = self.automatic_steppers[attribute]
if isinstance(stepper, MinMaxStepper):
min_ = stepper.minmax.min
max_ = stepper.minmax.max
value = (max_ - min_) * fraction + min_
await self.on(**{attribute: value})
await self.on(light_on=light_on, **{attribute: value})

@action
async def on_full(self, attribute: str) -> None:
async def on_full(self, attribute: str, light_on: bool = None) -> None:
stepper = self.automatic_steppers[attribute]
stepper.previous_direction = Stepper.TOGGLE_UP
await self.set_value(attribute, 1)
await self.set_value(attribute, 1, light_on=light_on)

@action
async def on_min(self, attribute: str) -> None:
async def on_min(self, attribute: str, light_on: bool = None) -> None:
stepper = self.automatic_steppers[attribute]
stepper.previous_direction = Stepper.TOGGLE_DOWN
await self.set_value(attribute, 0)
await self.set_value(attribute, 0, light_on=light_on)

@action
async def sync(self) -> None:
Expand Down Expand Up @@ -436,7 +452,7 @@ async def change_light_state(
attributes = {attribute: xy_color}
if action_type == "hold":
attributes["transition"] = self.delay / 1000
await self.on(**attributes)
await self.on(**attributes, light_on=True)
# In case of xy_color mode it never finishes the loop, the hold loop
# will only stop if the hold action is called when releasing the button.
# I haven't experimented any problems with it, but a future implementation
Expand All @@ -445,14 +461,14 @@ async def change_light_state(
if self.check_smooth_power_on(
attribute, direction, await self.get_entity_state(self.light["name"])
):
await self.on_min(attribute)
await self.on_min(attribute, light_on=False)
# # After smooth power on, the light should not brighten up.
return True
new_state_attribute, exceeded = stepper.step(old, direction)
attributes = {attribute: new_state_attribute}
if action_type == "hold":
attributes["transition"] = self.delay / 1000
await self.on(**attributes)
await self.on(**attributes, light_on=True)
self.value_attribute = new_state_attribute
return exceeded

Expand Down
23 changes: 22 additions & 1 deletion docs/faq.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,25 @@ This does not mean that any other integration will not work, but they might not

#### 5. Error: "Value for X attribute could not be retrieved from light Y"

This error is shown when the light has support for the X attribute (e.g. brightness or color_temp) and the attribute is not in the state attribute of the entity. You can check whether the attribute X is shown in the state attributes from the "Developer Tools > States".
This error is shown when the light has support for the X attribute (e.g. brightness or color_temp) and the attribute is not in the state attribute of the entity. You can check whether the attribute X is shown in the state attributes from the "Developer Tools > States".

#### 6. Light is not turning on to the previous brightness

Zigbee does not support transition natively to lights, so this attribute depends on the integration you have installed for your light. If you encountered this problem is because ControllerX, by default, sends the `transition` attribute to the light(s) through an HA call and the integration for your light does not support transition when turning on or off and it leads to an unexpected behaviour. In fact, if you go to AppDaemon logs, you will be able to see the service call that ControllerX does when pressing the buttons. You can then, replicate those calls on "Developer Tools > Services". These are the issues created related to this problem on the different integrations:

- [Zigbee2MQTT](https://github.com/Koenkk/zigbee-herdsman-converters/issues/1073) (FIXED)
- [Hue integration](https://github.com/home-assistant/core/issues/32894) (OPEN)

However, while the problem is not in the scope of ControllerX, there is a workaround that will help you fix this problem while losing the transition when turning on/off or toggeling. For this, you could add `add_transition_turn_toggle: false` to your controller configuration. This is an example:

```yaml
problem_fixed:
module: controllerx
class: E1810Controller
controller: sensor.livingroom_controller_action
integration: z2m
light: light.bedroom
add_transition_turn_toggle: false
```
This will keep using transition when changing brightness or color, but not when turning on/off the light.
27 changes: 14 additions & 13 deletions docs/start/type-configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,19 +17,20 @@ This controller allows the devices to control light or group of lights. This all
- Smooth increase/decrease (holding button) of brightness and color
- Color loop changing if the light supports xy color.

| key | type | value | description |
| ----------------- | -------------------- | ----------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `light`\* | string \| dictionary | `group.livingroom_lights` or `light.kitchen` | The light (or group of lights) you want to control |
| `manual_steps` | int | 10 | Number of steps to go from min to max when clicking. If the value is 2 with one click you will set the light to 50% and with another one to 100%. |
| `automatic_steps` | int | 10 | Number of steps to go from min to max when smoothing. If the value is 2 with one click you will set the light to 50% and with another one to 100%. |
| `min_brightness` | int | 1 | The minimum brightness to set to the light. |
| `max_brightness` | int | 255 | The maximum brightness to set to the light. |
| `min_color_temp` | int | 153 | The minimum color temperature to set to the light. |
| `max_color_temp` | int | 500 | The maximum color temperature to set to the light. |
| `smooth_power_on` | boolean | False | If `True` the associated light will be set to minimum brightness when brightness up is clicked or hold ad light is off. |
| `delay` | int | [Controller specific](/controllerx/controllers) | Delay in milliseconds that takes between sending the instructions to the light (for the smooth functionality). Note that the maximum value is 1000 and if leaving to 0, you might get uncommon behavior. |
| `transition` | int | 300 | Time in milliseconds that takes the light to transition from one state to another one. |
| `add_transition` | boolean | True | If `true` adds transition if supported, otherwise it does not adds the `transition` attribute. |
| key | type | value | description |
| ---------------------------- | -------------------- | ----------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `light`\* | string \| dictionary | `group.livingroom_lights` or `light.kitchen` | The light (or group of lights) you want to control |
| `manual_steps` | int | 10 | Number of steps to go from min to max when clicking. If the value is 2 with one click you will set the light to 50% and with another one to 100%. |
| `automatic_steps` | int | 10 | Number of steps to go from min to max when smoothing. If the value is 2 with one click you will set the light to 50% and with another one to 100%. |
| `min_brightness` | int | 1 | The minimum brightness to set to the light. |
| `max_brightness` | int | 255 | The maximum brightness to set to the light. |
| `min_color_temp` | int | 153 | The minimum color temperature to set to the light. |
| `max_color_temp` | int | 500 | The maximum color temperature to set to the light. |
| `smooth_power_on` | boolean | False | If `True` the associated light will be set to minimum brightness when brightness up is clicked or hold ad light is off. |
| `delay` | int | [Controller specific](/controllerx/controllers) | Delay in milliseconds that takes between sending the instructions to the light (for the smooth functionality). Note that the maximum value is 1000 and if leaving to 0, you might get uncommon behavior. |
| `transition` | int | 300 | Time in milliseconds that takes the light to transition from one state to another one. |
| `add_transition` | boolean | True | If `true` adds transition if supported, otherwise it does not adds the `transition` attribute. |
| `add_transition_turn_toggle` | boolean | True | If `false` does not add transition when turning on/off or toggling, otherwise it adds the `transition` attribute to the call. See [FAQ #6](/controllerx/faq#6-light-is-not-turning-on-to-the-previous-brightness) for a further explanation on the use of this parameter. |

_\* Required fields_

Expand Down
Loading

0 comments on commit 3bca712

Please sign in to comment.