@@ -101,39 +164,46 @@ export class SliderButtonCardEditor extends LitElement implements LovelaceCardEd
.includeDomains=${getEnumValues(Domain)}
.value=${this._entity}
.configValue=${'entity'}
- label="${localize('tabs.general.entity')}"
- allow-custom-entity
- @value-changed=${this._valueChangedEntity}
+ @change=${this._valueChangedEntity}
>
-
+ @input=${this._valueChanged}
+ >
+ ${this._renderOptionSelector(`attribute`, this._entityAttributes, localize('tabs.general.attribute'), this._attribute)}
-
-
+
-
-
-
+
+
+
-
-
-
+
+
+
+
+
+
-
+ >
+
@@ -142,32 +212,36 @@ export class SliderButtonCardEditor extends LitElement implements LovelaceCardEd
-
-
+
-
-
+
-
+ >
+
${this.renderStateColor('icon')}
-
+ @value-changed=${this._valueChangedSelect}
+ >
@@ -176,66 +250,45 @@ export class SliderButtonCardEditor extends LitElement implements LovelaceCardEd
-
-
- ${this.directions.map(direction => {
- return html`
- ${localize(`direction.${direction}`)}
- `;
- })}
-
-
-
-
- ${this.backgrounds.map(background => {
- return html`
- ${localize(`background.${background}`)}
- `;
- })}
-
-
-
+ ${this._renderOptionSelector(
+ `slider.direction`,
+ this.directions.map(direction => {
+ return {'value': direction, 'label': localize(`direction.${direction}`)}
+ }), localize('tabs.slider.direction'),
+ this._slider.direction || ''
+ )}
+ ${this._renderOptionSelector(
+ `slider.background`,
+ this.backgrounds.map(background => {
+ return {'value': background, 'label': localize(`background.${background}`)}
+ }), localize('tabs.slider.background'),
+ this._slider.background || ''
+ )}
${this.renderBrightness('slider')}
${this.renderStateColor('slider')}
-
-
+
-
-
-
+
+
+
-
-
-
+
+
+
-
+ >
+
@@ -244,67 +297,61 @@ export class SliderButtonCardEditor extends LitElement implements LovelaceCardEd
-
-
- ${this.actionModes.map(mode => {
- return html`
- ${localize(`mode.${mode}`)}
- `;
- })}
-
-
+ ${this._renderOptionSelector(
+ `action_button.mode`,
+ this.actionModes.map(mode => {
+ return {'value': mode, 'label': localize(`mode.${mode}`)}
+ }), localize('tabs.action_button.mode'),
+ this._action_button.mode || ''
+ )}
${this._action_button.mode === ActionButtonMode.CUSTOM
? html`
-
-
+
+
`
- :
+ :
''}
-
-
+
-
+ >
+
${this._action_button.mode === ActionButtonMode.CUSTOM
? html`
-
-
+
-
- `
+ >
+
+ `
:
''}
${this._action_button.mode === ActionButtonMode.CUSTOM
? html`
-
+ @value-changed=${this._valueChangedSelect}
+ >
`
:
''}
@@ -318,55 +365,55 @@ export class SliderButtonCardEditor extends LitElement implements LovelaceCardEd
protected renderBrightness(path: string): TemplateResult | void {
const item = this[`_${path}`];
return html`
-
-
+
-
+ >
+
`;
}
protected renderStateColor(path: string): TemplateResult | void {
const item = this[`_${path}`];
return html`
-
-
+
-
+ >
+
`;
}
private _initialize(): void {
if (this.hass === undefined) return;
if (this._config === undefined) return;
- if (this._helpers === undefined) return;
this._initialized = true;
}
- private async loadCardHelpers(): Promise
{
- this._helpers = await (window as any).loadCardHelpers();
- }
-
private _valueChangedSelect(ev): void {
+ const target = ev.target;
const value = ev.detail.value;
if (!value) {
return;
}
- this._changeValue(value.parentElement?.configValue, value.itemValue);
+ this._changeValue(target.configValue, value);
}
private _valueChangedEntity(ev): void {
const target = ev.target;
- const value = ev.detail?.value;
+ const value = ev.target?.value;
+ if (!value) {
+ return;
+ }
const updateDefaults = computeDomain(value) !== computeDomain(this._config?.entity || 'light.dummy');
+ this._changeValue(target.configValue, value);
this._changeValue('name', '');
+ this._changeValue('attribute', '');
this._changeValue('icon.icon', '');
- this._changeValue(target.configValue, value);
if (updateDefaults) {
const cfg = copy(this._config);
applyPatch(cfg, ['slider'], getSliderDefaultForEntity(value));
@@ -377,7 +424,7 @@ export class SliderButtonCardEditor extends LitElement implements LovelaceCardEd
private _valueChanged(ev): void {
const target = ev.target;
- const value = ev.detail?.value;
+ const value = ev.target?.value;
this._changeValue(target.configValue, target.checked !== undefined ? target.checked : value);
}
@@ -401,7 +448,10 @@ export class SliderButtonCardEditor extends LitElement implements LovelaceCardEd
static get styles(): CSSResult {
return css`
- ha-switch {
+ mwc-textfield {
+ width: 100%;
+ }
+ mwc-switch {
padding: 16px 6px;
}
.side-by-side {
diff --git a/src/localize/languages/en.json b/src/localize/languages/en.json
index a3da46e..378ed67 100644
--- a/src/localize/languages/en.json
+++ b/src/localize/languages/en.json
@@ -9,9 +9,11 @@
"general": {
"title": "General",
"entity": "Entity (Required)",
+ "attribute": "Attribute (Optional)",
"name": "Name (Optional)",
"show_name": "Show name?",
"show_state": "Show state?",
+ "show_attribute": "Show attribute?",
"compact": "Compact?"
},
"icon": {
@@ -45,6 +47,7 @@
},
"direction": {
"left-right": "Left to right",
+ "right-left": "Right to left",
"top-bottom": "Top to bottom",
"bottom-top": "Bottom to top"
},
diff --git a/src/localize/languages/sk.json b/src/localize/languages/sk.json
new file mode 100644
index 0000000..31e3666
--- /dev/null
+++ b/src/localize/languages/sk.json
@@ -0,0 +1,65 @@
+{
+ "common": {
+ "version": "v",
+ "invalid_configuration": "Neplatná konfigurácia",
+ "show_warning": "Zobraziť warning",
+ "show_error": "Zobraziť error"
+ },
+ "tabs": {
+ "general": {
+ "title": "Všeobecné",
+ "entity": "Entita (požadovaná)",
+ "attribute": "Atribút (Voliteľné)",
+ "name": "Názov (voliteľný)",
+ "show_name": "Zobraziť názov?",
+ "show_state": "Zobraziť stav?",
+ "show_attribute": "Zobraziť atribútt?",
+ "compact": "Kompaktné?"
+ },
+ "icon": {
+ "title": "Ikona",
+ "icon": "Ikona (voliteľné)",
+ "show_icon": "Zobraziť ikonu?",
+ "use_state_color": "Use state color?",
+ "tap_action": "Klepnite na akciu"
+ },
+ "slider": {
+ "title": "Posuvník",
+ "direction": "Smer",
+ "background": "Pozadie",
+ "use_brightness": "Použiť jas?",
+ "show_track": "Zobraziť skladbu?",
+ "toggle_on_click": "Pôsobiť ako prepínač (zakázať posúvanie)",
+ "force_square": "Silový štvorec?"
+ },
+ "action_button": {
+ "title": "Akčné tlačidlo",
+ "mode": "Režim",
+ "icon": "Ikona",
+ "show_button": "Zobraziť tlačidlo?",
+ "show_spinner": "Zobraziť číselník?",
+ "tap_action": "Klepnite na akciu"
+ }
+ },
+ "state": {
+ "off": "Vypnúť",
+ "on": "Zapnúť"
+ },
+ "direction": {
+ "left-right": "Zľava doprava",
+ "right-left": "Zprava do ľava",
+ "top-bottom": "Zhora nadol",
+ "bottom-top": "Zdola nahor"
+ },
+ "background": {
+ "striped": "Prúžkované",
+ "gradient": "Gradient",
+ "solid": "Pevné",
+ "triangle": "Trojuholník",
+ "custom": "Voliteľné"
+ },
+ "mode": {
+ "toggle": "Prepnúť",
+ "custom": "Voliteľné"
+ }
+}
diff --git a/src/localize/localize.ts b/src/localize/localize.ts
index 6a2ee28..e1024f8 100644
--- a/src/localize/localize.ts
+++ b/src/localize/localize.ts
@@ -2,12 +2,12 @@ import * as en from './languages/en.json';
import * as de from './languages/de.json';
import * as fr from './languages/fr.json';
import * as he from './languages/he.json';
+import * as ko from './languages/ko.json';
import * as nl from './languages/nl.json';
import * as pl from './languages/pl.json';
import * as pt from './languages/pt.json';
import * as ru from './languages/ru.json';
-import * as ko from './languages/ko.json';
-
+import * as sk from './languages/sk.json';
// eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -16,11 +16,12 @@ const languages: any = {
de: de,
fr: fr,
he: he,
+ ko: ko,
nl: nl,
pl: pl,
pt: pt,
ru: ru,
- ko: ko,
+ sk: sk,
};
export function localize(string: string, search = '', replace = ''): string {
diff --git a/src/slider-button-card.ts b/src/slider-button-card.ts
index 8ab80ad..6667655 100644
--- a/src/slider-button-card.ts
+++ b/src/slider-button-card.ts
@@ -1,7 +1,7 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import { ActionHandlerEvent, applyThemesOnElement, computeStateDomain, handleAction, hasConfigOrEntityChanged, HomeAssistant, LovelaceCard, LovelaceCardEditor, STATES_OFF, toggleEntity } from 'custom-card-helpers';
import copy from 'fast-copy';
-import { css, CSSResult, customElement, html, LitElement, property, PropertyValues, query, state, TemplateResult } from 'lit-element';
+import { css, CSSResult, customElement, eventOptions, html, LitElement, property, PropertyValues, query, state, TemplateResult } from 'lit-element';
import { classMap } from 'lit-html/directives/class-map';
import { ifDefined } from 'lit-html/directives/if-defined';
import { styleMap } from 'lit-html/directives/style-map';
@@ -13,7 +13,7 @@ import './editor';
import { localize } from './localize/localize';
import type { SliderButtonCardConfig } from './types';
-import { ActionButtonConfigDefault, ActionButtonMode, IconConfigDefault } from './types';
+import { ActionButtonConfigDefault, ActionButtonMode, IconConfigDefault, SliderDirections } from './types';
import { getSliderDefaultForEntity } from './utils';
/* eslint no-console: 0 */
@@ -144,15 +144,18 @@ export class SliderButtonCard extends LitElement implements LovelaceCard {
tabindex="0"
.label=${`SliderButton: ${this.config.entity || 'No Entity Defined'}`}
class="${classMap({ 'square': this.config.slider?.force_square || false, 'hide-name': !this.config.show_name, 'hide-state': !this.config.show_state, 'hide-action': !this.config.action_button?.show , 'compact': this.config.compact === true })}"
+ data-mode="${this.config.slider?.direction}"
>
-
${this.ctrl.hasToggle
? html`
@@ -180,7 +184,7 @@ export class SliderButtonCard extends LitElement implements LovelaceCard {
}
private renderText(): TemplateResult {
- if (!this.config.show_name && !this.config.show_state) {
+ if (!this.config.show_name && !this.config.show_state && !this.config.show_attribute) {
return html``;
}
return html`
@@ -189,19 +193,33 @@ export class SliderButtonCard extends LitElement implements LovelaceCard {
? html`
${this.ctrl.name}
`
- : ''}
- ${this.config.show_state
- ? html`
-
- ${this.ctrl.isUnavailable
- ? html`
- ${this.hass.localize('state.default.unavailable')}
- ` : html`
- ${this.ctrl.label}
- `}
-
+ : ''}
+
+
+ ${this.config.show_state
+ ? html`
+
+ ${this.ctrl.isUnavailable
+ ? html`
+ ${this.hass.localize('state.default.unavailable')}
+ ` : html`
+ ${this.ctrl.label}
+ `}
+
+ `
+ : ''}
+
+ ${this.config.show_attribute
+ ? html`
+
+ ${this.config.show_state && this.ctrl.attributeLabel
+ ? html ` · `
+ : ''}
+ ${this.ctrl.attributeLabel}
+
`
: ''}
+
`;
}
@@ -378,8 +396,12 @@ export class SliderButtonCard extends LitElement implements LovelaceCard {
return color;
}
+ @eventOptions({passive: true})
private onPointerDown(event: PointerEvent): void {
- event.preventDefault();
+ if (this.config.slider?.direction === SliderDirections.TOP_BOTTOM
+ || this.config.slider?.direction === SliderDirections.BOTTOM_TOP) {
+ event.preventDefault();
+ }
event.stopPropagation();
if (this.ctrl.isSliderDisabled) {
return;
@@ -387,14 +409,36 @@ export class SliderButtonCard extends LitElement implements LovelaceCard {
this.slider.setPointerCapture(event.pointerId);
}
+ @eventOptions({passive: true})
private onPointerUp(event: PointerEvent): void {
if (this.ctrl.isSliderDisabled) {
return;
}
+
+ if (this.config.slider?.direction === SliderDirections.TOP_BOTTOM
+ || this.config.slider?.direction === SliderDirections.BOTTOM_TOP) {
+ this.setStateValue(this.ctrl.targetValue);
+ this.slider.releasePointerCapture(event.pointerId);
+ }
+
+ if (!this.slider.hasPointerCapture(event.pointerId)) {
+ return;
+ }
+
this.setStateValue(this.ctrl.targetValue);
this.slider.releasePointerCapture(event.pointerId);
}
+ private onPointerCancel(event: PointerEvent): void {
+ if (this.config.slider?.direction === SliderDirections.TOP_BOTTOM
+ || this.config.slider?.direction === SliderDirections.BOTTOM_TOP) {
+ return;
+ }
+ this.updateValue(this.ctrl.value, false);
+ this.slider.releasePointerCapture(event.pointerId);
+ }
+
+ @eventOptions({passive: true})
private onPointerMove(event: any): void {
if (this.ctrl.isSliderDisabled) {
return;
@@ -424,10 +468,14 @@ export class SliderButtonCard extends LitElement implements LovelaceCard {
display: flex;
flex-direction: column;
justify-content: space-between;
- touch-action: none;
+ touch-action: pan-y;
overflow: hidden;
--mdc-icon-size: 2.2em;
}
+ ha-card[data-mode="top-bottom"],
+ ha-card[data-mode="bottom-top"] {
+ touch-action: none;
+ }
ha-card.square {
aspect-ratio: 1 / 1;
}
@@ -468,8 +516,12 @@ export class SliderButtonCard extends LitElement implements LovelaceCard {
min-height: 7rem;
width: 100%;
display: block;
- overflow: hidden;
+ overflow: hidden;
transition: all 0.2s ease-in-out;
+ touch-action: pan-y;
+ }
+ .button[data-mode="top-bottom"],
+ .button[data-mode="bottom-top"] {
touch-action: none;
}
ha-card.compact .button {
@@ -543,7 +595,7 @@ export class SliderButtonCard extends LitElement implements LovelaceCard {
/* --- LABEL --- */
.name {
- color: var(--label-color-on, var(--primary-text-color, white));
+ color: var(--label-color-on, var(--primary-text-color, white));
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
@@ -564,7 +616,7 @@ export class SliderButtonCard extends LitElement implements LovelaceCard {
/* --- STATE --- */
.state {
- color: var(--state-color-on, var(--label-badge-text-color, white));
+ color: var(--state-color-on, var(--label-badge-text-color, white));
text-overflow: ellipsis;
white-space: nowrap;
text-shadow: var(--state-text-shadow);
@@ -585,7 +637,38 @@ export class SliderButtonCard extends LitElement implements LovelaceCard {
overflow: hidden;
}
-
+ /* --- ATTRIBUTE --- */
+
+ .attribute {
+ /*
+ color: var(--state-color-on, var(--label-badge-text-color, white));
+ text-overflow: ellipsis;
+ overflow: hidden;
+ white-space: nowrap;
+ text-shadow: var(--state-text-shadow);
+ max-width: calc(50% -2em);
+ transition: font-size 0.1s ease-in-out;
+ border: 1px solid red;
+ */
+ }
+
+ .compact .attribute {
+ display: inline-block;
+ max-width: calc(100% - 0em);
+ overflow: hidden;
+ }
+
+ .oneliner {
+ color: var(--state-color-on, var(--label-badge-text-color, white));
+ text-overflow: ellipsis;
+ overflow: hidden;
+ white-space: nowrap;
+ max-width: 20px;
+ width: 20px;
+ text-shadow: var(--state-text-shadow);
+ transition: font-size 0.1s ease-in-out;
+ /*border: 1px solid blue;*/
+ }
/* --- SLIDER --- */
.slider {
@@ -646,6 +729,9 @@ export class SliderButtonCard extends LitElement implements LovelaceCard {
--slider-bg: linear-gradient(var(--slider-bg-direction), transparent 0%, transparent 50%, var(--slider-color) 50%, var(--slider-color) 100%);
border-right: 0px solid;
}
+ .slider[data-background="triangle"][data-mode="right-left"] .slider-bg {
+ --slider-bg-direction: to bottom left;
+ }
.slider[data-background="triangle"][data-mode="bottom-top"] .slider-bg {
--slider-bg-direction: to top left;
}
@@ -667,6 +753,9 @@ export class SliderButtonCard extends LitElement implements LovelaceCard {
.slider[data-background="striped"][data-mode="top-bottom"] .slider-bg {
--slider-bg-size: 100% 4px;
}
+ .slider[data-mode="right-left"] .slider-bg {
+ --slider-bg-direction: to left;
+ }
.slider[data-mode="bottom-top"] .slider-bg {
--slider-bg-direction: to top;
}
@@ -687,6 +776,9 @@ export class SliderButtonCard extends LitElement implements LovelaceCard {
.changing .slider .slider-thumb {
transition: none;
}
+ .slider[data-mode="right-left"] .slider-thumb {
+ transform: translateX(calc(var(--slider-value) * -1)) !important;
+ }
.slider[data-mode="top-bottom"] .slider-thumb {
transform: translateY(var(--slider-value)) !important;
}
diff --git a/src/types.ts b/src/types.ts
index aed8af8..75d32d3 100644
--- a/src/types.ts
+++ b/src/types.ts
@@ -11,9 +11,11 @@ declare global {
export interface SliderButtonCardConfig extends LovelaceCardConfig {
type: string;
entity: string;
+ attribute?: string;
name?: string;
show_name?: boolean;
show_state?: boolean;
+ show_attribute?: boolean;
icon?: IconConfig;
action_button?: ActionButtonConfig;
slider?: SliderConfig;
@@ -59,6 +61,7 @@ export enum ActionButtonMode {
export enum SliderDirections {
LEFT_RIGHT = 'left-right',
+ RIGHT_LEFT = 'right-left',
TOP_BOTTOM = 'top-bottom',
BOTTOM_TOP = 'bottom-top',
}
@@ -77,9 +80,12 @@ export enum Domain {
FAN = 'fan',
COVER = 'cover',
INPUT_BOOLEAN = 'input_boolean',
+ INPUT_NUMBER = 'input_number',
MEDIA_PLAYER = 'media_player',
+ NUMBER = 'number',
CLIMATE = 'climate',
LOCK = 'lock',
+ AUTOMATION = 'automation',
}
export const ActionButtonConfigDefault: ActionButtonConfig = {
@@ -119,6 +125,7 @@ export const SliderConfigDefaultDomain: Map
= new Map([
show_track: false,
toggle_on_click: false,
force_square: false,
+ show_attribute: false,
}],
[Domain.FAN, {
direction: SliderDirections.LEFT_RIGHT,
@@ -128,6 +135,7 @@ export const SliderConfigDefaultDomain: Map = new Map([
show_track: false,
toggle_on_click: false,
force_square: false,
+ show_attribute: false,
}],
[Domain.SWITCH, {
direction: SliderDirections.LEFT_RIGHT,
@@ -137,6 +145,16 @@ export const SliderConfigDefaultDomain: Map = new Map([
show_track: false,
toggle_on_click: true,
force_square: false,
+ show_attribute: false,
+ }],
+ [Domain.AUTOMATION, {
+ direction: SliderDirections.LEFT_RIGHT,
+ background: SliderBackground.SOLID,
+ use_state_color: false,
+ use_percentage_bg_opacity: false,
+ show_track: false,
+ toggle_on_click: true,
+ force_square: false,
}],
[Domain.COVER, {
direction: SliderDirections.TOP_BOTTOM,
@@ -147,6 +165,7 @@ export const SliderConfigDefaultDomain: Map = new Map([
show_track: false,
force_square: false,
invert: true,
+ show_attribute: false,
}],
[Domain.INPUT_BOOLEAN, {
direction: SliderDirections.LEFT_RIGHT,
@@ -156,6 +175,16 @@ export const SliderConfigDefaultDomain: Map = new Map([
show_track: false,
toggle_on_click: true,
force_square: false,
+ show_attribute: false,
+ }],
+ [Domain.INPUT_NUMBER, {
+ direction: SliderDirections.LEFT_RIGHT,
+ background: SliderBackground.SOLID,
+ use_state_color: false,
+ use_percentage_bg_opacity: false,
+ show_track: false,
+ toggle_on_click: false,
+ force_square: false,
}],
[Domain.MEDIA_PLAYER, {
direction: SliderDirections.LEFT_RIGHT,
@@ -165,6 +194,8 @@ export const SliderConfigDefaultDomain: Map = new Map([
show_track: true,
toggle_on_click: false,
force_square: false,
+ show_attribute: true,
+ attribute: "media_title",
}],
[Domain.LOCK, {
direction: SliderDirections.LEFT_RIGHT,
@@ -174,6 +205,7 @@ export const SliderConfigDefaultDomain: Map = new Map([
show_track: false,
toggle_on_click: true,
force_square: false,
+ show_attribute: false,
}],
[Domain.CLIMATE, {
direction: SliderDirections.LEFT_RIGHT,
@@ -183,6 +215,7 @@ export const SliderConfigDefaultDomain: Map = new Map([
show_track: true,
toggle_on_click: false,
force_square: false,
+ show_attribute: false,
}],
]);