Trigger happy automation

This could be a FAQ, but I did not manage to find any discussion, so here goes …

I expected this automation to set the light to a random color when a movie starts playing:

automation:
  - alias: "Apple TV starts playing"
    trigger:
      - platform: state
        entity_id: media_player.apple_tv
        to: 'playing'
    action:
      service: light.turn_on
      entity_id: light.movie
      data_template:
        rgb_color: ['{{ (range(0, 255)|random) }}', '{{ (range(0, 255)|random) }}', '{{ (range(0, 255)|random) }}']
        brightness: 80

In real life, though, it keeps changing colors every few seconds while the movie is playing.

What happens is that the entity attributes (the play position in particular) are updated during the play. So HA sees a change in the entity, notices that the target state is ‘playing’ and thus runs the action.

I think this is very confusing. An action can then run due to all kinds of changes that are not mentioned in the automation. Getting it to work like I want requires listing all possible “from” states, except ‘playing’.

So I made this change to see if I could get things working like I expected (this says to only run the action if the mentioned state has actually changed, or if no from/to filters are supplied at all):

--- a/homeassistant/helpers/event.py
+++ b/homeassistant/helpers/event.py
@@ -74,7 +74,9 @@ def async_track_state_change(hass, entity_ids, action, from_state=None,
         else:
             new_state = None

-        if _matcher(old_state, from_state) and _matcher(new_state, to_state):
+        no_filter = (from_state == MATCH_ALL and to_state == MATCH_ALL)
+        any_change = (old_state != new_state)
+        if no_filter or (any_change and _matcher(old_state, from_state) and _matcher(new_state, to_state):
             hass.async_run_job(action, event.data.get('entity_id'),
                                event.data.get('old_state'),
                                event.data.get('new_state'))

With that code change in place the original behavior can still be achieved by putting the state comparison into a condition. Written like that, this also works like I would expect – the entity has changed and the state is ‘playing’:

  - alias: "Apple TV is playing"
    trigger:
      - platform: state
        entity_id: media_player.apple_tv
    condition:
      - condition: state
        entity_id: media_player.apple_tv
        state: 'playing'
    action:
      # ...

This was just a quick test but it makes much more sense to me. So I guess my question is, why is HA not working like that? I must be missing something :slight_smile:

If this could be double checked and everything I would support this change. I didn’t think attributes would affect that. I agree that how you imagined it is what I thought it did as well.

I agree. Re-asserting the state shouldn’t re-trigger events.

But I wonder how many people are already exploiting this and if it’d be disruptive to change it.

I now code around these by using the “from:” key along with “to:” like

to: 'playing'
from: 'idle'

I’d suggest mentioning this on GitHub.

If we have to use from, it should be updated to support multiple states instead of requiring multiple automations.

I just use multiple triggers.

- alias: SqueezePlay begins playing
  trigger:
    - platform: state
      entity_id: media_player.squeezeplay
      to: 'playing'
      from: 'idle'
    - platform: state
      entity_id: media_player.squeezeplay
      to: 'playing'
      from: 'off'

Oh cool, still not ideal but better. I guess the triggers are treated as ‘or’ then so any one of them can trigger the automation.

Right. Automations can have as many triggers as you like. And variables from the trigger (entity_id, attributes) are available to templates in the automation.

Thanks for your support, it’s good to know that I am not alone.

As suggested, I now created an issue on GitHub: https://github.com/home-assistant/home-assistant/issues/6499

Hey, I love the idea to turn off the lights, when the apple tv plays movies. But it drives me crazy that the lights turn off, while I am playing music, so I came up with this solution. Maybe someone can use it:

- alias: movie_begins
  trigger:
    platform: state
    entity_id: media_player.apple_tv
    to: 'playing'
    for:
      minutes: 2
      seconds: 0
  condition:
    condition: and
    conditions:
      - condition: sun
        after: sunset
      - condition: template
        value_template: '{{ is_state_attr("media_player.apple_tv", "media_content_type", "movie") }}'
  action:
    - service: script.movie