Device triggers from zigbee2mqtt vs mqtt listen, and sometimes a bit about blueprints

There are two separate questions, but they are very much connected so I will post them together.

Zigbee2MQTT documentation recommends device triggers for buttons on smart things.
This means that to set up a trigger, you need a device ID and IEEE address, and neither can be templated.
This is already an inconvenience, but when it comes to blueprints, it becomes a disaster: there appears to be no way to use MQTT device trigger in a blueprint.
So far I have found two ways of dealing with this:

  • Using all possible triggers, and then processing the payload in a script. LImited and does not allow for sequences like (press and hold two seconds, press another button, sneeze).
  • Throwing an event upon a device trigger and doing all through events.

And first question: am I missing something here?

Back to Zigbee2MQTT. Device triggers are recommended, and two alternative options are suggested: state change and listening to a topic with condition.
Now, why exactly are these ones not recommended? I can imagine that state change monitoring might introduce delays, but how about MQTT listening? If there would be a possibility to enforce QOS [12], there should be no difference with device trigger except for some minor overhead for monitoring the topic.

And I am unsure how to phrase the question, but the essence is likely: considering all above, and all I have missed, is there a pretty way to efficiently process actions from Z2M devices, which is templateable and blueprintable?

3 Likes

I can’t add anything unfortunately, but I ran into the same issue earlier. It’s an odd one as both Z2M and Home Assistant recommend the MQTT Device Triggers, but I don’t see how they can work in the current Blueprints format due to the two id requirements in the trigger.

Also, there doesn’t appear to be a trigger object for devices (or at least it isn’t documented here https://www.home-assistant.io/docs/automation/templating/). This means you can’t pull through the ‘subtype’ to create the actions.

This might be intentional, you can create the automations from the device page and it will give you the option to select the action type. This might well be the intended behaviour, and would be good to get some clarity either way.

Homeassistant might, but Z2M uses mqtt discovery to generate entities, you shouldn’t be using mqtt triggers for Z2M, you should be using the states of the entities.

Also, as a point, homeassistant has been pushing device triggers as the best thing since sliced bread for a while, but they’re really not. You don’t have to spend long on the forums fixing people’s automations to see that. Use them at your own risk.

First of all, states by their very idea are not suited for events. Keeping consistent flow of a sensor states is one thing, delivering a discrete change is another. This may, but is not necessarily done one through another.
Second, Zigbee2MQTT recommends using device triggers in its documentation.
Third, some devices simply don’t have states, only triggers. Which may be consumed either as mqtt device triggers or by listening to a topic.

The Zigbee2Mqtt website also recommends the device triggers though (https://www.zigbee2mqtt.io/integration/home_assistant.html). It’s not that the device triggers themselves are an issue, from the sounds of it both myself and the OP have had no issue setting them up as triggers.

It is that the new Blueprints feature seems incompatible at this time, and it would be useful to know if that was by design or is awaiting an update.

Allow me to disagree. It is not just blueprints. The whole lack of template ability in device triggers is limiting. Also, iEEE addresses in some cases might be redundant, for they exist in device specs. So this might definitely be something to improve.
However, a trigger is not something that can exist with living templates, so in order to create a set of triggers for a device of a certain type, a work around may, indeed, be done in blueprints.

Well, that is a very sad turn of events. Seems someone over there drank the kool aid.

All I can say is that this is a bad move and will lead to many complications down the road.

Z2M has always represented buttons as sensors with states and it works well.

trigger:
  platform: state
  entity_id: sensor.my_button
  to: 'single_click' 

This can easily be used in blueprints too, simply change entity_id key value to the !input of your choice.

Is it the right time to summon @Koenkk?

This way is also described in the manual, but called legacy and is discouraged. I am trying to understand why. And also, what is wrong with MQTT triggers, if anything.

MQTT triggers are OK - although they’re more complicatecd than state triggers so in my opinion should only be used where a state trigger is not available, but that’s personal choice. The problem is the DEVICE triggers and actions. Here’s why…

(This is going to be quite a long post, but explains it the best I can, so try and stick with it :slight_smile: )

Imagine this for a simple scenario:

  • You have a downstairs in a house with two rooms - living room and kitchen.
  • Each has a zigbee light in them - light.livingroom and light.kitchen
  • You also create a light group called light.downstairs that conrols both the livingroom and kitchen light together.

So you buy a hue 4 button remote and position in the doorway that joins the two rooms. The hue 4 button remote has three events for each button - single_click, long_click and double_click.

You decide to automate as follows:

  • single clicks will control the livingroom
  • double clicks will control the kitchen
  • long clicks will control both
  • the on button will turn the appropriate light on
  • the off button will turn the appropriate light off
  • the brightness up button will increase the brightness of the appropriate light by 10%
  • the brightness down button will decrease the brightness of the appropriate light by 10%

Using device discovery, your hue 4 button remote has created a sensor called sensor.light_switch

So, using the simple state based triggers and actions (which support templating) you can create a fairly simple single automation to do all of the above

(Psuedo code to avoid writing the templates, and making this post even longer!)

alias: control all downstairs lighting
trigger:
  platform: state
  entity_id: sensor.light_switch
action:
  - choose:
      - conditions: >
          # template to define if this was an on or off button that was pressed
        sequence:
          - service: "light.turn_{{ trigger.to_state.state|replace('-press', '') }}"
            entity_id: >
               # template to define if this was a single_click, long_click or double_click
               # and output livingroom, kitchen or downstairs as apprpriate
    default:
       # this means one of the brightness buttons has been pressed
      - service: light.turn_on
        data:
          entity_id: >
            # template to define if it was a single_click, long_click or double_click
            # and output livingroom, kitchen or downstairs as apprpriate
          brightness_step_pct: "{{ 10 if trigger.to_state.state == 'up-press' else -10 }}"

Now to do this with device triggers and actions, you would need 12 automations (or a very complicated single automation with 12 triggers and 12 actions) due to the inability to template. And crucially each automation will contain the device_id for the trigger and a device_id for the lights in the actions. This is where the problems start.

Scenario 1 - minor failure

Imagine that the hue 4 button remote falls off the wall and smashes into a thousand pieces so has to be replaced. When it is replaced you can set the entity_id for the replacement to the same entity_id that you had before. 20 seconds work, and the automation I gave above continues to work.

BUT the device_id will have changed because homeassistant sees it as a new device, so if you used the device triggers you now have to go through 12 automations and change the device id in all your triggers. This wouldn’t be quite so bad if device_ids were easy to discern, but they’re very long ‘random’ things and frankly it’s a pain in the backside.

Scenario 2 - big failure

Imagine that your zigbee dongle fails. You purchase a new dongle and set about re-pairing your bulbs and your hue remote. Again, you can quickly set the entity_ids back to what they were before, but homeassistant sees these all as new devices and assigns them all new device_ids.

Again, using my state based automation above, there is no further work to do. The entity_ids match again, so the automation works the same as it did before.

But for device based automations, you now have to go through and replace 12 device_ids in the triggers and 12 device_ids in the actions.

Somewhere in this process it is likely a mistake is going to be made and you’re going to end up in a world of problems.

All of these problems have been caused by only 3 entities/devices. When you multiply this by how many entities/devices you actually have in your house, using device triggers and actions is a recipe for a headache.

I have around 40 zigbee devices in my house currently and intend to add more. The idea of using device triggers and actions to control them sends shivers down my spine!

Hope this makes sense.

7 Likes

(I should also add [before I’m accused of being negative!] that I’m sure in the future device triggers and actions will be improved, and then they probably will end up being the best option, they’re very clever and work well with the UI for creating functional automations - my concern at the moment is that they are being pushed as the best option now, when they aren’t for many reasons including the ones above)

Oooh, and one final note on this subject from me…

Have a look at the three examples on the page that is linked above for the three ways to use Z2M for triggers and note the complexity of the three different options to do the exact same thing.

State based = 7 lines of code, nothing to mess up. Easy to recover if a device needs replacing as no changes will be needed to the automation. Can also be modified later with ease if circumstances change and templates are required.

MQTT based = 10 lines of code, requires a template condition to filter the event you want so adds complexity, but just as easy to recover if device needs replacing, and just as easy to modify.

Device based = The example shows 11 lines of code, but uses a state based action so actually using devices would make it 14 lines. Requires knowledge of types and subtypes of devices. Requires knowledge of arbitrary device_id. Lots to go wrong. Requires manually editing device_id if device needs replacing. Cannot be modified to add templating and will need to be completely rewritten as either state or mqtt based if circumstances change.

I don’t think you’re being negative at all, as things currently stand you are 100% right. It’s just bonkers that both pieces of software recommend it as the best option, despite it being demonstrably worse when it comes to creating automations, which is a pretty significant part of running Home Assistant.

1 Like

Yeah, I mean Koen hasn’t responded yet, but depending on the tone of voice you read the Z2M docs in they could either mean

  • Z2M recommends using this on homeassistant

Or

  • homeassistant recommends doing it this way

If it’s meant to be the second one then whilst it is ‘true’ it doesn’t actually endorse that as the best option for zigbee2mqtt, although I agree that the way it is written it does sound more like the first one.

Either way I’d be interested in his thoughts on the matter.

I don’t think it’s a zigbee2mqtt issue, and i’m assuming their recommendation for it is due to Home Assistant currently stating it as the '‘better’ option (i.e. Z2M are just following).

I had a quick scan through the Home Assistant github to see if I can find anything in the issues/pull requests but nothing obvious stood out, which makes me think this might all be intentional.

2 Likes

I just found out the hard way about this :frowning: . Trying to live non-legacy and live in the future, but my blueprint is not clearly useless because I can’t find a way how to template discovery_id + contain a lot of duplication. Consider this simple control of a IKEA floalt LED panel with the Tradfi 5 button controller:

blueprint:
  name: Z2M - IKEA five button remote for lights with mqtt device triggers
  description: 'Control lights with an IKEA five button remote (the round ones).
    This is a fork of @Frenck''s blueprint for ZHA found here https://community.home-assistant.io/t/zha-ikea-five-button-remote-for-lights/253804 '
  domain: automation
  input:
    remote:
      name: Remote
      description: IKEA remote to use
      selector:
        device:
          integration: mqtt
          manufacturer: IKEA
          model: TRADFRI remote control (E1524/E1810)
    light:
      name: Light(s)
      description: The light(s) to control
      selector:
        target:
          entity:
            domain: light
    force_brightness:
      name: Force turn on brightness
      description: 'Force the brightness to the set level below, when the "on" button
        on the remote is pushed and lights turn on.

        '
      default: false
      selector:
        boolean: {}
    brightness:
      name: Brightness
      description: Brightness of the light(s) when turning on
      default: 50
      selector:
        number:
          min: 0.0
          max: 100.0
          mode: slider
          step: 1.0
          unit_of_measurement: '%'
  source_url: https://community.home-assistant.io/t/zigbee2mqtt-ikea-five-button-remote/255308
mode: restart
max_exceeded: silent
variables:
  force_brightness: !input 'force_brightness'
trigger:
- platform: device
  domain: mqtt
  device_id: !input 'remote'
  type: action
  subtype: toggle
  discovery_id: 0x804b50fffe82fb13 action_toggle
  id: "_toggle"
- platform: device
  domain: mqtt
  device_id: !input 'remote'
  type: action
  subtype: brightness_up_click
  discovery_id: 0x804b50fffe82fb13 action_brightness_up_click
  id: "_brightness_up"
- platform: device
  domain: mqtt
  device_id: !input 'remote'
  type: action
  subtype: brightness_down_click
  discovery_id: 0x804b50fffe82fb13 action_brightness_down_click
  id: "_brightness_down"
- platform: device
  domain: mqtt
  device_id: !input 'remote'
  type: action
  subtype: brightness_up_hold
  discovery_id: 0x804b50fffe82fb13 action_brightness_up_hold
  id: "_brightness_up_hold"
- platform: device
  domain: mqtt
  device_id: !input 'remote'
  type: action
  subtype: brightness_up_release
  discovery_id: 0x804b50fffe82fb13 action_brightness_up_release
  id: "_brightness_up_release"
action:
- variables:
    command: '{{ trigger.payload }}'
- choose:
  - conditions:
    - '{{ command == ''toggle'' }}'
    sequence:
    - choose:
      - conditions: '{{ force_brightness }}'
        sequence:
        - service: light.toggle
          target: !input 'light'
          data:
            transition: 1
            brightness_pct: !input 'brightness'
      default:
      - service: light.toggle
        target: !input 'light'
        data:
          transition: 1
  - conditions:
    - '{{ command == ''brightness_up_click'' }}'
    sequence:
    - service: light.turn_on
      target: !input 'light'
      data:
        brightness_step_pct: 10
        transition: 1
  - conditions:
    - "{{ command == 'brightness_down_click' }}"
    sequence:
    - service: light.turn_on
      target: !input light
      data:
        brightness_step_pct: -10
        transition: 1
  - conditions:
      - "{{ command == 'brightness_up_hold' }}"
    sequence:
      - repeat:
          until:
            - condition: trigger
              id: "_brightness_up_release"
          sequence:
            - service: light.turn_on
              data:
                brightness_step_pct: 10
                transition: 1
              target: !input light
            - delay: '1'

EDit: Add firt-class support for MQTT Device Triggers (to have feature parity with legacy triggers)