mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-24 09:46:36 +00:00
Add area filter selector for default dashboard (#18779)
Co-authored-by: Bram Kragten <mail@bramkragten.nl>
This commit is contained in:
parent
9b20e1cf56
commit
7727f34e8f
96
src/components/ha-area-filter.ts
Normal file
96
src/components/ha-area-filter.ts
Normal file
@ -0,0 +1,96 @@
|
||||
import { mdiChevronRight, mdiSofa } from "@mdi/js";
|
||||
import { CSSResultGroup, LitElement, TemplateResult, css, html } from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import { fireEvent } from "../common/dom/fire_event";
|
||||
import { showAreaFilterDialog } from "../dialogs/area-filter/show-area-filter-dialog";
|
||||
import { HomeAssistant } from "../types";
|
||||
import "./ha-svg-icon";
|
||||
import "./ha-textfield";
|
||||
|
||||
export type AreaFilterValue = {
|
||||
hidden?: string[];
|
||||
order?: string[];
|
||||
};
|
||||
|
||||
@customElement("ha-area-filter")
|
||||
export class HaAreaPicker extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@property() public label?: string;
|
||||
|
||||
@property({ attribute: false }) public value?: AreaFilterValue;
|
||||
|
||||
@property() public helper?: string;
|
||||
|
||||
@property({ type: Boolean }) public disabled = false;
|
||||
|
||||
@property({ type: Boolean }) public required = false;
|
||||
|
||||
protected render(): TemplateResult {
|
||||
const allAreasCount = Object.keys(this.hass.areas).length;
|
||||
const hiddenAreasCount = this.value?.hidden?.length ?? 0;
|
||||
|
||||
const description =
|
||||
hiddenAreasCount === 0
|
||||
? this.hass.localize("ui.components.area-filter.all_areas")
|
||||
: allAreasCount === hiddenAreasCount
|
||||
? this.hass.localize("ui.components.area-filter.no_areas")
|
||||
: this.hass.localize("ui.components.area-filter.area_count", {
|
||||
count: allAreasCount - hiddenAreasCount,
|
||||
});
|
||||
|
||||
return html`
|
||||
<ha-list-item
|
||||
tabindex="0"
|
||||
role="button"
|
||||
hasMeta
|
||||
twoline
|
||||
graphic="icon"
|
||||
@click=${this._edit}
|
||||
@keydown=${this._edit}
|
||||
.disabled=${this.disabled}
|
||||
>
|
||||
<ha-svg-icon slot="graphic" .path=${mdiSofa}></ha-svg-icon>
|
||||
<span>${this.label}</span>
|
||||
<span slot="secondary">${description}</span>
|
||||
<ha-svg-icon
|
||||
slot="meta"
|
||||
.label=${this.hass.localize("ui.common.edit")}
|
||||
.path=${mdiChevronRight}
|
||||
></ha-svg-icon>
|
||||
</ha-list-item>
|
||||
`;
|
||||
}
|
||||
|
||||
private async _edit(ev) {
|
||||
if (ev.defaultPrevented) {
|
||||
return;
|
||||
}
|
||||
if (ev.type === "keydown" && ev.key !== "Enter" && ev.key !== " ") {
|
||||
return;
|
||||
}
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
const value = await showAreaFilterDialog(this, {
|
||||
title: this.label,
|
||||
initialValue: this.value,
|
||||
});
|
||||
if (!value) return;
|
||||
fireEvent(this, "value-changed", { value });
|
||||
}
|
||||
|
||||
static get styles(): CSSResultGroup {
|
||||
return css`
|
||||
ha-list-item {
|
||||
--mdc-list-side-padding-left: 8px;
|
||||
--mdc-list-side-padding-right: 8px;
|
||||
}
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"ha-area-filter": HaAreaPicker;
|
||||
}
|
||||
}
|
41
src/components/ha-selector/ha-selector-area-filter.ts
Normal file
41
src/components/ha-selector/ha-selector-area-filter.ts
Normal file
@ -0,0 +1,41 @@
|
||||
import { LitElement, html } from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import type { AreaFilterSelector } from "../../data/selector";
|
||||
import { HomeAssistant } from "../../types";
|
||||
import "../ha-area-filter";
|
||||
|
||||
@customElement("ha-selector-area_filter")
|
||||
export class HaAreaFilterSelector extends LitElement {
|
||||
@property() public hass!: HomeAssistant;
|
||||
|
||||
@property() public selector!: AreaFilterSelector;
|
||||
|
||||
@property() public value?: any;
|
||||
|
||||
@property() public label?: string;
|
||||
|
||||
@property() public helper?: string;
|
||||
|
||||
@property({ type: Boolean }) public disabled = false;
|
||||
|
||||
@property({ type: Boolean }) public required = true;
|
||||
|
||||
protected render() {
|
||||
return html`
|
||||
<ha-area-filter
|
||||
.hass=${this.hass}
|
||||
.value=${this.value}
|
||||
.label=${this.label}
|
||||
.helper=${this.helper}
|
||||
.disabled=${this.disabled}
|
||||
.required=${this.required}
|
||||
></ha-area-filter>
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"ha-selector-area_filter": HaAreaFilterSelector;
|
||||
}
|
||||
}
|
@ -13,6 +13,7 @@ const LOAD_ELEMENTS = {
|
||||
action: () => import("./ha-selector-action"),
|
||||
addon: () => import("./ha-selector-addon"),
|
||||
area: () => import("./ha-selector-area"),
|
||||
area_filter: () => import("./ha-selector-area-filter"),
|
||||
attribute: () => import("./ha-selector-attribute"),
|
||||
assist_pipeline: () => import("./ha-selector-assist-pipeline"),
|
||||
boolean: () => import("./ha-selector-boolean"),
|
||||
|
@ -123,3 +123,22 @@ export const getAreaDeviceLookup = (
|
||||
}
|
||||
return areaDeviceLookup;
|
||||
};
|
||||
|
||||
export const areaCompare =
|
||||
(entries?: HomeAssistant["areas"], order?: string[]) =>
|
||||
(a: string, b: string) => {
|
||||
const indexA = order ? order.indexOf(a) : -1;
|
||||
const indexB = order ? order.indexOf(b) : 1;
|
||||
if (indexA === -1 && indexB === -1) {
|
||||
const nameA = entries?.[a].name ?? a;
|
||||
const nameB = entries?.[b].name ?? b;
|
||||
return stringCompare(nameA, nameB);
|
||||
}
|
||||
if (indexA === -1) {
|
||||
return 1;
|
||||
}
|
||||
if (indexB === -1) {
|
||||
return -1;
|
||||
}
|
||||
return indexA - indexB;
|
||||
};
|
||||
|
@ -15,6 +15,7 @@ export type Selector =
|
||||
| ActionSelector
|
||||
| AddonSelector
|
||||
| AreaSelector
|
||||
| AreaFilterSelector
|
||||
| AttributeSelector
|
||||
| BooleanSelector
|
||||
| ColorRGBSelector
|
||||
@ -77,6 +78,11 @@ export interface AreaSelector {
|
||||
} | null;
|
||||
}
|
||||
|
||||
export interface AreaFilterSelector {
|
||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
||||
area_filter: {} | null;
|
||||
}
|
||||
|
||||
export interface AttributeSelector {
|
||||
attribute: {
|
||||
entity_id?: string;
|
||||
|
218
src/dialogs/area-filter/area-filter-dialog.ts
Normal file
218
src/dialogs/area-filter/area-filter-dialog.ts
Normal file
@ -0,0 +1,218 @@
|
||||
import "@material/mwc-list/mwc-list";
|
||||
import { mdiDrag, mdiEye, mdiEyeOff } from "@mdi/js";
|
||||
import { CSSResultGroup, LitElement, css, html, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { classMap } from "lit/directives/class-map";
|
||||
import { repeat } from "lit/directives/repeat";
|
||||
import type { SortableEvent } from "sortablejs";
|
||||
import { fireEvent } from "../../common/dom/fire_event";
|
||||
import type { AreaFilterValue } from "../../components/ha-area-filter";
|
||||
import "../../components/ha-button";
|
||||
import "../../components/ha-icon-button";
|
||||
import "../../components/ha-list-item";
|
||||
import { areaCompare } from "../../data/area_registry";
|
||||
import { sortableStyles } from "../../resources/ha-sortable-style";
|
||||
import type { SortableInstance } from "../../resources/sortable";
|
||||
import { haStyleDialog } from "../../resources/styles";
|
||||
import { HomeAssistant } from "../../types";
|
||||
import { HassDialog } from "../make-dialog-manager";
|
||||
import { AreaFilterDialogParams } from "./show-area-filter-dialog";
|
||||
|
||||
@customElement("dialog-area-filter")
|
||||
export class DialogAreaFilter
|
||||
extends LitElement
|
||||
implements HassDialog<AreaFilterDialogParams>
|
||||
{
|
||||
@property({ attribute: false }) public hass?: HomeAssistant;
|
||||
|
||||
@state() private _dialogParams?: AreaFilterDialogParams;
|
||||
|
||||
@state() private _hidden: string[] = [];
|
||||
|
||||
@state() private _areas: string[] = [];
|
||||
|
||||
private _sortable?: SortableInstance;
|
||||
|
||||
public async showDialog(dialogParams: AreaFilterDialogParams): Promise<void> {
|
||||
this._dialogParams = dialogParams;
|
||||
this._hidden = dialogParams.initialValue?.hidden ?? [];
|
||||
const order = dialogParams.initialValue?.order ?? [];
|
||||
const allAreas = Object.keys(this.hass!.areas);
|
||||
this._areas = allAreas.concat().sort(areaCompare(this.hass!.areas, order));
|
||||
await this.updateComplete;
|
||||
this._createSortable();
|
||||
}
|
||||
|
||||
public closeDialog(): void {
|
||||
this._dialogParams = undefined;
|
||||
this._hidden = [];
|
||||
this._areas = [];
|
||||
this._destroySortable();
|
||||
fireEvent(this, "dialog-closed", { dialog: this.localName });
|
||||
}
|
||||
|
||||
private _submit(): void {
|
||||
const order = this._areas.filter((area) => !this._hidden.includes(area));
|
||||
const value: AreaFilterValue = {
|
||||
hidden: this._hidden,
|
||||
order,
|
||||
};
|
||||
this._dialogParams?.submit?.(value);
|
||||
this.closeDialog();
|
||||
}
|
||||
|
||||
private _cancel(): void {
|
||||
this._dialogParams?.cancel?.();
|
||||
this.closeDialog();
|
||||
}
|
||||
|
||||
private async _createSortable() {
|
||||
const Sortable = (await import("../../resources/sortable")).default;
|
||||
if (this._sortable) return;
|
||||
this._sortable = new Sortable(this.shadowRoot!.querySelector(".areas")!, {
|
||||
animation: 150,
|
||||
fallbackClass: "sortable-fallback",
|
||||
handle: ".handle",
|
||||
onChoose: (evt: SortableEvent) => {
|
||||
(evt.item as any).placeholder =
|
||||
document.createComment("sort-placeholder");
|
||||
evt.item.after((evt.item as any).placeholder);
|
||||
},
|
||||
onEnd: (evt: SortableEvent) => {
|
||||
// put back in original location
|
||||
if ((evt.item as any).placeholder) {
|
||||
(evt.item as any).placeholder.replaceWith(evt.item);
|
||||
delete (evt.item as any).placeholder;
|
||||
}
|
||||
this._dragged(evt);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
private _destroySortable() {
|
||||
this._sortable?.destroy();
|
||||
this._sortable = undefined;
|
||||
}
|
||||
|
||||
private _dragged(ev: SortableEvent): void {
|
||||
if (ev.oldIndex === ev.newIndex) return;
|
||||
|
||||
const areas = this._areas.concat();
|
||||
|
||||
const option = areas.splice(ev.oldIndex!, 1)[0];
|
||||
areas.splice(ev.newIndex!, 0, option);
|
||||
|
||||
this._areas = areas;
|
||||
}
|
||||
|
||||
protected render() {
|
||||
if (!this._dialogParams || !this.hass) {
|
||||
return nothing;
|
||||
}
|
||||
|
||||
const allAreas = this._areas;
|
||||
|
||||
return html`
|
||||
<ha-dialog
|
||||
open
|
||||
@closed=${this._cancel}
|
||||
.heading=${this._dialogParams.title ??
|
||||
this.hass.localize("ui.components.area-filter.title")}
|
||||
>
|
||||
<mwc-list class="areas">
|
||||
${repeat(
|
||||
allAreas,
|
||||
(area) => area,
|
||||
(area, _idx) => {
|
||||
const isVisible = !this._hidden.includes(area);
|
||||
const name = this.hass!.areas[area]?.name || area;
|
||||
return html`
|
||||
<ha-list-item
|
||||
class=${classMap({ hidden: !isVisible })}
|
||||
hasMeta
|
||||
graphic="icon"
|
||||
noninteractive
|
||||
>
|
||||
<ha-svg-icon
|
||||
class="handle"
|
||||
.path=${mdiDrag}
|
||||
slot="graphic"
|
||||
></ha-svg-icon>
|
||||
${name}
|
||||
<ha-icon-button
|
||||
tabindex="0"
|
||||
class="action"
|
||||
.path=${isVisible ? mdiEye : mdiEyeOff}
|
||||
slot="meta"
|
||||
.label=${this.hass!.localize(
|
||||
`ui.components.area-filter.${
|
||||
isVisible ? "hide" : "show"
|
||||
}`,
|
||||
{ area: name }
|
||||
)}
|
||||
.area=${area}
|
||||
@click=${this._toggle}
|
||||
></ha-icon-button>
|
||||
</ha-list-item>
|
||||
`;
|
||||
}
|
||||
)}
|
||||
</mwc-list>
|
||||
<ha-button slot="secondaryAction" dialogAction="cancel">
|
||||
${this.hass.localize("ui.common.cancel")}
|
||||
</ha-button>
|
||||
<ha-button @click=${this._submit} slot="primaryAction">
|
||||
${this.hass.localize("ui.common.submit")}
|
||||
</ha-button>
|
||||
</ha-dialog>
|
||||
`;
|
||||
}
|
||||
|
||||
_toggle(ev) {
|
||||
const area = ev.target.area;
|
||||
const hidden = [...(this._hidden ?? [])];
|
||||
if (hidden.includes(area)) {
|
||||
hidden.splice(hidden.indexOf(area), 1);
|
||||
} else {
|
||||
hidden.push(area);
|
||||
}
|
||||
this._hidden = hidden;
|
||||
}
|
||||
|
||||
static get styles(): CSSResultGroup {
|
||||
return [
|
||||
sortableStyles,
|
||||
haStyleDialog,
|
||||
css`
|
||||
ha-dialog {
|
||||
/* Place above other dialogs */
|
||||
--dialog-z-index: 104;
|
||||
--dialog-content-padding: 0;
|
||||
}
|
||||
ha-list-item {
|
||||
overflow: visible;
|
||||
}
|
||||
.hidden {
|
||||
opacity: 0.3;
|
||||
}
|
||||
.handle {
|
||||
cursor: grab;
|
||||
}
|
||||
.actions {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
ha-icon-button {
|
||||
display: block;
|
||||
margin: -12px;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"dialog-area-filter": DialogAreaFilter;
|
||||
}
|
||||
}
|
38
src/dialogs/area-filter/show-area-filter-dialog.ts
Normal file
38
src/dialogs/area-filter/show-area-filter-dialog.ts
Normal file
@ -0,0 +1,38 @@
|
||||
import { fireEvent } from "../../common/dom/fire_event";
|
||||
import type { AreaFilterValue } from "../../components/ha-area-filter";
|
||||
|
||||
export interface AreaFilterDialogParams {
|
||||
title?: string;
|
||||
initialValue?: AreaFilterValue;
|
||||
submit?: (value?: AreaFilterValue) => void;
|
||||
cancel?: () => void;
|
||||
}
|
||||
|
||||
export const showAreaFilterDialog = (
|
||||
element: HTMLElement,
|
||||
dialogParams: AreaFilterDialogParams
|
||||
) =>
|
||||
new Promise<AreaFilterValue | null>((resolve) => {
|
||||
const origCancel = dialogParams.cancel;
|
||||
const origSubmit = dialogParams.submit;
|
||||
|
||||
fireEvent(element, "show-dialog", {
|
||||
dialogTag: "dialog-area-filter",
|
||||
dialogImport: () => import("./area-filter-dialog"),
|
||||
dialogParams: {
|
||||
...dialogParams,
|
||||
cancel: () => {
|
||||
resolve(null);
|
||||
if (origCancel) {
|
||||
origCancel();
|
||||
}
|
||||
},
|
||||
submit: (code: AreaFilterValue) => {
|
||||
resolve(code);
|
||||
if (origSubmit) {
|
||||
origSubmit(code);
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
@ -7,6 +7,7 @@ import { splitByGroups } from "../../../common/entity/split_by_groups";
|
||||
import { stripPrefixFromEntityName } from "../../../common/entity/strip_prefix_from_entity_name";
|
||||
import { stringCompare } from "../../../common/string/compare";
|
||||
import { LocalizeFunc } from "../../../common/translations/localize";
|
||||
import type { AreaFilterValue } from "../../../components/ha-area-filter";
|
||||
import {
|
||||
EnergyPreferences,
|
||||
GridSourceTypeEnergyPreference,
|
||||
@ -27,6 +28,7 @@ import {
|
||||
} from "../cards/types";
|
||||
import { EntityConfig } from "../entity-rows/types";
|
||||
import { ButtonsHeaderFooterConfig } from "../header-footer/types";
|
||||
import { areaCompare } from "../../../data/area_registry";
|
||||
|
||||
const HIDE_DOMAIN = new Set([
|
||||
"automation",
|
||||
@ -447,9 +449,7 @@ export const generateDefaultViewConfig = (
|
||||
entities: HassEntities,
|
||||
localize: LocalizeFunc,
|
||||
energyPrefs?: EnergyPreferences,
|
||||
areasPrefs?: {
|
||||
hidden?: string[];
|
||||
},
|
||||
areasPrefs?: AreaFilterValue,
|
||||
hideEntitiesWithoutAreas?: boolean,
|
||||
hideEnergy?: boolean
|
||||
): LovelaceViewConfig => {
|
||||
@ -511,15 +511,12 @@ export const generateDefaultViewConfig = (
|
||||
|
||||
const areaCards: LovelaceCardConfig[] = [];
|
||||
|
||||
const sortedAreas = Object.entries(
|
||||
splittedByAreaDevice.areasWithEntities
|
||||
).sort((a, b) => {
|
||||
const areaA = areaEntries[a[0]];
|
||||
const areaB = areaEntries[b[0]];
|
||||
return stringCompare(areaA.name, areaB.name);
|
||||
});
|
||||
const sortedAreas = Object.keys(splittedByAreaDevice.areasWithEntities).sort(
|
||||
areaCompare(areaEntries, areasPrefs?.order)
|
||||
);
|
||||
|
||||
for (const [areaId, areaEntities] of sortedAreas) {
|
||||
for (const areaId of sortedAreas) {
|
||||
const areaEntities = splittedByAreaDevice.areasWithEntities[areaId];
|
||||
const area = areaEntries[areaId];
|
||||
areaCards.push(
|
||||
...computeCards(
|
||||
|
@ -1,6 +1,5 @@
|
||||
import { html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import memoizeOne from "memoize-one";
|
||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||
import "../../../../components/ha-form/ha-form";
|
||||
import type {
|
||||
@ -13,11 +12,9 @@ import { LovelaceStrategyEditor } from "../../strategies/types";
|
||||
|
||||
const SCHEMA = [
|
||||
{
|
||||
name: "hidden_areas",
|
||||
name: "areas",
|
||||
selector: {
|
||||
area: {
|
||||
multiple: true,
|
||||
},
|
||||
area_filter: {},
|
||||
},
|
||||
},
|
||||
{
|
||||
@ -40,12 +37,6 @@ const SCHEMA = [
|
||||
},
|
||||
] as const satisfies readonly HaFormSchema[];
|
||||
|
||||
type FormData = {
|
||||
hidden_areas: string[];
|
||||
hide_energy?: boolean;
|
||||
hide_entities_without_area?: boolean;
|
||||
};
|
||||
|
||||
@customElement("hui-original-states-dashboard-strategy-editor")
|
||||
export class HuiOriginalStatesDashboarStrategyEditor
|
||||
extends LitElement
|
||||
@ -60,44 +51,15 @@ export class HuiOriginalStatesDashboarStrategyEditor
|
||||
this._config = config;
|
||||
}
|
||||
|
||||
private _configToFormData = memoizeOne(
|
||||
(config: OriginalStatesDashboardStrategyConfig): FormData => {
|
||||
const { areas, ...rest } = config;
|
||||
return {
|
||||
...rest,
|
||||
hidden_areas: areas?.hidden || [],
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
private _formDataToConfig = memoizeOne(
|
||||
(data: FormData): OriginalStatesDashboardStrategyConfig => {
|
||||
const { hidden_areas, ...rest } = data;
|
||||
const areas =
|
||||
hidden_areas.length > 0
|
||||
? {
|
||||
hidden: hidden_areas,
|
||||
}
|
||||
: undefined;
|
||||
return {
|
||||
type: "original-states",
|
||||
...rest,
|
||||
areas,
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
protected render() {
|
||||
if (!this.hass || !this._config) {
|
||||
return nothing;
|
||||
}
|
||||
|
||||
const data = this._configToFormData(this._config);
|
||||
|
||||
return html`
|
||||
<ha-form
|
||||
.hass=${this.hass}
|
||||
.data=${data}
|
||||
.data=${this._config}
|
||||
.schema=${SCHEMA}
|
||||
.computeLabel=${this._computeLabelCallback}
|
||||
@value-changed=${this._valueChanged}
|
||||
@ -106,14 +68,13 @@ export class HuiOriginalStatesDashboarStrategyEditor
|
||||
}
|
||||
|
||||
private _valueChanged(ev: CustomEvent): void {
|
||||
const data = ev.detail.value as FormData;
|
||||
const config = this._formDataToConfig(data);
|
||||
fireEvent(this, "config-changed", { config });
|
||||
const data = ev.detail.value;
|
||||
fireEvent(this, "config-changed", { config: data });
|
||||
}
|
||||
|
||||
private _computeLabelCallback = (schema: SchemaUnion<typeof SCHEMA>) => {
|
||||
switch (schema.name) {
|
||||
case "hidden_areas":
|
||||
case "areas":
|
||||
case "hide_energy":
|
||||
case "hide_entities_without_area":
|
||||
return this.hass?.localize(
|
||||
|
@ -2,6 +2,7 @@ import { STATE_NOT_RUNNING } from "home-assistant-js-websocket";
|
||||
import { ReactiveElement } from "lit";
|
||||
import { customElement } from "lit/decorators";
|
||||
import { isComponentLoaded } from "../../../common/config/is_component_loaded";
|
||||
import type { AreaFilterValue } from "../../../components/ha-area-filter";
|
||||
import { getEnergyPreferences } from "../../../data/energy";
|
||||
import { LovelaceViewConfig } from "../../../data/lovelace/config/view";
|
||||
import { HomeAssistant } from "../../../types";
|
||||
@ -9,9 +10,7 @@ import { generateDefaultViewConfig } from "../common/generate-lovelace-config";
|
||||
|
||||
export type OriginalStatesViewStrategyConfig = {
|
||||
type: "original-states";
|
||||
areas?: {
|
||||
hidden?: string[];
|
||||
};
|
||||
areas?: AreaFilterValue;
|
||||
hide_entities_without_area?: boolean;
|
||||
hide_energy?: boolean;
|
||||
};
|
||||
|
@ -512,6 +512,14 @@
|
||||
"failed_create_area": "Failed to create area."
|
||||
}
|
||||
},
|
||||
"area-filter": {
|
||||
"title": "Areas",
|
||||
"no_areas": "No areas",
|
||||
"area_count": "{count} {count, plural,\n one {area}\n other {areas}\n}",
|
||||
"all_areas": "All areas",
|
||||
"show": "Show {area}",
|
||||
"hide": "Hide {area}"
|
||||
},
|
||||
"statistic-picker": {
|
||||
"statistic": "Statistic",
|
||||
"no_statistics": "You don't have any statistics",
|
||||
@ -5269,7 +5277,7 @@
|
||||
},
|
||||
"strategy": {
|
||||
"original-states": {
|
||||
"hidden_areas": "Hidden Areas",
|
||||
"areas": "Areas",
|
||||
"hide_entities_without_area": "Hide entities without area",
|
||||
"hide_energy": "Hide energy"
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user