periodic push
This commit is contained in:
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
@@ -1,158 +0,0 @@
|
||||
import {cssData} from './styles.js?v=1.3.0';
|
||||
import ThermostatUI from './thermostat_card.lib.js?v=1.3.0';
|
||||
console.info("%c Thermostat Card \n%c Version 1.3.0 ", "color: orange; font-weight: bold; background: black", "color: white; font-weight: bold; background: dimgray");
|
||||
class ThermostatCard extends HTMLElement {
|
||||
constructor() {
|
||||
super();
|
||||
this.attachShadow({ mode: 'open' });
|
||||
}
|
||||
set hass(hass) {
|
||||
const config = this._config;
|
||||
const entity = hass.states[config.entity];
|
||||
if(!entity)return;
|
||||
let ambient_temperature = entity.attributes.current_temperature || 0;
|
||||
if (config.ambient_temperature && hass.states[config.ambient_temperature])
|
||||
ambient_temperature = hass.states[config.ambient_temperature].state;
|
||||
let hvac_state = entity.state;
|
||||
|
||||
const new_state = {
|
||||
entity: entity,
|
||||
min_value: entity.attributes.min_temp,
|
||||
max_value: entity.attributes.max_temp,
|
||||
ambient_temperature: ambient_temperature,
|
||||
target_temperature: entity.attributes.temperature,
|
||||
target_temperature_low: entity.attributes.target_temp_low,
|
||||
target_temperature_high: entity.attributes.target_temp_high,
|
||||
hvac_state: entity.state,
|
||||
hvac_modes:entity.attributes.hvac_modes,
|
||||
preset_mode: entity.attributes.preset_mode,
|
||||
away: (entity.attributes.away_mode == 'on' ? true : false)
|
||||
}
|
||||
|
||||
if (!this._saved_state ||
|
||||
(this._saved_state.min_value != new_state.min_value ||
|
||||
this._saved_state.max_value != new_state.max_value ||
|
||||
this._saved_state.ambient_temperature != new_state.ambient_temperature ||
|
||||
this._saved_state.target_temperature != new_state.target_temperature ||
|
||||
this._saved_state.target_temperature_low != new_state.target_temperature_low ||
|
||||
this._saved_state.target_temperature_high != new_state.target_temperature_high ||
|
||||
this._saved_state.hvac_state != new_state.hvac_state ||
|
||||
this._saved_state.preset_mode != new_state.preset_mode ||
|
||||
this._saved_state.away != new_state.away)) {
|
||||
this._saved_state = new_state;
|
||||
this.thermostat.updateState(new_state,hass);
|
||||
}
|
||||
this._hass = hass;
|
||||
}
|
||||
|
||||
openProp(entityId) {
|
||||
this.fire('hass-more-info', { entityId });
|
||||
}
|
||||
fire(type, detail, options) {
|
||||
|
||||
options = options || {}
|
||||
detail = detail === null || detail === undefined ? {} : detail
|
||||
const e = new Event(type, {
|
||||
bubbles: options.bubbles === undefined ? true : options.bubbles,
|
||||
cancelable: Boolean(options.cancelable),
|
||||
composed: options.composed === undefined ? true : options.composed,
|
||||
})
|
||||
|
||||
e.detail = detail
|
||||
this.dispatchEvent(e)
|
||||
return e
|
||||
}
|
||||
|
||||
_controlSetPoints() {
|
||||
|
||||
if (this.thermostat.dual) {
|
||||
if (this.thermostat.temperature.high != this._saved_state.target_temperature_high ||
|
||||
this.thermostat.temperature.low != this._saved_state.target_temperature_low)
|
||||
this._hass.callService('climate', 'set_temperature', {
|
||||
entity_id: this._config.entity,
|
||||
target_temp_high: this.thermostat.temperature.high,
|
||||
target_temp_low: this.thermostat.temperature.low,
|
||||
});
|
||||
} else {
|
||||
if (this.thermostat.temperature.target != this._saved_state.target_temperature)
|
||||
this._hass.callService('climate', 'set_temperature', {
|
||||
entity_id: this._config.entity,
|
||||
temperature: this.thermostat.temperature.target,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
setConfig(config) {
|
||||
// Check config
|
||||
if (!config.entity && config.entity.split(".")[0] === 'climate') {
|
||||
throw new Error('Please define an entity');
|
||||
}
|
||||
|
||||
// Cleanup DOM
|
||||
const root = this.shadowRoot;
|
||||
|
||||
if (root.lastChild) root.removeChild(root.lastChild);
|
||||
|
||||
// Prepare config defaults
|
||||
const cardConfig = deepClone(config);
|
||||
// cardConfig.hvac = Object.assign({}, config.hvac);
|
||||
|
||||
if (!cardConfig.diameter) cardConfig.diameter = 400;
|
||||
if (!cardConfig.pending) cardConfig.pending = 3;
|
||||
if (!cardConfig.idle_zone) cardConfig.idle_zone = 2;
|
||||
if (!cardConfig.step) cardConfig.step = 0.5;
|
||||
if (!cardConfig.highlight_tap) cardConfig.highlight_tap = false;
|
||||
if (!cardConfig.no_card) cardConfig.no_card = false;
|
||||
if (!cardConfig.chevron_size) cardConfig.chevron_size = 50;
|
||||
if (!cardConfig.num_ticks) cardConfig.num_ticks = 150;
|
||||
if (!cardConfig.tick_degrees) cardConfig.tick_degrees = 300;
|
||||
|
||||
// Extra config values generated for simplicity of updates
|
||||
cardConfig.radius = cardConfig.diameter / 2;
|
||||
cardConfig.ticks_outer_radius = cardConfig.diameter / 30;
|
||||
cardConfig.ticks_inner_radius = cardConfig.diameter / 8;
|
||||
cardConfig.offset_degrees = 180 - (360 - cardConfig.tick_degrees) / 2;
|
||||
cardConfig.control = this._controlSetPoints.bind(this);
|
||||
cardConfig.propWin = this.openProp.bind(this);
|
||||
this.thermostat = new ThermostatUI(cardConfig);
|
||||
|
||||
if (cardConfig.no_card === true) {
|
||||
|
||||
const card = document.createElement('ha-card');
|
||||
card.className = "no_card";
|
||||
const style = document.createElement('style');
|
||||
style.textContent = cssData();
|
||||
card.appendChild(style);
|
||||
card.appendChild(this.thermostat.container);
|
||||
root.appendChild(card);
|
||||
|
||||
}
|
||||
else {
|
||||
|
||||
const card = document.createElement('ha-card');
|
||||
const style = document.createElement('style');
|
||||
style.textContent = cssData();
|
||||
card.appendChild(style);
|
||||
card.appendChild(this.thermostat.container);
|
||||
root.appendChild(card);
|
||||
}
|
||||
this._config = cardConfig;
|
||||
}
|
||||
}
|
||||
customElements.define('thermostat-card', ThermostatCard);
|
||||
|
||||
function deepClone(value) {
|
||||
if (!(!!value && typeof value == 'object')) {
|
||||
return value;
|
||||
}
|
||||
if (Object.prototype.toString.call(value) == '[object Date]') {
|
||||
return new Date(value.getTime());
|
||||
}
|
||||
if (Array.isArray(value)) {
|
||||
return value.map(deepClone);
|
||||
}
|
||||
var result = {};
|
||||
Object.keys(value).forEach(
|
||||
function(key) { result[key] = deepClone(value[key]); });
|
||||
return result;
|
||||
}
|
||||
Binary file not shown.
@@ -1,303 +0,0 @@
|
||||
export function cssData(user) {
|
||||
var css =`
|
||||
|
||||
ha-card {
|
||||
overflow: hidden;
|
||||
--rail_border_color: transparent;
|
||||
--auto_color: rgb(227, 99, 4, 1);
|
||||
--cool_color: rgba(0, 122, 241, 0.6);
|
||||
--cool_colorc: rgba(0, 122, 241, 1);
|
||||
--heat_color: #ff8100;
|
||||
--heat_colorc: rgb(227, 99, 4, 1);
|
||||
--manual_color: #44739e;
|
||||
--off_color: #8a8a8a;
|
||||
--fan_only_color: #D7DBDD;
|
||||
--dry_color: #efbd07;
|
||||
--idle_color: #808080;
|
||||
--unknown_color: #bac;
|
||||
--text-color: white;
|
||||
}
|
||||
ha-card.no_card{
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
ha-card.no_card .prop{
|
||||
display: none;
|
||||
}
|
||||
.auto, .heat_cool {
|
||||
--mode_color: var(--auto_color);
|
||||
}
|
||||
|
||||
.cool {
|
||||
--mode_color: var(--cool_color);
|
||||
}
|
||||
|
||||
.heat {
|
||||
--mode_color: var(--heat_color);
|
||||
}
|
||||
|
||||
.manual {
|
||||
--mode_color: var(--manual_color);
|
||||
}
|
||||
|
||||
.off {
|
||||
--mode_color: var(--off_color);
|
||||
}
|
||||
.more {
|
||||
--mode_color: var(--off_color);
|
||||
}
|
||||
.fan_only {
|
||||
--mode_color: var(--fan_only_color);
|
||||
}
|
||||
|
||||
.eco {
|
||||
--mode_color: var(--auto_color);
|
||||
}
|
||||
|
||||
.dry {
|
||||
--mode_color: var(--dry_color);
|
||||
}
|
||||
|
||||
.idle {
|
||||
--mode_color: var(--idle_color);
|
||||
}
|
||||
|
||||
.unknown-mode {
|
||||
--mode_color: var(--unknown_color);
|
||||
}
|
||||
.c_body {
|
||||
padding: 5% 5% 5% 5%;
|
||||
}
|
||||
.c_icon{
|
||||
position: absolute;
|
||||
cursor: pointer;
|
||||
top: 0;
|
||||
right: 0;
|
||||
z-index: 25;
|
||||
}
|
||||
.climate_info {
|
||||
position: absolute;
|
||||
top: 82%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
width: 14%;
|
||||
height: 14%;
|
||||
--background-color: white;
|
||||
}
|
||||
.modes, .mode_color{
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
.modes ha-icon {
|
||||
color: var(--mode_color);
|
||||
--mdc-icon-size: 100%;
|
||||
}
|
||||
.dialog{
|
||||
background-color:#0000008c;
|
||||
width: 90%;
|
||||
height: 90%;
|
||||
margin: 5%;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
align-content: center;
|
||||
top: 45%;
|
||||
left: 45%;
|
||||
backdrop-filter: blur(6px) grayscale(50%);
|
||||
box-shadow: 0px 0px 10px 0px #696969;
|
||||
border: 1px solid #ffffff;
|
||||
}
|
||||
.dialog span {
|
||||
width:33%;
|
||||
margin: 3% 0;
|
||||
}
|
||||
.dialog ha-icon {
|
||||
color: var(--mode_color);
|
||||
--mdc-icon-size: 70%;
|
||||
margin: 15%;
|
||||
}
|
||||
.dialog.hide{
|
||||
display: none;
|
||||
}
|
||||
.dialog.pending{
|
||||
border: 1px solid var(--mode_color);
|
||||
box-shadow: 0px 0px 10px 0px var(--mode_color);
|
||||
animation: dialog-pending .8s infinite alternate;
|
||||
}
|
||||
|
||||
@keyframes dialog-pending
|
||||
{
|
||||
from {box-shadow: 0px 0px 10px 0px var(--mode_color);}
|
||||
to {box-shadow: 0px 0px 0px 0px var(--mode_color);}
|
||||
}
|
||||
|
||||
.dot_r{
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
background-color: white;
|
||||
border-radius: 50%;
|
||||
display: inline-block;
|
||||
opacity: 0.1;
|
||||
}
|
||||
.dot_h{
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
|
||||
|
||||
.dial {
|
||||
user-select: none;
|
||||
|
||||
--thermostat-off-fill: #000000c2;
|
||||
--thermostat-path-color: rgba(255, 255, 255, 0.3);
|
||||
--thermostat-path-active-color: rgba(255, 255, 255, 0.8);
|
||||
--thermostat-path-active-color-large: rgba(255, 255, 255, 1);
|
||||
--thermostat-text-color: white;
|
||||
}
|
||||
|
||||
.dial.has-thermo .dial__ico__leaf {
|
||||
visibility: hidden;
|
||||
}
|
||||
.dial .dial__shape {
|
||||
transition: fill 0.5s;
|
||||
fill: var(--thermostat-off-fill);
|
||||
}
|
||||
|
||||
.dial__ico__thermo {
|
||||
fill: var(--thermostat-path-active-color);
|
||||
opacity: 0;
|
||||
transition: opacity 0.5s;
|
||||
pointer-events: none;
|
||||
}
|
||||
.dial.has-thermo .dial__ico__thermo {
|
||||
display: block;
|
||||
opacity: 1;
|
||||
pointer-events: initial;
|
||||
}
|
||||
.dial__editableIndicator {
|
||||
fill: white;
|
||||
fill-rule: evenodd;
|
||||
opacity: 0;
|
||||
transition: opacity 0.5s;
|
||||
}
|
||||
.dial__temperatureControl {
|
||||
fill: white;
|
||||
opacity: 0;
|
||||
transition: opacity 0.2s;
|
||||
}
|
||||
.dial__temperatureControl.control-visible {
|
||||
opacity: 0.2;
|
||||
}
|
||||
.dial--edit .dial__editableIndicator {
|
||||
opacity: 1;
|
||||
}
|
||||
.dial--state--off .dial__shape {
|
||||
fill: var(--thermostat-off-fill);
|
||||
}
|
||||
.dial--state--heat .dial__shape {
|
||||
fill: var(--heat_colorc);
|
||||
}
|
||||
.dial--state--cool .dial__shape {
|
||||
fill: var(--cool_colorc);
|
||||
}
|
||||
.dial--state--auto .dial__shape {
|
||||
fill: var(--auto_color);
|
||||
}
|
||||
.dial--state--fan_only .dial__shape {
|
||||
fill: var(--fan_only_color);
|
||||
}
|
||||
.dial--state--dry .dial__shape {
|
||||
fill: var(--dry_color);
|
||||
}
|
||||
.dial--state--idle .dial__shape {
|
||||
fill: var(--idle_color);
|
||||
}
|
||||
.dial__ticks path {
|
||||
fill: var(--thermostat-path-color);
|
||||
}
|
||||
.dial__ticks path.active {
|
||||
fill: var(--mode_color);
|
||||
}
|
||||
.dial__ticks path.active.large {
|
||||
fill: var(--mode_color);
|
||||
}
|
||||
.dial text, .dial text tspan {
|
||||
fill: var(--thermostat-text-color);
|
||||
text-anchor: middle;
|
||||
font-family: Helvetica, sans-serif;
|
||||
alignment-baseline: central;
|
||||
dominant-baseline: central;
|
||||
}
|
||||
.dial__lbl--target {
|
||||
font-size: 120px;
|
||||
font-weight: bold;
|
||||
visibility: hidden;
|
||||
}
|
||||
.dial__lbl--low, .dial__lbl--high {
|
||||
font-size: 90px;
|
||||
font-weight: bold;
|
||||
visibility: hidden;
|
||||
}
|
||||
.dial.in_control .dial__lbl--target {
|
||||
visibility: visible;
|
||||
}
|
||||
.dial.in_control .dial__lbl--low {
|
||||
visibility: visible;
|
||||
}
|
||||
.dial.in_control .dial__lbl--high {
|
||||
visibility: visible;
|
||||
}
|
||||
.dial__lbl--ambient {
|
||||
font-size: 120px;
|
||||
font-weight: bold;
|
||||
visibility: visible;
|
||||
}
|
||||
.dial.in_control.has_dual .dial__chevron--low,
|
||||
.dial.in_control.has_dual .dial__chevron--high {
|
||||
visibility: visible;
|
||||
}
|
||||
.dial.in_control .dial__chevron--target {
|
||||
visibility: visible;
|
||||
}
|
||||
.dial.in_control.has_dual .dial__chevron--target {
|
||||
visibility: hidden;
|
||||
}
|
||||
.dial .dial__chevron {
|
||||
visibility: hidden;
|
||||
fill: none;
|
||||
stroke: var(--thermostat-text-color);
|
||||
stroke-width: 4px;
|
||||
opacity: 0.3;
|
||||
}
|
||||
.dial .dial__chevron.pressed {
|
||||
opacity: 1;
|
||||
}
|
||||
.dial.in_control .dial__lbl--ambient {
|
||||
visibility: hidden;
|
||||
}
|
||||
.dial__lbl--super--ambient, .dial__lbl--super--target {
|
||||
font-size: 40px;
|
||||
font-weight: bold;
|
||||
}
|
||||
.dial__lbl--super--high, .dial__lbl--super--low {
|
||||
font-size: 30px;
|
||||
font-weight: bold;
|
||||
}
|
||||
.dial__lbl--ring {
|
||||
font-size: 22px;
|
||||
font-weight: bold;
|
||||
}
|
||||
.dial__lbl--title {
|
||||
font-size: 24px;
|
||||
}
|
||||
`
|
||||
return css;
|
||||
}
|
||||
|
||||
Binary file not shown.
@@ -1,659 +0,0 @@
|
||||
export default class ThermostatUI {
|
||||
get container() {
|
||||
return this._container
|
||||
}
|
||||
set dual(val) {
|
||||
this._dual = val
|
||||
}
|
||||
get dual() {
|
||||
return this._dual;
|
||||
}
|
||||
get in_control() {
|
||||
return this._in_control;
|
||||
}
|
||||
get temperature() {
|
||||
return {
|
||||
low: this._low,
|
||||
high: this._high,
|
||||
target: this._target,
|
||||
}
|
||||
}
|
||||
get ambient() {
|
||||
return this._ambient;
|
||||
}
|
||||
set temperature(val) {
|
||||
this._ambient = val.ambient;
|
||||
this._low = val.low;
|
||||
this._high = val.high;
|
||||
this._target = val.target;
|
||||
if (this._low && this._high) this.dual = true;
|
||||
}
|
||||
constructor(config) {
|
||||
|
||||
this._config = config; // need certain options for updates
|
||||
this._ticks = []; // need for dynamic tick updates
|
||||
this._controls = []; // need for managing highlight and clicks
|
||||
this._dual = false; // by default is single temperature
|
||||
this._container = document.createElement('div');
|
||||
this._main_icon = document.createElement('div');
|
||||
this._modes_dialog = document.createElement('div');
|
||||
config.title = config.title === null || config.title === undefined ? 'Title' : config.title
|
||||
|
||||
this._ic = document.createElement('div');
|
||||
this._ic.className = "prop";
|
||||
this._ic.innerHTML = `<ha-icon-button id="more" icon="mdi:dots-vertical" class="c_icon" role="button" tabindex="0" aria-disabled="false"></ha-icon-button>`;
|
||||
this._container.appendChild(this._ic)
|
||||
|
||||
// this._container.appendChild(this._buildTitle(config.title));
|
||||
this._ic.addEventListener('click', () => this.openProp());
|
||||
this._container.appendChild(this._load_icon('',''));
|
||||
this.c_body = document.createElement('div');
|
||||
this.c_body.className = 'c_body';
|
||||
const root = this._buildCore(config.diameter);
|
||||
root.appendChild(this._buildDial(config.radius));
|
||||
root.appendChild(this._buildTicks(config.num_ticks));
|
||||
root.appendChild(this._buildRing(config.radius));
|
||||
root.appendChild(this._buildThermoIcon(config.radius));
|
||||
root.appendChild(this._buildDialSlot(1));
|
||||
root.appendChild(this._buildDialSlot(2));
|
||||
root.appendChild(this._buildDialSlot(3));
|
||||
|
||||
root.appendChild(this._buildText(config.radius, 'title', 0));
|
||||
root.appendChild(this._buildText(config.radius, 'ambient', 0));
|
||||
root.appendChild(this._buildText(config.radius, 'target', 0));
|
||||
root.appendChild(this._buildText(config.radius, 'low', -config.radius / 2.5));
|
||||
root.appendChild(this._buildText(config.radius, 'high', config.radius / 3));
|
||||
root.appendChild(this._buildChevrons(config.radius, 0, 'low', 0.7, -config.radius / 2.5));
|
||||
root.appendChild(this._buildChevrons(config.radius, 0, 'high', 0.7, config.radius / 3));
|
||||
root.appendChild(this._buildChevrons(config.radius, 0, 'target', 1, 0));
|
||||
root.appendChild(this._buildChevrons(config.radius, 180, 'low', 0.7, -config.radius / 2.5));
|
||||
root.appendChild(this._buildChevrons(config.radius, 180, 'high', 0.7, config.radius / 3));
|
||||
root.appendChild(this._buildChevrons(config.radius, 180, 'target', 1, 0));
|
||||
|
||||
|
||||
this.c_body.appendChild(root);
|
||||
this._container.appendChild(this.c_body);
|
||||
this._root = root;
|
||||
this._buildControls(config.radius);
|
||||
this._root.addEventListener('click', () => this._enableControls());
|
||||
this._container.appendChild(this._buildDialog());
|
||||
this._main_icon.addEventListener('click', () => this._openDialog());
|
||||
this._modes_dialog.addEventListener('click', () => this._hideDialog());
|
||||
this._updateText('title', config.title);
|
||||
}
|
||||
|
||||
updateState(options,hass) {
|
||||
|
||||
const config = this._config;
|
||||
const away = options.away || false;
|
||||
this.entity = options.entity;
|
||||
this.min_value = options.min_value;
|
||||
this.max_value = options.max_value;
|
||||
this.hvac_state = options.hvac_state;
|
||||
this.preset_mode = options.preset_mode;
|
||||
this.hvac_modes = options.hvac_modes;
|
||||
this.temperature = {
|
||||
low: options.target_temperature_low,
|
||||
high: options.target_temperature_high,
|
||||
target: options.target_temperature,
|
||||
ambient: options.ambient_temperature,
|
||||
}
|
||||
|
||||
this._updateClass('has_dual', this.dual);
|
||||
let tick_label, from, to;
|
||||
const tick_indexes = [];
|
||||
const ambient_index = SvgUtil.restrictToRange(Math.round((this.ambient - this.min_value) / (this.max_value - this.min_value) * config.num_ticks), 0, config.num_ticks - 1);
|
||||
const target_index = SvgUtil.restrictToRange(Math.round((this._target - this.min_value) / (this.max_value - this.min_value) * config.num_ticks), 0, config.num_ticks - 1);
|
||||
const high_index = SvgUtil.restrictToRange(Math.round((this._high - this.min_value) / (this.max_value - this.min_value) * config.num_ticks), 0, config.num_ticks - 1);
|
||||
const low_index = SvgUtil.restrictToRange(Math.round((this._low - this.min_value) / (this.max_value - this.min_value) * config.num_ticks), 0, config.num_ticks - 1);
|
||||
|
||||
if (!this.dual) {
|
||||
tick_label = [this._target, this.ambient].sort();
|
||||
this._updateTemperatureSlot(tick_label[0], -8, `temperature_slot_1`);
|
||||
this._updateTemperatureSlot(tick_label[1], 8, `temperature_slot_2`);
|
||||
|
||||
switch (this.hvac_state) {
|
||||
case 'dry':
|
||||
this._load_icon(this.hvac_state, 'water-percent');
|
||||
break;
|
||||
case 'fan_only':
|
||||
this._load_icon(this.hvac_state, 'fan');
|
||||
break;
|
||||
case 'cool':
|
||||
this._load_icon(this.hvac_state, 'snowflake');
|
||||
|
||||
if (target_index <= ambient_index) {
|
||||
from = target_index;
|
||||
to = ambient_index;
|
||||
}
|
||||
break;
|
||||
case 'heat':
|
||||
this._load_icon(this.hvac_state, 'fire');
|
||||
|
||||
if (target_index >= ambient_index) {
|
||||
from = ambient_index;
|
||||
to = target_index;
|
||||
}
|
||||
break;
|
||||
case 'auto':
|
||||
this._load_icon(this.hvac_state, 'atom');
|
||||
|
||||
if (target_index >= ambient_index) {
|
||||
from = ambient_index;
|
||||
to = target_index;
|
||||
}
|
||||
break;
|
||||
case 'off':
|
||||
this._load_icon(this.hvac_state, 'power');
|
||||
break;
|
||||
default:
|
||||
this._load_icon('more', 'dots-horizontal');
|
||||
}
|
||||
} else {
|
||||
tick_label = [this._low, this._high, this.ambient].sort();
|
||||
this._updateTemperatureSlot(null, 0, `temperature_slot_1`);
|
||||
this._updateTemperatureSlot(null, 0, `temperature_slot_2`);
|
||||
this._updateTemperatureSlot(null, 0, `temperature_slot_3`);
|
||||
|
||||
switch (this.hvac_state) {
|
||||
case 'cool':
|
||||
if (high_index < ambient_index) {
|
||||
from = high_index;
|
||||
to = ambient_index;
|
||||
this._updateTemperatureSlot(this.ambient, 8, `temperature_slot_3`);
|
||||
this._updateTemperatureSlot(this._high, -8, `temperature_slot_2`);
|
||||
}
|
||||
break;
|
||||
case 'heat':
|
||||
if (low_index > ambient_index) {
|
||||
from = ambient_index;
|
||||
to = low_index;
|
||||
this._updateTemperatureSlot(this.ambient, -8, `temperature_slot_1`);
|
||||
this._updateTemperatureSlot(this._low, 8, `temperature_slot_2`);
|
||||
}
|
||||
break;
|
||||
case 'off':
|
||||
if (high_index < ambient_index) {
|
||||
from = high_index;
|
||||
to = ambient_index;
|
||||
this._updateTemperatureSlot(this.ambient, 8, `temperature_slot_3`);
|
||||
this._updateTemperatureSlot(this._high, -8, `temperature_slot_2`);
|
||||
}
|
||||
if (low_index > ambient_index) {
|
||||
from = ambient_index;
|
||||
to = low_index;
|
||||
this._updateTemperatureSlot(this.ambient, -8, `temperature_slot_1`);
|
||||
this._updateTemperatureSlot(this._low, 8, `temperature_slot_2`);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
tick_label.forEach(item => tick_indexes.push(SvgUtil.restrictToRange(Math.round((item - this.min_value) / (this.max_value - this.min_value) * config.num_ticks), 0, config.num_ticks - 1)));
|
||||
this._updateTicks(from, to, tick_indexes, this.hvac_state);
|
||||
// this._updateColor(this.hvac_state, this.preset_mode);
|
||||
this._updateText('ambient', this.ambient);
|
||||
this._updateEdit(false);
|
||||
this._updateDialog(this.hvac_modes,hass);
|
||||
}
|
||||
|
||||
_temperatureControlClicked(index) {
|
||||
const config = this._config;
|
||||
let chevron;
|
||||
this._root.querySelectorAll('path.dial__chevron').forEach(el => SvgUtil.setClass(el, 'pressed', false));
|
||||
if (this.in_control) {
|
||||
if (this.dual) {
|
||||
switch (index) {
|
||||
case 0:
|
||||
// clicked top left
|
||||
chevron = this._root.querySelectorAll('path.dial__chevron--low')[1];
|
||||
this._low = this._low + config.step;
|
||||
if ((this._low + config.idle_zone) >= this._high) this._low = this._high - config.idle_zone;
|
||||
break;
|
||||
case 1:
|
||||
// clicked top right
|
||||
chevron = this._root.querySelectorAll('path.dial__chevron--high')[1];
|
||||
this._high = this._high + config.step;
|
||||
if (this._high > this.max_value) this._high = this.max_value;
|
||||
break;
|
||||
case 2:
|
||||
// clicked bottom right
|
||||
chevron = this._root.querySelectorAll('path.dial__chevron--high')[0];
|
||||
this._high = this._high - config.step;
|
||||
if ((this._high - config.idle_zone) <= this._low) this._high = this._low + config.idle_zone;
|
||||
break;
|
||||
case 3:
|
||||
// clicked bottom left
|
||||
chevron = this._root.querySelectorAll('path.dial__chevron--low')[0];
|
||||
this._low = this._low - config.step;
|
||||
if (this._low < this.min_value) this._low = this.min_value;
|
||||
break;
|
||||
}
|
||||
SvgUtil.setClass(chevron, 'pressed', true);
|
||||
setTimeout(() => SvgUtil.setClass(chevron, 'pressed', false), 200);
|
||||
if (config.highlight_tap)
|
||||
SvgUtil.setClass(this._controls[index], 'control-visible', true);
|
||||
}
|
||||
else {
|
||||
if (index < 2) {
|
||||
// clicked top
|
||||
chevron = this._root.querySelectorAll('path.dial__chevron--target')[1];
|
||||
this._target = this._target + config.step;
|
||||
if (this._target > this.max_value) this._target = this.max_value;
|
||||
if (config.highlight_tap) {
|
||||
SvgUtil.setClass(this._controls[0], 'control-visible', true);
|
||||
SvgUtil.setClass(this._controls[1], 'control-visible', true);
|
||||
}
|
||||
} else {
|
||||
// clicked bottom
|
||||
chevron = this._root.querySelectorAll('path.dial__chevron--target')[0];
|
||||
this._target = this._target - config.step;
|
||||
if (this._target < this.min_value) this._target = this.min_value;
|
||||
if (config.highlight_tap) {
|
||||
SvgUtil.setClass(this._controls[2], 'control-visible', true);
|
||||
SvgUtil.setClass(this._controls[3], 'control-visible', true);
|
||||
}
|
||||
}
|
||||
SvgUtil.setClass(chevron, 'pressed', true);
|
||||
setTimeout(() => SvgUtil.setClass(chevron, 'pressed', false), 200);
|
||||
}
|
||||
if (config.highlight_tap) {
|
||||
setTimeout(() => {
|
||||
SvgUtil.setClass(this._controls[0], 'control-visible', false);
|
||||
SvgUtil.setClass(this._controls[1], 'control-visible', false);
|
||||
SvgUtil.setClass(this._controls[2], 'control-visible', false);
|
||||
SvgUtil.setClass(this._controls[3], 'control-visible', false);
|
||||
}, 200);
|
||||
}
|
||||
} else {
|
||||
this._enableControls();
|
||||
}
|
||||
}
|
||||
|
||||
_updateEdit(show_edit) {
|
||||
SvgUtil.setClass(this._root, 'dial--edit', show_edit);
|
||||
}
|
||||
|
||||
_enableControls() {
|
||||
const config = this._config;
|
||||
this._in_control = true;
|
||||
this._updateClass('in_control', this.in_control);
|
||||
if (this._timeoutHandler) clearTimeout(this._timeoutHandler);
|
||||
this._updateEdit(true);
|
||||
//this._updateClass('has-thermo', true);
|
||||
this._updateText('target', this.temperature.target);
|
||||
this._updateText('low', this.temperature.low);
|
||||
this._updateText('high', this.temperature.high);
|
||||
this._timeoutHandler = setTimeout(() => {
|
||||
this._updateText('ambient', this.ambient);
|
||||
this._updateEdit(false);
|
||||
//this._updateClass('has-thermo', false);
|
||||
this._in_control = false;
|
||||
this._updateClass('in_control', this.in_control);
|
||||
config.control();
|
||||
}, config.pending * 1000);
|
||||
}
|
||||
|
||||
_updateClass(class_name, flag) {
|
||||
SvgUtil.setClass(this._root, class_name, flag);
|
||||
}
|
||||
|
||||
_updateText(id, value) {
|
||||
const lblTarget = this._root.querySelector(`#${id}`).querySelectorAll('tspan');
|
||||
const text = Math.floor(value);
|
||||
if (value) {
|
||||
lblTarget[0].textContent = text;
|
||||
if (value % 1 != 0) {
|
||||
lblTarget[1].textContent = Math.round(value % 1 * 10);
|
||||
} else {
|
||||
lblTarget[1].textContent = '';
|
||||
}
|
||||
}
|
||||
|
||||
if (this.in_control && id == 'target' && this.dual) {
|
||||
lblTarget[0].textContent = '·';
|
||||
}
|
||||
|
||||
if(id =='title'){
|
||||
lblTarget[0].textContent = value;
|
||||
lblTarget[1].textContent = '';
|
||||
}
|
||||
}
|
||||
|
||||
_updateTemperatureSlot(value, offset, slot) {
|
||||
|
||||
const config = this._config;
|
||||
const lblSlot1 = this._root.querySelector(`#${slot}`)
|
||||
lblSlot1.textContent = value != null ? SvgUtil.superscript(value) : '';
|
||||
|
||||
const peggedValue = SvgUtil.restrictToRange(value, this.min_value, this.max_value);
|
||||
const position = [config.radius, config.ticks_outer_radius - (config.ticks_outer_radius - config.ticks_inner_radius) / 2];
|
||||
let degs = config.tick_degrees * (peggedValue - this.min_value) / (this.max_value - this.min_value) - config.offset_degrees + offset;
|
||||
const pos = SvgUtil.rotatePoint(position, degs, [config.radius, config.radius]);
|
||||
SvgUtil.attributes(lblSlot1, {
|
||||
x: pos[0],
|
||||
y: pos[1]
|
||||
});
|
||||
}
|
||||
|
||||
_updateColor(state, preset_mode) {
|
||||
|
||||
if(Object.prototype.toString.call(preset_mode) === "[object String]"){
|
||||
|
||||
if(state != 'off' && preset_mode.toLowerCase() == 'idle')
|
||||
state = 'idle'
|
||||
this._root.classList.forEach(c => {
|
||||
if (c.indexOf('dial--state--') != -1)
|
||||
this._root.classList.remove(c);
|
||||
});
|
||||
this._root.classList.add('dial--state--' + state);
|
||||
}
|
||||
}
|
||||
|
||||
_updateTicks(from, to, large_ticks, hvac_state) {
|
||||
const config = this._config;
|
||||
|
||||
const tickPoints = [
|
||||
[config.radius - 1, config.ticks_outer_radius],
|
||||
[config.radius + 1, config.ticks_outer_radius],
|
||||
[config.radius + 1, config.ticks_inner_radius],
|
||||
[config.radius - 1, config.ticks_inner_radius]
|
||||
];
|
||||
const tickPointsLarge = [
|
||||
[config.radius - 1.5, config.ticks_outer_radius],
|
||||
[config.radius + 1.5, config.ticks_outer_radius],
|
||||
[config.radius + 1.5, config.ticks_inner_radius + 20],
|
||||
[config.radius - 1.5, config.ticks_inner_radius + 20]
|
||||
];
|
||||
|
||||
this._ticks.forEach((tick, index) => {
|
||||
let isLarge = false;
|
||||
let isActive = (index >= from && index <= to) ? 'active '+hvac_state : '';
|
||||
large_ticks.forEach(i => isLarge = isLarge || (index == i));
|
||||
if (isLarge) isActive += ' large';
|
||||
const theta = config.tick_degrees / config.num_ticks;
|
||||
SvgUtil.attributes(tick, {
|
||||
d: SvgUtil.pointsToPath(SvgUtil.rotatePoints(isLarge ? tickPointsLarge : tickPoints, index * theta - config.offset_degrees, [config.radius, config.radius])),
|
||||
class: isActive
|
||||
});
|
||||
});
|
||||
}
|
||||
_updateDialog(modes,hass){
|
||||
this._modes_dialog.innerHTML = "";
|
||||
for(var i=0;i<modes.length;i++){
|
||||
let icon;
|
||||
let mode = modes[i];
|
||||
switch (mode) {
|
||||
case 'dry':
|
||||
icon = 'water-percent';
|
||||
break;
|
||||
case 'fan_only':
|
||||
icon = 'fan';
|
||||
break;
|
||||
case 'cool':
|
||||
icon = 'snowflake';
|
||||
break;
|
||||
case 'heat':
|
||||
icon = 'fire';
|
||||
break;
|
||||
case 'auto':
|
||||
icon = 'atom';
|
||||
break;
|
||||
case 'off':
|
||||
icon = 'power';
|
||||
break;
|
||||
}
|
||||
let d = document.createElement('span');
|
||||
d.innerHTML = `<ha-icon class="modeicon ${mode}" icon="mdi:${icon}"></ha-icon>`
|
||||
d.addEventListener('click', (e) => this._setMode(e,mode,hass));
|
||||
//this._modes[i].push(d);
|
||||
this._modes_dialog.appendChild(d)
|
||||
}
|
||||
}
|
||||
_buildCore(diameter) {
|
||||
return SvgUtil.createSVGElement('svg', {
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
viewBox: '0 0 ' + diameter + ' ' + diameter,
|
||||
class: 'dial'
|
||||
})
|
||||
}
|
||||
|
||||
openProp() {
|
||||
this._config.propWin(this.entity.entity_id)
|
||||
}
|
||||
_openDialog(){
|
||||
this._modes_dialog.className = "dialog modes";
|
||||
}
|
||||
_hideDialog(){
|
||||
this._modes_dialog.className = "dialog modes hide";
|
||||
}
|
||||
_setMode(e, mode,hass){
|
||||
console.log(mode);
|
||||
let config = this._config;
|
||||
if (this._timeoutHandlerMode) clearTimeout(this._timeoutHandlerMode);
|
||||
hass.callService('climate', 'set_hvac_mode', {
|
||||
entity_id: this._config.entity,
|
||||
hvac_mode: mode,
|
||||
});
|
||||
this._modes_dialog.className = "dialog modes "+mode+" pending";
|
||||
this._timeoutHandlerMode = setTimeout(() => {
|
||||
this._modes_dialog.className = "dialog modes hide";
|
||||
}, config.pending * 1000);
|
||||
e.stopPropagation();
|
||||
}
|
||||
_load_icon(state, ic_name){
|
||||
|
||||
let ic_dot = 'dot_r'
|
||||
if(ic_name == ''){
|
||||
ic_dot = 'dot_h'
|
||||
}
|
||||
|
||||
this._main_icon.innerHTML = `
|
||||
<div class="climate_info">
|
||||
<div class="mode_color"><span class="${ic_dot}"></span></div>
|
||||
<div class="modes"><ha-icon class="${state}" icon="mdi:${ic_name}"></ha-icon></div>
|
||||
</div>
|
||||
`;
|
||||
return this._main_icon;
|
||||
}
|
||||
_buildDialog(){
|
||||
this._modes_dialog.className = "dialog modes hide";
|
||||
return this._modes_dialog;
|
||||
}
|
||||
// build black dial
|
||||
_buildDial(radius) {
|
||||
return SvgUtil.createSVGElement('circle', {
|
||||
cx: radius,
|
||||
cy: radius,
|
||||
r: radius,
|
||||
class: 'dial__shape'
|
||||
})
|
||||
}
|
||||
// build circle around
|
||||
_buildRing(radius) {
|
||||
return SvgUtil.createSVGElement('path', {
|
||||
d: SvgUtil.donutPath(radius, radius, radius - 4, radius - 8),
|
||||
class: 'dial__editableIndicator',
|
||||
})
|
||||
}
|
||||
|
||||
_buildTicks(num_ticks) {
|
||||
const tick_element = SvgUtil.createSVGElement('g', {
|
||||
class: 'dial__ticks'
|
||||
});
|
||||
for (let i = 0; i < num_ticks; i++) {
|
||||
const tick = SvgUtil.createSVGElement('path', {})
|
||||
this._ticks.push(tick);
|
||||
tick_element.appendChild(tick);
|
||||
}
|
||||
return tick_element;
|
||||
}
|
||||
|
||||
_buildChevrons(radius, rotation, id, scale, offset) {
|
||||
const config = this._config;
|
||||
const translation = rotation > 0 ? -1 : 1;
|
||||
const width = config.chevron_size;
|
||||
const chevron_def = ["M", 0, 0, "L", width / 2, width * 0.3, "L", width, 0].map((x) => isNaN(x) ? x : x * scale).join(' ');
|
||||
const translate = [radius - width / 2 * scale * translation + offset, radius + 70 * scale * 1.1 * translation];
|
||||
const chevron = SvgUtil.createSVGElement('path', {
|
||||
class: `dial__chevron dial__chevron--${id}`,
|
||||
d: chevron_def,
|
||||
transform: `translate(${translate[0]},${translate[1]}) rotate(${rotation})`
|
||||
});
|
||||
return chevron;
|
||||
}
|
||||
|
||||
_buildThermoIcon(radius) {
|
||||
const thermoScale = radius / 3 / 100;
|
||||
const thermoDef = 'M 37.999 38.261 V 7 c 0 -3.859 -3.141 -7 -7 -7 s -7 3.141 -7 7 v 31.261 c -3.545 2.547 -5.421 6.769 -4.919 11.151 c 0.629 5.482 5.066 9.903 10.551 10.512 c 0.447 0.05 0.895 0.074 1.339 0.074 c 2.956 0 5.824 -1.08 8.03 -3.055 c 2.542 -2.275 3.999 -5.535 3.999 -8.943 C 42.999 44.118 41.14 40.518 37.999 38.261 Z M 37.666 55.453 c -2.146 1.921 -4.929 2.8 -7.814 2.482 c -4.566 -0.506 -8.261 -4.187 -8.785 -8.752 c -0.436 -3.808 1.28 -7.471 4.479 -9.56 l 0.453 -0.296 V 38 h 1 c 0.553 0 1 -0.447 1 -1 s -0.447 -1 -1 -1 h -1 v -3 h 1 c 0.553 0 1 -0.447 1 -1 s -0.447 -1 -1 -1 h -1 v -3 h 1 c 0.553 0 1 -0.447 1 -1 s -0.447 -1 -1 -1 h -1 v -3 h 1 c 0.553 0 1 -0.447 1 -1 s -0.447 -1 -1 -1 h -1 v -3 h 1 c 0.553 0 1 -0.447 1 -1 s -0.447 -1 -1 -1 h -1 v -3 h 1 c 0.553 0 1 -0.447 1 -1 s -0.447 -1 -1 -1 h -1 V 8 h 1 c 0.553 0 1 -0.447 1 -1 s -0.447 -1 -1 -1 H 26.1 c 0.465 -2.279 2.484 -4 4.899 -4 c 2.757 0 5 2.243 5 5 v 1 h -1 c -0.553 0 -1 0.447 -1 1 s 0.447 1 1 1 h 1 v 3 h -1 c -0.553 0 -1 0.447 -1 1 s 0.447 1 1 1 h 1 v 3 h -1 c -0.553 0 -1 0.447 -1 1 s 0.447 1 1 1 h 1 v 3 h -1 c -0.553 0 -1 0.447 -1 1 s 0.447 1 1 1 h 1 v 3 h -1 c -0.553 0 -1 0.447 -1 1 s 0.447 1 1 1 h 1 v 3 h -1 c -0.553 0 -1 0.447 -1 1 s 0.447 1 1 1 h 1 v 4.329 l 0.453 0.296 c 2.848 1.857 4.547 4.988 4.547 8.375 C 40.999 50.841 39.784 53.557 37.666 55.453 Z'.split(' ').map((x) => isNaN(x) ? x : x * thermoScale).join(' ');
|
||||
const translate = [radius - (thermoScale * 100 * 0.3), radius * 1.65]
|
||||
return SvgUtil.createSVGElement('path', {
|
||||
class: 'dial__ico__thermo',
|
||||
d: thermoDef,
|
||||
transform: 'translate(' + translate[0] + ',' + translate[1] + ')'
|
||||
});
|
||||
}
|
||||
|
||||
_buildDialSlot(index) {
|
||||
return SvgUtil.createSVGElement('text', {
|
||||
class: 'dial__lbl dial__lbl--ring',
|
||||
id: `temperature_slot_${index}`
|
||||
})
|
||||
}
|
||||
|
||||
_buildText(radius, name, offset) {
|
||||
const target = SvgUtil.createSVGElement('text', {
|
||||
x: radius + offset,
|
||||
y: radius-(name=='title'?radius/2:0),
|
||||
class: `dial__lbl dial__lbl--${name}`,
|
||||
id: name
|
||||
});
|
||||
const text = SvgUtil.createSVGElement('tspan', {
|
||||
});
|
||||
// hack
|
||||
if (name == 'target' || name == 'ambient') offset += 20;
|
||||
const superscript = SvgUtil.createSVGElement('tspan', {
|
||||
x: radius + radius / 3.1 + offset,
|
||||
y: radius - radius / 6,
|
||||
class: `dial__lbl--super--${name}`
|
||||
});
|
||||
target.appendChild(text);
|
||||
target.appendChild(superscript);
|
||||
return target;
|
||||
}
|
||||
|
||||
_buildControls(radius) {
|
||||
let startAngle = 270;
|
||||
let loop = 4;
|
||||
for (let index = 0; index < loop; index++) {
|
||||
const angle = 360 / loop;
|
||||
const sector = SvgUtil.anglesToSectors(radius, startAngle, angle);
|
||||
const controlsDef = 'M' + sector.L + ',' + sector.L + ' L' + sector.L + ',0 A' + sector.L + ',' + sector.L + ' 1 0,1 ' + sector.X + ', ' + sector.Y + ' z';
|
||||
const path = SvgUtil.createSVGElement('path', {
|
||||
class: 'dial__temperatureControl',
|
||||
fill: 'blue',
|
||||
d: controlsDef,
|
||||
transform: 'rotate(' + sector.R + ', ' + sector.L + ', ' + sector.L + ')'
|
||||
});
|
||||
this._controls.push(path);
|
||||
path.addEventListener('click', () => this._temperatureControlClicked(index));
|
||||
this._root.appendChild(path);
|
||||
startAngle = startAngle + angle;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class SvgUtil {
|
||||
static createSVGElement(tag, attributes) {
|
||||
const element = document.createElementNS('http://www.w3.org/2000/svg', tag);
|
||||
this.attributes(element, attributes)
|
||||
return element;
|
||||
}
|
||||
static attributes(element, attrs) {
|
||||
for (let i in attrs) {
|
||||
element.setAttribute(i, attrs[i]);
|
||||
}
|
||||
}
|
||||
// Rotate a cartesian point about given origin by X degrees
|
||||
static rotatePoint(point, angle, origin) {
|
||||
const radians = angle * Math.PI / 180;
|
||||
const x = point[0] - origin[0];
|
||||
const y = point[1] - origin[1];
|
||||
const x1 = x * Math.cos(radians) - y * Math.sin(radians) + origin[0];
|
||||
const y1 = x * Math.sin(radians) + y * Math.cos(radians) + origin[1];
|
||||
return [x1, y1];
|
||||
}
|
||||
// Rotate an array of cartesian points about a given origin by X degrees
|
||||
static rotatePoints(points, angle, origin) {
|
||||
return points.map((point) => this.rotatePoint(point, angle, origin));
|
||||
}
|
||||
// Given an array of points, return an SVG path string representing the shape they define
|
||||
static pointsToPath(points) {
|
||||
return points.map((point, iPoint) => (iPoint > 0 ? 'L' : 'M') + point[0] + ' ' + point[1]).join(' ') + 'Z';
|
||||
}
|
||||
static circleToPath(cx, cy, r) {
|
||||
return [
|
||||
"M", cx, ",", cy,
|
||||
"m", 0 - r, ",", 0,
|
||||
"a", r, ",", r, 0, 1, ",", 0, r * 2, ",", 0,
|
||||
"a", r, ",", r, 0, 1, ",", 0, 0 - r * 2, ",", 0,
|
||||
"z"
|
||||
].join(' ').replace(/\s,\s/g, ",");
|
||||
}
|
||||
static donutPath(cx, cy, rOuter, rInner) {
|
||||
return this.circleToPath(cx, cy, rOuter) + " " + this.circleToPath(cx, cy, rInner);
|
||||
}
|
||||
|
||||
static superscript(n) {
|
||||
|
||||
if ((n - Math.floor(n)) !== 0)
|
||||
n = Number(n).toFixed(1);;
|
||||
const x = `${n}${n == 0 ? '' : ''}`;
|
||||
return x;
|
||||
}
|
||||
|
||||
// Restrict a number to a min + max range
|
||||
static restrictToRange(val, min, max) {
|
||||
if (val < min) return min;
|
||||
if (val > max) return max;
|
||||
return val;
|
||||
}
|
||||
static setClass(el, className, state) {
|
||||
|
||||
|
||||
el.classList[state ? 'add' : 'remove'](className);
|
||||
}
|
||||
|
||||
static anglesToSectors(radius, startAngle, angle) {
|
||||
let aRad = 0 // Angle in Rad
|
||||
let z = 0 // Size z
|
||||
let x = 0 // Side x
|
||||
let X = 0 // SVG X coordinate
|
||||
let Y = 0 // SVG Y coordinate
|
||||
const aCalc = (angle > 180) ? 360 - angle : angle;
|
||||
aRad = aCalc * Math.PI / 180;
|
||||
z = Math.sqrt(2 * radius * radius - (2 * radius * radius * Math.cos(aRad)));
|
||||
if (aCalc <= 90) {
|
||||
x = radius * Math.sin(aRad);
|
||||
}
|
||||
else {
|
||||
x = radius * Math.sin((180 - aCalc) * Math.PI / 180);
|
||||
}
|
||||
Y = Math.sqrt(z * z - x * x);
|
||||
if (angle <= 180) {
|
||||
X = radius + x;
|
||||
}
|
||||
else {
|
||||
X = radius - x;
|
||||
}
|
||||
return {
|
||||
L: radius,
|
||||
X: X,
|
||||
Y: Y,
|
||||
R: startAngle
|
||||
}
|
||||
}
|
||||
}
|
||||
Binary file not shown.
@@ -1,590 +0,0 @@
|
||||
/*
|
||||
* Author : duytruong
|
||||
* Github : https://github.com/konnectedvn
|
||||
* Description :
|
||||
* Date : 01 Feb 2021 08:44:30+07:00
|
||||
* Based on : github.com/DBuit/hass-smart-home-panel-card (Thanks to DBuit!)
|
||||
*/
|
||||
console.info("%c [konnected.vn] Vertical Slider Cover Card \n%c Version v0.1.5","color: red; font-weight: bold; background: black", "color: white; font-weight: bold; background: dimgray");
|
||||
import {
|
||||
LitElement,
|
||||
html,
|
||||
css
|
||||
} from "https://unpkg.com/lit-element@2.0.1/lit-element.js?module";
|
||||
class VerticalSliderCoverCard extends LitElement {
|
||||
|
||||
static get properties() {
|
||||
return {
|
||||
hass: {},
|
||||
config: {},
|
||||
active: {},
|
||||
sliderVal: { type: Array }
|
||||
};
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.sliderVal = [];
|
||||
}
|
||||
|
||||
render() {
|
||||
var primaryTextColor = "var(--primary-text-color)";
|
||||
var icon = this.config.icon ? this.config.icon : "mdi:blinds";
|
||||
var iconSize = this.config.iconSize ? this.config.iconSize: "28px";
|
||||
var iconColor = this.config.iconColor ? this.config.iconColor : primaryTextColor;
|
||||
|
||||
var positionWidth = this.config.positionWidth ? this.config.positionWidth : "100px";
|
||||
var positionHeight = this.config.positionHeight ? this.config.positionHeight : "300px";
|
||||
var showName = this.config.showName ? this.config.showName : true;
|
||||
var showPosition = this.config.showPosition ? this.config.showPosition : true;
|
||||
var switchWidth = this.config.switchWidth ? this.config.switchWidth : positionWidth;
|
||||
var switchHeight = this.config.switchHeight ? this.config.switchHeight : switchWidth;
|
||||
var showSwitch = this.config.showSwitch;
|
||||
var switchFontColor = this.config.switchFontColor ? this.config.switchFontColor : primaryTextColor;
|
||||
var gapWidth = this.config.gapWidth ? this.config.gapWidth : "50px";
|
||||
|
||||
var countText = this.config.countText ? this.config.countText : "covers open";
|
||||
var countTextFontColor = this.config.countTextFontColor ? this.config.countTextFontColor : primaryTextColor;
|
||||
var openBaseline = this.config.closedBaseline ? this.config.closedBaseline : 0;
|
||||
var entityCounter = 0;
|
||||
|
||||
var showButton = this.config.showButton ? this.config.showButton : false;
|
||||
var buttonText = this.config.buttonText ? this.config.buttonText : "Home";
|
||||
var buttonPath = this.config.buttonPath ? this.config.buttonPath : "/lovelace/0";
|
||||
var buttonService = this.config.buttonService ? this.config.buttonService: "";
|
||||
var buttonData = this.config.buttonData ? this.config.buttonData : "";
|
||||
var buttonFontColor = this.config.buttonFontColor ? this.config.buttonFontColor : primaryTextColor;
|
||||
|
||||
var background = this.config.background ? this.config.background : "transparent";
|
||||
var sideColor1 = this.config.sideColor1 ? this.config.sideColor1 : "#ffcccc";
|
||||
var sideColor2 = this.config.sideColor2 ? this.config.sideColor2 : '#b30000';
|
||||
var switchColor = this.config.switchColor ? this.config.switchColor : sideColor2;
|
||||
var closedColor = this.config.closedColor ? this.config.closedColor : "hsl(0, 0%, 20%)";
|
||||
var openColor = this.config.openColor ? this.config.openColor : "hsl(0, 0%, 90%, 0.6)";
|
||||
var panelType = this.config.panelType;
|
||||
var showSidebar = this.config.showSidebar;
|
||||
var titleSize = this.config.titleSize ? this.config.titleSize : "40px";
|
||||
var titleFontColor = this.config.titleFontColor ? this.config.titleFontColor : primaryTextColor;
|
||||
|
||||
return html`
|
||||
<ha-card>
|
||||
<div class="page" style="background:${background};">
|
||||
|
||||
<div class="side" style="${this._panelSize(panelType)};--show-sidebar:${this._showFlex(showSidebar)};--sideColor-1:${sideColor1};--sideColor-2:${sideColor2};">
|
||||
<div class="header">
|
||||
|
||||
</div>
|
||||
<div class="center">
|
||||
<div class="icon">
|
||||
<ha-icon style="--mdc-icon-size:${iconSize};--icon-color:${iconColor};" icon="${icon}" />
|
||||
</div>
|
||||
<h1 style="--title-size:${titleSize};--title-font-color:${titleFontColor};">${this.config.title}</h1>
|
||||
<h3 style="--count-font-color:${countTextFontColor};">${this._stateCount(openBaseline)} ${countText}</h3>
|
||||
</div>
|
||||
<div class="bottom">
|
||||
${showButton ? html`<button class="back-btn" style="--button-size:${this._buttonFont(titleSize,buttonText)}px;--button-font-color:${buttonFontColor};" @click=${e => this._navigate(buttonPath,buttonService,buttonData)}>${buttonText}</button>` : html``}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="main">
|
||||
<div class="inner-main">
|
||||
${this.config.entities.map(ent => {
|
||||
entityCounter++;
|
||||
var switchValue = 0;
|
||||
const stateObj = this.hass.states[ent.entity];
|
||||
switch(stateObj.state) {
|
||||
case 'open':
|
||||
switchValue = 100;
|
||||
break;
|
||||
case 'closed':
|
||||
switchValue = 0;
|
||||
break;
|
||||
default:
|
||||
switchValue = 0;
|
||||
}
|
||||
return stateObj ? html`
|
||||
<div class="cover" style="--cover-width:${this._coverSize(positionWidth,gapWidth,panelType)};--center-slider:${this._centerSliders(panelType)};">
|
||||
<div class="cover-slider">
|
||||
<p class="cover-name" style="--show-name: ${this._showBlock(showName)};--cover-fontSize: ${this._coverNameFont(positionWidth,gapWidth)}px;">${ent.name || stateObj.attributes.friendly_name}</p>
|
||||
${stateObj.attributes.supported_features > 6 ? html`
|
||||
<p class="cover-position" style="--show-position: ${this._showBlock(showPosition)};--cover-fontSize: ${parseInt(positionWidth.replace(/px/,"")) / 4 - (parseInt(positionWidth.replace(/px/,"")) - 60) / 4}px;">${this._coverPosition(stateObj.state, stateObj.attributes.current_position, stateObj.entity_id)}</p>
|
||||
<div class="range-holder" style="--slider-height: ${positionHeight};--closed-color: ${closedColor};">
|
||||
<input type="range" class="${stateObj.state}" style="--slider-width: ${positionWidth};--slider-height: ${positionHeight};--closed-color: ${closedColor};--open-color: ${openColor};" .value="${stateObj.state === "closed" ? 0 : Math.round(stateObj.attributes.current_position)}" @input=${e => this._sliderChange(e.target.value, stateObj.entity_id)}} @change=${e => this._setPosition(stateObj.entity_id, e.target.value, ent.script)}>
|
||||
</div>
|
||||
` : html`
|
||||
<h4>${stateObj.state}</h4>
|
||||
<div class="switch-holder" style="--switch-height: ${switchHeight};">
|
||||
<input type="range" class="${stateObj.state}" style="--switch-width: ${switchWidth};--switch-height: ${switchHeight};" value="0" min="0" max="1" .value="${switchValue}" @change=${e => this._switch(stateObj)}>
|
||||
</div>
|
||||
`}
|
||||
<div class="toggle" style="--show-switch: ${this._showFlex(showSwitch)};">
|
||||
<input ?checked=${stateObj.state == "open"} type="checkbox" id="toggle${entityCounter}" class="toggle-btn" @change=${e => this._switch(stateObj)} />
|
||||
<label for="toggle${entityCounter}" style="--switch-width: ${switchWidth};--switch-height: ${switchHeight};--switch-font-color: ${switchFontColor};--switch-color: ${switchColor};--switch-labelSize: ${parseInt(switchWidth.replace(/px/,"")) / 5}px;"><span></span></label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`: html``;
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ha-card>
|
||||
`;
|
||||
}
|
||||
|
||||
updated() {}
|
||||
|
||||
_sliderChange(value, entity_id){
|
||||
this.sliderVal[entity_id] = {val: value, active: true};
|
||||
this.requestUpdate();
|
||||
}
|
||||
|
||||
_coverPosition(coverState, coverPos, entity_id){
|
||||
if (coverState === "closed") {
|
||||
return '0';
|
||||
} else if (typeof this.sliderVal[entity_id] === 'undefined' || !this.sliderVal[entity_id]['active']) {
|
||||
return Math.round(coverPos);
|
||||
} else {
|
||||
return this.sliderVal[entity_id]['val'];
|
||||
}
|
||||
}
|
||||
|
||||
_setPosition(entity_id, value, script) {
|
||||
if (this.hass.states[entity_id].attributes.current_position === value) {
|
||||
return;
|
||||
}
|
||||
this.hass.callService("cover", "set_cover_position", {
|
||||
entity_id: entity_id,
|
||||
position: value
|
||||
});
|
||||
this.sliderVal[entity_id]['active'] = false;
|
||||
if (script) {
|
||||
this.hass.callService("script", "turn_on", {
|
||||
entity_id: script
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
_stateCount(baseline) {
|
||||
let count = 0;
|
||||
this.config.entities.map(ent => {
|
||||
const stateObj = this.hass.states[ent.entity];
|
||||
if(stateObj.state === "open" && baseline === 0) {
|
||||
count++;
|
||||
} else if (stateObj.attributes.current_position >= baseline && baseline > 0) {
|
||||
count++;
|
||||
}
|
||||
})
|
||||
return count;
|
||||
}
|
||||
|
||||
_panelSize(panelType) {
|
||||
let sideWidth = 40;
|
||||
if (panelType === true) {
|
||||
sideWidth = 30;
|
||||
}
|
||||
return "--side-width:" + sideWidth + "px";
|
||||
}
|
||||
|
||||
_showFlex(showSidebar) {
|
||||
if (showSidebar === false) {
|
||||
return "none";
|
||||
}
|
||||
return "flex";
|
||||
}
|
||||
|
||||
_showBlock(confValue) {
|
||||
if(confValue === false) {
|
||||
return "none";
|
||||
}
|
||||
return "block";
|
||||
}
|
||||
|
||||
_coverSize(positionWidth, gapWidth, panelType) {
|
||||
if (panelType === false) {
|
||||
return (parseInt(positionWidth.replace(/px/,"")) + parseInt(gapWidth.replace(/px/,""))) + "px";
|
||||
} else {
|
||||
return "50%";
|
||||
}
|
||||
}
|
||||
|
||||
_buttonFont(titleSize,buttonText) {
|
||||
let fieldSize = parseInt(titleSize.replace(/px/,"")) * this.config.title.length;
|
||||
let buttonSize = fieldSize / buttonText.length;
|
||||
return buttonSize * 0.5;
|
||||
}
|
||||
|
||||
_centerSliders(panelType) {
|
||||
if (panelType === true) {
|
||||
return "0 auto";
|
||||
} else {
|
||||
return "0";
|
||||
}
|
||||
}
|
||||
|
||||
_coverNameFont(positionWidth, gapWidth) {
|
||||
let maxLength = 0;
|
||||
this.config.entities.map(ent => {
|
||||
const stateObj = this.hass.states[ent.entity];
|
||||
let name = ent.name || stateObj.attributes.friendly_name;
|
||||
if(name.length > maxLength) {
|
||||
maxLength = name.length;
|
||||
}
|
||||
})
|
||||
let fontsize = parseInt(positionWidth.replace(/px/,""));
|
||||
if (parseInt(gapWidth.replace(/px/,"")) > 50) {
|
||||
fontsize = fontsize + (parseInt(gapWidth.replace(/px/,"")) / 2);
|
||||
} else {
|
||||
fontsize = fontsize + parseInt(gapWidth.replace(/px/,""));
|
||||
}
|
||||
return ((( fontsize - 4 ) / maxLength) * 1.8) | 0;
|
||||
}
|
||||
|
||||
_switch(state) {
|
||||
this.hass.callService("cover", "stop_cover", {
|
||||
entity_id: state.entity_id
|
||||
});
|
||||
}
|
||||
|
||||
_navigate(path,service,data) {
|
||||
if (service.length === 0) {
|
||||
window.location.href = path;
|
||||
} else {
|
||||
let domain = service.split(".",2)[0];
|
||||
let ser = service.split(".",2)[1];
|
||||
this.hass.callService(domain,ser, {
|
||||
entity_id: data
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
setConfig(config) {
|
||||
if (!config.entities) {
|
||||
throw new Error("You need to define entities");
|
||||
}
|
||||
if (!config.title) {
|
||||
throw new Error("You need to define a title");
|
||||
}
|
||||
for (var i = 0, len = config.entities.length; i < len; i++) {
|
||||
if (config.entities[i].entity === undefined) {
|
||||
throw new Error(config.entities[i] + " is INVALID! Should be object: - entity: " + config.entities[i] + ".");
|
||||
}
|
||||
}
|
||||
this.config = config;
|
||||
}
|
||||
getCardSize() {
|
||||
return this.config.entities.length + 1;
|
||||
}
|
||||
|
||||
static get styles() {
|
||||
return css`
|
||||
:host([is-panel]) ha-card {
|
||||
left: 50;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
}
|
||||
ha-card {
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
.page {
|
||||
width:100%;
|
||||
height:100%;
|
||||
display:flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
.page > .side {
|
||||
padding: 10px;
|
||||
width: var(--side-width)%;
|
||||
display:var(--show-sidebar);
|
||||
flex-direction:column;
|
||||
background: rgb(28,122,226);
|
||||
background: linear-gradient(145deg, var(--sideColor-1) 0%, var(--sideColor-2) 90%);
|
||||
justify-content:space-between;
|
||||
}
|
||||
.side .header {
|
||||
}
|
||||
.side .center {
|
||||
display:flex;
|
||||
flex-direction:column;
|
||||
}
|
||||
.side .center .icon {
|
||||
display:block;
|
||||
overflow:hidden;
|
||||
text-align:center;
|
||||
}
|
||||
.side .center .icon ha-icon {
|
||||
color:var(--icon-color);
|
||||
}
|
||||
.side .center h1 {
|
||||
color:var(--title-font-color);
|
||||
margin:10px 0 0 5px;
|
||||
font-weight:400;
|
||||
font-size: var(--title-size);
|
||||
line-height: var(--title-size);
|
||||
text-align: center;
|
||||
}
|
||||
.side .center h3 {
|
||||
color:var(--count-font-color);
|
||||
margin:5px 0 5px 0;
|
||||
font-size: 120%;
|
||||
font-weight: 400;
|
||||
line-height: 100%;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.side .bottom {
|
||||
}
|
||||
|
||||
.back-btn {
|
||||
border:2px solid var(--button-font-color);
|
||||
color:var(--button-font-color);
|
||||
background:transparent;
|
||||
font-size:var(--button-size);
|
||||
border-radius: 4px;
|
||||
width:100%;
|
||||
display:block;
|
||||
padding: 10px 0;
|
||||
}
|
||||
|
||||
.page > .main {
|
||||
width:100%;
|
||||
overflow-x:scroll;
|
||||
padding-bottom: 0px;
|
||||
-ms-overflow-style: none; /* IE and Edge */
|
||||
scrollbar-width: none; /* Firefox */
|
||||
}
|
||||
.page > .main::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
.page > .main > .inner-main {
|
||||
display:flex;
|
||||
flex-direction:row;
|
||||
align-items:center;
|
||||
height:100%;
|
||||
margin: auto;
|
||||
padding-right: 0px;
|
||||
}
|
||||
.page > .main > .inner-main > .cover {
|
||||
width: var(--cover-width);
|
||||
display:inline-block;
|
||||
margin: var(--center-slider);
|
||||
padding-bottom: 0px;
|
||||
}
|
||||
|
||||
.cover .icon {
|
||||
margin: 0 auto;
|
||||
text-align:center;
|
||||
display:block;
|
||||
height: 50px;
|
||||
width: 50px;
|
||||
color: rgba(255,255,255,0.3);
|
||||
font-size: 30px;
|
||||
padding-top:5px;
|
||||
}
|
||||
.cover .icon ha-icon {
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
text-align:center;
|
||||
}
|
||||
.cover .icon.on ha-icon {
|
||||
fill: #f7d959;
|
||||
}
|
||||
h2 {
|
||||
color: #FFF;
|
||||
display: block;
|
||||
font-weight: 300;
|
||||
margin-bottom: 10px;
|
||||
text-align: center;
|
||||
font-size:20px;
|
||||
margin-top:0;
|
||||
}
|
||||
|
||||
h3 {
|
||||
color: #FFF;
|
||||
display: block;
|
||||
font-weight: 300;
|
||||
margin-top: 5px;
|
||||
margin-bottom: 5px;
|
||||
text-align: center;
|
||||
font-size:18px;
|
||||
}
|
||||
|
||||
.cover-name {
|
||||
display: var(--show-name);
|
||||
font-weight: 300;
|
||||
margin-top: calc(var(--cover-fontSize) / 3);
|
||||
margin-bottom: calc(var(--cover-fontSize) / 2);
|
||||
text-align: center;
|
||||
font-size: var(--cover-fontSize);
|
||||
}
|
||||
.cover-position {
|
||||
display: var(--show-position);
|
||||
font-weight: 300;
|
||||
margin-top: calc(var(--cover-fontsize) / 2);
|
||||
margin-bottom: var(--cover-fontsize);
|
||||
text-align: center;
|
||||
font-size: var(--cover-fontSize);
|
||||
}
|
||||
.cover-slider .cover-name, .cover-position {
|
||||
color: var(--primary-text-color);
|
||||
}
|
||||
|
||||
h4 {
|
||||
color: var(--primary-text-color);
|
||||
display: block;
|
||||
font-weight: 300;
|
||||
margin-bottom: 20px;
|
||||
text-align: center;
|
||||
font-size:16px;
|
||||
margin-top:0;
|
||||
}
|
||||
.cover-position:after {
|
||||
content: "%";
|
||||
padding-left: 1px;
|
||||
}
|
||||
|
||||
.range-holder {
|
||||
height: var(--slider-height);
|
||||
position:relative;
|
||||
display: block;
|
||||
}
|
||||
.range-holder input[type="range"] {
|
||||
outline: 0;
|
||||
border: 0;
|
||||
border-radius: 4px;
|
||||
width: var(--slider-height);
|
||||
margin: 0;
|
||||
transition: box-shadow 0.2s ease-in-out;
|
||||
-webkit-transform:rotate(270deg);
|
||||
-moz-transform:rotate(270deg);
|
||||
-o-transform:rotate(270deg);
|
||||
-ms-transform:rotate(270deg);
|
||||
transform:rotate(270deg);
|
||||
overflow: hidden;
|
||||
height: var(--slider-width);
|
||||
-webkit-appearance: none;
|
||||
background-color: var(--closed-color);
|
||||
position: absolute;
|
||||
top: calc(50% - (var(--slider-width) / 2));
|
||||
right: calc(50% - (var(--slider-height) / 2));
|
||||
}
|
||||
.range-holder input[type="range"]::-webkit-slider-runnable-track {
|
||||
height: var(--slider-width);
|
||||
-webkit-appearance: none;
|
||||
color: var(--open-color);
|
||||
margin-top: 0px;
|
||||
transition: box-shadow 0.2s ease-in-out;
|
||||
}
|
||||
.range-holder input[type="range"]::-webkit-slider-thumb {
|
||||
width: calc((var(--slider-width) / 5) + 2px);
|
||||
border-right:8px solid var(--closed-color);
|
||||
border-left:8px solid var(--closed-color);
|
||||
border-top:20px solid var(--closed-color);
|
||||
border-bottom:20px solid var(--closed-color);
|
||||
-webkit-appearance: none;
|
||||
height: var(--slider-width);
|
||||
cursor: ew-resize;
|
||||
background: var(--closed-color);
|
||||
box-shadow: -350px 0 0 350px var(--open-color), inset 0 0 0 80px var(--open-color);
|
||||
border-radius: 0;
|
||||
transition: box-shadow 0.2s ease-in-out;
|
||||
position: relative;
|
||||
top: 0;
|
||||
}
|
||||
// .range-holder input[type="range"].on::-webkit-slider-thumb {
|
||||
// border-color: #1c7ae2;
|
||||
// box-shadow: -350px 0 0 350px #1c7ae2, inset 0 0 0 80px #FFF;
|
||||
// }
|
||||
|
||||
.switch-holder {
|
||||
height: var(--switch-height);
|
||||
position:relative;
|
||||
display: block;
|
||||
}
|
||||
.switch-holder input[type="range"] {
|
||||
outline: 0;
|
||||
border: 0;
|
||||
border-radius: 4px;
|
||||
width: calc(var(--switch-height) - 20px);
|
||||
margin: 0;
|
||||
transition: box-shadow 0.2s ease-in-out;
|
||||
-webkit-transform: rotate(270deg);
|
||||
-moz-transform: rotate(270deg);
|
||||
-o-transform: rotate(270deg);
|
||||
-ms-transform: rotate(270deg);
|
||||
transform: rotate(270deg);
|
||||
overflow: hidden;
|
||||
height: calc(var(--switch-width) - 20px);
|
||||
-webkit-appearance: none;
|
||||
background-color: var(--switch-color);
|
||||
color: var(--switch-font-color);
|
||||
padding: 10px;
|
||||
position: absolute;
|
||||
top: calc(50% - (var(--switch-width) / 2));
|
||||
right: calc(50% - (var(--switch-height) / 2));
|
||||
}
|
||||
.switch-holder input[type="range"]::-webkit-slider-runnable-track {
|
||||
height: calc(var(--switch-width) - 20px);
|
||||
-webkit-appearance: none;
|
||||
color: var(--switch-color);
|
||||
margin-top: -1px;
|
||||
transition: box-shadow 0.2s ease-in-out;
|
||||
}
|
||||
.switch-holder input[type="range"]::-webkit-slider-thumb {
|
||||
width: calc(var(--switch-height) / 2);
|
||||
-webkit-appearance: none;
|
||||
height: calc(var(--switch-width) - 20px);
|
||||
cursor: ew-resize;
|
||||
background: var(--switch-color);
|
||||
color: var(--switch-font-color);
|
||||
transition: box-shadow 0.2s ease-in-out;
|
||||
box-shadow: -340px 0 0 350px #4d4d4d, inset 0 0 0 80px #969696;
|
||||
position: relative;
|
||||
top: 0;
|
||||
border-radius: 4px;
|
||||
}
|
||||
// .switch-holder input[type="range"].on::-webkit-slider-thumb {
|
||||
// box-shadow: -340px 0 0 350px #4d4d4d, inset 0 0 0 80px #1c7ae2;
|
||||
// }
|
||||
.toggle {
|
||||
margin-top: 20px;
|
||||
margin-bottom: 10px;
|
||||
display: var(--show-switch);
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.toggle > input.toggle-btn {
|
||||
display: none;
|
||||
}
|
||||
.toggle > input.toggle-btn + label {
|
||||
border: 1px solid #FFF;
|
||||
background: transparent;
|
||||
width: var(--switch-width);
|
||||
height: var(--switch-height);
|
||||
text-align:center;
|
||||
line-height: var(--switch-height);
|
||||
cursor: pointer;
|
||||
border-radius: 4px;
|
||||
color: var(--switch-font-color);
|
||||
display:block;
|
||||
font-size:var(--switch-labelSize);
|
||||
}
|
||||
.toggle > input.toggle-btn + label:active,
|
||||
.toggle > input.toggle-btn + label {
|
||||
background: var(--switch-color);
|
||||
border-color: var(--switch-color);
|
||||
}
|
||||
.toggle > input.toggle-btn + label> span:before {
|
||||
content: 'STOP';
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
||||
}
|
||||
customElements.define('vertical-slider-cover-card', VerticalSliderCoverCard);
|
||||
Binary file not shown.
File diff suppressed because one or more lines are too long
Binary file not shown.
Reference in New Issue
Block a user