Use areas when generating Lovelace config (#3175)

* Use areas when generating Lovelace config

* Add missing return type
This commit is contained in:
Paulus Schoutsen 2019-05-08 16:34:53 -07:00 committed by GitHub
parent 89a35a0062
commit 32e68c1a4b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 146 additions and 27 deletions

View File

@ -75,7 +75,7 @@
"es6-object-assign": "^1.1.0",
"fecha": "^3.0.2",
"hls.js": "^0.12.4",
"home-assistant-js-websocket": "^4.1.1",
"home-assistant-js-websocket": "^4.1.2",
"intl-messageformat": "^2.2.0",
"jquery": "^3.3.1",
"js-yaml": "^3.13.0",

View 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);
});
});

View File

@ -17,6 +17,19 @@ import computeDomain from "../../../common/entity/compute_domain";
import { EntityRowConfig, WeblinkConfig } from "../entity-rows/types";
import { LocalizeFunc } from "../../../common/translations/localize";
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 DOMAINS_BADGES = [
@ -34,6 +47,55 @@ const HIDE_DOMAIN = new Set([
"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 = (
states: Array<[string, HassEntity]>,
entityCardOptions: Partial<EntitiesCardConfig>
@ -124,6 +186,51 @@ const computeDefaultViewStates = (hass: HomeAssistant): HassEntities => {
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 = (
localize: LocalizeFunc,
path: string,
@ -208,10 +315,10 @@ const generateViewConfig = (
return view;
};
export const generateLovelaceConfig = (
export const generateLovelaceConfig = async (
hass: HomeAssistant,
localize: LocalizeFunc
): LovelaceConfig => {
): Promise<LovelaceConfig> => {
const viewEntities = extractViews(hass.states);
const views = viewEntities.map((viewEntity: GroupEntity) => {
@ -241,27 +348,23 @@ export const generateLovelaceConfig = (
viewEntities.length === 0 ||
viewEntities[0].entity_id !== DEFAULT_VIEW_ENTITY_ID
) {
const states = computeDefaultViewStates(hass);
// 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;
// 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);
}
});
views.unshift(
generateViewConfig(
localize,
"default_view",
"Home",
undefined,
states,
groupOrders
)
);
const [areas, devices, entities] = await Promise.all([
subscribeOne(hass, subscribeAreaRegistry),
subscribeOne(hass, subscribeDeviceRegistry),
subscribeOne(hass, subscribeEntityRegistry),
]);
const registries = { areas, devices, entities };
views.unshift(generateDefaultViewConfig(hass, registries));
// Add map of geo locations to default view if loaded
if (hass.config.components.includes("geo_location")) {

View File

@ -171,7 +171,7 @@ class LovelacePanel extends LitElement {
this._errorMsg = err.message;
return;
}
conf = generateLovelaceConfig(this.hass!, this.hass!.localize);
conf = await generateLovelaceConfig(this.hass!, this.hass!.localize);
confMode = "generated";
}

View File

@ -7237,10 +7237,10 @@ hoek@6.x.x:
resolved "https://registry.yarnpkg.com/hoek/-/hoek-6.1.3.tgz#73b7d33952e01fe27a38b0457294b79dd8da242c"
integrity sha512-YXXAAhmF9zpQbC7LEcREFtXfGq5K1fmd+4PHkBq8NUqmzW3G+Dq10bI/i0KucLRwss3YYFQ0fSfoxBZYiGUqtQ==
home-assistant-js-websocket@^4.1.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/home-assistant-js-websocket/-/home-assistant-js-websocket-4.1.1.tgz#b85152c223a20bfe8827b817b927fd97cc021157"
integrity sha512-hNk8bj9JObd3NpgQ1+KtQCbSoz/TWockC8T/L8KvsPrDtkl1oQddajirumaMDgrJg/su4QsxFNUcDPGJyJ05UA==
home-assistant-js-websocket@^4.1.2:
version "4.1.2"
resolved "https://registry.yarnpkg.com/home-assistant-js-websocket/-/home-assistant-js-websocket-4.1.2.tgz#dbcdb4b67df8d189d29bbf5603771d5bc80ef031"
integrity sha512-/I0m6FTDEq3LkzFc4tmgHJHTj9gWA6Wn/fgaa1ghIJJY0Yqb3x6whovN5pRNFsl6bnKzOCR+nmJ2ruVTBa5mVQ==
homedir-polyfill@^1.0.0, homedir-polyfill@^1.0.1:
version "1.0.3"