Skip to content

Commit

Permalink
Add pressed and released event to allow usage as momentary button
Browse files Browse the repository at this point in the history
  • Loading branch information
indykoning committed Jul 15, 2020
1 parent d447819 commit c3ae0f3
Show file tree
Hide file tree
Showing 5 changed files with 141 additions and 2 deletions.
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ Lovelace Button card for your entities.
## Features

- works with any toggleable entity
- 6 available actions on **tap** and/or **hold** and/or **double click**: `none`, `toggle`, `more-info`, `navigate`, `url` and `call-service`
- 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`
- state display (optional)
- custom color (optional), or based on light rgb value/temperature
- custom state definition with customizable color, icon and style (optional)
Expand Down Expand Up @@ -98,6 +98,8 @@ Lovelace Button card for your entities.
| `tap_action` | object | optional | See [Action](#Action) | Define the type of action on click, if undefined, toggle will be used. |
| `hold_action` | object | optional | See [Action](#Action) | Define the type of action on hold, if undefined, nothing happens. |
| `double_tap_action` | object | optional | See [Action](#Action) | Define the type of action on double click, if undefined, nothing happens. |
| `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. |
| `release_action` | object | optional | See [Action](#Action) | Define the type of action on releasing the button, if undefined, nothing happens. |
| `name` | string | optional | `Air conditioner` | Define an optional text to show below the icon. Supports templates, see [templates](#javascript-templates) |
| `state_display` | string | optional | `On` | Override the way the state is displayed. Supports templates, see [templates](#javascript-templates) |
| `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) |
Expand Down
4 changes: 4 additions & 0 deletions src/action-handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import { directive, PropertyPart } from 'lit-html';
// tslint:disable-next-line
import { Ripple } from '@material/mwc-ripple';
import { myFireEvent } from './my-fire-event';
import { ButtonCardConfig } from './types';
import { HomeAssistant, ActionConfig, fireEvent, forwardHaptic, navigate, toggleEntity } from 'custom-card-helpers';

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

Expand Down Expand Up @@ -95,6 +97,7 @@ class ActionHandler extends HTMLElement implements ActionHandler {
});

const start = (ev: Event): void => {
myFireEvent(element, 'action', { action: 'press' });
this.held = false;
let x;
let y;
Expand Down Expand Up @@ -129,6 +132,7 @@ class ActionHandler extends HTMLElement implements ActionHandler {
const end = (ev: Event): void => {
// Prevent mouse event if touch event
ev.preventDefault();
myFireEvent(element, 'action', { action: 'release' });
if (['touchend', 'touchcancel'].includes(ev.type) && this.timer === undefined) {
if (this.isRepeating && this.repeatTimeout) {
clearInterval(this.repeatTimeout);
Expand Down
41 changes: 40 additions & 1 deletion src/button-card.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ import {
ButtonCardEmbeddedCardsConfig,
} from './types';
import { actionHandler } from './action-handler';
import { handleActionConfig } from './handle-action';
import {
computeDomain,
computeEntity,
Expand Down Expand Up @@ -630,7 +631,15 @@ class ButtonCard extends LitElement {
const tap_action = this._getTemplateOrValue(state, this._config!.tap_action!.action);
const hold_action = this._getTemplateOrValue(state, this._config!.hold_action!.action);
const double_tap_action = this._getTemplateOrValue(state, this._config!.double_tap_action!.action);
if (tap_action != 'none' || hold_action != 'none' || double_tap_action != 'none') {
const press_action = this._getTemplateOrValue(state, this._config!.press_action!.action);
const release_action = this._getTemplateOrValue(state, this._config!.release_action!.action);
if (
tap_action != 'none' ||
hold_action != 'none' ||
double_tap_action != 'none' ||
press_action != 'none' ||
release_action != 'none'
) {
clickable = true;
} else {
clickable = false;
Expand Down Expand Up @@ -933,6 +942,8 @@ class ButtonCard extends LitElement {
this._config = {
hold_action: { action: 'none' },
double_tap_action: { action: 'none' },
press_action: { action: 'none' },
release_action: { action: 'none' },
layout: 'vertical',
size: '40%',
color_type: 'icon',
Expand Down Expand Up @@ -1049,6 +1060,12 @@ class ButtonCard extends LitElement {
private _handleAction(ev: any): void {
if (ev.detail && ev.detail.action) {
switch (ev.detail.action) {
case 'press':
this._handlePress(ev);
break;
case 'release':
this._handleRelease(ev);
break;
case 'tap':
this._handleTap(ev);
break;
Expand All @@ -1064,6 +1081,28 @@ class ButtonCard extends LitElement {
}
}

private _handlePress(ev): void {
const config = ev.target.config;
if (!config) return;
handleActionConfig(
this,
this._hass!,
this._evalActions(config, 'press_action'),
this._evalActions(config, 'press_action').press_action,
);
}

private _handleRelease(ev): void {
const config = ev.target.config;
if (!config) return;
handleActionConfig(
this,
this._hass!,
this._evalActions(config, 'release_action'),
this._evalActions(config, 'release_action').release_action,
);
}

private _handleTap(ev): void {
const config = ev.target.config;
if (!config) return;
Expand Down
92 changes: 92 additions & 0 deletions src/handle-action.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import { HomeAssistant, ActionConfig, fireEvent, forwardHaptic, navigate, toggleEntity } from 'custom-card-helpers';

export const handleActionConfig = (
node: HTMLElement,
hass: HomeAssistant,
config: {
entity?: string;
camera_image?: string;
hold_action?: ActionConfig;
tap_action?: ActionConfig;
double_tap_action?: ActionConfig;
},
actionConfig: ActionConfig | undefined,
): void => {
if (!actionConfig) {
actionConfig = {
action: 'more-info',
};
}

if (
actionConfig.confirmation &&
(!actionConfig.confirmation.exemptions ||
!actionConfig.confirmation.exemptions.some(e => e.user === hass!.user!.id))
) {
forwardHaptic('warning');

if (!confirm(actionConfig.confirmation.text || `Are you sure you want to ${actionConfig.action}?`)) {
return;
}
}

switch (actionConfig.action) {
case 'more-info':
if (config.entity || config.camera_image) {
fireEvent(node, 'hass-more-info', {
entityId: config.entity ? config.entity : config.camera_image!,
});
}
break;
case 'navigate':
if (actionConfig.navigation_path) {
navigate(node, actionConfig.navigation_path);
}
break;
case 'url':
if (actionConfig.url_path) {
window.open(actionConfig.url_path);
}
break;
case 'toggle':
if (config.entity) {
toggleEntity(hass, config.entity!);
forwardHaptic('success');
}
break;
case 'call-service': {
if (!actionConfig.service) {
forwardHaptic('failure');
return;
}
const [domain, service] = actionConfig.service.split('.', 2);
hass.callService(domain, service, actionConfig.service_data);
forwardHaptic('success');
}
}
};

export const handleAction = (
node: HTMLElement,
hass: HomeAssistant,
config: {
entity?: string;
camera_image?: string;
hold_action?: ActionConfig;
tap_action?: ActionConfig;
double_tap_action?: ActionConfig;
},
action: string,
): void => {
let actionConfig: ActionConfig | undefined;

if (action === 'double_tap' && config.double_tap_action) {
actionConfig = config.double_tap_action;
} else if (action === 'hold' && config.hold_action) {
actionConfig = config.hold_action;
} else if (action === 'tap' && config.tap_action) {
actionConfig = config.tap_action;
}

handleActionConfig(node, hass, config, actionConfig);
};
2 changes: 2 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ export interface ButtonCardConfig {
tap_action?: ActionConfig;
hold_action?: ActionConfig;
double_tap_action?: ActionConfig;
press_action?: ActionConfig;
release_action?: ActionConfig;
show_name?: boolean;
show_state?: boolean;
show_icon?: boolean;
Expand Down

0 comments on commit c3ae0f3

Please sign in to comment.