Update Hold/Tap Actions to Objects (#2182)

* Update Action to Object for entity-button

* Update Actions to Object for Glance Card

* Update to use HandleClick function

* Add navigation

* Updating to use navigation path from Action Config

* Type handle events in glance

* Update Service checks

* Update Picture elements

* oops

* Adding nav back until we convert picture

* Extend typing
This commit is contained in:
Zack Arnett 2018-12-05 08:27:22 -05:00 committed by Paulus Schoutsen
parent 0f895fd3a1
commit de3a467697
10 changed files with 122 additions and 101 deletions

View File

@ -21,6 +21,36 @@ export interface LovelaceCardConfig {
[key: string]: any; [key: string]: any;
} }
export interface ToggleActionConfig {
action: "toggle";
}
export interface CallServiceActionConfig {
action: "call-service";
service: string;
service_data?: { [key: string]: any };
}
export interface NavigateActionConfig {
action: "navigate";
navigation_path: string;
}
export interface MoreInfoActionConfig {
action: "more-info";
}
export interface NoActionConfig {
action: "none";
}
export type ActionConfig =
| ToggleActionConfig
| CallServiceActionConfig
| NavigateActionConfig
| MoreInfoActionConfig
| NoActionConfig;
export const fetchConfig = (hass: HomeAssistant): Promise<LovelaceConfig> => export const fetchConfig = (hass: HomeAssistant): Promise<LovelaceConfig> =>
hass.callWS({ hass.callWS({
type: "lovelace/config", type: "lovelace/config",

View File

@ -15,23 +15,20 @@ import stateIcon from "../../../common/entity/state_icon";
import computeStateDomain from "../../../common/entity/compute_state_domain"; import computeStateDomain from "../../../common/entity/compute_state_domain";
import computeStateName from "../../../common/entity/compute_state_name"; import computeStateName from "../../../common/entity/compute_state_name";
import applyThemesOnElement from "../../../common/dom/apply_themes_on_element"; import applyThemesOnElement from "../../../common/dom/apply_themes_on_element";
import { toggleEntity } from "../common/entity/toggle-entity";
import { HomeAssistant, LightEntity } from "../../../types"; import { HomeAssistant, LightEntity } from "../../../types";
import { hassLocalizeLitMixin } from "../../../mixins/lit-localize-mixin"; import { hassLocalizeLitMixin } from "../../../mixins/lit-localize-mixin";
import { LovelaceCard } from "../types"; import { LovelaceCard } from "../types";
import { LovelaceCardConfig } from "../../../data/lovelace"; import { LovelaceCardConfig, ActionConfig } from "../../../data/lovelace";
import { longPress } from "../common/directives/long-press-directive"; import { longPress } from "../common/directives/long-press-directive";
import { fireEvent } from "../../../common/dom/fire_event"; import { handleClick } from "../common/handle-click";
interface Config extends LovelaceCardConfig { interface Config extends LovelaceCardConfig {
entity: string; entity: string;
name?: string; name?: string;
icon?: string; icon?: string;
theme?: string; theme?: string;
tap_action?: "toggle" | "call-service" | "more-info"; tap_action?: ActionConfig;
hold_action?: "toggle" | "call-service" | "more-info"; hold_action?: ActionConfig;
service?: string;
service_data?: object;
} }
class HuiEntityButtonCard extends hassLocalizeLitMixin(LitElement) class HuiEntityButtonCard extends hassLocalizeLitMixin(LitElement)
@ -82,8 +79,8 @@ class HuiEntityButtonCard extends hassLocalizeLitMixin(LitElement)
return html` return html`
${this.renderStyle()} ${this.renderStyle()}
<ha-card <ha-card
@ha-click="${() => this.handleClick(false)}" @ha-click="${this._handleTap}"
@ha-hold="${() => this.handleClick(true)}" @ha-hold="${this._handleHold}"
.longPress="${longPress()}" .longPress="${longPress()}"
> >
${ ${
@ -187,34 +184,12 @@ class HuiEntityButtonCard extends hassLocalizeLitMixin(LitElement)
return `hsl(${hue}, 100%, ${100 - sat / 2}%)`; return `hsl(${hue}, 100%, ${100 - sat / 2}%)`;
} }
private handleClick(hold: boolean): void { private _handleTap() {
const config = this._config; handleClick(this, this.hass!, this._config!, false);
if (!config) {
return;
}
const stateObj = this.hass!.states[config.entity];
if (!stateObj) {
return;
}
const entityId = stateObj.entity_id;
const action = hold ? config.hold_action : config.tap_action || "more-info";
switch (action) {
case "toggle":
toggleEntity(this.hass!, entityId);
break;
case "call-service":
if (!config.service) {
return;
}
const [domain, service] = config.service.split(".", 2);
const serviceData = { entity_id: entityId, ...config.service_data };
this.hass!.callService(domain, service, serviceData);
break;
case "more-info":
fireEvent(this, "hass-more-info", { entityId });
break;
default:
} }
private _handleHold() {
handleClick(this, this.hass!, this._config!, true);
} }
} }

View File

@ -7,14 +7,12 @@ import {
import { TemplateResult } from "lit-html"; import { TemplateResult } from "lit-html";
import { classMap } from "lit-html/directives/classMap"; import { classMap } from "lit-html/directives/classMap";
import { fireEvent } from "../../../common/dom/fire_event";
import { hassLocalizeLitMixin } from "../../../mixins/lit-localize-mixin"; import { hassLocalizeLitMixin } from "../../../mixins/lit-localize-mixin";
import { HomeAssistant } from "../../../types"; import { HomeAssistant } from "../../../types";
import { LovelaceCard, LovelaceCardEditor } from "../types"; import { LovelaceCard, LovelaceCardEditor } from "../types";
import { LovelaceCardConfig } from "../../../data/lovelace"; import { LovelaceCardConfig, ActionConfig } from "../../../data/lovelace";
import { longPress } from "../common/directives/long-press-directive"; import { longPress } from "../common/directives/long-press-directive";
import { EntityConfig } from "../entity-rows/types"; import { EntityConfig } from "../entity-rows/types";
import { toggleEntity } from "../common/entity/toggle-entity";
import { processConfigEntities } from "../common/process-config-entities"; import { processConfigEntities } from "../common/process-config-entities";
import computeStateDisplay from "../../../common/entity/compute_state_display"; import computeStateDisplay from "../../../common/entity/compute_state_display";
@ -24,12 +22,11 @@ import applyThemesOnElement from "../../../common/dom/apply_themes_on_element";
import "../../../components/entity/state-badge"; import "../../../components/entity/state-badge";
import "../../../components/ha-card"; import "../../../components/ha-card";
import "../../../components/ha-icon"; import "../../../components/ha-icon";
import { handleClick } from "../common/handle-click";
export interface ConfigEntity extends EntityConfig { export interface ConfigEntity extends EntityConfig {
tap_action?: "toggle" | "call-service" | "more-info"; tap_action?: ActionConfig;
hold_action?: "toggle" | "call-service" | "more-info"; hold_action?: ActionConfig;
service?: string;
service_data?: object;
} }
export interface Config extends LovelaceCardConfig { export interface Config extends LovelaceCardConfig {
@ -75,9 +72,12 @@ export class HuiGlanceCard extends hassLocalizeLitMixin(LitElement)
for (const entity of entities) { for (const entity of entities) {
if ( if (
(entity.tap_action === "call-service" || (entity.tap_action &&
entity.hold_action === "call-service") && entity.tap_action.action === "call-service" &&
!entity.service !entity.tap_action.service) ||
(entity.hold_action &&
entity.hold_action.action === "call-service" &&
!entity.hold_action.service)
) { ) {
throw new Error( throw new Error(
'Missing required property "service" when tap_action or hold_action is call-service' 'Missing required property "service" when tap_action or hold_action is call-service'
@ -203,8 +203,8 @@ export class HuiGlanceCard extends hassLocalizeLitMixin(LitElement)
<div <div
class="entity" class="entity"
.entityConf="${entityConf}" .entityConf="${entityConf}"
@ha-click="${(ev) => this.handleClick(ev, false)}" @ha-click="${this._handleTap}"
@ha-hold="${(ev) => this.handleClick(ev, true)}" @ha-hold="${this._handleHold}"
.longPress="${longPress()}" .longPress="${longPress()}"
> >
${ ${
@ -243,24 +243,14 @@ export class HuiGlanceCard extends hassLocalizeLitMixin(LitElement)
`; `;
} }
private handleClick(ev: MouseEvent, hold: boolean): void { private _handleTap(ev: MouseEvent) {
const config = (ev.currentTarget as any).entityConf as ConfigEntity; const config = (ev.currentTarget as any).entityConf as ConfigEntity;
const entityId = config.entity; handleClick(this, this.hass!, config, false);
const action = hold ? config.hold_action : config.tap_action || "more-info";
switch (action) {
case "toggle":
toggleEntity(this.hass!, entityId);
break;
case "call-service":
const [domain, service] = config.service!.split(".", 2);
const serviceData = { entity_id: entityId, ...config.service_data };
this.hass!.callService(domain, service, serviceData);
break;
case "more-info":
fireEvent(this, "hass-more-info", { entityId });
break;
default:
} }
private _handleHold(ev: MouseEvent) {
const config = (ev.currentTarget as any).entityConf as ConfigEntity;
handleClick(this, this.hass!, config, true);
} }
} }

View File

@ -12,7 +12,7 @@ import computeStateName from "../../../common/entity/compute_state_name";
import { longPress } from "../common/directives/long-press-directive"; import { longPress } from "../common/directives/long-press-directive";
import { hassLocalizeLitMixin } from "../../../mixins/lit-localize-mixin"; import { hassLocalizeLitMixin } from "../../../mixins/lit-localize-mixin";
import { HomeAssistant } from "../../../types"; import { HomeAssistant } from "../../../types";
import { LovelaceCardConfig } from "../../../data/lovelace"; import { LovelaceCardConfig, ActionConfig } from "../../../data/lovelace";
import { LovelaceCard } from "../types"; import { LovelaceCard } from "../types";
import { handleClick } from "../common/handle-click"; import { handleClick } from "../common/handle-click";
import { UNAVAILABLE } from "../../../data/entity"; import { UNAVAILABLE } from "../../../data/entity";
@ -25,10 +25,8 @@ interface Config extends LovelaceCardConfig {
camera_image?: string; camera_image?: string;
state_image?: {}; state_image?: {};
aspect_ratio?: string; aspect_ratio?: string;
tap_action?: "toggle" | "call-service" | "more-info" | "navigate"; tap_action?: ActionConfig;
hold_action?: "toggle" | "call-service" | "more-info" | "navigate"; hold_action?: ActionConfig;
service?: string;
service_data?: object;
show_name?: boolean; show_name?: boolean;
show_state?: boolean; show_state?: boolean;
} }
@ -109,7 +107,7 @@ class HuiPictureEntityCard extends hassLocalizeLitMixin(LitElement)
}" }"
.entity="${this._config.entity}" .entity="${this._config.entity}"
.aspectRatio="${this._config.aspect_ratio}" .aspectRatio="${this._config.aspect_ratio}"
@ha-click="${this._handleClick}" @ha-click="${this._handleTap}"
@ha-hold="${this._handleHold}" @ha-hold="${this._handleHold}"
.longPress="${longPress()}" .longPress="${longPress()}"
class="${ class="${
@ -157,7 +155,7 @@ class HuiPictureEntityCard extends hassLocalizeLitMixin(LitElement)
`; `;
} }
private _handleClick() { private _handleTap() {
handleClick(this, this.hass!, this._config!, false); handleClick(this, this.hass!, this._config!, false);
} }

View File

@ -20,7 +20,7 @@ export const computeTooltip = (
: config.entity; : config.entity;
} }
switch (config.tap_action) { switch (config.tap_action && config.tap_action.action) {
case "navigate": case "navigate":
tooltip = `Navigate to ${config.navigation_path}`; tooltip = `Navigate to ${config.navigation_path}`;
break; break;

View File

@ -1,47 +1,55 @@
import { HomeAssistant } from "../../../types"; import { HomeAssistant } from "../../../types";
import { LovelaceElementConfig } from "../elements/types";
import { fireEvent } from "../../../common/dom/fire_event"; import { fireEvent } from "../../../common/dom/fire_event";
import { navigate } from "../../../common/navigate"; import { navigate } from "../../../common/navigate";
import { toggleEntity } from "../../../../src/panels/lovelace/common/entity/toggle-entity"; import { toggleEntity } from "../../../../src/panels/lovelace/common/entity/toggle-entity";
import { LovelaceCardConfig } from "../../../data/lovelace"; import { ActionConfig } from "../../../data/lovelace";
export const handleClick = ( export const handleClick = (
node: HTMLElement, node: HTMLElement,
hass: HomeAssistant, hass: HomeAssistant,
config: LovelaceElementConfig | LovelaceCardConfig, config: {
entity?: string;
hold_action?: ActionConfig;
tap_action?: ActionConfig;
},
hold: boolean hold: boolean
): void => { ): void => {
let action = config.tap_action || "more-info"; let actionConfig: ActionConfig | undefined;
if (hold && config.hold_action) { if (hold && config.hold_action) {
action = config.hold_action; actionConfig = config.hold_action;
} else if (!hold && config.tap_action) {
actionConfig = config.tap_action;
} }
if (action === "none") { if (!actionConfig) {
return; actionConfig = {
action: "more-info",
};
} }
switch (action) { switch (actionConfig.action) {
case "more-info": case "more-info":
if (config.entity) { if (config.entity) {
fireEvent(node, "hass-more-info", { entityId: config.entity }); fireEvent(node, "hass-more-info", { entityId: config.entity });
} }
break; break;
case "navigate": case "navigate":
navigate(node, config.navigation_path ? config.navigation_path : ""); if (actionConfig.navigation_path) {
navigate(node, actionConfig.navigation_path);
}
break; break;
case "toggle": case "toggle":
if (config.entity) {
toggleEntity(hass, config.entity!); toggleEntity(hass, config.entity!);
}
break; break;
case "call-service": { case "call-service": {
if (config.service) { if (!actionConfig.service) {
const [domain, service] = config.service.split(".", 2); return;
const serviceData = {
entity_id: config.entity,
...config.service_data,
};
hass.callService(domain, service, serviceData);
} }
const [domain, service] = actionConfig.service.split(".", 2);
hass.callService(domain, service, actionConfig.service_data);
} }
} }
}; };

View File

@ -41,13 +41,21 @@ export class HuiIconElement extends hassLocalizeLitMixin(LitElement)
<ha-icon <ha-icon
.icon="${this._config.icon}" .icon="${this._config.icon}"
.title="${computeTooltip(this.hass!, this._config)}" .title="${computeTooltip(this.hass!, this._config)}"
@ha-click="${() => handleClick(this, this.hass!, this._config!, false)}" @ha-click="${this._handleTap}"
@ha-hold="${() => handleClick(this, this.hass!, this._config!, true)}" @ha-hold="${this._handleHold}"
.longPress="${longPress()}" .longPress="${longPress()}"
></ha-icon> ></ha-icon>
`; `;
} }
private _handleTap() {
handleClick(this, this.hass!, this._config!, false);
}
private _handleHold() {
handleClick(this, this.hass!, this._config!, true);
}
private renderStyle(): TemplateResult { private renderStyle(): TemplateResult {
return html` return html`
<style> <style>

View File

@ -33,7 +33,10 @@ export class HuiImageElement extends hassLocalizeLitMixin(LitElement)
throw Error("Error in element configuration"); throw Error("Error in element configuration");
} }
this.classList.toggle("clickable", config.tap_action !== "none"); this.classList.toggle(
"clickable",
config.tap_action && config.tap_action.action !== "none"
);
this._config = config; this._config = config;
} }
@ -54,7 +57,7 @@ export class HuiImageElement extends hassLocalizeLitMixin(LitElement)
.stateFilter="${this._config.state_filter}" .stateFilter="${this._config.state_filter}"
.title="${computeTooltip(this.hass!, this._config)}" .title="${computeTooltip(this.hass!, this._config)}"
.aspectRatio="${this._config.aspect_ratio}" .aspectRatio="${this._config.aspect_ratio}"
@ha-click="${this._handleClick}" @ha-click="${this._handleTap}"
@ha-hold="${this._handleHold}" @ha-hold="${this._handleHold}"
.longPress="${longPress()}" .longPress="${longPress()}"
></hui-image> ></hui-image>
@ -76,7 +79,7 @@ export class HuiImageElement extends hassLocalizeLitMixin(LitElement)
`; `;
} }
private _handleClick() { private _handleTap() {
handleClick(this, this.hass!, this._config!, false); handleClick(this, this.hass!, this._config!, false);
} }

View File

@ -43,8 +43,8 @@ class HuiStateLabelElement extends hassLocalizeLitMixin(LitElement)
${this.renderStyle()} ${this.renderStyle()}
<div <div
.title="${computeTooltip(this.hass!, this._config)}" .title="${computeTooltip(this.hass!, this._config)}"
@ha-click="${() => handleClick(this, this.hass!, this._config!, false)}" @ha-click="${this._handleTap}"
@ha-hold="${() => handleClick(this, this.hass!, this._config!, true)}" @ha-hold="${this._handleHold}"
.longPress="${longPress()}" .longPress="${longPress()}"
> >
${this._config.prefix}${ ${this._config.prefix}${
@ -56,6 +56,14 @@ class HuiStateLabelElement extends hassLocalizeLitMixin(LitElement)
`; `;
} }
private _handleTap() {
handleClick(this, this.hass!, this._config!, false);
}
private _handleHold() {
handleClick(this, this.hass!, this._config!, true);
}
private renderStyle(): TemplateResult { private renderStyle(): TemplateResult {
return html` return html`
<style> <style>

View File

@ -1,14 +1,15 @@
import { HomeAssistant } from "../../../types"; import { HomeAssistant } from "../../../types";
import { ActionConfig } from "../../../data/lovelace";
export interface LovelaceElementConfig { export interface LovelaceElementConfig {
type: string; type: string;
style: object; style: object;
entity?: string; entity?: string;
hold_action?: string; hold_action?: ActionConfig;
navigation_path?: string;
service?: string; service?: string;
service_data?: object; service_data?: object;
tap_action?: string; navigation_path?: string;
tap_action?: ActionConfig;
title?: string; title?: string;
} }