From 4b4a1af316dd67ca40852780a552f75ddad36443 Mon Sep 17 00:00:00 2001 From: Florian Brinker Date: Sun, 21 Mar 2021 21:41:34 +0100 Subject: [PATCH] Add battery overview --- .gitignore | 2 ++ README.md | 1 + config/views/devices.yaml | 32 +++++++++++++++++- config/views/overview.yaml | 31 ++++++++++++++++- config/views/robots.yaml | 1 - configuration.yaml | 4 ++- ui-lovelace.yaml | 1 - .../battery-state-card/battery-state-card.js | 2 ++ www/vacuums/Dobby_liveMap.png | Bin 14808 -> 0 bytes 9 files changed, 69 insertions(+), 5 deletions(-) create mode 100644 www/lovelace/custom/battery-state-card/battery-state-card.js delete mode 100644 www/vacuums/Dobby_liveMap.png diff --git a/.gitignore b/.gitignore index be588c0..168631b 100644 --- a/.gitignore +++ b/.gitignore @@ -29,3 +29,5 @@ google_assistant_service_keys.json secrets.yaml secrets.js known_devices.yaml + +/www/vacuums/*.png diff --git a/README.md b/README.md index 0b50210..bc15c15 100644 --- a/README.md +++ b/README.md @@ -70,6 +70,7 @@ I use the following software, running in docker containers, on my Raspberry Pi: * https://github.com/thomasloven/lovelace-auto-entities * https://github.com/bradcrc/Now-Playing-Card * https://github.com/bradcrc/color-lite-card +* https://github.com/maxwroc/battery-state-card ## Packages * https://github.com/Barma-lej/halandroid diff --git a/config/views/devices.yaml b/config/views/devices.yaml index 411c1b1..b049d41 100644 --- a/config/views/devices.yaml +++ b/config/views/devices.yaml @@ -43,4 +43,34 @@ cards: entities: - entity: switch.wallboard_display name: Display - icon: mdi:tablet \ No newline at end of file + icon: mdi:tablet + + - type: custom:battery-state-card + title: "Batterie Übersicht" + sort_by_level: "asc" + #collapse: 4 + filter: + include: + - name: entity_id + value: "*_battery_level" + - name: attributes.device_class + value: battery + - name: attributes.battery_level + operator: "exists" + exclude: + - name: state + operator: "matches" + value: "/charging|discharging/i" + - name: entity_id + operator: "matches" + value: "/samsung|pixel/i" + - name: state + operator: ">" + value: 99 + - name: attributes.friendly_name # require a friendly_name + operator: "=" + value: + bulk_rename: + - from: "Batterie" + - from: "Battery" + - from: "Power" \ No newline at end of file diff --git a/config/views/overview.yaml b/config/views/overview.yaml index db36901..f517376 100644 --- a/config/views/overview.yaml +++ b/config/views/overview.yaml @@ -2,6 +2,35 @@ title: Übersicht path: overview icon: "mdi:tablet-dashboard" cards: + - type: custom:battery-state-card + title: "Batterie Warnung" + sort_by_level: "asc" + filter: + include: + - name: entity_id + value: "*_battery_level" + - name: attributes.device_class + value: battery + - name: attributes.battery_level + operator: "exists" + exclude: + - name: state + operator: "matches" + value: "/charging|discharging/i" + - name: entity_id + operator: "matches" + value: "/samsung|pixel/i" + - name: state + operator: ">" + value: 5 + - name: attributes.friendly_name # require a friendly_name + operator: "=" + value: + bulk_rename: + - from: "Batterie" + - from: "Battery" + - from: "Power" + - type: conditional conditions: - entity: binary_sensor.openclose_10 @@ -50,4 +79,4 @@ cards: aspect_ratio: 60% entities: - device_tracker.sm_g985f - - device_tracker.pixel_4 \ No newline at end of file + - device_tracker.pixely \ No newline at end of file diff --git a/config/views/robots.yaml b/config/views/robots.yaml index acdb81f..92bc228 100644 --- a/config/views/robots.yaml +++ b/config/views/robots.yaml @@ -1,6 +1,5 @@ title: Roboter path: robots -badges: [] icon: 'mdi:robot-vacuum-variant' cards: # Dobby diff --git a/configuration.yaml b/configuration.yaml index 435b8b1..ef67da2 100644 --- a/configuration.yaml +++ b/configuration.yaml @@ -17,7 +17,7 @@ homeassistant: - 192.168.0.0/16 - fd00::/8 # landroid - #packages: !include_dir_named packages + packages: !include_dir_named packages config: conversation: @@ -58,6 +58,8 @@ lovelace: type: module - url: /local/lovelace/custom/now-playing-card/now-playing-card.js type: module + - url: /local/lovelace/custom/battery-state-card/battery-state-card.js + type: module tts: - platform: google_translate diff --git a/ui-lovelace.yaml b/ui-lovelace.yaml index 79fd880..1c9201d 100644 --- a/ui-lovelace.yaml +++ b/ui-lovelace.yaml @@ -8,7 +8,6 @@ views: - !include config/views/media.yaml - !include config/views/robots.yaml - !include config/views/humidity.yaml - - !include config/views/floorplan.yaml - !include config/views/instagram.yaml - !include config/views/devices.yaml - !include config/views/cctv.yaml diff --git a/www/lovelace/custom/battery-state-card/battery-state-card.js b/www/lovelace/custom/battery-state-card/battery-state-card.js new file mode 100644 index 0000000..dc3dbfa --- /dev/null +++ b/www/lovelace/custom/battery-state-card/battery-state-card.js @@ -0,0 +1,2 @@ +!function(){"use strict";var t=t||Object.getPrototypeOf(customElements.get("home-assistant-main"));const{html:e,css:i}=t.prototype;console.info("%c BATTERY-STATE-CARD %c 1.6.4","color: white; background: forestgreen; font-weight: 700;","color: forestgreen; background: white; font-weight: 700;");const n=(t,e="warn")=>{console[e]("[battery-state-card] "+t)},r=t=>(t=t.replace("#",""),{r:parseInt(t.substr(0,2),16),g:parseInt(t.substr(2,2),16),b:parseInt(t.substr(4,2),16)}),s=t=>!isNaN(Number(t)),o=t=>Array.isArray(t)?t:t?[t]:[],a=(t,e)=>{switch(typeof t){case"string":const i={};return i[e]=t,i;case"object":return Object.assign({},t)}return t},c=t=>t&&e`
${t}
`,l=(t,i)=>t&&e`
`,h=t=>e`
${l(t.icon,t.levelColor)}
${t.name} ${c(t.secondary_info)}
${t.level}${s(t.level)?e` %`:""}
`,d=(t,i)=>{return e`${t?(n=t,e`
${n}
`):""}
${i}
`;var n},u=i`.clickable{cursor:pointer}.truncate{white-space:nowrap;text-overflow:ellipsis;overflow:hidden}.entity-spacing{margin:8px 0}.entity-spacing:first-child{margin-top:0}.entity-spacing:last-child{margin-bottom:0}.entity-row{display:flex;align-items:center}.entity-row .name{flex:1;margin:0 6px}.entity-row .secondary{color:var(--secondary-text-color)}.entity-row .icon{flex:0 0 40px;border-radius:50%;text-align:center;line-height:40px;margin-right:10px}.expandWrapper>.toggler{cursor:pointer}.expandWrapper>.toggler>.name{flex:1}.expandWrapper>.toggler div.chevron{transform:rotate(-90deg);font-size:26px;height:40px;width:40px;display:flex;justify-content:center;align-items:center}.expandWrapper>.toggler .chevron,.expandWrapper>.toggler+div{transition:all .5s ease}.expandWrapper>.toggler.expanded .chevron{transform:rotate(-90deg) scaleX(-1)}.expandWrapper>.toggler+div{overflow:hidden}.expandWrapper>.toggler:not(.expanded)+div{max-height:0!important}`,g={"more-info":t=>{const e=new Event("hass-more-info",{composed:!0});e.detail={entityId:t.entity.entity},t.card.dispatchEvent(e)},navigate:t=>{if(!t.config.navigation_path)return void n("Missing 'navigation_path' for 'navigate' tap action");window.history.pushState(null,"",t.config.navigation_path);const e=new Event("location-changed",{composed:!0});e.detail={replace:!1},window.dispatchEvent(e)},"call-service":t=>{if(!t.config.service)return void n("Missing 'service' for 'call-service' tap action");const[e,i]=t.config.service.split(".",2),r=Object.assign({},t.config.service_data);f.hass.callService(e,i,r)},url:t=>{t.config.url_path?window.location.href=t.config.url_path:n("Missing 'url_path' for 'url' tap action")}};class f{static getAction(t){return t.config&&"none"!=t.config.action?e=>{e.stopPropagation(),t.config.action in g?g[t.config.action](t):n("Unknown tap action type: "+t.config.action)}:null}}class p{constructor(t,e){this.config=t,this.action=e,this._level="Unknown",this._charging=!1,this._secondary_info=null,this._is_hidden=!1,this.updated=!1,this.colorPattern=/^#[A-Fa-f0-9]{6}$/,this.stringValuePattern=/\b([0-9]{1,3})\s?%/,this._name=t.name||t.entity}get entity_id(){return this.config.entity}get data_required_for(){var t;return(null===(t=this.config.charging_state)||void 0===t?void 0:t.entity_id)?[this.config.entity,this.config.charging_state.entity_id]:[this.config.entity]}set name(t){this.updated=this.updated||this._name!=t,this._name=t}get name(){let t=this._name;return o(this.config.bulk_rename).forEach(e=>{t="/"==e.from[0]&&"/"==e.from[e.from.length-1]?t.replace(new RegExp(e.from.substr(1,e.from.length-2)),e.to||""):t.replace(e.from,e.to||"")}),t}set level(t){this.updated=this.updated||this._level!=t,this._level=t}get level(){return this._level}set charging(t){this.updated=this.updated||this.charging!=t,this._charging=t}get charging(){return this._charging}get is_hidden(){return this._is_hidden}set is_hidden(t){this.updated=this.updated||this._is_hidden!=t,this._is_hidden=t}get secondary_info(){return this._secondary_info}set secondary_info(t){this.updated=this.updated||this._secondary_info!=t,this._secondary_info=t}get levelColor(){var t,e;const i="inherit",n=Number(this._level);if(this.charging&&(null===(t=this.config.charging_state)||void 0===t?void 0:t.color))return this.config.charging_state.color;if(isNaN(n)||n>100||n<0)return i;if(this.config.color_gradient&&this.isColorGradientValid(this.config.color_gradient))return function(t,e){e/=100;const i=t.map((e,i)=>({pct:1/(t.length-1)*i,color:r(e)}));let n=1;for(n=1;nn<=t.value))||void 0===e?void 0:e.color)||i}get icon(){var t;const e=Number(this._level);if(this.charging&&(null===(t=this.config.charging_state)||void 0===t?void 0:t.icon))return this.config.charging_state.icon;if(this.config.icon)return this.config.icon;if(isNaN(e)||e>100||e<0)return"mdi:battery-unknown";const i=10*Math.round(e/10);switch(i){case 100:return this.charging?"mdi:battery-charging-100":"mdi:battery";case 0:return this.charging?"mdi:battery-charging-outline":"mdi:battery-outline";default:return(this.charging?"mdi:battery-charging-":"mdi:battery-")+i}}get classNames(){const t=[];return this.action&&t.push("clickable"),!s(this.level)&&t.push("non-numeric-state"),t.join(" ")}update(t){const e=t.states[this.config.entity];e?(this.updated=!1,this.name=this.config.name||e.attributes.friendly_name,this.level=this.getLevel(e,t),this.charging=this.getChargingState(t),this.secondary_info=this.setSecondaryInfo(t,e)):n("Entity not found: "+this.config.entity,"error")}getLevel(t,e){var i;const r=e.localize("state.default.unknown");let o;if(this.config.attribute)o=t.attributes[this.config.attribute],null==o&&(n(`Attribute "${this.config.attribute}" doesn't exist on "${this.config.entity}" entity`),o=r);else{const e=[t.attributes.battery_level,t.attributes.battery,t.state];o=(null===(i=e.find(t=>null!=t))||void 0===i?void 0:i.toString())||r}if(this.config.state_map){const t=this.config.state_map.find(t=>t.from===o);void 0===t?s(o)||n(`Missing option for '${o}' in 'state_map'`):o=t.to.toString()}if(!s(o)){const t=this.stringValuePattern.exec(o);null!=t&&(o=t[1])}return this.config.multiplier&&s(o)&&(o=(this.config.multiplier*Number(o)).toString()),o=void 0===this.config.value_override?o:this.config.value_override,s(o)||(o=o.charAt(0).toUpperCase()+o.slice(1)),o}getChargingState(t){const e=this.config.charging_state;if(!e)return!1;let i=this.level,r=t.states[this.config.entity];if(e.entity_id){if(r=t.states[e.entity_id],!r)return n(`'charging_state' entity id (${e.entity_id}) not found`),!1;i=r.state}const s=o(e.attribute);if(0!=s.length){const t=s.find(t=>null!=r.attributes[t.name]);return!!t&&(null==t.value||r.attributes[t.name]==t.value)}const a=o(e.state);return 0==a.length?!!i:a.some(t=>t==i)}setSecondaryInfo(t,e){var i;if(this.config.secondary_info){if("charging"==this.config.secondary_info)return this.charging?(null===(i=this.config.charging_state)||void 0===i?void 0:i.secondary_info_text)||"Charging":null;{const i=e[this.config.secondary_info]||e.attributes[this.config.secondary_info]||this.config.secondary_info;return isNaN(Date.parse(i))?i:((t,e)=>{let i=Date.parse(e);if(isNaN(i))return t.localize("ui.components.relative_time.never");i=Math.round((Date.now()-i)/1e3);let n="";return n=i<60?t.localize("ui.components.relative_time.past_duration.second","count",i):i<3600?t.localize("ui.components.relative_time.past_duration.minute","count",Math.round(i/60)):i<86400?t.localize("ui.components.relative_time.past_duration.hour","count",Math.round(i/3600)):i<604800?t.localize("ui.components.relative_time.past_duration.day","count",Math.round(i/86400)):t.localize("ui.components.relative_time.past_duration.week","count",Math.round(i/604800)),n})(t,i)}}return null}isColorGradientValid(t){if(!(t.length<2)){for(const e of t)if(!this.colorPattern.test(e))return n("Color '${color}' is not valid. Please provide valid HTML hex color in #XXXXXX format."),!1;return!0}n("Value for 'color_gradient' should be an array with at least 2 colors.")}}const v=(t,e,i)=>t.findIndex(t=>{var n,r;if(t.group_id&&!(null===(r=null===(n=i[t.group_id])||void 0===n?void 0:n.entity_id)||void 0===r?void 0:r.some(t=>e.entity_id==t)))return!1;if(t.entities&&!t.entities.some(t=>e.entity_id==t))return!1;const s=isNaN(Number(e.level))?0:Number(e.level);return s>=t.min&&s<=t.max});var m=t=>t.forEach(t=>{null==t.min&&(t.min=0),null!=t.max&&t.max{if((null==i?void 0:i.group_id)&&!t[i.group_id])throw new Error("Group not found: "+i.group_id);let n=null==i?void 0:i.name;!n&&(null==i?void 0:i.group_id)&&(n=t[i.group_id].friendly_name);let r=null==i?void 0:i.icon;return void 0===r&&(null==i?void 0:i.group_id)&&(r=t[i.group_id].icon),{name:n,icon:r,batteries:e,secondary_info:null==i?void 0:i.secondary_info}},_=(t,e)=>t=t.replace(/\{[a-z]+\}/g,t=>{switch(t){case"{min}":return e.batteries.reduce((t,e)=>t>Number(e.level)?Number(e.level):t,100).toString();case"{max}":return e.batteries.reduce((t,e)=>tt>Number(e.level)?Number(e.level):t,100).toString(),n=e.batteries.reduce((t,e)=>tvoid 0!==t,contains:(t,e)=>void 0!==t&&-1!=t.toString().indexOf(e.toString()),"=":(t,e)=>t==e,">":(t,e)=>Number(t)>e,"<":(t,e)=>Number(t)=":(t,e)=>Number(t)>=e,"<=":(t,e)=>Number(t)<=e,matches:(t,e)=>{if(void 0===t)return!1;let i;const n=(e=e.toString()).match(x);return n?i=new RegExp(n[1],n[2]):-1!=e.indexOf("*")&&(i=new RegExp("^"+e.replace(/\*/g,".*")+"$")),i?i.test(t.toString()):t===e}};class N{constructor(t){this.config=t}get is_permanent(){return"state"!=this.config.name}isValid(t,e){const i=this.getValue(t,e);return this.meetsExpectations(i)}getValue(t,e){if(this.config.name)return 0==this.config.name.indexOf("attributes.")?t.attributes[this.config.name.substr(11)]:"state"==this.config.name&&void 0!==e?e:t[this.config.name];n("Missing filter 'name' property")}meetsExpectations(t){let e=this.config.operator;if(!e)if(void 0===this.config.value)e="exists";else{const t=this.config.value.toString();e=-1!=t.indexOf("*")||"/"==t[0]&&"/"==t[t.length-1]?"matches":"="}const i=w[e];return i?i(t,this.config.value):(n(`Operator '${this.config.operator}' not supported. Supported operators: ${Object.keys(w).join(", ")}`),!1)}}class E{constructor(t,e){var i,n,r,s;this.config=t,this.cardNode=e,this.batteries=[],this.groupsToResolve=[],this.groupsData={},this.initialized=!1,this.include=null===(n=null===(i=t.filter)||void 0===i?void 0:i.include)||void 0===n?void 0:n.map(t=>new N(t)),this.exclude=null===(s=null===(r=t.filter)||void 0===r?void 0:r.exclude)||void 0===s?void 0:s.map(t=>new N(t)),this.include||(this.initialized=!1),this.processExplicitEntities()}update(t){let e=!1;return this.initialized||(this.initialized=!0,e=this.processGroups(t)||e,e=this.processIncludes(t)||e),e=this.updateBatteries(t)||e,e&&this.processExcludes(t),e}getBatteries(){return((t,e,i)=>{const n={batteries:[],groups:[]};if(!t)return n.batteries=e,n;if("number"==typeof t){let r=e.filter(t=>!t.is_hidden);n.batteries=r.slice(0,t),n.groups.push(y(i,r.slice(t)))}else m(t),e.forEach(e=>{const r=v(t,e,i);-1==r?n.batteries.push(e):(n.groups[r]=n.groups[r]||y(i,[],t[r]),n.groups[r].batteries.push(e))});return n.groups.forEach(t=>{t.name&&(t.name=_(t.name,t)),t.secondary_info&&(t.secondary_info=_(t.secondary_info,t))}),n})(this.config.collapse,this.batteries,this.groupsData)}createBattery(t){return b.filter(e=>null==t[e]).forEach(e=>t[e]=this.config[e]),new p(t,f.getAction({card:this.cardNode,config:a(t.tap_action||this.config.tap_action||null,"action"),entity:t}))}processExplicitEntities(){let t=this.config.entity?[this.config]:(this.config.entities||[]).map(t=>("string"==typeof t&&(t={entity:t}),t));t=t.filter(t=>{if(!t.entity)throw new Error("Invalid configuration - missing property 'entity' on:\n"+JSON.stringify(t));return!t.entity.startsWith("group.")||(this.groupsToResolve.push(t.entity),!1)}),this.config.collapse&&Array.isArray(this.config.collapse)&&this.config.collapse.forEach(e=>{e.group_id?-1==this.groupsToResolve.indexOf(e.group_id)&&this.groupsToResolve.push(e.group_id):e.entities&&e.entities.forEach(e=>{t.some(t=>t.entity==e)||t.push({entity:e})})}),this.batteries=t.map(t=>this.createBattery(t))}processIncludes(t){let e=!1;return this.include?(Object.keys(t.states).forEach(i=>{var n;(null===(n=this.include)||void 0===n?void 0:n.some(e=>e.isValid(t.states[i])))&&!this.batteries.some(t=>t.entity_id==i)&&(e=!0,this.batteries.push(this.createBattery({entity:i})))}),e):e}processGroups(t){let e=!1;return this.groupsToResolve.forEach(i=>{const r=t.states[i];if(!r)return void n(`Group "${i}" not found`);const s=r.attributes;Array.isArray(s.entity_id)?(s.entity_id.forEach(t=>{this.batteries.some(e=>e.entity_id==t)||(e=!0,this.batteries.push(this.createBattery({entity:t})))}),this.groupsData[i]=s):n(`Entities not found in "${i}"`)}),this.groupsToResolve=[],e}processExcludes(t){if(null==this.exclude)return;const e=this.exclude,i=[];this.batteries.forEach((n,r)=>{let s=!1;for(let o of e)o.isValid(t.states[n.entity_id],n.level)&&(o.is_permanent?i.push(r):s=!0);n.is_hidden=s}),i.reverse().forEach(t=>this.batteries.splice(t,1))}updateBatteries(t){let e=!1;if(this.batteries.forEach((i,n)=>{i.update(t),e=e||i.updated}),e){switch(this.config.sort_by_level){case"asc":this.batteries.sort((t,e)=>this.sort(t.level,e.level));break;case"desc":this.batteries.sort((t,e)=>this.sort(e.level,t.level));break;default:this.config.sort_by_level&&n("Unknown sort option. Allowed values: 'asc', 'desc'")}this.batteries=[...this.batteries]}return e}sort(t,e){let i=Number(t),n=Number(e);return i=isNaN(i)?-1:i,n=isNaN(n)?-1:n,i-n}}customElements.define("battery-state-card",class extends t{constructor(){super(...arguments),this.rawConfig="",this.config={},this.simpleView=!1,this.batteryProvider=null,this.cssStyles="",this.triggerRender=function(t,e){let i;return(...e)=>{i&&(clearTimeout(i),i=null),i=setTimeout(()=>t.apply(null,e),100)}}(()=>this.requestUpdate())}static get styles(){return u}setConfig(t){var e;if(!(t.entities||t.entity||(null===(e=t.filter)||void 0===e?void 0:e.include)||Array.isArray(t.collapse)))throw new Error("You need to define entities, filter.include or collapse.group");const i=JSON.stringify(t);this.rawConfig!==i&&(this.rawConfig=i,this.config=JSON.parse(i),this.simpleView=!!this.config.entity,this.batteryProvider=new E(this.config,this),this.triggerRender())}set hass(t){f.hass=t;this.batteryProvider.update(t)&&this.triggerRender()}render(){const t=this.batteryProvider.getBatteries();if(this.simpleView)return h(t.batteries[0]);let i=[];return t.batteries.forEach(t=>!t.is_hidden&&i.push(h(t))),t.groups.forEach(t=>{const n=[];var r,s;t.batteries.forEach(t=>!t.is_hidden&&n.push(h(t))),n.length&&i.push((r=n,s=t,Math.random().toString().substr(2),e`
${l(s.icon,s.iconColor)}
${s.name} ${c(s.secondary_info)}
${r}
`))}),0==i.length?e``:d(this.config.name||this.config.title,i)}updated(){var t;if(!(null===(t=this.config)||void 0===t?void 0:t.style)||this.cssStyles==this.config.style)return;this.cssStyles=this.config.style;let e=this.shadowRoot.querySelector("style");e||(e=document.createElement("style"),e.type="text/css",this.shadowRoot.appendChild(e)),e.innerHTML=((t,e)=>e.replace(/([^\r\n,{}]+)(,(?=[^}]*{)|\s*{)/g,e=>`${t} ${e}`))("ha-card",this.cssStyles)}getCardSize(){var t;let e=(null===(t=this.config.entities)||void 0===t?void 0:t.length)||1;return this.config.collapse?"number"==typeof this.config.collapse?this.config.collapse+1:this.config.collapse.length+1:e+1}})}(); +//# sourceMappingURL=battery-state-card.js.map diff --git a/www/vacuums/Dobby_liveMap.png b/www/vacuums/Dobby_liveMap.png deleted file mode 100644 index bbf50e12800954a51abf39a0077ea9c54f89a302..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14808 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