Skip to content

Commit b8f3869

Browse files
committed
Add pressed and released event to allow usage as momentary button
1 parent d447819 commit b8f3869

File tree

4 files changed

+102
-3
lines changed

4 files changed

+102
-3
lines changed

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ Lovelace Button card for your entities.
5858
## Features
5959

6060
- works with any toggleable entity
61-
- 6 available actions on **tap** and/or **hold** and/or **double click**: `none`, `toggle`, `more-info`, `navigate`, `url` and `call-service`
61+
- 6 available actions on **tap** and/or **hold** and/or **double click** and/or **press**and/or **release**: `none`, `toggle`, `more-info`, `navigate`, `url` and `call-service`
6262
- state display (optional)
6363
- custom color (optional), or based on light rgb value/temperature
6464
- custom state definition with customizable color, icon and style (optional)
@@ -98,6 +98,8 @@ Lovelace Button card for your entities.
9898
| `tap_action` | object | optional | See [Action](#Action) | Define the type of action on click, if undefined, toggle will be used. |
9999
| `hold_action` | object | optional | See [Action](#Action) | Define the type of action on hold, if undefined, nothing happens. |
100100
| `double_tap_action` | object | optional | See [Action](#Action) | Define the type of action on double click, if undefined, nothing happens. |
101+
| `press_action` | object | optional | See [Action](#Action) | Define the type of action on press (triggers the moment your finger presses the button), if undefined, nothing happens. |
102+
| `release_action` | object | optional | See [Action](#Action) | Define the type of action on releasing the button, if undefined, nothing happens. |
101103
| `name` | string | optional | `Air conditioner` | Define an optional text to show below the icon. Supports templates, see [templates](#javascript-templates) |
102104
| `state_display` | string | optional | `On` | Override the way the state is displayed. Supports templates, see [templates](#javascript-templates) |
103105
| `label` | string | optional | Any string that you want | Display a label below the card. See [Layouts](#layout) for more information. Supports templates, see [templates](#javascript-templates) |

src/action-handler.ts

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ import { directive, PropertyPart } from 'lit-html';
33
// tslint:disable-next-line
44
import { Ripple } from '@material/mwc-ripple';
55
import { myFireEvent } from './my-fire-event';
6+
import { ButtonCardConfig } from './types';
7+
import { HomeAssistant, ActionConfig, fireEvent, forwardHaptic, navigate, toggleEntity } from 'custom-card-helpers';
68

79
const isTouch = 'ontouchstart' in window || navigator.maxTouchPoints > 0 || navigator.msMaxTouchPoints > 0;
810

@@ -95,6 +97,7 @@ class ActionHandler extends HTMLElement implements ActionHandler {
9597
});
9698

9799
const start = (ev: Event): void => {
100+
myFireEvent(element, 'action', { action: 'press' });
98101
this.held = false;
99102
let x;
100103
let y;
@@ -129,6 +132,7 @@ class ActionHandler extends HTMLElement implements ActionHandler {
129132
const end = (ev: Event): void => {
130133
// Prevent mouse event if touch event
131134
ev.preventDefault();
135+
myFireEvent(element, 'action', { action: 'release' });
132136
if (['touchend', 'touchcancel'].includes(ev.type) && this.timer === undefined) {
133137
if (this.isRepeating && this.repeatTimeout) {
134138
clearInterval(this.repeatTimeout);
@@ -220,3 +224,66 @@ export const actionHandlerBind = (element: ActionHandlerElement, options: Action
220224
export const actionHandler = directive((options: ActionHandlerOptions = {}) => (part: PropertyPart): void => {
221225
actionHandlerBind(part.committer.element as ActionHandlerElement, options);
222226
});
227+
228+
export const executeAction = (
229+
node: HTMLElement,
230+
hass: HomeAssistant,
231+
config: ButtonCardConfig,
232+
action: string,
233+
): void => {
234+
let actionConfig: ActionConfig | undefined;
235+
actionConfig = config[action];
236+
237+
if (!actionConfig) {
238+
actionConfig = {
239+
action: 'more-info',
240+
};
241+
}
242+
243+
if (
244+
actionConfig.confirmation &&
245+
(!actionConfig.confirmation.exemptions ||
246+
!actionConfig.confirmation.exemptions.some(e => e.user === hass!.user!.id))
247+
) {
248+
forwardHaptic('warning');
249+
250+
if (!confirm(actionConfig.confirmation.text || `Are you sure you want to ${actionConfig.action}?`)) {
251+
return;
252+
}
253+
}
254+
255+
switch (actionConfig.action) {
256+
case 'more-info':
257+
if (config.entity) {
258+
fireEvent(node, 'hass-more-info', {
259+
entityId: config.entity,
260+
});
261+
}
262+
break;
263+
case 'navigate':
264+
if (actionConfig.navigation_path) {
265+
navigate(node, actionConfig.navigation_path);
266+
}
267+
break;
268+
case 'url':
269+
if (actionConfig.url_path) {
270+
window.open(actionConfig.url_path);
271+
}
272+
break;
273+
case 'toggle':
274+
if (config.entity) {
275+
toggleEntity(hass, config.entity!);
276+
forwardHaptic('success');
277+
}
278+
break;
279+
case 'call-service': {
280+
if (!actionConfig.service) {
281+
forwardHaptic('failure');
282+
return;
283+
}
284+
const [domain, service] = actionConfig.service.split('.', 2);
285+
hass.callService(domain, service, actionConfig.service_data);
286+
forwardHaptic('success');
287+
}
288+
}
289+
};

src/button-card.ts

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ import {
3939
ButtonCardEmbeddedCards,
4040
ButtonCardEmbeddedCardsConfig,
4141
} from './types';
42-
import { actionHandler } from './action-handler';
42+
import { actionHandler, executeAction } from './action-handler';
4343
import {
4444
computeDomain,
4545
computeEntity,
@@ -630,7 +630,15 @@ class ButtonCard extends LitElement {
630630
const tap_action = this._getTemplateOrValue(state, this._config!.tap_action!.action);
631631
const hold_action = this._getTemplateOrValue(state, this._config!.hold_action!.action);
632632
const double_tap_action = this._getTemplateOrValue(state, this._config!.double_tap_action!.action);
633-
if (tap_action != 'none' || hold_action != 'none' || double_tap_action != 'none') {
633+
const press_action = this._getTemplateOrValue(state, this._config!.press_action!.action);
634+
const release_action = this._getTemplateOrValue(state, this._config!.release_action!.action);
635+
if (
636+
tap_action != 'none' ||
637+
hold_action != 'none' ||
638+
double_tap_action != 'none' ||
639+
press_action != 'none' ||
640+
release_action != 'none'
641+
) {
634642
clickable = true;
635643
} else {
636644
clickable = false;
@@ -933,6 +941,8 @@ class ButtonCard extends LitElement {
933941
this._config = {
934942
hold_action: { action: 'none' },
935943
double_tap_action: { action: 'none' },
944+
press_action: { action: 'none' },
945+
release_action: { action: 'none' },
936946
layout: 'vertical',
937947
size: '40%',
938948
color_type: 'icon',
@@ -1049,6 +1059,12 @@ class ButtonCard extends LitElement {
10491059
private _handleAction(ev: any): void {
10501060
if (ev.detail && ev.detail.action) {
10511061
switch (ev.detail.action) {
1062+
case 'press':
1063+
this._handlePress(ev);
1064+
break;
1065+
case 'release':
1066+
this._handleRelease(ev);
1067+
break;
10521068
case 'tap':
10531069
this._handleTap(ev);
10541070
break;
@@ -1064,6 +1080,18 @@ class ButtonCard extends LitElement {
10641080
}
10651081
}
10661082

1083+
private _handlePress(ev): void {
1084+
const config = ev.target.config;
1085+
if (!config) return;
1086+
executeAction(this, this._hass!, this._evalActions(config, 'press_action'), 'press_action');
1087+
}
1088+
1089+
private _handleRelease(ev): void {
1090+
const config = ev.target.config;
1091+
if (!config) return;
1092+
executeAction(this, this._hass!, this._evalActions(config, 'release_action'), 'release_action');
1093+
}
1094+
10671095
private _handleTap(ev): void {
10681096
const config = ev.target.config;
10691097
if (!config) return;

src/types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ export interface ButtonCardConfig {
1515
tap_action?: ActionConfig;
1616
hold_action?: ActionConfig;
1717
double_tap_action?: ActionConfig;
18+
press_action?: ActionConfig;
19+
release_action?: ActionConfig;
1820
show_name?: boolean;
1921
show_state?: boolean;
2022
show_icon?: boolean;

0 commit comments

Comments
 (0)