BACnet automation examples for Home Assistant

Complete automations for common tasks. Replace the entity IDs with your own. The examples use the automation syntax of Home Assistant 2024.10 and newer.

Occupancy override from a calendar

Command an area to occupied while a calendar event runs, and release the command when it ends so the controller's own schedule takes over again. Actions: bacnet.write_value and bacnet.relinquish. The occupancy object here is a Multi-State Output (a number entity); check its bacnet_state_text attribute for which value means occupied on your device.

alias: Meeting room occupied during calendar events
triggers:
  - trigger: calendar
    event: start
    entity_id: calendar.meeting_room
    id: start
  - trigger: calendar
    event: end
    entity_id: calendar.meeting_room
    id: end
actions:
  - choose:
      - conditions:
          - condition: trigger
            id: start
        sequence:
          - action: bacnet.write_value
            target:
              entity_id: number.meeting_room_occupancy
            data:
              value: 1        # occupied on this device
              priority: 11
    default:
      - action: bacnet.relinquish
        target:
          entity_id: number.meeting_room_occupancy
        data:
          priority: 11        # release the same priority you wrote at

Always relinquish at the priority you wrote at. Turning a switch off or writing 0 commands the object off; only bacnet.relinquish releases it.

Temporary setpoint with a climate entity

Set a comfort setpoint in the morning and hand control back to the controller in the evening. Turning a BACnet climate entity off relinquishes its setpoint. See Climate.

alias: Office comfort setpoint on weekdays
triggers:
  - trigger: time
    at: "07:00:00"
    id: "on"
  - trigger: time
    at: "18:00:00"
    id: "off"
conditions:
  - condition: time
    weekday: [mon, tue, wed, thu, fri]
actions:
  - if:
      - condition: trigger
        id: "on"
    then:
      - action: climate.set_temperature
        target:
          entity_id: climate.office_setpoint
        data:
          temperature: 21.5
    else:
      - action: climate.set_hvac_mode
        target:
          entity_id: climate.office_setpoint
        data:
          hvac_mode: "off"

Get notified when a device stops responding

Entities become unavailable when their device doesn't answer. After 10 failed polls a message also appears in Settings → Repairs; this automation notifies you sooner. See also Troubleshooting.

alias: BACnet device offline
triggers:
  - trigger: state
    entity_id: sensor.ahu_1_supply_temperature
    to: unavailable
    for: "00:05:00"
actions:
  - action: notify.notify
    data:
      message: "AHU 1 is not responding to BACnet requests."

Notify on BACnet alarms

Uses the bacnet_alarm event; see Alarms.

alias: BACnet alarm notification
triggers:
  - trigger: event
    event_type: bacnet_alarm
actions:
  - action: notify.notify
    data:
      message: >-
        {% if trigger.event.data.event_state == 'normal' %}
          Alarm cleared: {{ trigger.event.data.object_name }}
        {% else %}
          Alarm: {{ trigger.event.data.object_name }} is {{ trigger.event.data.event_state }}
        {% endif %}

Calendar events as schedule exceptions

Instead of overriding the controller, add each calendar event to its Schedule object as an exception. The controller then knows about the event in advance, so features like optimal start can prepare the space. See Schedules for the rules. This automation adds the exception 12 hours before the event and removes it when the event ends.

alias: Calendar events as BACnet schedule exceptions
triggers:
  - trigger: calendar
    event: start
    entity_id: calendar.meeting_room
    offset: "-12:00:00"
    id: add
  - trigger: calendar
    event: end
    entity_id: calendar.meeting_room
    id: remove
actions:
  - action: bacnet.read_schedule
    target:
      entity_id: sensor.meeting_room_schedule
    response_variable: schedule
  - variables:
      event_start: "{{ trigger.calendar_event.start }}"
      event_end: "{{ trigger.calendar_event.end }}"
      current: "{{ schedule['sensor.meeting_room_schedule'].exception_schedule or [] }}"
  - if:
      - condition: trigger
        id: add
    then:
      - action: bacnet.write_schedule
        target:
          entity_id: sensor.meeting_room_schedule
        data:
          exception_schedule: >-
            {% set start = as_local(as_datetime(event_start)) %}
            {% set end = as_local(as_datetime(event_end)) %}
            {{ current + [{
                 'period': {'date': start.date().isoformat()},
                 'priority': 11,
                 'time_values': [
                   {'time': start.strftime('%H:%M:%S'), 'value': 1},
                   {'time': end.strftime('%H:%M:%S'), 'value': none}
                 ]
               }] }}
    else:
      - action: bacnet.write_schedule
        target:
          entity_id: sensor.meeting_room_schedule
        data:
          exception_schedule: >-
            {% set start = as_local(as_datetime(event_start)) %}
            {% set ns = namespace(keep=[]) %}
            {% for ex in current %}
              {% set d = ex.period.get('date') %}
              {% if not (d and ex.priority == 11 and d.year == start.year
                         and d.month == start.month and d.day == start.day) %}
                {% set ns.keep = ns.keep + [ex] %}
              {% endif %}
            {% endfor %}
            {{ ns.keep }}
          allow_removal: true