mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-22 08:46:35 +00:00
Add area config page (#5343)
* Add area config page * Comments * Update ha-config-area-page.ts * Update ha-config-area-page.ts
This commit is contained in:
parent
f6dac98abd
commit
5a2e08647f
@ -53,6 +53,9 @@ export const fallbackDeviceName = (
|
|||||||
return undefined;
|
return undefined;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const devicesInArea = (devices: DeviceRegistryEntry[], areaId: string) =>
|
||||||
|
devices.filter((device) => device.area_id === areaId);
|
||||||
|
|
||||||
export const updateDeviceRegistryEntry = (
|
export const updateDeviceRegistryEntry = (
|
||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
deviceId: string,
|
deviceId: string,
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
import { fireEvent } from "../../common/dom/fire_event";
|
import { fireEvent } from "../../common/dom/fire_event";
|
||||||
|
import { TemplateResult } from "lit-html";
|
||||||
|
|
||||||
interface BaseDialogParams {
|
interface BaseDialogParams {
|
||||||
confirmText?: string;
|
confirmText?: string;
|
||||||
text?: string;
|
text?: string | TemplateResult;
|
||||||
title?: string;
|
title?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -88,6 +88,7 @@ export class HaTabsSubpageDataTable extends LitElement {
|
|||||||
.route=${this.route}
|
.route=${this.route}
|
||||||
.tabs=${this.tabs}
|
.tabs=${this.tabs}
|
||||||
>
|
>
|
||||||
|
<div slot="toolbar-icon"><slot name="toolbar-icon"></slot></div>
|
||||||
${this.narrow
|
${this.narrow
|
||||||
? html`
|
? html`
|
||||||
<div slot="header">
|
<div slot="header">
|
||||||
|
@ -109,9 +109,9 @@ class DialogAreaDetail extends LitElement {
|
|||||||
name: this._name.trim(),
|
name: this._name.trim(),
|
||||||
};
|
};
|
||||||
if (this._params!.entry) {
|
if (this._params!.entry) {
|
||||||
await this._params!.updateEntry(values);
|
await this._params!.updateEntry!(values);
|
||||||
} else {
|
} else {
|
||||||
await this._params!.createEntry(values);
|
await this._params!.createEntry!(values);
|
||||||
}
|
}
|
||||||
this._params = undefined;
|
this._params = undefined;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@ -124,7 +124,7 @@ class DialogAreaDetail extends LitElement {
|
|||||||
private async _deleteEntry() {
|
private async _deleteEntry() {
|
||||||
this._submitting = true;
|
this._submitting = true;
|
||||||
try {
|
try {
|
||||||
if (await this._params!.removeEntry()) {
|
if (await this._params!.removeEntry!()) {
|
||||||
this._params = undefined;
|
this._params = undefined;
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
|
397
src/panels/config/areas/ha-config-area-page.ts
Normal file
397
src/panels/config/areas/ha-config-area-page.ts
Normal file
@ -0,0 +1,397 @@
|
|||||||
|
import "@material/mwc-button";
|
||||||
|
import "@polymer/paper-dialog-scrollable/paper-dialog-scrollable";
|
||||||
|
import "@polymer/paper-input/paper-input";
|
||||||
|
import {
|
||||||
|
css,
|
||||||
|
CSSResult,
|
||||||
|
customElement,
|
||||||
|
html,
|
||||||
|
LitElement,
|
||||||
|
property,
|
||||||
|
TemplateResult,
|
||||||
|
} from "lit-element";
|
||||||
|
import "../../../components/dialog/ha-paper-dialog";
|
||||||
|
import { haStyle } from "../../../resources/styles";
|
||||||
|
import { HomeAssistant, Route } from "../../../types";
|
||||||
|
import memoizeOne from "memoize-one";
|
||||||
|
import {
|
||||||
|
AreaRegistryEntry,
|
||||||
|
updateAreaRegistryEntry,
|
||||||
|
deleteAreaRegistryEntry,
|
||||||
|
} from "../../../data/area_registry";
|
||||||
|
import {
|
||||||
|
DeviceRegistryEntry,
|
||||||
|
devicesInArea,
|
||||||
|
computeDeviceName,
|
||||||
|
} from "../../../data/device_registry";
|
||||||
|
import { configSections } from "../ha-panel-config";
|
||||||
|
import {
|
||||||
|
showAreaRegistryDetailDialog,
|
||||||
|
loadAreaRegistryDetailDialog,
|
||||||
|
} from "./show-dialog-area-registry-detail";
|
||||||
|
import { showConfirmationDialog } from "../../../dialogs/generic/show-dialog-box";
|
||||||
|
import { RelatedResult, findRelated } from "../../../data/search";
|
||||||
|
import { isComponentLoaded } from "../../../common/config/is_component_loaded";
|
||||||
|
import { computeStateName } from "../../../common/entity/compute_state_name";
|
||||||
|
import { ifDefined } from "lit-html/directives/if-defined";
|
||||||
|
|
||||||
|
@customElement("ha-config-area-page")
|
||||||
|
class HaConfigAreaPage extends LitElement {
|
||||||
|
@property() public hass!: HomeAssistant;
|
||||||
|
@property() public areaId!: string;
|
||||||
|
@property() public areas!: AreaRegistryEntry[];
|
||||||
|
@property() public devices!: DeviceRegistryEntry[];
|
||||||
|
@property({ type: Boolean, reflect: true }) public narrow!: boolean;
|
||||||
|
@property() public isWide!: boolean;
|
||||||
|
@property() public showAdvanced!: boolean;
|
||||||
|
@property() public route!: Route;
|
||||||
|
@property() private _related?: RelatedResult;
|
||||||
|
|
||||||
|
private _area = memoizeOne((areaId: string, areas: AreaRegistryEntry[]):
|
||||||
|
| AreaRegistryEntry
|
||||||
|
| undefined => areas.find((area) => area.area_id === areaId));
|
||||||
|
|
||||||
|
private _devices = memoizeOne(
|
||||||
|
(areaId: string, devices: DeviceRegistryEntry[]): DeviceRegistryEntry[] =>
|
||||||
|
devicesInArea(devices, areaId)
|
||||||
|
);
|
||||||
|
|
||||||
|
protected firstUpdated(changedProps) {
|
||||||
|
super.firstUpdated(changedProps);
|
||||||
|
loadAreaRegistryDetailDialog();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected updated(changedProps) {
|
||||||
|
super.updated(changedProps);
|
||||||
|
if (changedProps.has("areaId")) {
|
||||||
|
this._findRelated();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected render(): TemplateResult {
|
||||||
|
const area = this._area(this.areaId, this.areas);
|
||||||
|
|
||||||
|
if (!area) {
|
||||||
|
return html`
|
||||||
|
<hass-error-screen
|
||||||
|
error="${this.hass.localize("ui.panel.config.areas.area_not_found")}"
|
||||||
|
></hass-error-screen>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
const devices = this._devices(this.areaId, this.devices);
|
||||||
|
|
||||||
|
return html`
|
||||||
|
<hass-tabs-subpage
|
||||||
|
.hass=${this.hass}
|
||||||
|
.narrow=${this.narrow}
|
||||||
|
.tabs=${configSections.integrations}
|
||||||
|
.route=${this.route}
|
||||||
|
>
|
||||||
|
${this.narrow
|
||||||
|
? html`
|
||||||
|
<span slot="header">
|
||||||
|
${area.name}
|
||||||
|
</span>
|
||||||
|
`
|
||||||
|
: ""}
|
||||||
|
|
||||||
|
<paper-icon-button
|
||||||
|
slot="toolbar-icon"
|
||||||
|
icon="hass:settings"
|
||||||
|
.entry=${area}
|
||||||
|
@click=${this._showSettings}
|
||||||
|
></paper-icon-button>
|
||||||
|
|
||||||
|
<div class="container">
|
||||||
|
${!this.narrow
|
||||||
|
? html`
|
||||||
|
<div class="fullwidth">
|
||||||
|
<h1>${area.name}</h1>
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
: ""}
|
||||||
|
<div class="column">
|
||||||
|
<ha-card
|
||||||
|
.header=${this.hass.localize("ui.panel.config.devices.caption")}
|
||||||
|
>${devices.length
|
||||||
|
? devices.map(
|
||||||
|
(device) =>
|
||||||
|
html`
|
||||||
|
<a href="/config/devices/device/${device.id}">
|
||||||
|
<paper-item>
|
||||||
|
<paper-item-body>
|
||||||
|
${computeDeviceName(device, this.hass)}
|
||||||
|
</paper-item-body>
|
||||||
|
<ha-icon-next></ha-icon-next>
|
||||||
|
</paper-item>
|
||||||
|
</a>
|
||||||
|
`
|
||||||
|
)
|
||||||
|
: html`
|
||||||
|
<paper-item class="no-link"
|
||||||
|
>${this.hass.localize(
|
||||||
|
"ui.panel.config.devices.no_devices"
|
||||||
|
)}</paper-item
|
||||||
|
>
|
||||||
|
`}
|
||||||
|
</ha-card>
|
||||||
|
</div>
|
||||||
|
<div class="column">
|
||||||
|
${isComponentLoaded(this.hass, "automation")
|
||||||
|
? html`
|
||||||
|
<ha-card
|
||||||
|
.header=${this.hass.localize(
|
||||||
|
"ui.panel.config.devices.automation.automations"
|
||||||
|
)}
|
||||||
|
>${this._related?.automation?.length
|
||||||
|
? this._related.automation.map((automation) => {
|
||||||
|
const state = this.hass.states[automation];
|
||||||
|
return state
|
||||||
|
? html`
|
||||||
|
<div>
|
||||||
|
<a
|
||||||
|
href=${ifDefined(
|
||||||
|
state.attributes.id
|
||||||
|
? `/config/automation/edit/${state.attributes.id}`
|
||||||
|
: undefined
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<paper-item
|
||||||
|
.disabled=${!state.attributes.id}
|
||||||
|
>
|
||||||
|
<paper-item-body>
|
||||||
|
${computeStateName(state)}
|
||||||
|
</paper-item-body>
|
||||||
|
<ha-icon-next></ha-icon-next>
|
||||||
|
</paper-item>
|
||||||
|
</a>
|
||||||
|
${!state.attributes.id
|
||||||
|
? html`
|
||||||
|
<paper-tooltip
|
||||||
|
>${this.hass.localize(
|
||||||
|
"ui.panel.config.devices.cant_edit"
|
||||||
|
)}
|
||||||
|
</paper-tooltip>
|
||||||
|
`
|
||||||
|
: ""}
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
: "";
|
||||||
|
})
|
||||||
|
: html`
|
||||||
|
<paper-item class="no-link"
|
||||||
|
>${this.hass.localize(
|
||||||
|
"ui.panel.config.devices.automation.no_automations"
|
||||||
|
)}</paper-item
|
||||||
|
>
|
||||||
|
`}
|
||||||
|
</ha-card>
|
||||||
|
`
|
||||||
|
: ""}
|
||||||
|
</div>
|
||||||
|
<div class="column">
|
||||||
|
${isComponentLoaded(this.hass, "scene")
|
||||||
|
? html`
|
||||||
|
<ha-card
|
||||||
|
.header=${this.hass.localize(
|
||||||
|
"ui.panel.config.devices.scene.scenes"
|
||||||
|
)}
|
||||||
|
>${this._related?.scene?.length
|
||||||
|
? this._related.scene.map((scene) => {
|
||||||
|
const state = this.hass.states[scene];
|
||||||
|
return state
|
||||||
|
? html`
|
||||||
|
<div>
|
||||||
|
<a
|
||||||
|
href=${ifDefined(
|
||||||
|
state.attributes.id
|
||||||
|
? `/config/scene/edit/${state.attributes.id}`
|
||||||
|
: undefined
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<paper-item
|
||||||
|
.disabled=${!state.attributes.id}
|
||||||
|
>
|
||||||
|
<paper-item-body>
|
||||||
|
${computeStateName(state)}
|
||||||
|
</paper-item-body>
|
||||||
|
<ha-icon-next></ha-icon-next>
|
||||||
|
</paper-item>
|
||||||
|
</a>
|
||||||
|
${!state.attributes.id
|
||||||
|
? html`
|
||||||
|
<paper-tooltip
|
||||||
|
>${this.hass.localize(
|
||||||
|
"ui.panel.config.devices.cant_edit"
|
||||||
|
)}
|
||||||
|
</paper-tooltip>
|
||||||
|
`
|
||||||
|
: ""}
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
: "";
|
||||||
|
})
|
||||||
|
: html`
|
||||||
|
<paper-item class="no-link"
|
||||||
|
>${this.hass.localize(
|
||||||
|
"ui.panel.config.devices.scene.no_scenes"
|
||||||
|
)}</paper-item
|
||||||
|
>
|
||||||
|
`}
|
||||||
|
</ha-card>
|
||||||
|
`
|
||||||
|
: ""}
|
||||||
|
${isComponentLoaded(this.hass, "script")
|
||||||
|
? html`
|
||||||
|
<ha-card
|
||||||
|
.header=${this.hass.localize(
|
||||||
|
"ui.panel.config.devices.script.scripts"
|
||||||
|
)}
|
||||||
|
>${this._related?.script?.length
|
||||||
|
? this._related.script.map((script) => {
|
||||||
|
const state = this.hass.states[script];
|
||||||
|
return state
|
||||||
|
? html`
|
||||||
|
<a
|
||||||
|
href=${ifDefined(
|
||||||
|
state.attributes.id
|
||||||
|
? `/config/script/edit/${state.attributes.id}`
|
||||||
|
: undefined
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<paper-item>
|
||||||
|
<paper-item-body>
|
||||||
|
${computeStateName(state)}
|
||||||
|
</paper-item-body>
|
||||||
|
<ha-icon-next></ha-icon-next>
|
||||||
|
</paper-item>
|
||||||
|
</a>
|
||||||
|
`
|
||||||
|
: "";
|
||||||
|
})
|
||||||
|
: html`
|
||||||
|
<paper-item class="no-link">
|
||||||
|
${this.hass.localize(
|
||||||
|
"ui.panel.config.devices.script.no_scripts"
|
||||||
|
)}</paper-item
|
||||||
|
>
|
||||||
|
`}
|
||||||
|
</ha-card>
|
||||||
|
`
|
||||||
|
: ""}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</hass-tabs-subpage>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _findRelated() {
|
||||||
|
this._related = await findRelated(this.hass, "area", this.areaId);
|
||||||
|
}
|
||||||
|
|
||||||
|
private _showSettings(ev: MouseEvent) {
|
||||||
|
const entry: AreaRegistryEntry = (ev.currentTarget! as any).entry;
|
||||||
|
this._openDialog(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
private _openDialog(entry?: AreaRegistryEntry) {
|
||||||
|
showAreaRegistryDetailDialog(this, {
|
||||||
|
entry,
|
||||||
|
updateEntry: async (values) =>
|
||||||
|
updateAreaRegistryEntry(this.hass!, entry!.area_id, values),
|
||||||
|
removeEntry: async () => {
|
||||||
|
if (
|
||||||
|
!(await showConfirmationDialog(this, {
|
||||||
|
title: this.hass.localize(
|
||||||
|
"ui.panel.config.areas.delete.confirmation_title"
|
||||||
|
),
|
||||||
|
text: this.hass.localize(
|
||||||
|
"ui.panel.config.areas.delete.confirmation_text"
|
||||||
|
),
|
||||||
|
dismissText: this.hass.localize("ui.common.no"),
|
||||||
|
confirmText: this.hass.localize("ui.common.yes"),
|
||||||
|
}))
|
||||||
|
) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await deleteAreaRegistryEntry(this.hass!, entry!.area_id);
|
||||||
|
return true;
|
||||||
|
} catch (err) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static get styles(): CSSResult[] {
|
||||||
|
return [
|
||||||
|
haStyle,
|
||||||
|
css`
|
||||||
|
h1 {
|
||||||
|
margin-top: 0;
|
||||||
|
font-family: var(--paper-font-headline_-_font-family);
|
||||||
|
-webkit-font-smoothing: var(
|
||||||
|
--paper-font-headline_-_-webkit-font-smoothing
|
||||||
|
);
|
||||||
|
font-size: var(--paper-font-headline_-_font-size);
|
||||||
|
font-weight: var(--paper-font-headline_-_font-weight);
|
||||||
|
letter-spacing: var(--paper-font-headline_-_letter-spacing);
|
||||||
|
line-height: var(--paper-font-headline_-_line-height);
|
||||||
|
opacity: var(--dark-primary-opacity);
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
margin: auto;
|
||||||
|
max-width: 1000px;
|
||||||
|
margin-top: 32px;
|
||||||
|
margin-bottom: 32px;
|
||||||
|
}
|
||||||
|
.column {
|
||||||
|
padding: 8px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
width: 33%;
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
.fullwidth {
|
||||||
|
padding: 8px;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
.column > *:not(:first-child) {
|
||||||
|
margin-top: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
:host([narrow]) .column {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
:host([narrow]) .container {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
paper-item {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
text-decoration: none;
|
||||||
|
color: var(--primary-text-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
paper-item.no-link {
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"ha-config-area-page": HaConfigAreaPage;
|
||||||
|
}
|
||||||
|
}
|
217
src/panels/config/areas/ha-config-areas-dashboard.ts
Normal file
217
src/panels/config/areas/ha-config-areas-dashboard.ts
Normal file
@ -0,0 +1,217 @@
|
|||||||
|
import {
|
||||||
|
LitElement,
|
||||||
|
TemplateResult,
|
||||||
|
html,
|
||||||
|
css,
|
||||||
|
CSSResult,
|
||||||
|
property,
|
||||||
|
customElement,
|
||||||
|
} from "lit-element";
|
||||||
|
import "@polymer/paper-item/paper-item";
|
||||||
|
import "@polymer/paper-item/paper-item-body";
|
||||||
|
|
||||||
|
import { HomeAssistant, Route } from "../../../types";
|
||||||
|
import {
|
||||||
|
AreaRegistryEntry,
|
||||||
|
createAreaRegistryEntry,
|
||||||
|
} from "../../../data/area_registry";
|
||||||
|
import "../../../components/ha-fab";
|
||||||
|
import "../../../layouts/hass-loading-screen";
|
||||||
|
import "../../../layouts/hass-tabs-subpage-data-table";
|
||||||
|
import "../ha-config-section";
|
||||||
|
import {
|
||||||
|
showAreaRegistryDetailDialog,
|
||||||
|
loadAreaRegistryDetailDialog,
|
||||||
|
} from "./show-dialog-area-registry-detail";
|
||||||
|
import { configSections } from "../ha-panel-config";
|
||||||
|
import memoizeOne from "memoize-one";
|
||||||
|
import {
|
||||||
|
DataTableColumnContainer,
|
||||||
|
RowClickedEvent,
|
||||||
|
} from "../../../components/data-table/ha-data-table";
|
||||||
|
import {
|
||||||
|
devicesInArea,
|
||||||
|
DeviceRegistryEntry,
|
||||||
|
} from "../../../data/device_registry";
|
||||||
|
import { navigate } from "../../../common/navigate";
|
||||||
|
import { HASSDomEvent } from "../../../common/dom/fire_event";
|
||||||
|
import { showAlertDialog } from "../../../dialogs/generic/show-dialog-box";
|
||||||
|
|
||||||
|
@customElement("ha-config-areas-dashboard")
|
||||||
|
export class HaConfigAreasDashboard extends LitElement {
|
||||||
|
@property() public hass!: HomeAssistant;
|
||||||
|
@property() public isWide?: boolean;
|
||||||
|
@property() public narrow!: boolean;
|
||||||
|
@property() public route!: Route;
|
||||||
|
@property() public areas!: AreaRegistryEntry[];
|
||||||
|
@property() public devices!: DeviceRegistryEntry[];
|
||||||
|
|
||||||
|
private _areas = memoizeOne(
|
||||||
|
(areas: AreaRegistryEntry[], devices: DeviceRegistryEntry[]) => {
|
||||||
|
return areas.map((area) => {
|
||||||
|
return {
|
||||||
|
...area,
|
||||||
|
devices: devicesInArea(devices, area.area_id).length,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
private _columns = memoizeOne(
|
||||||
|
(narrow: boolean): DataTableColumnContainer =>
|
||||||
|
narrow
|
||||||
|
? {
|
||||||
|
name: {
|
||||||
|
title: this.hass.localize(
|
||||||
|
"ui.panel.config.areas.data_table.area"
|
||||||
|
),
|
||||||
|
sortable: true,
|
||||||
|
filterable: true,
|
||||||
|
grows: true,
|
||||||
|
direction: "asc",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
: {
|
||||||
|
name: {
|
||||||
|
title: this.hass.localize(
|
||||||
|
"ui.panel.config.areas.data_table.area"
|
||||||
|
),
|
||||||
|
sortable: true,
|
||||||
|
filterable: true,
|
||||||
|
grows: true,
|
||||||
|
direction: "asc",
|
||||||
|
},
|
||||||
|
devices: {
|
||||||
|
title: this.hass.localize(
|
||||||
|
"ui.panel.config.areas.data_table.devices"
|
||||||
|
),
|
||||||
|
sortable: true,
|
||||||
|
type: "numeric",
|
||||||
|
width: "20%",
|
||||||
|
direction: "asc",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
protected render(): TemplateResult {
|
||||||
|
return html`
|
||||||
|
<hass-tabs-subpage-data-table
|
||||||
|
.hass=${this.hass}
|
||||||
|
.narrow=${this.narrow}
|
||||||
|
back-path="/config"
|
||||||
|
.tabs=${configSections.integrations}
|
||||||
|
.route=${this.route}
|
||||||
|
.columns=${this._columns(this.narrow)}
|
||||||
|
.data=${this._areas(this.areas, this.devices)}
|
||||||
|
@row-click=${this._handleRowClicked}
|
||||||
|
.noDataText=${this.hass.localize(
|
||||||
|
"ui.panel.config.areas.picker.no_areas"
|
||||||
|
)}
|
||||||
|
id="area_id"
|
||||||
|
>
|
||||||
|
<paper-icon-button
|
||||||
|
slot="toolbar-icon"
|
||||||
|
icon="hass:help-circle"
|
||||||
|
@click=${this._showHelp}
|
||||||
|
></paper-icon-button>
|
||||||
|
</hass-tabs-subpage-data-table>
|
||||||
|
<ha-fab
|
||||||
|
?is-wide=${this.isWide}
|
||||||
|
?narrow=${this.narrow}
|
||||||
|
icon="hass:plus"
|
||||||
|
title="${this.hass.localize(
|
||||||
|
"ui.panel.config.areas.picker.create_area"
|
||||||
|
)}"
|
||||||
|
@click=${this._createArea}
|
||||||
|
></ha-fab>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected firstUpdated(changedProps) {
|
||||||
|
super.firstUpdated(changedProps);
|
||||||
|
loadAreaRegistryDetailDialog();
|
||||||
|
}
|
||||||
|
|
||||||
|
private _createArea() {
|
||||||
|
this._openDialog();
|
||||||
|
}
|
||||||
|
|
||||||
|
private _showHelp() {
|
||||||
|
showAlertDialog(this, {
|
||||||
|
title: this.hass.localize("ui.panel.config.areas.caption"),
|
||||||
|
text: html`
|
||||||
|
${this.hass.localize("ui.panel.config.areas.picker.introduction")}
|
||||||
|
<p>
|
||||||
|
${this.hass.localize("ui.panel.config.areas.picker.introduction2")}
|
||||||
|
</p>
|
||||||
|
<a href="/config/integrations/dashboard">
|
||||||
|
${this.hass.localize(
|
||||||
|
"ui.panel.config.areas.picker.integrations_page"
|
||||||
|
)}
|
||||||
|
</a>
|
||||||
|
`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private _handleRowClicked(ev: HASSDomEvent<RowClickedEvent>) {
|
||||||
|
const areaId = ev.detail.id;
|
||||||
|
navigate(this, `/config/areas/area/${areaId}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
private _openDialog(entry?: AreaRegistryEntry) {
|
||||||
|
showAreaRegistryDetailDialog(this, {
|
||||||
|
entry,
|
||||||
|
createEntry: async (values) =>
|
||||||
|
createAreaRegistryEntry(this.hass!, values),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static get styles(): CSSResult {
|
||||||
|
return css`
|
||||||
|
hass-loading-screen {
|
||||||
|
--app-header-background-color: var(--sidebar-background-color);
|
||||||
|
--app-header-text-color: var(--sidebar-text-color);
|
||||||
|
}
|
||||||
|
a {
|
||||||
|
color: var(--primary-color);
|
||||||
|
}
|
||||||
|
ha-card {
|
||||||
|
max-width: 600px;
|
||||||
|
margin: 16px auto;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
.empty {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
paper-item {
|
||||||
|
cursor: pointer;
|
||||||
|
padding-top: 4px;
|
||||||
|
padding-bottom: 4px;
|
||||||
|
}
|
||||||
|
ha-fab {
|
||||||
|
position: fixed;
|
||||||
|
bottom: 16px;
|
||||||
|
right: 16px;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
ha-fab[is-wide] {
|
||||||
|
bottom: 24px;
|
||||||
|
right: 24px;
|
||||||
|
}
|
||||||
|
ha-fab[narrow] {
|
||||||
|
bottom: 84px;
|
||||||
|
}
|
||||||
|
ha-fab.rtl {
|
||||||
|
right: auto;
|
||||||
|
left: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
ha-fab[is-wide].rtl {
|
||||||
|
bottom: 24px;
|
||||||
|
right: auto;
|
||||||
|
left: 24px;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
}
|
@ -1,226 +1,120 @@
|
|||||||
|
import "./ha-config-areas-dashboard";
|
||||||
|
import "./ha-config-area-page";
|
||||||
|
import { compare } from "../../../common/string/compare";
|
||||||
import {
|
import {
|
||||||
LitElement,
|
|
||||||
TemplateResult,
|
|
||||||
html,
|
|
||||||
css,
|
|
||||||
CSSResult,
|
|
||||||
property,
|
|
||||||
customElement,
|
|
||||||
} from "lit-element";
|
|
||||||
import "@polymer/paper-item/paper-item";
|
|
||||||
import "@polymer/paper-item/paper-item-body";
|
|
||||||
|
|
||||||
import { HomeAssistant, Route } from "../../../types";
|
|
||||||
import {
|
|
||||||
AreaRegistryEntry,
|
|
||||||
updateAreaRegistryEntry,
|
|
||||||
deleteAreaRegistryEntry,
|
|
||||||
createAreaRegistryEntry,
|
|
||||||
subscribeAreaRegistry,
|
subscribeAreaRegistry,
|
||||||
|
AreaRegistryEntry,
|
||||||
} from "../../../data/area_registry";
|
} from "../../../data/area_registry";
|
||||||
import "../../../components/ha-card";
|
|
||||||
import "../../../components/ha-fab";
|
|
||||||
import "../../../layouts/hass-tabs-subpage";
|
|
||||||
import "../../../layouts/hass-loading-screen";
|
|
||||||
import "../ha-config-section";
|
|
||||||
import {
|
import {
|
||||||
showAreaRegistryDetailDialog,
|
HassRouterPage,
|
||||||
loadAreaRegistryDetailDialog,
|
RouterOptions,
|
||||||
} from "./show-dialog-area-registry-detail";
|
} from "../../../layouts/hass-router-page";
|
||||||
import { classMap } from "lit-html/directives/class-map";
|
import { property, customElement, PropertyValues } from "lit-element";
|
||||||
import { computeRTL } from "../../../common/util/compute_rtl";
|
import { HomeAssistant } from "../../../types";
|
||||||
|
import { ConfigEntry, getConfigEntries } from "../../../data/config_entries";
|
||||||
|
import {
|
||||||
|
DeviceRegistryEntry,
|
||||||
|
subscribeDeviceRegistry,
|
||||||
|
} from "../../../data/device_registry";
|
||||||
import { UnsubscribeFunc } from "home-assistant-js-websocket";
|
import { UnsubscribeFunc } from "home-assistant-js-websocket";
|
||||||
import { configSections } from "../ha-panel-config";
|
|
||||||
import { showConfirmationDialog } from "../../../dialogs/generic/show-dialog-box";
|
|
||||||
|
|
||||||
@customElement("ha-config-areas")
|
@customElement("ha-config-areas")
|
||||||
export class HaConfigAreas extends LitElement {
|
class HaConfigAreas extends HassRouterPage {
|
||||||
@property() public hass!: HomeAssistant;
|
@property() public hass!: HomeAssistant;
|
||||||
@property() public isWide?: boolean;
|
|
||||||
@property() public narrow!: boolean;
|
@property() public narrow!: boolean;
|
||||||
@property() public route!: Route;
|
@property() public isWide!: boolean;
|
||||||
@property() private _areas?: AreaRegistryEntry[];
|
@property() public showAdvanced!: boolean;
|
||||||
private _unsubAreas?: UnsubscribeFunc;
|
|
||||||
|
protected routerOptions: RouterOptions = {
|
||||||
|
defaultPage: "dashboard",
|
||||||
|
routes: {
|
||||||
|
dashboard: {
|
||||||
|
tag: "ha-config-areas-dashboard",
|
||||||
|
cache: true,
|
||||||
|
},
|
||||||
|
area: {
|
||||||
|
tag: "ha-config-area-page",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
@property() private _configEntries: ConfigEntry[] = [];
|
||||||
|
@property() private _deviceRegistryEntries: DeviceRegistryEntry[] = [];
|
||||||
|
@property() private _areas: AreaRegistryEntry[] = [];
|
||||||
|
|
||||||
|
private _unsubs?: UnsubscribeFunc[];
|
||||||
|
|
||||||
|
public connectedCallback() {
|
||||||
|
super.connectedCallback();
|
||||||
|
|
||||||
|
if (!this.hass) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this._loadData();
|
||||||
|
}
|
||||||
|
|
||||||
public disconnectedCallback() {
|
public disconnectedCallback() {
|
||||||
super.disconnectedCallback();
|
super.disconnectedCallback();
|
||||||
if (this._unsubAreas) {
|
if (this._unsubs) {
|
||||||
this._unsubAreas();
|
while (this._unsubs.length) {
|
||||||
|
this._unsubs.pop()!();
|
||||||
|
}
|
||||||
|
this._unsubs = undefined;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
|
||||||
if (!this.hass || this._areas === undefined) {
|
|
||||||
return html`
|
|
||||||
<hass-loading-screen></hass-loading-screen>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
return html`
|
|
||||||
<hass-tabs-subpage
|
|
||||||
.hass=${this.hass}
|
|
||||||
.narrow=${this.narrow}
|
|
||||||
back-path="/config"
|
|
||||||
.route=${this.route}
|
|
||||||
.tabs=${configSections.integrations}
|
|
||||||
>
|
|
||||||
<ha-config-section .isWide=${this.isWide}>
|
|
||||||
<span slot="header">
|
|
||||||
${this.hass.localize("ui.panel.config.areas.picker.header")}
|
|
||||||
</span>
|
|
||||||
<span slot="introduction">
|
|
||||||
${this.hass.localize("ui.panel.config.areas.picker.introduction")}
|
|
||||||
<p>
|
|
||||||
${this.hass.localize(
|
|
||||||
"ui.panel.config.areas.picker.introduction2"
|
|
||||||
)}
|
|
||||||
</p>
|
|
||||||
<a href="/config/integrations/dashboard">
|
|
||||||
${this.hass.localize(
|
|
||||||
"ui.panel.config.areas.picker.integrations_page"
|
|
||||||
)}
|
|
||||||
</a>
|
|
||||||
</span>
|
|
||||||
<ha-card>
|
|
||||||
${this._areas.map((entry) => {
|
|
||||||
return html`
|
|
||||||
<paper-item @click=${this._openEditEntry} .entry=${entry}>
|
|
||||||
<paper-item-body>
|
|
||||||
${entry.name}
|
|
||||||
</paper-item-body>
|
|
||||||
</paper-item>
|
|
||||||
`;
|
|
||||||
})}
|
|
||||||
${this._areas.length === 0
|
|
||||||
? html`
|
|
||||||
<div class="empty">
|
|
||||||
${this.hass.localize("ui.panel.config.areas.no_areas")}
|
|
||||||
<mwc-button @click=${this._createArea}>
|
|
||||||
${this.hass.localize("ui.panel.config.areas.create_area")}
|
|
||||||
</mwc-button>
|
|
||||||
</div>
|
|
||||||
`
|
|
||||||
: html``}
|
|
||||||
</ha-card>
|
|
||||||
</ha-config-section>
|
|
||||||
</hass-tabs-subpage>
|
|
||||||
|
|
||||||
<ha-fab
|
|
||||||
?is-wide=${this.isWide}
|
|
||||||
?narrow=${this.narrow}
|
|
||||||
icon="hass:plus"
|
|
||||||
title="${this.hass.localize("ui.panel.config.areas.create_area")}"
|
|
||||||
@click=${this._createArea}
|
|
||||||
class="${classMap({
|
|
||||||
rtl: computeRTL(this.hass),
|
|
||||||
})}"
|
|
||||||
></ha-fab>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected firstUpdated(changedProps) {
|
protected firstUpdated(changedProps) {
|
||||||
super.firstUpdated(changedProps);
|
super.firstUpdated(changedProps);
|
||||||
loadAreaRegistryDetailDialog();
|
this.addEventListener("hass-reload-entries", () => {
|
||||||
}
|
this._loadData();
|
||||||
|
|
||||||
protected updated(changedProps) {
|
|
||||||
super.updated(changedProps);
|
|
||||||
if (!this._unsubAreas) {
|
|
||||||
this._unsubAreas = subscribeAreaRegistry(
|
|
||||||
this.hass.connection,
|
|
||||||
(areas) => {
|
|
||||||
this._areas = areas;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private _createArea() {
|
|
||||||
this._openDialog();
|
|
||||||
}
|
|
||||||
|
|
||||||
private _openEditEntry(ev: MouseEvent) {
|
|
||||||
const entry: AreaRegistryEntry = (ev.currentTarget! as any).entry;
|
|
||||||
this._openDialog(entry);
|
|
||||||
}
|
|
||||||
private _openDialog(entry?: AreaRegistryEntry) {
|
|
||||||
showAreaRegistryDetailDialog(this, {
|
|
||||||
entry,
|
|
||||||
createEntry: async (values) =>
|
|
||||||
createAreaRegistryEntry(this.hass!, values),
|
|
||||||
updateEntry: async (values) =>
|
|
||||||
updateAreaRegistryEntry(this.hass!, entry!.area_id, values),
|
|
||||||
removeEntry: async () => {
|
|
||||||
if (
|
|
||||||
!(await showConfirmationDialog(this, {
|
|
||||||
title: this.hass.localize(
|
|
||||||
"ui.panel.config.areas.delete.confirmation_title"
|
|
||||||
),
|
|
||||||
text: this.hass.localize(
|
|
||||||
"ui.panel.config.areas.delete.confirmation_text"
|
|
||||||
),
|
|
||||||
dismissText: this.hass.localize("ui.common.no"),
|
|
||||||
confirmText: this.hass.localize("ui.common.yes"),
|
|
||||||
}))
|
|
||||||
) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
await deleteAreaRegistryEntry(this.hass!, entry!.area_id);
|
|
||||||
return true;
|
|
||||||
} catch (err) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
static get styles(): CSSResult {
|
protected updated(changedProps: PropertyValues) {
|
||||||
return css`
|
super.updated(changedProps);
|
||||||
hass-loading-screen {
|
if (!this._unsubs && changedProps.has("hass")) {
|
||||||
--app-header-background-color: var(--sidebar-background-color);
|
this._loadData();
|
||||||
--app-header-text-color: var(--sidebar-text-color);
|
}
|
||||||
}
|
}
|
||||||
a {
|
|
||||||
color: var(--primary-color);
|
|
||||||
}
|
|
||||||
ha-card {
|
|
||||||
max-width: 600px;
|
|
||||||
margin: 16px auto;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
.empty {
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
paper-item {
|
|
||||||
cursor: pointer;
|
|
||||||
padding-top: 4px;
|
|
||||||
padding-bottom: 4px;
|
|
||||||
}
|
|
||||||
ha-fab {
|
|
||||||
position: fixed;
|
|
||||||
bottom: 16px;
|
|
||||||
right: 16px;
|
|
||||||
z-index: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
ha-fab[is-wide] {
|
protected updatePageEl(pageEl) {
|
||||||
bottom: 24px;
|
pageEl.hass = this.hass;
|
||||||
right: 24px;
|
|
||||||
}
|
|
||||||
ha-fab[narrow] {
|
|
||||||
bottom: 84px;
|
|
||||||
}
|
|
||||||
ha-fab.rtl {
|
|
||||||
right: auto;
|
|
||||||
left: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
ha-fab[is-wide].rtl {
|
if (this._currentPage === "area") {
|
||||||
bottom: 24px;
|
pageEl.areaId = this.routeTail.path.substr(1);
|
||||||
right: auto;
|
}
|
||||||
left: 24px;
|
|
||||||
}
|
pageEl.entries = this._configEntries;
|
||||||
`;
|
pageEl.devices = this._deviceRegistryEntries;
|
||||||
|
pageEl.areas = this._areas;
|
||||||
|
pageEl.narrow = this.narrow;
|
||||||
|
pageEl.isWide = this.isWide;
|
||||||
|
pageEl.showAdvanced = this.showAdvanced;
|
||||||
|
pageEl.route = this.routeTail;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _loadData() {
|
||||||
|
getConfigEntries(this.hass).then((configEntries) => {
|
||||||
|
this._configEntries = configEntries.sort((conf1, conf2) =>
|
||||||
|
compare(conf1.title, conf2.title)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
if (this._unsubs) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this._unsubs = [
|
||||||
|
subscribeAreaRegistry(this.hass.connection, (areas) => {
|
||||||
|
this._areas = areas;
|
||||||
|
}),
|
||||||
|
subscribeDeviceRegistry(this.hass.connection, (entries) => {
|
||||||
|
this._deviceRegistryEntries = entries;
|
||||||
|
}),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"ha-config-areas": HaConfigAreas;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,11 +6,11 @@ import {
|
|||||||
|
|
||||||
export interface AreaRegistryDetailDialogParams {
|
export interface AreaRegistryDetailDialogParams {
|
||||||
entry?: AreaRegistryEntry;
|
entry?: AreaRegistryEntry;
|
||||||
createEntry: (values: AreaRegistryEntryMutableParams) => Promise<unknown>;
|
createEntry?: (values: AreaRegistryEntryMutableParams) => Promise<unknown>;
|
||||||
updateEntry: (
|
updateEntry?: (
|
||||||
updates: Partial<AreaRegistryEntryMutableParams>
|
updates: Partial<AreaRegistryEntryMutableParams>
|
||||||
) => Promise<unknown>;
|
) => Promise<unknown>;
|
||||||
removeEntry: () => Promise<boolean>;
|
removeEntry?: () => Promise<boolean>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const loadAreaRegistryDetailDialog = () =>
|
export const loadAreaRegistryDetailDialog = () =>
|
||||||
|
@ -785,6 +785,10 @@
|
|||||||
"areas": {
|
"areas": {
|
||||||
"caption": "Areas",
|
"caption": "Areas",
|
||||||
"description": "Overview of all areas in your home.",
|
"description": "Overview of all areas in your home.",
|
||||||
|
"data_table": {
|
||||||
|
"area": "Area",
|
||||||
|
"devices": "Devices"
|
||||||
|
},
|
||||||
"picker": {
|
"picker": {
|
||||||
"header": "Areas",
|
"header": "Areas",
|
||||||
"introduction": "Areas are used to organize where devices are. This information will be used throughout Home Assistant to help you in organizing your interface, permissions and integrations with other systems.",
|
"introduction": "Areas are used to organize where devices are. This information will be used throughout Home Assistant to help you in organizing your interface, permissions and integrations with other systems.",
|
||||||
@ -1442,6 +1446,7 @@
|
|||||||
"unknown_error": "Unknown error",
|
"unknown_error": "Unknown error",
|
||||||
"name": "Name",
|
"name": "Name",
|
||||||
"update": "Update",
|
"update": "Update",
|
||||||
|
"no_devices": "No devices",
|
||||||
"automation": {
|
"automation": {
|
||||||
"automations": "Automations",
|
"automations": "Automations",
|
||||||
"no_automations": "No automations",
|
"no_automations": "No automations",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user