fix: Release the microphone to the browser when a call ends (#2685)

The microphone is connected when a call needs it and released the moment
that
call ends, so the browser stops reporting it as in use at hangup rather
than
`disconnect_seconds` later.

Existing configurations are migrated automatically by the visual editor.

 - Closes: #2681 

BREAKING CHANGE: `live.microphone.disconnect_seconds` is removed. The
microphone is released when a call ends, so there is no idle countdown
to
configure. Use `live.microphone.always_connected` to hold it open
instead.

BREAKING CHANGE: The `microphone_connect` and `microphone_disconnect`
actions
are removed. The card owns the microphone lifecycle; `microphone_mute`
and
`microphone_unmute` remain.

BREAKING CHANGE: `call` is removed from `live.microphone.auto_mute`,
whose
default is now `[]`. The microphone is muted when a call ends regardless
of
this option.

BREAKING CHANGE: `microphone_unmute` has no effect outside a call.
Nothing
carries the audio at any other time, so the request is ignored rather
than
opening the microphone.
This commit is contained in:
Dermot Duffy
2026-08-14 16:17:31 -07:00
committed by GitHub
parent 65bc52d18e
commit 85d6811761
33 changed files with 1480 additions and 572 deletions
+7 -37
View File
@@ -289,24 +289,6 @@ action: custom:advanced-camera-card-action
advanced_camera_card_action: menu_toggle
```
## `microphone_connect`
Connect the microphone for [2-way audio](../../../usage/2-way-audio.md).
```yaml
action: custom:advanced-camera-card-action
advanced_camera_card_action: microphone_connect
```
## `microphone_disconnect`
Disconnect the microphone during [2-way audio](../../../usage/2-way-audio.md).
```yaml
action: custom:advanced-camera-card-action
advanced_camera_card_action: microphone_disconnect
```
## `microphone_mute`
Mute the microphone during [2-way audio](../../../usage/2-way-audio.md).
@@ -913,48 +895,36 @@ elements:
advanced_camera_card_action: menu_toggle
- type: custom:advanced-camera-card-menu-icon
icon: mdi:alpha-p-circle
title: Microphone connect
tap_action:
action: custom:advanced-camera-card-action
advanced_camera_card_action: microphone_connect
- type: custom:advanced-camera-card-menu-icon
icon: mdi:alpha-q-circle
title: Microphone disconnect
tap_action:
action: custom:advanced-camera-card-action
advanced_camera_card_action: microphone_disconnect
- type: custom:advanced-camera-card-menu-icon
icon: mdi:alpha-r-circle
title: Microphone mute
tap_action:
action: custom:advanced-camera-card-action
advanced_camera_card_action: microphone_mute
- type: custom:advanced-camera-card-menu-icon
icon: mdi:alpha-s-circle
icon: mdi:alpha-q-circle
title: Microphone unmute
tap_action:
action: custom:advanced-camera-card-action
advanced_camera_card_action: microphone_unmute
- type: custom:advanced-camera-card-menu-icon
icon: mdi:alpha-t-circle
icon: mdi:alpha-r-circle
title: Mute
tap_action:
action: custom:advanced-camera-card-action
advanced_camera_card_action: mute
- type: custom:advanced-camera-card-menu-icon
icon: mdi:alpha-u-circle
icon: mdi:alpha-s-circle
title: Pause
tap_action:
action: custom:advanced-camera-card-action
advanced_camera_card_action: pause
- type: custom:advanced-camera-card-menu-icon
icon: mdi:alpha-v-circle
icon: mdi:alpha-t-circle
title: Play
tap_action:
action: custom:advanced-camera-card-action
advanced_camera_card_action: play
- type: custom:advanced-camera-card-menu-icon
icon: mdi:alpha-x-circle
icon: mdi:alpha-u-circle
title: Real PTZ Preset
tap_action:
action: custom:advanced-camera-card-action
@@ -962,14 +932,14 @@ elements:
ptz_action: preset
ptz_preset: doorway
- type: custom:advanced-camera-card-menu-icon
icon: mdi:alpha-y-circle
icon: mdi:alpha-v-circle
title: Show PTZ Controls
tap_action:
action: custom:advanced-camera-card-action
advanced_camera_card_action: ptz_controls
enabled: true
- type: custom:advanced-camera-card-menu-icon
icon: mdi:alpha-z-circle
icon: mdi:alpha-x-circle
title: Go to precise digital location
tap_action:
action: custom:advanced-camera-card-action
+18 -18
View File
@@ -421,28 +421,28 @@ triggers:
muted: true
```
| Parameter | Description |
| ----------------------- | --------------------------------------------------------------------------------------------------------------------------------- |
| `condition` / `trigger` | Must be `microphone`. |
| `connected` | If `true` or `false`, matches when the card does or does not hold an open microphone stream from the browser. See the note below. |
| `muted` | If `true` or `false`, matches when the microphone is muted or unmuted respectively. |
| Parameter | Description |
| ----------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
| `condition` / `trigger` | Must be `microphone`. |
| `connected` | If `true` or `false`, matches when the card does or does not hold an open microphone stream from the browser. See the note below. |
| `muted` | If `true` or `false`, matches when the microphone is muted or unmuted respectively. A disconnected microphone counts as muted. See the note below. |
> [!NOTE]
> The `connected` parameter is about **browser microphone access**, not about
> calls. It is `true` while the card holds an open microphone stream from the
> browser -- the same state that lights your browser's "microphone in use"
> indicator (e.g. useful for showing privacy indicators).
> The `connected` parameter is about **browser microphone access**. It is `true`
> while the card holds an open microphone stream from the browser -- the same
> state that lights your browser's "microphone in use" indicator (e.g. useful for
> showing privacy indicators).
>
> A [two-way audio](../usage/2-way-audio.md) call opens the microphone, but so do
> several things that involve no call at all: setting
> [`live.microphone.always_connected`](live.md?id=microphone), or a
> [`microphone_connect`](actions/custom/README.md?id=microphone_connect) or
> [`microphone_unmute`](actions/custom/README.md?id=microphone_unmute) action. It
> closes again after
> [`live.microphone.disconnect_seconds`](live.md?id=microphone) of disuse, or on
> a `microphone_disconnect` action.
> The microphone is connected while a [two-way audio](../usage/2-way-audio.md)
> call is in progress, and closes when that call ends. It is also open for as
> long as the card runs when
> [`live.microphone.always_connected`](live.md?id=microphone) is set.
>
> To match a call in progress, use [`call`](#call) instead.
> `muted` is `true` whenever the microphone is disconnected, so outside a call
> it is always `true`. It is `false` only during a call with the microphone
> unmuted. `muted: false` is therefore the way to match "the user can be heard
> right now"; to match the absence of a call, use [`call`](#call) rather than
> `muted: true`.
## `not`
+7 -10
View File
@@ -222,13 +222,12 @@ live:
microphone:
```
| Option | Default | Description |
| ------------------------------------ | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `always_connected` | `false` | Whether or not to keep the microphone stream continually connected while the card is running, or only connect it when first needed (default) -- typically when a [two-way audio](../usage/2-way-audio.md) call is started. In the latter case there'll be a connection reset the first time the microphone connects -- using this option can avoid that reset. |
| `auto_mute` | `[call]` | A list of conditions in which the microphone is muted. `unselected` will automatically mute the microphone when a camera is unselected in the carousel or grid. `hidden` will automatically mute the microphone when the card becomes hidden (e.g. browser/tab change). `call` will automatically mute the microphone when an answered [two-way audio](../usage/2-way-audio.md) call ends. Use an empty list (`[]`) to never automatically mute the microphone via these conditions. |
| `auto_unmute` | `[]` | A list of conditions in which the microphone is unmuted. `selected` will automatically unmute the microphone when a camera is selected in the carousel or grid. `visible` will automatically unmute when the card becomes visible. `call` will automatically unmute the microphone when a [two-way audio](../usage/2-way-audio.md) call is started (or answered for inbound calls). By default this list is empty, so the microphone stays muted even after answering (push-to-talk) -- tap the microphone button in the call overlay to talk. The microphone is still connected when the call starts (just left muted), so the browser may prompt for microphone permission at that point. |
| `disconnect_seconds` | `90` | The number of seconds after microphone usage to disconnect the microphone from the stream. `0` implies never. Not relevant if `always_connected` is `true`. The countdown is suspended for the duration of a [two-way audio](../usage/2-way-audio.md) call, and restarts in full when the call ends. |
| `mute_after_microphone_mute_seconds` | `60` | The number of seconds after the microphone mutes to automatically mute the inbound audio when `live.auto_mute` includes `microphone`. |
| Option | Default | Description |
| ------------------------------------ | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `always_connected` | `false` | Whether or not to keep the microphone connected while the card is running. By default the microphone is connected when a [two-way audio](../usage/2-way-audio.md) call needs it and disconnected when that call ends. Setting this to `true` connects it at card load and never disconnects it, which avoids the connection setup on the first call at the cost of the browser reporting the microphone as in use for as long as the card is running. |
| `auto_mute` | `[]` | A list of conditions in which the microphone is muted. `unselected` will automatically mute the microphone when a camera is unselected in the carousel or grid. `hidden` will automatically mute the microphone when the card becomes hidden (e.g. browser/tab change). Use an empty list (`[]`, the default) to never automatically mute the microphone via these conditions. The microphone is always muted when a call ends. |
| `auto_unmute` | `[]` | A list of conditions in which the microphone is unmuted. `call` will automatically unmute the microphone when a [two-way audio](../usage/2-way-audio.md) call is started (or answered for inbound calls). `selected` will automatically unmute the microphone when a camera is selected in the carousel or grid. `visible` will automatically unmute when the card becomes visible. By default this list is empty, so the microphone stays muted even after answering (push-to-talk) -- tap the microphone button in the call overlay to talk. Unmuting only has an effect during a call: at any other time nothing can carry the audio, so the request is ignored. |
| `mute_after_microphone_mute_seconds` | `60` | The number of seconds after the microphone mutes to automatically mute the inbound audio when `live.auto_mute` includes `microphone`. |
See [Using 2-way audio](../usage/2-way-audio.md) for more information about the very particular requirements that must be followed for 2-way audio to work.
@@ -304,10 +303,8 @@ live:
24h: true
microphone:
always_connected: false
auto_mute:
- call
auto_mute: []
auto_unmute: []
disconnect_seconds: 90
mute_after_microphone_mute_seconds: 60
display:
mode: single
File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 27 KiB

After

Width:  |  Height:  |  Size: 27 KiB

+3 -3
View File
@@ -62,9 +62,9 @@ note over User, Card
end note
Card --> User : Inbound audio mutes (<color:#43a047>**""live.auto_mute""**</color>)
Card -> Mic : Microphone mutes (<color:#43a047>**""live.microphone.auto_mute""**</color>)
Card -> Mic : Microphone mutes and disconnects
note over Mic
Disconnects after <color:#43a047>**""live.microphone.disconnect_seconds""**</color>
unless <color:#43a047>**""live.microphone.always_connected""**</color>
The browser stops reporting the microphone as in use, unless
<color:#43a047>**""live.microphone.always_connected""**</color> holds it open
end note
@enduml
+10 -4
View File
@@ -69,16 +69,22 @@ enabled by default and appears in the `live` view whenever the selected camera
[`always_connected`](../configuration/live.md?id=microphone) microphone option
is set to `true`. On the first call there may be a brief `webrtc` connection
reset to include 2-way audio.
- The browser asks for microphone permission when a call needs it. How often it
asks depends on the browser: Chrome remembers the choice for the site, Safari
asks once per page load, and Firefox asks for every call unless _Remember this
decision_ is ticked. Set
[`always_connected`](../configuration/live.md?id=microphone) to `true` to be
asked once at card load instead.
- While a call is in progress the card locks disruptive actions (camera and
substream changes, casting, reload, etc.) so an accidental tap, swipe, or
button press doesn't cut the call off. Set
[`live.controls.call.lock`](../configuration/live.md?id=call) to `false` to
disable this.
- End the call with the overlay's end-call button. When the call ends the
microphone and inbound audio are muted again.
- The video automatically resets to remove the microphone after the number of
seconds specified by [`disconnect_seconds`](../configuration/live.md?id=microphone)
have elapsed since the call ended.
microphone and inbound audio are muted again, and the microphone is
disconnected. The exception is
[`always_connected`](../configuration/live.md?id=microphone), which holds the
microphone connected for as long as the card is running.
Calls can also be controlled programmatically with the
[`call_start`](../configuration/actions/custom/README.md?id=call_start),
+33 -33
View File
@@ -51,39 +51,39 @@ To send an action to a specific named Advanced Camera Card:
Only a subset of all [actions](../configuration/actions/README.md) are supported in URL form.
| Action | Supported in URL | Explanation |
| ------------------------------------------------------------------------------------- | ------------------------ | -------------------------------------------------------------------------------------------------------------------------------- |
| `camera_select` | :white_check_mark: | |
| `camera_ui` | :white_check_mark: | |
| `clip` | :white_check_mark: | |
| `clips` | :white_check_mark: | |
| `default` | :white_check_mark: | |
| `diagnostics` | :white_check_mark: | |
| `download` | :heavy_multiplication_x: | Latest media information is not available on initial render. |
| `expand` | :white_check_mark: | |
| `folder` | :white_check_mark: | |
| `folders` | :white_check_mark: | |
| `fullscreen` | :heavy_multiplication_x: | Javascript does not support activating fullscreen without direct human interaction. Use `expand` as an alternative. |
| `gallery` | :white_check_mark: | |
| `image` | :white_check_mark: | |
| `live` | :white_check_mark: | |
| `media` | :white_check_mark: | |
| `media_player` | :heavy_multiplication_x: | Please [request](https://github.com/dermotduffy/advanced-camera-card/issues) if you need this. |
| `menu_toggle` | :white_check_mark: | |
| `microphone_connect`, `microphone_disconnect`, `microphone_mute`, `microphone_unmute` | :heavy_multiplication_x: | |
| `mute`, `unmute` | :heavy_multiplication_x: | |
| `play`, `pause` | :heavy_multiplication_x: | |
| `ptz` | :heavy_multiplication_x: | Please [request](https://github.com/dermotduffy/advanced-camera-card/issues) if you need this. |
| `recording` | :white_check_mark: | |
| `recordings` | :white_check_mark: | |
| `review` | :white_check_mark: | |
| `reviews` | :white_check_mark: | |
| `screenshot` | :heavy_multiplication_x: | Latest media information is not available on initial render. |
| `ptz_controls` | :heavy_multiplication_x: | Please [request](https://github.com/dermotduffy/advanced-camera-card/issues) if you need this. |
| `snapshot` | :white_check_mark: | |
| `snapshots` | :white_check_mark: | |
| `substream_off` | :white_check_mark: | The `camera` field is not exposed via URL; the selected camera is used. Use the action config directly to target another camera. |
| `substream_on` | :white_check_mark: | Pass the substream camera ID as the URL value to engage it directly; omit the value to cycle. |
| Action | Supported in URL | Explanation |
| -------------------------------------- | ------------------------ | -------------------------------------------------------------------------------------------------------------------------------- |
| `camera_select` | :white_check_mark: | |
| `camera_ui` | :white_check_mark: | |
| `clip` | :white_check_mark: | |
| `clips` | :white_check_mark: | |
| `default` | :white_check_mark: | |
| `diagnostics` | :white_check_mark: | |
| `download` | :heavy_multiplication_x: | Latest media information is not available on initial render. |
| `expand` | :white_check_mark: | |
| `folder` | :white_check_mark: | |
| `folders` | :white_check_mark: | |
| `fullscreen` | :heavy_multiplication_x: | Javascript does not support activating fullscreen without direct human interaction. Use `expand` as an alternative. |
| `gallery` | :white_check_mark: | |
| `image` | :white_check_mark: | |
| `live` | :white_check_mark: | |
| `media` | :white_check_mark: | |
| `media_player` | :heavy_multiplication_x: | Please [request](https://github.com/dermotduffy/advanced-camera-card/issues) if you need this. |
| `menu_toggle` | :white_check_mark: | |
| `microphone_mute`, `microphone_unmute` | :heavy_multiplication_x: | |
| `mute`, `unmute` | :heavy_multiplication_x: | |
| `play`, `pause` | :heavy_multiplication_x: | |
| `ptz` | :heavy_multiplication_x: | Please [request](https://github.com/dermotduffy/advanced-camera-card/issues) if you need this. |
| `recording` | :white_check_mark: | |
| `recordings` | :white_check_mark: | |
| `review` | :white_check_mark: | |
| `reviews` | :white_check_mark: | |
| `screenshot` | :heavy_multiplication_x: | Latest media information is not available on initial render. |
| `ptz_controls` | :heavy_multiplication_x: | Please [request](https://github.com/dermotduffy/advanced-camera-card/issues) if you need this. |
| `snapshot` | :white_check_mark: | |
| `snapshots` | :white_check_mark: | |
| `substream_off` | :white_check_mark: | The `camera` field is not exposed via URL; the selected camera is used. Use the action config directly to target another camera. |
| `substream_on` | :white_check_mark: | Pass the substream camera ID as the URL value to engage it directly; omit the value to cycle. |
## Examples