Why and how to avoid device_ids in automations and scripts

Don’t forget that the first post in a community guide is a wiki - feel free to add to it or correct it.

This guide was written with reference to:

Home Assistant 2023.8.3
Supervisor 2023.08.1
Operating System 10.5
Frontend 20230802.1 - latest

Background

When you start writing automations, a device trigger seems the obvious choice. Everybody knows what a device is and it comes at the top of the UI dropdown list.

It works… but it is certainly not a “great way to start” because it is storing up problems for the future. Far better to use entity, with state or numeric_state.

Device_id problems

  • The yaml will be longer and harder to follow, which may make maintenance difficult, especially a few months down the line when you have forgotten the details. For the same reason, sharing code (on this forum, for example) is harder. Since you can’t add comments to automations each step needs to be as clear as possible. Why have this:
trigger: 
  - type: battery_level
    platform: device
    device_id: 16fbcd84302f4606819c58559aa90c82
    entity_id: 6c2e634686ebe58e571fefcdd6474f22
    domain: sensor
    below: 20

When you could have this:

trigger:
  - platform: numeric_state
    entity_id: sensor.mobile_battery_level
    below: "20"
  • If you replace a device you will have to go through every automation it appears in, changing the device_id. If you only ever use entity_ids in your triggers, conditions and actions, then when you have to replace a device you can change the new entity_id to match the old one in one place, and all your automations keep working.

  • Automations using device_id report as unavailable if they depend on missing devices.

  • Device actions, triggers and conditions do not support more advanced features like templating.

Devices and entities

So entity ids are preferable to device ids. What’s the difference?

Essentially, devices are physical objects, while entities are the functions or services they provide.

For example, a Philips Hue motion sensor is a device which acts as a container for seven entities.

  • Three of the entities are sensors (temperature, light and motion). The temperature and light sensors have a numeric state; the motion sensor is binary - its state will be either “on” or “off”.

  • Two are switches allowing light and motion sensors to be switched on and off; these are also binary.

  • Two are diagnostic (battery level and zigbee connectivity). The battery level sensor will have a numeric state; the zigbee sensor will have a state such as “connected”.

Confusingly, while all devices should have associated entities, an entity does not have to belong to a device. For example, you might want to have a binary sensor that was “on” when your phone was connected to your home wi-fi and otherwise “off”, even if it was connected elsewhere. In this case you could create a template sensor, derived from, but not part of the device:

template:
  - binary_sensor:
      - name: "phone on wifi"
        state: >
          {{ is_state('sensor.mobile_wifi_connection', 'MyNetwork') }}

In addition a great many entities are created by integrations that have nothing to do with any device:

A few entities are not part of a device or service. Examples of standalone entities are automation, script, scene entities, and helper entities.

States

Numeric state triggers are for anything you can count. Both binary sensors and non-numeric sensors use State triggers and conditions (binary sensors will just be “on” or “off”).

Unfortunately, in an effort to make the UI more friendly, some developers have introduced substitutions. In the Philips Hue example above, for example, the motion sensor will show as either “Clear” or “Detected” in the UI.

But it’s a binary sensor, and if you look at the yaml it will always be “on” or “off”:

trigger:
  - platform: state
    entity_id:
      - binary_sensor.yard_motion_sensor_motion
    from: "off"
    to: "on"

If you write in yaml, “on” and “off” are the states you have to use. Note too that in the UI all the states have an initial capital letter; in yaml they should be lower case. If you need to check which states you should be using, go to Developer Tools | States:

Binary sensor state in Developer Tools

Buttons

Having said all that, buttons are an oddity. They do not have a state. A button press is an event - it doesn’t remain on or off. The entity does, however, retain a timestamp showing the last time it was pressed and this becomes its state, allowing it to be used as an automation trigger when it changes.

Actions

To avoid device ids in the action section of an automation, choose call service from the dropdown list.

Most service names begin with the device class the service will act on - light, switch, media_player etc. - followed by the action - turn on, turn off, play, pause, etc.

You will then be invited to select one or more target entities.

Changing the entity name on a new device

If you replace one of your devices, say the motion sensor mentioned above, you can change the names of the entities associated with it to the names you have already used in your automations, saving yourself a lot of work. Go to Devices & Services | Devices and select the device. For each of the entities in turn, click on the name, then on the cogwheeel “settings” icon top right.

You can edit the Entity ID field to match the entity ID from the old device.

13 Likes

That should have been fixed. The automations should now report as unavailable if they depend on missing devices. However that change may have made things worse:

Damn. I got that from one of your posts :grinning_face_with_smiling_eyes:. Guide updated, thanks…

Excellent advice! Just went through and updated all my automations to not use device IDs in the actions, but some of the automations use device_id’s in the conditions. is there a way to fix that? Most of my conditions are simple “is it on”, for example:

      - condition: device
        device_id: 5f3f155a7995b74b6a36ec9555cfc217
        domain: media_player
        entity_id: media_player.big_tv
        type: is_on

Use a State condition (or whatever the appropriate condition is for other cases)…

- condition: state
  entity_id: media_player.big_tv
  state: 'on'
2 Likes

How does one apply this advice to a manufacturer-specific ZHA command? Example: I have an Inovelli switch and the Device action gives me this “Issue effect for individual LED” option which then enables various fields (defined here in ZHA):

The YAML for this action looks like:

type: issue_individual_led_effect
domain: zha
device_id: ccd62cc4a251872fa2edef785bdf798e
led_number: 4
effect_type: Clear
color: 150
level: 100
duration: 255

However, there does not appear to be a service named zha.issue_individual_led_effect and when I try to use that name in a “Call service” action I don’t see the fields anymore:

Further, I was hoping to be able to set the device id (or entity id) of this action using a template, so that I can fill in a script input there. Is that possible?

The service call is zha.issue_zigbee_cluster_command.

Here’s a script blueprint:

2 Likes

Here’s a current Jan-2024 output from the UI editor.

Which of these 2 conditions would you rather troubleshoot?
2 - 32 character random hex strings or an entity_id?

action:
  - choose:
      - conditions:
          - condition: trigger
            id:
              - dirty
        sequence:
          - type: turn_on
            device_id: a98db942fb9411339403edc7c7f9e3de
            entity_id: fa492ac875bdb706179edac181b3297a
            domain: fan
      - conditions:
          - condition: trigger
            id:
              - clean
        sequence:
          - service: fan.turn_off
            target:
              entity_id: fan.r2d2
            data: {}

I rest my case.

So how does using Entities affect automations say for instance i have a motion sensor that turns on a led strip if all the upstairs lights are off. If these are smart bulbs and someone flicks the (not smart) switch and the bulb becomes unavailable will this cancel the automation or will it count “unavailable” as off?

The state will be unavailable. If you want to trigger from on to off or from on to unavailable or unknown do this:

trigger:
  - platform: state
    entity_id: light.my_smart_light
    from: 'on'

if you only want to trigger from on to off do:

trigger:
  - platform: state
    entity_id: light.my_smart_light
    from: 'on'
    to: 'off'

Correct. Unavailable is unavailable, unknown is unknown, neither would be considered as off.

Off is off.