-
Notifications
You must be signed in to change notification settings - Fork 10
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
ab580cf
commit 9d73719
Showing
1 changed file
with
150 additions
and
70 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,15 +1,15 @@ | ||
//TODO | ||
//Add automatic area on <p bottom> when supported | ||
//value template support | ||
//Scale text when card is smaller than ~200px | ||
//Select mode color | ||
//Keep displaying newValue until the old value is updated | ||
import { | ||
LitElement, | ||
html, | ||
css | ||
} from "https://unpkg.com/[email protected]/lit-element.js?module"; | ||
|
||
console.info('%c DIMMER-BUTTON %c 0.3 ','color: antiquewhite; background: #B565C6;','color: salmon; background: gray;'); | ||
|
||
class DimmerButton extends LitElement { | ||
|
||
static get properties() { | ||
|
@@ -20,14 +20,15 @@ class DimmerButton extends LitElement { | |
} | ||
|
||
static getStubConfig() { | ||
return { entity: '#Required',name: '#friendly_name',mode: '#supports "brightness" or "color_temp" for light and "volume" for media_player', bottom: "#optional text under name", height: "",background: "",foreground: "",icon: "",on_icon: "",off_icon: "",on_color: "",off_color: "" } | ||
return { entity: '#Required',name: '#friendly_name',mode: '#supports "brightness" or "color_temp" for light and "volume" for media_player', direction: "horizontal", bottom: "#optional text under name", height: "",background: "",foreground: "",icon: "",on_icon: "",off_icon: "",on_color: "",off_color: "" } | ||
} | ||
|
||
constructor() { | ||
super(); | ||
this.hold = false; | ||
this.dim = false; | ||
this.disabled = false; | ||
this.move = false; | ||
this.start = false; | ||
this.delta = 3; | ||
this.startY; | ||
this.startX; | ||
|
@@ -41,6 +42,9 @@ class DimmerButton extends LitElement { | |
this.maxVol; | ||
this.newValue = 0; | ||
this.longPress = null; | ||
this.active = ''; | ||
this.cardWidth; | ||
this.vertical = false; | ||
} | ||
|
||
entityConfig(entity) { | ||
|
@@ -68,14 +72,14 @@ class DimmerButton extends LitElement { | |
this.iconOff = this.config.icon ? this.config.icon : this.config.off_icon ? this.config.off_icon : entity.attributes.icon ? entity.attributes.icon : "mdi:eye"; | ||
this.mode = "static"; | ||
this.displayState = entity.attributes.unit_of_measurement ? entity.attributes.unit_of_measurement: ''; | ||
this.disabled = true; | ||
this.rangeMax = 0; | ||
}else if(entity.entity_id.includes("media_player.")){ | ||
this.iconOn = this.config.icon ? this.config.icon : this.config.on_icon ? this.config.on_icon : entity.attributes.icon ? entity.attributes.icon : "mdi:cast"; | ||
this.iconOff = this.config.icon ? this.config.icon : this.config.off_icon ? this.config.off_icon : entity.attributes.icon ? entity.attributes.icon : "mdi:cast"; | ||
if(entity.attributes.supported_features & 4 && this.config.mode == "volume") { | ||
this.mode = "volume"; | ||
this.displayState = (entity.state === "playing" ? '• '+(this.newValue != 0 ? this.newValue : Math.round((entity.attributes.volume_level*100)))+'%' : ''); | ||
this.maxVol = this.config.max_volume ? this.config.max_volume : 100; | ||
this.rangeMax = this.maxVol; | ||
this.rangeValue = (entity.attributes.volume_level*100); | ||
}else{ | ||
|
@@ -92,21 +96,36 @@ class DimmerButton extends LitElement { | |
render() { | ||
const entity = this.config.entity; | ||
const entityStates = this.hass.states[entity] | ||
let background = this.config.background ? this.config.background : "var(--ha-card-background)"; | ||
let foreground = this.config.foreground ? this.config.foreground : "var(--primary-color)"; | ||
const name = this.config.name ? this.config.name : entityStates.attributes.friendly_name; | ||
const onColor = this.config.on_color ? this.config.on_color : "#fdd835"; | ||
const offColor = this.config.off_color ? this.config.off_color : "gray"; | ||
const cardHeight = this.config.height ? this.config.height : "150px"; | ||
const bottomText = this.config.bottom; | ||
let bottomText = parseInt(cardHeight) >= 150 ? this.config.bottom : ''; | ||
let background = this.config.background ? this.config.background : "var(--ha-card-background)"; | ||
let foreground = this.config.foreground ? this.config.foreground : "var(--primary-color)"; | ||
let fontSizeH = parseInt(cardHeight) >= 150 ? "20px" : (20-((150-parseInt(cardHeight))/25))+"px"; | ||
let fontSizeW = this.cardWidth >= 200 ? "20px" : (20-((200-this.cardWidth)/12.5))+"px"; | ||
let fontSize = fontSizeH < fontSizeW ? fontSizeH : fontSizeW; | ||
this.vertical = this.config.direction == 'vertical' ? true : false; | ||
this.entityConfig(entityStates); | ||
return html` | ||
<ha-card> | ||
<div class="button" style="${this.mode == "static" ? (entityStates.state == "on" ? "--dimmer-background:"+foreground : "--dimmer-background:"+background) : "--dimmer-background:"+background};--dimmer-foreground:${foreground};--color-on:${onColor};--color-off:${offColor};--card-height:${cardHeight};"> | ||
<p class="top ${entityStates.state}"><ha-icon class="icon" icon=${entityStates.state === "off" ? this.iconOff : this.iconOn}></ha-icon>${entityStates.state} ${this.displayState}</p> | ||
<p class="middle">${name}</p> | ||
${bottomText ? html`<p class="bottom">${bottomText}</p>`: ''} | ||
<input type="range" ?disabled="${this.disabled}" min="0" max="${this.rangeMax}" class="${entityStates.state}" .value="${this.rangeValue}" | ||
<div class="button ${this.active}" style=" | ||
${this.mode == "static" ? (entityStates.state == "on" ? "--dimmer-background:"+foreground : "--dimmer-background:"+background) : "--dimmer-background:"+background}; | ||
--dimmer-foreground:${foreground}; | ||
--color-on:${onColor}; | ||
--color-off:${offColor}; | ||
--card-height:${cardHeight}; | ||
--font-size:${fontSize}; | ||
--rotation:${this.vertical ? '270deg' : '0deg' }; | ||
${this.vertical ? "--range-width:"+cardHeight+"; --range-height:500px; --right:500px; --touch: none" : "--range-width: 100%; --range-height:100%; --right:0; --touch: pan-y;" };" | ||
> | ||
<div class="text"> | ||
<span class="top ${entityStates.state}"><ha-icon class="icon" icon=${entityStates.state === "off" ? this.iconOff : this.iconOn}></ha-icon>${entityStates.state} ${this.displayState}</span> | ||
<span class="middle">${name}</span> | ||
${bottomText ? html`<span class="bottom">${bottomText}</span>`: ''} | ||
</div> | ||
<input type="range" min="0" max="${entityStates.state !== "unavailable" ? this.rangeMax : "0" }" .value="${this.rangeValue}" | ||
@pointerdown=${e => this._startCords(entity, e)} | ||
@pointerup=${e => this._endCords(entityStates, e)} | ||
@pointermove=${e => this._moveHandler(e)} | ||
|
@@ -123,40 +142,49 @@ class DimmerButton extends LitElement { | |
let diffY = Math.abs(this.startY-e.pageY); | ||
let posDelta = 6; | ||
if(diffX > posDelta || diffY > posDelta){ | ||
clearTimeout(this.longPress); | ||
if(this.start) { | ||
this.move = true; | ||
} | ||
clearTimeout(this.longPress); | ||
}; | ||
} | ||
|
||
_displayValue(e) { | ||
this.newValue = parseInt(e); | ||
this.requestUpdate(); | ||
if(this.move){ | ||
this.newValue = parseInt(e); | ||
this.requestUpdate(); | ||
} | ||
} | ||
|
||
_startCords(entity, e) { | ||
this.startX = e.pageX; | ||
this.startY = e.pageY; | ||
this.clientY = e.clientY; | ||
this.start = true; | ||
this.active = "active"; | ||
let target = e.target.parentElement; | ||
this.longPress = setTimeout(() => this._moreInfo('hass-more-info', { entityId: this.config.entity }, target), 600); | ||
} | ||
|
||
_endCords(entity, e) { | ||
clearTimeout(this.longPress); | ||
this.newValue = 0; | ||
let diffX = Math.abs(e.pageX - this.startX); | ||
let diffY = Math.abs(e.pageY - this.startY); | ||
let scrollY = Math.abs(e.clientY - this.clientY); | ||
this.move = false; | ||
this.start = false; | ||
this.active = ''; | ||
if(this.hold){ | ||
this.hold = false; | ||
return false; | ||
this.hold = false; | ||
return false; | ||
}; | ||
if((diffX < this.delta && diffY < this.delta)&&(e.button == 0 || e.button == undefined)){ | ||
this.dim = false; | ||
this._toggle(entity); | ||
}else{ | ||
this.dim = true; | ||
this.dim = true; | ||
}; | ||
if(scrollY > 50){ | ||
if(scrollY > 50 && !this.vertical){ | ||
this.dim = false; | ||
} | ||
} | ||
|
@@ -204,113 +232,157 @@ class DimmerButton extends LitElement { | |
let num = 0; | ||
if(this.dim){ | ||
switch(this.mode){ | ||
case "brightness": | ||
this.hass.callService("homeassistant", "turn_on", { | ||
entity_id: entity.entity_id, | ||
brightness: value * 2.55 | ||
}); | ||
break; | ||
case "color_temp": | ||
num = Math.round(((entity.attributes.max_mireds-entity.attributes.min_mireds)*(value/100))+entity.attributes.min_mireds); | ||
this.hass.callService("light", "turn_on", { | ||
entity_id: entity.entity_id, | ||
color_temp: num | ||
}); | ||
break; | ||
case "volume": | ||
this.maxVol = this.config.max_volume ? this.config.max_volume : 100; | ||
num = this.maxVol>value ? (value/100) : (this.maxVol/100); | ||
this.hass.callService("media_player", "volume_set", { | ||
entity_id: entity.entity_id, | ||
volume_level: num | ||
}); | ||
break; | ||
case "pause": | ||
case "toggle": | ||
this._toggle(entity); | ||
break; | ||
case "brightness": | ||
this.hass.callService("homeassistant", "turn_on", { | ||
entity_id: entity.entity_id, | ||
brightness: value * 2.55 | ||
}); | ||
break; | ||
case "color_temp": | ||
num = Math.round(((entity.attributes.max_mireds-entity.attributes.min_mireds)*(value/100))+entity.attributes.min_mireds); | ||
this.hass.callService("light", "turn_on", { | ||
entity_id: entity.entity_id, | ||
color_temp: num | ||
}); | ||
break; | ||
case "volume": | ||
num = this.maxVol>value ? (value/100) : (this.maxVol/100); | ||
this.hass.callService("media_player", "volume_set", { | ||
entity_id: entity.entity_id, | ||
volume_level: num | ||
}); | ||
break; | ||
case "pause": | ||
case "toggle": | ||
this._toggle(entity); | ||
break; | ||
default: | ||
} | ||
}else{ | ||
e.target.value = this.rangeValue; | ||
this.newValue = 0; | ||
this.dim = false; | ||
this.requestUpdate(); | ||
return false; | ||
e.target.value = this.rangeValue; | ||
this.newValue = 0; | ||
this.dim = false; | ||
this.requestUpdate(); | ||
return false; | ||
} | ||
this.dim = false; | ||
} | ||
|
||
updated(changedProperties) { | ||
this.cardWidth = this.getBoundingClientRect().width; | ||
if(!this.move){ | ||
this.newValue = 0; | ||
} | ||
} | ||
|
||
setConfig(config) { | ||
if (!config.entity) { | ||
throw new Error("You need to define entity"); | ||
throw new Error("You need to define an entity"); | ||
} | ||
this.config = config; | ||
} | ||
|
||
getCardSize() { | ||
return 3; | ||
const cardSize = this.config.height ? Math.round(parseInt(this.config.height)/50) : 3; | ||
return cardSize; | ||
} | ||
|
||
static get styles() { | ||
return css` | ||
p{ | ||
position: relative; | ||
ha-card{ | ||
background: none; | ||
} | ||
.text{ | ||
overflow: hidden; | ||
display: flex; | ||
flex-flow: column wrap; | ||
padding-left: 6%; | ||
height: 100%; | ||
width: 94%; | ||
} | ||
span{ | ||
z-index: 1; | ||
pointer-events: none; | ||
text-transform: capitalize; | ||
margin: 0; | ||
} | ||
p.off, p.paused, p.unavailable { | ||
span.off, span.paused, span.unavailable { | ||
color: var(--color-off); | ||
} | ||
p.on, p.playing { | ||
span.on, span.playing { | ||
color: var(--color-on); | ||
} | ||
.icon{ | ||
margin: 10px; | ||
margin-right: 10px; | ||
--mdc-icon-size: 30px; | ||
top: -3px; | ||
position: relative; | ||
} | ||
.top { | ||
font-size: var(--paper-font-title_-_font-size); | ||
padding: 30px 0 0 20px; | ||
font-size: var(--font-size); | ||
min-height: 35px; | ||
max-height: 40px; | ||
min-width: 40%; | ||
flex-grow: 1; | ||
padding-top: calc(var(--card-height) / 5); | ||
padding-right: 5%; | ||
margin-bottom: -20px; | ||
} | ||
.middle { | ||
font-size: var(--paper-font-title_-_font-size); | ||
font-size: var(--font-size); | ||
font-weight: var(--paper-font-title_-_font-weight); | ||
padding: 20px 0 5px 35px; | ||
min-height: 30px; | ||
max-height: 35px; | ||
min-width: 50%; | ||
flex-grow: 1; | ||
padding-left: 1%; | ||
padding-top: calc(3px + calc(var(--card-height) / 5)); | ||
} | ||
.bottom { | ||
font-size: var(--paper-font-body1_-_font-size); | ||
font-weight: var(--paper-font-body1_-_font-weight); | ||
padding: 5px 0 0 35px; | ||
flex-grow: 10; | ||
max-height: 40px; | ||
padding-left: 1%; | ||
padding-right: 10%; | ||
white-space: nowrap; | ||
} | ||
.button { | ||
height: var(--card-height); | ||
width: 100%; | ||
position: relative; | ||
background: var(--dimmer-background); | ||
background-size: cover; | ||
border-radius: var(--ha-card-border-radius); | ||
touch-action: pan-y; | ||
touch-action: var(--touch); | ||
overflow: hidden; | ||
} | ||
.button input[type="range"] { | ||
border-radius: var(--ha-card-border-radius); | ||
width: 100%; | ||
margin: 0; | ||
transition: box-shadow 0.2s ease-in-out; | ||
overflow: hidden; | ||
height: 100%; | ||
-webkit-appearance: none; | ||
background: none; | ||
position: absolute; | ||
width: var(--range-width); | ||
height: var(--range-height); | ||
top: 0; | ||
right: 0; | ||
right: var(--right); | ||
-webkit-transform:rotate(var(--rotation)); | ||
-moz-transform:rotate(var(--rotation)); | ||
-o-transform:rotate(var(--rotation)); | ||
-ms-transform:rotate(var(--rotation)); | ||
transform:rotate(var(--rotation)); | ||
transform-origin: top right; | ||
} | ||
.button input[type="range"]::-webkit-slider-runnable-track { | ||
|
@@ -345,11 +417,19 @@ class DimmerButton extends LitElement { | |
.button input[type="range"]:hover { | ||
cursor: pointer; | ||
} | ||
.active{ | ||
-webkit-transform: scaleX(0.97) scaleY(0.95); | ||
-ms-transform: scaleX(0.97) scaleY(0.95); | ||
transform: scaleX(0.97) scaleY(0.95); | ||
transition:all 0.3s ease-in; | ||
} | ||
span.effect { | ||
position: absolute; | ||
opacity: 0; | ||
animation: ripple 200ms ease-in-out; | ||
background-color: rgba(255, 255, 255, 0.9); | ||
animation: ripple 200ms ease-in; | ||
background-color: rgba(255, 255, 255, 0.7); | ||
height: 100%; | ||
width: 100%; | ||
left: 0; | ||
|