diff --git a/pyproject.toml b/pyproject.toml
index 1432ca6257..7eb2cd6854 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project]
name = "home-assistant-frontend"
-version = "20220706.0"
+version = "20220707.0"
license = {text = "Apache-2.0"}
description = "The Home Assistant frontend"
readme = "README.md"
diff --git a/src/data/area_registry.ts b/src/data/area_registry.ts
index ffb3d4c153..c316785cfc 100644
--- a/src/data/area_registry.ts
+++ b/src/data/area_registry.ts
@@ -3,6 +3,8 @@ import { Store } from "home-assistant-js-websocket/dist/store";
import { stringCompare } from "../common/string/compare";
import { debounce } from "../common/util/debounce";
import { HomeAssistant } from "../types";
+import { DeviceRegistryEntry } from "./device_registry";
+import { EntityRegistryEntry } from "./entity_registry";
export interface AreaRegistryEntry {
area_id: string;
@@ -10,6 +12,14 @@ export interface AreaRegistryEntry {
picture: string | null;
}
+export interface AreaEntityLookup {
+ [areaId: string]: EntityRegistryEntry[];
+}
+
+export interface AreaDeviceLookup {
+ [areaId: string]: DeviceRegistryEntry[];
+}
+
export interface AreaRegistryEntryMutableParams {
name: string;
picture?: string | null;
@@ -79,3 +89,35 @@ export const subscribeAreaRegistry = (
conn,
onChange
);
+
+export const getAreaEntityLookup = (
+ entities: EntityRegistryEntry[]
+): AreaEntityLookup => {
+ const areaEntityLookup: AreaEntityLookup = {};
+ for (const entity of entities) {
+ if (!entity.area_id) {
+ continue;
+ }
+ if (!(entity.area_id in areaEntityLookup)) {
+ areaEntityLookup[entity.area_id] = [];
+ }
+ areaEntityLookup[entity.area_id].push(entity);
+ }
+ return areaEntityLookup;
+};
+
+export const getAreaDeviceLookup = (
+ devices: DeviceRegistryEntry[]
+): AreaDeviceLookup => {
+ const areaDeviceLookup: AreaDeviceLookup = {};
+ for (const device of devices) {
+ if (!device.area_id) {
+ continue;
+ }
+ if (!(device.area_id in areaDeviceLookup)) {
+ areaDeviceLookup[device.area_id] = [];
+ }
+ areaDeviceLookup[device.area_id].push(device);
+ }
+ return areaDeviceLookup;
+};
diff --git a/src/data/device_registry.ts b/src/data/device_registry.ts
index 6fb72404aa..63b050c856 100644
--- a/src/data/device_registry.ts
+++ b/src/data/device_registry.ts
@@ -126,3 +126,19 @@ export const sortDeviceRegistryByName = (entries: DeviceRegistryEntry[]) =>
entries.sort((entry1, entry2) =>
caseInsensitiveStringCompare(entry1.name || "", entry2.name || "")
);
+
+export const getDeviceEntityLookup = (
+ entities: EntityRegistryEntry[]
+): DeviceEntityLookup => {
+ const deviceEntityLookup: DeviceEntityLookup = {};
+ for (const entity of entities) {
+ if (!entity.device_id) {
+ continue;
+ }
+ if (!(entity.device_id in deviceEntityLookup)) {
+ deviceEntityLookup[entity.device_id] = [];
+ }
+ deviceEntityLookup[entity.device_id].push(entity);
+ }
+ return deviceEntityLookup;
+};
diff --git a/src/panels/config/devices/device-detail/integration-elements/zwave_js/device-actions.ts b/src/panels/config/devices/device-detail/integration-elements/zwave_js/device-actions.ts
index 180d33adb0..3ff12f5386 100644
--- a/src/panels/config/devices/device-detail/integration-elements/zwave_js/device-actions.ts
+++ b/src/panels/config/devices/device-detail/integration-elements/zwave_js/device-actions.ts
@@ -99,6 +99,13 @@ export const getZwaveDeviceActions = async (
),
action: async () => {
if (
+ isNodeFirmwareUpdateInProgress ||
+ (await fetchZwaveNodeIsFirmwareUpdateInProgress(hass, device.id))
+ ) {
+ showZWaveJUpdateFirmwareNodeDialog(el, {
+ device,
+ });
+ } else if (
await showConfirmationDialog(el, {
text: hass.localize(
"ui.panel.config.zwave_js.update_firmware.warning"
diff --git a/src/panels/history/ha-panel-history.ts b/src/panels/history/ha-panel-history.ts
index ec643ea537..8be17099f5 100644
--- a/src/panels/history/ha-panel-history.ts
+++ b/src/panels/history/ha-panel-history.ts
@@ -18,12 +18,10 @@ import { css, html, LitElement, PropertyValues } from "lit";
import { property, state } from "lit/decorators";
import { LocalStorage } from "../../common/decorators/local-storage";
import { ensureArray } from "../../common/ensure-array";
-import { computeDomain } from "../../common/entity/compute_domain";
-import { computeStateName } from "../../common/entity/compute_state_name";
import { navigate } from "../../common/navigate";
import {
createSearchParam,
- extractSearchParam,
+ extractSearchParamsObject,
} from "../../common/url/search-params";
import { computeRTL } from "../../common/util/compute_rtl";
import "../../components/chart/state-history-charts";
@@ -34,13 +32,17 @@ import "../../components/ha-icon-button";
import "../../components/ha-menu-button";
import "../../components/ha-target-picker";
import {
- DeviceRegistryEntry,
+ AreaDeviceLookup,
+ AreaEntityLookup,
+ getAreaDeviceLookup,
+ getAreaEntityLookup,
+} from "../../data/area_registry";
+import {
+ DeviceEntityLookup,
+ getDeviceEntityLookup,
subscribeDeviceRegistry,
} from "../../data/device_registry";
-import {
- EntityRegistryEntry,
- subscribeEntityRegistry,
-} from "../../data/entity_registry";
+import { subscribeEntityRegistry } from "../../data/entity_registry";
import { computeHistory, fetchDateWS } from "../../data/history";
import "../../layouts/ha-app-layout";
import { SubscribeMixin } from "../../mixins/subscribe-mixin";
@@ -67,23 +69,11 @@ class HaPanelHistory extends SubscribeMixin(LitElement) {
@state() private _ranges?: DateRangePickerRanges;
- @state() private _devices?: { [deviceId: string]: DeviceRegistryEntry };
+ @state() private _deviceEntityLookup?: DeviceEntityLookup;
- @state() private _entities?: { [entityId: string]: EntityRegistryEntry };
+ @state() private _areaEntityLookup?: AreaEntityLookup;
- @state() private _stateEntities?: { [entityId: string]: EntityRegistryEntry };
-
- @state() private _deviceIdToEntities?: {
- [deviceId: string]: EntityRegistryEntry[];
- };
-
- @state() private _areaIdToEntities?: {
- [areaId: string]: EntityRegistryEntry[];
- };
-
- @state() private _areaIdToDevices?: {
- [areaId: string]: DeviceRegistryEntry[];
- };
+ @state() private _areaDeviceLookup?: AreaDeviceLookup;
public constructor() {
super();
@@ -100,52 +90,11 @@ class HaPanelHistory extends SubscribeMixin(LitElement) {
public hassSubscribe(): UnsubscribeFunc[] {
return [
subscribeEntityRegistry(this.hass.connection!, (entities) => {
- this._entities = entities.reduce((accumulator, current) => {
- accumulator[current.entity_id] = current;
- return accumulator;
- }, {});
- this._deviceIdToEntities = entities.reduce((accumulator, current) => {
- if (!current.device_id) {
- return accumulator;
- }
- let found = accumulator[current.device_id];
- if (found === undefined) {
- found = [];
- accumulator[current.device_id] = found;
- }
- found.push(current);
- return accumulator;
- }, {});
- this._areaIdToEntities = entities.reduce((accumulator, current) => {
- if (!current.area_id) {
- return accumulator;
- }
- let found = accumulator[current.area_id];
- if (found === undefined) {
- found = [];
- accumulator[current.area_id] = found;
- }
- found.push(current);
- return accumulator;
- }, {});
+ this._deviceEntityLookup = getDeviceEntityLookup(entities);
+ this._areaEntityLookup = getAreaEntityLookup(entities);
}),
subscribeDeviceRegistry(this.hass.connection!, (devices) => {
- this._devices = devices.reduce((accumulator, current) => {
- accumulator[current.id] = current;
- return accumulator;
- }, {});
- this._areaIdToDevices = devices.reduce((accumulator, current) => {
- if (!current.area_id) {
- return accumulator;
- }
- let found = accumulator[current.area_id];
- if (found === undefined) {
- found = [];
- accumulator[current.area_id] = found;
- }
- found.push(current);
- return accumulator;
- }, {});
+ this._areaDeviceLookup = getAreaDeviceLookup(devices);
}),
];
}
@@ -172,7 +121,7 @@ class HaPanelHistory extends SubscribeMixin(LitElement) {
: ""}
@@ -246,9 +195,10 @@ class HaPanelHistory extends SubscribeMixin(LitElement) {
[addDays(weekStart, -7), addDays(weekEnd, -7)],
};
- const entityIds = extractSearchParam("entity_id");
- const deviceIds = extractSearchParam("device_id");
- const areaIds = extractSearchParam("area_id");
+ const searchParams = extractSearchParamsObject();
+ const entityIds = searchParams.entity_id;
+ const deviceIds = searchParams.device_id;
+ const areaIds = searchParams.area_id;
if (entityIds || deviceIds || areaIds) {
this._targetPickerValue = {};
}
@@ -265,11 +215,11 @@ class HaPanelHistory extends SubscribeMixin(LitElement) {
this._targetPickerValue!.area_id = splitIds;
}
- const startDate = extractSearchParam("start_date");
+ const startDate = searchParams.start_date;
if (startDate) {
this._startDate = new Date(startDate);
}
- const endDate = extractSearchParam("end_date");
+ const endDate = searchParams.end_date;
if (endDate) {
this._endDate = new Date(endDate);
}
@@ -282,12 +232,9 @@ class HaPanelHistory extends SubscribeMixin(LitElement) {
changedProps.has("_endDate") ||
changedProps.has("_targetPickerValue") ||
(!this._stateHistory &&
- (changedProps.has("_entities") ||
- changedProps.has("_devices") ||
- changedProps.has("_stateEntities") ||
- changedProps.has("_deviceIdToEntities") ||
- changedProps.has("_areaIdToEntities") ||
- changedProps.has("_areaIdToDevices"))))
+ (changedProps.has("_deviceEntityLookup") ||
+ changedProps.has("_areaEntityLookup") ||
+ changedProps.has("_areaDeviceLookup"))))
) {
this._getHistory();
}
@@ -300,29 +247,6 @@ class HaPanelHistory extends SubscribeMixin(LitElement) {
if (!oldHass || oldHass.language !== this.hass.language) {
this.rtl = computeRTL(this.hass);
}
-
- if (this._entities) {
- const stateEntities: { [entityId: string]: EntityRegistryEntry } = {};
- const regEntityIds = new Set(Object.keys(this._entities));
- for (const entityId of Object.keys(this.hass.states)) {
- if (regEntityIds.has(entityId)) {
- continue;
- }
- stateEntities[entityId] = {
- name: computeStateName(this.hass.states[entityId]),
- entity_id: entityId,
- platform: computeDomain(entityId),
- disabled_by: null,
- hidden_by: null,
- area_id: null,
- config_entry_id: null,
- device_id: null,
- icon: null,
- entity_category: null,
- };
- }
- this._stateEntities = stateEntities;
- }
}
private _removeAll() {
@@ -331,10 +255,14 @@ class HaPanelHistory extends SubscribeMixin(LitElement) {
}
private async _getHistory() {
+ if (!this._targetPickerValue) {
+ return;
+ }
this._isLoading = true;
const entityIds = this._getEntityIds();
if (entityIds === undefined) {
+ this._isLoading = false;
this._stateHistory = undefined;
return;
}
@@ -344,34 +272,34 @@ class HaPanelHistory extends SubscribeMixin(LitElement) {
this._stateHistory = [];
return;
}
+ try {
+ const dateHistory = await fetchDateWS(
+ this.hass,
+ this._startDate,
+ this._endDate,
+ entityIds
+ );
- const dateHistory = await fetchDateWS(
- this.hass,
- this._startDate,
- this._endDate,
- entityIds
- );
-
- this._stateHistory = computeHistory(
- this.hass,
- dateHistory,
- this.hass.localize
- );
- this._isLoading = false;
+ this._stateHistory = computeHistory(
+ this.hass,
+ dateHistory,
+ this.hass.localize
+ );
+ } finally {
+ this._isLoading = false;
+ }
}
private _getEntityIds(): string[] | undefined {
if (
!this._targetPickerValue ||
- this._entities === undefined ||
- this._stateEntities === undefined ||
- this._devices === undefined ||
- this._deviceIdToEntities === undefined ||
- this._areaIdToEntities === undefined ||
- this._areaIdToDevices === undefined
+ this._deviceEntityLookup === undefined ||
+ this._areaEntityLookup === undefined ||
+ this._areaDeviceLookup === undefined
) {
return undefined;
}
+
const entityIds = new Set();
let {
area_id: searchingAreaId,
@@ -382,7 +310,7 @@ class HaPanelHistory extends SubscribeMixin(LitElement) {
if (searchingAreaId) {
searchingAreaId = ensureArray(searchingAreaId);
for (const singleSearchingAreaId of searchingAreaId) {
- const foundEntities = this._areaIdToEntities[singleSearchingAreaId];
+ const foundEntities = this._areaEntityLookup[singleSearchingAreaId];
if (foundEntities?.length) {
for (const foundEntity of foundEntities) {
if (foundEntity.entity_category === null) {
@@ -391,19 +319,24 @@ class HaPanelHistory extends SubscribeMixin(LitElement) {
}
}
- const foundDevices = this._areaIdToDevices[singleSearchingAreaId];
- if (foundDevices) {
- for (const foundDevice of foundDevices) {
- const foundDeviceEntities =
- this._deviceIdToEntities[foundDevice.id];
- for (const foundDeviceEntity of foundDeviceEntities) {
- if (
- (!foundDeviceEntity.area_id ||
- foundDeviceEntity.area_id === singleSearchingAreaId) &&
- foundDeviceEntity.entity_category === null
- ) {
- entityIds.add(foundDeviceEntity.entity_id);
- }
+ const foundDevices = this._areaDeviceLookup[singleSearchingAreaId];
+ if (!foundDevices?.length) {
+ continue;
+ }
+
+ for (const foundDevice of foundDevices) {
+ const foundDeviceEntities = this._deviceEntityLookup[foundDevice.id];
+ if (!foundDeviceEntities?.length) {
+ continue;
+ }
+
+ for (const foundDeviceEntity of foundDeviceEntities) {
+ if (
+ (!foundDeviceEntity.area_id ||
+ foundDeviceEntity.area_id === singleSearchingAreaId) &&
+ foundDeviceEntity.entity_category === null
+ ) {
+ entityIds.add(foundDeviceEntity.entity_id);
}
}
}
@@ -413,18 +346,20 @@ class HaPanelHistory extends SubscribeMixin(LitElement) {
if (searchingDeviceId) {
searchingDeviceId = ensureArray(searchingDeviceId);
for (const singleSearchingDeviceId of searchingDeviceId) {
- const foundEntities = this._deviceIdToEntities[singleSearchingDeviceId];
- if (foundEntities?.length) {
- for (const foundEntity of foundEntities) {
- if (foundEntity.entity_category === null) {
- entityIds.add(foundEntity.entity_id);
- }
+ const foundEntities = this._deviceEntityLookup[singleSearchingDeviceId];
+ if (!foundEntities?.length) {
+ continue;
+ }
+
+ for (const foundEntity of foundEntities) {
+ if (foundEntity.entity_category === null) {
+ entityIds.add(foundEntity.entity_id);
}
}
}
}
- if (searchingEntityId !== undefined) {
+ if (searchingEntityId) {
searchingEntityId = ensureArray(searchingEntityId);
for (const singleSearchingEntityId of searchingEntityId) {
entityIds.add(singleSearchingEntityId);