feat: Trigger automations on call answered, rejected and hung up (#2591)

This commit is contained in:
Dermot Duffy
2026-07-22 11:11:57 -07:00
committed by GitHub
parent 2400e20134
commit 87ecbc7e45
19 changed files with 737 additions and 97 deletions
+47 -13
View File
@@ -25,12 +25,14 @@ _"when this becomes true"_. A few types are restricted to one role (`config` is
trigger-only; the composites and `user` / `user_agent` are condition-only), as
noted at the top of each type below.
For the card-state types (`camera`, `view`, `fullscreen`, `expand`, `call`,
For the card-state types (`camera`, `view`, `fullscreen`, `expand`,
`display_mode`, `media_loaded`, `microphone`, `interaction`, `triggered`) a
trigger's value is **optional**: give it a value to fire only when the state
changes _to_ that value, or **omit it to fire on any change**. (The stock `state`
trigger behaves the same way when `from`/`to` are omitted). As a condition the
value keeps its usual per-type meaning, as described below.
value keeps its usual per-type meaning, as described below. `call` is the
exception: its trigger matches a change by where it started and ended, using
`from`/`to` like the stock `state` trigger.
```yaml
# A trigger initiates an automation; conditions are then checked.
@@ -94,25 +96,56 @@ conditions:
## `call`
Matches whether a [two-way audio](../usage/2-way-audio.md) call is in progress.
As a **condition**, true while the call state matches; as a **trigger**, fires
when it becomes a match (e.g. `call: true` fires when a call starts).
Matches the phase of a [two-way audio](../usage/2-way-audio.md) call. A call is
in one of three phases:
| Phase | Meaning |
| ---------- | ------------------------------------------------------------- |
| `idle` | No call is in progress. |
| `ringing` | An inbound call is ringing and has not been answered. |
| `answered` | A call is in progress. An outbound call starts in this phase. |
As a **condition**, true while the call is in a matching phase. As a **trigger**,
fires when the phase _changes_, optionally limited to changes that start at
`from` and end at `to`. This is what separates answering a call from rejecting
one: both leave `ringing`, but they arrive at different phases.
```yaml
# As a condition:
conditions:
- condition: call
call: true
call: [ringing, answered]
# As a trigger:
triggers:
# An inbound call was answered.
- trigger: call
call: true
from: ringing
to: answered
# A ringing call ended without being answered (rejected, timed out, or the
# user navigated away).
- trigger: call
from: ringing
to: idle
# An answered call was hung up.
- trigger: call
from: answered
to: idle
# A ringing call was either answered or rejected.
- trigger: call
from: ringing
```
| Parameter | Description |
| ----------------------- | ---------------------------------------------------------------------------------------------- |
| `condition` / `trigger` | Must be `call`. |
| `call` | If `true` or `false`, matches when a two-way audio call is or is not in progress respectively. |
| Parameter | Description |
| ----------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- |
| `condition` / `trigger` | Must be `call`. |
| `call` | Condition only. A phase, or a list of phases any of which match. |
| `from` | Trigger only. Restricts firing to changes that start at this phase, or any phase in a list. If omitted, the phase changed from may be anything. |
| `to` | Trigger only. Restricts firing to changes that end at this phase, or any phase in a list. If omitted, the phase changed to may be anything. |
?> An outbound call is answered as soon as it connects, so it moves from `idle`
straight to `answered` without ever ringing. A trigger with `to: answered` and no
`from` will therefore also fire when the user starts an outbound call. Add
`from: ringing` to match only inbound calls that were answered.
## `camera`
@@ -721,7 +754,7 @@ conditions:
- condition: view
views: [live]
- condition: call
call: true
call: [ringing, answered]
- condition: camera
cameras:
- camera.office
@@ -786,7 +819,8 @@ conditions:
```yaml
triggers:
- trigger: call
call: true
from: ringing
to: answered
- trigger: camera
cameras:
- camera.office
+79
View File
@@ -423,6 +423,85 @@ profiles:
- doorbell
```
### Alerting the rest of the house
A wall tablet only helps whoever is standing in front of it. This example uses
the [`call` trigger](configuration/conditions-triggers.md?id=call) to drive an
`input_boolean` that a normal Home Assistant automation can watch, so the rest of
the house "rings" too -- and, crucially, **stops** ringing the moment somebody deals
with the caller.
The card knows things Home Assistant cannot see on its own: whether anyone
actually picked the call up, and whether it was answered or dismissed. Each
phase change gets its own automation.
```yaml
type: custom:advanced-camera-card
cameras:
- camera_entity: camera.front_door
live_provider: go2rtc
go2rtc:
modes:
- webrtc
profiles:
- doorbell
automations:
# It started ringing: ring the rest of the house and light the porch so the
# visitor is actually visible on camera.
- triggers:
- trigger: call
to: ringing
actions:
- action: perform-action
perform_action: input_boolean.turn_on
target:
entity_id: input_boolean.doorbell_ringing
- action: perform-action
perform_action: light.turn_on
target:
entity_id: light.porch
# Somebody answered on the tablet: silence every other device immediately.
# `from: ringing` keeps this from firing when *you* start an outbound call.
- triggers:
- trigger: call
from: ringing
to: answered
actions:
- action: perform-action
perform_action: input_boolean.turn_off
target:
entity_id: input_boolean.doorbell_ringing
# Nobody picked up, or somebody rejected it: stop ringing, and announce that
# a visitor was missed.
- triggers:
- trigger: call
from: ringing
to: idle
actions:
- action: perform-action
perform_action: input_boolean.turn_off
target:
entity_id: input_boolean.doorbell_ringing
- action: perform-action
perform_action: notify.mobile_app_phone
data:
message: Somebody was at the front door and nobody answered.
# The conversation ended. Only fires for calls that were actually answered,
# so an unanswered ring never turns the porch light off early.
- triggers:
- trigger: call
from: answered
to: idle
actions:
- action: perform-action
perform_action: light.turn_off
target:
entity_id: light.porch
```
## Events from other cameras
`dependencies.cameras` allows events/recordings for other cameras to be shown