diff --git a/README.md b/README.md index 665f26a1..12f63294 100644 --- a/README.md +++ b/README.md @@ -529,7 +529,7 @@ Parameters for the `custom:frigate-card-menu-submenu` element are identical to t | `state_color` | `true` | Whether or not the title and icon should be stylized based on state. | | `selected` | `false` | Whether or not to show this item as selected. | | `style` | | Position and style the element using CSS. | -| `tap_action`, `double_tap_action` or `hold_action` | | Standard [Home Assistant action configuration](https://www.home-assistant.io/lovelace/actions). | +| `tap_action`, `double_tap_action`, `hold_action`, `start_tap`, `end_tap` | | Standard [Home Assistant action configuration](https://www.home-assistant.io/lovelace/actions). | See the [Configuring a Submenu example](#configuring-a-submenu-example). @@ -587,8 +587,9 @@ time as the snapshot (if any). ## Card & View Actions Actions may be attached to the card itself, to trigger action when the card -experiences a `tap`, `double_tap` or `hold` event. These actions can be -specified both for the overall card and for individual groups of view. +experiences a `tap`, `double_tap`, `hold`, `start_tap` or `end_tap` event. These +actions can be specified both for the overall card and for individual groups of +view. | Configuration path | Views to which it refers | | - | - | @@ -614,6 +615,19 @@ _also_, which means that a card-wide `tap` action probably isn't that useful as it may be disorienting to the user and will trigger on all kinds of basic interaction on the card (e.g. tapping/clicking a menu button). +### Special Custom Action Types: `start_tap` and `end_tap` + +The card has partial support for two special action types `start_tap` and +`end_tap` which occur when a tap is started (e.g. mouse is pressed down / +touch begins), and ended (e.g. mouse released / touch ends) respectively. This +might be useful for PTZ cameras cameras to start/stop movement on touch. + +**Caveats**: This support is only partial. Stock [Home Assistant picture +elements](https://www.home-assistant.io/lovelace/picture-elements/) do not +support these actions when rendered onto the card, but Frigate card controls +(e.g. card/view actions as described above, menu icons and submenus) do support +them by default. Network latency may introduce unavoidable imprecision between +`end_tap` and action actually occurring. ## Menu Modes @@ -927,7 +941,7 @@ elements: ### Adding Card-wide Actions -You can add actions to the card to be trigger on `tap`, `double_tap` or `hold`. See [actions](#actions) above. +You can add actions to the card to be trigger on `tap`, `double_tap`, `hold`, `start_tap` or `end_tap`. See [actions](#actions) above.
Expand: Adding a card-wide action diff --git a/src/action-handler-directive.ts b/src/action-handler-directive.ts index cc281235..714bd0bc 100644 --- a/src/action-handler-directive.ts +++ b/src/action-handler-directive.ts @@ -117,6 +117,8 @@ class ActionHandler extends HTMLElement implements ActionHandler { this.held = true; }, this.holdTime); } + + fireEvent(element, 'action', { action: 'start_tap' }); }; const end = (ev: Event): void => { @@ -133,6 +135,9 @@ class ActionHandler extends HTMLElement implements ActionHandler { this.stopAnimation(); this.timer = undefined; } + + fireEvent(element, 'action', { action: 'end_tap' }); + if (options?.hasHold && this.held) { fireEvent(element, 'action', { action: 'hold' }); } else if (options?.hasDoubleClick) { diff --git a/src/card.ts b/src/card.ts index fdef5b30..8c3eacf4 100644 --- a/src/card.ts +++ b/src/card.ts @@ -14,7 +14,6 @@ import { HomeAssistant, LovelaceCardEditor, getLovelace, - handleAction, hasAction, } from 'custom-card-helpers'; import screenfull from 'screenfull'; @@ -46,6 +45,7 @@ import { contentsChanged, convertActionToFrigateCardCustomAction, createFrigateCardCustomAction, + frigateCardHandleAction, getActionConfigGivenAction, getCameraIcon, getCameraTitle, @@ -102,8 +102,8 @@ import { * * The card supports actions being configured in a number of places (e.g. tap on an * element, double_tap on a menu item, hold on the live view). These actions are - * handled by handleAction() from custom-card-helpers. For Frigate-card specific - * actions, handleAction() call will result in an ll-custom DOM event being + * handled frigateCardHandleAction(). For Frigate-card specific actions, + * frigateCardHandleAction() call will result in an ll-custom DOM event being * fired, which needs to be caught at the card level to handle. */ @@ -880,6 +880,8 @@ export class FrigateCard extends LitElement { hold_action?: ActionType; tap_action?: ActionType; double_tap_action?: ActionType; + start_tap_action?: ActionType; + end_tap_action?: ActionType; }, ): void { const interaction = ev.detail.action; @@ -888,12 +890,17 @@ export class FrigateCard extends LitElement { config && node && interaction && - // Don't call handleAction() unless there is explicitly an action defined - // (as it uses a default that is unhelpful for views that have default - // tap/click actions). + // Don't call frigateCardHandleAction() unless there is explicitly an + // action defined (as it uses a default that is unhelpful for views that + // have default tap/click actions). getActionConfigGivenAction(interaction, config) ) { - handleAction(node, this._hass as HomeAssistant, config, ev.detail.action); + frigateCardHandleAction( + node, + this._hass as HomeAssistant, + config, + ev.detail.action, + ); } // Set the 'screensaver' timer. @@ -950,9 +957,7 @@ export class FrigateCard extends LitElement { * @returns `true` if it's allowed, `false` otherwise. */ protected _isAutomatedViewUpdateAllowed(): boolean { - return ( - this._getConfig().view.update_force || !this._interactionTimerID - ); + return this._getConfig().view.update_force || !this._interactionTimerID; } /** diff --git a/src/common.ts b/src/common.ts index b589cac9..01eb1b76 100644 --- a/src/common.ts +++ b/src/common.ts @@ -1,5 +1,9 @@ import { HassEntity, MessageBase } from 'home-assistant-js-websocket'; -import { HomeAssistant, stateIcon } from 'custom-card-helpers'; +import { + HomeAssistant, + handleActionConfig, + stateIcon, +} from 'custom-card-helpers'; import { StyleInfo } from 'lit/directives/style-map'; import { ZodSchema, z } from 'zod'; import { isEqual } from 'lodash-es'; @@ -302,10 +306,12 @@ export function getActionConfigGivenAction( hold_action?: ActionType; tap_action?: ActionType; double_tap_action?: ActionType; + start_tap_action?: ActionType; + end_tap_action?: ActionType; }, -): ActionType | null { +): ActionType | undefined { if (!interaction || !config) { - return null; + return undefined; } if (interaction == 'tap' && config.tap_action) { return config.tap_action; @@ -313,8 +319,12 @@ export function getActionConfigGivenAction( return config.hold_action; } else if (interaction == 'double_tap' && config.double_tap_action) { return config.double_tap_action; + } else if (interaction == 'end_tap' && config.end_tap_action) { + return config.end_tap_action; + } else if (interaction == 'start_tap' && config.start_tap_action) { + return config.start_tap_action; } - return null; + return undefined; } /** @@ -479,4 +489,30 @@ export function arrayMove(target: unknown[], from: number, to: number): void { */ export function contentsChanged(n: unknown, o: unknown): boolean { return !isEqual(n, o); -} \ No newline at end of file +} + +/** + * Frigate card custom version of handleAction + * (https://github.com/custom-cards/custom-card-helpers/blob/master/src/handle-action.ts) + * that handles the custom action events the card supports. + * @param node The node that fired the event. + * @param hass The Home Assistant object. + * @param config The multi-action configuration. + * @param action The action string (e.g. 'hold') + */ +export const frigateCardHandleAction = ( + node: HTMLElement, + hass: HomeAssistant, + config: { + entity?: string; + camera_image?: string; + hold_action?: ActionType; + tap_action?: ActionType; + double_tap_action?: ActionType; + start_tap_action?: ActionType; + end_tap_action?: ActionType; + }, + action: string, +): void => { + handleActionConfig(node, hass, config, getActionConfigGivenAction(action, config)); +}; diff --git a/src/components/menu.ts b/src/components/menu.ts index 36046859..3a877128 100644 --- a/src/components/menu.ts +++ b/src/components/menu.ts @@ -1,4 +1,4 @@ -import { HASSDomEvent, HomeAssistant, handleAction, hasAction } from 'custom-card-helpers'; +import { HASSDomEvent, HomeAssistant, hasAction } from 'custom-card-helpers'; import { CSSResultGroup, LitElement, TemplateResult, html, unsafeCSS } from 'lit'; import { customElement, property, state } from 'lit/decorators.js'; import { classMap } from 'lit/directives/class-map.js'; @@ -18,6 +18,7 @@ import type { } from '../types.js'; import { convertActionToFrigateCardCustomAction, + frigateCardHandleAction, getActionConfigGivenAction, refreshDynamicStateParameters, } from '../common.js'; @@ -103,7 +104,7 @@ export class FrigateCardMenu extends LitElement { // Collapse menu after the user clicks on something. this.expanded = false; - handleAction(this, this.hass as HomeAssistant, config, interaction); + frigateCardHandleAction(this, this.hass as HomeAssistant, config, interaction); } /** diff --git a/src/types.ts b/src/types.ts index c8865038..d2fe26d4 100644 --- a/src/types.ts +++ b/src/types.ts @@ -162,14 +162,16 @@ const actionBaseSchema = z tap_action: actionSchema.optional(), hold_action: actionSchema.optional(), double_tap_action: actionSchema.optional(), + start_tap_action: actionSchema.optional(), + end_tap_action: actionSchema.optional(), }) + // Passthrough to allow (at least) entity/camera_image to go through. This + // card doesn't need these attributes, but handleAction() in + // custom_card_helpers may depending on how the action is configured. .passthrough(); export type Actions = z.infer; const actionsSchema = z.object({ - // Passthrough to allow (at least) entity/camera_image to go through. This - // card doesn't need these attributes, but handleAction() in - // custom_card_helpers may depending on how the action is configured. actions: actionBaseSchema.optional(), });