Skip to content

Commit

Permalink
Merge pull request #502 from xaviml/fix/hue-dimmer-z2m-mapping
Browse files Browse the repository at this point in the history
fix(hue-dimmer): change z2m mapping to be compatible with non-legacy mapping
  • Loading branch information
xaviml authored Jun 5, 2022
2 parents 33b97a4 + d30cc59 commit bdbf6b4
Show file tree
Hide file tree
Showing 7 changed files with 211 additions and 60 deletions.
6 changes: 3 additions & 3 deletions RELEASE_NOTES.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[![downloads](https://img.shields.io/github/downloads/xaviml/controllerx/VERSION_TAG/total?style=for-the-badge)](http://github.com/xaviml/controllerx/releases/VERSION_TAG)

<!--:warning: This major/minor change contains a breaking change.-->
:warning: This minor change contains a breaking change.

_This minor change does not contain any breaking changes._
_Note: Remember to restart the AppDaemon addon/server after updating to a new version._
Expand All @@ -10,9 +10,9 @@ PRERELEASE_NOTE

- Allow passing the delay time (in seconds) to `release_delay` attribute. [ #497 ]

<!--
## :hammer: Fixes
-->

- :warning: Change [Hue Dimmer](https://BASE_URL/controllerx/controllers/HueDimmer) mapping for Zigbee2MQTT to be compatible with `legacy: false` mapping. This option will need to be [enabled from Zigbee2MQTT](https://www.zigbee2mqtt.io/devices/324131092621.html#options). [ #496 ]

<!--
## :clock2: Performance
Expand Down
24 changes: 12 additions & 12 deletions apps/controllerx/cx_devices/philips.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,18 @@
class HueDimmerController(LightController):
def get_z2m_actions_mapping(self) -> DefaultActionsMapping:
return {
"on-press": Light.ON,
"on-hold": Light.HOLD_COLOR_UP,
"on-hold-release": Light.RELEASE,
"up-press": Light.CLICK_BRIGHTNESS_UP,
"up-hold": Light.HOLD_BRIGHTNESS_UP,
"up-hold-release": Light.RELEASE,
"down-press": Light.CLICK_BRIGHTNESS_DOWN,
"down-hold": Light.HOLD_BRIGHTNESS_DOWN,
"down-hold-release": Light.RELEASE,
"off-press": Light.OFF,
"off-hold": Light.HOLD_COLOR_DOWN,
"off-hold-release": Light.RELEASE,
"on_press": Light.ON,
"on_hold": Light.HOLD_COLOR_UP,
"on_hold_release": Light.RELEASE,
"up_press": Light.CLICK_BRIGHTNESS_UP,
"up_hold": Light.HOLD_BRIGHTNESS_UP,
"up_hold_release": Light.RELEASE,
"down_press": Light.CLICK_BRIGHTNESS_DOWN,
"down_hold": Light.HOLD_BRIGHTNESS_DOWN,
"down_hold_release": Light.RELEASE,
"off_press": Light.OFF,
"off_hold": Light.HOLD_COLOR_DOWN,
"off_hold_release": Light.RELEASE,
}

def get_deconz_actions_mapping(self) -> DefaultActionsMapping:
Expand Down
73 changes: 42 additions & 31 deletions docs/device_template.md
Original file line number Diff line number Diff line change
@@ -1,40 +1,51 @@
{% set device = controller_docs.name %}
{% set ct_docs = controller_docs.controller_type_docs %}

<table>
<tr>
<th>Picture</th>
<th>
<a href="#">Controller types</a>
</th>
<th>Integrations</th>
</tr>
<tr>
<td style="vertical-align: middle;"><img src="/controllerx/assets/controllers/{{ device }}.jpeg" /></td>
<td style="vertical-align: middle;">
<ul>
{% for ctrl in controllers %}
<li>
<a href="#{{ ctrl.type | lower | replace(' ','-') }}">{{ ctrl.type }}</a> — <code>{{ctrl.cls}}</code>
</li>
{% endfor %}
</ul>
</td>
<td style="vertical-align: middle;">
<ul>
{% for integration in controllers[0].integrations_list %}
<li>
{{ INTEGRATIONS_TITLES[integration] }} ({{ integration }})
</li>
{% endfor %}
</ul>
</td>
</tr>
</table>
{% for controller in controllers %}
<tr>
<th>Picture</th>
<th>
<a href="#">Controller types</a>
</th>
<th>Integrations</th>
</tr>
<tr>
<td style="vertical-align: middle;"><img src="/controllerx/assets/controllers/{{ device }}.jpeg" /></td>
<td style="vertical-align: middle;">
<ul>
{% for ctrl in ct_docs %}
<li>
<a href="#{{ ctrl.type | lower | replace(' ','-') }}">{{ ctrl.type }}</a> — <code>{{ctrl.cls}}</code>
</li>
{% endfor %}
</ul>
</td>
<td style="vertical-align: middle;">
<ul>
{% for integration in ct_docs[0].integrations_list %}
<li>
{{ INTEGRATIONS_TITLES[integration] }} ({{ integration }})
</li>
{% endfor %}
</ul>
</td>
</tr>
</table>

{% if controller_docs.notes is not none %}

## Notes

{{ controller_docs.notes }}
{% endif %}

{% for controller in ct_docs %}

## {{ controller.type }}

Class: `{{ controller.cls }}`

{% if controller.delay is not none%}
{% if controller.delay is not none %}
Default delay: `{{ controller.delay }}ms`
{% endif %}

Expand Down
4 changes: 3 additions & 1 deletion docs/docs/controllers.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ Currently **{{ controllers | length }}** devices are supported.
{% for device, controller_docs in controllers.items() %}
<tr>
<td style="vertical-align: middle;"><h3><a href="{{ device }}">{{ device }}</a></h3></td>
<td style="vertical-align: middle;">{{ controller_docs[0].integrations_titles | join(", ") }}</td>
<td style="vertical-align: middle;">
{{ controller_docs.controller_type_docs[0].integrations_titles | join(", ") }}
</td>
<td style="vertical-align: middle;"><img src="/controllerx/assets/controllers/{{ device }}.jpeg" /></td>
</tr>
{% endfor %}
Expand Down
5 changes: 2 additions & 3 deletions docs/gen_device_pages.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,11 @@

template = env.get_template("device_template.md")

for device, controllers in devices.items():
for device, controller_docs in devices.items():
with mkdocs_gen_files.open(f"controllers/{device}.md", "w") as f:
print(
template.render(
device=device,
controllers=controllers,
controller_docs=controller_docs,
INTEGRATIONS_TITLES=INTEGRATIONS_TITLES,
),
file=f,
Expand Down
38 changes: 28 additions & 10 deletions docs/main.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
from collections import defaultdict
from dataclasses import dataclass
from pathlib import Path
from typing import Any, Callable, Dict, List, Optional, Tuple
from unittest import mock

import appdaemon.plugins.hass.hassapi as hass
import cx_devices as devices_module
import yaml
from cx_const import ActionEvent, DefaultActionsMapping
from cx_core import (
CoverController,
Expand Down Expand Up @@ -57,9 +59,12 @@
},
]

with open(Path(__file__).parent / "notes.yaml") as f:
NOTES: Dict[str, str] = yaml.full_load(f)


@dataclass
class ControllerDocs:
class ControllerTypeDocs:
order: int
type: str
cls: str
Expand Down Expand Up @@ -131,6 +136,13 @@ def make_table(self) -> str:
return "\n".join(table)


@dataclass
class ControllerDocs:
name: str
controller_type_docs: List[ControllerTypeDocs]
notes: Optional[str]


def get_device_name(controller: str) -> str:
return (
controller.replace("Light", "")
Expand All @@ -156,7 +168,7 @@ def get_controller_type(controller: TypeController[Entity]) -> Tuple[str, int]:
)


def get_controller_docs(controller: TypeController[Entity]) -> ControllerDocs:
def get_controller_docs(controller: TypeController[Entity]) -> ControllerTypeDocs:
controller_type, order = get_controller_type(controller)
controller_class = controller.__class__.__name__
delay: Optional[int] = None
Expand Down Expand Up @@ -186,7 +198,7 @@ def get_controller_docs(controller: TypeController[Entity]) -> ControllerDocs:
for action_event, predefined_action in mapping.items():
mappings[predefined_action][integration].append(action_event)

return ControllerDocs(
return ControllerTypeDocs(
order=order,
type=controller_type,
cls=controller_class,
Expand All @@ -206,21 +218,27 @@ def get_controllers() -> List[TypeController[Entity]]:
return controller_instances


def get_devices() -> Dict[str, List[ControllerDocs]]:
def get_devices() -> Dict[str, ControllerDocs]:
devices = defaultdict(list)
for controller in get_controllers():
device_name = get_device_name(controller.__class__.__name__)
controller_docs = get_controller_docs(controller)
devices[device_name].append(controller_docs)
# Sort the controller types,
# so they appear in the same order in the documentaiton
for device in devices:
devices[device].sort(key=lambda c: c.order)
return devices

devices_docs: Dict[str, ControllerDocs] = {}
for device_name, docs in devices.items():
# Sort the controller types,
# so they appear in the same order in the documentaiton
docs = sorted(docs, key=lambda c: c.order)
devices_docs[device_name] = ControllerDocs(
name=device_name, controller_type_docs=docs, notes=NOTES.get(device_name)
)

return devices_docs


def define_env(env: MacrosPlugin) -> None:
@env.macro # type: ignore[misc]
def devices() -> Dict[str, List[ControllerDocs]]:
def devices() -> Dict[str, ControllerDocs]:
devices = get_devices()
return dict(sorted(devices.items(), key=lambda device: device[0]))
121 changes: 121 additions & 0 deletions docs/notes.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
E1744: |
If you want to use the default controller, you will need to set `legacy: false` in the Zigbee2MQTT configuration.
Read more in <a href="https://www.zigbee2mqtt.io/devices/E1744.html#legacy-integration">here</a>.
HueDimmer: |
In case of working with <strong>z2m</strong> the device needs to be flagged as `legacy: false` as
<a href="https://www.zigbee2mqtt.io/devices/324131092621.html#options">Zigbee2MQTT
documentation recommends</a>.
In case of working with <strong>z2m</strong> and wanting to use the multiclick functionality, then
you need to make sure to remove from the device specific (devices.yaml) the
`multiple_press_timeout` option.
In case of <strong>deCONZ</strong>, all the click actions are mapped with X002 actions, so when the
button is released after being clicked.
LutronPJ22B: |
For the State integration, it requires the <a href="https://github.com/upsert/lutron-caseta-pro">LutronCasetaPro</a>
CUSTOM integration by upsert. THIS WILL NOT WORK with the default Lutron Caseta integration.
All Lutron Caseta Pro Pico remotes supported by LutronCasetaPro are supported using this
controller type except for the 4-button PJ2-4B remotes which is separate.
LutronPJ22BRL: |
For the State integration, it requires the <a href="https://github.com/upsert/lutron-caseta-pro">LutronCasetaPro</a>
CUSTOM integration by upsert. THIS WILL NOT WORK with the default Lutron Caseta integration.
All Lutron Caseta Pro Pico remotes supported by LutronCasetaPro are supported using this
controller type except for the 4-button PJ2-4B remotes which is separate.
LutronPJ23BRL: |
For the State integration, it requires the <a href="https://github.com/upsert/lutron-caseta-pro">LutronCasetaPro</a>
CUSTOM integration by upsert. THIS WILL NOT WORK with the default Lutron Caseta integration.
All Lutron Caseta Pro Pico remotes supported by LutronCasetaPro are supported using this
controller type except for the 4-button PJ2-4B remotes which is separate.
LutronPJ24B: |
For the State integration, it requires the <a href="https://github.com/upsert/lutron-caseta-pro">LutronCasetaPro</a>
CUSTOM integration by upsert. THIS WILL NOT WORK with the default Lutron Caseta integration.
MFKZQ01LM: |
This controller does not have a self-explained way to use it since its main
purpose is to be customized, this is why we recommend to customize it with
<a href="/controllerx/advanced/custom-controllers">custom mapping</a>.
deCONZ integration should be added with <a href="/controllerx/others/integrations#deconz">type gesture</a>.
MLI404011: |
Some buttons are left to be mapped for both integrations.
Please read more about in <a href="https://github.com/xaviml/controllerx/issues/111">here (deconz)</a>
and <a href="https://github.com/xaviml/controllerx/issues/176">here (z2m)</a>.
OsramAC025XX00NJ: |
This controller is supported for AC0251100NJ / AC0251400NJ / AC0251600NJ / AC0251700NJ.
Different models are just different colours.
Philips929003017102: |
This controller has 4 modes and depending on which one is used,
it will trigger one action or another: single_rocker, single_push_button, dual_rocker, dual_push_button.
You can read more about it in these threads:
<a href="https://github.com/dresden-elektronik/deconz-rest-plugin/issues/4566">deconz</a> and
<a href="https://github.com/Koenkk/zigbee-herdsman-converters/issues/2393">zigbee2mqtt</a>.
PTM215X: |
Check the button mapping in <a href="https://phoscon.de/en/support#pairing-friends-of-hue-switch">here</a>.
ROB2000070: |
Note that each button perform the same actions.
This is because ControllerX is designed to control just one entity (light or media player).
So, you can include the `actions` parameter to include the actions needed,
or use the `mapping` attribute to define a <a href="/controllerx/advanced/custom-controllers">custom mapping</a>.
You can check <a href="https://github.com/xaviml/controllerx/issues/18#issuecomment-582535634">here</a> for reference.
This controller is a white-label version of the Sunricher SR-ZG9001K8-DIM.
TS0044: |
Note that each button perform the same actions.
This is because ControllerX is designed to control just one entity (light or media player).
So, you can include the `actions` parameter to include the actions needed,
or use the `mapping` attribute to define a <a href="/controllerx/advanced/custom-controllers">custom mapping</a>.
You can check <a href="https://github.com/xaviml/controllerx/issues/18#issuecomment-582535634">here</a> for reference.
Also, this controller support hold action, but not release. This means that there is not
smooth brightness changes when holding, just step by step everytime is held.
TS0044F: |
This device is very similar to the TS0044 Tuya, except that it doesn't hold action, double clicks.
Note that each button perform the same actions.
This is because ControllerX is designed to control just one entity (light or media player).
So, you can include the `actions` parameter to include the actions needed,
or use the `mapping` attribute to define a <a href="/controllerx/advanced/custom-controllers">custom mapping</a>.
You can check <a href="https://github.com/xaviml/controllerx/issues/18#issuecomment-582535634">here</a> for reference.
Also, this controller doesn't support hold action. This means that there is not
smooth brightness changes when holding, just step by step everytime it's pressed.
W2049: |
This controller fires "on" action when arrows are held as well as the hold arrow action.
So this means that when long pressing the arrows, the light(s) will turn on before changing the color.
WXKG01LM: |
The ZHA implementation of this button does not yet support hold and release actions.
WXKG02LM: |
Note that all actions related to both, left and right, do the same.
This is because ControllerX is design to control just one entity (light or media player).
So you can include the `actions` parameter to include the actions needed.
You can check <a href="https://github.com/xaviml/controllerx/issues/18#issuecomment-582535634">here</a> for reference.
Also, this controller support hold action, but not release. This means that there is not
smooth brightness changes when holding, just step by step everytime is held.
WXKG06LM: |
This controller support hold action, but not release. This means that there is not
smooth brightness changes when holding, just step by step everytime is held.
WXKG07LM: |
Note that all actions related to both, left and right, do the same.
This is because ControllerX is design to control just one entity (light or media player).
So you can include the `actions` parameter to include the actions needed.
You can check <a href="https://github.com/xaviml/controllerx/issues/18#issuecomment-582535634">here</a> for reference.
Also, this controller support hold action, but not release. This means that there is not
smooth brightness changes when holding, just step by step everytime is held.

0 comments on commit bdbf6b4

Please sign in to comment.