diff --git a/README.md b/README.md
index ed316a12..b4095efd 100644
--- a/README.md
+++ b/README.md
@@ -882,6 +882,21 @@ If multiple cameras are configured in the card, use [overrides](#overrides) to c
See [media layout examples](#media-layout-examples).
+
+
+### 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).|
+
### Using AlexxIT's WebRTC Card
@@ -1100,7 +1115,9 @@ Parameters for the `custom:frigate-card-ptz` element:
| `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`.|
-##### Command descriptions
+
+
+##### Action descriptions
| Value | Description |
| - | - |
@@ -2441,6 +2458,16 @@ performance:
```
+
+ Expand: Other options
+
+Reference: [Other Options](#other-options).
+
+```yaml
+card_id: main
+```
+
+
### Basic cameras configuration
@@ -3377,6 +3404,41 @@ mode: single
```
+
+
+### Passing the card actions from the URL
+
+The card can respond to actions in the query string (see [below](#query-string-actions)).
+
+
+ Expand: Selecting the kitchen camera and opening the expanded view
+
+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
+```
+
+
+
+ Expand: Choosing the clips view on a named card
+
+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
+```
+
+
## Card Refreshes
@@ -3437,6 +3499,58 @@ view:
timeout_seconds: 30
```
+
+
+### 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
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/).
diff --git a/src/card.ts b/src/card.ts
index 3b971c30..0567edef 100644
--- a/src/card.ts
+++ b/src/card.ts
@@ -92,6 +92,7 @@ import merge from 'lodash-es/merge';
import { FrigateCardInitializer } from './utils/initializer.js';
import 'web-dialog';
import { downloadMedia } from './utils/download.js';
+import { getActionsFromQueryString } from './utils/querystring.js';
/** A note on media callbacks:
*
@@ -1398,15 +1399,26 @@ class FrigateCard extends LitElement {
}
}
- /**
- * Handle a request for a card action.
- * @param ev The action requested.
- */
- protected _cardActionHandler(ev: CustomEvent): void {
+ protected _cardActionEventHandler(ev: CustomEvent): void {
const frigateCardAction = convertActionToFrigateCardCustomAction(ev.detail);
- if (!this._view || !frigateCardAction) {
+ if (frigateCardAction) {
+ this._cardActionHandler(frigateCardAction);
+ }
+ }
+
+ protected _cardActionHandler(frigateCardAction: FrigateCardCustomAction): void {
+ if (!this._view) {
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;
switch (action) {
@@ -1985,7 +1997,7 @@ class FrigateCard extends LitElement {
class="${classMap(cardClasses)}"
style="${styleMap(cardStyle)}"
@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:view:change=${this._changeViewHandler.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).
*/
diff --git a/src/localize/localize.ts b/src/localize/localize.ts
index af74e995..c082917a 100644
--- a/src/localize/localize.ts
+++ b/src/localize/localize.ts
@@ -52,9 +52,9 @@ export function getLanguage(hass?: HomeAssistant): string {
export const loadLanguages = async (hass: HomeAssistant): Promise => {
const lang = getLanguage(hass);
if (lang === 'it') {
- languages['it'] = await import('./languages/it.json');
+ languages[lang] = await import('./languages/it.json');
} else if (lang === 'pt_BR') {
- languages['pt_BR'] = await import('./languages/pt-BR.json');
+ languages[lang] = await import('./languages/pt-BR.json');
}
if (lang) {
diff --git a/src/types.ts b/src/types.ts
index 6592ed4e..29ef0a04 100644
--- a/src/types.ts
+++ b/src/types.ts
@@ -205,6 +205,9 @@ const frigateCardCustomActionsBaseSchema = customActionSchema.extend({
// Syntactic sugar to avoid 'fire-dom-event' as part of an external API.
.transform((): 'fire-dom-event' => '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 = [
@@ -1316,6 +1319,9 @@ export const frigateCardConfigSchema = z.object({
// Support for card_mod (https://github.com/thomasloven/lovelace-card-mod).
card_mod: z.unknown(),
+ // Card ID (used for query string commands).
+ card_id: z.string().optional(),
+
// Stock lovelace card config.
type: z.string(),
test_gui: z.boolean().optional(),
diff --git a/src/utils/action.ts b/src/utils/action.ts
index e6449d45..e64c0c37 100644
--- a/src/utils/action.ts
+++ b/src/utils/action.ts
@@ -19,7 +19,7 @@ import {
* @returns A FrigateCardCustomAction or null if it cannot be converted.
*/
export function convertActionToFrigateCardCustomAction(
- action: ActionType | null,
+ action: unknown,
): FrigateCardCustomAction | null {
if (!action) {
return null;
@@ -38,6 +38,7 @@ export function convertActionToFrigateCardCustomAction(
export function createFrigateCardCustomAction(
action: FrigateCardAction,
args?: {
+ cardID?: string;
camera?: string;
media_player?: string;
media_player_action?: 'play' | 'stop';
@@ -51,6 +52,7 @@ export function createFrigateCardCustomAction(
action: 'fire-dom-event',
frigate_card_action: action,
camera: args.camera as string,
+ ...(args.cardID && { card_id: args.cardID})
};
}
if (action === 'media_player') {
@@ -62,11 +64,13 @@ export function createFrigateCardCustomAction(
frigate_card_action: action,
media_player: args.media_player,
media_player_action: args.media_player_action,
+ ...(args.cardID && { card_id: args.cardID})
};
}
return {
action: 'fire-dom-event',
frigate_card_action: action,
+ ...(args?.cardID && { card_id: args.cardID})
};
}
diff --git a/src/utils/querystring.ts b/src/utils/querystring.ts
new file mode 100644
index 00000000..ccf916e3
--- /dev/null
+++ b/src/utils/querystring.ts
@@ -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(\/(?\w+))?\/(?\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;
+};