/* * 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`

${this.config.title}

${this._stateCount(openBaseline)} ${countText}

${showButton ? html`` : html``}
${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`

${ent.name || stateObj.attributes.friendly_name}

${stateObj.attributes.supported_features > 6 ? html`

${this._coverPosition(stateObj.state, stateObj.attributes.current_position, stateObj.entity_id)}

this._sliderChange(e.target.value, stateObj.entity_id)}} @change=${e => this._setPosition(stateObj.entity_id, e.target.value, ent.script)}>
` : html`

${stateObj.state}

this._switch(stateObj)}>
`}
this._switch(stateObj)} />
`: html``; })}
`; } 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);