From cf3e3e579bcaa2501a39d3d4dc35ee28899923c9 Mon Sep 17 00:00:00 2001 From: Gerben ten Hove Date: Mon, 17 Dec 2018 22:06:04 +0100 Subject: [PATCH 1/7] Rewrite --- custom_updater.json | 4 +- power-wheel-card/CHANGELOG.md | 3 + power-wheel-card/power-wheel-card.js | 107 ++++++++++++++++----------- 3 files changed, 67 insertions(+), 47 deletions(-) diff --git a/custom_updater.json b/custom_updater.json index d4b070e..1d15c61 100644 --- a/custom_updater.json +++ b/custom_updater.json @@ -1,7 +1,7 @@ { "power-wheel-card": { - "updated_at": "2018-12-13", - "version": "0.0.3", + "updated_at": "2018-12-17", + "version": "0.0.4", "remote_location": "https://raw.githubusercontent.com/gurbyz/custom-cards-lovelace/master/power-wheel-card/power-wheel-card.js", "visit_repo": "https://github.com/gurbyz/custom-cards-lovelace/tree/master/power-wheel-card", "changelog": "https://github.com/gurbyz/custom-cards-lovelace/blob/master/power-wheel-card/CHANGELOG.md" diff --git a/power-wheel-card/CHANGELOG.md b/power-wheel-card/CHANGELOG.md index 9168fef..3bf858a 100644 --- a/power-wheel-card/CHANGELOG.md +++ b/power-wheel-card/CHANGELOG.md @@ -1,5 +1,8 @@ Changelog ==== +## 0.0.4 +### Improvements +* Rewrite of the code. ## 0.0.3 ### New features diff --git a/power-wheel-card/power-wheel-card.js b/power-wheel-card/power-wheel-card.js index c340d21..0164ec6 100644 --- a/power-wheel-card/power-wheel-card.js +++ b/power-wheel-card/power-wheel-card.js @@ -8,53 +8,70 @@ class PowerWheelCard extends LitElement { hass: Object, config: Object, } - } + }; + + _generateClass(producingIsPositive, value) { + if (producingIsPositive) { + return value > 0 ? 'producing' : ((value < 0) ? 'consuming' : 'inactive'); + } else { + return value > 0 ? 'consuming' : ((value < 0) ? 'producing' : 'inactive'); + } + }; + + _makePositionObject(hass, entity, configIcon, defaultIcon, producingIsPositive) { + const stateObj = hass.states[entity]; + const stateStr = stateObj ? parseFloat(stateObj.state).toFixed(this.decimals) : 'unavailable'; + const icon = configIcon ? configIcon : (stateObj && stateObj.attributes.icon ? stateObj.attributes.icon : defaultIcon); + const classValue = this._generateClass(producingIsPositive, stateObj && parseFloat(stateObj.state)); + + return { + stateObj, + stateStr, + icon, + classValue + }; + }; _render({ hass, config }) { - const solarPowerState = hass.states[config.solar_power_entity]; - const solarPowerStateStr = solarPowerState ? parseFloat(solarPowerState.state).toFixed(this.decimals) : 'unavailable'; - const solarPowerIcon = config.solar_power_icon ? config.solar_power_icon - : (solarPowerState && solarPowerState.attributes.icon ? solarPowerState.attributes.icon : 'mdi:weather-sunny'); - const solarPowerClass = (solarPowerState && parseFloat(solarPowerState.state) > 0) ? 'producing' : 'inactive'; + const data = { + solar: {}, + grid: {}, + home: {} + }; - const gridPowerState = hass.states[config.grid_power_entity]; - const gridPowerStateStr = gridPowerState ? parseFloat(gridPowerState.state).toFixed(this.decimals) : 'unavailable'; - const gridPowerIcon = config.grid_power_icon ? config.grid_power_icon - : (gridPowerState && gridPowerState.attributes.icon ? gridPowerState.attributes.icon : 'mdi:flash-circle'); - const gridPowerClass = (gridPowerState && parseFloat(gridPowerState.state) > 0) - ? 'consuming' : ((gridPowerState && parseFloat(gridPowerState.state) < 0) ? 'producing' : 'inactive'); + data.solar = this._makePositionObject(hass, config.solar_power_entity, config.solar_power_icon, 'mdi:weather-sunny', true); + data.grid = this._makePositionObject(hass, config.grid_power_entity, config.grid_power_icon, 'mdi:flash-circle', false); - let homePowerState, - homePowerStateStr, - homePowerClass, - homePowerIcon; if (config.home_power_entity) { // home power value by sensor - homePowerState = hass.states[config.home_power_entity]; - homePowerStateStr = homePowerState ? parseFloat(homePowerState.state).toFixed(this.decimals) : 'unavailable'; - homePowerClass = (homePowerState && parseFloat(homePowerState.state) > 0) ? 'consuming' : 'inactive'; - homePowerIcon = config.home_power_icon ? config.home_power_icon - : (homePowerState && homePowerState.attributes.icon ? homePowerState.attributes.icon : 'mdi:home'); + data.home = this._makePositionObject(hass, config.home_power_entity, config.home_power_icon, 'mdi:home', false); } else { // home power value by calculation - if (solarPowerState && gridPowerState) { - homePowerStateStr = (parseFloat(solarPowerState.state) + parseFloat(gridPowerState.state)).toFixed(this.decimals); - homePowerClass = parseFloat(solarPowerState.state) + parseFloat(gridPowerState.state) > 0 ? 'consuming' : 'inactive'; + if (data.solar.stateObj && data.grid.stateObj) { + data.home = { + stateObj: {}, + stateStr: (parseFloat(data.solar.stateObj.state) + parseFloat(data.grid.stateObj.state)).toFixed(this.decimals), + icon: config.home_power_icon ? config.home_power_icon : 'mdi:home', + classValue: this._generateClass(false, parseFloat(data.solar.stateObj.state) + parseFloat(data.grid.stateObj.state)), + }; } else { - homePowerStateStr = 'unavailable'; - homePowerClass = 'inactive'; + data.home = { + stateObj: {}, + stateStr: 'unavailable', + icon: config.home_power_icon ? config.home_power_icon : 'mdi:home', + classValue: 'inactive' + }; } - homePowerIcon = config.home_power_icon ? config.home_power_icon : 'mdi:home'; } - const unitStr = solarPowerState && gridPowerState - && solarPowerState.attributes.unit_of_measurement && gridPowerState.attributes.unit_of_measurement - && solarPowerState.attributes.unit_of_measurement == gridPowerState.attributes.unit_of_measurement - ? solarPowerState.attributes.unit_of_measurement : 'unknown unit'; + const unitStr = data.solar.stateObj && data.grid.stateObj + && data.solar.stateObj.attributes.unit_of_measurement && data.grid.stateObj.attributes.unit_of_measurement + && data.solar.stateObj.attributes.unit_of_measurement == data.grid.stateObj.attributes.unit_of_measurement + ? data.solar.stateObj.attributes.unit_of_measurement : 'unknown unit'; - const solar2gridClass = gridPowerState && parseFloat(gridPowerState.state) < 0 ? 'active' : 'inactive'; - const solar2homeClass = solarPowerState && parseFloat(solarPowerState.state) > 0 - && gridPowerState && parseFloat(homePowerStateStr) != 0 ? 'active' : 'inactive'; - const grid2homeClass = gridPowerState && parseFloat(gridPowerState.state) > 0 - && solarPowerState && parseFloat(homePowerStateStr) != 0 ? 'active' : 'inactive'; + const solar2gridClass = data.grid.stateObj && parseFloat(data.grid.stateObj.state) < 0 ? 'active' : 'inactive'; + const solar2homeClass = data.solar.stateObj && parseFloat(data.solar.stateObj.state) > 0 + && data.grid.stateObj && parseFloat(data.home.stateStr) != 0 ? 'active' : 'inactive'; + const grid2homeClass = data.grid.stateObj && parseFloat(data.grid.stateObj.state) > 0 + && data.solar.stateObj && parseFloat(data.home.stateStr) != 0 ? 'active' : 'inactive'; return html` + ${this.energy_capable ? html`` : ''}
${this.title}
@@ -161,6 +199,7 @@ class PowerWheelCard extends LitElement { `; }; + _arrowCell(arrowObj) { return html`
@@ -168,6 +207,7 @@ class PowerWheelCard extends LitElement {
`; }; + _handleClick(ev, stateObj) { if (!stateObj) { return; @@ -181,6 +221,14 @@ class PowerWheelCard extends LitElement { this.shadowRoot.dispatchEvent(event); } + _toggleView(ev) { + if (this.view === 'power') { + this.view = 'energy'; + } else { + this.view = 'power'; + } + } + setConfig(config) { if (!config.solar_power_entity) { throw new Error('You need to define a solar_power_entity'); @@ -189,10 +237,14 @@ class PowerWheelCard extends LitElement { throw new Error('You need to define a grid_power_entity'); } this.title = config.title ? config.title : 'Power wheel'; - if (config.decimals && !Number.isInteger(config.decimals)) { - throw new Error('Decimals should be an integer'); + if (config.power_decimals && !Number.isInteger(config.power_decimals)) { + throw new Error('Power_decimals should be an integer'); + } + this.power_decimals = config.power_decimals ? config.power_decimals : 0; + if (config.energy_decimals && !Number.isInteger(config.energy_decimals)) { + throw new Error('Energy_decimals should be an integer'); } - this.decimals = config.decimals ? config.decimals : 0; + this.energy_decimals = config.energy_decimals ? config.energy_decimals : 3; this.colorPowerIcons = config.color_power_icons ? (config.color_power_icons == true) : false; this.consumingColor = this.colorPowerIcons ? (config.consuming_color ? config.consuming_color : 'var(--label-badge-yellow, #f4b400)') @@ -200,6 +252,11 @@ class PowerWheelCard extends LitElement { this.producingColor = this.colorPowerIcons ? (config.producing_color ? config.producing_color : 'var(--label-badge-green, #0da035)') : 'var(--state-icon-unavailable-color, #bdbdbd)'; + if (config.initial_view && !['power', 'energy'].includes(config.initial_view)) { + throw new Error("Initial_view should 'power' or 'energy'"); + } + this.view = config.initial_view ? config.initial_view : 'power'; + this.energy_capable = config.solar_energy_entity && config.grid_energy_entity; this.config = config; } From 4961c105e33c19df089cf73dae76917a1a7e154d Mon Sep 17 00:00:00 2001 From: Gerben ten Hove Date: Fri, 21 Dec 2018 23:16:32 +0100 Subject: [PATCH 5/7] Remember view across lovelace view changes. --- power-wheel-card/power-wheel-card.js | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/power-wheel-card/power-wheel-card.js b/power-wheel-card/power-wheel-card.js index 3827a05..af5436e 100644 --- a/power-wheel-card/power-wheel-card.js +++ b/power-wheel-card/power-wheel-card.js @@ -68,7 +68,7 @@ class PowerWheelCard extends LitElement { }; let arrowData = {}; - if (this.view === 'energy' && this.energy_capable) { + if (config.view === 'energy' && this.energy_capable) { data.solar = this._makePositionObject(hass, config.solar_energy_entity, config.solar_power_icon, 'mdi:weather-sunny', this.energy_decimals, true); data.grid = this._makePositionObject(hass, config.grid_energy_entity, config.grid_power_icon, @@ -171,7 +171,7 @@ class PowerWheelCard extends LitElement { } - ${this.energy_capable ? html`` : ''} + ${this.energy_capable ? html`` : ''}
${this.title}
@@ -204,6 +204,7 @@ class PowerWheelCard extends LitElement { return html`
+
 ${arrowObj.valueStr} 
`; }; @@ -221,11 +222,11 @@ class PowerWheelCard extends LitElement { this.shadowRoot.dispatchEvent(event); } - _toggleView(ev) { - if (this.view === 'power') { - this.view = 'energy'; + _toggleView(ev, config) { + if (config.view === 'power') { + config.view = 'energy'; } else { - this.view = 'power'; + config.view = 'power'; } } @@ -255,7 +256,9 @@ class PowerWheelCard extends LitElement { if (config.initial_view && !['power', 'energy'].includes(config.initial_view)) { throw new Error("Initial_view should 'power' or 'energy'"); } - this.view = config.initial_view ? config.initial_view : 'power'; + if (!config.view) { + config.view = config.initial_view ? config.initial_view : 'power'; + } this.energy_capable = config.solar_energy_entity && config.grid_energy_entity; this.config = config; } From 6a25eaacbaa361416bfe3b1a84910557645b1b50 Mon Sep 17 00:00:00 2001 From: Gerben ten Hove Date: Sat, 22 Dec 2018 11:58:51 +0100 Subject: [PATCH 6/7] Renamed a few card parameters. Padding. Readme. --- custom_updater.json | 2 +- power-wheel-card/CHANGELOG.md | 9 ++++- power-wheel-card/README.md | 53 ++++++++++++++++++---------- power-wheel-card/power-wheel-card.js | 19 +++++----- 4 files changed, 53 insertions(+), 30 deletions(-) diff --git a/custom_updater.json b/custom_updater.json index 1d15c61..0bb985e 100644 --- a/custom_updater.json +++ b/custom_updater.json @@ -1,6 +1,6 @@ { "power-wheel-card": { - "updated_at": "2018-12-17", + "updated_at": "2018-12-22", "version": "0.0.4", "remote_location": "https://raw.githubusercontent.com/gurbyz/custom-cards-lovelace/master/power-wheel-card/power-wheel-card.js", "visit_repo": "https://github.com/gurbyz/custom-cards-lovelace/tree/master/power-wheel-card", diff --git a/power-wheel-card/CHANGELOG.md b/power-wheel-card/CHANGELOG.md index 5c832ef..9a1ce74 100644 --- a/power-wheel-card/CHANGELOG.md +++ b/power-wheel-card/CHANGELOG.md @@ -7,9 +7,16 @@ If you want to use the *energy view* the first two are required. The third one for *home energy* will be calculated if omitted. * A button to toggle between *power view* and the new *energy view*. The button is visible only when the *energy view* is available. * A card parameter `initial_view` to set the initial view to *power view* or *energy view*. Valid values are `"power"` (default) and `"energy"`. -* **BREAKING** Split up of card parameter `decimals` into `power_decimals` and `energy_decimals`. If you used `decimals` before, just rename it to `power_decimals`. +* A card parameter `energy_decimals` to set the number of decimals used in the *energy view*. ### Improvements * Rewrite of the code. +* Preparations in the code for showing values near arrows in a future release. +* A slightly better positioning of the icons. +* **BREAKING.** Renamed card parameter `decimals` to `power_decimals`. +* **BREAKING.** Renamed card parameter `solar_power_icon` to `solar_icon`. +* **BREAKING.** Renamed card parameter `grid_power_icon` to `grid_icon`. +* **BREAKING.** Renamed card parameter `home_power_icon` to `home_icon`. +* **BREAKING.** Renamed card parameter `color_power_icons` to `color_icons`. ## 0.0.3 ### New features diff --git a/power-wheel-card/README.md b/power-wheel-card/README.md index 517ba61..7b80237 100644 --- a/power-wheel-card/README.md +++ b/power-wheel-card/README.md @@ -1,19 +1,22 @@ power-wheel-card ==== -An intuïtive way to represent the power that your home is consuming or producing. +An intuïtive way to represent the power and energy that your home is consuming or producing. > This component is discussed [here](https://community.home-assistant.io/t/lovelace-power-wheel-card/82374) on the Home Assistant forum. ## Features Features of the custom power-wheel-card: -* Displays the three power values (solar, grid and home) in 'a wheel'. +* Displays the three values (solar, grid and home) in 'a wheel'. +* Has different views for power values and energy values. The initial view can be set. There is a toggle button to switch between views. * Optionally calculates the current power that your home is consuming: home power. Input for the calculation is the (produced) solar power and the (consumed or produced) grid power. -* Displays the transition between these powers as arrows. +* Optionally calculates the energy that your home is consuming: home energy. + Input for the calculation is the (produced) solar energy and the (consumed or produced) grid energy. +* Displays the transition between these powers as arrows. (In *power view* only.) E.g. if your solar power panels produce power, the arrow from solar to home turns active. And if your solar power panels produce enough power to deliver some back to the grid, the arrow from solar to grid turns active. * Optionally uses icons of your own choice, which can be set by card parameters or taken from your `customize:` sensor settings. -* Optionally colors the consuming power icons yellow and the producing power icons green. You can choose your own colors for consuming and producing. +* Optionally colors the consuming icons yellow and the producing icons green. You can choose your own colors for consuming and producing. * Works for default theme and custom themes that use [standard CSS vars](https://github.com/home-assistant/home-assistant-polymer/blob/master/src/resources/ha-style.js). * Has support for [custom_updater](https://github.com/custom-components/custom_updater) custom component to check for new release via the custom tracker-card. @@ -25,13 +28,13 @@ Features of the custom power-wheel-card: - This sensor has a `unit_of_measurement` set up, e.g. `'W'` or `'kW'`. - The sensor value should be of type *int* or *float*. - The sensor value should be positive. - - The sensor could have an icon (optional) that will override the icon in the power-wheel-card if the card parameter `solar_power_icon` is not used. + - The sensor could have an icon (optional) that will override the icon in the power-wheel-card if the card parameter `solar_icon` is not used. 1. You need to have a working sensor for your grid power. Write down the entity id of this sensor. This is *YOUR_GRID_POWER_SENSOR* in the instructions below. - This sensor has **the same** `unit_of_measurement` set up as the sensor for solar power. - Preferably this sensor has the same update interval as the sensor for solar power. (If not, the calculated value for home power can give unreal results sometimes.) - The sensor value should be of type *int* or *float*. - The sensor value should be **negative** for **producing** power to the grid and **positive** for **consuming** power of the grid. - - The sensor could have an icon (optional) that will override the icon in the power-wheel-card if the card parameter `grid_power_icon` is not used. + - The sensor could have an icon (optional) that will override the icon in the power-wheel-card if the card parameter `grid_icon` is not used. Nb. You don't need a sensor for your home power, but you can use if you have it available. The value will be calculated if your don't supply this sensor as card parameter. @@ -64,6 +67,9 @@ For the grid power I used a [dsmr sensor](https://www.home-assistant.io/componen Because the dsmr sensor supplies 2 separate sensors for grid power consumption and grid power production, I had to combine them into one grid power sensor. And because my solar power sensor and dsmr sensor don't report in the same unit of measurement, I had to convert that as well. +> **Tip.** If you are creating extra sensors for the power-wheel-card, maybe you want to exclude them in your `recorder:` setting. +Extra sensors based on your heavily updating DSMR sensors will let your database grow fast. + ## Instructions 1. Check the requirements above. If you don't comply to the requirements, the card won't be much of use for you or just won't work. 1. Download the file [power-wheel-card.js](https://raw.githubusercontent.com/gurbyz/custom-cards-lovelace/master/power-wheel-card/power-wheel-card.js). @@ -87,7 +93,7 @@ views: - type: "custom:power-wheel-card" solar_power_entity: sensor.YOUR_SOLAR_POWER_SENSOR grid_power_entity: sensor.YOUR_GRID_POWER_SENSOR - color_power_icons: true + color_icons: true ``` ## Parameters @@ -97,15 +103,20 @@ views: |type|string|**required**||Type of the card. Use `"custom:power-wheel-card"`.| |title|string|optional|`"Power wheel"`|Title of the card.| |solar_power_entity|string|**required**||Entity id of your solar power sensor. E.g. `sensor.YOUR_SOLAR_POWER_SENSOR`. See requirements above.| -|solar_power_icon|string|optional|The icon of your own customized solar power sensor. If not available, then `"mdi:weather-sunny"` will be used.|Icon for solar power.| |grid_power_entity|string|**required**||Entity id of your grid power sensor. E.g. `sensor.YOUR_GRID_POWER_SENSOR`. See requirements above.| -|grid_power_icon|string|optional|The icon of your own customized grid power sensor. If not available, then `"mdi:flash-circle"` will be used.|Icon for grid power.| |home_power_entity|string|optional|Default the home power value will be calculated.|Entity id of your home power sensor.| -|home_power_icon|string|optional|The icon of your own customized home power sensor if `home_power_entity` is set. If not available, then `"mdi:home"` will be used.|Icon for home power.| -|decimals|integer|optional|`0`|Number of decimals for the power values.| -|color_power_icons|boolean|optional|`false`|To color the consuming power icons green and the producing power icons yellow.| -|consuming_color|string|optional|The yellow color for `--label-badge-yellow` from your theme. If not available, then `"#f4b400"` will be used.|CSS color code for consuming power icons if `color_power_icons` is set to `true`. Examples: `"orange"`, `"#ffcc66"` or `"rgb(200,100,50)"`. Don't forget the quotation marks when using the `#` color notation.| -|producing_color|string|optional|The green color for `--label-badge-green` from your theme. If not available, then `"#0da035"` will be used.|CSS color code for producing power icons if `color_power_icons` is set to `true`.| +|solar_energy_entity|string|optional|Default the *energy view* will not be enabled.|Entity id of your solar energy sensor. E.g. `sensor.YOUR_SOLAR_ENERGY_SENSOR`. See requirements above.| +|grid_energy_entity|string|optional|Default the *energy view* will not be enabled.|Entity id of your grid energy sensor. E.g. `sensor.YOUR_GRID_ENERGY_SENSOR`. See requirements above.| +|home_energy_entity|string|optional|Default the *energy view* will not be enabled. If `solar_energy_entity` and `grid_energy_entity` are set, then default the home energy value will be calculated.|Entity id of your home energy sensor.| +|solar_icon|string|optional|The icon of your own customized solar sensor(s). If not available, then `"mdi:weather-sunny"` will be used.|Icon for solar power and energy.| +|grid_icon|string|optional|The icon of your own customized grid sensor(s). If not available, then `"mdi:flash-circle"` will be used.|Icon for grid power and energy.| +|home_icon|string|optional|The icon of your own customized home sensor(s) if its entity parameter is set. If not available, then `"mdi:home"` will be used.|Icon for home power and energy.| +|power_decimals|integer|optional|`0`|Number of decimals for the power values.| +|energy_decimals|integer|optional|`3`|Number of decimals for the energy values.| +|color_icons|boolean|optional|`false`|To color the consuming icons green and the producing icons yellow. This setting only is affecting the three big icons for *solar*, *home* and *grid*. The arrows have colors by default.| +|consuming_color|string|optional|The yellow color for `--label-badge-yellow` from your theme. If not available, then `"#f4b400"` will be used.|CSS color code for consuming power icons if `color_icons` is set to `true`. Examples: `"orange"`, `"#ffcc66"` or `"rgb(200,100,50)"`. Don't forget the quotation marks when using the `#` color notation.| +|producing_color|string|optional|The green color for `--label-badge-green` from your theme. If not available, then `"#0da035"` will be used.|CSS color code for producing power icons if `color_icons` is set to `true`.| +|initial_view|string|optional|`"power"`|The initial view that will displayed. Allowed values are `"power"` for *power view* and `"energy"` for *energy view*.| ### More about icons The icons for solar power and grid power can be set by card parameters as shown in the table above. @@ -130,14 +141,18 @@ A more advanced example for in the `ui-lovelace.yaml` file: - type: "custom:power-wheel-card" title: "Power distribution" solar_power_entity: sensor.YOUR_SOLAR_POWER_SENSOR - solar_power_icon: "mdi:white-balance-sunny" grid_power_entity: sensor.YOUR_GRID_POWER_SENSOR - grid_power_icon: "mdi:flash" - home_power_icon: "mdi:home-assistant" - decimals: 2 - color_power_icons: true + solar_energy_entity: sensor.YOUR_SOLAR_ENERGY_SENSOR + grid_energy_entity: sensor.YOUR_GRID_ENERGY_SENSOR + solar_icon: "mdi:white-balance-sunny" + grid_icon: "mdi:flash" + home_icon: "mdi:home-assistant" + power_decimals: 2 + energy_decimals: 2 + color_icons: true consuming_color: "#33ff33" producing_color: "#dd5500" + initial_view: "energy" ``` ## License diff --git a/power-wheel-card/power-wheel-card.js b/power-wheel-card/power-wheel-card.js index af5436e..b254e7e 100644 --- a/power-wheel-card/power-wheel-card.js +++ b/power-wheel-card/power-wheel-card.js @@ -69,11 +69,11 @@ class PowerWheelCard extends LitElement { let arrowData = {}; if (config.view === 'energy' && this.energy_capable) { - data.solar = this._makePositionObject(hass, config.solar_energy_entity, config.solar_power_icon, + data.solar = this._makePositionObject(hass, config.solar_energy_entity, config.solar_icon, 'mdi:weather-sunny', this.energy_decimals, true); - data.grid = this._makePositionObject(hass, config.grid_energy_entity, config.grid_power_icon, + data.grid = this._makePositionObject(hass, config.grid_energy_entity, config.grid_icon, 'mdi:flash-circle', this.energy_decimals, false); - data.home = this._makeHomePositionObject(hass, data, config.home_energy_entity, config.home_power_icon, + data.home = this._makeHomePositionObject(hass, data, config.home_energy_entity, config.home_icon, 'mdi:home', this.energy_decimals); arrowData = { solar2grid: { @@ -90,11 +90,11 @@ class PowerWheelCard extends LitElement { }, }; } else { - data.solar = this._makePositionObject(hass, config.solar_power_entity, config.solar_power_icon, + data.solar = this._makePositionObject(hass, config.solar_power_entity, config.solar_icon, 'mdi:weather-sunny', this.power_decimals, true); - data.grid = this._makePositionObject(hass, config.grid_power_entity, config.grid_power_icon, + data.grid = this._makePositionObject(hass, config.grid_power_entity, config.grid_icon, 'mdi:flash-circle', this.power_decimals, false); - data.home = this._makeHomePositionObject(hass, data, config.home_power_entity, config.home_power_icon, + data.home = this._makeHomePositionObject(hass, data, config.home_power_entity, config.home_icon, 'mdi:home', this.power_decimals); arrowData = { solar2grid: { @@ -164,6 +164,7 @@ class PowerWheelCard extends LitElement { color: ${this.producingColor}; } ha-icon#toggle-button { + padding-top: 4px; width: 24px; height: 24px; float: right; @@ -246,11 +247,11 @@ class PowerWheelCard extends LitElement { throw new Error('Energy_decimals should be an integer'); } this.energy_decimals = config.energy_decimals ? config.energy_decimals : 3; - this.colorPowerIcons = config.color_power_icons ? (config.color_power_icons == true) : false; - this.consumingColor = this.colorPowerIcons + this.colorIcons = config.color_icons ? (config.color_icons == true) : false; + this.consumingColor = this.colorIcons ? (config.consuming_color ? config.consuming_color : 'var(--label-badge-yellow, #f4b400)') : 'var(--state-icon-unavailable-color, #bdbdbd)'; - this.producingColor = this.colorPowerIcons + this.producingColor = this.colorIcons ? (config.producing_color ? config.producing_color : 'var(--label-badge-green, #0da035)') : 'var(--state-icon-unavailable-color, #bdbdbd)'; if (config.initial_view && !['power', 'energy'].includes(config.initial_view)) { From a3f197ca1b7ace4e18a5c0c74067a30c2d1325d9 Mon Sep 17 00:00:00 2001 From: Gerben ten Hove Date: Sat, 22 Dec 2018 13:19:31 +0100 Subject: [PATCH 7/7] New example images. Energy view reqs in readme. --- power-wheel-card/README.md | 56 +++++++++++++++++------ power-wheel-card/example-card-dark.gif | Bin 4352 -> 0 bytes power-wheel-card/example-card.gif | Bin 4210 -> 0 bytes power-wheel-card/example_energy_view.gif | Bin 0 -> 5457 bytes power-wheel-card/example_power_view.gif | Bin 0 -> 5277 bytes 5 files changed, 41 insertions(+), 15 deletions(-) delete mode 100644 power-wheel-card/example-card-dark.gif delete mode 100644 power-wheel-card/example-card.gif create mode 100644 power-wheel-card/example_energy_view.gif create mode 100644 power-wheel-card/example_power_view.gif diff --git a/power-wheel-card/README.md b/power-wheel-card/README.md index 7b80237..115bcc4 100644 --- a/power-wheel-card/README.md +++ b/power-wheel-card/README.md @@ -7,7 +7,7 @@ An intuïtive way to represent the power and energy that your home is consuming ## Features Features of the custom power-wheel-card: * Displays the three values (solar, grid and home) in 'a wheel'. -* Has different views for power values and energy values. The initial view can be set. There is a toggle button to switch between views. +* Has different views for showing power values and showing energy values: the *power view* and the *energy view*. The initial view can be set. There is a toggle button to switch between views. * Optionally calculates the current power that your home is consuming: home power. Input for the calculation is the (produced) solar power and the (consumed or produced) grid power. * Optionally calculates the energy that your home is consuming: home energy. @@ -20,21 +20,21 @@ Features of the custom power-wheel-card: * Works for default theme and custom themes that use [standard CSS vars](https://github.com/home-assistant/home-assistant-polymer/blob/master/src/resources/ha-style.js). * Has support for [custom_updater](https://github.com/custom-components/custom_updater) custom component to check for new release via the custom tracker-card. -![example1](./example-card.gif "The power-wheel-card in Default theme") -![example2](./example-card-dark.gif "The power-wheel-card in a random dark theme") +![example1](./example_power_view.gif "The power-wheel-card displaying the power view") +![example2](./example_energy_view.gif "The power-wheel-card displaying the energy view") -## Requirements +## Requirements for the *power view* 1. You need to have a working sensor for your solar power. Write down the entity id of this sensor. This is *YOUR_SOLAR_POWER_SENSOR* in the instructions below. - This sensor has a `unit_of_measurement` set up, e.g. `'W'` or `'kW'`. - The sensor value should be of type *int* or *float*. - The sensor value should be positive. - - The sensor could have an icon (optional) that will override the icon in the power-wheel-card if the card parameter `solar_icon` is not used. + - The sensor could have an icon (optional) that will override the default icon in the power-wheel-card if the card parameter `solar_icon` is not used. 1. You need to have a working sensor for your grid power. Write down the entity id of this sensor. This is *YOUR_GRID_POWER_SENSOR* in the instructions below. - This sensor has **the same** `unit_of_measurement` set up as the sensor for solar power. - Preferably this sensor has the same update interval as the sensor for solar power. (If not, the calculated value for home power can give unreal results sometimes.) - The sensor value should be of type *int* or *float*. - The sensor value should be **negative** for **producing** power to the grid and **positive** for **consuming** power of the grid. - - The sensor could have an icon (optional) that will override the icon in the power-wheel-card if the card parameter `grid_icon` is not used. + - The sensor could have an icon (optional) that will override the default icon in the power-wheel-card if the card parameter `grid_icon` is not used. Nb. You don't need a sensor for your home power, but you can use if you have it available. The value will be calculated if your don't supply this sensor as card parameter. @@ -70,6 +70,29 @@ And because my solar power sensor and dsmr sensor don't report in the same unit > **Tip.** If you are creating extra sensors for the power-wheel-card, maybe you want to exclude them in your `recorder:` setting. Extra sensors based on your heavily updating DSMR sensors will let your database grow fast. +## Requirements for the *energy view* +The *energy view* itself is not required. As a result you don't have to specify any *energy view* related card parameters. +The toggle button for switching views won't be displayed. + +> **Tip.** You can skip this paragraph and [start](#instructions) with a more simple setup first. + +But if you want the *energy view*: +1. Decide what kind of energy sensors you want to use. You could use your *smart meter counters* directly, but using self made sensors for e.g. *energy consumed or produced since last midnight* could provide more meaningful information on your power-wheel-card. +Especially since a future release will be able to convert the values into costs and savings. Then you would be able to see the actual energy costs/savings today. +1. You need to have a working sensor for your solar energy. Write down the entity id of this sensor. This is *YOUR_SOLAR_ENERGY_SENSOR* in the instructions below. + - This sensor has a `unit_of_measurement` set up, e.g. `'Wh'` or `'kWh'`. + - The sensor value should be of type *int* or *float*. + - The sensor value should be positive. + - The sensor could have an icon (optional) that will override the default icon in the power-wheel-card if the card parameter `solar_icon` is not used. +1. You need to have a working sensor for your grid energy. Write down the entity id of this sensor. This is *YOUR_GRID_ENERGY_SENSOR* in the instructions below. + - This sensor has **the same** `unit_of_measurement` set up as the sensor for solar energy. + - Preferably this sensor has the same update interval as the sensor for solar energy. (If not, the calculated value for home energy can give unreal results sometimes.) + - The sensor value should be of type *int* or *float*. + - The sensor value should be **negative** for **producing** energy to the grid and **positive** for **consuming** energy of the grid. + - The sensor could have an icon (optional) that will override the default con in the power-wheel-card if the card parameter `grid_icon` is not used. + +Nb. You don't need a sensor for your home energy, but you can use if you have it available. The value will be calculated if your don't supply this sensor as card parameter. + ## Instructions 1. Check the requirements above. If you don't comply to the requirements, the card won't be much of use for you or just won't work. 1. Download the file [power-wheel-card.js](https://raw.githubusercontent.com/gurbyz/custom-cards-lovelace/master/power-wheel-card/power-wheel-card.js). @@ -82,7 +105,7 @@ resources: type: module ``` -> Note: The actual number in `v=A_NUMBER` isn't relevant. You can increase the number whenever updating the source code to avoid having to manually clear the cache of your browsers and mobile apps. +> **Note.** The actual number in `v=A_NUMBER` isn't relevant. You can increase the number whenever updating the source code to avoid having to manually clear the cache of your browsers and mobile apps. 5. Include a configuration for the power-wheel-card in your `ui-lovelace.yaml` file: @@ -96,7 +119,9 @@ views: color_icons: true ``` -## Parameters +There are many more card parameters available, but it's advised to start with this simple setup to get things running. + +## Card parameters | Parameter | Type | Mandatory? | Default | Description | |--------|------|------------|---------|-------------| @@ -119,19 +144,20 @@ views: |initial_view|string|optional|`"power"`|The initial view that will displayed. Allowed values are `"power"` for *power view* and `"energy"` for *energy view*.| ### More about icons -The icons for solar power and grid power can be set by card parameters as shown in the table above. -If you don't specify them as card parameters, the icons are taken from your own sensors for solar power and grid power. -You could have specified those with the `customize` option for `homeassistant` in your `configuration.yaml`. -If you haven't set up icons for them, default icons will be used. For solar power: `mdi:weather-sunny`; and for grid power: `mdi:flash-circle`. +The icons for solar and grid can be set by card parameters as shown in the table above. +If you don't specify them as card parameters, the icons are taken from your own sensors for solar power and grid power (in the *power view*) and from your own sensors for solar energy and grid energy (in the *energy view*). +You could have specified those with the `customize:` option for `homeassistant` in your `configuration.yaml`. + +If you haven't set up any icons for them, default icons will be used in all views. For solar: `mdi:weather-sunny`; and for grid: `mdi:flash-circle`. -An example for reusing the icons of your sensors, to be put in `configuration.yaml`: +An example for reusing the icons of your sensors used in the *power view*, to be put in `configuration.yaml`: ```yaml homeassistant: customize: - sensor.solar_power: + sensor.YOUR_SOLAR_POWER_SENSOR: icon: mdi:white-balance-sunny - sensor.grid_power: + sensor.YOUR_GRID_POWER_SENSOR: icon: mdi:flash ``` diff --git a/power-wheel-card/example-card-dark.gif b/power-wheel-card/example-card-dark.gif deleted file mode 100644 index d882db2f396bd563cf10aaca8594b38dc878b2bd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4352 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 d-oC#6xxRtleSX6%U}St7<(iZBc* z+gL7Bx4NSRNtx`SnrzpSnfJc$AMlssmDM($dm`Z`Gbp=UmLJzkU1m zl{?(1yeC)ln+pmG;_f|TmbRCdmnT;6QXX{PdemK2Rh3oKo5SIAx!l60m&MJmo<4nA z_U!NbZT$~B-aPJl*WKOC`E!ulH`K`g@bcx$)>k7Pg3mm`XwTa(Z{NP%d`H-PueCX_ z=lS6H=HP+NLDS74lg%OXS05%fhix~99X@{ixH;nb=F_zJv(Nj{uVP`)z?ZL^UoMEp zqb4RMMkeRQQwhT9AL8lMFEc+RGq=QFvm~>Hb8~Z(^FPJ&rC+}q>vr~?oZP(py9I^!ii+7KrDf$6_a9U~d{kBaxTdzQ z@L~hEvFSe!~L9o3tBeL-5+dv%`3iyV!)cQnd5qlyN{VF9lrFI5xMY(Mp8zC~4#)AeX@DLViIVV~ z7IaPYJ2;FNS;TUGa?+(EiMTFCwwG!$U8+r$5g7KR>#K0bNR@e6Tn>1PMCCgs6VY43)?cV)0F%g0+8CP zWDM$T|9;zu1$IXH{ zh>D`|2vTY=Hs#=U;w}&^#EeETHHBk1lQ$&*>d$`bF83uJDpMx2if~&T?_4Tiwb27%Fuv=0LAXBItT5 z5Xwf0LNJ%L!X3zXY}&5hN?mpd3ag~6*Y!>LEBVC^5 zlfg^NdAQRT<6|)eh+lU`xD2hN%4aS$L`VW%t6eat0o!$TIs<_2UKSK zQPPfsV9e}lz+W53D(`x?nyaa1jNsMwOe=~8A0gjC3391>Q-DA{gYy^85R>3j{KQu3 zd+?*w0#u^sx;YbxtTwpGNV(vxavgm8lgIJZcIZ=R;yBPP&F!KlQbtHX)VtB>m4nXA zHDL(J*DXic<03n7RUy&!t*LK-vDnroWrM0^nTmBW9VO|7%1^Vq$F9zl-!zQ*T6r}* zX13bDK4z}&oWs`j>i<8*;lTw@W+5o3Bn>y(z0km2CU_#x)nYzd_6)vyewQaI;xbEyiY=M zPVSH0H1+L59fmq=>a1n_XiQQmi;h#oT?MM`rQS`05@SDU0;-On9 zn1gIkR$9|`n;}4#XO%FgtIjBU(yXn2u@M9T4`mw0(7>s0(IuAr^c!l?O{2la3!tri z?u@>bBfDHviBUmlWR?PnNFtI6fQ*7E}?eWuFh77|tXi z?GPFWT>+y_tG2N6CPV5G8MBKggey1mFhO;cT}D6u5NWC@+~?B|y&>h<5D*G`i8jpF zhC6SheL|3kj4Qh8TXwKDWmYl$o=r7d2zYB7Bt22b&9^)fcf@Yby5NDpNk+Tkh+OuJ zoefWs{mQm@JwT=H{L}>M_Qb=*xYXNf0Ytb}@L9^ee2ar=oCjdr4yRn*Qx(s+k4{R` zbWe}wyN*>mz#`*dBmh^l1H!r>s23u;AAh{V(|R{E*w^~> z`Hw0YVdYS*40zDHLia}zz{orV*5S#W2?ip>R@3?FGYRa*`T?+WK?b2sw3hWD(}QLA zH*o!X6@nJ?5n&aw^zE>kpx*wf-gE@sj!;a6cG>i#$YEAhg0^{C=u?THH1z~tiJUEm zsD`^aR$mNB!X_(q-Fx?G?sQ7Z@VBgnmd}sBZFFs%8Ow5Sl|S?&hFtkdANE2kMT)L-(so~9%avj=@ zZZ~k!|CQ$Jq$zs^l1eI&PuxBOw7~bnw^kFAa?(5^%_}?I_p76K<2x~K!5l}!k$k*G zrABDo{`9E0EafNaGKYITC?i0EY6D*Dn9tFaKmsSD8kDv(U~IEVOE%;C%E_nH?~bGS z-O*grycA}WQ+y%W)W}!L9djU~TYkJ+XX>T4eMC;g$dp#_Z?AD;%01crlRFzGWNmU_ znMirGl@}ETwVr~<$HFUO)k6;tdcI%26PX_+8(P#_BK6y8E`cDqI7e&nL{mOCp2?dJ zFfmXaTG+NybyH30yecR?=#!jh@x@=)e^%Co%y?$D=errRY~<{=)Yf8X>}ENA%US-D z_Kq-QRPECbAA**f(Bj!vKie&~&77`n-!K2J7cf!?nRIS>y>+i<+!wrwjl9dJ@8pfL zFb~XAK4r&3PV*NT?UPSmxVEF9ShsFI!}R5Q(qg{jt4{}$3|~h$W!+u0R6Q(NGiK~v z3dXg~$bK+;b0B=FqObI;(;vupWj6vUTt7{sI$|;(*DNPRw9VaHGau?0|H=8sdp@XR z%kWztY<>YQ;eyrC?0#GVKAQ)pY?7tJS;#ZJpVUH&5voCTphPpq`MMGgRFED)CT%|? z%EZA;u{w0AN~mJ?jvq=18_Bm)M=S9sP)4Go^5Hw$t!Q5mds%K5Jo3y*N**S=kA1(TA-ojtMrkHtLLKD4QSY9KC9% z&azobqS!#E&KSTpcAwCo#kx%0NEU%@kr2b+S_lIC>l`)|Ky9*+uGz;c+^L!Cb7y%>j7NXAfb6aw|~P#8*ljJXKK znnv3xQ*XFX7zso~zNXEMb7U>njZ=}Jn1a>4f5`_3nq$GYu@5^&)5kU3uzY3_F#(l~ zX;q{sg~iQMQFFb>c_P9Jhp@o?Vaf-ut-@`n2_Z8f<+ym|<`c;@SbjF><$;aN3-mF- z2J@1LtDpp(U}c+fkcBL4rdY8ZbcV1Q!HD1aa(xqyYuTwG0Vx`liT5BB0bn?3VidRz z`Ubc_7GgFrel{%i>^u0aT%?yM@pMnBg~0SRyt^n^$HRCM|k9&&C4*~U+LKtZ2j-FlpyRtBJvc#4y4nsT$V z&;aekMmXYFFf$h7Lzp*^I7+>BYAOBa>d{_p^E?mCp}Ieu%Ax2;FdQQrAwZ}MW|~Z0 z77v6A*(`@1)k$XKq8~5~9DGSc2 zbQoU*6@w+Yc6A7I`xEjHF&9K|b(VF&Lt7OV?1#s{_IK=X`B@=(6)Yr01h14rP*|Cp zqWsyM+!5Qml?TvgQV{k)4o4-s@c`Oph5wC8p>TdkZ(*vpQoL=xOR0%r*BX~ z0NOQhimI|IFzxq0O*ycWE{|Ub!>?222J)p9lu%c-@EMJOG_-Bszb9D$PKpp^)?h3h zi6zH#m`F7;=urZft^?fnBg7B4-JgVuO96Wrdz@Ct6nHSnFqZ|`?k;c3EMqAHGnUPE z@zz6lsv3g~djxv6Pb&Sj4K=x+pbZeJ1+?Z;MFt&wv>$;F^I(uc!d=3yL`jhc01Iib z^k4x*cCmoufpfrY;pt!vR4eYVdHGUD!hn5L__kCK7EU_M+l{ArhTFjOh;VH-yujv= zIh8c`0iSfrz0)_uXgg7j%zk4pPx~4EKnO4U2^b4NQ&^eEtjySft;SNndMAqO3V*g$ zH-1dt#}i{n2D_c%M5t6}{XVoVby4*1hN^0dluPaqJTd`?CRNA%B$Zi94>l0*`_PmD zb1{6FgTkR{1ZPeev(hYcUCOWyP!uU=XsLQQS1AHjc$@(is_1WkH9b={78Grv6(t`f z+;0SmwpINVB7IS0YR55KTsC{_fQfR(Cp^SAyeL$g{`;};QAGUW zXwhKg)o?Jj!L*>kyrn_E3Vypxe+mfKG~wEMagW7v?FzUKE!^WDxlS8gs%oQ)MWdTu m_@CeLE{Q!`drjVQ*H9IXbSLZ3W{ywQZaM@u=o!GtcIKb diff --git a/power-wheel-card/example_energy_view.gif b/power-wheel-card/example_energy_view.gif new file mode 100644 index 0000000000000000000000000000000000000000..fb5c6e49145edecc5f2ac736f040694efc431817 GIT binary patch literal 5457 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 PWGkB^5X??a@i`K8sSrKO>t9>Qg4 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 literal 0 HcmV?d00001