feat: Add PIP (Picture-in-Picture) support (#2391)

- Closes: #1657
This commit is contained in:
Dermot Duffy
2026-03-05 20:19:20 -08:00
committed by GitHub
parent ecc8ecfd7e
commit c398562d40
32 changed files with 735 additions and 14 deletions
@@ -350,6 +350,20 @@ action: custom:advanced-camera-card-action
advanced_camera_card_action: pause
```
## `pip`
Toggle Picture-in-Picture mode. Floats the video element as a native browser overlay. Only supported by elements based on videos (e.g. notably not the `image`, or `jsmpeg` [live providers](../../cameras/live-provider.md)).
```yaml
action: custom:advanced-camera-card-action
advanced_camera_card_action: pip
```
| Parameter | Description |
| ----------------------------- | --------------------------------------------- |
| `action` | Must be `custom:advanced-camera-card-action`. |
| `advanced_camera_card_action` | Must be `pip`. |
## `play`
Play the selected media.
@@ -702,6 +716,12 @@ elements:
tap_action:
action: custom:advanced-camera-card-action
advanced_camera_card_action: fullscreen
- type: custom:advanced-camera-card-menu-icon
icon: mdi:picture-in-picture-bottom-right
title: PIP
tap_action:
action: custom:advanced-camera-card-action
advanced_camera_card_action: pip
- type: custom:advanced-camera-card-menu-icon
icon: mdi:alpha-l-circle
title: Show image view
+14 -8
View File
@@ -44,6 +44,7 @@ menu:
| `media_player` | The `media_player` menu button: sends the visible media to a remote media player. Supports Frigate clips, snapshots and live camera (only for cameras that specify a `camera_entity` and only using the default HA stream (equivalent to the `ha` live provider)). `jsmpeg` or `webrtc-card` are not supported, although live can still be played as long as `camera_entity` is specified. In the player list, a `tap` will send the media to the player, a `hold` will stop the media on the player. |
| `microphone` | The `microphone` button allows usage of 2-way audio in certain configurations. See [Using 2-way audio](../usage/2-way-audio.md). |
| `mute` | The `mute` button: toggles the mute state of the selected media. |
| `pip` | The `pip` menu button: enter Picture-in-Picture mode. Floats the video element as a native browser overlay. |
| `play` | The `play` button: toggles the play/pause state of the selected media. |
| `ptz_controls` | The `ptz_controls` button shows or hides the PTZ controls. |
| `ptz_home` | The `ptz_home` button allows easily returning the camera to default home position. |
@@ -56,14 +57,14 @@ menu:
### Options for each button
| Option | Default | Description |
| ------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `alignment` | `matching` | Whether this menu item should have an alignment that is `matching` the menu alignment or `opposing` the menu alignment. Can be used to create two separate groups of buttons on the menu. The `priority` option orders buttons within a given `alignment`. |
| `enabled` | `true` for `camera_ui`, `cameras`, `display_mode`, `download`, `folders`, `fullscreen`, `gallery`, `info`, `iris`, `live`, `media_player`, `set_review`, `substreams` and `timeline`. `false` for `clips`, `expand`, `image`, `microphone`, `mute`, `play`, `ptz_controls`, `ptz_home`, `recordings`, `reviews`, `screenshot` and `snapshots`. | Whether or not to show the button. |
| `icon` | | An icon to overriding the default for that button, e.g. `mdi:camera-front`. See also [custom icons](../usage/custom-icons.md). |
| `permanent` | `false` | If `false` the menu item is hidden when the menu has the `hidden` style and the menu is closed, otherwise it is shown (and sorted to the front). |
| `priority` | `50` | The menu item priority. Higher priority items are ordered closer to the start of the menu alignment (i.e. a button with priority `70` will order further to the left than a button with priority `60`). Priority applies separately to `matching` and `opposing` groups (see `alignment` above). Minimum `0`, maximum `100`. |
| `state_color` | `true` | Whether to colorize the button based on the state of a related entity (where applicable). |
| Option | Default | Description |
| ------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `alignment` | `matching` | Whether this menu item should have an alignment that is `matching` the menu alignment or `opposing` the menu alignment. Can be used to create two separate groups of buttons on the menu. The `priority` option orders buttons within a given `alignment`. |
| `enabled` | `true` for `camera_ui`, `cameras`, `display_mode`, `download`, `folders`, `fullscreen`, `gallery`, `info`, `iris`, `live`, `media_player`, `set_review`, `substreams` and `timeline`. `false` for `clips`, `expand`, `image`, `microphone`, `mute`, `pip`, `play`, `ptz_controls`, `ptz_home`, `recordings`, `reviews`, `screenshot` and `snapshots`. | Whether or not to show the button. |
| `icon` | | An icon to overriding the default for that button, e.g. `mdi:camera-front`. See also [custom icons](../usage/custom-icons.md). |
| `permanent` | `false` | If `false` the menu item is hidden when the menu has the `hidden` style and the menu is closed, otherwise it is shown (and sorted to the front). |
| `priority` | `50` | The menu item priority. Higher priority items are ordered closer to the start of the menu alignment (i.e. a button with priority `70` will order further to the left than a button with priority `60`). Priority applies separately to `matching` and `opposing` groups (see `alignment` above). Minimum `0`, maximum `100`. |
| `state_color` | `true` | Whether to colorize the button based on the state of a related entity (where applicable). |
### Additional options: `microphone`
@@ -180,6 +181,11 @@ menu:
enabled: false
alignment: matching
icon: mdi:volume-off
pip:
priority: 50
enabled: false
alignment: matching
icon: mdi:picture-in-picture-bottom-right
play:
priority: 50
enabled: false
+23
View File
@@ -257,6 +257,29 @@ that is currently loaded supports 2-way audio. See [Using 2-way
audio](usage/2-way-audio.md) for more information about the requirements that
must be followed.
### Picture-in-Picture only shows video but not other card controls
Picture-in-Picture (PIP) uses the browser's [native video PIP
API](https://caniuse.com/picture-in-picture) which floats the raw video element
into a small window. This means:
- **No card UI in the PIP window.** Only the video itself is shown — no menu,
status bar, timeline, or other card elements. The card remains fully
functional on the dashboard behind it.
- **Limited browser support.** Not all browsers support the PIP API. See
[Can I use: Picture-in-Picture](https://caniuse.com/picture-in-picture) for
current browser compatibility.
- **Non-video live providers are not supported.** Providers such as `image` or
`jsmpeg` do not use a native `<video>` element so PIP is unavailable for
these.
A more fully featured PIP mode (showing the entire card in a floating window)
was explored using the experimental [Document Picture-in-Picture
API](https://caniuse.com/mdn-api_documentpictureinpicture), however it proved
unworkable: Home Assistant state updates cannot reach a card in a separate
document, and browser-managed styles (`adoptedStyleSheets`) are cleared when
elements move between documents — resulting in an unstyled, non-updating card.
### New version not working in Chrome
When upgrading the card it's recommended to reset the frontend cache. Sometimes