diff --git a/src/common/entity/battery_icon.ts b/src/common/entity/battery_icon.ts
new file mode 100644
index 0000000000..26ffb3a3d8
--- /dev/null
+++ b/src/common/entity/battery_icon.ts
@@ -0,0 +1,28 @@
+/** Return an icon representing a battery state. */
+import { HassEntity } from "home-assistant-js-websocket";
+
+export const batteryIcon = (
+ batteryState: HassEntity,
+ batteryChargingState?: HassEntity
+) => {
+ const battery = Number(batteryState.state);
+ const battery_charging =
+ batteryChargingState && batteryChargingState.state === "on";
+
+ if (isNaN(battery)) {
+ return "hass:battery-unknown";
+ }
+
+ var icon = "hass:battery";
+ const batteryRound = Math.round(battery / 10) * 10;
+ if (battery_charging && battery > 10) {
+ icon += `-charging-${batteryRound}`;
+ } else if (battery_charging) {
+ icon += "-outline";
+ } else if (battery <= 5) {
+ icon += "-alert";
+ } else if (battery > 5 && battery < 95) {
+ icon += `-${batteryRound}`;
+ }
+ return icon;
+};
diff --git a/src/common/entity/sensor_icon.ts b/src/common/entity/sensor_icon.ts
index 32d7436d3b..1ec97ff5d9 100644
--- a/src/common/entity/sensor_icon.ts
+++ b/src/common/entity/sensor_icon.ts
@@ -2,6 +2,7 @@
import { HassEntity } from "home-assistant-js-websocket";
import { UNIT_C, UNIT_F } from "../const";
import { domainIcon } from "./domain_icon";
+import { batteryIcon } from "./battery_icon";
const fixedDeviceClassIcons = {
humidity: "hass:water-percent",
@@ -19,29 +20,7 @@ export const sensorIcon = (state: HassEntity) => {
return fixedDeviceClassIcons[dclass];
}
if (dclass === "battery") {
- const battery = Number(state.state);
- if (isNaN(battery)) {
- return "hass:battery-unknown";
- }
- const batteryRound = Math.round(battery / 10) * 10;
- if (batteryRound >= 100) {
- return "hass:battery";
- }
- if (batteryRound <= 0) {
- return "hass:battery-alert";
- }
- // Will return one of the following icons: (listed so extractor picks up)
- // hass:battery-10
- // hass:battery-20
- // hass:battery-30
- // hass:battery-40
- // hass:battery-50
- // hass:battery-60
- // hass:battery-70
- // hass:battery-80
- // hass:battery-90
- // We obscure 'hass' in iconname so this name does not get picked up
- return `${"hass"}:battery-${batteryRound}`;
+ return batteryIcon(state);
}
const unit = state.attributes.unit_of_measurement;
diff --git a/src/components/entity/ha-battery-icon.ts b/src/components/entity/ha-battery-icon.ts
new file mode 100644
index 0000000000..19429bd20c
--- /dev/null
+++ b/src/components/entity/ha-battery-icon.ts
@@ -0,0 +1,20 @@
+import { batteryIcon } from "../../common/entity/battery_icon";
+import "../ha-icon";
+import { customElement, html, property, LitElement } from "lit-element";
+
+@customElement("ha-battery-icon")
+class HaBatteryIcon extends LitElement {
+ @property() public batteryStateObj;
+
+ @property() public batteryChargingStateObj;
+
+ protected render() {
+ return html`
+
+ `;
+ }
+}
+
+customElements.define("ha-battery-icon", HaBatteryIcon);
diff --git a/src/data/entity_registry.ts b/src/data/entity_registry.ts
index 5abaa9084b..a5d897ca8b 100644
--- a/src/data/entity_registry.ts
+++ b/src/data/entity_registry.ts
@@ -37,6 +37,17 @@ export const findBatteryEntity = (
hass.states[entity.entity_id].attributes.device_class === "battery"
);
+export const findBatteryChargingEntity = (
+ hass: HomeAssistant,
+ entities: EntityRegistryEntry[]
+): EntityRegistryEntry | undefined =>
+ entities.find(
+ (entity) =>
+ hass.states[entity.entity_id] &&
+ hass.states[entity.entity_id].attributes.device_class ===
+ "battery_charging"
+ );
+
export const computeEntityRegistryName = (
hass: HomeAssistant,
entry: EntityRegistryEntry
diff --git a/src/panels/config/devices/ha-config-device-page.ts b/src/panels/config/devices/ha-config-device-page.ts
index fd10d1d175..9987b894cc 100644
--- a/src/panels/config/devices/ha-config-device-page.ts
+++ b/src/panels/config/devices/ha-config-device-page.ts
@@ -14,7 +14,7 @@ import { isComponentLoaded } from "../../../common/config/is_component_loaded";
import { computeStateName } from "../../../common/entity/compute_state_name";
import { createValidEntityId } from "../../../common/entity/valid_entity_id";
import { compare } from "../../../common/string/compare";
-import "../../../components/entity/ha-state-icon";
+import "../../../components/entity/ha-battery-icon";
import "../../../components/ha-icon-next";
import { AreaRegistryEntry } from "../../../data/area_registry";
import { ConfigEntry } from "../../../data/config_entries";
@@ -26,6 +26,7 @@ import {
import {
EntityRegistryEntry,
findBatteryEntity,
+ findBatteryChargingEntity,
updateEntityRegistryEntry,
} from "../../../data/entity_registry";
import { SceneEntities, showSceneEditor } from "../../../data/scene";
@@ -117,6 +118,11 @@ export class HaConfigDevicePage extends LitElement {
| EntityRegistryEntry
| undefined => findBatteryEntity(this.hass, entities));
+ private _batteryChargingEntity = memoizeOne(
+ (entities: EntityRegistryEntry[]): EntityRegistryEntry | undefined =>
+ findBatteryChargingEntity(this.hass, entities)
+ );
+
protected firstUpdated(changedProps) {
super.firstUpdated(changedProps);
loadDeviceRegistryDetailDialog();
@@ -145,9 +151,13 @@ export class HaConfigDevicePage extends LitElement {
const integrations = this._integrations(device, this.entries);
const entities = this._entities(this.deviceId, this.entities);
const batteryEntity = this._batteryEntity(entities);
+ const batteryChargingEntity = this._batteryChargingEntity(entities);
const batteryState = batteryEntity
? this.hass.states[batteryEntity.entity_id]
: undefined;
+ const batteryChargingState = batteryChargingEntity
+ ? this.hass.states[batteryChargingEntity.entity_id]
+ : undefined;
const area = this._computeArea(this.areas, device);
return html`
@@ -201,10 +211,11 @@ export class HaConfigDevicePage extends LitElement {
? html`
${batteryState.state}%
-
+ .batteryStateObj=${batteryState}
+ .batteryChargingStateObj=${batteryChargingState}
+ >
`
: ""
diff --git a/src/panels/config/devices/ha-config-devices-dashboard.ts b/src/panels/config/devices/ha-config-devices-dashboard.ts
index 907756ad5f..673d0ff7f3 100644
--- a/src/panels/config/devices/ha-config-devices-dashboard.ts
+++ b/src/panels/config/devices/ha-config-devices-dashboard.ts
@@ -14,7 +14,7 @@ import {
DataTableRowData,
RowClickedEvent,
} from "../../../components/data-table/ha-data-table";
-import "../../../components/entity/ha-state-icon";
+import "../../../components/entity/ha-battery-icon";
import { AreaRegistryEntry } from "../../../data/area_registry";
import { ConfigEntry } from "../../../data/config_entries";
import {
@@ -25,6 +25,7 @@ import {
import {
EntityRegistryEntry,
findBatteryEntity,
+ findBatteryChargingEntity,
} from "../../../data/entity_registry";
import { domainToName } from "../../../data/integration";
import "../../../layouts/hass-tabs-subpage-data-table";
@@ -35,7 +36,7 @@ interface DeviceRowData extends DeviceRegistryEntry {
device?: DeviceRowData;
area?: string;
integration?: string;
- battery_entity?: string;
+ battery_entity?: [string | undefined, string | undefined];
}
@customElement("ha-config-devices-dashboard")
@@ -167,7 +168,10 @@ export class HaConfigDeviceDashboard extends LitElement {
)
.join(", ")
: "No integration",
- battery_entity: this._batteryEntity(device.id, deviceEntityLookup),
+ battery_entity: [
+ this._batteryEntity(device.id, deviceEntityLookup),
+ this._batteryChargingEntity(device.id, deviceEntityLookup),
+ ],
};
});
@@ -201,17 +205,25 @@ export class HaConfigDeviceDashboard extends LitElement {
sortable: true,
type: "numeric",
width: "90px",
- template: (batteryEntity: string) => {
- const battery = batteryEntity
- ? this.hass.states[batteryEntity]
- : undefined;
+ template: (
+ batteryEntityPair: DeviceRowData["battery_entity"]
+ ) => {
+ const battery =
+ batteryEntityPair && batteryEntityPair[0]
+ ? this.hass.states[batteryEntityPair[0]]
+ : undefined;
+ const batteryCharging =
+ batteryEntityPair && batteryEntityPair[1]
+ ? this.hass.states[batteryEntityPair[1]]
+ : undefined;
return battery
? html`
${isNaN(battery.state as any) ? "-" : battery.state}%
-
+ .batteryStateObj=${battery}
+ .batteryChargingStateObj=${batteryCharging}
+ >
`
: html` - `;
},
@@ -267,17 +279,25 @@ export class HaConfigDeviceDashboard extends LitElement {
type: "numeric",
width: "15%",
maxWidth: "90px",
- template: (batteryEntity: string) => {
- const battery = batteryEntity
- ? this.hass.states[batteryEntity]
- : undefined;
+ template: (
+ batteryEntityPair: DeviceRowData["battery_entity"]
+ ) => {
+ const battery =
+ batteryEntityPair && batteryEntityPair[0]
+ ? this.hass.states[batteryEntityPair[0]]
+ : undefined;
+ const batteryCharging =
+ batteryEntityPair && batteryEntityPair[1]
+ ? this.hass.states[batteryEntityPair[1]]
+ : undefined;
return battery && !isNaN(battery.state as any)
? html`
${battery.state}%
-
+ .batteryStateObj=${battery}
+ .batteryChargingStateObj=${batteryCharging}
+ >
`
: html` - `;
},
@@ -336,6 +356,17 @@ export class HaConfigDeviceDashboard extends LitElement {
return batteryEntity ? batteryEntity.entity_id : undefined;
}
+ private _batteryChargingEntity(
+ deviceId: string,
+ deviceEntityLookup: DeviceEntityLookup
+ ): string | undefined {
+ const batteryChargingEntity = findBatteryChargingEntity(
+ this.hass,
+ deviceEntityLookup[deviceId] || []
+ );
+ return batteryChargingEntity ? batteryChargingEntity.entity_id : undefined;
+ }
+
private _handleRowClicked(ev: HASSDomEvent) {
const deviceId = ev.detail.id;
navigate(this, `/config/devices/device/${deviceId}`);