Lovelace tweaks (#3439)

This commit is contained in:
Paulus Schoutsen 2019-07-30 10:37:21 -07:00 committed by GitHub
parent 4edcd5f2ef
commit b158f15d93
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 89 additions and 59 deletions

View File

@ -29,7 +29,9 @@ export class HuiMarkdownCard extends LitElement implements LovelaceCard {
@property() private _config?: MarkdownCardConfig; @property() private _config?: MarkdownCardConfig;
public getCardSize(): number { public getCardSize(): number {
return this._config!.content.split("\n").length; return (
this._config!.content.split("\n").length + (this._config!.title ? 1 : 0)
);
} }
public setConfig(config: MarkdownCardConfig): void { public setConfig(config: MarkdownCardConfig): void {

View File

@ -4,7 +4,11 @@ import {
LovelaceCardConfig, LovelaceCardConfig,
LovelaceViewConfig, LovelaceViewConfig,
} from "../../../data/lovelace"; } from "../../../data/lovelace";
import { HassEntity, HassEntities } from "home-assistant-js-websocket"; import {
HassEntity,
HassEntities,
HassConfig,
} from "home-assistant-js-websocket";
import extractViews from "../../../common/entity/extract_views"; import extractViews from "../../../common/entity/extract_views";
import getViewEntities from "../../../common/entity/get_view_entities"; import getViewEntities from "../../../common/entity/get_view_entities";
@ -47,12 +51,6 @@ const HIDE_DOMAIN = new Set([
"geo_location", "geo_location",
]); ]);
interface Registries {
areas: AreaRegistryEntry[];
devices: DeviceRegistryEntry[];
entities: EntityRegistryEntry[];
}
let subscribedRegistries = false; let subscribedRegistries = false;
interface SplittedByAreas { interface SplittedByAreas {
@ -61,20 +59,22 @@ interface SplittedByAreas {
} }
const splitByAreas = ( const splitByAreas = (
registries: Registries, areaEntries: AreaRegistryEntry[],
deviceEntries: DeviceRegistryEntry[],
entityEntries: EntityRegistryEntry[],
entities: HassEntities entities: HassEntities
): SplittedByAreas => { ): SplittedByAreas => {
const allEntities = { ...entities }; const allEntities = { ...entities };
const areasWithEntities: SplittedByAreas["areasWithEntities"] = []; const areasWithEntities: SplittedByAreas["areasWithEntities"] = [];
for (const area of registries.areas) { for (const area of areaEntries) {
const areaEntities: HassEntity[] = []; const areaEntities: HassEntity[] = [];
const areaDevices = new Set( const areaDevices = new Set(
registries.devices deviceEntries
.filter((device) => device.area_id === area.area_id) .filter((device) => device.area_id === area.area_id)
.map((device) => device.id) .map((device) => device.id)
); );
for (const entity of registries.entities) { for (const entity of entityEntries) {
if ( if (
areaDevices.has( areaDevices.has(
// @ts-ignore // @ts-ignore
@ -172,25 +172,28 @@ const computeCards = (
return cards; return cards;
}; };
const computeDefaultViewStates = (hass: HomeAssistant): HassEntities => { const computeDefaultViewStates = (entities: HassEntities): HassEntities => {
const states = {}; const states = {};
Object.keys(hass.states).forEach((entityId) => { Object.keys(entities).forEach((entityId) => {
const stateObj = hass.states[entityId]; const stateObj = entities[entityId];
if ( if (
!stateObj.attributes.hidden && !stateObj.attributes.hidden &&
!HIDE_DOMAIN.has(computeStateDomain(stateObj)) !HIDE_DOMAIN.has(computeStateDomain(stateObj))
) { ) {
states[entityId] = hass.states[entityId]; states[entityId] = entities[entityId];
} }
}); });
return states; return states;
}; };
const generateDefaultViewConfig = ( export const generateDefaultViewConfig = (
hass: HomeAssistant, areaEntries: AreaRegistryEntry[],
registries: Registries deviceEntries: DeviceRegistryEntry[],
entityEntries: EntityRegistryEntry[],
entities: HassEntities,
localize: LocalizeFunc
): LovelaceViewConfig => { ): LovelaceViewConfig => {
const states = computeDefaultViewStates(hass); const states = computeDefaultViewStates(entities);
const path = "default_view"; const path = "default_view";
const title = "Home"; const title = "Home";
const icon = undefined; const icon = undefined;
@ -204,10 +207,15 @@ const generateDefaultViewConfig = (
} }
}); });
const splittedByAreas = splitByAreas(registries, states); const splittedByAreas = splitByAreas(
areaEntries,
deviceEntries,
entityEntries,
states
);
const config = generateViewConfig( const config = generateViewConfig(
hass.localize, localize,
path, path,
title, title,
icon, icon,
@ -217,12 +225,15 @@ const generateDefaultViewConfig = (
const areaCards: LovelaceCardConfig[] = []; const areaCards: LovelaceCardConfig[] = [];
splittedByAreas.areasWithEntities.forEach(([area, entities]) => { splittedByAreas.areasWithEntities.forEach(([area, areaEntities]) => {
areaCards.push( areaCards.push(
...computeCards(entities.map((entity) => [entity.entity_id, entity]), { ...computeCards(
title: area.name, areaEntities.map((entity) => [entity.entity_id, entity]),
show_header_toggle: true, {
}) title: area.name,
show_header_toggle: true,
}
)
); );
}); });
@ -315,14 +326,44 @@ const generateViewConfig = (
return view; return view;
}; };
export const generateLovelaceConfig = async ( export const generateLovelaceConfigFromHass = async (hass: HomeAssistant) => {
hass: HomeAssistant, // 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.connection, () => undefined);
subscribeDeviceRegistry(hass.connection, () => undefined);
subscribeEntityRegistry(hass.connection, () => undefined);
}
const [areaEntries, deviceEntries, entityEntries] = await Promise.all([
subscribeOne(hass.connection, subscribeAreaRegistry),
subscribeOne(hass.connection, subscribeDeviceRegistry),
subscribeOne(hass.connection, subscribeEntityRegistry),
]);
return generateLovelaceConfigFromData(
hass.config,
areaEntries,
deviceEntries,
entityEntries,
hass.states,
hass.localize
);
};
export const generateLovelaceConfigFromData = async (
config: HassConfig,
areaEntries: AreaRegistryEntry[],
deviceEntries: DeviceRegistryEntry[],
entityEntries: EntityRegistryEntry[],
entities: HassEntities,
localize: LocalizeFunc localize: LocalizeFunc
): Promise<LovelaceConfig> => { ): Promise<LovelaceConfig> => {
const viewEntities = extractViews(hass.states); const viewEntities = extractViews(entities);
const views = viewEntities.map((viewEntity: GroupEntity) => { const views = viewEntities.map((viewEntity: GroupEntity) => {
const states = getViewEntities(hass.states, viewEntity); const states = getViewEntities(entities, viewEntity);
// In the case of a normal view, we use group order as specified in view // In the case of a normal view, we use group order as specified in view
const groupOrders = {}; const groupOrders = {};
@ -340,7 +381,7 @@ export const generateLovelaceConfig = async (
); );
}); });
let title = hass.config.location_name; let title = config.location_name;
// User can override default view. If they didn't, we will add one // User can override default view. If they didn't, we will add one
// that contains all entities. // that contains all entities.
@ -348,26 +389,18 @@ export const generateLovelaceConfig = async (
viewEntities.length === 0 || viewEntities.length === 0 ||
viewEntities[0].entity_id !== DEFAULT_VIEW_ENTITY_ID viewEntities[0].entity_id !== DEFAULT_VIEW_ENTITY_ID
) { ) {
// We want to keep the registry subscriptions alive after generating the UI views.unshift(
// so that we don't serve up stale data after changing areas. generateDefaultViewConfig(
if (!subscribedRegistries) { areaEntries,
subscribedRegistries = true; deviceEntries,
subscribeAreaRegistry(hass.connection, () => undefined); entityEntries,
subscribeDeviceRegistry(hass.connection, () => undefined); entities,
subscribeEntityRegistry(hass.connection, () => undefined); localize
} )
);
const [areas, devices, entities] = await Promise.all([
subscribeOne(hass.connection, subscribeAreaRegistry),
subscribeOne(hass.connection, subscribeDeviceRegistry),
subscribeOne(hass.connection, subscribeEntityRegistry),
]);
const registries = { areas, devices, entities };
views.unshift(generateDefaultViewConfig(hass, registries));
// 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 (config.components.includes("geo_location")) {
if (views[0] && views[0].cards) { if (views[0] && views[0].cards) {
views[0].cards.push({ views[0].cards.push({
type: "map", type: "map",
@ -382,12 +415,6 @@ export const generateLovelaceConfig = async (
} }
} }
if (__DEMO__) {
views[0].cards!.unshift({
type: "custom:ha-demo-card",
});
}
// User has no entities // User has no entities
if (views.length === 1 && views[0].cards!.length === 0) { if (views.length === 1 && views[0].cards!.length === 0) {
import(/* webpackChunkName: "hui-empty-state-card" */ "../cards/hui-empty-state-card"); import(/* webpackChunkName: "hui-empty-state-card" */ "../cards/hui-empty-state-card");

View File

@ -20,7 +20,7 @@ import {
property, property,
} from "lit-element"; } from "lit-element";
import { showSaveDialog } from "./editor/show-save-config-dialog"; import { showSaveDialog } from "./editor/show-save-config-dialog";
import { generateLovelaceConfig } from "./common/generate-lovelace-config"; import { generateLovelaceConfigFromHass } from "./common/generate-lovelace-config";
import { showToast } from "../../util/toast"; import { showToast } from "../../util/toast";
interface LovelacePanelConfig { interface LovelacePanelConfig {
@ -162,7 +162,7 @@ class LovelacePanel extends LitElement {
} }
private async _regenerateConfig() { private async _regenerateConfig() {
const conf = await generateLovelaceConfig(this.hass!, this.hass!.localize); const conf = await generateLovelaceConfigFromHass(this.hass!);
this._setLovelaceConfig(conf, "generated"); this._setLovelaceConfig(conf, "generated");
this._state = "loaded"; this._state = "loaded";
} }
@ -241,7 +241,7 @@ class LovelacePanel extends LitElement {
this._errorMsg = err.message; this._errorMsg = err.message;
return; return;
} }
conf = await generateLovelaceConfig(this.hass!, this.hass!.localize); conf = await generateLovelaceConfigFromHass(this.hass!);
confMode = "generated"; confMode = "generated";
} }

View File

@ -352,6 +352,7 @@ class HUIRoot extends LitElement {
} }
#view > * { #view > * {
flex: 1; flex: 1;
width: 100%;
} }
paper-item { paper-item {
cursor: pointer; cursor: pointer;