♻️ change entity-button to button card (#4581)

* ♻️ change entity-button to button card

* maintain separate entity-button class
This commit is contained in:
Ian Richardson 2020-02-14 03:56:08 -06:00 committed by GitHub
parent b2243f480c
commit 3f7c29a6f6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 292 additions and 271 deletions

View File

@ -395,7 +395,7 @@ export const demoLovelaceTeachingbirds: DemoConfig["lovelace"] = () => ({
cards: [ cards: [
{ {
entity: "script.air_cleaner_quiet", entity: "script.air_cleaner_quiet",
type: "entity-button", type: "button",
name: "AC bed", name: "AC bed",
tap_action: { tap_action: {
action: "call-service", action: "call-service",
@ -408,7 +408,7 @@ export const demoLovelaceTeachingbirds: DemoConfig["lovelace"] = () => ({
}, },
{ {
entity: "script.air_cleaner_auto", entity: "script.air_cleaner_auto",
type: "entity-button", type: "button",
name: "AC bed", name: "AC bed",
tap_action: { tap_action: {
action: "call-service", action: "call-service",
@ -421,7 +421,7 @@ export const demoLovelaceTeachingbirds: DemoConfig["lovelace"] = () => ({
}, },
{ {
entity: "script.air_cleaner_turbo", entity: "script.air_cleaner_turbo",
type: "entity-button", type: "button",
name: "AC bed", name: "AC bed",
tap_action: { tap_action: {
action: "call-service", action: "call-service",
@ -434,7 +434,7 @@ export const demoLovelaceTeachingbirds: DemoConfig["lovelace"] = () => ({
}, },
{ {
entity: "script.ac_off", entity: "script.ac_off",
type: "entity-button", type: "button",
name: "AC", name: "AC",
tap_action: { tap_action: {
action: "call-service", action: "call-service",
@ -447,7 +447,7 @@ export const demoLovelaceTeachingbirds: DemoConfig["lovelace"] = () => ({
}, },
{ {
entity: "script.ac_on", entity: "script.ac_on",
type: "entity-button", type: "button",
name: "AC", name: "AC",
tap_action: { tap_action: {
action: "call-service", action: "call-service",
@ -658,7 +658,7 @@ export const demoLovelaceTeachingbirds: DemoConfig["lovelace"] = () => ({
action: "call-service", action: "call-service",
service: "script.goodnight", service: "script.goodnight",
}, },
type: "entity-button", type: "button",
icon: "mdi:weather-night", icon: "mdi:weather-night",
}, },
{ {
@ -670,7 +670,7 @@ export const demoLovelaceTeachingbirds: DemoConfig["lovelace"] = () => ({
}, },
service: "scene.turn_on", service: "scene.turn_on",
}, },
type: "entity-button", type: "button",
icon: "mdi:coffee-outline", icon: "mdi:coffee-outline",
}, },
{ {
@ -682,7 +682,7 @@ export const demoLovelaceTeachingbirds: DemoConfig["lovelace"] = () => ({
}, },
service: "scene.turn_on", service: "scene.turn_on",
}, },
type: "entity-button", type: "button",
icon: "mdi:television-classic", icon: "mdi:television-classic",
}, },
], ],
@ -743,7 +743,7 @@ export const demoLovelaceTeachingbirds: DemoConfig["lovelace"] = () => ({
}, },
service: "light.toggle", service: "light.toggle",
}, },
type: "entity-button", type: "button",
icon: "mdi:page-layout-footer", icon: "mdi:page-layout-footer",
}, },
{ {
@ -755,7 +755,7 @@ export const demoLovelaceTeachingbirds: DemoConfig["lovelace"] = () => ({
}, },
service: "light.toggle", service: "light.toggle",
}, },
type: "entity-button", type: "button",
icon: "mdi:page-layout-header", icon: "mdi:page-layout-header",
}, },
], ],

View File

@ -15,14 +15,14 @@ const CONFIGS = [
{ {
heading: "Basic example", heading: "Basic example",
config: ` config: `
- type: entity-button - type: button
entity: light.bed_light entity: light.bed_light
`, `,
}, },
{ {
heading: "With Name", heading: "With Name",
config: ` config: `
- type: entity-button - type: button
name: Bedroom name: Bedroom
entity: light.bed_light entity: light.bed_light
`, `,
@ -30,7 +30,7 @@ const CONFIGS = [
{ {
heading: "With Icon", heading: "With Icon",
config: ` config: `
- type: entity-button - type: button
entity: light.bed_light entity: light.bed_light
icon: mdi:hotel icon: mdi:hotel
`, `,
@ -38,7 +38,7 @@ const CONFIGS = [
{ {
heading: "Without State", heading: "Without State",
config: ` config: `
- type: entity-button - type: button
entity: light.bed_light entity: light.bed_light
show_state: false show_state: false
`, `,
@ -46,7 +46,7 @@ const CONFIGS = [
{ {
heading: "Custom Tap Action (toggle)", heading: "Custom Tap Action (toggle)",
config: ` config: `
- type: entity-button - type: button
entity: light.bed_light entity: light.bed_light
tap_action: tap_action:
action: toggle action: toggle
@ -55,7 +55,7 @@ const CONFIGS = [
{ {
heading: "Running Service", heading: "Running Service",
config: ` config: `
- type: entity-button - type: button
entity: light.bed_light entity: light.bed_light
service: light.toggle service: light.toggle
`, `,
@ -63,13 +63,13 @@ const CONFIGS = [
{ {
heading: "Invalid Entity", heading: "Invalid Entity",
config: ` config: `
- type: entity-button - type: button
entity: sensor.invalid_entity entity: sensor.invalid_entity
`, `,
}, },
]; ];
class DemoEntityButtonEntity extends PolymerElement { class DemoButtonEntity extends PolymerElement {
static get template() { static get template() {
return html` return html`
<demo-cards <demo-cards
@ -97,4 +97,4 @@ class DemoEntityButtonEntity extends PolymerElement {
} }
} }
customElements.define("demo-hui-entity-button-card", DemoEntityButtonEntity); customElements.define("demo-hui-button-card", DemoButtonEntity);

View File

@ -0,0 +1,256 @@
import {
html,
LitElement,
PropertyValues,
TemplateResult,
CSSResult,
css,
customElement,
property,
} from "lit-element";
import { HassEntity } from "home-assistant-js-websocket";
import { styleMap } from "lit-html/directives/style-map";
import { ifDefined } from "lit-html/directives/if-defined";
import "@material/mwc-ripple";
import "../../../components/ha-card";
import "../components/hui-warning";
import { isValidEntityId } from "../../../common/entity/valid_entity_id";
import { stateIcon } from "../../../common/entity/state_icon";
import { computeStateDomain } from "../../../common/entity/compute_state_domain";
import { computeStateName } from "../../../common/entity/compute_state_name";
import { applyThemesOnElement } from "../../../common/dom/apply_themes_on_element";
import { computeDomain } from "../../../common/entity/compute_domain";
import { HomeAssistant, LightEntity } from "../../../types";
import { LovelaceCard, LovelaceCardEditor } from "../types";
import { DOMAINS_TOGGLE } from "../../../common/const";
import { ButtonCardConfig } from "./types";
import { actionHandler } from "../common/directives/action-handler-directive";
import { hasAction } from "../common/has-action";
import { handleAction } from "../common/handle-action";
import { ActionHandlerEvent } from "../../../data/lovelace";
import { computeActiveState } from "../../../common/entity/compute_active_state";
import { iconColorCSS } from "../../../common/style/icon_color_css";
@customElement("hui-button-card")
export class HuiButtonCard extends LitElement implements LovelaceCard {
public static async getConfigElement(): Promise<LovelaceCardEditor> {
await import(
/* webpackChunkName: "hui-button-card-editor" */ "../editor/config-elements/hui-button-card-editor"
);
return document.createElement("hui-button-card-editor");
}
public static getStubConfig(): object {
return {
tap_action: { action: "toggle" },
hold_action: { action: "more-info" },
show_icon: true,
show_name: true,
};
}
@property() public hass?: HomeAssistant;
@property() private _config?: ButtonCardConfig;
public getCardSize(): number {
return 2;
}
public setConfig(config: ButtonCardConfig): void {
if (config.entity && !isValidEntityId(config.entity)) {
throw new Error("Invalid Entity");
}
this._config = {
theme: "default",
hold_action: { action: "more-info" },
double_tap_action: { action: "none" },
show_icon: true,
show_name: true,
...config,
};
if (config.entity && DOMAINS_TOGGLE.has(computeDomain(config.entity))) {
this._config = {
tap_action: {
action: "toggle",
},
...this._config,
};
} else {
this._config = {
tap_action: {
action: "more-info",
},
...this._config,
};
}
}
protected shouldUpdate(changedProps: PropertyValues): boolean {
if (changedProps.has("_config")) {
return true;
}
const oldHass = changedProps.get("hass") as HomeAssistant | undefined;
if (
!oldHass ||
oldHass.themes !== this.hass!.themes ||
oldHass.language !== this.hass!.language
) {
return true;
}
return (
Boolean(this._config!.entity) &&
oldHass.states[this._config!.entity!] !==
this.hass!.states[this._config!.entity!]
);
}
protected render(): TemplateResult {
if (!this._config || !this.hass) {
return html``;
}
const stateObj = this._config.entity
? this.hass.states[this._config.entity]
: undefined;
if (this._config.entity && !stateObj) {
return html`
<hui-warning
>${this.hass.localize(
"ui.panel.lovelace.warning.entity_not_found",
"entity",
this._config.entity
)}</hui-warning
>
`;
}
return html`
<ha-card
@action=${this._handleAction}
.actionHandler=${actionHandler({
hasHold: hasAction(this._config!.hold_action),
hasDoubleClick: hasAction(this._config!.double_tap_action),
})}
tabindex=${ifDefined(
hasAction(this._config.tap_action) ? "0" : undefined
)}
>
${this._config.show_icon
? html`
<ha-icon
data-domain=${ifDefined(
stateObj ? computeStateDomain(stateObj) : undefined
)}
data-state=${ifDefined(
stateObj ? computeActiveState(stateObj) : undefined
)}
.icon=${this._config.icon ||
(stateObj ? stateIcon(stateObj) : "")}
style=${styleMap({
filter: stateObj ? this._computeBrightness(stateObj) : "",
color: stateObj ? this._computeColor(stateObj) : "",
height: this._config.icon_height
? this._config.icon_height
: "auto",
})}
></ha-icon>
`
: ""}
${this._config.show_name
? html`
<span>
${this._config.name ||
(stateObj ? computeStateName(stateObj) : "")}
</span>
`
: ""}
<mwc-ripple></mwc-ripple>
</ha-card>
`;
}
protected updated(changedProps: PropertyValues): void {
super.updated(changedProps);
if (!this._config || !this.hass) {
return;
}
const oldHass = changedProps.get("hass") as HomeAssistant | undefined;
const oldConfig = changedProps.get("_config") as
| ButtonCardConfig
| undefined;
if (
!oldHass ||
!oldConfig ||
oldHass.themes !== this.hass.themes ||
oldConfig.theme !== this._config.theme
) {
applyThemesOnElement(this, this.hass.themes, this._config.theme);
}
}
static get styles(): CSSResult {
return css`
ha-card {
cursor: pointer;
display: flex;
flex-direction: column;
align-items: center;
text-align: center;
padding: 4% 0;
font-size: 1.2rem;
}
ha-card:focus {
outline: none;
background: var(--divider-color);
}
ha-icon {
width: 40%;
height: auto;
color: var(--paper-item-icon-color, #44739e);
}
${iconColorCSS}
`;
}
private _computeBrightness(stateObj: HassEntity | LightEntity): string {
if (!stateObj.attributes.brightness) {
return "";
}
const brightness = stateObj.attributes.brightness;
return `brightness(${(brightness + 245) / 5}%)`;
}
private _computeColor(stateObj: HassEntity | LightEntity): string {
if (!stateObj.attributes.hs_color) {
return "";
}
const [hue, sat] = stateObj.attributes.hs_color;
if (sat <= 10) {
return "";
}
return `hsl(${hue}, 100%, ${100 - sat / 2}%)`;
}
private _handleAction(ev: ActionHandlerEvent) {
handleAction(this, this.hass!, this._config!, ev.detail.action!);
}
}
declare global {
interface HTMLElementTagNameMap {
"hui-button-card": HuiButtonCard;
}
}

View File

@ -1,246 +1,9 @@
import { import { customElement } from "lit-element";
html,
LitElement,
PropertyValues,
TemplateResult,
CSSResult,
css,
customElement,
property,
} from "lit-element";
import { HassEntity } from "home-assistant-js-websocket";
import { styleMap } from "lit-html/directives/style-map";
import { ifDefined } from "lit-html/directives/if-defined";
import "@material/mwc-ripple";
import "../../../components/ha-card"; import { HuiButtonCard } from "./hui-button-card";
import "../components/hui-warning";
import { isValidEntityId } from "../../../common/entity/valid_entity_id";
import { stateIcon } from "../../../common/entity/state_icon";
import { computeStateDomain } from "../../../common/entity/compute_state_domain";
import { computeStateName } from "../../../common/entity/compute_state_name";
import { applyThemesOnElement } from "../../../common/dom/apply_themes_on_element";
import { computeDomain } from "../../../common/entity/compute_domain";
import { HomeAssistant, LightEntity } from "../../../types";
import { LovelaceCard, LovelaceCardEditor } from "../types";
import { DOMAINS_TOGGLE } from "../../../common/const";
import { EntityButtonCardConfig } from "./types";
import { actionHandler } from "../common/directives/action-handler-directive";
import { hasAction } from "../common/has-action";
import { handleAction } from "../common/handle-action";
import { ActionHandlerEvent } from "../../../data/lovelace";
import { computeActiveState } from "../../../common/entity/compute_active_state";
import { iconColorCSS } from "../../../common/style/icon_color_css";
@customElement("hui-entity-button-card") @customElement("hui-entity-button-card")
class HuiEntityButtonCard extends LitElement implements LovelaceCard { class HuiEntityButtonCard extends HuiButtonCard {}
public static async getConfigElement(): Promise<LovelaceCardEditor> {
await import(
/* webpackChunkName: "hui-entity-button-card-editor" */ "../editor/config-elements/hui-entity-button-card-editor"
);
return document.createElement("hui-entity-button-card-editor");
}
public static getStubConfig(): object {
return {
tap_action: { action: "toggle" },
hold_action: { action: "more-info" },
show_icon: true,
show_name: true,
};
}
@property() public hass?: HomeAssistant;
@property() private _config?: EntityButtonCardConfig;
public getCardSize(): number {
return 2;
}
public setConfig(config: EntityButtonCardConfig): void {
if (!isValidEntityId(config.entity)) {
throw new Error("Invalid Entity");
}
this._config = {
theme: "default",
hold_action: { action: "more-info" },
double_tap_action: { action: "none" },
show_icon: true,
show_name: true,
...config,
};
if (DOMAINS_TOGGLE.has(computeDomain(config.entity))) {
this._config = {
tap_action: {
action: "toggle",
},
...this._config,
};
} else {
this._config = {
tap_action: {
action: "more-info",
},
...this._config,
};
}
}
protected shouldUpdate(changedProps: PropertyValues): boolean {
if (changedProps.has("_config")) {
return true;
}
const oldHass = changedProps.get("hass") as HomeAssistant | undefined;
if (
!oldHass ||
oldHass.themes !== this.hass!.themes ||
oldHass.language !== this.hass!.language
) {
return true;
}
return (
oldHass.states[this._config!.entity] !==
this.hass!.states[this._config!.entity]
);
}
protected render(): TemplateResult {
if (!this._config || !this.hass) {
return html``;
}
const stateObj = this.hass.states[this._config.entity];
if (!stateObj) {
return html`
<hui-warning
>${this.hass.localize(
"ui.panel.lovelace.warning.entity_not_found",
"entity",
this._config.entity
)}</hui-warning
>
`;
}
return html`
<ha-card
@action=${this._handleAction}
.actionHandler=${actionHandler({
hasHold: hasAction(this._config!.hold_action),
hasDoubleClick: hasAction(this._config!.double_tap_action),
})}
tabindex=${ifDefined(
hasAction(this._config.tap_action) ||
this._config.tap_action === undefined
? "0"
: undefined
)}
>
${this._config.show_icon
? html`
<ha-icon
data-domain=${computeStateDomain(stateObj)}
data-state=${computeActiveState(stateObj)}
.icon=${this._config.icon || stateIcon(stateObj)}
style=${styleMap({
filter: this._computeBrightness(stateObj),
color: this._computeColor(stateObj),
height: this._config.icon_height
? this._config.icon_height
: "auto",
})}
></ha-icon>
`
: ""}
${this._config.show_name
? html`
<span>
${this._config.name || computeStateName(stateObj)}
</span>
`
: ""}
<mwc-ripple></mwc-ripple>
</ha-card>
`;
}
protected updated(changedProps: PropertyValues): void {
super.updated(changedProps);
if (!this._config || !this.hass) {
return;
}
const oldHass = changedProps.get("hass") as HomeAssistant | undefined;
const oldConfig = changedProps.get("_config") as
| EntityButtonCardConfig
| undefined;
if (
!oldHass ||
!oldConfig ||
oldHass.themes !== this.hass.themes ||
oldConfig.theme !== this._config.theme
) {
applyThemesOnElement(this, this.hass.themes, this._config.theme);
}
}
static get styles(): CSSResult {
return css`
ha-card {
cursor: pointer;
display: flex;
flex-direction: column;
align-items: center;
text-align: center;
padding: 4% 0;
font-size: 1.2rem;
}
ha-card:focus {
outline: none;
}
ha-icon {
width: 40%;
height: auto;
color: var(--paper-item-icon-color, #44739e);
}
${iconColorCSS}
`;
}
private _computeBrightness(stateObj: HassEntity | LightEntity): string {
if (!stateObj.attributes.brightness) {
return "";
}
const brightness = stateObj.attributes.brightness;
return `brightness(${(brightness + 245) / 5}%)`;
}
private _computeColor(stateObj: HassEntity | LightEntity): string {
if (!stateObj.attributes.hs_color) {
return "";
}
const [hue, sat] = stateObj.attributes.hs_color;
if (sat <= 10) {
return "";
}
return `hsl(${hue}, 100%, ${100 - sat / 2}%)`;
}
private _handleAction(ev: ActionHandlerEvent) {
handleAction(this, this.hass!, this._config!, ev.detail.action!);
}
}
declare global { declare global {
interface HTMLElementTagNameMap { interface HTMLElementTagNameMap {

View File

@ -47,8 +47,8 @@ export interface EntitiesCardConfig extends LovelaceCardConfig {
state_color?: boolean; state_color?: boolean;
} }
export interface EntityButtonCardConfig extends LovelaceCardConfig { export interface ButtonCardConfig extends LovelaceCardConfig {
entity: string; entity?: string;
name?: string; name?: string;
show_name?: boolean; show_name?: boolean;
icon?: string; icon?: string;

View File

@ -1,4 +1,5 @@
import "../cards/hui-entities-card"; import "../cards/hui-entities-card";
import "../cards/hui-button-card";
import "../cards/hui-entity-button-card"; import "../cards/hui-entity-button-card";
import "../cards/hui-glance-card"; import "../cards/hui-glance-card";
import "../cards/hui-history-graph-card"; import "../cards/hui-history-graph-card";
@ -14,6 +15,7 @@ import { createLovelaceElement } from "./create-element-base";
const ALWAYS_LOADED_TYPES = new Set([ const ALWAYS_LOADED_TYPES = new Set([
"entities", "entities",
"button",
"entity-button", "entity-button",
"error", "error",
"glance", "glance",

View File

@ -18,7 +18,7 @@ const cards: string[] = [
"alarm-panel", "alarm-panel",
"conditional", "conditional",
"entities", "entities",
"entity-button", "button",
"entity-filter", "entity-filter",
"gauge", "gauge",
"glance", "glance",

View File

@ -22,7 +22,7 @@ import { LovelaceCardEditor } from "../../types";
import { fireEvent } from "../../../../common/dom/fire_event"; import { fireEvent } from "../../../../common/dom/fire_event";
import { configElementStyle } from "./config-elements-style"; import { configElementStyle } from "./config-elements-style";
import { ActionConfig } from "../../../../data/lovelace"; import { ActionConfig } from "../../../../data/lovelace";
import { EntityButtonCardConfig } from "../../cards/types"; import { ButtonCardConfig } from "../../cards/types";
const cardConfigStruct = struct({ const cardConfigStruct = struct({
type: "string", type: "string",
@ -37,14 +37,14 @@ const cardConfigStruct = struct({
theme: "string?", theme: "string?",
}); });
@customElement("hui-entity-button-card-editor") @customElement("hui-button-card-editor")
export class HuiEntityButtonCardEditor extends LitElement export class HuiButtonCardEditor extends LitElement
implements LovelaceCardEditor { implements LovelaceCardEditor {
@property() public hass?: HomeAssistant; @property() public hass?: HomeAssistant;
@property() private _config?: EntityButtonCardConfig; @property() private _config?: ButtonCardConfig;
public setConfig(config: EntityButtonCardConfig): void { public setConfig(config: ButtonCardConfig): void {
config = cardConfigStruct(config); config = cardConfigStruct(config);
this._config = config; this._config = config;
} }
@ -108,7 +108,7 @@ export class HuiEntityButtonCardEditor extends LitElement
.label="${this.hass.localize( .label="${this.hass.localize(
"ui.panel.lovelace.editor.card.generic.entity" "ui.panel.lovelace.editor.card.generic.entity"
)} (${this.hass.localize( )} (${this.hass.localize(
"ui.panel.lovelace.editor.card.config.required" "ui.panel.lovelace.editor.card.config.optional"
)})" )})"
.hass="${this.hass}" .hass="${this.hass}"
.value="${this._entity}" .value="${this._entity}"
@ -250,6 +250,6 @@ export class HuiEntityButtonCardEditor extends LitElement
declare global { declare global {
interface HTMLElementTagNameMap { interface HTMLElementTagNameMap {
"hui-entity-button-card-editor": HuiEntityButtonCardEditor; "hui-button-card-editor": HuiButtonCardEditor;
} }
} }

View File

@ -1811,8 +1811,8 @@
"show_header_toggle": "Show Header Toggle?", "show_header_toggle": "Show Header Toggle?",
"toggle": "Toggle entities." "toggle": "Toggle entities."
}, },
"entity-button": { "button": {
"name": "Entity Button" "name": "Button"
}, },
"entity-filter": { "entity-filter": {
"name": "Entity Filter" "name": "Entity Filter"