Home Assistant has no native “custom webhook” notification service the way DSM or Proxmox do — you build the payload yourself inside an automation, using the rest_command or notify.rest integration. The upside: you control every field. The downside: there is no official external contract Tempo can conform to. This page documents the contract Tempo expects, and gives you a drop-in Jinja template to produce it.

This score ships with 13 severity rules that demonstrate the full range of Tempo’s matching engine: multi-key matches (entity_id + state), glob patterns, presentation templates that rewrite the title with values pulled from the payload.

No adapter on the Tempo side is required.


Install

  1. Download home-assistant.tempo-score from the button above (or keep the bundled copy Tempo seeded on first launch).
  2. Double-click. Tempo opens a review sheet — click Install.
  3. In Tempo Settings → Ingestion, add a token named home-assistant bound to com.home-assistant. Copy the token.
  4. Note your Tempo endpoint: http://<your-mac-hostname>:7776/events.
  5. Configure Home Assistant (see below).

Home Assistant side — the rest_command

Add this to your configuration.yaml:

rest_command:
  tempo_event:
    url: "http://your-mac.local:7776/events"
    method: POST
    headers:
      Content-Type: "application/json"
      X-Tempo-Token: "YOUR_TOKEN_HERE"
    payload: >
      {{ payload }}
    content_type: "application/json"

Reload REST commands (Developer Tools → YAML → REST Commands) or restart HA.

The payload contract

Tempo expects this shape. Fields marked required are the minimum; everything else is optional metadata that your severity rules and actions can reference with ${metadata.xxx}.

{
  "title": "string — human-readable title (required; overridden by score rules)",
  "providerIdentifier": "com.home-assistant",
  "eventType": "alert",
  "metadata": {
    "entity_id":     "binary_sensor.kitchen_smoke",
    "state":         "on",
    "friendly_name": "Kitchen smoke detector",
    "area":          "Kitchen",
    "device_class":  "smoke",
    "automation":    "optional — set this instead of entity_id for automation triggers"
  }
}

Reserved Tempo fields (title, providerIdentifier, eventType) are at the top level. Everything Home Assistant specific lives in metadata.

Jinja template — one automation, all entities

Drop this automation into Settings → Automations & Scenes. It fires on any state change for any entity in the domains you care about (smoke, leak, door, window, motion, battery, climate), builds the Tempo payload with interpolated values, and POSTs it.

alias: "Tempo — forward state changes"
mode: queued
max: 25
trigger:
  - platform: state
    entity_id:
      - binary_sensor.kitchen_smoke
      - binary_sensor.garage_leak
      - binary_sensor.front_door
      - binary_sensor.living_room_motion
      - sensor.master_bedroom_battery
      - climate.living_room
action:
  - service: rest_command.tempo_event
    data:
      payload: >-
        {
          "title": "{{ state_attr(trigger.entity_id, 'friendly_name') or trigger.entity_id }}",
          "providerIdentifier": "com.home-assistant",
          "eventType": "alert",
          "metadata": {
            "entity_id":     "{{ trigger.entity_id }}",
            "state":         "{{ trigger.to_state.state }}",
            "friendly_name": "{{ state_attr(trigger.entity_id, 'friendly_name') or trigger.entity_id }}",
            "area":          "{{ area_name(trigger.entity_id) or 'Home' }}",
            "device_class":  "{{ state_attr(trigger.entity_id, 'device_class') or '' }}"
          }
        }

For automation events (e.g. a scene firing), use a separate automation that sets metadata.automation instead of metadata.entity_id:

alias: "Tempo — forward automation fires"
trigger:
  - platform: event
    event_type: automation_triggered
action:
  - service: rest_command.tempo_event
    data:
      payload: >-
        {
          "title": "Automation: {{ trigger.event.data.name }}",
          "providerIdentifier": "com.home-assistant",
          "eventType": "alert",
          "metadata": {
            "automation": "{{ trigger.event.data.name }}"
          }
        }

How the score classifies events

The bundled score runs device_class-first: matches on the stable device_class attribute that Home Assistant assigns to entities (e.g. smoke, moisture, motion, tamper) take priority over entity_id substring patterns. This is more robust because raw integration names like binary_sensor.0x00158d0001abc_ias_zone (Zigbee2MQTT) carry no semantic info — device_class is the signal that survives renames.

Rules are evaluated in order, first match wins. Highlights:

SeverityWhat triggers it
criticaldevice_class: smoke / carbon_monoxide / gas / moisture / safety (state on); alarm_control_panel.* state=triggered; legacy entity_id glob (*_smoke*, *_leak*, *_water*) for users who haven’t set device_class
warningdevice_class: tamper / problem (state on); device_class: battery (low); alarm_control_panel.* state=pending; event_type: homeassistant_stop
infoDoor/window/motion sensors; alarm panel armed/disarmed/arming transitions; locks (locked/unlocked); presence (person.* / device_tracker.* home/not_home); homeassistant_start; device_class: update; climate domain catch-all; automation: *
okSmoke cleared; moisture cleared (state: off for those classes)

For the full list (~30 rules with title/subtitle templates), open the score file in Tempo’s Score Editor or read ~/Library/Application Support/Tempo/Scores/com.home-assistant.json.

Why device_class beats entity_id matching

Two of the most common HA installs (Zigbee2MQTT, ESPHome auto-discovery) name entities by hardware ID, not semantics. A smoke detector might be binary_sensor.0x00158d0001abc_ias_zone rather than binary_sensor.kitchen_smoke. The legacy entity_id glob rules catch the second pattern but miss the first. The device_class: smoke match catches both, because Home Assistant assigns device_class based on what the sensor reports, not how you named it.

The legacy entity_id rules are kept as a fallback — for users who haven’t set device_class on a custom integration, named their entities semantically, or are using older HA Core versions where some integrations didn’t expose device_class.

Match semantics

Actions provided (5 total)

GroupActions
HA UIOpen dashboard · Open entity history · Open automations page
ClipboardCopy entity ID
DocsHome Assistant docs

Customizing

Verifying

Trigger any of the entities you listed in the automation. Tempo’s live feed should show a new alert within a second.

If not, see Troubleshooting below.


Troubleshooting

If events don’t land in Tempo, run these five checks in order.

1. Is Tempo reachable from Home Assistant? — SSH to the HA host (or use the SSH add-on terminal) and run:

curl -v http://your-mac.local:7776/health

A 200 OK means reachability is fine. A timeout or “No route to host” is a network problem (firewall on the Mac, WiFi isolation, HA running in a VLAN that can’t reach the Mac).

2. Does the token work and does Tempo accept your payload shape? — from the same HA host, send a synthetic event that mimics the automation’s payload:

curl -X POST http://your-mac.local:7776/events \
  -H 'Content-Type: application/json' \
  -H 'X-Tempo-Token: YOUR_TOKEN_HERE' \
  -d '{"title":"smoke probe","providerIdentifier":"com.home-assistant","eventType":"alert","metadata":{"entity_id":"binary_sensor.kitchen_smoke","state":"on","friendly_name":"Kitchen smoke detector","area":"Kitchen","device_class":"smoke"}}'

A 200 or 202 with “Smoke detected in Kitchen” appearing in Tempo’s feed means ingestion + the score’s presentation rule both work. A 401 means the token is wrong or not bound to com.home-assistant; a 422 means the JSON is malformed.

3. Are the packets reaching the Mac? — open Terminal on the Mac and watch inbound traffic:

sudo tcpdump -i any -A 'tcp port 7776 and src host your-ha-host.local'

Trigger a state change in HA. You should see the JSON body in the output. If nothing appears, HA’s rest_command is failing before the request leaves — check HA logs (Settings → System → Logs) for errors on rest_command.tempo_event.

4. What is Tempo doing right now? — stream Tempo’s live logs:

log stream --predicate 'subsystem == "app.tempoapp.Tempo"' --level debug

Useful to watch the score’s rule evaluation in real time.

5. What did Tempo see historically? — grep the rolling file log:

grep -h com.home-assistant ~/Library/Application\ Support/Tempo/Logs/tempo-*.log | tail -50

Every HA event Tempo has touched appears with timestamp and outcome. This is what Settings → Help → Export diagnostics bundle packages up.


References