Simpler way to define similar automations and reuse code?

No worries, take your time. I’m busy as well, will see if I find some time on the weekend.

OK here we go. I’ve commented the output so you can see what action I took, but summarising these were:

  • 1 x Press and release
  • 1 x press, hold, release
  • 2 x press
  • 3 x press
  • 4 x press
  • 5 x press

I’ve also checked the model number listed in deconz for the switch, and it is Xiaomi WXKG01LM

# Single press and release:

Event 1 fired 6:57 PM:
{
    "event_type": "deconz_event",
    "data": {
        "id": "study_button",
        "unique_id": "00:15:8d:00:03:3e:f7:b0",
        "event": 1002
    },
    "origin": "LOCAL",
    "time_fired": "2020-01-30T18:57:16.151335+00:00",
    "context": {
        "id": "30426998b12142bbb7c8b06c241969f2",
        "parent_id": null,
        "user_id": null
    }
}
Event 0 fired 6:57 PM:
{
    "event_type": "deconz_event",
    "data": {
        "id": "study_button",
        "unique_id": "00:15:8d:00:03:3e:f7:b0",
        "event": 1000
    },
    "origin": "LOCAL",
    "time_fired": "2020-01-30T18:57:15.944754+00:00",
    "context": {
        "id": "953a3a4cd7c740bd98893ce51beb646e",
        "parent_id": null,
        "user_id": null
    }
}


# Single press, hold and release

Event 2 fired 6:57 PM:
{
    "event_type": "deconz_event",
    "data": {
        "id": "study_button",
        "unique_id": "00:15:8d:00:03:3e:f7:b0",
        "event": 1003
    },
    "origin": "LOCAL",
    "time_fired": "2020-01-30T18:57:57.352069+00:00",
    "context": {
        "id": "aa7d2459cd72433d9d71be89d53e056a",
        "parent_id": null,
        "user_id": null
    }
}
Event 1 fired 6:57 PM:
{
    "event_type": "deconz_event",
    "data": {
        "id": "study_button",
        "unique_id": "00:15:8d:00:03:3e:f7:b0",
        "event": 1001
    },
    "origin": "LOCAL",
    "time_fired": "2020-01-30T18:57:54.090486+00:00",
    "context": {
        "id": "4463623904d2488b94bd9f1b38255727",
        "parent_id": null,
        "user_id": null
    }
}
Event 0 fired 6:57 PM:
{
    "event_type": "deconz_event",
    "data": {
        "id": "study_button",
        "unique_id": "00:15:8d:00:03:3e:f7:b0",
        "event": 1000
    },
    "origin": "LOCAL",
    "time_fired": "2020-01-30T18:57:53.565833+00:00",
    "context": {
        "id": "c58ce673d7ed43ceac7c60845351a28e",
        "parent_id": null,
        "user_id": null
    }
}

# 2 X press

Event 0 fired 7:00 PM:
{
    "event_type": "deconz_event",
    "data": {
        "id": "study_button",
        "unique_id": "00:15:8d:00:03:3e:f7:b0",
        "event": 1004
    },
    "origin": "LOCAL",
    "time_fired": "2020-01-30T19:00:17.980291+00:00",
    "context": {
        "id": "ae42649fc0204d498e2ac4461185e8ae",
        "parent_id": null,
        "user_id": null
    }
}

# 3 X press

Event 0 fired 7:00 PM:
{
    "event_type": "deconz_event",
    "data": {
        "id": "study_button",
        "unique_id": "00:15:8d:00:03:3e:f7:b0",
        "event": 1005
    },
    "origin": "LOCAL",
    "time_fired": "2020-01-30T19:00:30.207910+00:00",
    "context": {
        "id": "a34e635853d64cc7ab4296585b821256",
        "parent_id": null,
        "user_id": null
    }
}

# 4 X press

Event 0 fired 7:00 PM:
{
    "event_type": "deconz_event",
    "data": {
        "id": "study_button",
        "unique_id": "00:15:8d:00:03:3e:f7:b0",
        "event": 1006
    },
    "origin": "LOCAL",
    "time_fired": "2020-01-30T19:00:36.582055+00:00",
    "context": {
        "id": "e4b439dc41624e83bf554f84bfdd8b61",
        "parent_id": null,
        "user_id": null
    }
}


# 5 X press

Event 0 fired 7:00 PM:
{
    "event_type": "deconz_event",
    "data": {
        "id": "study_button",
        "unique_id": "00:15:8d:00:03:3e:f7:b0",
        "event": 1010
    },
    "origin": "LOCAL",
    "time_fired": "2020-01-30T19:00:46.103412+00:00",
    "context": {
        "id": "19310dd1fe6143229224c482b6ef60a7",
        "parent_id": null,
        "user_id": null
    }
}

Do you need anything else?
THanks!

I see two release events with the same event (1000) but the press events all have different events.

How does one interpret this data to determine when a press event occurred?

1000 is fired at the beginning of a single press - either short or long.

The release event is:
1002 - quick release
1003 - long release

I think 1001 is fired during the hold.

I.e., for short press and release, the events are:
1000 - press
1002 - release

for long press and release, the events are:
1000 - press
1001 - hold
1003 - release

FYI - note I copied the events directly from the log, so the most recent event (i.e., the release) is listed first

Thanks, that makes sense. I didn’t know it identified two kinds of press events.

I also now realize that in your first example, the events are in reverse-chronological order.

No that’s all I need. I have something you can test. Did you already install AppDaemon?

Yeah, I’ve got AppDaemon installed, I’m hoping the configuration is correct…

Ok founds logs, it should be working

I’ll provide you the test file and instructions tomorrow, need to :sleeping:

All good! I was already asleep anyway :slight_smile:

Sorry for only coming back now, some other things popped up on my to-do list :slight_smile:

I didn’t release anything new yet as I need more feedback from testers on some things.

Put this into wireless_remote.py and put the file into the appdaemon apps folder.

"""Define automations for DeCONZ/ZHA remotes."""
from appdaemon.plugins.hass.hassapi import Hass


class SwitchBase(Hass):
    """Define a base feature for all switches."""

    def initialize(self) -> None:
        """Initialize."""
        self.lights = self.args["lights"]
        self.event_type = self.args["event_type"]
        self.event_id = self.args.get("event_id")

        self.button_config = self.args.get("button_config", {})

    def dim_light(self, direction: str) -> None:
        """Start dimming of light."""
        if direction == "up":
            bri_inc = 254
        else:
            bri_inc = -254
        for light in self.lights:
            if self.is_deconz_light(light):
                self.call_service(
                    "deconz/configure",
                    field=self.get_deconz_field(light),
                    entity=light,
                    data={"bri_inc": bri_inc, "transitiontime": 50},
                )
            else:
                self.log("Dimming not supported yet for non-deconz lights")

    def stop_dim_light(self) -> None:
        """Stop dimming of light."""
        for light in self.lights:
            if self.is_deconz_light(light):
                self.call_service(
                    "deconz/configure",
                    field=self.get_deconz_field(light),
                    entity=light,
                    data={"bri_inc": 0},
                )
            else:
                self.log("Dimming not supported yet for non-deconz lights")

    def is_deconz_light(self, light: str) -> bool:
        """Return true if the light is a deconz light."""
        return self.get_state(light, attribute="is_deconz_group") is not None

    def get_deconz_field(self, light: str) -> str:
        """Return the DeCONZ light type of the given light."""
        if self.get_state(light, attribute="is_deconz_group"):
            return "/action"
        else:
            return "/state"

    def action(self, button_config: dict) -> None:
        """Call the respective service based on the passed button config."""
        service = button_config["service"]
        entity_id = button_config["entity_id"]
        data = button_config.get("data", {})

        self.call_service(f"{service.replace('.','/')}", entity_id=entity_id, **data)


class XiaomiWXKG01LM(SwitchBase):
    """Define a Mi round smart wireless switch base feature"""

    def initialize(self) -> None:
        """Configure"""
        super().initialize()
        self.button_map = {
            1000: "short_press",
            1002: "short_press_release",
            1001: "long_press",
            1003: "long_press_release",
            1004: "double_press",
            1005: "triple_press",
            1006: "quadruple_press",
            1010: "many_press",
        }

        # take action when button is pressed
        self.listen_event(
            self.button_pressed_cb,
            self.event_type,
            id=self.event_id
        )

    def button_pressed_cb(self, event_name: str, data: dict, kwargs: dict) -> None:
        """Take action when button is pressed."""
        button_code = data["event"]
        button_name = self.button_map[button_code]

        if button_name in self.button_config:
            self.action(self.button_config[button_name])
        else:
            self.log(f"Button '{button_name}' not configured. No action.")

And put this into apps.yaml

xiaomi_remote_study_room:
  module: wireless_remote
  class: XiaomiWXKG01LM
  event_type: deconz_event
  event_id: study_button #id that identifies the remote, you can see this in the events, it took the one from your code
  button_config:
    short_press_release:
      service: light.turn_on
      entity_id: 
        - light.study_ceiling
      data:
        brightness: 150
    double_press:
      service: light.turn_off
      entity_id: 
        - light.study_ceiling

This configuration will turn on light.study_ceiling at 150 brightness when the button is released after a short press and turn off light.study_ceiling when you do a double press. You can configure the other buttons in the same manner. The names of the button presses are:

  • short_press
  • short_press_release
  • long_press
  • long_press_release
  • double_press
  • triple_press
  • quadruple_press
  • many_press

I would love to get some feedback, as you are the first one testing it with this switch.

Does any of the remote send a continuous event on holding the button or just one event at the beginning of the “hold” period?

Do you require smooth dimming of lights as well or just assigning different button presses to different service calls?

I complete forgot to send you the events! Sorry about that!

The Ikea dimmer sends continuous events. The Sylvania and the Hue dimmer don’t (I think, I need to test it again). As for smooth dimming? Doesn’t matter to me as I usually use remotes for on/off and voice to set to various levels.

This is the hold event from the Ikea dimmer:

{
    "event_type": "zha_event",
    "data": {
        "unique_id": "cc:cc:cc:ff:fe:99:12:49:1:0x0008",
        "device_ieee": "cc:cc:cc:ff:fe:99:12:49",
        "endpoint_id": 1,
        "cluster_id": 8,
        "command": "stop",
        "args": []
    },
    "origin": "LOCAL",
    "time_fired": "2020-02-02T15:37:17.393168+00:00",
    "context": {
        "id": "83e0a17834184401b335d55370236542",
        "parent_id": null,
        "user_id": null
    }
}

{
    "event_type": "zha_event",
    "data": {
        "unique_id": "cc:cc:cc:ff:fe:99:12:49:1:0x0008",
        "device_ieee": "cc:cc:cc:ff:fe:99:12:49",
        "endpoint_id": 1,
        "cluster_id": 8,
        "command": "move_with_on_off",
        "args": [
            0,
            83
        ]
    },
    "origin": "LOCAL",
    "time_fired": "2020-02-02T15:37:13.853017+00:00",
    "context": {
        "id": "06b8b5fcba6a4d2ba3cac43ab1c6888d",
        "parent_id": null,
        "user_id": null
    }
}

[edit] Yeah, holding the dim up/down on the Hue remote generates separate events unlike the Ikea dimmer (which does a start and then stop at the end of the hold).

Ok, I see now that unfortunately the zha_events are completely different from the deconz_events.

So you need to provide me the whole set of button presses :sweat_smile:

if you remove event_id and make it event_data with kwargs, you can make this more generic. You might need another configuration attribute that maps important event data to your listeners.

Thanks for the suggestion. I’m already working on making it more generic, but first I need to understand the mapping of the zha events. Also I’m not sure whether I want to continue this project or not as the app provided by @xaviml is way more advanced, dimming works for non-deconz lights as well, and it already has support for ZigBee2MQTT and probably soon for ZHA as well.

Hi all,

I recently released the code to support ZHA. It’s still in a pre-release (dev branch), but you can download from HACS marking “Show beta” . You can bind the following controllers for ZHA for the moment:

  • E1810 (for a light and media player)
  • E1743
  • E1744 (symfonisk controller for light and media player)

As @Burningstone says this appdaemon app integrates with z2m and deconz as well. However, I could not integrate the Hue Dimmer due to not be properly implemented in the zha. It sends the click action as well as the hold one and it does not have an action for the release of the on/off buttons. If someone has a question, don’t hesitate to ask me or open a issue in the repository: https://github.com/xaviml/controllerx

1 Like

@123 Can you elaborate on this? I have not used scripts on HA yet.

I suggest reviewing the documentation for Scripts.

The relevant section for this discussion is titled: Passing variables to scripts