├── CHANGELOG.md ├── LICENSE ├── README.md ├── battery-entity-row.js ├── example.png ├── hacs.json └── tracker.json /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | All notable changes to this project will be documented in this file. 3 | 4 | ## 1.3.1 5 | 6 | **Fixed:** 7 | - Incorrect over-simplification of secondary_info handling 8 | 9 | ## 1.3.0 10 | 11 | **Added:** 12 | - Support `last-updated` on secondary_info (#10) 13 | - Support custom entity attributes on secondary_info (#10) 14 | 15 | **Fixed:** 16 | - Breaking change to relative datetime objects (#9) 17 | 18 | ## 1.2.0 19 | 20 | **Added:** 21 | - Support some predefined string values (#5) 22 | - Support custom charging state or attribute values (#7) 23 | 24 | **Changed:** 25 | - Prevent unnecessary re-rendering with `shouldUpdate` function (#8) 26 | - Refactor and improve configuration handling 27 | 28 | ## 1.1.0 29 | 30 | **Added:** 31 | - Support for battery charging state or attribute (#4) 32 | 33 | **Changed:** 34 | - Improve unknown/unavailable battery state display 35 | - Allow disabling/hiding `unit` with value `false` 36 | 37 | ## 1.0.0 38 | 39 | - **Initial release** 40 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Ben Tomlin 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # battery-entity-row 2 | 3 | Show battery states or attributes with dynamic icon on entity rows in Home Assistant's Lovelace UI 4 | 5 | [![GH-release](https://img.shields.io/github/v/release/benct/lovelace-battery-entity-row.svg?style=flat-square)](https://github.com/benct/lovelace-battery-entity-row/releases) 6 | [![GH-downloads](https://img.shields.io/github/downloads/benct/lovelace-battery-entity-row/total?style=flat-square)](https://github.com/benct/lovelace-battery-entity-row/releases) 7 | [![GH-last-commit](https://img.shields.io/github/last-commit/benct/lovelace-battery-entity-row.svg?style=flat-square)](https://github.com/benct/lovelace-battery-entity-row/commits/master) 8 | [![GH-code-size](https://img.shields.io/github/languages/code-size/benct/lovelace-battery-entity-row.svg?color=red&style=flat-square)](https://github.com/benct/lovelace-battery-entity-row) 9 | [![hacs_badge](https://img.shields.io/badge/HACS-Default-orange.svg?style=flat-square)](https://github.com/hacs) 10 | 11 | Rewritten and improved version of cbulock's [battery-entity](https://github.com/cbulock/lovelace-battery-entity) card _(deprecated/unmaintained)_. 12 | 13 | **NOTE:** This is not a standalone lovelace card, but a row element for the [entities](https://www.home-assistant.io/lovelace/entities/) card. 14 | If you need a standalone card or want _a lot_ more customizability, check out maxwroc's [battery-state-card](https://github.com/maxwroc/battery-state-card). 15 | 16 | ## Installation 17 | 18 | Manually add [battery-entity-row.js](https://raw.githubusercontent.com/benct/lovelace-battery-entity-row/master/battery-entity-row.js) 19 | to your `/www/` folder and add the following to the `configuration.yaml` file: 20 | 21 | ```yaml 22 | lovelace: 23 | resources: 24 | - url: /local/battery-entity-row.js?v=1.3.1 25 | type: module 26 | ``` 27 | 28 | _OR_ install using [HACS](https://hacs.xyz/) and add this (if in YAML mode): 29 | 30 | ```yaml 31 | lovelace: 32 | resources: 33 | - url: /hacsfiles/lovelace-battery-entity-row/battery-entity-row.js 34 | type: module 35 | ``` 36 | 37 | The above configuration can be managed directly in the Configuration -> Lovelace Dashboards -> Resources panel when not using YAML mode, 38 | or added by clicking the "Add to lovelace" button on the HACS dashboard after installing the plugin. 39 | 40 | ## Configuration 41 | 42 | This card produces an `entity-row` and must therefore be configured as an entity in an [entities](https://www.home-assistant.io/lovelace/entities/) card. 43 | 44 | The battery level value is fetched from the entity `state`, from the attribute `battery` or `battery_level`, 45 | or from a custom attribute defined with the `attribute` option. Numeric values (`0-100`) and some predefined 46 | string values (`high`, `normal`, `low`, etc..) are supported as a battery level value. 47 | 48 | | Name | Type | Default | Description | 49 | | --------------- | ----------- | --------------- | ------------------------------------------------------------------------------ | 50 | | type | string | **Required** | `custom:battery-entity-row` | 51 | | entity | string | **Required** | `domain.my_entity_id` | 52 | | attribute | string | `battery_level` | Override battery level attribute | 53 | | name | string | `friendly_name` | Override entity `friendly_name` | 54 | | secondary\_info | string | | `last-changed`, `last-updated` or an attribute of the entity. | 55 | | unit | string/bool | `%` | Override default `unit`, or hide with `false` | 56 | | icon | string | | Override dynamic battery `icon` | 57 | | warning | number | `35` | Level at which the icon will appear yellow | 58 | | critical | number | `15` | Level at which the icon will appear red | 59 | | charging | bool/object | `false` | Indicate charging based on entity state. See charging object for more options. | 60 | 61 | Currently limited support for `secondary_info` option with value `last-changed`. 62 | 63 | ### Charging object 64 | 65 | | Name | Type | Default | Description | 66 | | --------- | ----------- | -------------------- | ---------------------------------------------------- | 67 | | entity | string | `main entity` | Get charging state from another entity | 68 | | attribute | string | | Get charging state from an attribute | 69 | | state | string/list | `"on"`, `"charging"` | Add values that indicate charging (case insensitive) | 70 | 71 | ## Examples 72 | 73 | ![battery-entity-row](https://raw.githubusercontent.com/benct/lovelace-battery-entity-row/master/example.png) 74 | 75 | ```yaml 76 | type: entities 77 | entities: 78 | - type: custom:battery-entity-row 79 | entity: sensor.bedroom_temperature 80 | 81 | - type: custom:battery-entity-row 82 | entity: sensor.bedroom_temperature 83 | attribute: battery_percent 84 | name: Some battery 85 | unit: percent 86 | icon: mdi:battery-alert 87 | secondary_info: last-changed 88 | warning: 50 89 | critical: 25 90 | charging: true 91 | 92 | - type: custom:battery-entity-row 93 | entity: sensor.bedroom_temperature 94 | name: Charging battery 95 | charging: 96 | entity: binary_sensor.bedroom_temperature_charger 97 | attribute: charging 98 | state: 99 | - Enabled 100 | - is_charging 101 | ``` 102 | 103 | Usage in [auto-entities](https://github.com/thomasloven/lovelace-auto-entities) card: 104 | 105 | ```yaml 106 | type: custom:auto-entities 107 | card: 108 | type: entities 109 | filter: 110 | include: 111 | - entity_id: sensor.battery* # or use other matchers 112 | options: 113 | type: custom:battery-entity-row 114 | 115 | ``` 116 | 117 | ## My cards 118 | 119 | [xiaomi-vacuum-card](https://github.com/benct/lovelace-xiaomi-vacuum-card) | 120 | [multiple-entity-row](https://github.com/benct/lovelace-multiple-entity-row) | 121 | [github-entity-row](https://github.com/benct/lovelace-github-entity-row) | 122 | [battery-entity-row](https://github.com/benct/lovelace-battery-entity-row) | 123 | [~~attribute-entity-row~~](https://github.com/benct/lovelace-attribute-entity-row) 124 | 125 | [![BMC](https://www.buymeacoffee.com/assets/img/custom_images/white_img.png)](https://www.buymeacoff.ee/benct) 126 | -------------------------------------------------------------------------------- /battery-entity-row.js: -------------------------------------------------------------------------------- 1 | ((LitElement) => { 2 | console.info( 3 | '%c BATTERY-ENTITY-ROW %c 1.3.1 ', 4 | 'color: cyan; background: black; font-weight: bold;', 5 | 'color: darkblue; background: white; font-weight: bold;', 6 | ); 7 | const {html, css} = LitElement.prototype; 8 | 9 | const defaultOnStates = ['on', 'charging', 'true']; 10 | 11 | class BatteryEntityRow extends LitElement { 12 | 13 | static get properties() { 14 | return { 15 | _hass: Object, 16 | _config: Object, 17 | stateObj: Object 18 | } 19 | } 20 | 21 | static get styles() { 22 | return css` 23 | :host { 24 | display: flex; 25 | align-items: center; 26 | } 27 | .flex { 28 | flex: 1; 29 | margin-left: 16px; 30 | display: flex; 31 | justify-content: space-between; 32 | align-items: center; 33 | min-width: 0; 34 | } 35 | .info, state-badge { 36 | cursor: pointer; 37 | } 38 | .secondary { 39 | color: var(--secondary-text-color); 40 | } 41 | .good { 42 | color: var(--label-badge-green); 43 | } 44 | .warning { 45 | color: var(--label-badge-yellow); 46 | } 47 | .critical { 48 | color: var(--label-badge-red); 49 | }`; 50 | } 51 | 52 | render() { 53 | if (!this._hass || !this._config) return html``; 54 | if (!this.stateObj) return this.renderWarning(); 55 | 56 | const charging = this.getChargingState(this._config.charging) 57 | const batteryValue = this.getBatteryLevel(this._config.attribute); 58 | 59 | const isUnavailable = !batteryValue || ['unavailable', 'unknown'].includes(batteryValue); 60 | const isNumeric = !isNaN(parseFloat(batteryValue)) && isFinite(batteryValue); 61 | 62 | const numericValue = isUnavailable ? null : isNumeric ? batteryValue : this.parseStringValue(batteryValue); 63 | 64 | const icon = this._config.icon || this.getIcon(numericValue, charging); 65 | const color = this.getColor(numericValue); 66 | 67 | const name = this._config.name || this.stateObj.attributes.friendly_name; 68 | const unit = this._config.unit === false ? null : (this._config.unit || (isNumeric ? '%' : null)); 69 | const state = isUnavailable 70 | ? this._hass.localize('state.default.unknown') 71 | : html`${batteryValue}${unit && html` ${unit}`}`; 72 | 73 | return html` 74 | 79 | 80 |
81 |
${name}${this.renderSecondaryInfo()}
82 |
${state}
83 |
`; 84 | } 85 | 86 | renderSecondaryInfo() { 87 | const secondaryInfo = this._config.secondary_info; 88 | let content = undefined; 89 | 90 | if (secondaryInfo === 'last-changed') { 91 | content = html``; 92 | } else if (secondaryInfo === 'last-updated') { 93 | content = html``; 94 | } else if (secondaryInfo in this.stateObj.attributes) { 95 | content = this.stateObj.attributes[secondaryInfo]; 96 | } 97 | return content ? html`
${content}
` : null; 98 | } 99 | 100 | renderWarning() { 101 | return html` 102 | ${this._hass.localize('ui.panel.lovelace.warning.entity_not_found', 'entity', this._config.entity)} 103 | `; 104 | } 105 | 106 | setConfig(config) { 107 | if (!config.entity) throw new Error('Please define a valid entity.'); 108 | 109 | this.moreInfo = () => this.fireEvent(this, 'hass-more-info', {entityId: config.entity}); 110 | this._config = config; 111 | } 112 | 113 | shouldUpdate(changedProps) { 114 | return changedProps.has('stateObj'); 115 | } 116 | 117 | set hass(hass) { 118 | this._hass = hass; 119 | 120 | if (hass && this._config) { 121 | this.stateObj = this._config.entity in hass.states ? hass.states[this._config.entity] : null; 122 | } 123 | } 124 | 125 | getBatteryLevel(attribute) { 126 | let batteryValue = this.stateObj.state; 127 | if (this.stateObj.attributes.battery) batteryValue = this.stateObj.attributes.battery; 128 | if (this.stateObj.attributes.battery_level) batteryValue = this.stateObj.attributes.battery_level; 129 | if (this.stateObj.attributes[attribute]) batteryValue = this.stateObj.attributes[attribute]; 130 | return !isNaN(parseFloat(batteryValue)) && isFinite(batteryValue) 131 | ? Math.round(parseInt(batteryValue, 10)) : batteryValue; 132 | } 133 | 134 | getChargingState(chargingConfig) { 135 | if (!chargingConfig) return false; 136 | if (chargingConfig === true) { 137 | return defaultOnStates.includes(this.stateObj.state.toString().toLowerCase()); 138 | } 139 | 140 | const additionalStates = chargingConfig.state || []; 141 | const onStates = defaultOnStates.concat(additionalStates).map(value => value.toString().toLowerCase()); 142 | 143 | const entity = (chargingConfig.entity && chargingConfig.entity in this._hass.states) 144 | ? this._hass.states[chargingConfig.entity] : this.stateObj; 145 | const state = chargingConfig.attribute ? entity.attributes[chargingConfig.attribute] : entity.state; 146 | return onStates.includes(state.toString().toLowerCase()); 147 | } 148 | 149 | getIcon(batteryLevel, charging) { 150 | if (!batteryLevel) return 'mdi:battery-unknown'; 151 | const roundedLevel = Math.round(batteryLevel / 10) * 10; 152 | return roundedLevel >= 100 153 | ? (charging ? 'mdi:battery-charging-100' : 'mdi:battery') 154 | : roundedLevel === 0 155 | ? (charging ? 'mdi:battery-charging-outline' : 'mdi:battery-outline') 156 | : (charging ? 'mdi:battery-charging-' : 'mdi:battery-') + roundedLevel; 157 | } 158 | 159 | getColor(batteryLevel) { 160 | if (!batteryLevel) return 'unknown'; 161 | const warning = this._config.warning || 35; 162 | const critical = this._config.critical || 15; 163 | return (batteryLevel > warning) 164 | ? 'good' 165 | : (batteryLevel > critical) 166 | ? 'warning' 167 | : 'critical'; 168 | } 169 | 170 | parseStringValue(v) { 171 | const val = v.toString().toLowerCase(); 172 | if (['full', 'high', 'max', 'maximum'].includes(val)) return 90; 173 | if (['medium', 'med', 'normal'].includes(val)) return 50; 174 | if (['low', 'min', 'minimal'].includes(val)) return 10; 175 | if (['empty', 'critical', 'none'].includes(val)) return 0; 176 | return null; 177 | } 178 | 179 | fireEvent(node, type, detail = {}, options = {}) { 180 | const event = new Event(type, { 181 | bubbles: options.bubbles || true, 182 | cancelable: options.cancelable || true, 183 | composed: options.composed || true, 184 | }); 185 | event.detail = detail; 186 | node.dispatchEvent(event); 187 | } 188 | } 189 | 190 | customElements.define('battery-entity-row', BatteryEntityRow); 191 | })(window.LitElement || Object.getPrototypeOf(customElements.get('hui-masonry-view') || customElements.get('hui-view'))); 192 | -------------------------------------------------------------------------------- /example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/benct/lovelace-battery-entity-row/e7d1eabe8464b946dbb161ef6f61dbee437169ab/example.png -------------------------------------------------------------------------------- /hacs.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Battery Entity Row", 3 | "filename": "battery-entity-row.js", 4 | "render_readme": true 5 | } 6 | -------------------------------------------------------------------------------- /tracker.json: -------------------------------------------------------------------------------- 1 | { 2 | "battery-entity-row": { 3 | "updated_at": "2021-03-12", 4 | "version": "1.3.1", 5 | "remote_location": "https://raw.githubusercontent.com/benct/lovelace-battery-entity-row/master/battery-entity-row.js", 6 | "visit_repo": "https://github.com/benct/lovelace-battery-entity-row", 7 | "changelog": "https://github.com/benct/lovelace-battery-entity-row/blob/master/CHANGELOG.md" 8 | } 9 | } 10 | --------------------------------------------------------------------------------