diff --git a/setup.py b/setup.py index 540e09e98a..a6fa1bada9 100644 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ from setuptools import setup, find_packages setup( name="home-assistant-frontend", - version="20211028.0", + version="20211103.0", description="The Home Assistant frontend", url="https://github.com/home-assistant/frontend", author="The Home Assistant Authors", diff --git a/src/common/entity/compute_state_display.ts b/src/common/entity/compute_state_display.ts index fd99a022da..1836d5d6a5 100644 --- a/src/common/entity/compute_state_display.ts +++ b/src/common/entity/compute_state_display.ts @@ -4,7 +4,7 @@ import { FrontendLocaleData } from "../../data/translation"; import { formatDate } from "../datetime/format_date"; import { formatDateTime } from "../datetime/format_date_time"; import { formatTime } from "../datetime/format_time"; -import { formatNumber } from "../number/format_number"; +import { formatNumber, isNumericState } from "../number/format_number"; import { LocalizeFunc } from "../translations/localize"; import { computeStateDomain } from "./compute_state_domain"; @@ -20,7 +20,8 @@ export const computeStateDisplay = ( return localize(`state.default.${compareState}`); } - if (stateObj.attributes.unit_of_measurement) { + // Entities with a `unit_of_measurement` or `state_class` are numeric values and should use `formatNumber` + if (isNumericState(stateObj)) { if (stateObj.attributes.device_class === "monetary") { try { return formatNumber(compareState, locale, { @@ -31,8 +32,10 @@ export const computeStateDisplay = ( // fallback to default } } - return `${formatNumber(compareState, locale)} ${ + return `${formatNumber(compareState, locale)}${ stateObj.attributes.unit_of_measurement + ? " " + stateObj.attributes.unit_of_measurement + : "" }`; } diff --git a/src/common/number/format_number.ts b/src/common/number/format_number.ts index 89ad6da7a6..7cecd1c514 100644 --- a/src/common/number/format_number.ts +++ b/src/common/number/format_number.ts @@ -1,6 +1,15 @@ +import { HassEntity } from "home-assistant-js-websocket"; import { FrontendLocaleData, NumberFormat } from "../../data/translation"; import { round } from "./round"; +/** + * Returns true if the entity is considered numeric based on the attributes it has + * @param stateObj The entity state object + */ +export const isNumericState = (stateObj: HassEntity): boolean => + !!stateObj.attributes.unit_of_measurement || + !!stateObj.attributes.state_class; + export const numberFormatToLocale = ( localeOptions: FrontendLocaleData ): string | string[] | undefined => { diff --git a/src/components/entity/ha-state-label-badge.ts b/src/components/entity/ha-state-label-badge.ts index 1bdc3b2ea4..19ea56dbcf 100644 --- a/src/components/entity/ha-state-label-badge.ts +++ b/src/components/entity/ha-state-label-badge.ts @@ -14,7 +14,10 @@ import secondsToDuration from "../../common/datetime/seconds_to_duration"; import { computeStateDisplay } from "../../common/entity/compute_state_display"; import { computeStateDomain } from "../../common/entity/compute_state_domain"; import { computeStateName } from "../../common/entity/compute_state_name"; -import { formatNumber } from "../../common/number/format_number"; +import { + formatNumber, + isNumericState, +} from "../../common/number/format_number"; import { UNAVAILABLE, UNKNOWN } from "../../data/entity"; import { timerTimeRemaining } from "../../data/timer"; import { HomeAssistant } from "../../types"; @@ -145,7 +148,7 @@ export class HaStateLabelBadge extends LitElement { return entityState.state === UNKNOWN || entityState.state === UNAVAILABLE ? "-" - : entityState.attributes.unit_of_measurement + : isNumericState(entityState) ? formatNumber(entityState.state, this.hass!.locale) : computeStateDisplay( this.hass!.localize, diff --git a/src/components/ha-dialog.ts b/src/components/ha-dialog.ts index 0b1633845d..8c7c0a36d2 100644 --- a/src/components/ha-dialog.ts +++ b/src/components/ha-dialog.ts @@ -38,6 +38,7 @@ export class HaDialog extends Dialog { .mdc-dialog { --mdc-dialog-scroll-divider-color: var(--divider-color); z-index: var(--dialog-z-index, 7); + -webkit-backdrop-filter: var(--dialog-backdrop-filter, none); backdrop-filter: var(--dialog-backdrop-filter, none); } .mdc-dialog__actions { diff --git a/src/dialogs/more-info/controls/more-info-camera.ts b/src/dialogs/more-info/controls/more-info-camera.ts index 65f2459f71..64d07826a2 100644 --- a/src/dialogs/more-info/controls/more-info-camera.ts +++ b/src/dialogs/more-info/controls/more-info-camera.ts @@ -10,7 +10,8 @@ import { property, state } from "lit/decorators"; import { isComponentLoaded } from "../../../common/config/is_component_loaded"; import { supportsFeature } from "../../../common/entity/supports-feature"; import "../../../components/ha-camera-stream"; -import { HaCheckbox } from "../../../components/ha-checkbox"; +import type { HaCheckbox } from "../../../components/ha-checkbox"; +import "../../../components/ha-checkbox"; import { CameraEntity, CameraPreferences, diff --git a/src/dialogs/more-info/controls/more-info-light.ts b/src/dialogs/more-info/controls/more-info-light.ts index 023b4cf0a8..cc656c8e60 100644 --- a/src/dialogs/more-info/controls/more-info-light.ts +++ b/src/dialogs/more-info/controls/more-info-light.ts @@ -209,6 +209,7 @@ class MoreInfoLight extends LitElement { ? html`
-
${entry.stateName || entry.entity_id}
+
+ ${entry.stateName + ? stripPrefixFromEntityName( + entry.stateName, + `${this.deviceName} `.toLowerCase() + ) + : entry.entity_id} +
`; diff --git a/src/panels/config/devices/ha-config-device-page.ts b/src/panels/config/devices/ha-config-device-page.ts index 5a2d375d53..386e7efe14 100644 --- a/src/panels/config/devices/ha-config-device-page.ts +++ b/src/panels/config/devices/ha-config-device-page.ts @@ -121,7 +121,13 @@ export class HaConfigDevicePage extends LitElement { const result = groupBy(entities, (entry) => entry.entity_category ? entry.entity_category - : ["sensor", "binary_sensor"].includes(computeDomain(entry.entity_id)) + : [ + "sensor", + "binary_sensor", + "camera", + "device_tracker", + "weather", + ].includes(computeDomain(entry.entity_id)) ? "sensor" : "control" ) as Record< diff --git a/src/panels/lovelace/cards/hui-entity-card.ts b/src/panels/lovelace/cards/hui-entity-card.ts index 6e3e3d4728..890f95dec2 100644 --- a/src/panels/lovelace/cards/hui-entity-card.ts +++ b/src/panels/lovelace/cards/hui-entity-card.ts @@ -15,7 +15,10 @@ import { computeStateDisplay } from "../../../common/entity/compute_state_displa import { computeStateDomain } from "../../../common/entity/compute_state_domain"; import { computeStateName } from "../../../common/entity/compute_state_name"; import { isValidEntityId } from "../../../common/entity/valid_entity_id"; -import { formatNumber } from "../../../common/number/format_number"; +import { + formatNumber, + isNumericState, +} from "../../../common/number/format_number"; import { iconColorCSS } from "../../../common/style/icon_color_css"; import "../../../components/ha-card"; import "../../../components/ha-icon"; @@ -143,7 +146,7 @@ export class HuiEntityCard extends LitElement implements LovelaceCard { stateObj.attributes[this._config.attribute!] ) : this.hass.localize("state.default.unknown") - : stateObj.attributes.unit_of_measurement + : isNumericState(stateObj) ? formatNumber(stateObj.state, this.hass.locale) : computeStateDisplay( this.hass.localize, diff --git a/src/panels/lovelace/cards/hui-plant-status-card.ts b/src/panels/lovelace/cards/hui-plant-status-card.ts index 010388ccf0..507d82f10a 100644 --- a/src/panels/lovelace/cards/hui-plant-status-card.ts +++ b/src/panels/lovelace/cards/hui-plant-status-card.ts @@ -1,7 +1,7 @@ import { - mdiEmoticonPoop, + mdiSprout, mdiThermometer, - mdiWater, + mdiWaterPercent, mdiWhiteBalanceSunny, } from "@mdi/js"; import { HassEntity } from "home-assistant-js-websocket"; @@ -29,10 +29,10 @@ import { LovelaceCard, LovelaceCardEditor } from "../types"; import { PlantAttributeTarget, PlantStatusCardConfig } from "./types"; const SENSOR_ICONS = { - moisture: mdiWater, + moisture: mdiWaterPercent, temperature: mdiThermometer, brightness: mdiWhiteBalanceSunny, - conductivity: mdiEmoticonPoop, + conductivity: mdiSprout, battery: undefined, }; diff --git a/test/common/entity/compute_state_display.ts b/test/common/entity/compute_state_display.ts index 0ac83da339..4d9e2cca67 100644 --- a/test/common/entity/compute_state_display.ts +++ b/test/common/entity/compute_state_display.ts @@ -96,6 +96,20 @@ describe("computeStateDisplay", () => { ); }); + it("Localizes and formats numeric sensor value with state_class", () => { + const stateObj: any = { + entity_id: "sensor.test", + state: "1234.5", + attributes: { + state_class: "measurement", + }, + }; + assert.strictEqual( + computeStateDisplay(localize, stateObj, localeData), + "1,234.5 m" + ); + }); + it("Localizes unknown sensor value with units", () => { const altLocalize = (message, ...args) => { if (message === "state.sensor.unknown") {