Linting...
This commit is contained in:
parent
60792cd382
commit
e61d4187f2
|
@ -31,6 +31,7 @@
|
|||
"editor.formatOnPaste": false,
|
||||
"editor.formatOnSave": true,
|
||||
"editor.formatOnType": true,
|
||||
"files.trimTrailingWhitespace": true
|
||||
"files.trimTrailingWhitespace": true,
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,9 +2,7 @@ import typescript from 'rollup-plugin-typescript2';
|
|||
import commonjs from '@rollup/plugin-commonjs';
|
||||
import nodeResolve from '@rollup/plugin-node-resolve';
|
||||
import babel from 'rollup-plugin-babel';
|
||||
import {
|
||||
terser
|
||||
} from 'rollup-plugin-terser';
|
||||
import { terser } from 'rollup-plugin-terser';
|
||||
import serve from 'rollup-plugin-serve';
|
||||
import json from '@rollup/plugin-json';
|
||||
|
||||
|
@ -21,25 +19,30 @@ const serveopts = {
|
|||
};
|
||||
|
||||
const plugins = [
|
||||
nodeResolve({}),
|
||||
commonjs(),
|
||||
typescript(),
|
||||
json(),
|
||||
babel({
|
||||
exclude: 'node_modules/**',
|
||||
}),
|
||||
dev && serve(serveopts),
|
||||
!dev && terser({
|
||||
nodeResolve({}),
|
||||
commonjs(),
|
||||
typescript(),
|
||||
json(),
|
||||
babel({
|
||||
exclude: 'node_modules/**',
|
||||
}),
|
||||
dev && serve(serveopts),
|
||||
!dev &&
|
||||
terser({
|
||||
mangle: {
|
||||
safari10: true,
|
||||
}), ];
|
||||
|
||||
export default [{
|
||||
input: 'src/button-card.ts',
|
||||
output: {
|
||||
dir: './dist',
|
||||
format: 'es',
|
||||
sourcemap: dev ? true : false,
|
||||
},
|
||||
plugins: [...plugins],
|
||||
}, ];
|
||||
}),
|
||||
];
|
||||
|
||||
export default [
|
||||
{
|
||||
input: 'src/button-card.ts',
|
||||
output: {
|
||||
dir: './dist',
|
||||
format: 'es',
|
||||
sourcemap: dev ? true : false,
|
||||
},
|
||||
plugins: [...plugins],
|
||||
},
|
||||
];
|
||||
|
|
|
@ -4,9 +4,7 @@ import { directive, PropertyPart } from 'lit-html';
|
|||
import { Ripple } from '@material/mwc-ripple';
|
||||
import { myFireEvent } from './my-fire-event';
|
||||
|
||||
const isTouch = 'ontouchstart' in window ||
|
||||
navigator.maxTouchPoints > 0 ||
|
||||
navigator.msMaxTouchPoints > 0;
|
||||
const isTouch = 'ontouchstart' in window || navigator.maxTouchPoints > 0 || navigator.msMaxTouchPoints > 0;
|
||||
|
||||
interface ActionHandler extends HTMLElement {
|
||||
holdTime: number;
|
||||
|
@ -25,7 +23,7 @@ declare global {
|
|||
interface ActionHandlerOptions {
|
||||
hasHold?: boolean;
|
||||
hasDoubleClick?: boolean;
|
||||
repeat?: Number;
|
||||
repeat?: number;
|
||||
}
|
||||
|
||||
interface ActionHandlerDetail {
|
||||
|
@ -45,14 +43,14 @@ class ActionHandler extends HTMLElement implements ActionHandler {
|
|||
|
||||
private repeatTimeout: NodeJS.Timeout | undefined;
|
||||
|
||||
private isRepeating: boolean = false;
|
||||
private isRepeating = false;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.ripple = document.createElement('mwc-ripple');
|
||||
}
|
||||
|
||||
public connectedCallback() {
|
||||
public connectedCallback(): void {
|
||||
Object.assign(this.style, {
|
||||
position: 'absolute',
|
||||
width: isTouch ? '100px' : '50px',
|
||||
|
@ -64,15 +62,7 @@ class ActionHandler extends HTMLElement implements ActionHandler {
|
|||
this.appendChild(this.ripple);
|
||||
this.ripple.primary = true;
|
||||
|
||||
[
|
||||
'touchcancel',
|
||||
'mouseout',
|
||||
'mouseup',
|
||||
'touchmove',
|
||||
'mousewheel',
|
||||
'wheel',
|
||||
'scroll',
|
||||
].forEach((ev) => {
|
||||
['touchcancel', 'mouseout', 'mouseup', 'touchmove', 'mousewheel', 'wheel', 'scroll'].forEach(ev => {
|
||||
document.addEventListener(
|
||||
ev,
|
||||
() => {
|
||||
|
@ -80,12 +70,12 @@ class ActionHandler extends HTMLElement implements ActionHandler {
|
|||
this.stopAnimation();
|
||||
this.timer = undefined;
|
||||
},
|
||||
{ passive: true }
|
||||
{ passive: true },
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
public bind(element: ActionHandlerElement, options) {
|
||||
public bind(element: ActionHandlerElement, options): void {
|
||||
if (element.actionHandler) {
|
||||
return;
|
||||
}
|
||||
|
@ -104,7 +94,7 @@ class ActionHandler extends HTMLElement implements ActionHandler {
|
|||
return false;
|
||||
});
|
||||
|
||||
const start = (ev: Event) => {
|
||||
const start = (ev: Event): void => {
|
||||
this.held = false;
|
||||
let x;
|
||||
let y;
|
||||
|
@ -123,25 +113,23 @@ class ActionHandler extends HTMLElement implements ActionHandler {
|
|||
this.isRepeating = true;
|
||||
this.repeatTimeout = setInterval(() => {
|
||||
myFireEvent(element, 'action', { action: 'hold' });
|
||||
}, options.repeat)
|
||||
}, options.repeat);
|
||||
}
|
||||
}, this.holdTime);
|
||||
};
|
||||
|
||||
const handleEnter = (ev: KeyboardEvent) => {
|
||||
const handleEnter = (ev: KeyboardEvent): void => {
|
||||
if (ev.keyCode !== 13) {
|
||||
return;
|
||||
}
|
||||
// eslint-disable-next-line @typescript-eslint/no-use-before-define
|
||||
end(ev);
|
||||
};
|
||||
|
||||
const end = (ev: Event) => {
|
||||
const end = (ev: Event): void => {
|
||||
// Prevent mouse event if touch event
|
||||
ev.preventDefault();
|
||||
if (
|
||||
['touchend', 'touchcancel'].includes(ev.type) &&
|
||||
this.timer === undefined
|
||||
) {
|
||||
if (['touchend', 'touchcancel'].includes(ev.type) && this.timer === undefined) {
|
||||
if (this.isRepeating && this.repeatTimeout) {
|
||||
clearInterval(this.repeatTimeout);
|
||||
this.isRepeating = false;
|
||||
|
@ -160,10 +148,7 @@ class ActionHandler extends HTMLElement implements ActionHandler {
|
|||
myFireEvent(element, 'action', { action: 'hold' });
|
||||
}
|
||||
} else if (options.hasDoubleClick) {
|
||||
if (
|
||||
(ev.type === 'click' && (ev as MouseEvent).detail < 2) ||
|
||||
!this.dblClickTimeout
|
||||
) {
|
||||
if ((ev.type === 'click' && (ev as MouseEvent).detail < 2) || !this.dblClickTimeout) {
|
||||
this.dblClickTimeout = window.setTimeout(() => {
|
||||
this.dblClickTimeout = undefined;
|
||||
myFireEvent(element, 'action', { action: 'tap' });
|
||||
|
@ -188,7 +173,7 @@ class ActionHandler extends HTMLElement implements ActionHandler {
|
|||
element.addEventListener('keyup', handleEnter);
|
||||
}
|
||||
|
||||
private startAnimation(x: number, y: number) {
|
||||
private startAnimation(x: number, y: number): void {
|
||||
Object.assign(this.style, {
|
||||
left: `${x}px`,
|
||||
top: `${y}px`,
|
||||
|
@ -199,7 +184,7 @@ class ActionHandler extends HTMLElement implements ActionHandler {
|
|||
this.ripple.unbounded = true;
|
||||
}
|
||||
|
||||
private stopAnimation() {
|
||||
private stopAnimation(): void {
|
||||
this.ripple.active = false;
|
||||
this.ripple.disabled = true;
|
||||
this.style.display = 'none';
|
||||
|
@ -220,10 +205,7 @@ const getActionHandler = (): ActionHandler => {
|
|||
return actionhandler as ActionHandler;
|
||||
};
|
||||
|
||||
export const actionHandlerBind = (
|
||||
element: ActionHandlerElement,
|
||||
options: ActionHandlerOptions,
|
||||
) => {
|
||||
export const actionHandlerBind = (element: ActionHandlerElement, options: ActionHandlerOptions): void => {
|
||||
const actionhandler: ActionHandler = getActionHandler();
|
||||
if (!actionhandler) {
|
||||
return;
|
||||
|
@ -231,8 +213,6 @@ export const actionHandlerBind = (
|
|||
actionhandler.bind(element, options);
|
||||
};
|
||||
|
||||
export const actionHandler = directive(
|
||||
(options: ActionHandlerOptions = {}) => (part: PropertyPart) => {
|
||||
actionHandlerBind(part.committer.element as ActionHandlerElement, options);
|
||||
}
|
||||
);
|
||||
export const actionHandler = directive((options: ActionHandlerOptions = {}) => (part: PropertyPart): void => {
|
||||
actionHandlerBind(part.committer.element as ActionHandlerElement, options);
|
||||
});
|
||||
|
|
|
@ -1,18 +1,10 @@
|
|||
import {
|
||||
LitElement,
|
||||
html,
|
||||
customElement,
|
||||
property,
|
||||
TemplateResult,
|
||||
CSSResult,
|
||||
PropertyValues,
|
||||
} from 'lit-element';
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
/* eslint-disable @typescript-eslint/no-non-null-assertion */
|
||||
import { LitElement, html, customElement, property, TemplateResult, CSSResult, PropertyValues } from 'lit-element';
|
||||
import { styleMap, StyleInfo } from 'lit-html/directives/style-map';
|
||||
import { unsafeHTML } from 'lit-html/directives/unsafe-html';
|
||||
import { classMap, ClassInfo } from 'lit-html/directives/class-map';
|
||||
import {
|
||||
HassEntity,
|
||||
} from 'home-assistant-js-websocket';
|
||||
import { HassEntity } from 'home-assistant-js-websocket';
|
||||
import {
|
||||
stateIcon,
|
||||
HomeAssistant,
|
||||
|
@ -25,13 +17,7 @@ import {
|
|||
fireEvent,
|
||||
DOMAINS_TOGGLE,
|
||||
} from 'custom-card-helpers';
|
||||
import { BUTTON_CARD_VERSION } from './version-const';
|
||||
import {
|
||||
ButtonCardConfig,
|
||||
StateConfig,
|
||||
ExemptionUserConfig,
|
||||
ExemptionUsernameConfig,
|
||||
} from './types';
|
||||
import { ButtonCardConfig, StateConfig, ExemptionUserConfig, ExemptionUsernameConfig } from './types';
|
||||
import { actionHandler } from './action-handler';
|
||||
import {
|
||||
computeDomain,
|
||||
|
@ -47,9 +33,10 @@ import {
|
|||
} from './helpers';
|
||||
import { styles } from './styles';
|
||||
import myComputeStateDisplay from './compute_state_display';
|
||||
import * as pjson from '../package.json';
|
||||
|
||||
let helpers = (window as any).cardHelpers;
|
||||
const helperPromise = new Promise(async (resolve) => {
|
||||
const helperPromise = new Promise(async resolve => {
|
||||
if (helpers) resolve();
|
||||
if ((window as any).loadCardHelpers) {
|
||||
helpers = await (window as any).loadCardHelpers();
|
||||
|
@ -60,7 +47,7 @@ const helperPromise = new Promise(async (resolve) => {
|
|||
|
||||
/* eslint no-console: 0 */
|
||||
console.info(
|
||||
`%c BUTTON-CARD \n%c Version ${BUTTON_CARD_VERSION} `,
|
||||
`%c BUTTON-CARD \n%c Version ${pjson.version} `,
|
||||
'color: orange; font-weight: bold; background: black',
|
||||
'color: white; font-weight: bold; background: dimgray',
|
||||
);
|
||||
|
@ -88,19 +75,14 @@ class ButtonCard extends LitElement {
|
|||
|
||||
public connectedCallback(): void {
|
||||
super.connectedCallback();
|
||||
if (
|
||||
this.config
|
||||
&& this.config.entity
|
||||
&& computeDomain(this.config.entity) === 'timer'
|
||||
) {
|
||||
if (this.config && this.config.entity && computeDomain(this.config.entity) === 'timer') {
|
||||
const stateObj = this.hass!.states[this.config.entity];
|
||||
this._startInterval(stateObj);
|
||||
}
|
||||
}
|
||||
|
||||
private _createCard(config: any): any {
|
||||
if (helpers)
|
||||
return helpers.createCardElement(config);
|
||||
if (helpers) return helpers.createCardElement(config);
|
||||
else {
|
||||
const element = createThing(config);
|
||||
helperPromise.then(() => {
|
||||
|
@ -126,26 +108,22 @@ class ButtonCard extends LitElement {
|
|||
}
|
||||
|
||||
protected shouldUpdate(changedProps: PropertyValues): boolean {
|
||||
const forceUpdate = this._hasTemplate
|
||||
|| changedProps.has('_timeRemaining')
|
||||
? true : false;
|
||||
const forceUpdate = this._hasTemplate || changedProps.has('_timeRemaining') ? true : false;
|
||||
return forceUpdate || myHasConfigOrEntityChanged(this, changedProps);
|
||||
}
|
||||
|
||||
protected updated(changedProps: PropertyValues) {
|
||||
protected updated(changedProps: PropertyValues): void {
|
||||
super.updated(changedProps);
|
||||
|
||||
if (
|
||||
this.config
|
||||
&& this.config.entity
|
||||
&& computeDomain(this.config.entity) === 'timer'
|
||||
&& changedProps.has('hass')
|
||||
this.config &&
|
||||
this.config.entity &&
|
||||
computeDomain(this.config.entity) === 'timer' &&
|
||||
changedProps.has('hass')
|
||||
) {
|
||||
const stateObj = this.hass!.states[this.config.entity];
|
||||
const oldHass = changedProps.get('hass') as this['hass'];
|
||||
const oldStateObj = oldHass
|
||||
? oldHass.states[this.config.entity]
|
||||
: undefined;
|
||||
const oldStateObj = oldHass ? oldHass.states[this.config.entity] : undefined;
|
||||
|
||||
if (oldStateObj !== stateObj) {
|
||||
this._startInterval(stateObj);
|
||||
|
@ -167,10 +145,7 @@ class ButtonCard extends LitElement {
|
|||
this._calculateRemaining(stateObj);
|
||||
|
||||
if (stateObj.state === 'active') {
|
||||
this._interval = window.setInterval(
|
||||
() => this._calculateRemaining(stateObj),
|
||||
1000,
|
||||
);
|
||||
this._interval = window.setInterval(() => this._calculateRemaining(stateObj), 1000);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -183,42 +158,37 @@ class ButtonCard extends LitElement {
|
|||
return undefined;
|
||||
}
|
||||
|
||||
return secondsToDuration(
|
||||
this._timeRemaining || durationToSeconds(stateObj.attributes.duration),
|
||||
);
|
||||
return secondsToDuration(this._timeRemaining || durationToSeconds(stateObj.attributes.duration));
|
||||
}
|
||||
|
||||
private _getMatchingConfigState(state: HassEntity | undefined): StateConfig | undefined {
|
||||
if (!this.config!.state) {
|
||||
return undefined;
|
||||
}
|
||||
const hasTemplate = this.config!.state.find(
|
||||
elt => elt.operator === 'template',
|
||||
);
|
||||
const hasTemplate = this.config!.state.find(elt => elt.operator === 'template');
|
||||
if (!state && !hasTemplate) {
|
||||
return undefined;
|
||||
}
|
||||
let def: StateConfig | undefined;
|
||||
const retval = this.config!.state.find((elt) => {
|
||||
const retval = this.config!.state.find(elt => {
|
||||
if (elt.operator) {
|
||||
switch (elt.operator) {
|
||||
case '==':
|
||||
/* eslint eqeqeq: 0 */
|
||||
return (state && state.state == this._getTemplateOrValue(state, elt.value));
|
||||
return state && state.state == this._getTemplateOrValue(state, elt.value);
|
||||
case '<=':
|
||||
return (state && state.state <= this._getTemplateOrValue(state, elt.value));
|
||||
return state && state.state <= this._getTemplateOrValue(state, elt.value);
|
||||
case '<':
|
||||
return (state && state.state < this._getTemplateOrValue(state, elt.value));
|
||||
return state && state.state < this._getTemplateOrValue(state, elt.value);
|
||||
case '>=':
|
||||
return (state && state.state >= this._getTemplateOrValue(state, elt.value));
|
||||
return state && state.state >= this._getTemplateOrValue(state, elt.value);
|
||||
case '>':
|
||||
return (state && state.state > this._getTemplateOrValue(state, elt.value));
|
||||
return state && state.state > this._getTemplateOrValue(state, elt.value);
|
||||
case '!=':
|
||||
return (state && state.state != this._getTemplateOrValue(state, elt.value));
|
||||
return state && state.state != this._getTemplateOrValue(state, elt.value);
|
||||
case 'regex': {
|
||||
/* eslint no-unneeded-ternary: 0 */
|
||||
const matches = state
|
||||
&& state.state.match(this._getTemplateOrValue(state, elt.value)) ? true : false;
|
||||
const matches = state && state.state.match(this._getTemplateOrValue(state, elt.value)) ? true : false;
|
||||
return matches;
|
||||
}
|
||||
case 'template': {
|
||||
|
@ -231,7 +201,7 @@ class ButtonCard extends LitElement {
|
|||
return false;
|
||||
}
|
||||
} else {
|
||||
return state && (this._getTemplateOrValue(state, elt.value) == state.state);
|
||||
return state && this._getTemplateOrValue(state, elt.value) == state.state;
|
||||
}
|
||||
});
|
||||
if (!retval && def) {
|
||||
|
@ -242,37 +212,33 @@ class ButtonCard extends LitElement {
|
|||
|
||||
private _evalTemplate(state: HassEntity | undefined, func: any): any {
|
||||
/* eslint no-new-func: 0 */
|
||||
return new Function('states', 'entity', 'user', 'hass', 'variables', 'html',
|
||||
`'use strict'; ${func}`)
|
||||
.call(this, this.hass!.states, state, this.hass!.user, this.hass,
|
||||
this._evaledVariables, html);
|
||||
return new Function('states', 'entity', 'user', 'hass', 'variables', 'html', `'use strict'; ${func}`).call(
|
||||
this,
|
||||
this.hass!.states,
|
||||
state,
|
||||
this.hass!.user,
|
||||
this.hass,
|
||||
this._evaledVariables,
|
||||
html,
|
||||
);
|
||||
}
|
||||
|
||||
private _objectEvalTemplate(
|
||||
state: HassEntity | undefined,
|
||||
obj: any | undefined,
|
||||
) {
|
||||
private _objectEvalTemplate(state: HassEntity | undefined, obj: any | undefined): any {
|
||||
const objClone = JSON.parse(JSON.stringify(obj));
|
||||
return this._getTemplateOrValue(state, objClone);
|
||||
}
|
||||
|
||||
private _getTemplateOrValue(
|
||||
state: HassEntity | undefined,
|
||||
value: any | undefined,
|
||||
): any | undefined {
|
||||
private _getTemplateOrValue(state: HassEntity | undefined, value: any | undefined): any | undefined {
|
||||
if (['number', 'boolean'].includes(typeof value)) return value;
|
||||
if (!value) return value;
|
||||
if (['object'].includes(typeof value)) {
|
||||
Object.keys(value).forEach((key) => {
|
||||
Object.keys(value).forEach(key => {
|
||||
value[key] = this._getTemplateOrValue(state, value[key]);
|
||||
});
|
||||
return value;
|
||||
}
|
||||
const trimmed = value.trim();
|
||||
if (
|
||||
trimmed.substring(0, 3) === '[[['
|
||||
&& trimmed.slice(-3) === ']]]'
|
||||
) {
|
||||
if (trimmed.substring(0, 3) === '[[[' && trimmed.slice(-3) === ']]]') {
|
||||
return this._evalTemplate(state, trimmed.slice(3, -3));
|
||||
} else {
|
||||
return value;
|
||||
|
@ -290,10 +256,7 @@ class ButtonCard extends LitElement {
|
|||
}
|
||||
}
|
||||
|
||||
private _getColorForLightEntity(
|
||||
state: HassEntity | undefined,
|
||||
useTemperature: boolean,
|
||||
): string {
|
||||
private _getColorForLightEntity(state: HassEntity | undefined, useTemperature: boolean): string {
|
||||
let color: string = this.config!.default_color;
|
||||
if (state) {
|
||||
if (state.attributes.rgb_color) {
|
||||
|
@ -301,10 +264,12 @@ class ButtonCard extends LitElement {
|
|||
if (state.attributes.brightness) {
|
||||
color = applyBrightnessToColor(color, (state.attributes.brightness + 245) / 5);
|
||||
}
|
||||
} else if (useTemperature
|
||||
&& state.attributes.color_temp
|
||||
&& state.attributes.min_mireds
|
||||
&& state.attributes.max_mireds) {
|
||||
} else if (
|
||||
useTemperature &&
|
||||
state.attributes.color_temp &&
|
||||
state.attributes.min_mireds &&
|
||||
state.attributes.max_mireds
|
||||
) {
|
||||
color = getLightColorBasedOnTemperature(
|
||||
state.attributes.color_temp,
|
||||
state.attributes.min_mireds,
|
||||
|
@ -314,9 +279,7 @@ class ButtonCard extends LitElement {
|
|||
color = applyBrightnessToColor(color, (state.attributes.brightness + 245) / 5);
|
||||
}
|
||||
} else if (state.attributes.brightness) {
|
||||
color = applyBrightnessToColor(
|
||||
this._getDefaultColorForState(state), (state.attributes.brightness + 245) / 5,
|
||||
);
|
||||
color = applyBrightnessToColor(this._getDefaultColorForState(state), (state.attributes.brightness + 245) / 5);
|
||||
} else {
|
||||
color = this._getDefaultColorForState(state);
|
||||
}
|
||||
|
@ -324,10 +287,8 @@ class ButtonCard extends LitElement {
|
|||
return color;
|
||||
}
|
||||
|
||||
private _buildCssColorAttribute(
|
||||
state: HassEntity | undefined, configState: StateConfig | undefined,
|
||||
): string {
|
||||
let colorValue: string = '';
|
||||
private _buildCssColorAttribute(state: HassEntity | undefined, configState: StateConfig | undefined): string {
|
||||
let colorValue = '';
|
||||
let color: undefined | string;
|
||||
if (configState && configState.color) {
|
||||
colorValue = configState.color;
|
||||
|
@ -348,9 +309,7 @@ class ButtonCard extends LitElement {
|
|||
return color;
|
||||
}
|
||||
|
||||
private _buildIcon(
|
||||
state: HassEntity | undefined, configState: StateConfig | undefined,
|
||||
): string | undefined {
|
||||
private _buildIcon(state: HassEntity | undefined, configState: StateConfig | undefined): string | undefined {
|
||||
if (!this.config!.show_icon) {
|
||||
return undefined;
|
||||
}
|
||||
|
@ -360,18 +319,14 @@ class ButtonCard extends LitElement {
|
|||
} else if (this.config!.icon) {
|
||||
icon = this.config!.icon;
|
||||
} else {
|
||||
if (!state)
|
||||
return undefined;
|
||||
if (!state) return undefined;
|
||||
icon = stateIcon(state);
|
||||
}
|
||||
return this._getTemplateOrValue(state, icon);
|
||||
}
|
||||
|
||||
private _buildEntityPicture(
|
||||
state: HassEntity | undefined, configState: StateConfig | undefined,
|
||||
): string | undefined {
|
||||
if (!this.config!.show_entity_picture
|
||||
|| !state && !configState && !this.config!.entity_picture) {
|
||||
private _buildEntityPicture(state: HassEntity | undefined, configState: StateConfig | undefined): string | undefined {
|
||||
if (!this.config!.show_entity_picture || (!state && !configState && !this.config!.entity_picture)) {
|
||||
return undefined;
|
||||
}
|
||||
let entityPicture: string | undefined;
|
||||
|
@ -381,8 +336,7 @@ class ButtonCard extends LitElement {
|
|||
} else if (this.config!.entity_picture) {
|
||||
entityPicture = this.config!.entity_picture;
|
||||
} else if (state) {
|
||||
entityPicture = state.attributes && state.attributes.entity_picture
|
||||
? state.attributes.entity_picture : undefined;
|
||||
entityPicture = state.attributes && state.attributes.entity_picture ? state.attributes.entity_picture : undefined;
|
||||
}
|
||||
return this._getTemplateOrValue(state, entityPicture);
|
||||
}
|
||||
|
@ -404,7 +358,7 @@ class ButtonCard extends LitElement {
|
|||
...configStateStyle,
|
||||
};
|
||||
}
|
||||
Object.keys(style).forEach((key) => {
|
||||
Object.keys(style).forEach(key => {
|
||||
style[key] = this._getTemplateOrValue(state, style[key]);
|
||||
});
|
||||
return style;
|
||||
|
@ -416,35 +370,29 @@ class ButtonCard extends LitElement {
|
|||
styleType: string,
|
||||
): StyleInfo {
|
||||
let style: any = {};
|
||||
if (this.config!.styles
|
||||
&& this.config!.styles.custom_fields
|
||||
&& this.config!.styles.custom_fields[styleType]
|
||||
) {
|
||||
if (this.config!.styles && this.config!.styles.custom_fields && this.config!.styles.custom_fields[styleType]) {
|
||||
style = Object.assign(style, ...this.config!.styles.custom_fields[styleType]);
|
||||
}
|
||||
if (configState && configState.styles
|
||||
&& configState.styles.custom_fields
|
||||
&& configState.styles.custom_fields[styleType]
|
||||
if (
|
||||
configState &&
|
||||
configState.styles &&
|
||||
configState.styles.custom_fields &&
|
||||
configState.styles.custom_fields[styleType]
|
||||
) {
|
||||
let configStateStyle: StyleInfo = {};
|
||||
configStateStyle = Object.assign(
|
||||
configStateStyle,
|
||||
...configState.styles.custom_fields[styleType],
|
||||
);
|
||||
configStateStyle = Object.assign(configStateStyle, ...configState.styles.custom_fields[styleType]);
|
||||
style = {
|
||||
...style,
|
||||
...configStateStyle,
|
||||
};
|
||||
}
|
||||
Object.keys(style).forEach((key) => {
|
||||
Object.keys(style).forEach(key => {
|
||||
style[key] = this._getTemplateOrValue(state, style[key]);
|
||||
});
|
||||
return style;
|
||||
}
|
||||
|
||||
private _buildName(
|
||||
state: HassEntity | undefined, configState: StateConfig | undefined,
|
||||
): string | undefined {
|
||||
private _buildName(state: HassEntity | undefined, configState: StateConfig | undefined): string | undefined {
|
||||
if (this.config!.show_name === false) {
|
||||
return undefined;
|
||||
}
|
||||
|
@ -455,10 +403,11 @@ class ButtonCard extends LitElement {
|
|||
} else if (this.config!.name) {
|
||||
name = this.config!.name;
|
||||
} else if (state) {
|
||||
name = state.attributes && state.attributes.friendly_name
|
||||
? state.attributes.friendly_name : computeEntity(state.entity_id);
|
||||
name =
|
||||
state.attributes && state.attributes.friendly_name
|
||||
? state.attributes.friendly_name
|
||||
: computeEntity(state.entity_id);
|
||||
}
|
||||
|
||||
return this._getTemplateOrValue(state, name);
|
||||
}
|
||||
|
||||
|
@ -499,25 +448,21 @@ class ButtonCard extends LitElement {
|
|||
return units;
|
||||
}
|
||||
|
||||
private _buildLastChanged(
|
||||
state: HassEntity | undefined,
|
||||
style: StyleInfo,
|
||||
): TemplateResult | undefined {
|
||||
private _buildLastChanged(state: HassEntity | undefined, style: StyleInfo): TemplateResult | undefined {
|
||||
return this.config!.show_last_changed && state
|
||||
? html`
|
||||
<ha-relative-time
|
||||
id="label"
|
||||
class="ellipsis"
|
||||
.hass="${this.hass}"
|
||||
.datetime="${state.last_changed}"
|
||||
style=${styleMap(style)}
|
||||
></ha-relative-time>` : undefined;
|
||||
<ha-relative-time
|
||||
id="label"
|
||||
class="ellipsis"
|
||||
.hass="${this.hass}"
|
||||
.datetime="${state.last_changed}"
|
||||
style=${styleMap(style)}
|
||||
></ha-relative-time>
|
||||
`
|
||||
: undefined;
|
||||
}
|
||||
|
||||
private _buildLabel(
|
||||
state: HassEntity | undefined,
|
||||
configState: StateConfig | undefined,
|
||||
): string | undefined {
|
||||
private _buildLabel(state: HassEntity | undefined, configState: StateConfig | undefined): string | undefined {
|
||||
if (!this.config!.show_label) {
|
||||
return undefined;
|
||||
}
|
||||
|
@ -532,15 +477,12 @@ class ButtonCard extends LitElement {
|
|||
return this._getTemplateOrValue(state, label);
|
||||
}
|
||||
|
||||
private _buildCustomFields(
|
||||
state: HassEntity | undefined,
|
||||
configState: StateConfig | undefined,
|
||||
): TemplateResult {
|
||||
private _buildCustomFields(state: HassEntity | undefined, configState: StateConfig | undefined): TemplateResult {
|
||||
let result = html``;
|
||||
const fields: any = {};
|
||||
const cards: any = {};
|
||||
if (this.config!.custom_fields) {
|
||||
Object.keys(this.config!.custom_fields).forEach((key) => {
|
||||
Object.keys(this.config!.custom_fields).forEach(key => {
|
||||
const value = this.config!.custom_fields![key];
|
||||
if (!value.card) {
|
||||
fields[key] = this._getTemplateOrValue(state, value);
|
||||
|
@ -550,7 +492,7 @@ class ButtonCard extends LitElement {
|
|||
});
|
||||
}
|
||||
if (configState && configState.custom_fields) {
|
||||
Object.keys(configState.custom_fields).forEach((key) => {
|
||||
Object.keys(configState.custom_fields).forEach(key => {
|
||||
const value = configState!.custom_fields![key];
|
||||
if (!value!.card) {
|
||||
fields[key] = this._getTemplateOrValue(state, value);
|
||||
|
@ -559,22 +501,21 @@ class ButtonCard extends LitElement {
|
|||
}
|
||||
});
|
||||
}
|
||||
Object.keys(fields).forEach((key) => {
|
||||
Object.keys(fields).forEach(key => {
|
||||
if (fields[key] != undefined) {
|
||||
const customStyle: StyleInfo = {
|
||||
...this._buildCustomStyleGeneric(state, configState, key),
|
||||
'grid-area': key,
|
||||
};
|
||||
result = html`${result}
|
||||
<div id=${key}
|
||||
class="ellipsis"
|
||||
style=${styleMap(customStyle)}
|
||||
>
|
||||
${fields[key] && (fields[key] as any).type === 'html' ? fields[key] : unsafeHTML(fields[key])}
|
||||
</div>`;
|
||||
result = html`
|
||||
${result}
|
||||
<div id=${key} class="ellipsis" style=${styleMap(customStyle)}>
|
||||
${fields[key] && (fields[key] as any).type === 'html' ? fields[key] : unsafeHTML(fields[key])}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
});
|
||||
Object.keys(cards).forEach((key) => {
|
||||
Object.keys(cards).forEach(key => {
|
||||
if (cards[key] != undefined) {
|
||||
const customStyle: StyleInfo = {
|
||||
...this._buildCustomStyleGeneric(state, configState, key),
|
||||
|
@ -582,15 +523,18 @@ class ButtonCard extends LitElement {
|
|||
};
|
||||
const thing = this._createCard(cards[key]);
|
||||
thing.hass = this.hass;
|
||||
result = html`${result}
|
||||
<div id=${key}
|
||||
class="ellipsis"
|
||||
@click=${this._stopPropagation}
|
||||
@touchstart=${this._stopPropagation}
|
||||
style=${styleMap(customStyle)}
|
||||
>
|
||||
${thing}
|
||||
</div>`;
|
||||
result = html`
|
||||
${result}
|
||||
<div
|
||||
id=${key}
|
||||
class="ellipsis"
|
||||
@click=${this._stopPropagation}
|
||||
@touchstart=${this._stopPropagation}
|
||||
style=${styleMap(customStyle)}
|
||||
>
|
||||
${thing}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
});
|
||||
return result;
|
||||
|
@ -601,11 +545,7 @@ 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'
|
||||
) {
|
||||
if (tap_action != 'none' || hold_action != 'none' || double_tap_action != 'none') {
|
||||
clickable = true;
|
||||
} else {
|
||||
clickable = false;
|
||||
|
@ -617,9 +557,7 @@ class ButtonCard extends LitElement {
|
|||
return configState && configState.spin ? true : false;
|
||||
}
|
||||
|
||||
private _blankCardColoredHtml(
|
||||
cardStyle: StyleInfo,
|
||||
): TemplateResult {
|
||||
private _blankCardColoredHtml(cardStyle: StyleInfo): TemplateResult {
|
||||
const blankCardStyle = {
|
||||
background: 'none',
|
||||
'box-shadow': 'none',
|
||||
|
@ -629,7 +567,7 @@ class ButtonCard extends LitElement {
|
|||
<ha-card class="disabled" style=${styleMap(blankCardStyle)}>
|
||||
<div></div>
|
||||
</ha-card>
|
||||
`;
|
||||
`;
|
||||
}
|
||||
|
||||
private _cardHtml(): TemplateResult {
|
||||
|
@ -673,45 +611,54 @@ class ButtonCard extends LitElement {
|
|||
aspectRatio.display = 'inline';
|
||||
}
|
||||
this.style.setProperty('--button-card-light-color', this._getColorForLightEntity(this._stateObj, true));
|
||||
this.style.setProperty('--button-card-light-color-no-temperature', this._getColorForLightEntity(this._stateObj, false));
|
||||
this.style.setProperty(
|
||||
'--button-card-light-color-no-temperature',
|
||||
this._getColorForLightEntity(this._stateObj, false),
|
||||
);
|
||||
lockStyle = { ...lockStyle, ...lockStyleFromConfig };
|
||||
|
||||
const extraStyles = document.createElement('style');
|
||||
extraStyles.innerHTML = this.config!.extra_styles ? this._getTemplateOrValue(this._stateObj, this.config!.extra_styles) : '';
|
||||
extraStyles.innerHTML = this.config!.extra_styles
|
||||
? this._getTemplateOrValue(this._stateObj, this.config!.extra_styles)
|
||||
: '';
|
||||
return html`
|
||||
${extraStyles}
|
||||
<div id="aspect-ratio" style=${styleMap(aspectRatio)}>
|
||||
<ha-card
|
||||
id="card"
|
||||
class=${classMap(classList)}
|
||||
style=${styleMap(cardStyle)}
|
||||
@action=${this._handleAction}
|
||||
.actionHandler=${actionHandler({
|
||||
hasDoubleClick: this.config!.double_tap_action!.action !== 'none',
|
||||
hasHold: this.config!.hold_action!.action !== 'none',
|
||||
repeat: this.config!.hold_action!.repeat,
|
||||
})}
|
||||
.config="${this.config}"
|
||||
>
|
||||
${this._buttonContent(this._stateObj, configState, buttonColor)}
|
||||
${this.config!.lock
|
||||
&& this._getTemplateOrValue(this._stateObj, this.config!.lock.enabled) ? '' : html`<mwc-ripple id="ripple"></mwc-ripple>`}
|
||||
</ha-card>
|
||||
</div>
|
||||
${this._getLock(lockStyle)}
|
||||
<div id="aspect-ratio" style=${styleMap(aspectRatio)}>
|
||||
<ha-card
|
||||
id="card"
|
||||
class=${classMap(classList)}
|
||||
style=${styleMap(cardStyle)}
|
||||
@action=${this._handleAction}
|
||||
.actionHandler=${actionHandler({
|
||||
hasDoubleClick: this.config!.double_tap_action!.action !== 'none',
|
||||
hasHold: this.config!.hold_action!.action !== 'none',
|
||||
repeat: this.config!.hold_action!.repeat,
|
||||
})}
|
||||
.config="${this.config}"
|
||||
>
|
||||
${this._buttonContent(this._stateObj, configState, buttonColor)}
|
||||
${this.config!.lock && this._getTemplateOrValue(this._stateObj, this.config!.lock.enabled)
|
||||
? ''
|
||||
: html`
|
||||
<mwc-ripple id="ripple"></mwc-ripple>
|
||||
`}
|
||||
</ha-card>
|
||||
</div>
|
||||
${this._getLock(lockStyle)}
|
||||
`;
|
||||
}
|
||||
|
||||
private _getLock(lockStyle: StyleInfo): TemplateResult {
|
||||
if (this.config!.lock
|
||||
&& this._getTemplateOrValue(this._stateObj, this.config!.lock.enabled)) {
|
||||
if (this.config!.lock && this._getTemplateOrValue(this._stateObj, this.config!.lock.enabled)) {
|
||||
return html`
|
||||
<div id="overlay" style=${styleMap(lockStyle)}
|
||||
<div
|
||||
id="overlay"
|
||||
style=${styleMap(lockStyle)}
|
||||
@action=${this._handleUnlockType}
|
||||
.actionHandler=${actionHandler({
|
||||
hasDoubleClick: this.config!.lock!.unlock === 'double_tap',
|
||||
hasHold: this.config!.lock!.unlock === 'hold',
|
||||
})}
|
||||
hasDoubleClick: this.config!.lock!.unlock === 'double_tap',
|
||||
hasHold: this.config!.lock!.unlock === 'hold',
|
||||
})}
|
||||
.config="${this.config}"
|
||||
>
|
||||
<ha-icon id="lock" icon="mdi:lock-outline"></ha-icon>
|
||||
|
@ -727,20 +674,19 @@ class ButtonCard extends LitElement {
|
|||
color: string,
|
||||
): TemplateResult {
|
||||
const name = this._buildName(state, configState);
|
||||
const stateDisplay = this.config!.show_state && this.config!.state_display
|
||||
? this._getTemplateOrValue(state, this.config!.state_display)
|
||||
: undefined;
|
||||
const stateDisplay =
|
||||
this.config!.show_state && this.config!.state_display
|
||||
? this._getTemplateOrValue(state, this.config!.state_display)
|
||||
: undefined;
|
||||
const stateString = stateDisplay ? stateDisplay : this._buildStateString(state);
|
||||
const nameStateString = buildNameStateConcat(name, stateString);
|
||||
|
||||
switch (this.config!.layout) {
|
||||
case 'icon_name_state':
|
||||
case 'name_state':
|
||||
return this._gridHtml(state, configState, this.config!.layout, color,
|
||||
nameStateString, undefined);
|
||||
return this._gridHtml(state, configState, this.config!.layout, color, nameStateString, undefined);
|
||||
default:
|
||||
return this._gridHtml(state, configState, this.config!.layout, color,
|
||||
name, stateString);
|
||||
return this._gridHtml(state, configState, this.config!.layout, color, name, stateString);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -768,11 +714,28 @@ class ButtonCard extends LitElement {
|
|||
return html`
|
||||
<div id="container" class=${itemClass.join(' ')} style=${styleMap(gridStyleFromConfig)}>
|
||||
${iconTemplate ? iconTemplate : ''}
|
||||
${name ? html`<div id="name" class="ellipsis" style=${styleMap(nameStyleFromConfig)}>${(name as any).type === 'html' ? name : unsafeHTML(name)}</div>` : ''}
|
||||
${stateString ? html`<div id="state" class="ellipsis" style=${styleMap(stateStyleFromConfig)}>${(stateString as any).type === 'html' ? stateString : unsafeHTML(stateString)}</div>` : ''}
|
||||
${label && !lastChangedTemplate ? html`<div id="label" class="ellipsis" style=${styleMap(labelStyleFromConfig)}>${(label as any).type === 'html' ? label : unsafeHTML(label)}</div>` : ''}
|
||||
${lastChangedTemplate ? lastChangedTemplate : ''}
|
||||
${this._buildCustomFields(state, configState)}
|
||||
${name
|
||||
? html`
|
||||
<div id="name" class="ellipsis" style=${styleMap(nameStyleFromConfig)}>
|
||||
${(name as any).type === 'html' ? name : unsafeHTML(name)}
|
||||
</div>
|
||||
`
|
||||
: ''}
|
||||
${stateString
|
||||
? html`
|
||||
<div id="state" class="ellipsis" style=${styleMap(stateStyleFromConfig)}>
|
||||
${(stateString as any).type === 'html' ? stateString : unsafeHTML(stateString)}
|
||||
</div>
|
||||
`
|
||||
: ''}
|
||||
${label && !lastChangedTemplate
|
||||
? html`
|
||||
<div id="label" class="ellipsis" style=${styleMap(labelStyleFromConfig)}>
|
||||
${(label as any).type === 'html' ? label : unsafeHTML(label)}
|
||||
</div>
|
||||
`
|
||||
: ''}
|
||||
${lastChangedTemplate ? lastChangedTemplate : ''} ${this._buildCustomFields(state, configState)}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
@ -804,11 +767,27 @@ class ButtonCard extends LitElement {
|
|||
if (icon || entityPicture) {
|
||||
return html`
|
||||
<div id="img-cell" style=${styleMap(imgCellStyleFromConfig)}>
|
||||
${icon && !entityPicture && !liveStream ? html`<ha-icon style=${styleMap(haIconStyle)}
|
||||
.icon="${icon}" id="icon" ?rotating=${this._rotate(configState)}></ha-icon>` : ''}
|
||||
${icon && !entityPicture && !liveStream
|
||||
? html`
|
||||
<ha-icon
|
||||
style=${styleMap(haIconStyle)}
|
||||
.icon="${icon}"
|
||||
id="icon"
|
||||
?rotating=${this._rotate(configState)}
|
||||
></ha-icon>
|
||||
`
|
||||
: ''}
|
||||
${liveStream ? liveStream : ''}
|
||||
${entityPicture && !liveStream ? html`<img src="${entityPicture}" style=${styleMap(entityPictureStyle)}
|
||||
id="icon" ?rotating=${this._rotate(configState)} />` : ''}
|
||||
${entityPicture && !liveStream
|
||||
? html`
|
||||
<img
|
||||
src="${entityPicture}"
|
||||
style=${styleMap(entityPictureStyle)}
|
||||
id="icon"
|
||||
?rotating=${this._rotate(configState)}
|
||||
/>
|
||||
`
|
||||
: ''}
|
||||
</div>
|
||||
`;
|
||||
} else {
|
||||
|
@ -823,7 +802,7 @@ class ButtonCard extends LitElement {
|
|||
.hass=${this.hass}
|
||||
.cameraImage=${this.config!.entity}
|
||||
.entity=${this.config!.entity}
|
||||
cameraView='live'
|
||||
cameraView="live"
|
||||
style=${styleMap(style)}
|
||||
></hui-image>
|
||||
`;
|
||||
|
@ -908,7 +887,7 @@ class ButtonCard extends LitElement {
|
|||
if (!configEval) {
|
||||
return configEval;
|
||||
}
|
||||
Object.keys(configEval).forEach((key) => {
|
||||
Object.keys(configEval).forEach(key => {
|
||||
if (typeof configEval[key] === 'object') {
|
||||
configEval[key] = __evalObject(configEval[key]);
|
||||
} else {
|
||||
|
@ -924,7 +903,7 @@ class ButtonCard extends LitElement {
|
|||
return configDuplicate;
|
||||
}
|
||||
|
||||
private _handleAction(ev: any) {
|
||||
private _handleAction(ev: any): void {
|
||||
if (ev.detail && ev.detail.action) {
|
||||
switch (ev.detail.action) {
|
||||
case 'tap':
|
||||
|
@ -960,7 +939,7 @@ class ButtonCard extends LitElement {
|
|||
handleClick(this, this.hass!, this._evalActions(config, 'double_tap_action'), false, true);
|
||||
}
|
||||
|
||||
private _handleUnlockType(ev) {
|
||||
private _handleUnlockType(ev): void {
|
||||
const config = ev.target.config as ButtonCardConfig;
|
||||
if (!config) return;
|
||||
if (config.lock.unlock === ev.detail.action) {
|
||||
|
@ -974,9 +953,11 @@ class ButtonCard extends LitElement {
|
|||
if (this.config!.lock!.exemptions) {
|
||||
if (!this.hass!.user.name || !this.hass!.user.id) return;
|
||||
let matched = false;
|
||||
this.config!.lock!.exemptions.forEach((e) => {
|
||||
if (!matched && (e as ExemptionUserConfig).user === this.hass!.user.id
|
||||
|| (e as ExemptionUsernameConfig).username === this.hass!.user.name) {
|
||||
this.config!.lock!.exemptions.forEach(e => {
|
||||
if (
|
||||
(!matched && (e as ExemptionUserConfig).user === this.hass!.user.id) ||
|
||||
(e as ExemptionUsernameConfig).username === this.hass!.user.name
|
||||
) {
|
||||
matched = true;
|
||||
}
|
||||
});
|
||||
|
@ -1014,7 +995,7 @@ class ButtonCard extends LitElement {
|
|||
}, this.config!.lock!.duration! * 1000);
|
||||
}
|
||||
|
||||
private _stopPropagation(ev) {
|
||||
private _stopPropagation(ev): void {
|
||||
ev.stopPropagation();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,36 +2,24 @@ import { HassEntity } from 'home-assistant-js-websocket';
|
|||
import { LocalizeFunc } from 'custom-card-helpers';
|
||||
import { computeDomain } from './helpers';
|
||||
|
||||
export default (
|
||||
localize: LocalizeFunc,
|
||||
stateObj: HassEntity,
|
||||
): string | undefined => {
|
||||
export default (localize: LocalizeFunc, stateObj: HassEntity): string | undefined => {
|
||||
let display: string | undefined;
|
||||
const domain = computeDomain(stateObj.entity_id);
|
||||
|
||||
if (domain === 'binary_sensor') {
|
||||
// Try device class translation, then default binary sensor translation
|
||||
if (stateObj.attributes.device_class) {
|
||||
display = localize(
|
||||
`state.${domain}.${stateObj.attributes.device_class}.${stateObj.state}`,
|
||||
);
|
||||
display = localize(`state.${domain}.${stateObj.attributes.device_class}.${stateObj.state}`);
|
||||
}
|
||||
|
||||
if (!display) {
|
||||
display = localize(`state.${domain}.default.${stateObj.state}`);
|
||||
}
|
||||
} else if (
|
||||
stateObj.attributes.unit_of_measurement
|
||||
&& !['unknown', 'unavailable'].includes(stateObj.state)
|
||||
) {
|
||||
} else if (stateObj.attributes.unit_of_measurement && !['unknown', 'unavailable'].includes(stateObj.state)) {
|
||||
display = stateObj.state;
|
||||
} else if (domain === 'zwave') {
|
||||
if (['initializing', 'dead'].includes(stateObj.state)) {
|
||||
display = localize(
|
||||
`state.zwave.query_stage.${stateObj.state}`,
|
||||
'query_stage',
|
||||
stateObj.attributes.query_stage,
|
||||
);
|
||||
display = localize(`state.zwave.query_stage.${stateObj.state}`, 'query_stage', stateObj.attributes.query_stage);
|
||||
} else {
|
||||
display = localize(`state.zwave.default.${stateObj.state}`);
|
||||
}
|
||||
|
@ -41,9 +29,10 @@ export default (
|
|||
|
||||
// Fall back to default, component backend translation, or raw state if nothing else matches.
|
||||
if (!display) {
|
||||
display = localize(`state.default.${stateObj.state}`)
|
||||
|| localize(`component.${domain}.state.${stateObj.state}`)
|
||||
|| stateObj.state;
|
||||
display =
|
||||
localize(`state.default.${stateObj.state}`) ||
|
||||
localize(`component.${domain}.state.${stateObj.state}`) ||
|
||||
stateObj.state;
|
||||
}
|
||||
|
||||
return display;
|
||||
|
|
|
@ -13,8 +13,10 @@ export function computeEntity(entityId: string): string {
|
|||
|
||||
export function getColorFromVariable(color: string): string {
|
||||
if (color.substring(0, 3) === 'var') {
|
||||
return window.getComputedStyle(document.documentElement)
|
||||
.getPropertyValue(color.substring(4).slice(0, -1)).trim();
|
||||
return window
|
||||
.getComputedStyle(document.documentElement)
|
||||
.getPropertyValue(color.substring(4).slice(0, -1))
|
||||
.trim();
|
||||
}
|
||||
return color;
|
||||
}
|
||||
|
@ -24,29 +26,27 @@ export function getFontColorBasedOnBackgroundColor(backgroundColor: string): str
|
|||
if (colorObj.isValid && colorObj.getLuminance() > 0.5) {
|
||||
return 'rgb(62, 62, 62)'; // bright colors - black font
|
||||
} else {
|
||||
return 'rgb(234, 234, 234)';// dark colors - white font
|
||||
return 'rgb(234, 234, 234)'; // dark colors - white font
|
||||
}
|
||||
}
|
||||
|
||||
export function getLightColorBasedOnTemperature(
|
||||
current: number,
|
||||
min: number,
|
||||
max: number,
|
||||
): string {
|
||||
export function getLightColorBasedOnTemperature(current: number, min: number, max: number): string {
|
||||
const high = new TinyColor('rgb(255, 160, 0)'); // orange-ish
|
||||
const low = new TinyColor('rgb(166, 209, 255)'); // blue-ish
|
||||
const middle = new TinyColor('white');
|
||||
const mixAmount = (current - min) / (max - min) * 100;
|
||||
const mixAmount = ((current - min) / (max - min)) * 100;
|
||||
if (mixAmount < 50) {
|
||||
return tinycolor(low).mix(middle, mixAmount * 2).toRgbString();
|
||||
return tinycolor(low)
|
||||
.mix(middle, mixAmount * 2)
|
||||
.toRgbString();
|
||||
} else {
|
||||
return tinycolor(middle).mix(high, (mixAmount - 50) * 2).toRgbString();
|
||||
return tinycolor(middle)
|
||||
.mix(high, (mixAmount - 50) * 2)
|
||||
.toRgbString();
|
||||
}
|
||||
}
|
||||
|
||||
export function buildNameStateConcat(
|
||||
name: string | undefined, stateString: string | undefined,
|
||||
): string | undefined {
|
||||
export function buildNameStateConcat(name: string | undefined, stateString: string | undefined): string | undefined {
|
||||
if (!name && !stateString) {
|
||||
return undefined;
|
||||
}
|
||||
|
@ -63,9 +63,7 @@ export function buildNameStateConcat(
|
|||
return nameStateString;
|
||||
}
|
||||
|
||||
export function applyBrightnessToColor(
|
||||
color: string, brightness: number,
|
||||
): string {
|
||||
export function applyBrightnessToColor(color: string, brightness: number): string {
|
||||
const colorObj = new TinyColor(getColorFromVariable(color));
|
||||
if (colorObj.isValid) {
|
||||
const validColor = colorObj.mix('black', 100 - brightness).toString();
|
||||
|
@ -75,10 +73,7 @@ export function applyBrightnessToColor(
|
|||
}
|
||||
|
||||
// Check if config or Entity changed
|
||||
export function myHasConfigOrEntityChanged(
|
||||
element: any,
|
||||
changedProps: PropertyValues,
|
||||
): boolean {
|
||||
export function myHasConfigOrEntityChanged(element: any, changedProps: PropertyValues): boolean {
|
||||
if (changedProps.has('config')) {
|
||||
return true;
|
||||
}
|
||||
|
@ -87,10 +82,8 @@ export function myHasConfigOrEntityChanged(
|
|||
const oldHass = changedProps.get('hass') as HomeAssistant | undefined;
|
||||
if (oldHass) {
|
||||
return (
|
||||
oldHass.states[element.config!.entity]
|
||||
!== element.hass!.states[element.config!.entity]
|
||||
|| oldHass.states[element.config!.entity].attributes
|
||||
!== element.hass!.states[element.config!.entity].attributes
|
||||
oldHass.states[element.config!.entity] !== element.hass!.states[element.config!.entity] ||
|
||||
oldHass.states[element.config!.entity].attributes !== element.hass!.states[element.config!.entity].attributes
|
||||
);
|
||||
}
|
||||
return true;
|
||||
|
@ -100,17 +93,17 @@ export function myHasConfigOrEntityChanged(
|
|||
}
|
||||
|
||||
/**
|
||||
* Performs a deep merge of objects and returns new object. Does not modify
|
||||
* objects (immutable) and merges arrays via concatenation and filtering.
|
||||
*
|
||||
* @param {...object} objects - Objects to merge
|
||||
* @returns {object} New object with merged key/values
|
||||
*/
|
||||
* Performs a deep merge of objects and returns new object. Does not modify
|
||||
* objects (immutable) and merges arrays via concatenation and filtering.
|
||||
*
|
||||
* @param {...object} objects - Objects to merge
|
||||
* @returns {object} New object with merged key/values
|
||||
*/
|
||||
export function mergeDeep(...objects: any): any {
|
||||
const isObject = (obj: any) => obj && typeof obj === 'object';
|
||||
|
||||
return objects.reduce((prev: any, obj: any) => {
|
||||
Object.keys(obj).forEach((key) => {
|
||||
Object.keys(obj).forEach(key => {
|
||||
const pVal = prev[key];
|
||||
const oVal = obj[key];
|
||||
|
||||
|
@ -134,10 +127,10 @@ export function mergeStatesById(
|
|||
): StateConfig[] {
|
||||
let resultStateConfigs: StateConfig[] = [];
|
||||
if (intoStates) {
|
||||
intoStates.forEach((intoState) => {
|
||||
intoStates.forEach(intoState => {
|
||||
let localState = intoState;
|
||||
if (fromStates) {
|
||||
fromStates.forEach((fromState) => {
|
||||
fromStates.forEach(fromState => {
|
||||
if (fromState.id && intoState.id && fromState.id == intoState.id)
|
||||
localState = mergeDeep(localState, fromState);
|
||||
});
|
||||
|
@ -148,17 +141,13 @@ export function mergeStatesById(
|
|||
if (fromStates) {
|
||||
/* eslint eqeqeq: 0 no-confusing-arrow: 0 */
|
||||
resultStateConfigs = resultStateConfigs.concat(
|
||||
fromStates.filter(
|
||||
x => !intoStates
|
||||
? true
|
||||
: !intoStates.find(y => y.id && x.id ? y.id == x.id : false),
|
||||
),
|
||||
fromStates.filter(x => (!intoStates ? true : !intoStates.find(y => (y.id && x.id ? y.id == x.id : false)))),
|
||||
);
|
||||
}
|
||||
return resultStateConfigs;
|
||||
}
|
||||
|
||||
export function getLovelaceCast() {
|
||||
export function getLovelaceCast(): any {
|
||||
let root: any = document.querySelector('hc-main');
|
||||
root = root && root.shadowRoot;
|
||||
root = root && root.querySelector('hc-lovelace');
|
||||
|
|
|
@ -29,7 +29,7 @@
|
|||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
declare global {
|
||||
// tslint:disable-next-line
|
||||
// eslint-disable-next-line
|
||||
interface HASSDomEvents { }
|
||||
}
|
||||
|
||||
|
@ -62,10 +62,9 @@ export const myFireEvent = <HassEvent extends ValidHassDomEvent>(
|
|||
bubbles?: boolean;
|
||||
cancelable?: boolean;
|
||||
composed?: boolean;
|
||||
}
|
||||
) => {
|
||||
},
|
||||
): any => {
|
||||
options = options || {};
|
||||
// @ts-ignore
|
||||
detail = detail === null || detail === undefined ? {} : detail;
|
||||
const event = new Event(type, {
|
||||
bubbles: options.bubbles === undefined ? true : options.bubbles,
|
||||
|
|
|
@ -73,9 +73,15 @@ export const styles = css`
|
|||
transition: visibility 0s 1s, opacity 1s linear;
|
||||
}
|
||||
@keyframes blink {
|
||||
0%{opacity:0;}
|
||||
50%{opacity:1;}
|
||||
100%{opacity:0;}
|
||||
0% {
|
||||
opacity: 0;
|
||||
}
|
||||
50% {
|
||||
opacity: 1;
|
||||
}
|
||||
100% {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
@-webkit-keyframes rotating /* Safari and Chrome */ {
|
||||
from {
|
||||
|
@ -170,13 +176,13 @@ export const styles = css`
|
|||
}
|
||||
|
||||
#container.vertical {
|
||||
grid-template-areas: "i" "n" "s" "l";
|
||||
grid-template-areas: 'i' 'n' 's' 'l';
|
||||
grid-template-columns: 1fr;
|
||||
grid-template-rows: 1fr min-content min-content min-content;
|
||||
}
|
||||
/* Vertical No Icon */
|
||||
#container.vertical.no-icon {
|
||||
grid-template-areas: "n" "s" "l";
|
||||
grid-template-areas: 'n' 's' 'l';
|
||||
grid-template-columns: 1fr;
|
||||
grid-template-rows: 1fr min-content 1fr;
|
||||
}
|
||||
|
@ -192,7 +198,7 @@ export const styles = css`
|
|||
|
||||
/* Vertical No Icon No Name */
|
||||
#container.vertical.no-icon.no-name {
|
||||
grid-template-areas: "s" "l";
|
||||
grid-template-areas: 's' 'l';
|
||||
grid-template-columns: 1fr;
|
||||
grid-template-rows: 1fr 1fr;
|
||||
}
|
||||
|
@ -205,7 +211,7 @@ export const styles = css`
|
|||
|
||||
/* Vertical No Icon No State */
|
||||
#container.vertical.no-icon.no-state {
|
||||
grid-template-areas: "n" "l";
|
||||
grid-template-areas: 'n' 'l';
|
||||
grid-template-columns: 1fr;
|
||||
grid-template-rows: 1fr 1fr;
|
||||
}
|
||||
|
@ -218,7 +224,7 @@ export const styles = css`
|
|||
|
||||
/* Vertical No Icon No Label */
|
||||
#container.vertical.no-icon.no-label {
|
||||
grid-template-areas: "n" "s";
|
||||
grid-template-areas: 'n' 's';
|
||||
grid-template-columns: 1fr;
|
||||
grid-template-rows: 1fr 1fr;
|
||||
}
|
||||
|
@ -231,7 +237,7 @@ export const styles = css`
|
|||
|
||||
/* Vertical No Icon No Label No Name */
|
||||
#container.vertical.no-icon.no-label.no-name {
|
||||
grid-template-areas: "s";
|
||||
grid-template-areas: 's';
|
||||
grid-template-columns: 1fr;
|
||||
grid-template-rows: 1fr;
|
||||
}
|
||||
|
@ -240,7 +246,7 @@ export const styles = css`
|
|||
}
|
||||
/* Vertical No Icon No Label No State */
|
||||
#container.vertical.no-icon.no-label.no-state {
|
||||
grid-template-areas: "n";
|
||||
grid-template-areas: 'n';
|
||||
grid-template-columns: 1fr;
|
||||
grid-template-rows: 1fr;
|
||||
}
|
||||
|
@ -250,7 +256,7 @@ export const styles = css`
|
|||
|
||||
/* Vertical No Icon No Name No State */
|
||||
#container.vertical.no-icon.no-name.no-state {
|
||||
grid-template-areas: "l";
|
||||
grid-template-areas: 'l';
|
||||
grid-template-columns: 1fr;
|
||||
grid-template-rows: 1fr;
|
||||
}
|
||||
|
@ -259,52 +265,52 @@ export const styles = css`
|
|||
}
|
||||
|
||||
#container.icon_name_state {
|
||||
grid-template-areas: "i n" "l l";
|
||||
grid-template-areas: 'i n' 'l l';
|
||||
grid-template-columns: 40% 1fr;
|
||||
grid-template-rows: 1fr min-content;
|
||||
}
|
||||
|
||||
#container.icon_name {
|
||||
grid-template-areas: "i n" "s s" "l l";
|
||||
grid-template-areas: 'i n' 's s' 'l l';
|
||||
grid-template-columns: 40% 1fr;
|
||||
grid-template-rows: 1fr min-content min-content;
|
||||
}
|
||||
|
||||
#container.icon_state {
|
||||
grid-template-areas: "i s" "n n" "l l";
|
||||
grid-template-areas: 'i s' 'n n' 'l l';
|
||||
grid-template-columns: 40% 1fr;
|
||||
grid-template-rows: 1fr min-content min-content;
|
||||
}
|
||||
|
||||
#container.name_state {
|
||||
grid-template-areas: "i" "n" "l";
|
||||
grid-template-areas: 'i' 'n' 'l';
|
||||
grid-template-columns: 1fr;
|
||||
grid-template-rows: 1fr min-content min-content;
|
||||
}
|
||||
#container.name_state.no-icon {
|
||||
grid-template-areas: "n" "l";
|
||||
grid-template-areas: 'n' 'l';
|
||||
grid-template-columns: 1fr;
|
||||
grid-template-rows: 1fr 1fr;
|
||||
}
|
||||
#container.name_state.no-icon #name {
|
||||
align-self: end
|
||||
align-self: end;
|
||||
}
|
||||
#container.name_state.no-icon #label {
|
||||
align-self: start
|
||||
align-self: start;
|
||||
}
|
||||
|
||||
#container.name_state.no-icon.no-label {
|
||||
grid-template-areas: "n";
|
||||
grid-template-areas: 'n';
|
||||
grid-template-columns: 1fr;
|
||||
grid-template-rows: 1fr;
|
||||
}
|
||||
#container.name_state.no-icon.no-label #name {
|
||||
align-self: center
|
||||
align-self: center;
|
||||
}
|
||||
|
||||
/* icon_name_state2nd default */
|
||||
#container.icon_name_state2nd {
|
||||
grid-template-areas: "i n" "i s" "i l";
|
||||
grid-template-areas: 'i n' 'i s' 'i l';
|
||||
grid-template-columns: 40% 1fr;
|
||||
grid-template-rows: 1fr min-content 1fr;
|
||||
}
|
||||
|
@ -320,7 +326,7 @@ export const styles = css`
|
|||
|
||||
/* icon_name_state2nd No Label */
|
||||
#container.icon_name_state2nd.no-label {
|
||||
grid-template-areas: "i n" "i s";
|
||||
grid-template-areas: 'i n' 'i s';
|
||||
grid-template-columns: 40% 1fr;
|
||||
grid-template-rows: 1fr 1fr;
|
||||
}
|
||||
|
@ -333,7 +339,7 @@ export const styles = css`
|
|||
|
||||
/* icon_state_name2nd Default */
|
||||
#container.icon_state_name2nd {
|
||||
grid-template-areas: "i s" "i n" "i l";
|
||||
grid-template-areas: 'i s' 'i n' 'i l';
|
||||
grid-template-columns: 40% 1fr;
|
||||
grid-template-rows: 1fr min-content 1fr;
|
||||
}
|
||||
|
@ -349,7 +355,7 @@ export const styles = css`
|
|||
|
||||
/* icon_state_name2nd No Label */
|
||||
#container.icon_state_name2nd.no-label {
|
||||
grid-template-areas: "i s" "i n";
|
||||
grid-template-areas: 'i s' 'i n';
|
||||
grid-template-columns: 40% 1fr;
|
||||
grid-template-rows: 1fr 1fr;
|
||||
}
|
||||
|
@ -361,27 +367,27 @@ export const styles = css`
|
|||
}
|
||||
|
||||
#container.icon_label {
|
||||
grid-template-areas: "i l" "n n" "s s";
|
||||
grid-template-areas: 'i l' 'n n' 's s';
|
||||
grid-template-columns: 40% 1fr;
|
||||
grid-template-rows: 1fr min-content min-content;
|
||||
}
|
||||
|
||||
[style*="--aspect-ratio"] > :first-child {
|
||||
[style*='--aspect-ratio'] > :first-child {
|
||||
width: 100%;
|
||||
}
|
||||
[style*="--aspect-ratio"] > img {
|
||||
[style*='--aspect-ratio'] > img {
|
||||
height: auto;
|
||||
}
|
||||
@supports (--custom:property) {
|
||||
[style*="--aspect-ratio"] {
|
||||
@supports (--custom: property) {
|
||||
[style*='--aspect-ratio'] {
|
||||
position: relative;
|
||||
}
|
||||
[style*="--aspect-ratio"]::before {
|
||||
content: "";
|
||||
[style*='--aspect-ratio']::before {
|
||||
content: '';
|
||||
display: block;
|
||||
padding-bottom: calc(100% / (var(--aspect-ratio)));
|
||||
}
|
||||
[style*="--aspect-ratio"] > :first-child {
|
||||
[style*='--aspect-ratio'] > :first-child {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
|
|
|
@ -39,7 +39,8 @@ export interface ButtonCardConfig {
|
|||
extra_styles?: string;
|
||||
}
|
||||
|
||||
export type Layout = 'vertical'
|
||||
export type Layout =
|
||||
| 'vertical'
|
||||
| 'icon_name_state'
|
||||
| 'name_state'
|
||||
| 'icon_name'
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
export const BUTTON_CARD_VERSION = '3.2.3';
|
Loading…
Reference in New Issue