├── README.md ├── hacs.json ├── power-usage-card-regex.js └── power-usage-card-regex.png /README.md: -------------------------------------------------------------------------------- 1 | [![hacs_badge](https://img.shields.io/badge/HACS-Default-orange.svg?style=for-the-badge)](https://github.com/custom-components/hacs) 2 | # Lovelace power usage graph card with regexp 3 | 4 | This card will display a doughnut chard that gives insights in your current power usage. Unlike the original one, you don't need to enumerate all the devices you have - you just need to define a regular expression matching all the entities. 5 | 6 | Note: the card is not collecting any data, it is only displaying momentary values. If your devices report "power" (in Watt) and not "power usage" (kWh, sometimes Ws), then you should consider using "integration" (https://www.home-assistant.io/integrations/integration/) or "utility meter" (https://www.home-assistant.io/integrations/utility_meter/) components as input sources for this card. 7 | 8 | The card will suppress displaying entities which do not have a numeric state (normally "unavailable", on devices which are really unavailable or just not initialized yet). 9 | 10 | ## Usage 11 | 1. 12 | - HACS: add through HACS. You will likely need to have HACS include this plugin into the Lovelace ressources and to reload the page after it 13 | - manual: 14 | Add plugin .js as a module: 15 | ``` 16 | - url: /local/power-usage-card-regex.js 17 | type: module 18 | ``` 19 | 2. Add lovelace card to view: 20 | ``` 21 | - type: "custom:power-usage-card-regex" # Mandatory 22 | title: "Power consumption" # Optional customized title 23 | total_power_usage: sensor.power_consumption # Optional total power consumption (DSMR) sensor. 24 | # If available then other measured values will be 25 | # substracted from total to calculate 'unknown' value. 26 | unknownText: "Total" # Optional customized unknown text. Only applicable 27 | # with total_power_usage option enabled. 28 | filter: "^.*_total$" # Mandatory - regular expression to match; 29 | # all entities matching this RE will be treated 30 | # as included, if they have a numeric state; 31 | # their name will be taken from "friendly_name" attribute, 32 | # so set them wisely 33 | ``` 34 | 35 | ![screenshot](https://raw.githubusercontent.com/DBa2016/power-usage-card-regex/master/power-usage-card-regex.png) 36 | -------------------------------------------------------------------------------- /hacs.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Power Usage Card with Regular Expressions", 3 | "content_in_root": true, 4 | "filename": "power-usage-card-regex.js", 5 | "render_readme": true 6 | } 7 | -------------------------------------------------------------------------------- /power-usage-card-regex.js: -------------------------------------------------------------------------------- 1 | import "https://unpkg.com/chart.js@2.7.1/dist/Chart.bundle.min.js?module"; 2 | import "https://cdn.jsdelivr.net/npm/chartjs-plugin-colorschemes"; 3 | 4 | class PowerUsageCardRE extends HTMLElement { 5 | constructor() { 6 | super(); 7 | this.attachShadow({ mode: 'open' }); 8 | } 9 | 10 | setConfig(config) { 11 | if (!config.filter) { 12 | throw new Error('You need to define a filter (regular expression!)'); 13 | } 14 | const root = this.shadowRoot; 15 | if (root.lastChild) root.removeChild(root.lastChild); 16 | 17 | const card = document.createElement('ha-card'); 18 | const content = document.createElement('div'); 19 | const canvas = document.createElement('canvas'); 20 | const ctx = canvas.getContext('2d'); 21 | const style = document.createElement('style'); 22 | 23 | card.id ='ha-card'; 24 | content.id = 'content'; 25 | canvas.id = 'cnv'; 26 | content.style.height = '480px'; 27 | canvas.height=480; 28 | card.appendChild(content); 29 | card.appendChild(style); 30 | content.appendChild(canvas); 31 | root.appendChild(card); 32 | this._config = config; 33 | } 34 | 35 | set hass(hass) { 36 | const root = this.shadowRoot; 37 | const config = this._config; 38 | const card = root.getElementById("ha-card"); 39 | const content = root.getElementById("content"); 40 | const canvas = root.getElementById("cnv"); 41 | const ctx = canvas.getContext('2d'); 42 | // have to enumerate hass.states. Sigh. I want python list comprhension here! 43 | var entlist = []; 44 | for(var i in hass.states) { 45 | if(hass.states[i].entity_id.match(config.filter) && typeof(hass.states[i].state) !== "undefined" && hass.states[i].state.match(/^[0-9\.]+$/)) 46 | entlist.push(i); 47 | } 48 | const hassEntities = entlist.map(y => hass.states[y]); 49 | var entityNames = hassEntities.map(x => x.attributes.friendly_name); 50 | var entityData = hassEntities.map(x => x.state); 51 | card.header = config.title ? config.title : 'Power usage graph'; 52 | 53 | if (config.total_power_usage){ 54 | const totalEntity = hass.states[config.total_power_usage] 55 | const total = (totalEntity.attributes.unit_of_measurement == 'kW') ? totalEntity.state * 1000 : totalEntity.state; 56 | const measured = hassEntities.map(x => Number(x.state)).reduce(( accumulator, currentValue ) => accumulator + currentValue, 0); 57 | entityData.push(total - measured) 58 | entityNames.push(config.unknownText ? config.unknownText : 'Unknown'); 59 | } 60 | 61 | const emptyIndexes = entityData.reduce((arr, e, i) => ((e == 0) && arr.push(i), arr), []) 62 | entityData = entityData.filter((element, index, array) => !emptyIndexes.includes(index)); 63 | entityNames = entityNames.filter((element, index, array) => !emptyIndexes.includes(index)); 64 | 65 | const doughnutChart = new Chart(ctx, { 66 | type: 'doughnut', 67 | data: { 68 | labels: [], 69 | datasets: [{ 70 | data: [], 71 | borderWidth: 1, 72 | borderColor:'#00c0ef', 73 | label: 'liveCount', 74 | }] 75 | }, 76 | options: { 77 | responsive: true, 78 | maintainAspectRatio: true, 79 | animation: { duration: 0 }, 80 | legend: { 81 | position: 'bottom', 82 | display: true 83 | }, 84 | hover: { mode: 'index' }, 85 | plugins: {colorschemes: { scheme: 'brewer.Paired12' } } 86 | } 87 | }); 88 | 89 | var getData = function() { 90 | doughnutChart.data = { datasets: [{ data: entityData }], labels: entityNames }; 91 | doughnutChart.update(); 92 | }; 93 | getData(); 94 | } 95 | 96 | getCardSize() { 97 | return 3; 98 | } 99 | } 100 | 101 | customElements.define('power-usage-card-regex', PowerUsageCardRE); 102 | -------------------------------------------------------------------------------- /power-usage-card-regex.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DBa2016/power-usage-card-regex/cf6d4cd319a817ffd4febe9a92d2e41381b01998/power-usage-card-regex.png --------------------------------------------------------------------------------