mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-19 15:26:36 +00:00
Use areas when generating Lovelace config (#3175)
* Use areas when generating Lovelace config * Add missing return type
This commit is contained in:
parent
89a35a0062
commit
32e68c1a4b
@ -75,7 +75,7 @@
|
|||||||
"es6-object-assign": "^1.1.0",
|
"es6-object-assign": "^1.1.0",
|
||||||
"fecha": "^3.0.2",
|
"fecha": "^3.0.2",
|
||||||
"hls.js": "^0.12.4",
|
"hls.js": "^0.12.4",
|
||||||
"home-assistant-js-websocket": "^4.1.1",
|
"home-assistant-js-websocket": "^4.1.2",
|
||||||
"intl-messageformat": "^2.2.0",
|
"intl-messageformat": "^2.2.0",
|
||||||
"jquery": "^3.3.1",
|
"jquery": "^3.3.1",
|
||||||
"js-yaml": "^3.13.0",
|
"js-yaml": "^3.13.0",
|
||||||
|
16
src/common/util/subscribe-one.ts
Normal file
16
src/common/util/subscribe-one.ts
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
import { HomeAssistant } from "../../types";
|
||||||
|
import { UnsubscribeFunc } from "home-assistant-js-websocket";
|
||||||
|
|
||||||
|
export const subscribeOne = async <T>(
|
||||||
|
hass: HomeAssistant,
|
||||||
|
subscribe: (
|
||||||
|
hass: HomeAssistant,
|
||||||
|
onChange: (items: T) => void
|
||||||
|
) => UnsubscribeFunc
|
||||||
|
) =>
|
||||||
|
new Promise<T>((resolve) => {
|
||||||
|
const unsub = subscribe(hass, (items) => {
|
||||||
|
unsub();
|
||||||
|
resolve(items);
|
||||||
|
});
|
||||||
|
});
|
@ -17,6 +17,19 @@ import computeDomain from "../../../common/entity/compute_domain";
|
|||||||
import { EntityRowConfig, WeblinkConfig } from "../entity-rows/types";
|
import { EntityRowConfig, WeblinkConfig } from "../entity-rows/types";
|
||||||
import { LocalizeFunc } from "../../../common/translations/localize";
|
import { LocalizeFunc } from "../../../common/translations/localize";
|
||||||
import { EntitiesCardConfig } from "../cards/types";
|
import { EntitiesCardConfig } from "../cards/types";
|
||||||
|
import {
|
||||||
|
subscribeAreaRegistry,
|
||||||
|
AreaRegistryEntry,
|
||||||
|
} from "../../../data/area_registry";
|
||||||
|
import { subscribeOne } from "../../../common/util/subscribe-one";
|
||||||
|
import {
|
||||||
|
subscribeDeviceRegistry,
|
||||||
|
DeviceRegistryEntry,
|
||||||
|
} from "../../../data/device_registry";
|
||||||
|
import {
|
||||||
|
subscribeEntityRegistry,
|
||||||
|
EntityRegistryEntry,
|
||||||
|
} from "../../../data/entity_registry";
|
||||||
|
|
||||||
const DEFAULT_VIEW_ENTITY_ID = "group.default_view";
|
const DEFAULT_VIEW_ENTITY_ID = "group.default_view";
|
||||||
const DOMAINS_BADGES = [
|
const DOMAINS_BADGES = [
|
||||||
@ -34,6 +47,55 @@ const HIDE_DOMAIN = new Set([
|
|||||||
"geo_location",
|
"geo_location",
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
interface Registries {
|
||||||
|
areas: AreaRegistryEntry[];
|
||||||
|
devices: DeviceRegistryEntry[];
|
||||||
|
entities: EntityRegistryEntry[];
|
||||||
|
}
|
||||||
|
|
||||||
|
let subscribedRegistries = false;
|
||||||
|
|
||||||
|
interface SplittedByAreas {
|
||||||
|
areasWithEntities: Array<[AreaRegistryEntry, HassEntity[]]>;
|
||||||
|
otherEntities: HassEntities;
|
||||||
|
}
|
||||||
|
|
||||||
|
const splitByAreas = (
|
||||||
|
registries: Registries,
|
||||||
|
entities: HassEntities
|
||||||
|
): SplittedByAreas => {
|
||||||
|
const allEntities = { ...entities };
|
||||||
|
const areasWithEntities: SplittedByAreas["areasWithEntities"] = [];
|
||||||
|
|
||||||
|
for (const area of registries.areas) {
|
||||||
|
const areaEntities: HassEntity[] = [];
|
||||||
|
const areaDevices = new Set(
|
||||||
|
registries.devices
|
||||||
|
.filter((device) => device.area_id === area.area_id)
|
||||||
|
.map((device) => device.id)
|
||||||
|
);
|
||||||
|
for (const entity of registries.entities) {
|
||||||
|
if (
|
||||||
|
areaDevices.has(
|
||||||
|
// @ts-ignore
|
||||||
|
entity.device_id
|
||||||
|
) &&
|
||||||
|
entity.entity_id in allEntities
|
||||||
|
) {
|
||||||
|
areaEntities.push(allEntities[entity.entity_id]);
|
||||||
|
delete allEntities[entity.entity_id];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (areaEntities.length > 0) {
|
||||||
|
areasWithEntities.push([area, areaEntities]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
areasWithEntities,
|
||||||
|
otherEntities: allEntities,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
const computeCards = (
|
const computeCards = (
|
||||||
states: Array<[string, HassEntity]>,
|
states: Array<[string, HassEntity]>,
|
||||||
entityCardOptions: Partial<EntitiesCardConfig>
|
entityCardOptions: Partial<EntitiesCardConfig>
|
||||||
@ -124,6 +186,51 @@ const computeDefaultViewStates = (hass: HomeAssistant): HassEntities => {
|
|||||||
return states;
|
return states;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const generateDefaultViewConfig = (
|
||||||
|
hass: HomeAssistant,
|
||||||
|
registries: Registries
|
||||||
|
): LovelaceViewConfig => {
|
||||||
|
const states = computeDefaultViewStates(hass);
|
||||||
|
const path = "default_view";
|
||||||
|
const title = "Home";
|
||||||
|
const icon = undefined;
|
||||||
|
|
||||||
|
// In the case of a default view, we want to use the group order attribute
|
||||||
|
const groupOrders = {};
|
||||||
|
Object.keys(states).forEach((entityId) => {
|
||||||
|
const stateObj = states[entityId];
|
||||||
|
if (stateObj.attributes.order) {
|
||||||
|
groupOrders[entityId] = stateObj.attributes.order;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const splittedByAreas = splitByAreas(registries, states);
|
||||||
|
|
||||||
|
const config = generateViewConfig(
|
||||||
|
hass.localize,
|
||||||
|
path,
|
||||||
|
title,
|
||||||
|
icon,
|
||||||
|
splittedByAreas.otherEntities,
|
||||||
|
groupOrders
|
||||||
|
);
|
||||||
|
|
||||||
|
const areaCards: LovelaceCardConfig[] = [];
|
||||||
|
|
||||||
|
splittedByAreas.areasWithEntities.forEach(([area, entities]) => {
|
||||||
|
areaCards.push(
|
||||||
|
...computeCards(entities.map((entity) => [entity.entity_id, entity]), {
|
||||||
|
title: area.name,
|
||||||
|
show_header_toggle: true,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
config.cards!.unshift(...areaCards);
|
||||||
|
|
||||||
|
return config;
|
||||||
|
};
|
||||||
|
|
||||||
const generateViewConfig = (
|
const generateViewConfig = (
|
||||||
localize: LocalizeFunc,
|
localize: LocalizeFunc,
|
||||||
path: string,
|
path: string,
|
||||||
@ -208,10 +315,10 @@ const generateViewConfig = (
|
|||||||
return view;
|
return view;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const generateLovelaceConfig = (
|
export const generateLovelaceConfig = async (
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
localize: LocalizeFunc
|
localize: LocalizeFunc
|
||||||
): LovelaceConfig => {
|
): Promise<LovelaceConfig> => {
|
||||||
const viewEntities = extractViews(hass.states);
|
const viewEntities = extractViews(hass.states);
|
||||||
|
|
||||||
const views = viewEntities.map((viewEntity: GroupEntity) => {
|
const views = viewEntities.map((viewEntity: GroupEntity) => {
|
||||||
@ -241,27 +348,23 @@ export const generateLovelaceConfig = (
|
|||||||
viewEntities.length === 0 ||
|
viewEntities.length === 0 ||
|
||||||
viewEntities[0].entity_id !== DEFAULT_VIEW_ENTITY_ID
|
viewEntities[0].entity_id !== DEFAULT_VIEW_ENTITY_ID
|
||||||
) {
|
) {
|
||||||
const states = computeDefaultViewStates(hass);
|
// We want to keep the registry subscriptions alive after generating the UI
|
||||||
|
// so that we don't serve up stale data after changing areas.
|
||||||
|
if (!subscribedRegistries) {
|
||||||
|
subscribedRegistries = true;
|
||||||
|
subscribeAreaRegistry(hass, () => undefined);
|
||||||
|
subscribeDeviceRegistry(hass, () => undefined);
|
||||||
|
subscribeEntityRegistry(hass, () => undefined);
|
||||||
|
}
|
||||||
|
|
||||||
// In the case of a default view, we want to use the group order attribute
|
const [areas, devices, entities] = await Promise.all([
|
||||||
const groupOrders = {};
|
subscribeOne(hass, subscribeAreaRegistry),
|
||||||
Object.keys(states).forEach((entityId) => {
|
subscribeOne(hass, subscribeDeviceRegistry),
|
||||||
const stateObj = states[entityId];
|
subscribeOne(hass, subscribeEntityRegistry),
|
||||||
if (stateObj.attributes.order) {
|
]);
|
||||||
groupOrders[entityId] = stateObj.attributes.order;
|
const registries = { areas, devices, entities };
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
views.unshift(
|
views.unshift(generateDefaultViewConfig(hass, registries));
|
||||||
generateViewConfig(
|
|
||||||
localize,
|
|
||||||
"default_view",
|
|
||||||
"Home",
|
|
||||||
undefined,
|
|
||||||
states,
|
|
||||||
groupOrders
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
// Add map of geo locations to default view if loaded
|
// Add map of geo locations to default view if loaded
|
||||||
if (hass.config.components.includes("geo_location")) {
|
if (hass.config.components.includes("geo_location")) {
|
||||||
|
@ -171,7 +171,7 @@ class LovelacePanel extends LitElement {
|
|||||||
this._errorMsg = err.message;
|
this._errorMsg = err.message;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
conf = generateLovelaceConfig(this.hass!, this.hass!.localize);
|
conf = await generateLovelaceConfig(this.hass!, this.hass!.localize);
|
||||||
confMode = "generated";
|
confMode = "generated";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7237,10 +7237,10 @@ hoek@6.x.x:
|
|||||||
resolved "https://registry.yarnpkg.com/hoek/-/hoek-6.1.3.tgz#73b7d33952e01fe27a38b0457294b79dd8da242c"
|
resolved "https://registry.yarnpkg.com/hoek/-/hoek-6.1.3.tgz#73b7d33952e01fe27a38b0457294b79dd8da242c"
|
||||||
integrity sha512-YXXAAhmF9zpQbC7LEcREFtXfGq5K1fmd+4PHkBq8NUqmzW3G+Dq10bI/i0KucLRwss3YYFQ0fSfoxBZYiGUqtQ==
|
integrity sha512-YXXAAhmF9zpQbC7LEcREFtXfGq5K1fmd+4PHkBq8NUqmzW3G+Dq10bI/i0KucLRwss3YYFQ0fSfoxBZYiGUqtQ==
|
||||||
|
|
||||||
home-assistant-js-websocket@^4.1.1:
|
home-assistant-js-websocket@^4.1.2:
|
||||||
version "4.1.1"
|
version "4.1.2"
|
||||||
resolved "https://registry.yarnpkg.com/home-assistant-js-websocket/-/home-assistant-js-websocket-4.1.1.tgz#b85152c223a20bfe8827b817b927fd97cc021157"
|
resolved "https://registry.yarnpkg.com/home-assistant-js-websocket/-/home-assistant-js-websocket-4.1.2.tgz#dbcdb4b67df8d189d29bbf5603771d5bc80ef031"
|
||||||
integrity sha512-hNk8bj9JObd3NpgQ1+KtQCbSoz/TWockC8T/L8KvsPrDtkl1oQddajirumaMDgrJg/su4QsxFNUcDPGJyJ05UA==
|
integrity sha512-/I0m6FTDEq3LkzFc4tmgHJHTj9gWA6Wn/fgaa1ghIJJY0Yqb3x6whovN5pRNFsl6bnKzOCR+nmJ2ruVTBa5mVQ==
|
||||||
|
|
||||||
homedir-polyfill@^1.0.0, homedir-polyfill@^1.0.1:
|
homedir-polyfill@^1.0.0, homedir-polyfill@^1.0.1:
|
||||||
version "1.0.3"
|
version "1.0.3"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user