Merge pull request #286 from dermotduffy/on-release-action

Implement support for {start,end}_tap actions
This commit is contained in:
Dermot Duffy
2022-01-26 20:35:36 -08:00
committed by GitHub
6 changed files with 87 additions and 24 deletions
+18 -4
View File
@@ -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.
<details>
<summary>Expand: Adding a card-wide action</summary>
+5
View File
@@ -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) {
+15 -10
View File
@@ -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;
}
/**
+40 -4
View File
@@ -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;
}
/**
@@ -480,3 +490,29 @@ export function arrayMove(target: unknown[], from: number, to: number): void {
export function contentsChanged(n: unknown, o: unknown): boolean {
return !isEqual(n, o);
}
/**
* 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));
};
+3 -2
View File
@@ -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);
}
/**
+5 -3
View File
@@ -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<typeof actionBaseSchema>;
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(),
});