Add a nice locking mechanism (#139)
* Add a nice locking mecanism * Update documentation * Fix typo * Remove useless styles * Fix for touch devices * Disable ripple effect while locked * fix blank card height * Show last changed instead of label * Support for light temperature with color auto * Style for last_changed * Update documentation
This commit is contained in:
parent
364580a281
commit
880e7e37e1
34
README.md
34
README.md
|
@ -59,13 +59,15 @@ Lovelace Button card for your entities.
|
|||
| `show_state` | boolean | `false` | `true` \| `false` | Show the state on the card. defaults to false if not set |
|
||||
| `show_icon` | boolean | `true` | `true` \| `false` | Wether to show the icon or not. Unless redefined in `icon`, uses the default entity icon from hass |
|
||||
| `show_units` | boolean | `true` | `true` \| `false` | Display or hide the units of a sensor, if any. |
|
||||
| `show_label` | boolean | `false` | `true` \| `false` | Display or hide the `label`/`label_template`
|
||||
| `show_label` | boolean | `false` | `true` \| `false` | Display or hide the `label`/`label_template` |
|
||||
| `show_last_changed` | boolean | `false` | `true` \| `false` | Replace the label altogether and display the the `last_changed` attribute in a nice way (eg: `12 minutes ago`) |
|
||||
| `show_entity_picture` | boolean | `false` | `true` \| `false` | Replace the icon by the entity picture (if any) or the custom picture (if any). Falls back to using the icon if both are undefined |
|
||||
| `entity_picture` | string | optional | Can be any of `/local/*` file or a URL | Will override the icon/the default entity_picture with your own image. Best is to use a square image. You can also define one per state |
|
||||
| `units` | string | optional | `Kb/s`, `lux`, ... | Override or define the units to display after the state of the entity. If omitted, it's using the entity's units |
|
||||
| `styles` | object list | optional | | See [styles](#styles) |
|
||||
| `state` | object list | optional | See [State](#State) | State to use for the color, icon and style of the button. Multiple states can be defined |
|
||||
| `confirmation` | string | optional | Free-form text | Show a confirmation popup on tap with defined text |
|
||||
| `lock` | boolean | `false` | `true` \| `false` | See [lock](#lock). This will display a normal button with a lock symbol in the corner. Clicking the button will make the lock go away and enable the button to be manoeuvred for five seconds |
|
||||
| `layout` | string | optional | See [Layout](#Layout) | The layout of the button can be modified using this option |
|
||||
|
||||
### Action
|
||||
|
@ -131,7 +133,18 @@ Multiple values are possible, see the image below for examples:
|
|||
### Templates
|
||||
|
||||
`label_template` supports templating as well as `value` for `state` when `operator: template`
|
||||
* `label_template`: It will be interpreted as javascript code and the code should return a string
|
||||
* `label_template`: It will be interpreted as javascript code and the code should return a string.
|
||||
`label_template` supports inline HTML, so you can do stuff like:
|
||||
```yaml
|
||||
label_template: >
|
||||
return 'Connection: '
|
||||
+ (states['switch.connection'].state === 'on'
|
||||
? '<span style="color: #00FF00;">enabled</span>'
|
||||
: '<span style="color: #FF0000;">disabled</span>')
|
||||
+ ' / '
|
||||
+ (states['binary_sensor.status'].state === 'on' ? 'connected' : 'disconnected')
|
||||
```
|
||||
![label-template-example](examples/label_template.png)
|
||||
* `value` for `state` when `operator: template`: It will be interpreted as javascript code and the code should return a boolean (`true` or `false`)
|
||||
|
||||
Inside the javascript code, you'll have access to those variables:
|
||||
|
@ -699,6 +712,23 @@ Example with `template`:
|
|||
- color: green
|
||||
```
|
||||
|
||||
### Lock
|
||||
|
||||
![lock-animation](examples/lock.gif)
|
||||
|
||||
```yaml
|
||||
- type: horizontal-stack
|
||||
cards:
|
||||
- type: "custom:button-card"
|
||||
entity: switch.test
|
||||
lock: true
|
||||
- type: "custom:button-card"
|
||||
color_type: card
|
||||
lock: true
|
||||
color: black
|
||||
entity: switch.test
|
||||
```
|
||||
|
||||
## Credits
|
||||
|
||||
- [ciotlosm](https://github.com/ciotlosm) for the readme template and the awesome examples
|
||||
|
|
|
@ -2353,6 +2353,47 @@ const styleMap = directive(styleInfo => part => {
|
|||
styleMapCache.set(part, styleInfo);
|
||||
});
|
||||
|
||||
/**
|
||||
* @license
|
||||
* Copyright (c) 2017 The Polymer Project Authors. All rights reserved.
|
||||
* This code may only be used under the BSD style license found at
|
||||
* http://polymer.github.io/LICENSE.txt
|
||||
* The complete set of authors may be found at
|
||||
* http://polymer.github.io/AUTHORS.txt
|
||||
* The complete set of contributors may be found at
|
||||
* http://polymer.github.io/CONTRIBUTORS.txt
|
||||
* Code distributed by Google as part of the polymer project is also
|
||||
* subject to an additional IP rights grant found at
|
||||
* http://polymer.github.io/PATENTS.txt
|
||||
*/
|
||||
// For each part, remember the value that was last rendered to the part by the
|
||||
// unsafeHTML directive, and the DocumentFragment that was last set as a value.
|
||||
// The DocumentFragment is used as a unique key to check if the last value
|
||||
// rendered to the part was with unsafeHTML. If not, we'll always re-render the
|
||||
// value passed to unsafeHTML.
|
||||
const previousValues = new WeakMap();
|
||||
/**
|
||||
* Renders the result as HTML, rather than text.
|
||||
*
|
||||
* Note, this is unsafe to use with any user-provided input that hasn't been
|
||||
* sanitized or escaped, as it may lead to cross-site-scripting
|
||||
* vulnerabilities.
|
||||
*/
|
||||
const unsafeHTML = directive(value => part => {
|
||||
if (!(part instanceof NodePart)) {
|
||||
throw new Error('unsafeHTML can only be used in text bindings');
|
||||
}
|
||||
const previousValue = previousValues.get(part);
|
||||
if (previousValue !== undefined && isPrimitive(value) && value === previousValue.value && part.value === previousValue.fragment) {
|
||||
return;
|
||||
}
|
||||
const template = document.createElement('template');
|
||||
template.innerHTML = value; // innerHTML casts to string internally
|
||||
const fragment = document.importNode(template.content, true);
|
||||
part.setValue(fragment);
|
||||
previousValues.set(part, { value, fragment });
|
||||
});
|
||||
|
||||
/** Constants to be used in the frontend. */
|
||||
// Constants should be alphabetically sorted by name.
|
||||
// Arrays with values should be alphabetically sorted if order doesn't matter.
|
||||
|
@ -3290,6 +3331,15 @@ var TinyColor = function () {
|
|||
};
|
||||
return TinyColor;
|
||||
}();
|
||||
function tinycolor(color, opts) {
|
||||
if (color === void 0) {
|
||||
color = '';
|
||||
}
|
||||
if (opts === void 0) {
|
||||
opts = {};
|
||||
}
|
||||
return new TinyColor(color, opts);
|
||||
}
|
||||
|
||||
function computeDomain(entityId) {
|
||||
return entityId.substr(0, entityId.indexOf('.'));
|
||||
|
@ -3311,6 +3361,17 @@ function getFontColorBasedOnBackgroundColor(backgroundColor) {
|
|||
return 'rgb(234, 234, 234)'; // dark colors - white font
|
||||
}
|
||||
}
|
||||
function getLightColorBasedOnTemperature(current, min, max) {
|
||||
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;
|
||||
if (mixAmount < 50) {
|
||||
return tinycolor(low).mix(middle, mixAmount * 2).toRgbString();
|
||||
} else {
|
||||
return tinycolor(middle).mix(high, (mixAmount - 50) * 2).toRgbString();
|
||||
}
|
||||
}
|
||||
function buildNameStateConcat(name, stateString) {
|
||||
if (!name && !stateString) {
|
||||
return undefined;
|
||||
|
@ -3637,6 +3698,7 @@ const styles = css`
|
|||
cursor: pointer;
|
||||
overflow: hidden;
|
||||
box-sizing: border-box;
|
||||
position: relative;
|
||||
}
|
||||
ha-card.disabled {
|
||||
pointer-events: none;
|
||||
|
@ -3661,6 +3723,34 @@ const styles = css`
|
|||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
}
|
||||
#overlay {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
text-align: right;
|
||||
z-index: 1;
|
||||
}
|
||||
#lock {
|
||||
margin-top: 8px;
|
||||
opacity: 0.5;
|
||||
margin-right: 7px;
|
||||
-webkit-animation-duration: 5s;
|
||||
animation-duration: 5s;
|
||||
-webkit-animation-fill-mode: both;
|
||||
animation-fill-mode: both;
|
||||
}
|
||||
@keyframes fadeOut{
|
||||
0% {opacity: 0.5;}
|
||||
20% {opacity: 0;}
|
||||
80% {opacity: 0;}
|
||||
100% {opacity: 0.5;}
|
||||
}
|
||||
.fadeOut {
|
||||
-webkit-animation-name: fadeOut;
|
||||
animation-name: fadeOut;
|
||||
}
|
||||
@keyframes blink{
|
||||
0%{opacity:0;}
|
||||
50%{opacity:1;}
|
||||
|
@ -4031,6 +4121,11 @@ let ButtonCard = class ButtonCard extends LitElement {
|
|||
if (state.attributes.brightness) {
|
||||
color = applyBrightnessToColor(color, (state.attributes.brightness + 245) / 5);
|
||||
}
|
||||
} else if (state.attributes.color_temp && state.attributes.min_mireds && state.attributes.max_mireds) {
|
||||
color = getLightColorBasedOnTemperature(state.attributes.color_temp, state.attributes.min_mireds, state.attributes.max_mireds);
|
||||
if (state.attributes.brightness) {
|
||||
color = applyBrightnessToColor(color, (state.attributes.brightness + 245) / 5);
|
||||
}
|
||||
} else if (state.attributes.brightness) {
|
||||
color = applyBrightnessToColor(this._getDefaultColorForState(state), (state.attributes.brightness + 245) / 5);
|
||||
} else {
|
||||
|
@ -4127,6 +4222,9 @@ let ButtonCard = class ButtonCard extends LitElement {
|
|||
}
|
||||
return units;
|
||||
}
|
||||
_buildLastChanged(state, style) {
|
||||
return state ? html`<ha-relative-time .hass="${this.hass}" .datetime="${state.last_changed}" class="label" style=${styleMap(style)}></ha-relative-time>` : html``;
|
||||
}
|
||||
_buildLabel(state, configState) {
|
||||
if (!this.config.show_label) {
|
||||
return undefined;
|
||||
|
@ -4176,12 +4274,11 @@ let ButtonCard = class ButtonCard extends LitElement {
|
|||
_rotate(configState) {
|
||||
return configState && configState.spin ? true : false;
|
||||
}
|
||||
_blankCardColoredHtml(state, cardStyle) {
|
||||
const color = this._buildCssColorAttribute(state, undefined);
|
||||
const fontColor = getFontColorBasedOnBackgroundColor(color);
|
||||
_blankCardColoredHtml(cardStyle) {
|
||||
const blankCardStyle = Object.assign({ background: 'none', 'box-shadow': 'none' }, cardStyle);
|
||||
return html`
|
||||
<ha-card class="disabled" style=${styleMap(cardStyle)}>
|
||||
<div style="color: ${fontColor}; background-color: ${color};"></div>
|
||||
<ha-card class="disabled" style=${styleMap(blankCardStyle)}>
|
||||
<div></div>
|
||||
</ha-card>
|
||||
`;
|
||||
}
|
||||
|
@ -4191,6 +4288,7 @@ let ButtonCard = class ButtonCard extends LitElement {
|
|||
const color = this._buildCssColorAttribute(state, configState);
|
||||
let buttonColor = color;
|
||||
let cardStyle = {};
|
||||
const lockStyle = {};
|
||||
const configCardStyle = this._buildStyleGeneric(configState, 'card');
|
||||
if (configCardStyle.width) {
|
||||
this.style.setProperty('flex', '0 0 auto');
|
||||
|
@ -4198,12 +4296,13 @@ let ButtonCard = class ButtonCard extends LitElement {
|
|||
}
|
||||
switch (this.config.color_type) {
|
||||
case 'blank-card':
|
||||
return this._blankCardColoredHtml(state, configCardStyle);
|
||||
return this._blankCardColoredHtml(configCardStyle);
|
||||
case 'card':
|
||||
case 'label-card':
|
||||
{
|
||||
const fontColor = getFontColorBasedOnBackgroundColor(color);
|
||||
cardStyle.color = fontColor;
|
||||
lockStyle.color = fontColor;
|
||||
cardStyle['background-color'] = color;
|
||||
cardStyle = Object.assign({}, cardStyle, configCardStyle);
|
||||
buttonColor = 'inherit';
|
||||
|
@ -4215,11 +4314,22 @@ let ButtonCard = class ButtonCard extends LitElement {
|
|||
}
|
||||
return html`
|
||||
<ha-card class="button-card-main ${this._isClickable(state) ? '' : 'disabled'}" style=${styleMap(cardStyle)} @ha-click="${this._handleTap}" @ha-hold="${this._handleHold}" .longpress="${longPress()}" .config="${this.config}">
|
||||
${this._getLock(lockStyle)}
|
||||
${this._buttonContent(state, configState, buttonColor)}
|
||||
<mwc-ripple></mwc-ripple>
|
||||
${this.config.lock ? '' : html`<paper-ripple id="ripple"></paper-ripple>`}
|
||||
</ha-card>
|
||||
`;
|
||||
}
|
||||
_getLock(lockStyle) {
|
||||
if (this.config.lock) {
|
||||
return html`
|
||||
<div id="overlay" style=${styleMap(lockStyle)} @click=${this._handleLock} @touchstart=${this._handleLock}>
|
||||
<ha-icon id="lock" icon="mdi:lock-outline"></iron-icon>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
return html``;
|
||||
}
|
||||
_buttonContent(state, configState, color) {
|
||||
const name = this._buildName(state, configState);
|
||||
const stateString = this._buildStateString(state);
|
||||
|
@ -4239,6 +4349,7 @@ let ButtonCard = class ButtonCard extends LitElement {
|
|||
const nameStyleFromConfig = this._buildStyleGeneric(configState, 'name');
|
||||
const stateStyleFromConfig = this._buildStyleGeneric(configState, 'state');
|
||||
const labelStyleFromConfig = this._buildStyleGeneric(configState, 'label');
|
||||
const lastChangedTemplate = this._buildLastChanged(state, labelStyleFromConfig);
|
||||
if (!iconTemplate) itemClass.push('no-icon');
|
||||
if (!name) itemClass.push('no-name');
|
||||
if (!stateString) itemClass.push('no-state');
|
||||
|
@ -4248,7 +4359,8 @@ let ButtonCard = class ButtonCard extends LitElement {
|
|||
${iconTemplate ? iconTemplate : ''}
|
||||
${name ? html`<div class="name" style=${styleMap(nameStyleFromConfig)}>${name}</div>` : ''}
|
||||
${stateString ? html`<div class="state" style=${styleMap(stateStyleFromConfig)}>${stateString}</div>` : ''}
|
||||
${label ? html`<div class="label" style=${styleMap(labelStyleFromConfig)}>${label}</div>` : ''}
|
||||
${label && !this.config.show_last_changed ? html`<div class="label" style=${styleMap(labelStyleFromConfig)}>${unsafeHTML(label)}</div>` : ''}
|
||||
${this.config.show_last_changed ? lastChangedTemplate : ''}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
@ -4330,6 +4442,31 @@ let ButtonCard = class ButtonCard extends LitElement {
|
|||
const config = ev.target.config;
|
||||
handleClick(this, this.hass, config, true);
|
||||
}
|
||||
_handleLock(ev) {
|
||||
ev.stopPropagation();
|
||||
const overlay = this.shadowRoot.getElementById('overlay');
|
||||
const haCard = this.shadowRoot.firstElementChild;
|
||||
overlay.style.setProperty('pointer-events', 'none');
|
||||
const paperRipple = document.createElement('paper-ripple');
|
||||
const lock = this.shadowRoot.getElementById('lock');
|
||||
if (lock) {
|
||||
haCard.appendChild(paperRipple);
|
||||
const icon = document.createAttribute('icon');
|
||||
icon.value = 'mdi:lock-open-outline';
|
||||
lock.attributes.setNamedItem(icon);
|
||||
lock.classList.add('fadeOut');
|
||||
}
|
||||
window.setTimeout(() => {
|
||||
overlay.style.setProperty('pointer-events', '');
|
||||
if (lock) {
|
||||
lock.classList.remove('fadeOut');
|
||||
const icon = document.createAttribute('icon');
|
||||
icon.value = 'mdi:lock-outline';
|
||||
lock.attributes.setNamedItem(icon);
|
||||
haCard.removeChild(paperRipple);
|
||||
}
|
||||
}, 5000);
|
||||
}
|
||||
};
|
||||
__decorate([property()], ButtonCard.prototype, "hass", void 0);
|
||||
__decorate([property()], ButtonCard.prototype, "config", void 0);
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 8.3 KiB |
Binary file not shown.
After Width: | Height: | Size: 757 KiB |
|
@ -8,6 +8,7 @@ import {
|
|||
PropertyValues,
|
||||
} from 'lit-element';
|
||||
import { styleMap, StyleInfo } from 'lit-html/directives/style-map';
|
||||
import { unsafeHTML } from 'lit-html/directives/unsafe-html';
|
||||
import {
|
||||
HassEntity,
|
||||
} from 'home-assistant-js-websocket';
|
||||
|
@ -25,6 +26,7 @@ import {
|
|||
buildNameStateConcat,
|
||||
applyBrightnessToColor,
|
||||
hasConfigOrEntityChanged,
|
||||
getLightColorBasedOnTemperature,
|
||||
} from './helpers';
|
||||
import { handleClick } from './handle-click';
|
||||
import { longPress } from './long-press';
|
||||
|
@ -138,6 +140,17 @@ class ButtonCard extends LitElement {
|
|||
if (state.attributes.brightness) {
|
||||
color = applyBrightnessToColor(color, (state.attributes.brightness + 245) / 5);
|
||||
}
|
||||
} else if (state.attributes.color_temp
|
||||
&& state.attributes.min_mireds
|
||||
&& state.attributes.max_mireds) {
|
||||
color = getLightColorBasedOnTemperature(
|
||||
state.attributes.color_temp,
|
||||
state.attributes.min_mireds,
|
||||
state.attributes.max_mireds,
|
||||
);
|
||||
if (state.attributes.brightness) {
|
||||
color = applyBrightnessToColor(color, (state.attributes.brightness + 245) / 5);
|
||||
}
|
||||
} else if (state.attributes.brightness) {
|
||||
color = applyBrightnessToColor(
|
||||
this._getDefaultColorForState(state), (state.attributes.brightness + 245) / 5,
|
||||
|
@ -260,6 +273,13 @@ class ButtonCard extends LitElement {
|
|||
return units;
|
||||
}
|
||||
|
||||
private _buildLastChanged(
|
||||
state: HassEntity | undefined,
|
||||
style: StyleInfo,
|
||||
): TemplateResult {
|
||||
return state ? html`<ha-relative-time .hass="${this.hass}" .datetime="${state.last_changed}" class="label" style=${styleMap(style)}></ha-relative-time>` : html``;
|
||||
}
|
||||
|
||||
private _buildLabel(
|
||||
state: HassEntity | undefined,
|
||||
configState: StateConfig | undefined,
|
||||
|
@ -322,14 +342,16 @@ class ButtonCard extends LitElement {
|
|||
}
|
||||
|
||||
private _blankCardColoredHtml(
|
||||
state: HassEntity | undefined,
|
||||
cardStyle: StyleInfo,
|
||||
): TemplateResult {
|
||||
const color = this._buildCssColorAttribute(state, undefined);
|
||||
const fontColor = getFontColorBasedOnBackgroundColor(color);
|
||||
const blankCardStyle = {
|
||||
background: 'none',
|
||||
'box-shadow': 'none',
|
||||
...cardStyle,
|
||||
};
|
||||
return html`
|
||||
<ha-card class="disabled" style=${styleMap(cardStyle)}>
|
||||
<div style="color: ${fontColor}; background-color: ${color};"></div>
|
||||
<ha-card class="disabled" style=${styleMap(blankCardStyle)}>
|
||||
<div></div>
|
||||
</ha-card>
|
||||
`;
|
||||
}
|
||||
|
@ -340,6 +362,7 @@ class ButtonCard extends LitElement {
|
|||
const color = this._buildCssColorAttribute(state, configState);
|
||||
let buttonColor = color;
|
||||
let cardStyle: StyleInfo = {};
|
||||
const lockStyle: StyleInfo = {};
|
||||
const configCardStyle = this._buildStyleGeneric(configState, 'card');
|
||||
|
||||
if (configCardStyle.width) {
|
||||
|
@ -348,11 +371,12 @@ class ButtonCard extends LitElement {
|
|||
}
|
||||
switch (this.config!.color_type) {
|
||||
case 'blank-card':
|
||||
return this._blankCardColoredHtml(state, configCardStyle);
|
||||
return this._blankCardColoredHtml(configCardStyle);
|
||||
case 'card':
|
||||
case 'label-card': {
|
||||
const fontColor = getFontColorBasedOnBackgroundColor(color);
|
||||
cardStyle.color = fontColor;
|
||||
lockStyle.color = fontColor;
|
||||
cardStyle['background-color'] = color;
|
||||
cardStyle = { ...cardStyle, ...configCardStyle };
|
||||
buttonColor = 'inherit';
|
||||
|
@ -365,12 +389,24 @@ class ButtonCard extends LitElement {
|
|||
|
||||
return html`
|
||||
<ha-card class="button-card-main ${this._isClickable(state) ? '' : 'disabled'}" style=${styleMap(cardStyle)} @ha-click="${this._handleTap}" @ha-hold="${this._handleHold}" .longpress="${longPress()}" .config="${this.config}">
|
||||
${this._getLock(lockStyle)}
|
||||
${this._buttonContent(state, configState, buttonColor)}
|
||||
<mwc-ripple></mwc-ripple>
|
||||
${this.config!.lock ? '' : html`<paper-ripple id="ripple"></paper-ripple>`}
|
||||
</ha-card>
|
||||
`;
|
||||
}
|
||||
|
||||
private _getLock(lockStyle: StyleInfo): TemplateResult {
|
||||
if (this.config!.lock) {
|
||||
return html`
|
||||
<div id="overlay" style=${styleMap(lockStyle)} @click=${this._handleLock} @touchstart=${this._handleLock}>
|
||||
<ha-icon id="lock" icon="mdi:lock-outline"></iron-icon>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
return html``;
|
||||
}
|
||||
|
||||
private _buttonContent(
|
||||
state: HassEntity | undefined,
|
||||
configState: StateConfig | undefined,
|
||||
|
@ -405,6 +441,7 @@ class ButtonCard extends LitElement {
|
|||
const nameStyleFromConfig = this._buildStyleGeneric(configState, 'name');
|
||||
const stateStyleFromConfig = this._buildStyleGeneric(configState, 'state');
|
||||
const labelStyleFromConfig = this._buildStyleGeneric(configState, 'label');
|
||||
const lastChangedTemplate = this._buildLastChanged(state, labelStyleFromConfig);
|
||||
if (!iconTemplate) itemClass.push('no-icon');
|
||||
if (!name) itemClass.push('no-name');
|
||||
if (!stateString) itemClass.push('no-state');
|
||||
|
@ -415,7 +452,8 @@ class ButtonCard extends LitElement {
|
|||
${iconTemplate ? iconTemplate : ''}
|
||||
${name ? html`<div class="name" style=${styleMap(nameStyleFromConfig)}>${name}</div>` : ''}
|
||||
${stateString ? html`<div class="state" style=${styleMap(stateStyleFromConfig)}>${stateString}</div>` : ''}
|
||||
${label ? html`<div class="label" style=${styleMap(labelStyleFromConfig)}>${label}</div>` : ''}
|
||||
${label && !this.config!.show_last_changed ? html`<div class="label" style=${styleMap(labelStyleFromConfig)}>${unsafeHTML(label)}</div>` : ''}
|
||||
${this.config!.show_last_changed ? lastChangedTemplate : ''}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
@ -533,4 +571,31 @@ class ButtonCard extends LitElement {
|
|||
const config = ev.target.config;
|
||||
handleClick(this, this.hass!, config, true);
|
||||
}
|
||||
|
||||
private _handleLock(ev): void {
|
||||
ev.stopPropagation();
|
||||
const overlay = this.shadowRoot!.getElementById('overlay') as LitElement;
|
||||
const haCard = this.shadowRoot!.firstElementChild as LitElement;
|
||||
overlay.style.setProperty('pointer-events', 'none');
|
||||
const paperRipple = document.createElement('paper-ripple');
|
||||
|
||||
const lock = this.shadowRoot!.getElementById('lock') as LitElement;
|
||||
if (lock) {
|
||||
haCard.appendChild(paperRipple);
|
||||
const icon = document.createAttribute('icon');
|
||||
icon.value = 'mdi:lock-open-outline';
|
||||
lock.attributes.setNamedItem(icon);
|
||||
lock.classList.add('fadeOut');
|
||||
}
|
||||
window.setTimeout(() => {
|
||||
overlay.style.setProperty('pointer-events', '');
|
||||
if (lock) {
|
||||
lock.classList.remove('fadeOut');
|
||||
const icon = document.createAttribute('icon');
|
||||
icon.value = 'mdi:lock-outline';
|
||||
lock.attributes.setNamedItem(icon);
|
||||
haCard.removeChild(paperRipple);
|
||||
}
|
||||
}, 5000);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { PropertyValues } from 'lit-element';
|
||||
import { TinyColor } from '@ctrl/tinycolor';
|
||||
import tinycolor, { TinyColor } from '@ctrl/tinycolor';
|
||||
import { HomeAssistant } from './types';
|
||||
|
||||
export function computeDomain(entityId: string): string {
|
||||
|
@ -27,6 +27,22 @@ export function getFontColorBasedOnBackgroundColor(backgroundColor: string): str
|
|||
}
|
||||
}
|
||||
|
||||
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;
|
||||
if (mixAmount < 50) {
|
||||
return tinycolor(low).mix(middle, mixAmount * 2).toRgbString();
|
||||
} else {
|
||||
return tinycolor(middle).mix(high, (mixAmount - 50) * 2).toRgbString();
|
||||
}
|
||||
}
|
||||
|
||||
export function buildNameStateConcat(
|
||||
name: string | undefined, stateString: string | undefined,
|
||||
): string | undefined {
|
||||
|
|
|
@ -5,6 +5,7 @@ export const styles = css`
|
|||
cursor: pointer;
|
||||
overflow: hidden;
|
||||
box-sizing: border-box;
|
||||
position: relative;
|
||||
}
|
||||
ha-card.disabled {
|
||||
pointer-events: none;
|
||||
|
@ -29,6 +30,34 @@ export const styles = css`
|
|||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
}
|
||||
#overlay {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
text-align: right;
|
||||
z-index: 1;
|
||||
}
|
||||
#lock {
|
||||
margin-top: 8px;
|
||||
opacity: 0.5;
|
||||
margin-right: 7px;
|
||||
-webkit-animation-duration: 5s;
|
||||
animation-duration: 5s;
|
||||
-webkit-animation-fill-mode: both;
|
||||
animation-fill-mode: both;
|
||||
}
|
||||
@keyframes fadeOut{
|
||||
0% {opacity: 0.5;}
|
||||
20% {opacity: 0;}
|
||||
80% {opacity: 0;}
|
||||
100% {opacity: 0.5;}
|
||||
}
|
||||
.fadeOut {
|
||||
-webkit-animation-name: fadeOut;
|
||||
animation-name: fadeOut;
|
||||
}
|
||||
@keyframes blink{
|
||||
0%{opacity:0;}
|
||||
50%{opacity:1;}
|
||||
|
|
|
@ -16,6 +16,7 @@ export interface ButtonCardConfig {
|
|||
color_type: 'icon' | 'card' | 'label-card' | 'blank-card'
|
||||
color?: string;
|
||||
size: string;
|
||||
lock: boolean;
|
||||
tap_action?: ActionConfig;
|
||||
hold_action?: ActionConfig;
|
||||
show_name?: boolean;
|
||||
|
@ -23,6 +24,7 @@ export interface ButtonCardConfig {
|
|||
show_icon?: boolean;
|
||||
show_units?: boolean;
|
||||
show_entity_picture?: boolean;
|
||||
show_last_changed?: boolean;
|
||||
show_label?: boolean;
|
||||
label?: string;
|
||||
label_template?: string;
|
||||
|
|
Loading…
Reference in New Issue