mirror of
https://github.com/home-assistant/frontend.git
synced 2025-08-02 05:57:54 +00:00
Lovelace tweaks (#3439)
This commit is contained in:
parent
4edcd5f2ef
commit
b158f15d93
@ -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 {
|
||||||
|
@ -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");
|
||||||
|
@ -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";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -352,6 +352,7 @@ class HUIRoot extends LitElement {
|
|||||||
}
|
}
|
||||||
#view > * {
|
#view > * {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
|
width: 100%;
|
||||||
}
|
}
|
||||||
paper-item {
|
paper-item {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user