Add icon to areas (#19585)
* Add icon to areas * Fix gallery --------- Co-authored-by: Paul Bottein <paul.bottein@gmail.com>
This commit is contained in:
parent
b159f4c074
commit
f4859320eb
|
@ -10,6 +10,7 @@ import { mockHassioSupervisor } from "../../../../demo/src/stubs/hassio_supervis
|
|||
import { computeInitialHaFormData } from "../../../../src/components/ha-form/compute-initial-ha-form-data";
|
||||
import "../../../../src/components/ha-form/ha-form";
|
||||
import type { HaFormSchema } from "../../../../src/components/ha-form/types";
|
||||
import type { AreaRegistryEntry } from "../../../../src/data/area_registry";
|
||||
import { getEntity } from "../../../../src/fake_data/entity";
|
||||
import { provideHass } from "../../../../src/fake_data/provide_hass";
|
||||
import { HomeAssistant } from "../../../../src/types";
|
||||
|
@ -97,22 +98,25 @@ const DEVICES = [
|
|||
},
|
||||
];
|
||||
|
||||
const AREAS = [
|
||||
const AREAS: AreaRegistryEntry[] = [
|
||||
{
|
||||
area_id: "backyard",
|
||||
name: "Backyard",
|
||||
icon: null,
|
||||
picture: null,
|
||||
aliases: [],
|
||||
},
|
||||
{
|
||||
area_id: "bedroom",
|
||||
name: "Bedroom",
|
||||
icon: "mdi:bed",
|
||||
picture: null,
|
||||
aliases: [],
|
||||
},
|
||||
{
|
||||
area_id: "livingroom",
|
||||
name: "Livingroom",
|
||||
icon: "mdi:sofa",
|
||||
picture: null,
|
||||
aliases: [],
|
||||
},
|
||||
|
|
|
@ -9,6 +9,7 @@ import { mockEntityRegistry } from "../../../../demo/src/stubs/entity_registry";
|
|||
import { mockHassioSupervisor } from "../../../../demo/src/stubs/hassio_supervisor";
|
||||
import "../../../../src/components/ha-selector/ha-selector";
|
||||
import "../../../../src/components/ha-settings-row";
|
||||
import type { AreaRegistryEntry } from "../../../../src/data/area_registry";
|
||||
import { BlueprintInput } from "../../../../src/data/blueprint";
|
||||
import { showDialog } from "../../../../src/dialogs/make-dialog-manager";
|
||||
import { getEntity } from "../../../../src/fake_data/entity";
|
||||
|
@ -93,22 +94,25 @@ const DEVICES = [
|
|||
},
|
||||
];
|
||||
|
||||
const AREAS = [
|
||||
const AREAS: AreaRegistryEntry[] = [
|
||||
{
|
||||
area_id: "backyard",
|
||||
name: "Backyard",
|
||||
icon: null,
|
||||
picture: null,
|
||||
aliases: [],
|
||||
},
|
||||
{
|
||||
area_id: "bedroom",
|
||||
name: "Bedroom",
|
||||
icon: "mdi:bed",
|
||||
picture: null,
|
||||
aliases: [],
|
||||
},
|
||||
{
|
||||
area_id: "livingroom",
|
||||
name: "Livingroom",
|
||||
icon: "mdi:sofa",
|
||||
picture: null,
|
||||
aliases: [],
|
||||
},
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { ComboBoxLitRenderer } from "@vaadin/combo-box/lit";
|
||||
import { HassEntity } from "home-assistant-js-websocket";
|
||||
import { html, LitElement, PropertyValues, TemplateResult } from "lit";
|
||||
import { html, LitElement, nothing, PropertyValues, TemplateResult } from "lit";
|
||||
import { customElement, property, query, state } from "lit/decorators";
|
||||
import { classMap } from "lit/directives/class-map";
|
||||
import memoizeOne from "memoize-one";
|
||||
|
@ -36,8 +36,12 @@ type ScorableAreaRegistryEntry = ScorableTextItem & AreaRegistryEntry;
|
|||
|
||||
const rowRenderer: ComboBoxLitRenderer<AreaRegistryEntry> = (item) =>
|
||||
html`<ha-list-item
|
||||
graphic="icon"
|
||||
class=${classMap({ "add-new": item.area_id === "add_new" })}
|
||||
>
|
||||
${item.icon
|
||||
? html`<ha-icon slot="graphic" .icon=${item.icon}></ha-icon>`
|
||||
: nothing}
|
||||
${item.name}
|
||||
</ha-list-item>`;
|
||||
|
||||
|
@ -135,6 +139,7 @@ export class HaAreaPicker extends LitElement {
|
|||
area_id: "no_areas",
|
||||
name: this.hass.localize("ui.components.area-picker.no_areas"),
|
||||
picture: null,
|
||||
icon: null,
|
||||
aliases: [],
|
||||
},
|
||||
];
|
||||
|
@ -262,7 +267,9 @@ export class HaAreaPicker extends LitElement {
|
|||
}
|
||||
|
||||
if (areaIds) {
|
||||
outputAreas = areas.filter((area) => areaIds!.includes(area.area_id));
|
||||
outputAreas = outputAreas.filter((area) =>
|
||||
areaIds!.includes(area.area_id)
|
||||
);
|
||||
}
|
||||
|
||||
if (excludeAreas) {
|
||||
|
@ -277,6 +284,7 @@ export class HaAreaPicker extends LitElement {
|
|||
area_id: "no_areas",
|
||||
name: this.hass.localize("ui.components.area-picker.no_match"),
|
||||
picture: null,
|
||||
icon: null,
|
||||
aliases: [],
|
||||
},
|
||||
];
|
||||
|
@ -290,6 +298,7 @@ export class HaAreaPicker extends LitElement {
|
|||
area_id: "add_new",
|
||||
name: this.hass.localize("ui.components.area-picker.add_new"),
|
||||
picture: null,
|
||||
icon: "mdi:plus",
|
||||
aliases: [],
|
||||
},
|
||||
];
|
||||
|
|
|
@ -98,6 +98,7 @@ export class HaTargetPicker extends LitElement {
|
|||
area_id,
|
||||
area?.name || area_id,
|
||||
undefined,
|
||||
area?.icon,
|
||||
mdiSofa
|
||||
);
|
||||
})
|
||||
|
@ -110,6 +111,7 @@ export class HaTargetPicker extends LitElement {
|
|||
device_id,
|
||||
device ? computeDeviceName(device, this.hass) : device_id,
|
||||
undefined,
|
||||
undefined,
|
||||
mdiDevices
|
||||
);
|
||||
})
|
||||
|
@ -209,7 +211,8 @@ export class HaTargetPicker extends LitElement {
|
|||
id: string,
|
||||
name: string,
|
||||
entityState?: HassEntity,
|
||||
iconPath?: string
|
||||
icon?: string | null,
|
||||
fallbackIconPath?: string
|
||||
) {
|
||||
return html`
|
||||
<div
|
||||
|
@ -217,12 +220,17 @@ export class HaTargetPicker extends LitElement {
|
|||
[type]: true,
|
||||
})}"
|
||||
>
|
||||
${iconPath
|
||||
? html`<ha-svg-icon
|
||||
${icon
|
||||
? html`<ha-icon
|
||||
class="mdc-chip__icon mdc-chip__icon--leading"
|
||||
.path=${iconPath}
|
||||
></ha-svg-icon>`
|
||||
: ""}
|
||||
.icon=${icon}
|
||||
></ha-icon>`
|
||||
: fallbackIconPath
|
||||
? html`<ha-svg-icon
|
||||
class="mdc-chip__icon mdc-chip__icon--leading"
|
||||
.path=${fallbackIconPath}
|
||||
></ha-svg-icon>`
|
||||
: ""}
|
||||
${entityState
|
||||
? html`<ha-state-icon
|
||||
class="mdc-chip__icon mdc-chip__icon--leading"
|
||||
|
|
|
@ -9,6 +9,7 @@ export interface AreaRegistryEntry {
|
|||
area_id: string;
|
||||
name: string;
|
||||
picture: string | null;
|
||||
icon: string | null;
|
||||
aliases: string[];
|
||||
}
|
||||
|
||||
|
@ -23,6 +24,7 @@ export interface AreaDeviceLookup {
|
|||
export interface AreaRegistryEntryMutableParams {
|
||||
name: string;
|
||||
picture?: string | null;
|
||||
icon?: string | null;
|
||||
aliases?: string[];
|
||||
}
|
||||
|
||||
|
|
|
@ -1,19 +1,12 @@
|
|||
import { Connection, createCollection } from "home-assistant-js-websocket";
|
||||
import { Store } from "home-assistant-js-websocket/dist/store";
|
||||
import { stringCompare } from "../common/string/compare";
|
||||
import { AreaRegistryEntry } from "./area_registry";
|
||||
import { debounce } from "../common/util/debounce";
|
||||
import { AreaRegistryEntry } from "./area_registry";
|
||||
|
||||
const fetchAreaRegistry = (conn: Connection) =>
|
||||
conn
|
||||
.sendMessagePromise({
|
||||
type: "config/area_registry/list",
|
||||
})
|
||||
.then((areas) =>
|
||||
(areas as AreaRegistryEntry[]).sort((ent1, ent2) =>
|
||||
stringCompare(ent1.name, ent2.name)
|
||||
)
|
||||
);
|
||||
conn.sendMessagePromise<AreaRegistryEntry[]>({
|
||||
type: "config/area_registry/list",
|
||||
});
|
||||
|
||||
const subscribeAreaRegistryUpdates = (
|
||||
conn: Connection,
|
||||
|
|
|
@ -9,6 +9,7 @@ import { createCloseHeading } from "../../../components/ha-dialog";
|
|||
import "../../../components/ha-picture-upload";
|
||||
import type { HaPictureUpload } from "../../../components/ha-picture-upload";
|
||||
import "../../../components/ha-settings-row";
|
||||
import "../../../components/ha-icon-picker";
|
||||
import "../../../components/ha-textfield";
|
||||
import { AreaRegistryEntryMutableParams } from "../../../data/area_registry";
|
||||
import { CropOptions } from "../../../dialogs/image-cropper-dialog/show-image-cropper-dialog";
|
||||
|
@ -32,6 +33,8 @@ class DialogAreaDetail extends LitElement {
|
|||
|
||||
@state() private _picture!: string | null;
|
||||
|
||||
@state() private _icon!: string | null;
|
||||
|
||||
@state() private _error?: string;
|
||||
|
||||
@state() private _params?: AreaRegistryDetailDialogParams;
|
||||
|
@ -46,6 +49,7 @@ class DialogAreaDetail extends LitElement {
|
|||
this._name = this._params.entry ? this._params.entry.name : "";
|
||||
this._aliases = this._params.entry ? this._params.entry.aliases : [];
|
||||
this._picture = this._params.entry?.picture || null;
|
||||
this._icon = this._params.entry?.icon || null;
|
||||
await this.updateComplete;
|
||||
}
|
||||
|
||||
|
@ -101,6 +105,13 @@ class DialogAreaDetail extends LitElement {
|
|||
dialogInitialFocus
|
||||
></ha-textfield>
|
||||
|
||||
<ha-icon-picker
|
||||
.hass=${this.hass}
|
||||
.value=${this._icon}
|
||||
@value-changed=${this._iconChanged}
|
||||
.label=${this.hass.localize("ui.panel.config.areas.editor.icon")}
|
||||
></ha-icon-picker>
|
||||
|
||||
<ha-picture-upload
|
||||
.hass=${this.hass}
|
||||
.value=${this._picture}
|
||||
|
@ -152,23 +163,30 @@ class DialogAreaDetail extends LitElement {
|
|||
this._name = ev.target.value;
|
||||
}
|
||||
|
||||
private _iconChanged(ev) {
|
||||
this._error = undefined;
|
||||
this._icon = ev.detail.value;
|
||||
}
|
||||
|
||||
private _pictureChanged(ev: ValueChangedEvent<string | null>) {
|
||||
this._error = undefined;
|
||||
this._picture = (ev.target as HaPictureUpload).value;
|
||||
}
|
||||
|
||||
private async _updateEntry() {
|
||||
const create = !this._params!.entry;
|
||||
this._submitting = true;
|
||||
try {
|
||||
const values: AreaRegistryEntryMutableParams = {
|
||||
name: this._name.trim(),
|
||||
picture: this._picture,
|
||||
picture: this._picture || (create ? undefined : null),
|
||||
icon: this._icon || (create ? undefined : null),
|
||||
aliases: this._aliases,
|
||||
};
|
||||
if (this._params!.entry) {
|
||||
await this._params!.updateEntry!(values);
|
||||
} else {
|
||||
if (create) {
|
||||
await this._params!.createEntry!(values);
|
||||
} else {
|
||||
await this._params!.updateEntry!(values);
|
||||
}
|
||||
this.closeDialog();
|
||||
} catch (err: any) {
|
||||
|
@ -189,6 +207,7 @@ class DialogAreaDetail extends LitElement {
|
|||
haStyleDialog,
|
||||
css`
|
||||
ha-textfield,
|
||||
ha-icon-picker,
|
||||
ha-picture-upload {
|
||||
display: block;
|
||||
margin-bottom: 16px;
|
||||
|
|
|
@ -1,11 +1,9 @@
|
|||
import { consume } from "@lit-labs/context";
|
||||
import "@material/mwc-button";
|
||||
import "@material/mwc-list";
|
||||
import { mdiDelete, mdiDotsVertical, mdiImagePlus, mdiPencil } from "@mdi/js";
|
||||
import {
|
||||
HassEntity,
|
||||
UnsubscribeFunc,
|
||||
} from "home-assistant-js-websocket/dist/types";
|
||||
import { css, CSSResultGroup, html, LitElement, nothing } from "lit";
|
||||
import { HassEntity } from "home-assistant-js-websocket/dist/types";
|
||||
import { CSSResultGroup, LitElement, css, html, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { ifDefined } from "lit/directives/if-defined";
|
||||
import memoizeOne from "memoize-one";
|
||||
|
@ -18,33 +16,31 @@ import { afterNextRender } from "../../../common/util/render-status";
|
|||
import "../../../components/ha-card";
|
||||
import "../../../components/ha-icon-button";
|
||||
import "../../../components/ha-icon-next";
|
||||
import "../../../components/ha-list-item";
|
||||
import {
|
||||
AreaRegistryEntry,
|
||||
deleteAreaRegistryEntry,
|
||||
subscribeAreaRegistry,
|
||||
updateAreaRegistryEntry,
|
||||
} from "../../../data/area_registry";
|
||||
import { AutomationEntity } from "../../../data/automation";
|
||||
import { fullEntitiesContext } from "../../../data/context";
|
||||
import {
|
||||
computeDeviceName,
|
||||
DeviceRegistryEntry,
|
||||
computeDeviceName,
|
||||
sortDeviceRegistryByName,
|
||||
subscribeDeviceRegistry,
|
||||
} from "../../../data/device_registry";
|
||||
import {
|
||||
computeEntityRegistryName,
|
||||
EntityRegistryEntry,
|
||||
computeEntityRegistryName,
|
||||
sortEntityRegistryByName,
|
||||
subscribeEntityRegistry,
|
||||
} from "../../../data/entity_registry";
|
||||
import { SceneEntity } from "../../../data/scene";
|
||||
import { ScriptEntity } from "../../../data/script";
|
||||
import { findRelated, RelatedResult } from "../../../data/search";
|
||||
import { RelatedResult, findRelated } from "../../../data/search";
|
||||
import { showConfirmationDialog } from "../../../dialogs/generic/show-dialog-box";
|
||||
import { showMoreInfoDialog } from "../../../dialogs/more-info/show-ha-more-info-dialog";
|
||||
import "../../../layouts/hass-error-screen";
|
||||
import "../../../layouts/hass-subpage";
|
||||
import { SubscribeMixin } from "../../../mixins/subscribe-mixin";
|
||||
import { haStyle } from "../../../resources/styles";
|
||||
import { HomeAssistant } from "../../../types";
|
||||
import "../../logbook/ha-logbook";
|
||||
|
@ -52,7 +48,6 @@ import {
|
|||
loadAreaRegistryDetailDialog,
|
||||
showAreaRegistryDetailDialog,
|
||||
} from "./show-dialog-area-registry-detail";
|
||||
import "../../../components/ha-list-item";
|
||||
|
||||
declare type NameAndEntity<EntityType extends HassEntity> = {
|
||||
name: string;
|
||||
|
@ -60,7 +55,7 @@ declare type NameAndEntity<EntityType extends HassEntity> = {
|
|||
};
|
||||
|
||||
@customElement("ha-config-area-page")
|
||||
class HaConfigAreaPage extends SubscribeMixin(LitElement) {
|
||||
class HaConfigAreaPage extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@property() public areaId!: string;
|
||||
|
@ -71,24 +66,14 @@ class HaConfigAreaPage extends SubscribeMixin(LitElement) {
|
|||
|
||||
@property({ type: Boolean }) public showAdvanced = false;
|
||||
|
||||
@state() public _areas!: AreaRegistryEntry[];
|
||||
|
||||
@state() public _devices!: DeviceRegistryEntry[];
|
||||
|
||||
@state() public _entities!: EntityRegistryEntry[];
|
||||
@state()
|
||||
@consume({ context: fullEntitiesContext, subscribe: true })
|
||||
_entityReg!: EntityRegistryEntry[];
|
||||
|
||||
@state() private _related?: RelatedResult;
|
||||
|
||||
private _logbookTime = { recent: 86400 };
|
||||
|
||||
private _area = memoizeOne(
|
||||
(
|
||||
areaId: string,
|
||||
areas: AreaRegistryEntry[]
|
||||
): AreaRegistryEntry | undefined =>
|
||||
areas.find((area) => area.area_id === areaId)
|
||||
);
|
||||
|
||||
private _memberships = memoizeOne(
|
||||
(
|
||||
areaId: string,
|
||||
|
@ -150,26 +135,12 @@ class HaConfigAreaPage extends SubscribeMixin(LitElement) {
|
|||
}
|
||||
}
|
||||
|
||||
protected hassSubscribe(): (UnsubscribeFunc | Promise<UnsubscribeFunc>)[] {
|
||||
return [
|
||||
subscribeAreaRegistry(this.hass.connection, (areas) => {
|
||||
this._areas = areas;
|
||||
}),
|
||||
subscribeDeviceRegistry(this.hass.connection, (entries) => {
|
||||
this._devices = entries;
|
||||
}),
|
||||
subscribeEntityRegistry(this.hass.connection, (entries) => {
|
||||
this._entities = entries;
|
||||
}),
|
||||
];
|
||||
}
|
||||
|
||||
protected render() {
|
||||
if (!this._areas || !this._devices || !this._entities) {
|
||||
if (!this.hass.areas || !this.hass.devices || !this.hass.entities) {
|
||||
return nothing;
|
||||
}
|
||||
|
||||
const area = this._area(this.areaId, this._areas);
|
||||
const area = this.hass.areas[this.areaId];
|
||||
|
||||
if (!area) {
|
||||
return html`
|
||||
|
@ -182,8 +153,8 @@ class HaConfigAreaPage extends SubscribeMixin(LitElement) {
|
|||
|
||||
const memberships = this._memberships(
|
||||
this.areaId,
|
||||
this._devices,
|
||||
this._entities
|
||||
Object.values(this.hass.devices),
|
||||
this._entityReg
|
||||
);
|
||||
const { devices, entities } = memberships;
|
||||
|
||||
|
@ -617,7 +588,7 @@ class HaConfigAreaPage extends SubscribeMixin(LitElement) {
|
|||
}
|
||||
|
||||
private _renderScript(name: string, entityState: ScriptEntity) {
|
||||
const entry = this._entities.find(
|
||||
const entry = this._entityReg.find(
|
||||
(e) => e.entity_id === entityState.entity_id
|
||||
);
|
||||
let url = `/config/script/show/${entityState.entity_id}`;
|
||||
|
@ -657,7 +628,7 @@ class HaConfigAreaPage extends SubscribeMixin(LitElement) {
|
|||
}
|
||||
|
||||
private async _deleteConfirm() {
|
||||
const area = this._area(this.areaId, this._areas);
|
||||
const area = this.hass.areas[this.areaId];
|
||||
showConfirmationDialog(this, {
|
||||
title: this.hass.localize(
|
||||
"ui.panel.config.areas.delete.confirmation_title",
|
||||
|
@ -686,7 +657,6 @@ class HaConfigAreaPage extends SubscribeMixin(LitElement) {
|
|||
font-weight: 500;
|
||||
color: var(--secondary-text-color);
|
||||
}
|
||||
|
||||
img {
|
||||
border-radius: var(--ha-card-border-radius, 12px);
|
||||
width: 100%;
|
||||
|
|
|
@ -1,7 +1,13 @@
|
|||
import { mdiHelpCircle, mdiPlus } from "@mdi/js";
|
||||
import { UnsubscribeFunc } from "home-assistant-js-websocket";
|
||||
import { CSSResultGroup, LitElement, TemplateResult, css, html } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import {
|
||||
CSSResultGroup,
|
||||
LitElement,
|
||||
TemplateResult,
|
||||
css,
|
||||
html,
|
||||
nothing,
|
||||
} from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import { styleMap } from "lit/directives/style-map";
|
||||
import memoizeOne from "memoize-one";
|
||||
import { formatListWithAnds } from "../../../common/string/format-list";
|
||||
|
@ -11,19 +17,9 @@ import "../../../components/ha-svg-icon";
|
|||
import {
|
||||
AreaRegistryEntry,
|
||||
createAreaRegistryEntry,
|
||||
subscribeAreaRegistry,
|
||||
} from "../../../data/area_registry";
|
||||
import {
|
||||
DeviceRegistryEntry,
|
||||
subscribeDeviceRegistry,
|
||||
} from "../../../data/device_registry";
|
||||
import {
|
||||
EntityRegistryEntry,
|
||||
subscribeEntityRegistry,
|
||||
} from "../../../data/entity_registry";
|
||||
import { showAlertDialog } from "../../../dialogs/generic/show-dialog-box";
|
||||
import "../../../layouts/hass-tabs-subpage";
|
||||
import { SubscribeMixin } from "../../../mixins/subscribe-mixin";
|
||||
import { HomeAssistant, Route } from "../../../types";
|
||||
import "../ha-config-section";
|
||||
import { configSections } from "../ha-panel-config";
|
||||
|
@ -33,7 +29,7 @@ import {
|
|||
} from "./show-dialog-area-registry-detail";
|
||||
|
||||
@customElement("ha-config-areas-dashboard")
|
||||
export class HaConfigAreasDashboard extends SubscribeMixin(LitElement) {
|
||||
export class HaConfigAreasDashboard extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@property({ type: Boolean }) public isWide = false;
|
||||
|
@ -42,24 +38,18 @@ export class HaConfigAreasDashboard extends SubscribeMixin(LitElement) {
|
|||
|
||||
@property({ attribute: false }) public route!: Route;
|
||||
|
||||
@state() private _areas!: AreaRegistryEntry[];
|
||||
|
||||
@state() private _devices!: DeviceRegistryEntry[];
|
||||
|
||||
@state() private _entities!: EntityRegistryEntry[];
|
||||
|
||||
private _processAreas = memoizeOne(
|
||||
(
|
||||
areas: AreaRegistryEntry[],
|
||||
devices: DeviceRegistryEntry[],
|
||||
entities: EntityRegistryEntry[]
|
||||
) =>
|
||||
areas.map((area) => {
|
||||
areas: HomeAssistant["areas"],
|
||||
devices: HomeAssistant["devices"],
|
||||
entities: HomeAssistant["entities"]
|
||||
) => {
|
||||
const processArea = (area: AreaRegistryEntry) => {
|
||||
let noDevicesInArea = 0;
|
||||
let noServicesInArea = 0;
|
||||
let noEntitiesInArea = 0;
|
||||
|
||||
for (const device of devices) {
|
||||
for (const device of Object.values(devices)) {
|
||||
if (device.area_id === area.area_id) {
|
||||
if (device.entry_type === "service") {
|
||||
noServicesInArea++;
|
||||
|
@ -69,7 +59,7 @@ export class HaConfigAreasDashboard extends SubscribeMixin(LitElement) {
|
|||
}
|
||||
}
|
||||
|
||||
for (const entity of entities) {
|
||||
for (const entity of Object.values(entities)) {
|
||||
if (entity.area_id === area.area_id) {
|
||||
noEntitiesInArea++;
|
||||
}
|
||||
|
@ -81,24 +71,22 @@ export class HaConfigAreasDashboard extends SubscribeMixin(LitElement) {
|
|||
services: noServicesInArea,
|
||||
entities: noEntitiesInArea,
|
||||
};
|
||||
})
|
||||
};
|
||||
|
||||
return Object.values(areas).map(processArea);
|
||||
}
|
||||
);
|
||||
|
||||
protected hassSubscribe(): (UnsubscribeFunc | Promise<UnsubscribeFunc>)[] {
|
||||
return [
|
||||
subscribeAreaRegistry(this.hass.connection, (areas) => {
|
||||
this._areas = areas;
|
||||
}),
|
||||
subscribeDeviceRegistry(this.hass.connection, (entries) => {
|
||||
this._devices = entries;
|
||||
}),
|
||||
subscribeEntityRegistry(this.hass.connection, (entries) => {
|
||||
this._entities = entries;
|
||||
}),
|
||||
];
|
||||
}
|
||||
|
||||
protected render(): TemplateResult {
|
||||
const areas =
|
||||
!this.hass.areas || !this.hass.devices || !this.hass.entities
|
||||
? undefined
|
||||
: this._processAreas(
|
||||
this.hass.areas,
|
||||
this.hass.devices,
|
||||
this.hass.entities
|
||||
);
|
||||
|
||||
return html`
|
||||
<hass-tabs-subpage
|
||||
.hass=${this.hass}
|
||||
|
@ -115,52 +103,11 @@ export class HaConfigAreasDashboard extends SubscribeMixin(LitElement) {
|
|||
@click=${this._showHelp}
|
||||
></ha-icon-button>
|
||||
<div class="container">
|
||||
${!this._areas || !this._devices || !this._entities
|
||||
? ""
|
||||
: this._processAreas(
|
||||
this._areas,
|
||||
this._devices,
|
||||
this._entities
|
||||
).map(
|
||||
(area) =>
|
||||
html`<a href=${`/config/areas/area/${area.area_id}`}
|
||||
><ha-card outlined>
|
||||
<div
|
||||
style=${styleMap({
|
||||
backgroundImage: area.picture
|
||||
? `url(${area.picture})`
|
||||
: undefined,
|
||||
})}
|
||||
class="picture ${!area.picture ? "placeholder" : ""}"
|
||||
></div>
|
||||
<h1 class="card-header">${area.name}</h1>
|
||||
<div class="card-content">
|
||||
<div>
|
||||
${formatListWithAnds(
|
||||
this.hass.locale,
|
||||
[
|
||||
area.devices &&
|
||||
this.hass.localize(
|
||||
"ui.panel.config.integrations.config_entry.devices",
|
||||
{ count: area.devices }
|
||||
),
|
||||
area.services &&
|
||||
this.hass.localize(
|
||||
"ui.panel.config.integrations.config_entry.services",
|
||||
{ count: area.services }
|
||||
),
|
||||
area.entities &&
|
||||
this.hass.localize(
|
||||
"ui.panel.config.integrations.config_entry.entities",
|
||||
{ count: area.entities }
|
||||
),
|
||||
].filter((v): v is string => Boolean(v))
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</ha-card></a
|
||||
>`
|
||||
)}
|
||||
${areas?.length
|
||||
? html`<div class="areas">
|
||||
${areas.map((area) => this._renderArea(area))}
|
||||
</div>`
|
||||
: nothing}
|
||||
</div>
|
||||
<ha-fab
|
||||
slot="fab"
|
||||
|
@ -176,13 +123,55 @@ export class HaConfigAreasDashboard extends SubscribeMixin(LitElement) {
|
|||
`;
|
||||
}
|
||||
|
||||
private _renderArea(area) {
|
||||
return html`<a href=${`/config/areas/area/${area.area_id}`}>
|
||||
<ha-card outlined>
|
||||
<div
|
||||
style=${styleMap({
|
||||
backgroundImage: area.picture ? `url(${area.picture})` : undefined,
|
||||
})}
|
||||
class="picture ${!area.picture ? "placeholder" : ""}"
|
||||
>
|
||||
${!area.picture && area.icon
|
||||
? html`<ha-icon .icon=${area.icon}></ha-icon>`
|
||||
: ""}
|
||||
</div>
|
||||
<h1 class="card-header">${area.name}</h1>
|
||||
<div class="card-content">
|
||||
<div>
|
||||
${formatListWithAnds(
|
||||
this.hass.locale,
|
||||
[
|
||||
area.devices &&
|
||||
this.hass.localize(
|
||||
"ui.panel.config.integrations.config_entry.devices",
|
||||
{ count: area.devices }
|
||||
),
|
||||
area.services &&
|
||||
this.hass.localize(
|
||||
"ui.panel.config.integrations.config_entry.services",
|
||||
{ count: area.services }
|
||||
),
|
||||
area.entities &&
|
||||
this.hass.localize(
|
||||
"ui.panel.config.integrations.config_entry.entities",
|
||||
{ count: area.entities }
|
||||
),
|
||||
].filter((v): v is string => Boolean(v))
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</ha-card>
|
||||
</a>`;
|
||||
}
|
||||
|
||||
protected firstUpdated(changedProps) {
|
||||
super.firstUpdated(changedProps);
|
||||
loadAreaRegistryDetailDialog();
|
||||
}
|
||||
|
||||
private _createArea() {
|
||||
this._openDialog();
|
||||
this._openAreaDialog();
|
||||
}
|
||||
|
||||
private _showHelp() {
|
||||
|
@ -202,7 +191,7 @@ export class HaConfigAreasDashboard extends SubscribeMixin(LitElement) {
|
|||
});
|
||||
}
|
||||
|
||||
private _openDialog(entry?: AreaRegistryEntry) {
|
||||
private _openAreaDialog(entry?: AreaRegistryEntry) {
|
||||
showAreaRegistryDetailDialog(this, {
|
||||
entry,
|
||||
createEntry: async (values) =>
|
||||
|
@ -213,14 +202,17 @@ export class HaConfigAreasDashboard extends SubscribeMixin(LitElement) {
|
|||
static get styles(): CSSResultGroup {
|
||||
return css`
|
||||
.container {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
||||
grid-gap: 16px 16px;
|
||||
padding: 8px 16px 16px;
|
||||
margin: 0 auto 64px auto;
|
||||
max-width: 2000px;
|
||||
}
|
||||
.container > * {
|
||||
.areas {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
|
||||
grid-gap: 16px 16px;
|
||||
max-width: 2000px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
.areas > * {
|
||||
max-width: 500px;
|
||||
}
|
||||
ha-card {
|
||||
|
@ -239,6 +231,12 @@ export class HaConfigAreasDashboard extends SubscribeMixin(LitElement) {
|
|||
background-position: center;
|
||||
position: relative;
|
||||
}
|
||||
.placeholder {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
--mdc-icon-size: 48px;
|
||||
}
|
||||
.picture.placeholder::before {
|
||||
position: absolute;
|
||||
content: "";
|
||||
|
|
|
@ -1771,6 +1771,7 @@
|
|||
"update_area": "Update area",
|
||||
"delete": "Delete",
|
||||
"name": "Name",
|
||||
"icon": "Icon",
|
||||
"name_required": "Name is required",
|
||||
"area_id": "Area ID",
|
||||
"unknown_error": "Unknown error",
|
||||
|
|
Loading…
Reference in New Issue