Support executing actions from the query string.
This commit is contained in:
@@ -882,6 +882,21 @@ If multiple cameras are configured in the card, use [overrides](#overrides) to c
|
|||||||
|
|
||||||
See [media layout examples](#media-layout-examples).
|
See [media layout examples](#media-layout-examples).
|
||||||
|
|
||||||
|
<a name="other-options"></a>
|
||||||
|
|
||||||
|
### Other Options
|
||||||
|
|
||||||
|
All listed configuration options are under the top level, e.g.:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
type: custom:frigate-card
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
| Option | Default | Overridable | Description |
|
||||||
|
| - | - | - | - |
|
||||||
|
| `card_id` | | :heavy_multiplication_x: | **Advanced users only**: An optional ID to uniquely identify this card. For use when actions are being sent to card(s) via the [query string](#query-string-actions).|
|
||||||
|
|
||||||
<a name="webrtc"></a>
|
<a name="webrtc"></a>
|
||||||
|
|
||||||
### Using AlexxIT's WebRTC Card
|
### Using AlexxIT's WebRTC Card
|
||||||
@@ -1100,7 +1115,9 @@ Parameters for the `custom:frigate-card-ptz` element:
|
|||||||
| `action` | Must be `custom:frigate-card-action`. |
|
| `action` | Must be `custom:frigate-card-action`. |
|
||||||
| `frigate_card_action` | Call a Frigate Card action. Acceptable values are `default`, `clip`, `clips`, `image`, `live`, `recording`, `recordings`, `snapshot`, `snapshots`, `download`, `timeline`, `camera_ui`, `fullscreen`, `camera_select`, `menu_toggle`, `media_player`, `live_substream_select`, `expand_toggle`.|
|
| `frigate_card_action` | Call a Frigate Card action. Acceptable values are `default`, `clip`, `clips`, `image`, `live`, `recording`, `recordings`, `snapshot`, `snapshots`, `download`, `timeline`, `camera_ui`, `fullscreen`, `camera_select`, `menu_toggle`, `media_player`, `live_substream_select`, `expand_toggle`.|
|
||||||
|
|
||||||
##### Command descriptions
|
<a name="custom-actions"></a>
|
||||||
|
|
||||||
|
##### Action descriptions
|
||||||
|
|
||||||
| Value | Description |
|
| Value | Description |
|
||||||
| - | - |
|
| - | - |
|
||||||
@@ -2441,6 +2458,16 @@ performance:
|
|||||||
```
|
```
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>Expand: Other options</summary>
|
||||||
|
|
||||||
|
Reference: [Other Options](#other-options).
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
card_id: main
|
||||||
|
```
|
||||||
|
</details>
|
||||||
|
|
||||||
### Basic cameras configuration
|
### Basic cameras configuration
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
@@ -3377,6 +3404,41 @@ mode: single
|
|||||||
```
|
```
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
|
<a name="query-string-examples"></a>
|
||||||
|
|
||||||
|
### Passing the card actions from the URL
|
||||||
|
|
||||||
|
The card can respond to actions in the query string (see [below](#query-string-actions)).
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>Expand: Selecting the kitchen camera and opening the expanded view</summary>
|
||||||
|
|
||||||
|
This example assumes the dashboard URL is `https://ha.mydomain.org/lovelace-test/0`.
|
||||||
|
|
||||||
|
```
|
||||||
|
https://ha.mydomain.org/lovelace-test/0?frigate-card-action/camera_select=kitchen&frigate-card-action/expand_toggle
|
||||||
|
```
|
||||||
|
</details>
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>Expand: Choosing the clips view on a named card</summary>
|
||||||
|
|
||||||
|
This example assumes the dashboard URL is `https://ha.mydomain.org/lovelace-test/0`.
|
||||||
|
|
||||||
|
It assumes that one card (of potentially multiple Frigate Cards on the dashboard) is configured with a `card_id` parameter:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
type: custom:frigate-card
|
||||||
|
card_id: main
|
||||||
|
cameras:
|
||||||
|
[...]
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
https://ha.mydomain.org/lovelace-test/0?frigate-card-action/main/clips
|
||||||
|
```
|
||||||
|
</details>
|
||||||
|
|
||||||
<a name="media-layout-examples"></a>
|
<a name="media-layout-examples"></a>
|
||||||
|
|
||||||
## Card Refreshes
|
## Card Refreshes
|
||||||
@@ -3437,6 +3499,58 @@ view:
|
|||||||
timeout_seconds: 30
|
timeout_seconds: 30
|
||||||
```
|
```
|
||||||
|
|
||||||
|
<a name="query-string-actions"></a>
|
||||||
|
|
||||||
|
### Passing the card actions from the URL
|
||||||
|
|
||||||
|
It is possible to pass the Frigate card one or more actions from the URL (e.g. select a particular camera, open the live view in expanded mode, etc).
|
||||||
|
|
||||||
|
To send an action to *all* Frigate cards on a dashboard:
|
||||||
|
|
||||||
|
```
|
||||||
|
[PATH_TO_YOUR_HA_DASHBOARD]?frigate-card-action/[ACTION]=[VALUE]
|
||||||
|
```
|
||||||
|
|
||||||
|
To send an action to a named Frigate card on the dashboard:
|
||||||
|
|
||||||
|
```
|
||||||
|
[PATH_TO_YOUR_HA_DASHBOARD]?frigate-card-action/[CARD_ID]/[ACTION]=[VALUE]
|
||||||
|
```
|
||||||
|
|
||||||
|
| Parameter | Description |
|
||||||
|
| - | - |
|
||||||
|
| `ACTION` | One of the supported Frigate Card custom actions (see below). |
|
||||||
|
| `CARD_ID` | When specified only cards that have a `card_id` parameter will act. |
|
||||||
|
| `VALUE` | An optional value to use with the `camera_select` and `live_substream_select` actions. |
|
||||||
|
|
||||||
|
#### Actions
|
||||||
|
|
||||||
|
| Action | Supported in query string | Explanation |
|
||||||
|
| - | - | - |
|
||||||
|
| `camera_select` | :white_check_mark: | |
|
||||||
|
| `camera_ui`| :white_check_mark: | |
|
||||||
|
| `clip` | :white_check_mark: | |
|
||||||
|
| `clips` | :white_check_mark: | |
|
||||||
|
| `default` | :white_check_mark: | |
|
||||||
|
| `download`| :heavy_multiplication_x: | Latest media information is not available on initial render. |
|
||||||
|
| `expand_toggle` | :white_check_mark: | |
|
||||||
|
| `fullscreen` | :heavy_multiplication_x: | Javascript does not support activating fullscreen without direct human interaction. Use `expand_toggle` as an alternative. |
|
||||||
|
| `image` | :white_check_mark: | |
|
||||||
|
| `live_substream_select` | :white_check_mark: | |
|
||||||
|
| `live` | :white_check_mark: | |
|
||||||
|
| `media_player`| :heavy_multiplication_x: | Please [request](https://github.com/dermotduffy/frigate-hass-card/issues) if you need this. |
|
||||||
|
| `menu_toggle` | :white_check_mark: | |
|
||||||
|
| `recording` | :white_check_mark: | |
|
||||||
|
| `recordings` | :white_check_mark: | |
|
||||||
|
| `snapshot` | :white_check_mark: | |
|
||||||
|
| `snapshots` | :white_check_mark: | |
|
||||||
|
|
||||||
|
See [custom actions](#custom-actions) for a description of what the actions do.
|
||||||
|
|
||||||
|
#### Examples
|
||||||
|
|
||||||
|
See [query string examples](#query-string-examples) for examples of usage.
|
||||||
|
|
||||||
### Casting the Card
|
### Casting the Card
|
||||||
|
|
||||||
This card can be (Chrome) casted to a device (such as a [Nest Hub](https://store.google.com/us/product/nest_hub_2nd_gen)) through the use of [Home Assistant Cast](https://cast.home-assistant.io/).
|
This card can be (Chrome) casted to a device (such as a [Nest Hub](https://store.google.com/us/product/nest_hub_2nd_gen)) through the use of [Home Assistant Cast](https://cast.home-assistant.io/).
|
||||||
|
|||||||
+24
-7
@@ -92,6 +92,7 @@ import merge from 'lodash-es/merge';
|
|||||||
import { FrigateCardInitializer } from './utils/initializer.js';
|
import { FrigateCardInitializer } from './utils/initializer.js';
|
||||||
import 'web-dialog';
|
import 'web-dialog';
|
||||||
import { downloadMedia } from './utils/download.js';
|
import { downloadMedia } from './utils/download.js';
|
||||||
|
import { getActionsFromQueryString } from './utils/querystring.js';
|
||||||
|
|
||||||
/** A note on media callbacks:
|
/** A note on media callbacks:
|
||||||
*
|
*
|
||||||
@@ -1398,15 +1399,26 @@ class FrigateCard extends LitElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
protected _cardActionEventHandler(ev: CustomEvent<ActionType>): void {
|
||||||
* Handle a request for a card action.
|
|
||||||
* @param ev The action requested.
|
|
||||||
*/
|
|
||||||
protected _cardActionHandler(ev: CustomEvent<ActionType>): void {
|
|
||||||
const frigateCardAction = convertActionToFrigateCardCustomAction(ev.detail);
|
const frigateCardAction = convertActionToFrigateCardCustomAction(ev.detail);
|
||||||
if (!this._view || !frigateCardAction) {
|
if (frigateCardAction) {
|
||||||
|
this._cardActionHandler(frigateCardAction);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected _cardActionHandler(frigateCardAction: FrigateCardCustomAction): void {
|
||||||
|
if (!this._view) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
frigateCardAction.card_id &&
|
||||||
|
this._getConfig().card_id !== frigateCardAction.card_id
|
||||||
|
) {
|
||||||
|
// Command not intended for this card (e.g. query string command).
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const action = frigateCardAction.frigate_card_action;
|
const action = frigateCardAction.frigate_card_action;
|
||||||
|
|
||||||
switch (action) {
|
switch (action) {
|
||||||
@@ -1985,7 +1997,7 @@ class FrigateCard extends LitElement {
|
|||||||
class="${classMap(cardClasses)}"
|
class="${classMap(cardClasses)}"
|
||||||
style="${styleMap(cardStyle)}"
|
style="${styleMap(cardStyle)}"
|
||||||
@action=${(ev: CustomEvent) => this._actionHandler(ev, actions)}
|
@action=${(ev: CustomEvent) => this._actionHandler(ev, actions)}
|
||||||
@ll-custom=${this._cardActionHandler.bind(this)}
|
@ll-custom=${this._cardActionEventHandler.bind(this)}
|
||||||
@frigate-card:message=${this._messageHandler.bind(this)}
|
@frigate-card:message=${this._messageHandler.bind(this)}
|
||||||
@frigate-card:view:change=${this._changeViewHandler.bind(this)}
|
@frigate-card:view:change=${this._changeViewHandler.bind(this)}
|
||||||
@frigate-card:view:change-context=${this._addViewContextHandler.bind(this)}
|
@frigate-card:view:change-context=${this._addViewContextHandler.bind(this)}
|
||||||
@@ -2116,6 +2128,11 @@ class FrigateCard extends LitElement {
|
|||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected firstUpdated(): void {
|
||||||
|
// Execute query string actions after first render is complete.
|
||||||
|
getActionsFromQueryString().forEach((action) => this._cardActionHandler(action));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return compiled CSS styles (thus safe to use with unsafeCSS).
|
* Return compiled CSS styles (thus safe to use with unsafeCSS).
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -52,9 +52,9 @@ export function getLanguage(hass?: HomeAssistant): string {
|
|||||||
export const loadLanguages = async (hass: HomeAssistant): Promise<void> => {
|
export const loadLanguages = async (hass: HomeAssistant): Promise<void> => {
|
||||||
const lang = getLanguage(hass);
|
const lang = getLanguage(hass);
|
||||||
if (lang === 'it') {
|
if (lang === 'it') {
|
||||||
languages['it'] = await import('./languages/it.json');
|
languages[lang] = await import('./languages/it.json');
|
||||||
} else if (lang === 'pt_BR') {
|
} else if (lang === 'pt_BR') {
|
||||||
languages['pt_BR'] = await import('./languages/pt-BR.json');
|
languages[lang] = await import('./languages/pt-BR.json');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (lang) {
|
if (lang) {
|
||||||
|
|||||||
@@ -205,6 +205,9 @@ const frigateCardCustomActionsBaseSchema = customActionSchema.extend({
|
|||||||
// Syntactic sugar to avoid 'fire-dom-event' as part of an external API.
|
// Syntactic sugar to avoid 'fire-dom-event' as part of an external API.
|
||||||
.transform((): 'fire-dom-event' => 'fire-dom-event')
|
.transform((): 'fire-dom-event' => 'fire-dom-event')
|
||||||
.or(z.literal('fire-dom-event')),
|
.or(z.literal('fire-dom-event')),
|
||||||
|
|
||||||
|
// Card this command is intended for.
|
||||||
|
card_id: z.string().optional(),
|
||||||
});
|
});
|
||||||
|
|
||||||
const FRIGATE_CARD_GENERAL_ACTIONS = [
|
const FRIGATE_CARD_GENERAL_ACTIONS = [
|
||||||
@@ -1316,6 +1319,9 @@ export const frigateCardConfigSchema = z.object({
|
|||||||
// Support for card_mod (https://github.com/thomasloven/lovelace-card-mod).
|
// Support for card_mod (https://github.com/thomasloven/lovelace-card-mod).
|
||||||
card_mod: z.unknown(),
|
card_mod: z.unknown(),
|
||||||
|
|
||||||
|
// Card ID (used for query string commands).
|
||||||
|
card_id: z.string().optional(),
|
||||||
|
|
||||||
// Stock lovelace card config.
|
// Stock lovelace card config.
|
||||||
type: z.string(),
|
type: z.string(),
|
||||||
test_gui: z.boolean().optional(),
|
test_gui: z.boolean().optional(),
|
||||||
|
|||||||
+5
-1
@@ -19,7 +19,7 @@ import {
|
|||||||
* @returns A FrigateCardCustomAction or null if it cannot be converted.
|
* @returns A FrigateCardCustomAction or null if it cannot be converted.
|
||||||
*/
|
*/
|
||||||
export function convertActionToFrigateCardCustomAction(
|
export function convertActionToFrigateCardCustomAction(
|
||||||
action: ActionType | null,
|
action: unknown,
|
||||||
): FrigateCardCustomAction | null {
|
): FrigateCardCustomAction | null {
|
||||||
if (!action) {
|
if (!action) {
|
||||||
return null;
|
return null;
|
||||||
@@ -38,6 +38,7 @@ export function convertActionToFrigateCardCustomAction(
|
|||||||
export function createFrigateCardCustomAction(
|
export function createFrigateCardCustomAction(
|
||||||
action: FrigateCardAction,
|
action: FrigateCardAction,
|
||||||
args?: {
|
args?: {
|
||||||
|
cardID?: string;
|
||||||
camera?: string;
|
camera?: string;
|
||||||
media_player?: string;
|
media_player?: string;
|
||||||
media_player_action?: 'play' | 'stop';
|
media_player_action?: 'play' | 'stop';
|
||||||
@@ -51,6 +52,7 @@ export function createFrigateCardCustomAction(
|
|||||||
action: 'fire-dom-event',
|
action: 'fire-dom-event',
|
||||||
frigate_card_action: action,
|
frigate_card_action: action,
|
||||||
camera: args.camera as string,
|
camera: args.camera as string,
|
||||||
|
...(args.cardID && { card_id: args.cardID})
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
if (action === 'media_player') {
|
if (action === 'media_player') {
|
||||||
@@ -62,11 +64,13 @@ export function createFrigateCardCustomAction(
|
|||||||
frigate_card_action: action,
|
frigate_card_action: action,
|
||||||
media_player: args.media_player,
|
media_player: args.media_player,
|
||||||
media_player_action: args.media_player_action,
|
media_player_action: args.media_player_action,
|
||||||
|
...(args.cardID && { card_id: args.cardID})
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
action: 'fire-dom-event',
|
action: 'fire-dom-event',
|
||||||
frigate_card_action: action,
|
frigate_card_action: action,
|
||||||
|
...(args?.cardID && { card_id: args.cardID})
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,57 @@
|
|||||||
|
import { FrigateCardCustomAction } from '../types';
|
||||||
|
import { createFrigateCardCustomAction } from './action.js';
|
||||||
|
|
||||||
|
export const getActionsFromQueryString = (): FrigateCardCustomAction[] => {
|
||||||
|
const params = new URLSearchParams(window.location.search);
|
||||||
|
const actions: FrigateCardCustomAction[] = [];
|
||||||
|
const actionRE = new RegExp(/^frigate-card-action(\/(?<cardID>\w+))?\/(?<action>\w+)/);
|
||||||
|
|
||||||
|
for (const [key, value] of params.entries()) {
|
||||||
|
const match = key.match(actionRE);
|
||||||
|
if (!match || !match.groups) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const cardID: string | undefined = match.groups['cardID'];
|
||||||
|
const action = match.groups['action'];
|
||||||
|
|
||||||
|
let customAction: FrigateCardCustomAction | null = null;
|
||||||
|
switch (action) {
|
||||||
|
case 'camera_select':
|
||||||
|
case 'live_substream_select':
|
||||||
|
if (value) {
|
||||||
|
customAction = createFrigateCardCustomAction(action, {
|
||||||
|
camera: value,
|
||||||
|
cardID: cardID,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'camera_ui':
|
||||||
|
case 'clip':
|
||||||
|
case 'clips':
|
||||||
|
case 'default':
|
||||||
|
case 'diagnostics':
|
||||||
|
case 'download':
|
||||||
|
case 'expand_toggle':
|
||||||
|
case 'image':
|
||||||
|
case 'live':
|
||||||
|
case 'menu_toggle':
|
||||||
|
case 'recording':
|
||||||
|
case 'recordings':
|
||||||
|
case 'snapshot':
|
||||||
|
case 'snapshots':
|
||||||
|
case 'timeline':
|
||||||
|
customAction = createFrigateCardCustomAction(action, {
|
||||||
|
cardID: cardID,
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
console.warn(
|
||||||
|
`Frigate card received unknown card action in query string: ${action}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (customAction) {
|
||||||
|
actions.push(customAction);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return actions;
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user