├── .github └── workflows │ └── validation.yml ├── README.md ├── hacs.json └── touchpad-card.js /.github/workflows/validation.yml: -------------------------------------------------------------------------------- 1 | name: Validate plugin 2 | 3 | on: 4 | push: 5 | pull_request: 6 | schedule: 7 | - cron: '0 0 * * *' 8 | 9 | jobs: 10 | validate: 11 | runs-on: "ubuntu-latest" 12 | steps: 13 | - uses: "actions/checkout@v2" 14 | - name: HACS Action 15 | uses: "hacs/action@main" 16 | with: 17 | category: "plugin" 18 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [!["Buy Me A Coffee"](https://www.buymeacoffee.com/assets/img/custom_images/orange_img.png)](https://www.buymeacoffee.com/hackerino) [![hacs_badge](https://img.shields.io/badge/HACS-Default-41BDF5.svg?style=for-the-badge)](https://github.com/hacs/integration) 2 | 3 | # Touchpad Card for Homeassistant for samsung tv 4 | With this card you can control your samsung smart tv like with your normal physical remote. 5 | 6 | ## Take a look: 7 | ![remoteBell-modified](https://user-images.githubusercontent.com/64681499/209690449-d67ce6a3-02cb-477c-9b16-cc3071fe847b.png) 8 | 9 | 10 | # Features: 11 | - Functional touchpad; 12 | - Customizable doubleclick and hold actions(using ui-editor); 13 | - Haptic feedback; 14 | - Anything can be changed using the ui-editor; 15 | - Every icon can be changed trought the ui-editor; 16 | - Holding on volume or channel buttons will trigger the action repeatedly until stop holding; 17 | - Double click or hold action can be a script or a automation and can be selected using the editor; 18 | - The card will change the top icon to the current source image if available; 19 | - Double click on top part of the card to open more info dialog about the media player entity; 20 | # New features: 21 | - One card, multiple remotes; 22 | - Add/Remove/Move entities to the card; 23 | - Swipe on top part of card(name area) to switch to next entity; 24 | - The card can now work with the default homeassistant integration, thanks to the remote entity created by homeassistant; 25 | - Now you can change click action,and because of that you can now integrate tv's that dont work with service calls like ```remote.send_command || media_player.send_key```, by creating scripts and setting them as click action in the card editor; 26 | ## note 27 | 1) Create a separate view for the card, and set it to panel mode(1 card) **(VERY RECOMENDED)** 28 | 2) No need to write any yaml,all settings are available in the ui-editor(:sparkles:) 29 | 30 | ## Editor sample view: 31 | 32 | 33 | -------------------------------------------------------------------------------- /hacs.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "TV touchpad remote card", 3 | "content_in_root": false, 4 | "render_readme": true, 5 | "filename": "touchpad-card.js", 6 | "content_in_root": true 7 | } 8 | -------------------------------------------------------------------------------- /touchpad-card.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license 3 | * Copyright 2019 Google LLC 4 | * SPDX-License-Identifier: BSD-3-Clause 5 | */ 6 | const t$1=window,e$2=t$1.ShadowRoot&&(void 0===t$1.ShadyCSS||t$1.ShadyCSS.nativeShadow)&&"adoptedStyleSheets"in Document.prototype&&"replace"in CSSStyleSheet.prototype,s$3=Symbol(),n$3=new WeakMap;let o$3 = class o{constructor(t,e,n){if(this._$cssResult$=!0,n!==s$3)throw Error("CSSResult is not constructable. Use `unsafeCSS` or `css` instead.");this.cssText=t,this.t=e;}get styleSheet(){let t=this.o;const s=this.t;if(e$2&&void 0===t){const e=void 0!==s&&1===s.length;e&&(t=n$3.get(s)),void 0===t&&((this.o=t=new CSSStyleSheet).replaceSync(this.cssText),e&&n$3.set(s,t));}return t}toString(){return this.cssText}};const r$2=t=>new o$3("string"==typeof t?t:t+"",void 0,s$3),i$1=(t,...e)=>{const n=1===t.length?t[0]:e.reduce(((e,s,n)=>e+(t=>{if(!0===t._$cssResult$)return t.cssText;if("number"==typeof t)return t;throw Error("Value passed to 'css' function must be a 'css' function result: "+t+". Use 'unsafeCSS' to pass non-literal values, but take care to ensure page security.")})(s)+t[n+1]),t[0]);return new o$3(n,t,s$3)},S$1=(s,n)=>{e$2?s.adoptedStyleSheets=n.map((t=>t instanceof CSSStyleSheet?t:t.styleSheet)):n.forEach((e=>{const n=document.createElement("style"),o=t$1.litNonce;void 0!==o&&n.setAttribute("nonce",o),n.textContent=e.cssText,s.appendChild(n);}));},c$1=e$2?t=>t:t=>t instanceof CSSStyleSheet?(t=>{let e="";for(const s of t.cssRules)e+=s.cssText;return r$2(e)})(t):t; 7 | 8 | /** 9 | * @license 10 | * Copyright 2017 Google LLC 11 | * SPDX-License-Identifier: BSD-3-Clause 12 | */var s$2;const e$1=window,r$1=e$1.trustedTypes,h$1=r$1?r$1.emptyScript:"",o$2=e$1.reactiveElementPolyfillSupport,n$2={toAttribute(t,i){switch(i){case Boolean:t=t?h$1:null;break;case Object:case Array:t=null==t?t:JSON.stringify(t);}return t},fromAttribute(t,i){let s=t;switch(i){case Boolean:s=null!==t;break;case Number:s=null===t?null:Number(t);break;case Object:case Array:try{s=JSON.parse(t);}catch(t){s=null;}}return s}},a$1=(t,i)=>i!==t&&(i==i||t==t),l$2={attribute:!0,type:String,converter:n$2,reflect:!1,hasChanged:a$1};let d$1 = class d extends HTMLElement{constructor(){super(),this._$Ei=new Map,this.isUpdatePending=!1,this.hasUpdated=!1,this._$El=null,this.u();}static addInitializer(t){var i;this.finalize(),(null!==(i=this.h)&&void 0!==i?i:this.h=[]).push(t);}static get observedAttributes(){this.finalize();const t=[];return this.elementProperties.forEach(((i,s)=>{const e=this._$Ep(s,i);void 0!==e&&(this._$Ev.set(e,s),t.push(e));})),t}static createProperty(t,i=l$2){if(i.state&&(i.attribute=!1),this.finalize(),this.elementProperties.set(t,i),!i.noAccessor&&!this.prototype.hasOwnProperty(t)){const s="symbol"==typeof t?Symbol():"__"+t,e=this.getPropertyDescriptor(t,s,i);void 0!==e&&Object.defineProperty(this.prototype,t,e);}}static getPropertyDescriptor(t,i,s){return {get(){return this[i]},set(e){const r=this[t];this[i]=e,this.requestUpdate(t,r,s);},configurable:!0,enumerable:!0}}static getPropertyactions(t){return this.elementProperties.get(t)||l$2}static finalize(){if(this.hasOwnProperty("finalized"))return !1;this.finalized=!0;const t=Object.getPrototypeOf(this);if(t.finalize(),void 0!==t.h&&(this.h=[...t.h]),this.elementProperties=new Map(t.elementProperties),this._$Ev=new Map,this.hasOwnProperty("properties")){const t=this.properties,i=[...Object.getOwnPropertyNames(t),...Object.getOwnPropertySymbols(t)];for(const s of i)this.createProperty(s,t[s]);}return this.elementStyles=this.finalizeStyles(this.styles),!0}static finalizeStyles(i){const s=[];if(Array.isArray(i)){const e=new Set(i.flat(1/0).reverse());for(const i of e)s.unshift(c$1(i));}else void 0!==i&&s.push(c$1(i));return s}static _$Ep(t,i){const s=i.attribute;return !1===s?void 0:"string"==typeof s?s:"string"==typeof t?t.toLowerCase():void 0}u(){var t;this._$E_=new Promise((t=>this.enableUpdating=t)),this._$AL=new Map,this._$Eg(),this.requestUpdate(),null===(t=this.constructor.h)||void 0===t||t.forEach((t=>t(this)));}addController(t){var i,s;(null!==(i=this._$ES)&&void 0!==i?i:this._$ES=[]).push(t),void 0!==this.renderRoot&&this.isConnected&&(null===(s=t.hostConnected)||void 0===s||s.call(t));}removeController(t){var i;null===(i=this._$ES)||void 0===i||i.splice(this._$ES.indexOf(t)>>>0,1);}_$Eg(){this.constructor.elementProperties.forEach(((t,i)=>{this.hasOwnProperty(i)&&(this._$Ei.set(i,this[i]),delete this[i]);}));}createRenderRoot(){var t;const s=null!==(t=this.shadowRoot)&&void 0!==t?t:this.attachShadow(this.constructor.shadowRootactions);return S$1(s,this.constructor.elementStyles),s}connectedCallback(){var t;void 0===this.renderRoot&&(this.renderRoot=this.createRenderRoot()),this.enableUpdating(!0),null===(t=this._$ES)||void 0===t||t.forEach((t=>{var i;return null===(i=t.hostConnected)||void 0===i?void 0:i.call(t)}));}enableUpdating(t){}disconnectedCallback(){var t;null===(t=this._$ES)||void 0===t||t.forEach((t=>{var i;return null===(i=t.hostDisconnected)||void 0===i?void 0:i.call(t)}));}attributeChangedCallback(t,i,s){this._$AK(t,s);}_$EO(t,i,s=l$2){var e;const r=this.constructor._$Ep(t,s);if(void 0!==r&&!0===s.reflect){const h=(void 0!==(null===(e=s.converter)||void 0===e?void 0:e.toAttribute)?s.converter:n$2).toAttribute(i,s.type);this._$El=t,null==h?this.removeAttribute(r):this.setAttribute(r,h),this._$El=null;}}_$AK(t,i){var s;const e=this.constructor,r=e._$Ev.get(t);if(void 0!==r&&this._$El!==r){const t=e.getPropertyactions(r),h="function"==typeof t.converter?{fromAttribute:t.converter}:void 0!==(null===(s=t.converter)||void 0===s?void 0:s.fromAttribute)?t.converter:n$2;this._$El=r,this[r]=h.fromAttribute(i,t.type),this._$El=null;}}requestUpdate(t,i,s){let e=!0;void 0!==t&&(((s=s||this.constructor.getPropertyactions(t)).hasChanged||a$1)(this[t],i)?(this._$AL.has(t)||this._$AL.set(t,i),!0===s.reflect&&this._$El!==t&&(void 0===this._$EC&&(this._$EC=new Map),this._$EC.set(t,s))):e=!1),!this.isUpdatePending&&e&&(this._$E_=this._$Ej());}async _$Ej(){this.isUpdatePending=!0;try{await this._$E_;}catch(t){Promise.reject(t);}const t=this.scheduleUpdate();return null!=t&&await t,!this.isUpdatePending}scheduleUpdate(){return this.performUpdate()}performUpdate(){var t;if(!this.isUpdatePending)return;this.hasUpdated,this._$Ei&&(this._$Ei.forEach(((t,i)=>this[i]=t)),this._$Ei=void 0);let i=!1;const s=this._$AL;try{i=this.shouldUpdate(s),i?(this.willUpdate(s),null===(t=this._$ES)||void 0===t||t.forEach((t=>{var i;return null===(i=t.hostUpdate)||void 0===i?void 0:i.call(t)})),this.update(s)):this._$Ek();}catch(t){throw i=!1,this._$Ek(),t}i&&this._$AE(s);}willUpdate(t){}_$AE(t){var i;null===(i=this._$ES)||void 0===i||i.forEach((t=>{var i;return null===(i=t.hostUpdated)||void 0===i?void 0:i.call(t)})),this.hasUpdated||(this.hasUpdated=!0,this.firstUpdated(t)),this.updated(t);}_$Ek(){this._$AL=new Map,this.isUpdatePending=!1;}get updateComplete(){return this.getUpdateComplete()}getUpdateComplete(){return this._$E_}shouldUpdate(t){return !0}update(t){void 0!==this._$EC&&(this._$EC.forEach(((t,i)=>this._$EO(i,this[i],t))),this._$EC=void 0),this._$Ek();}updated(t){}firstUpdated(t){}};d$1.finalized=!0,d$1.elementProperties=new Map,d$1.elementStyles=[],d$1.shadowRootactions={mode:"open"},null==o$2||o$2({ReactiveElement:d$1}),(null!==(s$2=e$1.reactiveElementVersions)&&void 0!==s$2?s$2:e$1.reactiveElementVersions=[]).push("1.5.0"); 13 | 14 | /** 15 | * @license 16 | * Copyright 2017 Google LLC 17 | * SPDX-License-Identifier: BSD-3-Clause 18 | */ 19 | var t;const i=window,s$1=i.trustedTypes,e=s$1?s$1.createPolicy("lit-html",{createHTML:t=>t}):void 0,o$1=`lit$${(Math.random()+"").slice(9)}$`,n$1="?"+o$1,l$1=`<${n$1}>`,h=document,r=(t="")=>h.createComment(t),d=t=>null===t||"object"!=typeof t&&"function"!=typeof t,u=Array.isArray,c=t=>u(t)||"function"==typeof(null==t?void 0:t[Symbol.iterator]),v=/<(?:(!--|\/[^a-zA-Z])|(\/?[a-zA-Z][^>\s]*)|(\/?$))/g,a=/-->/g,f=/>/g,_=RegExp(">|[ \t\n\f\r](?:([^\\s\"'>=/]+)([ \t\n\f\r]*=[ \t\n\f\r]*(?:[^ \t\n\f\r\"'`<>=]|(\"|')|))|$)","g"),m=/'/g,p=/"/g,$=/^(?:script|style|textarea|title)$/i,g=t=>(i,...s)=>({_$litType$:t,strings:i,values:s}),y=g(1),x=Symbol.for("lit-noChange"),b=Symbol.for("lit-nothing"),T=new WeakMap,A=h.createTreeWalker(h,129,null,!1),E=(t,i)=>{const s=t.length-1,n=[];let h,r=2===i?"":"",d=v;for(let i=0;i"===u[0]?(d=null!=h?h:v,c=-1):void 0===u[1]?c=-2:(c=d.lastIndex-u[2].length,e=u[1],d=void 0===u[3]?_:'"'===u[3]?p:m):d===p||d===m?d=_:d===a||d===f?d=v:(d=_,h=void 0);const y=d===_&&t[i+1].startsWith("/>")?" ":"";r+=d===v?s+l$1:c>=0?(n.push(e),s.slice(0,c)+"$lit$"+s.slice(c)+o$1+y):s+o$1+(-2===c?(n.push(void 0),i):y);}const u=r+(t[s]||"")+(2===i?"":"");if(!Array.isArray(t)||!t.hasOwnProperty("raw"))throw Error("invalid template strings array");return [void 0!==e?e.createHTML(u):u,n]};class C{constructor({strings:t,_$litType$:i},e){let l;this.parts=[];let h=0,d=0;const u=t.length-1,c=this.parts,[v,a]=E(t,i);if(this.el=C.createElement(v,e),A.currentNode=this.el.content,2===i){const t=this.el.content,i=t.firstChild;i.remove(),t.append(...i.childNodes);}for(;null!==(l=A.nextNode())&&c.length0){l.textContent=s$1?s$1.emptyScript:"";for(let s=0;s2||""!==s[0]||""!==s[1]?(this._$AH=Array(s.length-1).fill(new String),this.strings=s):this._$AH=b;}get tagName(){return this.element.tagName}get _$AU(){return this._$AM._$AU}_$AI(t,i=this,s,e){const o=this.strings;let n=!1;if(void 0===o)t=P(this,t,i,0),n=!d(t)||t!==this._$AH&&t!==x,n&&(this._$AH=t);else {const e=t;let l,h;for(t=o[0],l=0;l{var e,o;const n=null!==(e=null==s?void 0:s.renderBefore)&&void 0!==e?e:i;let l=n._$litPart$;if(void 0===l){const t=null!==(o=null==s?void 0:s.renderBefore)&&void 0!==o?o:null;n._$litPart$=l=new N(i.insertBefore(r(),t),t,void 0,null!=s?s:{});}return l._$AI(t),l}; 20 | 21 | /** 22 | * @license 23 | * Copyright 2017 Google LLC 24 | * SPDX-License-Identifier: BSD-3-Clause 25 | */var l,o;class s extends d$1{constructor(){super(...arguments),this.renderactions={host:this},this._$Do=void 0;}createRenderRoot(){var t,e;const i=super.createRenderRoot();return null!==(t=(e=this.renderactions).renderBefore)&&void 0!==t||(e.renderBefore=i.firstChild),i}update(t){const i=this.render();this.hasUpdated||(this.renderactions.isConnected=this.isConnected),super.update(t),this._$Do=Z(i,this.renderRoot,this.renderactions);}connectedCallback(){var t;super.connectedCallback(),null===(t=this._$Do)||void 0===t||t.setConnected(!0);}disconnectedCallback(){var t;super.disconnectedCallback(),null===(t=this._$Do)||void 0===t||t.setConnected(!1);}render(){return x}}s.finalized=!0,s._$litElement$=!0,null===(l=globalThis.litElementHydrateSupport)||void 0===l||l.call(globalThis,{LitElement:s});const n=globalThis.litElementPolyfillSupport;null==n||n({LitElement:s});(null!==(o=globalThis.litElementVersions)&&void 0!==o?o:globalThis.litElementVersions=[]).push("3.2.2"); 26 | 27 | const chgConfig = new Event("config-changed", { bubbles: true, composed: true}); 28 | const vibrate = new Event('haptic', {bubbles: false}); 29 | const moreInfo = new Event('hass-more-info', { composed: true }); 30 | var clickTimer,holdTimer,holdInterval,holdRemove,msg,falseCheck = true; 31 | 32 | class MyElement extends s { 33 | static get properties() { 34 | return { 35 | hass: {}, 36 | config: {} 37 | }; 38 | } 39 | 40 | static get styles() { 41 | return i$1` 42 | img{ 43 | background-size: contain; 44 | border-radius: 15px; 45 | height: 40px; 46 | width: 40px; 47 | touch-action: none; 48 | margin: 20px 0px 20px 20px; 49 | float: left; 50 | } 51 | ha-icon{ 52 | pointer-events: none; 53 | touch-action: none; 54 | } 55 | #entity-area > ha-icon { 56 | color: white; 57 | float:left; 58 | margin:17px 0px 0px 20px; 59 | --mdc-icon-size: 45px ; 60 | } 61 | .touch-area{ 62 | display: block; 63 | width: 93%; 64 | margin: 9vh auto 0px; 65 | border-radius: 30px; 66 | margin-bottom: 10px; 67 | height: 55%; 68 | background: #6d767e; 69 | border: 1px solid black; 70 | touch-action:none; 71 | } 72 | #entity-area{ 73 | height : 10vh; 74 | width: 100%; 75 | margin : 0; 76 | } 77 | 78 | the-tv { 79 | position: relative; 80 | background-color: #343a40; 81 | overflow: hidden; 82 | display: flex; 83 | flex-flow: column; 84 | margin:0 auto 0; 85 | max-height: 750px ; 86 | max-width: 458px; 87 | border: double 4px transparent; 88 | border-radius: 30px; 89 | } 90 | .fancy-borders{ 91 | background-image: linear-gradient(#343a40, #343a40), radial-gradient(circle at top left, #f00,#3020ff); 92 | background-origin: border-box; 93 | background-clip: padding-box, border-box; 94 | } 95 | 96 | #buttons{ 97 | color: white; 98 | display: grid; 99 | grid-template-columns: auto auto auto; 100 | grid-template-rows: auto auto; 101 | gap: 0px auto; 102 | width: 96%; 103 | margin: 8vh 0 2vh ; 104 | height: 58%; 105 | --mdc-icon-size: 30px; 106 | padding-left: 13px; 107 | padding-right: 13px; 108 | grid-template-areas: 109 | "a b c" 110 | "d e f"; 111 | } 112 | #buttons button { 113 | color: white; 114 | border: 0.5px; 115 | border-top: solid grey ; 116 | border-left: solid grey; 117 | border-bottom: solid black; 118 | border-right: solid black; 119 | } 120 | b { 121 | margin: 30px auto auto 20px; 122 | color: white; 123 | display: inline-block; 124 | font-size: 23px; 125 | text-shadow: rgba(0, 0, 0, 0.2) 3px 3px 0px; 126 | } 127 | .power{ 128 | border: 2px; 129 | border-top: solid grey ; 130 | border-left: solid grey; 131 | border-bottom: solid black; 132 | border-right: solid black; 133 | background-color: transparent; 134 | box-shadow: none; 135 | border-radius: 3rem; 136 | float: right; 137 | margin: 20px; 138 | --mdc-icon-size: 35px; 139 | } 140 | .channel_up{ 141 | grid-area: a; 142 | border-radius: 3rem 3rem 0rem 0rem; 143 | height: 100%; 144 | width: 24vw; 145 | max-width: 118px; 146 | margin-left: auto; 147 | margin-right: auto; 148 | box-shadow: none; 149 | background-color: #6d767e; 150 | } 151 | .channel_down{ 152 | grid-area: d; 153 | border-radius: 0rem 0rem 3rem 3rem; 154 | height: 100%; 155 | width: 24vw; 156 | max-width: 118px; 157 | box-shadow: none; 158 | margin-left: auto; 159 | margin-right: auto; 160 | background-color: #6d767e; 161 | } 162 | .source{ 163 | grid-area: b; 164 | border-radius: 1.5rem; 165 | box-shadow: none; 166 | background-color: #6d767e; 167 | margin: auto; 168 | padding: 10px; 169 | height: fit-content; 170 | } 171 | .mute{ 172 | grid-area: e; 173 | border-radius: 1.5rem; 174 | box-shadow: none; 175 | background-color: #6d767e; 176 | margin: auto; 177 | padding: 10px; 178 | height: fit-content; 179 | } 180 | .volume_up{ 181 | grid-area: c; 182 | border-radius: 3rem 3rem 0rem 0rem; 183 | height: 100%; 184 | width: 24vw; 185 | max-width: 118px; 186 | box-shadow: none; 187 | margin-left: auto; 188 | margin-right: auto; 189 | background-color: #6d767e; 190 | } 191 | .volume_down{ 192 | grid-area: f; 193 | border-radius: 0rem 0rem 3rem 3rem; 194 | height: 100%; 195 | width: 24vw; 196 | max-width: 118px; 197 | margin-left: auto; 198 | margin-right: auto; 199 | box-shadow: none; 200 | background-color: #6d767e; 201 | } 202 | hr{ 203 | height:2px; 204 | width: 97%; 205 | border-width:0; 206 | color:#6d767e; 207 | background-color:#6d767e; 208 | } 209 | `; 210 | } 211 | 212 | 213 | static getConfigElement() { 214 | return document.createElement("contento-card-editor"); 215 | } 216 | 217 | static properties = { 218 | _current_entity: { state: true }, 219 | }; 220 | 221 | constructor(){ 222 | super(); 223 | this.t = []; 224 | } 225 | set hass(hass) { 226 | this._hass = hass; 227 | this.requestUpdate(); 228 | } 229 | 230 | 231 | render() { 232 | this.t = Object.keys(this.config[this._current_entity].icons).slice(2); 233 | return y` 234 | 236 |
237 | ${this.tvIconOrSource()} 238 | ${ this.config[this._current_entity].display_name } 239 | ${this.baseButton('power')} 240 |
241 |
242 |
243 | ${this.t.map(elm => this.baseButton(elm))} 244 |
245 | ${this.touchpad()} 246 |
247 | `; 248 | } 249 | 250 | tvIconOrSource() { 251 | if(this._hass.states[this.config[this._current_entity].entity]?.attributes.entity_picture != undefined) 252 | return y` 253 | `; 254 | else 255 | return y` 256 | `; 257 | } 258 | 259 | baseButton(cssName){ 260 | let button = document.createElement('button'); 261 | button.classList.add(cssName); 262 | if(cssName === 'power') 263 | button.style.color = this._hass.states[this.config[this._current_entity].entity].state === 'on' ? 'green' : 'red'; 264 | let icon = document.createElement('ha-icon'); 265 | icon.setAttribute('icon',this.config[this._current_entity].icons[cssName]); 266 | button.appendChild(icon); 267 | button.addEventListener('click', e => { 268 | e.stopImmediatePropagation(); 269 | clickTimer = setTimeout(() => { 270 | if(falseCheck){ 271 | this.feedback('light'); 272 | this.execute({type: 'click',src: cssName}); 273 | } 274 | falseCheck=true; 275 | }, 210); 276 | }); 277 | 278 | button.addEventListener('touchstart',e => { 279 | e.stopImmediatePropagation(); 280 | holdTimer = setTimeout(() => { 281 | this.feedback('light'); 282 | if(cssName.match(/^(channel_up|channel_down|volume_up|volume_down)$/)) 283 | holdInterval = setInterval(() => { 284 | this.execute({type: 'hold',src: cssName}); 285 | this.feedback('selection'); 286 | }, 450); 287 | else 288 | this.execute({type: 'hold',src: cssName}); 289 | }, 600); 290 | }); 291 | 292 | button.addEventListener('dblclick',e => { 293 | e.stopImmediatePropagation(); 294 | falseCheck = false; 295 | clearTimeout(clickTimer); 296 | clickTimer = null; 297 | this.execute({type: 'dblclick',src: cssName}); 298 | this.feedback('success'); 299 | }); 300 | 301 | button.addEventListener('touchend',e=>{ 302 | e.stopImmediatePropagation(); 303 | clearInterval(holdInterval); 304 | clearTimeout(clickTimer); 305 | clearTimeout(holdTimer); 306 | }); 307 | return button; 308 | } 309 | 310 | touchpad (){ 311 | let touchpad = document.createElement('button'); 312 | touchpad.setAttribute('id','touchpad'); 313 | touchpad.classList.add('touch-area'); 314 | touchpad.addEventListener('click', e => { 315 | e.stopImmediatePropagation(); 316 | clickTimer = setTimeout(() => { 317 | if(falseCheck){ 318 | this.execute({type: 'click',src: 'touchpad'}); 319 | this.feedback('light'); 320 | } 321 | falseCheck=true; 322 | }, 210); 323 | }); 324 | touchpad.addEventListener('dblclick',e => { 325 | e.stopImmediatePropagation(); 326 | this.feedback('success'); 327 | falseCheck = false; 328 | clearTimeout(clickTimer); 329 | clickTimer = null; 330 | this.execute({type: 'dblclick',src: 'touchpad'}); 331 | }); 332 | touchpad.addEventListener('touchstart' ,e => { 333 | e.stopImmediatePropagation(); 334 | this.touchStart(e); 335 | holdTimer = setTimeout(() => { 336 | this.feedback('medium'); 337 | this.execute({type: 'hold',src: 'touchpad'}); 338 | }, 700); 339 | }); 340 | touchpad.addEventListener('touchmove',e => { 341 | e.stopImmediatePropagation(); 342 | this.touchMove(e); 343 | }); 344 | touchpad.addEventListener('touchend', e => { 345 | e.stopImmediatePropagation(); 346 | clearTimeout(clickTimer); 347 | clearTimeout(holdTimer); 348 | }); 349 | return touchpad; 350 | } 351 | 352 | execute(act) { 353 | if (this.config[this._current_entity].actions[act.src][act.type].type === "default") { 354 | switch (act.type) { 355 | case 'click': 356 | switch (act.src) { 357 | case 'power': 358 | this._hass.callService('homeassistant','toggle',{entity_id: this.config[this._current_entity].entity}); 359 | break; 360 | case 'channel_up': 361 | if(this._hass.entities[this.config[this._current_entity].entity].platform.includes('samsungtv_smart')) 362 | this._hass.callService('media_player','play_media',{media_content_id: 'KEY_CHUP',media_content_type: 'send_key'},{entity_id: this.config[this._current_entity].entity}); 363 | else 364 | this._hass.callService('remote', 'send_command', { command: 'KEY_CHUP' }, { entity_id: this.config[this._current_entity].entity.replace("media_player", "remote") }); 365 | break; 366 | case 'channel_down': 367 | if(this._hass.entities[this.config[this._current_entity].entity].platform.includes('samsungtv_smart')) 368 | this._hass.callService('media_player','play_media',{media_content_id: 'KEY_CHDOWN',media_content_type: 'send_key'},{entity_id: this.config[this._current_entity].entity}); 369 | else 370 | this._hass.callService('remote', 'send_command', { command: 'KEY_CHDOWN' }, { entity_id: this.config[this._current_entity].entity.replace("media_player", "remote") }); 371 | break; 372 | case 'volume_up': 373 | this._hass.callService('media_player','volume_up',{entity_id: this.config[this._current_entity].entity}); 374 | break; 375 | case 'volume_down': 376 | this._hass.callService('media_player','volume_down',{entity_id: this.config[this._current_entity].entity}); 377 | break; 378 | case 'source': 379 | if (this._hass.entities[this.config[this._current_entity].entity].platform.includes('samsungtv_smart')) 380 | this._hass.callService('media_player','play_media',{media_content_id: 'KEY_SOURCE',media_content_type: 'send_key'},{entity_id: this.config[this._current_entity].entity}); 381 | else 382 | this._hass.callService('remote', 'send_command', { command: 'KEY_SOURCE' }, { entity_id: this.config[this._current_entity].entity.replace("media_player", "remote") }); 383 | break; 384 | case 'mute': 385 | this._hass.callService('media_player','volume_mute',{is_volume_muted: !this._hass.states[this.config[this._current_entity].entity].attributes.is_volume_muted},{entity_id: this.config[this._current_entity].entity}); 386 | break; 387 | case 'touchpad': 388 | if (this._hass.entities[this.config[this._current_entity].entity].platform.includes('samsungtv_smart')) 389 | this._hass.callService('media_player','play_media',{media_content_id: 'KEY_ENTER',media_content_type: 'send_key'},{entity_id: this.config[this._current_entity].entity}); 390 | else 391 | this._hass.callService('remote', 'send_command', { command: 'KEY_ENTER' }, { entity_id: this.config[this._current_entity].entity.replace("media_player", "remote") }); 392 | break; 393 | } 394 | break; 395 | case 'dblclick': 396 | if (act.src === 'touchpad') 397 | if(this._hass.entities[this.config[this._current_entity].entity].platform.includes('samsungtv_smart')) 398 | this._hass.callService('media_player','play_media',{media_content_id: 'KEY_RETURN',media_content_type: 'send_key'},{entity_id: this.config[this._current_entity].entity}); 399 | else 400 | this._hass.callService('remote','send_command', { command: 'KEY_RETURN' }, { entity_id: this.config[this._current_entity].entity.replace("media_player", "remote") }); 401 | break; 402 | case 'hold': 403 | switch (act.src) { 404 | case 'touchpad': 405 | if(this._hass.entities[this.config[this._current_entity].entity].platform.includes('samsungtv_smart')) 406 | this._hass.callService('media_player', 'play_media', { media_content_id: 'KEY_HOME', media_content_type: 'send_key' }, { entity_id: this.config[this._current_entity].entity }); 407 | else 408 | this._hass.callService('remote','send_command',{command: 'KEY_HOME'},{entity_id: this.config[this._current_entity].entity.replace("media_player", "remote")}); 409 | break; 410 | case 'channel_up': 411 | if (this._hass.entities[this.config[this._current_entity].entity].platform.includes('samsungtv_smart')) 412 | this._hass.callService('media_player', 'play_media', { media_content_id: 'KEY_CHUP', media_content_type: 'send_key' }, { entity_id: this.config[this._current_entity].entity }); 413 | else 414 | this._hass.callService('remote', 'send_command', { command: 'KEY_CHUP' }, { entity_id: this.config[this._current_entity].entity.replace("media_player", "remote") }); 415 | break; 416 | case 'channel_down': 417 | if(this._hass.entities[this.config[this._current_entity].entity].platform.includes('samsungtv_smart')) 418 | this._hass.callService('media_player', 'play_media', { media_content_id: 'KEY_CHDOWN', media_content_type: 'send_key' }, { entity_id: this.config[this._current_entity].entity }); 419 | else 420 | this._hass.callService('remote', 'send_command', { command: 'KEY_CHDOWN' }, { entity_id: this.config[this._current_entity].entity.replace("media_player", "remote") }); 421 | break; 422 | case 'volume_up': 423 | this._hass.callService('media_player', 'volume_up', { entity_id: this.config[this._current_entity].entity }); 424 | break; 425 | case 'volume_down': 426 | this._hass.callService('media_player', 'volume_down', { entity_id: this.config[this._current_entity].entity }); 427 | break; 428 | } 429 | break; 430 | } 431 | } else { 432 | switch (this.config[this._current_entity].actions[act.src][act.type].type) { 433 | case 'script': 434 | this._hass.callService('script', this.config[this._current_entity].actions[act.src][act.type].entity.substring(this.config[this._current_entity].actions[act.src][act.type].entity.indexOf('.') + 1)); 435 | break; 436 | case 'automation': 437 | this._hass.callService('automation', 'trigger', { entity_id: this.config[this._current_entity].actions[act.src][act.type].entity }); 438 | break; 439 | case 'toggle': 440 | this._hass.callService('homeassistant', 'toggle', { entity_id: this.config[this._current_entity].actions[act.src][act.type].entity }); 441 | break; 442 | case 'turn-on': 443 | this._hass.callService('homeassistant', 'turn_on', { entity_id: this.config[this._current_entity].actions[act.src][act.type].entity }); 444 | break; 445 | case 'turn-off': 446 | this._hass.callService('homeassistant', 'turn_off', { entity_id: this.config[this._current_entity].actions[act.src][act.type].entity }); 447 | break; 448 | } 449 | } 450 | } 451 | 452 | moreInfoAction(node){ 453 | moreInfo.detail = {entityId: this.config[this._current_entity].entity}; 454 | node.dispatchEvent(moreInfo); 455 | } 456 | feedback(type){ 457 | vibrate.detail = type; 458 | window.dispatchEvent(vibrate); 459 | } 460 | touchStart(e) { 461 | window.initialX = e.touches[0].clientX; 462 | window.initialY = e.touches[0].clientY; 463 | } 464 | 465 | changeCurrentEntity(e) { 466 | 467 | 468 | if( ! initialX || ! initialY){ 469 | return; 470 | } 471 | 472 | var currentX = e.touches[0].clientX; 473 | var currentY = e.touches[0].clientY; 474 | 475 | var diffX = initialX - currentX; 476 | var diffY = initialY - currentY; 477 | 478 | if (Math.abs(diffX) > Math.abs(diffY)) { 479 | if (diffX > 0) { 480 | if (!(Object.keys(this.config).indexOf(this._current_entity) === Object.keys(this.config).length - 1)) 481 | this._current_entity = Object.keys(this.config)[Object.keys(this.config).indexOf(this._current_entity) + 1]; 482 | } else { 483 | if (!(Object.keys(this.config).indexOf(this._current_entity) === 1)) 484 | this._current_entity = Object.keys(this.config)[Object.keys(this.config).indexOf(this._current_entity) - 1]; 485 | } 486 | } 487 | initialX = null; 488 | initialY = null; 489 | this.requestUpdate(); 490 | } 491 | 492 | touchMove(e, ha = this._hass) { 493 | if( ! initialX || ! initialY){ 494 | return; 495 | } 496 | 497 | var currentX = e.touches[0].clientX; 498 | var currentY = e.touches[0].clientY; 499 | 500 | var diffX = initialX - currentX; 501 | var diffY = initialY - currentY; 502 | 503 | if (Math.abs(diffX) > Math.abs(diffY)) { 504 | if (diffX > 0) { 505 | if(ha.entities[this.config[this._current_entity].entity].platform.includes('samsungtv_smart')) 506 | ha.callService("media_player", "play_media", { media_content_id: "KEY_LEFT", media_content_type: "send_key" }, { entity_id: this.config[this._current_entity].entity }); 507 | else 508 | ha.callService("remote", "send_command", { command: "KEY_LEFT" }, { entity_id: this.config[this._current_entity].entity.replace("media_player", "remote") }); 509 | this.feedback('selection'); 510 | } else { 511 | if(ha.entities[this.config[this._current_entity].entity].platform.includes('samsungtv_smart')) 512 | ha.callService("media_player", "play_media", { media_content_id: "KEY_RIGHT", media_content_type: "send_key" }, { entity_id: this.config[this._current_entity].entity }); 513 | else 514 | ha.callService("remote", "send_command", { command: "KEY_RIGHT" }, { entity_id: this.config[this._current_entity].entity.replace("media_player", "remote") }); 515 | this.feedback('selection'); 516 | } 517 | } else { 518 | if (diffY > 0) { 519 | if(ha.entities[this.config[this._current_entity].entity].platform.includes('samsungtv_smart')) 520 | ha.callService("media_player", "play_media", { media_content_id: "KEY_UP", media_content_type: "send_key" }, { entity_id: this.config[this._current_entity].entity }); 521 | else 522 | ha.callService("remote", "send_command", { command: "KEY_UP" }, { entity_id: this.config[this._current_entity].entity.replace("media_player", "remote") }); 523 | this.feedback('selection'); 524 | } else { 525 | if (ha.entities[this.config[this._current_entity].entity].platform.includes('samsungtv_smart')) 526 | ha.callService("media_player", "play_media", { media_content_id: "KEY_DOWN", media_content_type: "send_key" }, { entity_id: this.config[this._current_entity].entity }); 527 | else 528 | ha.callService("remote", "send_command", { command: "KEY_DOWN" }, { entity_id: this.config[this._current_entity].entity.replace("media_player", "remote") }); 529 | this.feedback('selection'); 530 | } 531 | } 532 | initialX = null; 533 | initialY = null; 534 | } 535 | setConfig(config) { 536 | if (Object.keys(config).length < 2) 537 | throw new Error('You need to define an entity'); 538 | this.config = config; 539 | if (this._current_entity === undefined || this.config[this._current_entity] === undefined) 540 | if( !Object.keys(this.config).slice(1).find(item => this.config[item].position === 1)) 541 | this._current_entity = Object.keys(this.config)[1]; 542 | else 543 | this._current_entity = Object.keys(this.config).slice(1).find(item => this.config[item].position === 1); 544 | this.requestUpdate(); 545 | } 546 | } 547 | class ContentCardEditor extends s { 548 | 549 | static get properties() { 550 | return { 551 | hass: {}, 552 | _config: {}, 553 | }; 554 | } 555 | static properties = { 556 | tabs: { state: true }, 557 | entityTabs: { state: true }, 558 | displayMessage: { state: true }, 559 | newEnt: { state: true }, 560 | message: { state: true }, 561 | }; 562 | 563 | constructor (){ 564 | super(); 565 | this.updateIt(); 566 | this.tabs = { power: false, source: false, mute: false, otherIcon: false, touchpad: false, settings: true, icon: false, click: false, dblclick: false, hold: false, volume: false, channel: false }; 567 | this.entityTabs = {}; 568 | this.newEnt = true; 569 | this.moveHint = true; 570 | this.displayMessage = false; 571 | this.setMessage('Thank you'); 572 | this.tmpName = ''; 573 | } 574 | 575 | render() { 576 | return y` 577 | ${this.makeMeEntityTabMenu()} 578 | `; 579 | } 580 | 581 | makeMeEntityTabMenu() { 582 | return y` 583 | ${this.alert()} 584 |
585 | ${Object.keys(this.entityTabs).map(e => { 586 | return y`
${e.slice(0,5)}
`; 587 | })} 588 | ${this.newEntityTab()} 589 |
590 | 591 | ${Object.keys(this.hass.states).filter(ent => ent.match('media_player[.]')).map((entity) => { 592 | if (this._config[entity.substring(13)] === undefined && !this.hass.entities[entity].platform.includes('cast')) 593 | return y` ${entity} `; 594 | else if (this._config[entity.substring(13)] !== undefined && entity.substring(13) === Object.keys(this.entityTabs).filter(e => this.entityTabs[e] === true)[0] && !this.hass.entities[entity].platform.includes('cast')) 595 | return y` ${entity} `; 596 | })} 597 | 598 | ${this._config?.[Object.keys(this.entityTabs).filter(e => this.entityTabs[e] === true)] != undefined ? this.makeMeTabMenu() : null} 599 | `; 600 | } 601 | 602 | 603 | makeMeTabMenu(){ 604 | return y` 605 |
606 |
607 |
608 |
609 |
610 |
611 |
612 |
613 |
614 | ${this.subTabs()} 615 |
616 | `; 617 | } 618 | 619 | subTabs(){ 620 | let normalCase = y` 621 |
622 |
Click
623 |
Double click
624 |
Hold
625 |
Icon
626 |
627 |
628 | ${this.putUpConfig()} 629 |
`; 630 | 631 | let otherIcon = y` 632 |
633 |
Volume
634 |
Channel
635 |
636 |
637 | ${this.putUpConfig()} 638 |
`; 639 | 640 | let touchpad = y` 641 |
642 |
Click
643 |
Double click
644 |
Hold
645 |
646 |
647 | ${this.putUpConfig()} 648 |
`; 649 | 650 | if (Object.keys(this.tabs).filter(e => this.tabs[e] === true)[0] !== 'otherIcon') 651 | if (Object.keys(this.tabs).filter(e => this.tabs[e] === true)[0] == 'touchpad') 652 | return touchpad; 653 | else 654 | return normalCase; 655 | else 656 | return otherIcon; 657 | } 658 | 659 | putUpConfig(){ 660 | let currSelection = Object.keys(this.tabs).filter(e => this.tabs[e] === true); 661 | if(currSelection[0] === 'settings') 662 | return this.generalConfig(); 663 | else 664 | switch (currSelection[1]) { 665 | case 'click': 666 | return this.clickConfig(currSelection[0]); 667 | case 'dblclick': 668 | return this.dblclickConfig(currSelection[0]); 669 | case 'hold': 670 | return this.holdConfig(currSelection[0]); 671 | case 'icon': 672 | return this.iconConfig(currSelection[0]); 673 | case 'volume': 674 | return this.doubleConfig(currSelection[1]); 675 | case 'channel': 676 | return this.doubleConfig(currSelection[1]); 677 | } 678 | } 679 | 680 | 681 | doubleConfig(a) { 682 | return y` 683 | ${this.iconConfig(a+'_up',true)} 684 | ${this.iconConfig(a + '_down',true)} 685 | ${this.clickConfig(a + '_up',true)} 686 | ${this.clickConfig(a+'_down',true)}`; 687 | } 688 | 689 | iconConfig(a, double = false) { 690 | return y` 691 | 692 | `; 693 | } 694 | 695 | clickConfig(a,double=false){ 696 | let directSelector = y` 697 | 698 | Default 699 | Toggle 700 | Turn on 701 | Turn off 702 | ${Object.keys(this.hass.states).filter(ent => ent.match(/automation|script/)).map(action => { 703 | return y` ${action} `; 704 | })} 705 | `; 706 | let advancedSelector = y ` 707 | 708 | Default 709 | Toggle 710 | Turn on 711 | Turn off 712 | ${Object.keys(this.hass.states).filter(ent => ent.match(/automation|script/)).map(action => { 713 | return y` ${action} `; 714 | })} 715 | 716 | 717 | ${Object.keys(this.hass.states).filter(e => !e.match(/script|automation|sensor|device_tracker|binary_sensor|number|update|person|sun|zone|persistent_notification|weather|input|camera|counter|select/)).map(entity => { 718 | return y` ${entity} `; 719 | })} 720 | `; 721 | if(this._config[Object.keys(this.entityTabs).filter(e => this.entityTabs[e] === true)].actions[a].click.type.match(/toggle|turn-on|turn-off/)) 722 | return advancedSelector; 723 | else 724 | return directSelector; 725 | } 726 | 727 | dblclickConfig(a){ 728 | let directSelector = y` 729 | 730 | ${a === 'touchpad' ? 'Default' : 'Nothing'} 731 | Toggle 732 | Turn on 733 | Turn off 734 | ${Object.keys(this.hass.states).filter(ent => ent.match(/automation|script/)).map(action => { 735 | return y` ${action} `; 736 | })} 737 | `; 738 | 739 | let advancedSelector = y ` 740 | 741 | ${a === 'touchpad' ? 'Default' : 'Nothing'} 742 | Toggle 743 | Turn on 744 | Turn off 745 | ${Object.keys(this.hass.states).filter(ent => ent.match(/automation|script/)).map(action => { 746 | return y` ${action} `; 747 | })} 748 | 749 | 750 | ${Object.keys(this.hass.states).filter(e => !e.match(/script|automation|sensor|device_tracker|binary_sensor|number|update|person|sun|zone|persistent_notification|weather|input|camera|counter|select/)).map(entity => { 751 | return y` ${entity} `; 752 | })} 753 | `; 754 | 755 | if(this._config[Object.keys(this.entityTabs).filter(e => this.entityTabs[e] === true)].actions[a].dblclick.type.match(/toggle|turn-on|turn-off/)) 756 | return advancedSelector; 757 | else 758 | return directSelector; 759 | 760 | } 761 | holdConfig(a){ 762 | let directSelector = y` 763 | 764 | ${a === 'touchpad' ? 'Default' : 'Nothing'} 765 | Toggle 766 | Turn on 767 | Turn off 768 | ${Object.keys(this.hass.states).filter(ent => ent.match(/automation|script/)).map(action => { 769 | return y` ${action} `; 770 | })} 771 | `; 772 | 773 | let advancedSelector = y ` 774 | 775 | ${a === 'touchpad' ? 'Default' : 'Nothing'} 776 | Toggle 777 | Turn on 778 | Turn off 779 | ${Object.keys(this.hass.states).filter(ent => ent.match(/automation|script/)).map(action => { 780 | return y` ${action} `; 781 | })} 782 | 783 | 784 | ${Object.keys(this.hass.states).filter(e => !e.match(/script|automation|sensor|device_tracker|binary_sensor|number|update|person|sun|zone|persistent_notification|weather|input|camera|counter|select/)).map(entity => { 785 | return y` ${entity} `; 786 | })} 787 | `; 788 | 789 | if(this._config[Object.keys(this.entityTabs).filter(e => this.entityTabs[e] === true)].actions[a].hold.type.match(/toggle|turn-on|turn-off/)) 790 | return advancedSelector; 791 | else 792 | return directSelector; 793 | 794 | } 795 | generalConfig() { 796 | if(this._config[Object.keys(this.entityTabs).filter(e => this.entityTabs[e] === true)].hasOwnProperty('icons') && this._config[Object.keys(this.entityTabs).filter(e => this.entityTabs[e] === true)].hasOwnProperty('entity')){ 797 | return y` 798 | 799 | ${this.iconConfig('topicon')} 800 | 801 | `; 802 | } 803 | } 804 | changeTab(e) { 805 | if(Object.keys(this.tabs).filter(e => this.tabs[e] == true)[0].match(/power|source|mute|touchpad/) ) 806 | if(Object.keys(this.tabs).filter(e => this.tabs[e] == true)[1].match(/click|dblclick|hold/)) 807 | if(this._config[Object.keys(this.entityTabs).filter(e => this.entityTabs[e] === true)].actions[Object.keys(this.tabs).filter(e => this.tabs[e] == true)[0]][Object.keys(this.tabs).filter(e => this.tabs[e] == true)[1]].type.match(/toggle|turn-on|turn-off/) && this._config[Object.keys(this.entityTabs).filter(e => this.entityTabs[e] === true)].actions[Object.keys(this.tabs).filter(e => this.tabs[e] == true)[0]][Object.keys(this.tabs).filter(e => this.tabs[e] == true)[1]].entity === null) 808 | this._config[Object.keys(this.entityTabs).filter(e => this.entityTabs[e] === true)].actions[Object.keys(this.tabs).filter(e => this.tabs[e] == true)[0]][Object.keys(this.tabs).filter(e => this.tabs[e] == true)[1]].type = 'no-action'; 809 | if(e.match(/^(click|dblclick|hold|icon|volume|channel)$/)) 810 | Object.keys(this.tabs).filter(val => val.match(/^(click|dblclick|hold|icon|volume|channel)$/)).forEach(va => this.tabs[va] = false); 811 | else if (e.match(/^(power|mute|source|otherIcon|touchpad|settings)$/)){ 812 | Object.keys(this.tabs).forEach(e => this.tabs[e] = false); 813 | if(e != 'settings' ) 814 | if(e === 'otherIcon') 815 | this.tabs['volume'] = true; 816 | else 817 | this.tabs['click'] = true; 818 | } 819 | this.tabs[e] = true; 820 | 821 | this.requestUpdate(); 822 | } 823 | 824 | setConfig(config) { 825 | this.orderConfig(config); 826 | if (Object.keys(this.entityTabs).length === 0) 827 | Object.keys(this._config).slice(1).forEach(e => { 828 | this.entityTabs[e] = false; 829 | }); 830 | if (Object.keys(this._config).length > 1) { 831 | if (Object.values(this.entityTabs).every(element => element === false) || Object.values(this.entityTabs).every(element => element === true)) { 832 | this.entityTabs[Object.keys(this.entityTabs)[0]] = true; 833 | } 834 | if (Object.keys(this._config).length === 2) 835 | this.entityTabs[Object.keys(this.entityTabs)[0]] = true; 836 | } 837 | } 838 | 839 | orderConfig(config){ 840 | let tempConfig = JSON.parse(JSON.stringify(config)); 841 | if (Object.keys(tempConfig).length > 0) { 842 | let newOrder = Object.keys(tempConfig).slice(1).sort( 843 | (a, b) => { 844 | return tempConfig[a].position - tempConfig[b].position 845 | }); 846 | delete this._config; 847 | this._config = {}; 848 | this._config.type = tempConfig.type; 849 | 850 | newOrder.forEach((item) => { 851 | this._config[item] = JSON.parse(JSON.stringify(tempConfig[item])); 852 | }); 853 | for (let i = 1; i < Object.keys(this._config).length; i++) { 854 | this._config[Object.keys(this._config)[i]].position = i; 855 | } 856 | } 857 | else 858 | this._config = JSON.parse(JSON.stringify(tempConfig)); 859 | return this._config; 860 | } 861 | 862 | orderEntityTabs(){ 863 | let tempEntityTabs = {}; 864 | let newOrder = Object.keys(this.entityTabs).sort( 865 | (a, b) => { 866 | return this._config[a].position - this._config[b].position 867 | }); 868 | delete this.entityTabs; 869 | newOrder.forEach((item) => { 870 | tempEntityTabs[item] = this.entityTabs[item]; 871 | }); 872 | this.entityTabs = tempEntityTabs; 873 | } 874 | 875 | alert() {; 876 | let alert = document.createElement('div'); 877 | alert.classList.add('alerts', 'fade'); 878 | alert.style.color = this.displayMessage ? 'var(--mdc-theme-primary)' : 'transparent'; 879 | alert.innerHTML = this.message; 880 | alert.offsetWidth; 881 | return alert; 882 | } 883 | 884 | 885 | newEntityTab() { 886 | if(this.newEnt) 887 | return y`
New
`; 888 | else if (this._config?.[Object.keys(this.entityTabs).filter(e => this.entityTabs[e] === true)] != undefined) 889 | return y` 890 |
891 |
892 | `; 893 | } 894 | 895 | addEntity() { 896 | Object.keys(this.entityTabs).forEach(e => { 897 | this.entityTabs[e] = false; 898 | }); 899 | this.newEnt = true; 900 | } 901 | 902 | removeEnt(what) { 903 | if (what === 'start') 904 | holdRemove = setTimeout(() => { 905 | let entToRemove = Object.keys(this.entityTabs).filter(e => this.entityTabs[e] === true); 906 | let i = Object.keys(this._config).indexOf(entToRemove[0]); 907 | delete this._config[entToRemove]; 908 | delete this.entityTabs[entToRemove]; 909 | if (Object.keys(this._config).length === 1) 910 | this.newEnt = true; 911 | else 912 | this.entityTabs[Object.keys(this.entityTabs)[0]] = true; 913 | for (i; i < Object.keys(this._config).length; i++){ 914 | this._config[Object.keys(this._config)[i]].position = i; 915 | } 916 | this.setMessage('removed ' + entToRemove); 917 | 918 | }, 1000); 919 | else if (what === 'stop') 920 | clearTimeout(holdRemove); 921 | } 922 | 923 | setMessage(message) { 924 | this.message = message; 925 | this.displayMessage = false; 926 | this.displayMessage = true; 927 | clearTimeout(msg); 928 | msg = setTimeout(() => { 929 | this.displayMessage = false; 930 | }, 5000); 931 | 932 | } 933 | 934 | touchStart(e){ 935 | window.initialX = e.touches[0].clientX; 936 | window.initialY = e.touches[0].clientY; 937 | } 938 | 939 | moveCurrentEntity(e) { 940 | if( ! initialX || ! initialY){ 941 | return; 942 | } 943 | 944 | var currentX = e.touches[0].clientX; 945 | var currentY = e.touches[0].clientY; 946 | 947 | var diffX = initialX - currentX; 948 | var diffY = initialY - currentY; 949 | if (Math.abs(diffX) > Math.abs(diffY)) { 950 | if (diffX > 0) { 951 | if(Object.keys(this.entityTabs).indexOf(Object.keys(this.entityTabs).filter(e => this.entityTabs[e] === true)[0]) === 0){ 952 | this.setMessage('first element'); 953 | } 954 | else{ 955 | this._config[Object.keys(this.entityTabs).filter(e => this.entityTabs[e] === true)[0]].position--; 956 | this._config[Object.keys(this.entityTabs)[Object.keys(this.entityTabs).indexOf(Object.keys(this.entityTabs).filter(e => this.entityTabs[e] === true)[0])-1]].position++; 957 | this.orderConfig(this._config); 958 | this.orderEntityTabs(); 959 | } 960 | 961 | } else { 962 | if(Object.keys(this.entityTabs).indexOf(Object.keys(this.entityTabs).filter(e => this.entityTabs[e] === true)[0]) === Object.keys(this.entityTabs).length-1){ 963 | this.setMessage('last element'); 964 | } 965 | else{ 966 | this._config[Object.keys(this.entityTabs).filter(e => this.entityTabs[e] === true)[0]].position++; 967 | this._config[Object.keys(this.entityTabs)[Object.keys(this.entityTabs).indexOf(Object.keys(this.entityTabs).filter(e => this.entityTabs[e] === true)[0])+1]].position--; 968 | this.orderConfig(this._config); 969 | this.orderEntityTabs(); 970 | } 971 | } 972 | } 973 | initialX = null; 974 | initialY = null; 975 | chgConfig.detail = { config: this._config }; 976 | this.dispatchEvent(chgConfig); 977 | this.requestUpdate(); 978 | } 979 | 980 | 981 | changeCurrentEntity(caller) { 982 | if (this.moveHint) { 983 | this.setMessage('Swipe on selected entity tab to move it'); 984 | this.moveHint = false; 985 | } 986 | Object.keys(this.entityTabs).forEach(e => { 987 | this.entityTabs[e] = false; 988 | }); 989 | this.entityTabs[caller] = true; 990 | 991 | chgConfig.detail = { config: this._config }; 992 | this.dispatchEvent(chgConfig); 993 | this.requestUpdate(); 994 | } 995 | 996 | 997 | updateIt(e, ha = this.hass) { 998 | 999 | if (e?.target.id === 'entity-selector' && e.target.value !== undefined && e.target.value !== null && e.target.value.length !== 0) { 1000 | if (this._config[e.target.value.substring(13)] === undefined) { 1001 | 1002 | if (!this.newEnt && this._config?.[Object.keys(this.entityTabs).filter(e => this.entityTabs[e] === true)]?.entity !== undefined) { 1003 | this._config[e.target.value.substring(13)] = JSON.parse(JSON.stringify(this._config[Object.keys(this.entityTabs).filter(e => this.entityTabs[e] === true)])); 1004 | delete this._config[Object.keys(this.entityTabs).filter(e => this.entityTabs[e] === true)]; 1005 | delete this.entityTabs[Object.keys(this.entityTabs).filter(e => this.entityTabs[e] === true)]; 1006 | } 1007 | 1008 | this.entityTabs[e.target.value.substring(13)] = true; 1009 | this._config[e.target.value.substring(13)] = {}; 1010 | this._config[e.target.value.substring(13)].entity = e.target.value; 1011 | this._config[e.target.value.substring(13)].display_name = this.hass.states[e.target.value].attributes.friendly_name.slice(0, 10); 1012 | this.tmpName = this._config[e.target.value.substring(13)].display_name; 1013 | if (!this._config[e.target.value.substring(13)].hasOwnProperty('position')) 1014 | this._config[e.target.value.substring(13)].position = Object.keys(this._config).length - 1; 1015 | } 1016 | 1017 | this.newEnt = false; 1018 | } 1019 | 1020 | if (this._config?.[Object.keys(this.entityTabs).filter(e => this.entityTabs[e] === true)]?.entity != undefined) { 1021 | 1022 | if(!this._config[Object.keys(this.entityTabs).filter(e => this.entityTabs[e] === true)[0]].hasOwnProperty('display_name')){ 1023 | this._config[Object.keys(this.entityTabs).filter(e => this.entityTabs[e] === true)[0]].display_name = this._config[Object.keys(this.entityTabs).filter(e => this.entityTabs[e] === true)[0]].display_name.slice(0,10) || this.hass.states[this._config[Object.keys(this.entityTabs).filter(e => this.entityTabs[e] === true)[0]]?.entity].attributes.friendly_name.slice(0,10); 1024 | } 1025 | 1026 | if(!this._config[Object.keys(this.entityTabs).filter(e => this.entityTabs[e] === true)].hasOwnProperty('fancy_borders')) 1027 | this._config[Object.keys(this.entityTabs).filter(e => this.entityTabs[e] === true)].fancy_borders = true; 1028 | 1029 | if(!this._config[Object.keys(this.entityTabs).filter(e => this.entityTabs[e] === true)].hasOwnProperty('actions')){ 1030 | this._config[Object.keys(this.entityTabs).filter(e => this.entityTabs[e] === true)].actions = { 1031 | power: {click: {type: 'default',entity: null}, dblclick: {type: 'no-action',entity: null},hold: {type: 'no-action',entity: null}}, 1032 | source: {click: {type: 'default',entity: null}, dblclick: {type: 'no-action',entity: null},hold: {type: 'no-action',entity: null}}, 1033 | mute: { click: { type: 'default', entity: null }, dblclick: { type: 'no-action', entity: null }, hold: { type: 'no-action', entity: null } }, 1034 | touchpad: { click: { type: 'default', entity: null }, dblclick: { type: 'default', entity: null }, hold: { type: 'default', entity: null } }, 1035 | volume_up: { click: { type: 'default', entity: null } , hold: { type: 'default', entity: null }}, 1036 | volume_down: { click: { type: 'default', entity: null } , hold: { type: 'default', entity: null }}, 1037 | channel_up: { click: { type: 'default', entity: null } , hold: { type: 'default', entity: null }}, 1038 | channel_down: { click: { type: 'default', entity: null } , hold: { type: 'default', entity: null }} 1039 | }; 1040 | } 1041 | 1042 | if(this._config[Object.keys(this.entityTabs).filter(e => this.entityTabs[e] === true)].hasOwnProperty('icons')){ 1043 | this._config[Object.keys(this.entityTabs).filter(e => this.entityTabs[e] === true)].icons = { 1044 | topicon: this._config[Object.keys(this.entityTabs).filter(e => this.entityTabs[e] === true)].icons.topicon === undefined ? 'mdi:youtube-tv' : this._config[Object.keys(this.entityTabs).filter(e => this.entityTabs[e] === true)].icons.topicon, 1045 | power: this._config[Object.keys(this.entityTabs).filter(e => this.entityTabs[e] === true)].icons.power === undefined ? 'mdi:power' : this._config[Object.keys(this.entityTabs).filter(e => this.entityTabs[e] === true)].icons.power, 1046 | channel_up: this._config[Object.keys(this.entityTabs).filter(e => this.entityTabs[e] === true)].icons.channel_up === undefined ? 'mdi:arrow-up-bold-circle' : this._config[Object.keys(this.entityTabs).filter(e => this.entityTabs[e] === true)].icons.channel_up, 1047 | channel_down: this._config[Object.keys(this.entityTabs).filter(e => this.entityTabs[e] === true)].icons.channel_down === undefined ? 'mdi:arrow-down-bold-circle' : this._config[Object.keys(this.entityTabs).filter(e => this.entityTabs[e] === true)].icons.channel_down, 1048 | source: this._config[Object.keys(this.entityTabs).filter(e => this.entityTabs[e] === true)].icons.source === undefined ? 'mdi:logout-variant' : this._config[Object.keys(this.entityTabs).filter(e => this.entityTabs[e] === true)].icons.source, 1049 | mute: this._config[Object.keys(this.entityTabs).filter(e => this.entityTabs[e] === true)].icons.mute === undefined ? 'mdi:volume-variant-off' : this._config[Object.keys(this.entityTabs).filter(e => this.entityTabs[e] === true)].icons.mute, 1050 | volume_up: this._config[Object.keys(this.entityTabs).filter(e => this.entityTabs[e] === true)].icons.volume_up === undefined ? 'mdi:volume-high' : this._config[Object.keys(this.entityTabs).filter(e => this.entityTabs[e] === true)].icons.volume_up, 1051 | volume_down: this._config[Object.keys(this.entityTabs).filter(e => this.entityTabs[e] === true)].icons.volume_down === undefined ? 'mdi:volume-medium' : this._config[Object.keys(this.entityTabs).filter(e => this.entityTabs[e] === true)].icons.volume_down 1052 | }; 1053 | 1054 | } 1055 | else { 1056 | this._config[Object.keys(this.entityTabs).filter(e => this.entityTabs[e] === true)].icons = { 1057 | topicon: 'mdi:youtube-tv', 1058 | power: 'mdi:power', 1059 | channel_up: 'mdi:arrow-up-bold-circle', 1060 | channel_down: 'mdi:arrow-down-bold-circle', 1061 | source: 'mdi:logout-variant', 1062 | mute: 'mdi:volume-variant-off', 1063 | volume_up: 'mdi:volume-high', 1064 | volume_down: 'mdi:volume-medium' 1065 | }; 1066 | } 1067 | 1068 | if (e?.target.id === 'click-service-selector' || e?.target.id === 'click-entity-selector') { 1069 | if(this._config[Object.keys(this.entityTabs).filter(e => this.entityTabs[e] === true)].actions[Object.keys(this.tabs).filter(e => this.tabs[e] === true)[0]].click.type !== e.target.value || this._config[Object.keys(this.entityTabs).filter(e => this.entityTabs[e] === true)].actions[Object.keys(this.tabs).filter(e => this.tabs[e] === true)[0]].click.entity !== e.target.value) 1070 | if(e?.target.id === 'click-service-selector') 1071 | if(e.target.value.match(/toggle|turn-on|turn-off|default/)){ 1072 | this._config[Object.keys(this.entityTabs).filter(e => this.entityTabs[e] === true)].actions[Object.keys(this.tabs).filter(e => this.tabs[e] === true)[0]].click.type = e.target.value; 1073 | if(e.target.value === 'default') 1074 | this._config[Object.keys(this.entityTabs).filter(e => this.entityTabs[e] === true)].actions[Object.keys(this.tabs).filter(e => this.tabs[e] === true)[0]].click.entity = null; 1075 | this.putUpConfig(); 1076 | this.requestUpdate(); 1077 | } 1078 | else { 1079 | this._config[Object.keys(this.entityTabs).filter(e => this.entityTabs[e] === true)].actions[Object.keys(this.tabs).filter(e => this.tabs[e] === true)[0]].click.type = e.target.value.substring(0,e.target.value.indexOf('.')); 1080 | this._config[Object.keys(this.entityTabs).filter(e => this.entityTabs[e] === true)].actions[Object.keys(this.tabs).filter(e => this.tabs[e] === true)[0]].click.entity = e.target.value; 1081 | } 1082 | else if(e?.target.id === 'click-entity-selector' ) 1083 | this._config[Object.keys(this.entityTabs).filter(e => this.entityTabs[e] === true)].actions[Object.keys(this.tabs).filter(e => this.tabs[e] === true)[0]].click.entity = e.target.value; 1084 | else 1085 | return; 1086 | } 1087 | if(e?.target.id === 'hold-service-selector' || e?.target.id === 'hold-entity-selector'){ 1088 | if(this._config[Object.keys(this.entityTabs).filter(e => this.entityTabs[e] === true)].actions[Object.keys(this.tabs).filter(e => this.tabs[e] === true)[0]].hold.type !== e.target.value || this._config[Object.keys(this.entityTabs).filter(e => this.entityTabs[e] === true)].actions[Object.keys(this.tabs).filter(e => this.tabs[e] === true)[0]].hold.entity !== e.target.value) 1089 | if(e?.target.id === 'hold-service-selector') 1090 | if(e.target.value.match(/toggle|turn-on|turn-off|no-action|default/)){ 1091 | this._config[Object.keys(this.entityTabs).filter(e => this.entityTabs[e] === true)].actions[Object.keys(this.tabs).filter(e => this.tabs[e] === true)[0]].hold.type = e.target.value; 1092 | if(e.target.value.match(/no-action|default/)) 1093 | this._config[Object.keys(this.entityTabs).filter(e => this.entityTabs[e] === true)].actions[Object.keys(this.tabs).filter(e => this.tabs[e] === true)[0]].hold.entity = null; 1094 | this.putUpConfig(); 1095 | this.requestUpdate(); 1096 | } 1097 | else { 1098 | this._config[Object.keys(this.entityTabs).filter(e => this.entityTabs[e] === true)].actions[Object.keys(this.tabs).filter(e => this.tabs[e] === true)[0]].hold.type = e.target.value.substring(0,e.target.value.indexOf('.')); 1099 | this._config[Object.keys(this.entityTabs).filter(e => this.entityTabs[e] === true)].actions[Object.keys(this.tabs).filter(e => this.tabs[e] === true)[0]].hold.entity = e.target.value; 1100 | } 1101 | else if(e?.target.id === 'hold-entity-selector' ) 1102 | this._config[Object.keys(this.entityTabs).filter(e => this.entityTabs[e] === true)].actions[Object.keys(this.tabs).filter(e => this.tabs[e] === true)[0]].hold.entity = e.target.value; 1103 | else 1104 | return; 1105 | } 1106 | if(e?.target.id === 'dblclick-service-selector' || e?.target.id === 'dblclick-entity-selector'){ 1107 | if(this._config[Object.keys(this.entityTabs).filter(e => this.entityTabs[e] === true)].actions[Object.keys(this.tabs).filter(e => this.tabs[e] === true)[0]].dblclick.type != e.target.value || this._config[Object.keys(this.entityTabs).filter(e => this.entityTabs[e] === true)].actions[Object.keys(this.tabs).filter(e => this.tabs[e] === true)[0]].dblclick.entity !== e.target.value) 1108 | if(e?.target.id === 'dblclick-service-selector') 1109 | if(e.target.value.match(/toggle|turn-on|turn-off|no-action|default/)){ 1110 | this._config[Object.keys(this.entityTabs).filter(e => this.entityTabs[e] === true)].actions[Object.keys(this.tabs).filter(e => this.tabs[e] === true)[0]].dblclick.type = e.target.value; 1111 | if(e.target.value.match(/no-action|default/)) 1112 | this._config[Object.keys(this.entityTabs).filter(e => this.entityTabs[e] === true)].actions[Object.keys(this.tabs).filter(e => this.tabs[e] === true)[0]].dblclick.entity = null; 1113 | this.putUpConfig(); 1114 | this.requestUpdate(); 1115 | } 1116 | else { 1117 | this._config[Object.keys(this.entityTabs).filter(e => this.entityTabs[e] === true)].actions[Object.keys(this.tabs).filter(e => this.tabs[e] === true)[0]].dblclick.type = e.target.value.substring(0,e.target.value.indexOf('.')); 1118 | this._config[Object.keys(this.entityTabs).filter(e => this.entityTabs[e] === true)].actions[Object.keys(this.tabs).filter(e => this.tabs[e] === true)[0]].dblclick.entity = e.target.value; 1119 | } 1120 | else if(e?.target.id === 'dblclick-entity-selector' ){ 1121 | this._config[Object.keys(this.entityTabs).filter(e => this.entityTabs[e] === true)].actions[Object.keys(this.tabs).filter(e => this.tabs[e] === true)[0]].dblclick.entity = e.target.value;} 1122 | else 1123 | return; 1124 | } 1125 | 1126 | 1127 | if (e?.target.id === 'volume-up-click-service-selector' || e?.target.id === 'volume-up-click-entity-selector') { 1128 | if(this._config[Object.keys(this.entityTabs).filter(e => this.entityTabs[e] === true)].actions['volume_up'].click.type !== e.target.value || this._config[Object.keys(this.entityTabs).filter(e => this.entityTabs[e] === true)].actions['volume_up'].click.entity !== e.target.value) 1129 | if(e?.target.id === 'volume-up-click-service-selector') 1130 | if(e.target.value.match(/toggle|turn-on|turn-off|default/)){ 1131 | this._config[Object.keys(this.entityTabs).filter(e => this.entityTabs[e] === true)].actions['volume_up'].click.type = e.target.value; 1132 | if(e.target.value === 'default'){ 1133 | this._config[Object.keys(this.entityTabs).filter(e => this.entityTabs[e] === true)].actions['volume_up'].click.type = 'default'; 1134 | this._config[Object.keys(this.entityTabs).filter(e => this.entityTabs[e] === true)].actions['volume_up'].click.entity = null;} 1135 | this.putUpConfig(); 1136 | this.requestUpdate(); 1137 | } 1138 | else { 1139 | this._config[Object.keys(this.entityTabs).filter(e => this.entityTabs[e] === true)].actions['volume_up'].click.type = e.target.value.substring(0,e.target.value.indexOf('.')); 1140 | this._config[Object.keys(this.entityTabs).filter(e => this.entityTabs[e] === true)].actions['volume_up'].click.entity = e.target.value; 1141 | } 1142 | else if(e?.target.id === 'volume-up-click-entity-selector' ) 1143 | this._config[Object.keys(this.entityTabs).filter(e => this.entityTabs[e] === true)].actions['volume_up'].click.entity = e.target.value; 1144 | else 1145 | return; 1146 | } 1147 | 1148 | 1149 | if (e?.target.id === 'volume-down-click-service-selector' || e?.target.id === 'volume-down-click-entity-selector') { 1150 | if(this._config[Object.keys(this.entityTabs).filter(e => this.entityTabs[e] === true)].actions['volume_down'].click.type !== e.target.value || this._config[Object.keys(this.entityTabs).filter(e => this.entityTabs[e] === true)].actions['volume_down'].click.entity !== e.target.value) 1151 | if(e?.target.id === 'volume-down-click-service-selector') 1152 | if(e.target.value.match(/toggle|turn-on|turn-off|default/)){ 1153 | this._config[Object.keys(this.entityTabs).filter(e => this.entityTabs[e] === true)].actions['volume_down'].click.type = e.target.value; 1154 | if(e.target.value === 'default'){ 1155 | this._config[Object.keys(this.entityTabs).filter(e => this.entityTabs[e] === true)].actions['volume_down'].click.type = 'default'; 1156 | this._config[Object.keys(this.entityTabs).filter(e => this.entityTabs[e] === true)].actions['volume_down'].click.entity = null;} 1157 | this.putUpConfig(); 1158 | this.requestUpdate(); 1159 | } 1160 | else { 1161 | this._config[Object.keys(this.entityTabs).filter(e => this.entityTabs[e] === true)].actions['volume_down'].click.type = e.target.value.substring(0,e.target.value.indexOf('.')); 1162 | this._config[Object.keys(this.entityTabs).filter(e => this.entityTabs[e] === true)].actions['volume_down'].click.entity = e.target.value; 1163 | } 1164 | else if(e?.target.id === 'volume-down-click-entity-selector' ) 1165 | this._config[Object.keys(this.entityTabs).filter(e => this.entityTabs[e] === true)].actions['volume_down'].click.entity = e.target.value; 1166 | else 1167 | return; 1168 | } 1169 | 1170 | 1171 | 1172 | if (e?.target.id === 'channel-up-click-service-selector' || e?.target.id === 'channel-up-click-entity-selector') { 1173 | if(this._config[Object.keys(this.entityTabs).filter(e => this.entityTabs[e] === true)].actions['channel_up'].click.type !== e.target.value || this._config[Object.keys(this.entityTabs).filter(e => this.entityTabs[e] === true)].actions['channel_up'].click.entity !== e.target.value) 1174 | if(e?.target.id === 'channel-up-click-service-selector') 1175 | if(e.target.value.match(/toggle|turn-on|turn-off|default/)){ 1176 | this._config[Object.keys(this.entityTabs).filter(e => this.entityTabs[e] === true)].actions['channel_up'].click.type = e.target.value; 1177 | if(e.target.value === 'default'){ 1178 | this._config[Object.keys(this.entityTabs).filter(e => this.entityTabs[e] === true)].actions['channel_up'].click.type = 'default'; 1179 | this._config[Object.keys(this.entityTabs).filter(e => this.entityTabs[e] === true)].actions['channel_up'].click.entity = null;} 1180 | this.putUpConfig(); 1181 | this.requestUpdate(); 1182 | } 1183 | else { 1184 | this._config[Object.keys(this.entityTabs).filter(e => this.entityTabs[e] === true)].actions['channel_up'].click.type = e.target.value.substring(0,e.target.value.indexOf('.')); 1185 | this._config[Object.keys(this.entityTabs).filter(e => this.entityTabs[e] === true)].actions['channel_up'].click.entity = e.target.value; 1186 | } 1187 | else if(e?.target.id === 'channel-up-click-entity-selector' ) 1188 | this._config[Object.keys(this.entityTabs).filter(e => this.entityTabs[e] === true)].actions['channel_up'].click.entity = e.target.value; 1189 | else 1190 | return; 1191 | } 1192 | 1193 | 1194 | 1195 | if (e?.target.id === 'channel-down-click-service-selector' || e?.target.id === 'channel-down-click-entity-selector') { 1196 | if(this._config[Object.keys(this.entityTabs).filter(e => this.entityTabs[e] === true)].actions['channel_down'].click.type !== e.target.value || this._config[Object.keys(this.entityTabs).filter(e => this.entityTabs[e] === true)].actions['channel_down'].click.entity !== e.target.value) 1197 | if(e?.target.id === 'channel-down-click-service-selector') 1198 | if(e.target.value.match(/toggle|turn-on|turn-off|default/)){ 1199 | this._config[Object.keys(this.entityTabs).filter(e => this.entityTabs[e] === true)].actions['channel_down'].click.type = e.target.value; 1200 | if(e.target.value === 'default'){ 1201 | this._config[Object.keys(this.entityTabs).filter(e => this.entityTabs[e] === true)].actions['channel_down'].click.type = 'default'; 1202 | this._config[Object.keys(this.entityTabs).filter(e => this.entityTabs[e] === true)].actions['channel_down'].click.entity = null;} 1203 | this.putUpConfig(); 1204 | this.requestUpdate(); 1205 | } 1206 | else { 1207 | this._config[Object.keys(this.entityTabs).filter(e => this.entityTabs[e] === true)].actions['channel_down'].click.type = e.target.value.substring(0,e.target.value.indexOf('.')); 1208 | this._config[Object.keys(this.entityTabs).filter(e => this.entityTabs[e] === true)].actions['channel_down'].click.entity = e.target.value; 1209 | } 1210 | else if(e?.target.id === 'channel-down-click-entity-selector' ) 1211 | this._config[Object.keys(this.entityTabs).filter(e => this.entityTabs[e] === true)].actions['channel_down'].click.entity = e.target.value; 1212 | else 1213 | return; 1214 | } 1215 | 1216 | if (e?.target.id === 'volume-up-icon-selector') { 1217 | if(Object.keys(this.tabs).filter(e => this.tabs[e] === true)[0] === 'otherIcon') 1218 | this._config[Object.keys(this.entityTabs).filter(e => this.entityTabs[e] === true)].icons['volume_up'] = e?.target?.value === undefined ? this._config[Object.keys(this.entityTabs).filter(e => this.entityTabs[e] === true)].icons['volume_up'] : e?.target?.value; 1219 | } 1220 | 1221 | if (e?.target.id === 'volume-down-icon-selector') { 1222 | if(Object.keys(this.tabs).filter(e => this.tabs[e] === true)[0] === 'otherIcon') 1223 | this._config[Object.keys(this.entityTabs).filter(e => this.entityTabs[e] === true)].icons['volume_down'] = e?.target?.value === undefined ? this._config[Object.keys(this.entityTabs).filter(e => this.entityTabs[e] === true)].icons['volume_down'] : e?.target?.value; 1224 | } 1225 | 1226 | if (e?.target.id === 'channel-up-icon-selector') { 1227 | if(Object.keys(this.tabs).filter(e => this.tabs[e] === true)[0] === 'otherIcon') 1228 | this._config[Object.keys(this.entityTabs).filter(e => this.entityTabs[e] === true)].icons['channel_up'] = e?.target?.value === undefined ? this._config[Object.keys(this.entityTabs).filter(e => this.entityTabs[e] === true)].icons['channel_up'] : e?.target?.value; 1229 | } 1230 | 1231 | if (e?.target.id === 'channel-down-icon-selector') { 1232 | if(Object.keys(this.tabs).filter(e => this.tabs[e] === true)[0] === 'otherIcon') 1233 | this._config[Object.keys(this.entityTabs).filter(e => this.entityTabs[e] === true)].icons['channel_down'] = e?.target?.value === undefined ? this._config[Object.keys(this.entityTabs).filter(e => this.entityTabs[e] === true)].icons['channel_down'] : e?.target?.value; 1234 | } 1235 | 1236 | if (e?.target.id === 'icon-selector') { 1237 | if (Object.keys(this.tabs).filter(e => this.tabs[e] === true)[0] === 'settings') { 1238 | this._config[Object.keys(this.entityTabs).filter(e => this.entityTabs[e] === true)].icons['topicon'] = e?.target?.value === undefined ? this._config[Object.keys(this.entityTabs).filter(e => this.entityTabs[e] === true)].icons['topicon'] : e?.target?.value; 1239 | } 1240 | else { 1241 | this._config[Object.keys(this.entityTabs).filter(e => this.entityTabs[e] === true)].icons[Object.keys(this.tabs).filter(e => this.tabs[e] === true)[0]] = e?.target?.value === undefined ? this._config[Object.keys(this.entityTabs).filter(e => this.entityTabs[e] === true)].icons[Object.keys(this.tabs).filter(e => this.tabs[e] === true)[0]] : e?.target?.value; 1242 | } 1243 | } 1244 | 1245 | if(e?.target.id === 'name'){ 1246 | this.tmpName = e?.target.value; 1247 | this._config[Object.keys(this.entityTabs).filter(e => this.entityTabs[e] === true)[0]].display_name = e?.target.value === '' ? this.hass.states[this._config[Object.keys(this.entityTabs).filter(e => this.entityTabs[e] === true)[0]].entity].attributes.friendly_name.slice(0,10) : e?.target.value; 1248 | } 1249 | 1250 | if(e?.target.id === 'fancy-borders-selector' ) 1251 | this._config[Object.keys(this.entityTabs).filter(e => this.entityTabs[e] === true)].fancy_borders = !this._config?.[Object.keys(this.entityTabs).filter(e => this.entityTabs[e] === true)].fancy_borders ; 1252 | } 1253 | 1254 | chgConfig.detail = { config: this._config }; 1255 | this.dispatchEvent(chgConfig); 1256 | this.requestUpdate(); 1257 | } 1258 | static get styles(){ 1259 | return i$1` 1260 | ha-select.main{ 1261 | border-top: 3px solid var(--mdc-theme-primary); 1262 | width: 100%; 1263 | margin-bottom: 0.5rem; 1264 | } 1265 | 1266 | .content > *{ 1267 | padding:0; 1268 | width:100%; 1269 | } 1270 | 1271 | .sub-selected { 1272 | color: white; 1273 | padding-bottom:0; 1274 | border-bottom: 3px solid var(--mdc-theme-primary); 1275 | border-top: 0; 1276 | } 1277 | .sub-tabs-row :not(.sub-selected){ 1278 | color: gray; 1279 | } 1280 | 1281 | ha-icon { 1282 | margin: 0; 1283 | color: gray; 1284 | } 1285 | .selected ha-icon { 1286 | color: white 1287 | } 1288 | 1289 | .sub-tabs-row .tab{ 1290 | display: inline-block; 1291 | padding-left: 1rem; 1292 | padding-right: 1rem; 1293 | } 1294 | .sub-tabs-row{ 1295 | padding-top: 7px; 1296 | padding-bottom: 2px; 1297 | text-align: center; 1298 | margin:0 auto auto auto; 1299 | } 1300 | .content{ 1301 | display:block; 1302 | } 1303 | .sub-tabs { 1304 | border-top: 3px solid var(--mdc-theme-primary); 1305 | border-radius: 0.2rem ; 1306 | background-color: var(--card-background-color); 1307 | } 1308 | 1309 | .ent-tab-row .tab{ 1310 | margin: 0 0.5rem; 1311 | text-align: center; 1312 | } 1313 | div.add{ 1314 | float: right; 1315 | display: inline-block; 1316 | transform: translateY(5px); 1317 | text-align: center; 1318 | padding: 0 0.3rem; 1319 | height: 100%; 1320 | } 1321 | div.remove{ 1322 | float: right; 1323 | display: inline-block; 1324 | transform: translateY(5px); 1325 | text-align: center; 1326 | padding: 0 0.3rem; 1327 | height: 100%; } 1328 | 1329 | .tab-row{ 1330 | text-align: center; 1331 | display: flex; 1332 | flex-direction: row; 1333 | } 1334 | 1335 | .tab{ 1336 | display: inline-block; 1337 | margin: auto; 1338 | } 1339 | 1340 | .selected { 1341 | border-radius: 0.9rem 0.9rem 0 0; 1342 | padding: 0.5rem 1.7rem; 1343 | background-color: var(--mdc-theme-primary); 1344 | } 1345 | 1346 | 1347 | 1348 | .alerts{ 1349 | text-align: center; 1350 | margin-bottom: 0.5rem; 1351 | } 1352 | .fade{ 1353 | animation: fadeOut 5s; 1354 | } 1355 | 1356 | @keyframes fadeOut { 1357 | 0% { opacity: 1; } 1358 | 100% { opacity: 0; } 1359 | } 1360 | 1361 | .hide{ 1362 | display: none; 1363 | } 1364 | .show{ 1365 | display: block; 1366 | } 1367 | `; 1368 | } 1369 | } 1370 | 1371 | customElements.define("contento-card-editor", ContentCardEditor); 1372 | 1373 | customElements.define('my-tv-card', MyElement); 1374 | window.customCards = window.customCards || []; 1375 | window.customCards.push({ 1376 | type: "my-tv-card", 1377 | name: "Touchpad remote for tv", 1378 | preview: false, 1379 | description: "A confortable tv remote with touchpad" 1380 | }); 1381 | --------------------------------------------------------------------------------