Use sensor device class for graph and precision (#18099)
Co-authored-by: Bram Kragten <mail@bramkragten.nl>
This commit is contained in:
parent
c6be4d6f4d
commit
aeaf091b50
|
@ -395,16 +395,28 @@ const processLineChartEntities = (
|
|||
};
|
||||
};
|
||||
|
||||
const stateUsesUnits = (state: HassEntity) =>
|
||||
attributesHaveUnits(state.attributes);
|
||||
const NUMERICAL_DOMAINS = ["counter", "input_number", "number"];
|
||||
|
||||
const attributesHaveUnits = (attributes: { [key: string]: any }) =>
|
||||
const isNumericFromDomain = (domain: string) =>
|
||||
NUMERICAL_DOMAINS.includes(domain);
|
||||
|
||||
const isNumericFromAttributes = (attributes: { [key: string]: any }) =>
|
||||
"unit_of_measurement" in attributes || "state_class" in attributes;
|
||||
|
||||
const isNumericSensorEntity = (
|
||||
stateObj: HassEntity,
|
||||
sensorNumericalDeviceClasses: string[]
|
||||
) =>
|
||||
stateObj.attributes.device_class != null &&
|
||||
sensorNumericalDeviceClasses.includes(stateObj.attributes.device_class);
|
||||
|
||||
const BLANK_UNIT = " ";
|
||||
|
||||
export const computeHistory = (
|
||||
hass: HomeAssistant,
|
||||
stateHistory: HistoryStates,
|
||||
localize: LocalizeFunc
|
||||
localize: LocalizeFunc,
|
||||
sensorNumericalDeviceClasses: string[]
|
||||
): HistoryResult => {
|
||||
const lineChartDevices: { [unit: string]: HistoryStates } = {};
|
||||
const timelineDevices: TimelineEntity[] = [];
|
||||
|
@ -417,28 +429,40 @@ export const computeHistory = (
|
|||
return;
|
||||
}
|
||||
|
||||
const domain = computeDomain(entityId);
|
||||
|
||||
const currentState =
|
||||
entityId in hass.states ? hass.states[entityId] : undefined;
|
||||
const stateWithUnitorStateClass =
|
||||
!currentState &&
|
||||
stateInfo.find((state) => state.a && attributesHaveUnits(state.a));
|
||||
const numericStateFromHistory =
|
||||
currentState || isNumericFromDomain(domain)
|
||||
? undefined
|
||||
: stateInfo.find(
|
||||
(state) => state.a && isNumericFromAttributes(state.a)
|
||||
);
|
||||
|
||||
let unit: string | undefined;
|
||||
|
||||
if (currentState && stateUsesUnits(currentState)) {
|
||||
unit = currentState.attributes.unit_of_measurement || " ";
|
||||
} else if (stateWithUnitorStateClass) {
|
||||
unit = stateWithUnitorStateClass.a.unit_of_measurement || " ";
|
||||
const isNumeric =
|
||||
isNumericFromDomain(domain) ||
|
||||
(currentState != null &&
|
||||
isNumericFromAttributes(currentState.attributes)) ||
|
||||
(currentState != null &&
|
||||
domain === "sensor" &&
|
||||
isNumericSensorEntity(currentState, sensorNumericalDeviceClasses)) ||
|
||||
numericStateFromHistory != null;
|
||||
|
||||
if (isNumeric) {
|
||||
unit =
|
||||
currentState?.attributes.unit_of_measurement ||
|
||||
numericStateFromHistory?.a.unit_of_measurement ||
|
||||
BLANK_UNIT;
|
||||
} else {
|
||||
unit = {
|
||||
zone: localize("ui.dialogs.more_info_control.zone.graph_unit"),
|
||||
climate: hass.config.unit_system.temperature,
|
||||
counter: "#",
|
||||
humidifier: "%",
|
||||
input_number: "#",
|
||||
number: "#",
|
||||
water_heater: hass.config.unit_system.temperature,
|
||||
}[computeDomain(entityId)];
|
||||
}[domain];
|
||||
}
|
||||
|
||||
if (!unit) {
|
||||
|
|
|
@ -13,3 +13,21 @@ export const getSensorDeviceClassConvertibleUnits = (
|
|||
type: "sensor/device_class_convertible_units",
|
||||
device_class: deviceClass,
|
||||
});
|
||||
|
||||
export type SensorNumericDeviceClasses = {
|
||||
numeric_device_classes: string[];
|
||||
};
|
||||
|
||||
let sensorNumericDeviceClassesCache: SensorNumericDeviceClasses | undefined;
|
||||
|
||||
export const getSensorNumericDeviceClasses = async (
|
||||
hass: HomeAssistant
|
||||
): Promise<SensorNumericDeviceClasses> => {
|
||||
if (sensorNumericDeviceClassesCache) {
|
||||
return sensorNumericDeviceClassesCache;
|
||||
}
|
||||
sensorNumericDeviceClassesCache = await hass.callWS({
|
||||
type: "sensor/numeric_device_classes",
|
||||
});
|
||||
return sensorNumericDeviceClassesCache!;
|
||||
};
|
||||
|
|
|
@ -1,28 +1,29 @@
|
|||
import { startOfYesterday, subHours } from "date-fns/esm";
|
||||
import { css, html, LitElement, PropertyValues, nothing } from "lit";
|
||||
import { customElement, property, state, query } from "lit/decorators";
|
||||
import { LitElement, PropertyValues, css, html, nothing } from "lit";
|
||||
import { customElement, property, query, state } from "lit/decorators";
|
||||
import { isComponentLoaded } from "../../common/config/is_component_loaded";
|
||||
import { fireEvent } from "../../common/dom/fire_event";
|
||||
import { computeDomain } from "../../common/entity/compute_domain";
|
||||
import { createSearchParam } from "../../common/url/search-params";
|
||||
import { ChartResizeOptions } from "../../components/chart/ha-chart-base";
|
||||
import "../../components/chart/state-history-charts";
|
||||
import type { StateHistoryCharts } from "../../components/chart/state-history-charts";
|
||||
import "../../components/chart/statistics-chart";
|
||||
import type { StatisticsChart } from "../../components/chart/statistics-chart";
|
||||
import {
|
||||
computeHistory,
|
||||
HistoryResult,
|
||||
computeHistory,
|
||||
subscribeHistoryStatesTimeWindow,
|
||||
} from "../../data/history";
|
||||
import {
|
||||
fetchStatistics,
|
||||
getStatisticMetadata,
|
||||
Statistics,
|
||||
StatisticsMetaData,
|
||||
StatisticsTypes,
|
||||
fetchStatistics,
|
||||
getStatisticMetadata,
|
||||
} from "../../data/recorder";
|
||||
import { getSensorNumericDeviceClasses } from "../../data/sensor";
|
||||
import { HomeAssistant } from "../../types";
|
||||
import type { StatisticsChart } from "../../components/chart/statistics-chart";
|
||||
import { ChartResizeOptions } from "../../components/chart/ha-chart-base";
|
||||
|
||||
declare global {
|
||||
interface HASSDomEvents {
|
||||
|
@ -213,6 +214,10 @@ export class MoreInfoHistory extends LitElement {
|
|||
if (this._subscribed) {
|
||||
this._unsubscribeHistory();
|
||||
}
|
||||
|
||||
const { numeric_device_classes: sensorNumericDeviceClasses } =
|
||||
await getSensorNumericDeviceClasses(this.hass);
|
||||
|
||||
this._subscribed = subscribeHistoryStatesTimeWindow(
|
||||
this.hass!,
|
||||
(combinedHistory) => {
|
||||
|
@ -223,7 +228,8 @@ export class MoreInfoHistory extends LitElement {
|
|||
this._stateHistory = computeHistory(
|
||||
this.hass!,
|
||||
combinedHistory,
|
||||
this.hass!.localize
|
||||
this.hass!.localize,
|
||||
sensorNumericDeviceClasses
|
||||
);
|
||||
},
|
||||
24,
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import "@material/mwc-button/mwc-button";
|
||||
import "@material/mwc-formfield/mwc-formfield";
|
||||
import { mdiContentCopy } from "@mdi/js";
|
||||
import { HassEntity } from "home-assistant-js-websocket";
|
||||
import {
|
||||
css,
|
||||
|
@ -11,7 +12,6 @@ import {
|
|||
} from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import memoizeOne from "memoize-one";
|
||||
import { mdiContentCopy } from "@mdi/js";
|
||||
import { isComponentLoaded } from "../../../common/config/is_component_loaded";
|
||||
import { fireEvent } from "../../../common/dom/fire_event";
|
||||
import { stopPropagation } from "../../../common/dom/stop_propagation";
|
||||
|
@ -25,6 +25,7 @@ import {
|
|||
LocalizeFunc,
|
||||
LocalizeKeys,
|
||||
} from "../../../common/translations/localize";
|
||||
import { copyToClipboard } from "../../../common/util/copy-clipboard";
|
||||
import "../../../components/ha-alert";
|
||||
import "../../../components/ha-area-picker";
|
||||
import "../../../components/ha-icon";
|
||||
|
@ -38,9 +39,9 @@ import "../../../components/ha-switch";
|
|||
import type { HaSwitch } from "../../../components/ha-switch";
|
||||
import "../../../components/ha-textfield";
|
||||
import {
|
||||
CameraPreferences,
|
||||
CAMERA_ORIENTATIONS,
|
||||
CAMERA_SUPPORT_STREAM,
|
||||
CameraPreferences,
|
||||
fetchCameraPrefs,
|
||||
STREAM_TYPE_HLS,
|
||||
updateCameraPrefs,
|
||||
|
@ -66,7 +67,10 @@ import {
|
|||
} from "../../../data/entity_registry";
|
||||
import { domainToName } from "../../../data/integration";
|
||||
import { getNumberDeviceClassConvertibleUnits } from "../../../data/number";
|
||||
import { getSensorDeviceClassConvertibleUnits } from "../../../data/sensor";
|
||||
import {
|
||||
getSensorDeviceClassConvertibleUnits,
|
||||
getSensorNumericDeviceClasses,
|
||||
} from "../../../data/sensor";
|
||||
import {
|
||||
getWeatherConvertibleUnits,
|
||||
WeatherUnits,
|
||||
|
@ -80,9 +84,8 @@ import { showVoiceAssistantsView } from "../../../dialogs/more-info/components/v
|
|||
import { showMoreInfoDialog } from "../../../dialogs/more-info/show-ha-more-info-dialog";
|
||||
import { haStyle } from "../../../resources/styles";
|
||||
import type { HomeAssistant } from "../../../types";
|
||||
import { showDeviceRegistryDetailDialog } from "../devices/device-registry-detail/show-dialog-device-registry-detail";
|
||||
import { copyToClipboard } from "../../../common/util/copy-clipboard";
|
||||
import { showToast } from "../../../util/toast";
|
||||
import { showDeviceRegistryDetailDialog } from "../devices/device-registry-detail/show-dialog-device-registry-detail";
|
||||
|
||||
const OVERRIDE_DEVICE_CLASSES = {
|
||||
cover: [
|
||||
|
@ -174,6 +177,8 @@ export class EntityRegistrySettingsEditor extends LitElement {
|
|||
|
||||
@state() private _sensorDeviceClassConvertibleUnits?: string[];
|
||||
|
||||
@state() private _sensorNumericalDeviceClasses?: string[];
|
||||
|
||||
@state() private _weatherConvertibleUnits?: WeatherUnits;
|
||||
|
||||
@state() private _defaultCode?: string | null;
|
||||
|
@ -195,8 +200,6 @@ export class EntityRegistrySettingsEditor extends LitElement {
|
|||
|
||||
this._name = this.entry.name || "";
|
||||
this._icon = this.entry.icon || "";
|
||||
this._deviceClass =
|
||||
this.entry.device_class || this.entry.original_device_class;
|
||||
this._origEntityId = this.entry.entity_id;
|
||||
this._areaId = this.entry.area_id;
|
||||
this._entityId = this.entry.entity_id;
|
||||
|
@ -294,6 +297,14 @@ export class EntityRegistrySettingsEditor extends LitElement {
|
|||
} else {
|
||||
this._numberDeviceClassConvertibleUnits = [];
|
||||
}
|
||||
if (domain === "sensor") {
|
||||
const { numeric_device_classes } = await getSensorNumericDeviceClasses(
|
||||
this.hass
|
||||
);
|
||||
this._sensorNumericalDeviceClasses = numeric_device_classes;
|
||||
} else {
|
||||
this._sensorNumericalDeviceClasses = [];
|
||||
}
|
||||
if (domain === "sensor" && this._deviceClass) {
|
||||
const { units } = await getSensorDeviceClassConvertibleUnits(
|
||||
this.hass,
|
||||
|
@ -558,7 +569,7 @@ export class EntityRegistrySettingsEditor extends LitElement {
|
|||
// Allow customizing the precision for a sensor with numerical device class,
|
||||
// a unit of measurement or state class
|
||||
((this._deviceClass &&
|
||||
!["date", "enum", "timestamp"].includes(this._deviceClass)) ||
|
||||
this._sensorNumericalDeviceClasses?.includes(this._deviceClass)) ||
|
||||
stateObj?.attributes.unit_of_measurement ||
|
||||
stateObj?.attributes.state_class)
|
||||
? html`
|
||||
|
|
|
@ -4,7 +4,7 @@ import {
|
|||
HassServiceTarget,
|
||||
UnsubscribeFunc,
|
||||
} from "home-assistant-js-websocket/dist/types";
|
||||
import { css, html, LitElement, PropertyValues } from "lit";
|
||||
import { LitElement, PropertyValues, css, html } from "lit";
|
||||
import { property, query, state } from "lit/decorators";
|
||||
import { ensureArray } from "../../common/array/ensure-array";
|
||||
import { storage } from "../../common/decorators/storage";
|
||||
|
@ -39,10 +39,11 @@ import {
|
|||
} from "../../data/device_registry";
|
||||
import { subscribeEntityRegistry } from "../../data/entity_registry";
|
||||
import {
|
||||
computeHistory,
|
||||
HistoryResult,
|
||||
computeHistory,
|
||||
subscribeHistory,
|
||||
} from "../../data/history";
|
||||
import { getSensorNumericDeviceClasses } from "../../data/sensor";
|
||||
import { SubscribeMixin } from "../../mixins/subscribe-mixin";
|
||||
import { haStyle } from "../../resources/styles";
|
||||
import { HomeAssistant } from "../../types";
|
||||
|
@ -306,6 +307,9 @@ class HaPanelHistory extends SubscribeMixin(LitElement) {
|
|||
|
||||
const now = new Date();
|
||||
|
||||
const { numeric_device_classes: sensorNumericDeviceClasses } =
|
||||
await getSensorNumericDeviceClasses(this.hass);
|
||||
|
||||
this._subscribed = subscribeHistory(
|
||||
this.hass,
|
||||
(history) => {
|
||||
|
@ -313,7 +317,8 @@ class HaPanelHistory extends SubscribeMixin(LitElement) {
|
|||
this._stateHistory = computeHistory(
|
||||
this.hass,
|
||||
history,
|
||||
this.hass.localize
|
||||
this.hass.localize,
|
||||
sensorNumericDeviceClasses
|
||||
);
|
||||
},
|
||||
this._startDate,
|
||||
|
|
|
@ -1,22 +1,23 @@
|
|||
import {
|
||||
css,
|
||||
CSSResultGroup,
|
||||
html,
|
||||
LitElement,
|
||||
PropertyValues,
|
||||
css,
|
||||
html,
|
||||
nothing,
|
||||
} from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { classMap } from "lit/directives/class-map";
|
||||
import { isComponentLoaded } from "../../../common/config/is_component_loaded";
|
||||
import "../../../components/chart/state-history-charts";
|
||||
import "../../../components/ha-card";
|
||||
import "../../../components/ha-alert";
|
||||
import "../../../components/ha-card";
|
||||
import {
|
||||
computeHistory,
|
||||
HistoryResult,
|
||||
computeHistory,
|
||||
subscribeHistoryStatesTimeWindow,
|
||||
} from "../../../data/history";
|
||||
import { getSensorNumericDeviceClasses } from "../../../data/sensor";
|
||||
import { HomeAssistant } from "../../../types";
|
||||
import { hasConfigOrEntitiesChanged } from "../common/has-changed";
|
||||
import { processConfigEntities } from "../common/process-config-entities";
|
||||
|
@ -97,10 +98,14 @@ export class HuiHistoryGraphCard extends LitElement implements LovelaceCard {
|
|||
this._unsubscribeHistory();
|
||||
}
|
||||
|
||||
private _subscribeHistory() {
|
||||
private async _subscribeHistory() {
|
||||
if (!isComponentLoaded(this.hass!, "history") || this._subscribed) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { numeric_device_classes: sensorNumericDeviceClasses } =
|
||||
await getSensorNumericDeviceClasses(this.hass!);
|
||||
|
||||
this._subscribed = subscribeHistoryStatesTimeWindow(
|
||||
this.hass!,
|
||||
(combinedHistory) => {
|
||||
|
@ -108,10 +113,12 @@ export class HuiHistoryGraphCard extends LitElement implements LovelaceCard {
|
|||
// Message came in before we had a chance to unload
|
||||
return;
|
||||
}
|
||||
|
||||
this._stateHistory = computeHistory(
|
||||
this.hass!,
|
||||
combinedHistory,
|
||||
this.hass!.localize
|
||||
this.hass!.localize,
|
||||
sensorNumericDeviceClasses
|
||||
);
|
||||
},
|
||||
this._hoursToShow,
|
||||
|
|
Loading…
Reference in New Issue