mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-30 20:56:36 +00:00
Add Area Registry (#2631)
This commit is contained in:
parent
b86bfa0395
commit
f1f1623d2f
39
src/data/area_registry.ts
Normal file
39
src/data/area_registry.ts
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
import { HomeAssistant } from "../types";
|
||||||
|
|
||||||
|
export interface AreaRegistryEntry {
|
||||||
|
area_id: string;
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AreaRegistryEntryMutableParams {
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const fetchAreaRegistry = (hass: HomeAssistant) =>
|
||||||
|
hass.callWS<AreaRegistryEntry[]>({ type: "config/area_registry/list" });
|
||||||
|
|
||||||
|
export const createAreaRegistryEntry = (
|
||||||
|
hass: HomeAssistant,
|
||||||
|
values: AreaRegistryEntryMutableParams
|
||||||
|
) =>
|
||||||
|
hass.callWS<AreaRegistryEntry>({
|
||||||
|
type: "config/area_registry/create",
|
||||||
|
...values,
|
||||||
|
});
|
||||||
|
|
||||||
|
export const updateAreaRegistryEntry = (
|
||||||
|
hass: HomeAssistant,
|
||||||
|
areaId: string,
|
||||||
|
updates: Partial<AreaRegistryEntryMutableParams>
|
||||||
|
) =>
|
||||||
|
hass.callWS<AreaRegistryEntry>({
|
||||||
|
type: "config/area_registry/update",
|
||||||
|
area_id: areaId,
|
||||||
|
...updates,
|
||||||
|
});
|
||||||
|
|
||||||
|
export const deleteAreaRegistryEntry = (hass: HomeAssistant, areaId: string) =>
|
||||||
|
hass.callWS({
|
||||||
|
type: "config/area_registry/delete",
|
||||||
|
area_id: areaId,
|
||||||
|
});
|
31
src/data/device_registry.ts
Normal file
31
src/data/device_registry.ts
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
import { HomeAssistant } from "../types";
|
||||||
|
|
||||||
|
export interface DeviceRegistryEntry {
|
||||||
|
id: string;
|
||||||
|
config_entries: string[];
|
||||||
|
connections: Array<[string, string]>;
|
||||||
|
manufacturer: string;
|
||||||
|
model?: string;
|
||||||
|
name?: string;
|
||||||
|
sw_version?: string;
|
||||||
|
hub_device_id?: string;
|
||||||
|
area_id?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DeviceRegistryEntryMutableParams {
|
||||||
|
area_id: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const fetchDeviceRegistry = (hass: HomeAssistant) =>
|
||||||
|
hass.callWS<DeviceRegistryEntry[]>({ type: "config/device_registry/list" });
|
||||||
|
|
||||||
|
export const updateDeviceRegistryEntry = (
|
||||||
|
hass: HomeAssistant,
|
||||||
|
deviceId: string,
|
||||||
|
updates: Partial<DeviceRegistryEntryMutableParams>
|
||||||
|
) =>
|
||||||
|
hass.callWS<DeviceRegistryEntry>({
|
||||||
|
type: "config/device_registry/update",
|
||||||
|
device_id: deviceId,
|
||||||
|
...updates,
|
||||||
|
});
|
160
src/panels/config/area_registry/dialog-area-registry-detail.ts
Normal file
160
src/panels/config/area_registry/dialog-area-registry-detail.ts
Normal file
@ -0,0 +1,160 @@
|
|||||||
|
import {
|
||||||
|
LitElement,
|
||||||
|
html,
|
||||||
|
css,
|
||||||
|
PropertyDeclarations,
|
||||||
|
CSSResult,
|
||||||
|
TemplateResult,
|
||||||
|
} from "lit-element";
|
||||||
|
import "@polymer/paper-dialog/paper-dialog";
|
||||||
|
import "@polymer/paper-dialog-scrollable/paper-dialog-scrollable";
|
||||||
|
import "@polymer/paper-input/paper-input";
|
||||||
|
|
||||||
|
import { AreaRegistryDetailDialogParams } from "./show-dialog-area-registry-detail";
|
||||||
|
import { PolymerChangedEvent } from "../../../polymer-types";
|
||||||
|
import { haStyleDialog } from "../../../resources/ha-style";
|
||||||
|
import { HomeAssistant } from "../../../types";
|
||||||
|
import { AreaRegistryEntryMutableParams } from "../../../data/area_registry";
|
||||||
|
|
||||||
|
class DialogAreaDetail extends LitElement {
|
||||||
|
public hass!: HomeAssistant;
|
||||||
|
private _name!: string;
|
||||||
|
private _error?: string;
|
||||||
|
private _params?: AreaRegistryDetailDialogParams;
|
||||||
|
private _submitting?: boolean;
|
||||||
|
|
||||||
|
static get properties(): PropertyDeclarations {
|
||||||
|
return {
|
||||||
|
_error: {},
|
||||||
|
_name: {},
|
||||||
|
_params: {},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public async showDialog(
|
||||||
|
params: AreaRegistryDetailDialogParams
|
||||||
|
): Promise<void> {
|
||||||
|
this._params = params;
|
||||||
|
this._error = undefined;
|
||||||
|
this._name = this._params.entry ? this._params.entry.name : "";
|
||||||
|
await this.updateComplete;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected render(): TemplateResult | void {
|
||||||
|
if (!this._params) {
|
||||||
|
return html``;
|
||||||
|
}
|
||||||
|
const nameInvalid = this._name.trim() === "";
|
||||||
|
return html`
|
||||||
|
<paper-dialog
|
||||||
|
with-backdrop
|
||||||
|
opened
|
||||||
|
@opened-changed="${this._openedChanged}"
|
||||||
|
>
|
||||||
|
<h2>${this._params.entry ? this._params.entry.name : "New Area"}</h2>
|
||||||
|
<paper-dialog-scrollable>
|
||||||
|
${this._error
|
||||||
|
? html`
|
||||||
|
<div class="error">${this._error}</div>
|
||||||
|
`
|
||||||
|
: ""}
|
||||||
|
<div class="form">
|
||||||
|
<paper-input
|
||||||
|
.value=${this._name}
|
||||||
|
@value-changed=${this._nameChanged}
|
||||||
|
label="Name"
|
||||||
|
error-message="Name is required"
|
||||||
|
.invalid=${nameInvalid}
|
||||||
|
></paper-input>
|
||||||
|
</div>
|
||||||
|
</paper-dialog-scrollable>
|
||||||
|
<div class="paper-dialog-buttons">
|
||||||
|
${this._params.entry
|
||||||
|
? html`
|
||||||
|
<paper-button
|
||||||
|
class="danger"
|
||||||
|
@click="${this._deleteEntry}"
|
||||||
|
.disabled=${this._submitting}
|
||||||
|
>
|
||||||
|
DELETE
|
||||||
|
</paper-button>
|
||||||
|
`
|
||||||
|
: html``}
|
||||||
|
<paper-button
|
||||||
|
@click="${this._updateEntry}"
|
||||||
|
.disabled=${nameInvalid || this._submitting}
|
||||||
|
>
|
||||||
|
${this._params.entry ? "UPDATE" : "CREATE"}
|
||||||
|
</paper-button>
|
||||||
|
</div>
|
||||||
|
</paper-dialog>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _nameChanged(ev: PolymerChangedEvent<string>) {
|
||||||
|
this._error = undefined;
|
||||||
|
this._name = ev.detail.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _updateEntry() {
|
||||||
|
try {
|
||||||
|
const values: AreaRegistryEntryMutableParams = {
|
||||||
|
name: this._name.trim(),
|
||||||
|
};
|
||||||
|
if (this._params!.entry) {
|
||||||
|
await this._params!.updateEntry(values);
|
||||||
|
} else {
|
||||||
|
await this._params!.createEntry(values);
|
||||||
|
}
|
||||||
|
this._params = undefined;
|
||||||
|
} catch (err) {
|
||||||
|
this._error = err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _deleteEntry() {
|
||||||
|
if (await this._params!.removeEntry()) {
|
||||||
|
this._params = undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private _openedChanged(ev: PolymerChangedEvent<boolean>): void {
|
||||||
|
if (!(ev.detail as any).value) {
|
||||||
|
this._params = undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static get styles(): CSSResult[] {
|
||||||
|
return [
|
||||||
|
haStyleDialog,
|
||||||
|
css`
|
||||||
|
paper-dialog {
|
||||||
|
min-width: 400px;
|
||||||
|
}
|
||||||
|
.form {
|
||||||
|
padding-bottom: 24px;
|
||||||
|
}
|
||||||
|
paper-button {
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
paper-button.danger {
|
||||||
|
font-weight: 500;
|
||||||
|
color: var(--google-red-500);
|
||||||
|
margin-left: -12px;
|
||||||
|
margin-right: auto;
|
||||||
|
}
|
||||||
|
.error {
|
||||||
|
color: var(--google-red-500);
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"dialog-area-registry-detail": DialogAreaDetail;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
customElements.define("dialog-area-registry-detail", DialogAreaDetail);
|
190
src/panels/config/area_registry/ha-config-area-registry.ts
Normal file
190
src/panels/config/area_registry/ha-config-area-registry.ts
Normal file
@ -0,0 +1,190 @@
|
|||||||
|
import {
|
||||||
|
LitElement,
|
||||||
|
TemplateResult,
|
||||||
|
html,
|
||||||
|
css,
|
||||||
|
CSSResult,
|
||||||
|
PropertyDeclarations,
|
||||||
|
} from "lit-element";
|
||||||
|
import "@polymer/paper-item/paper-item";
|
||||||
|
import "@polymer/paper-item/paper-item-body";
|
||||||
|
import "@polymer/paper-card/paper-card";
|
||||||
|
import "@polymer/paper-fab/paper-fab";
|
||||||
|
|
||||||
|
import { HomeAssistant } from "../../../types";
|
||||||
|
import {
|
||||||
|
AreaRegistryEntry,
|
||||||
|
fetchAreaRegistry,
|
||||||
|
updateAreaRegistryEntry,
|
||||||
|
deleteAreaRegistryEntry,
|
||||||
|
createAreaRegistryEntry,
|
||||||
|
} from "../../../data/area_registry";
|
||||||
|
import "../../../layouts/hass-subpage";
|
||||||
|
import "../../../layouts/hass-loading-screen";
|
||||||
|
import compare from "../../../common/string/compare";
|
||||||
|
import "../ha-config-section";
|
||||||
|
import {
|
||||||
|
showAreaRegistryDetailDialog,
|
||||||
|
loadAreaRegistryDetailDialog,
|
||||||
|
} from "./show-dialog-area-registry-detail";
|
||||||
|
|
||||||
|
class HaConfigAreaRegistry extends LitElement {
|
||||||
|
public hass?: HomeAssistant;
|
||||||
|
public isWide?: boolean;
|
||||||
|
private _items?: AreaRegistryEntry[];
|
||||||
|
|
||||||
|
static get properties(): PropertyDeclarations {
|
||||||
|
return {
|
||||||
|
hass: {},
|
||||||
|
isWide: {},
|
||||||
|
_items: {},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
protected render(): TemplateResult | void {
|
||||||
|
if (!this.hass || this._items === undefined) {
|
||||||
|
return html`
|
||||||
|
<hass-loading-screen></hass-loading-screen>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
return html`
|
||||||
|
<hass-subpage header="Area Registry">
|
||||||
|
<ha-config-section .isWide=${this.isWide}>
|
||||||
|
<span slot="header">Area Registry</span>
|
||||||
|
<span slot="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.
|
||||||
|
<p>
|
||||||
|
To place devices in an area, navigate to
|
||||||
|
<a href="/config/integrations">the integrations page</a> and then
|
||||||
|
click on a configured integration to get to the device cards.
|
||||||
|
</p>
|
||||||
|
</span>
|
||||||
|
<paper-card>
|
||||||
|
${this._items.map((entry) => {
|
||||||
|
return html`
|
||||||
|
<paper-item @click=${this._openEditEntry} .entry=${entry}>
|
||||||
|
<paper-item-body>
|
||||||
|
${entry.name}
|
||||||
|
</paper-item-body>
|
||||||
|
</paper-item>
|
||||||
|
`;
|
||||||
|
})}
|
||||||
|
${this._items.length === 0
|
||||||
|
? html`
|
||||||
|
<div class="empty">
|
||||||
|
Looks like you have no areas yet!
|
||||||
|
<paper-button @click=${this._createArea}>
|
||||||
|
CREATE AREA</paper-button
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
: html``}
|
||||||
|
</paper-card>
|
||||||
|
</ha-config-section>
|
||||||
|
</hass-subpage>
|
||||||
|
|
||||||
|
<paper-fab
|
||||||
|
?is-wide=${this.isWide}
|
||||||
|
icon="hass:plus"
|
||||||
|
title="Create Area"
|
||||||
|
@click=${this._createArea}
|
||||||
|
></paper-fab>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected firstUpdated(changedProps) {
|
||||||
|
super.firstUpdated(changedProps);
|
||||||
|
this._fetchData();
|
||||||
|
loadAreaRegistryDetailDialog();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _fetchData() {
|
||||||
|
this._items = (await fetchAreaRegistry(this.hass!)).sort((ent1, ent2) =>
|
||||||
|
compare(ent1.name, ent2.name)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
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) => {
|
||||||
|
const created = await createAreaRegistryEntry(this.hass!, values);
|
||||||
|
this._items = this._items!.concat(created).sort((ent1, ent2) =>
|
||||||
|
compare(ent1.name, ent2.name)
|
||||||
|
);
|
||||||
|
},
|
||||||
|
updateEntry: async (values) => {
|
||||||
|
const updated = await updateAreaRegistryEntry(
|
||||||
|
this.hass!,
|
||||||
|
entry!.area_id,
|
||||||
|
values
|
||||||
|
);
|
||||||
|
this._items = this._items!.map((ent) =>
|
||||||
|
ent === entry ? updated : ent
|
||||||
|
);
|
||||||
|
},
|
||||||
|
removeEntry: async () => {
|
||||||
|
if (
|
||||||
|
!confirm(`Are you sure you want to delete this area?
|
||||||
|
|
||||||
|
All devices in this area will become unassigned.`)
|
||||||
|
) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await deleteAreaRegistryEntry(this.hass!, entry!.area_id);
|
||||||
|
this._items = this._items!.filter((ent) => ent !== entry);
|
||||||
|
return true;
|
||||||
|
} catch (err) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static get styles(): CSSResult {
|
||||||
|
return css`
|
||||||
|
a {
|
||||||
|
color: var(--primary-color);
|
||||||
|
}
|
||||||
|
paper-card {
|
||||||
|
display: block;
|
||||||
|
max-width: 600px;
|
||||||
|
margin: 16px auto;
|
||||||
|
background-color: white;
|
||||||
|
}
|
||||||
|
.empty {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
paper-item {
|
||||||
|
cursor: pointer;
|
||||||
|
padding-top: 4px;
|
||||||
|
padding-bottom: 4px;
|
||||||
|
}
|
||||||
|
paper-fab {
|
||||||
|
position: fixed;
|
||||||
|
bottom: 16px;
|
||||||
|
right: 16px;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
paper-fab[is-wide] {
|
||||||
|
bottom: 24px;
|
||||||
|
right: 24px;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
customElements.define("ha-config-area-registry", HaConfigAreaRegistry);
|
@ -0,0 +1,28 @@
|
|||||||
|
import { fireEvent } from "../../../common/dom/fire_event";
|
||||||
|
import {
|
||||||
|
AreaRegistryEntry,
|
||||||
|
AreaRegistryEntryMutableParams,
|
||||||
|
} from "../../../data/area_registry";
|
||||||
|
|
||||||
|
export interface AreaRegistryDetailDialogParams {
|
||||||
|
entry?: AreaRegistryEntry;
|
||||||
|
createEntry: (values: AreaRegistryEntryMutableParams) => Promise<unknown>;
|
||||||
|
updateEntry: (
|
||||||
|
updates: Partial<AreaRegistryEntryMutableParams>
|
||||||
|
) => Promise<unknown>;
|
||||||
|
removeEntry: () => Promise<boolean>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const loadAreaRegistryDetailDialog = () =>
|
||||||
|
import(/* webpackChunkName: "entity-registry-detail-dialog" */ "./dialog-area-registry-detail");
|
||||||
|
|
||||||
|
export const showAreaRegistryDetailDialog = (
|
||||||
|
element: HTMLElement,
|
||||||
|
systemLogDetailParams: AreaRegistryDetailDialogParams
|
||||||
|
): void => {
|
||||||
|
fireEvent(element, "show-dialog", {
|
||||||
|
dialogTag: "dialog-area-registry-detail",
|
||||||
|
dialogImport: loadAreaRegistryDetailDialog,
|
||||||
|
dialogParams: systemLogDetailParams,
|
||||||
|
});
|
||||||
|
};
|
@ -8,6 +8,7 @@ import "./ha-config-entries-dashboard";
|
|||||||
import "./ha-config-entry-page";
|
import "./ha-config-entry-page";
|
||||||
import NavigateMixin from "../../../mixins/navigate-mixin";
|
import NavigateMixin from "../../../mixins/navigate-mixin";
|
||||||
import compare from "../../../common/string/compare";
|
import compare from "../../../common/string/compare";
|
||||||
|
import { fetchAreaRegistry } from "../../../data/area_registry";
|
||||||
|
|
||||||
class HaConfigEntries extends NavigateMixin(PolymerElement) {
|
class HaConfigEntries extends NavigateMixin(PolymerElement) {
|
||||||
static get template() {
|
static get template() {
|
||||||
@ -23,6 +24,7 @@ class HaConfigEntries extends NavigateMixin(PolymerElement) {
|
|||||||
<ha-config-entry-page
|
<ha-config-entry-page
|
||||||
hass="[[hass]]"
|
hass="[[hass]]"
|
||||||
config-entry="[[_configEntry]]"
|
config-entry="[[_configEntry]]"
|
||||||
|
areas="[[_areas]]"
|
||||||
entries="[[_entries]]"
|
entries="[[_entries]]"
|
||||||
entities="[[_entities]]"
|
entities="[[_entities]]"
|
||||||
devices="[[_devices]]"
|
devices="[[_devices]]"
|
||||||
@ -68,6 +70,11 @@ class HaConfigEntries extends NavigateMixin(PolymerElement) {
|
|||||||
*/
|
*/
|
||||||
_devices: Array,
|
_devices: Array,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Area Registry entries.
|
||||||
|
*/
|
||||||
|
_areas: Array,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Current flows that are in progress and have not been started by a user.
|
* Current flows that are in progress and have not been started by a user.
|
||||||
* For example, can be discovered devices that require more config.
|
* For example, can be discovered devices that require more config.
|
||||||
@ -136,6 +143,10 @@ class HaConfigEntries extends NavigateMixin(PolymerElement) {
|
|||||||
.then((devices) => {
|
.then((devices) => {
|
||||||
this._devices = devices;
|
this._devices = devices;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
fetchAreaRegistry(this.hass).then((areas) => {
|
||||||
|
this._areas = areas;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
_computeConfigEntry(routeData, entries) {
|
_computeConfigEntry(routeData, entries) {
|
||||||
|
@ -53,6 +53,7 @@ class HaConfigEntryPage extends NavigateMixin(
|
|||||||
<ha-device-card
|
<ha-device-card
|
||||||
class="card"
|
class="card"
|
||||||
hass="[[hass]]"
|
hass="[[hass]]"
|
||||||
|
areas="[[areas]]"
|
||||||
devices="[[devices]]"
|
devices="[[devices]]"
|
||||||
device="[[device]]"
|
device="[[device]]"
|
||||||
entities="[[entities]]"
|
entities="[[entities]]"
|
||||||
@ -97,6 +98,11 @@ class HaConfigEntryPage extends NavigateMixin(
|
|||||||
computed: "_computeNoDeviceEntities(configEntry, entities)",
|
computed: "_computeNoDeviceEntities(configEntry, entities)",
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Area registry entries
|
||||||
|
*/
|
||||||
|
areas: Array,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Device registry entries
|
* Device registry entries
|
||||||
*/
|
*/
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
import "@polymer/paper-item/paper-icon-item";
|
import "@polymer/paper-item/paper-icon-item";
|
||||||
import "@polymer/paper-item/paper-item-body";
|
import "@polymer/paper-item/paper-item-body";
|
||||||
import "@polymer/paper-card/paper-card";
|
import "@polymer/paper-card/paper-card";
|
||||||
|
import "@polymer/paper-dropdown-menu/paper-dropdown-menu";
|
||||||
|
import "@polymer/paper-item/paper-item";
|
||||||
|
import "@polymer/paper-listbox/paper-listbox";
|
||||||
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
||||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
||||||
|
|
||||||
@ -11,6 +14,7 @@ import LocalizeMixin from "../../../mixins/localize-mixin";
|
|||||||
import computeStateName from "../../../common/entity/compute_state_name";
|
import computeStateName from "../../../common/entity/compute_state_name";
|
||||||
import "../../../components/entity/state-badge";
|
import "../../../components/entity/state-badge";
|
||||||
import compare from "../../../common/string/compare";
|
import compare from "../../../common/string/compare";
|
||||||
|
import { updateDeviceRegistryEntry } from "../../../data/device_registry";
|
||||||
|
|
||||||
function computeEntityName(hass, entity) {
|
function computeEntityName(hass, entity) {
|
||||||
if (entity.name) return entity.name;
|
if (entity.name) return entity.name;
|
||||||
@ -68,6 +72,22 @@ class HaDeviceCard extends EventsMixin(LocalizeMixin(PolymerElement)) {
|
|||||||
[[localize('ui.panel.config.integrations.config_entry.manuf',
|
[[localize('ui.panel.config.integrations.config_entry.manuf',
|
||||||
'manufacturer', device.manufacturer)]]
|
'manufacturer', device.manufacturer)]]
|
||||||
</div>
|
</div>
|
||||||
|
<div class="area">
|
||||||
|
<paper-dropdown-menu
|
||||||
|
selected-item-label="{{selectedArea}}"
|
||||||
|
label="Area"
|
||||||
|
>
|
||||||
|
<paper-listbox
|
||||||
|
slot="dropdown-content"
|
||||||
|
selected="[[_computeSelectedArea(areas, device)]]"
|
||||||
|
>
|
||||||
|
<paper-item>No Area</paper-item>
|
||||||
|
<template is="dom-repeat" items="[[areas]]">
|
||||||
|
<paper-item area="[[item]]">[[item.name]]</paper-item>
|
||||||
|
</template>
|
||||||
|
</paper-listbox>
|
||||||
|
</paper-dropdown-menu>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<template is="dom-if" if="[[device.hub_device_id]]">
|
<template is="dom-if" if="[[device.hub_device_id]]">
|
||||||
<div class="extra-info">
|
<div class="extra-info">
|
||||||
@ -111,6 +131,7 @@ class HaDeviceCard extends EventsMixin(LocalizeMixin(PolymerElement)) {
|
|||||||
return {
|
return {
|
||||||
device: Object,
|
device: Object,
|
||||||
devices: Array,
|
devices: Array,
|
||||||
|
areas: Array,
|
||||||
entities: Array,
|
entities: Array,
|
||||||
hass: Object,
|
hass: Object,
|
||||||
narrow: {
|
narrow: {
|
||||||
@ -121,9 +142,43 @@ class HaDeviceCard extends EventsMixin(LocalizeMixin(PolymerElement)) {
|
|||||||
type: Array,
|
type: Array,
|
||||||
computed: "_computeChildDevices(device, devices)",
|
computed: "_computeChildDevices(device, devices)",
|
||||||
},
|
},
|
||||||
|
selectedArea: {
|
||||||
|
type: String,
|
||||||
|
observer: "_selectedAreaChanged",
|
||||||
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_computeSelectedArea(areas, device) {
|
||||||
|
if (!areas || !device || !device.area_id) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
// +1 because of "No Area" entry
|
||||||
|
return areas.findIndex((area) => area.area_id === device.area_id) + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
async _selectedAreaChanged(option) {
|
||||||
|
// Selected Option will transition to '' before transitioning to new value
|
||||||
|
if (option === "" || !this.device || !this.areas) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const area =
|
||||||
|
option === "No Area"
|
||||||
|
? undefined
|
||||||
|
: this.areas.find((ar) => ar.name === option);
|
||||||
|
|
||||||
|
if (
|
||||||
|
(!area && !this.device.area_id) ||
|
||||||
|
(area && area.area_id === this.device.area_id)
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await updateDeviceRegistryEntry(this.hass, this.device.id, {
|
||||||
|
area_id: area ? area.area_id : null,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
_computeChildDevices(device, devices) {
|
_computeChildDevices(device, devices) {
|
||||||
return devices
|
return devices
|
||||||
.filter((dev) => dev.hub_device_id === device.id)
|
.filter((dev) => dev.hub_device_id === device.id)
|
||||||
|
@ -10,7 +10,7 @@ import LocalizeMixin from "../../../mixins/localize-mixin";
|
|||||||
|
|
||||||
import isComponentLoaded from "../../../common/config/is_component_loaded";
|
import isComponentLoaded from "../../../common/config/is_component_loaded";
|
||||||
|
|
||||||
const CORE_PAGES = ["core", "customize", "entity_registry"];
|
const CORE_PAGES = ["core", "customize", "entity_registry", "area_registry"];
|
||||||
/*
|
/*
|
||||||
* @appliesMixin LocalizeMixin
|
* @appliesMixin LocalizeMixin
|
||||||
* @appliesMixin NavigateMixin
|
* @appliesMixin NavigateMixin
|
||||||
@ -54,6 +54,7 @@ class HaConfigNavigation extends LocalizeMixin(NavigateMixin(PolymerElement)) {
|
|||||||
"core",
|
"core",
|
||||||
"customize",
|
"customize",
|
||||||
"entity_registry",
|
"entity_registry",
|
||||||
|
"area_registry",
|
||||||
"automation",
|
"automation",
|
||||||
"script",
|
"script",
|
||||||
"zha",
|
"zha",
|
||||||
|
@ -9,6 +9,7 @@ import isComponentLoaded from "../../common/config/is_component_loaded";
|
|||||||
import EventsMixin from "../../mixins/events-mixin";
|
import EventsMixin from "../../mixins/events-mixin";
|
||||||
import NavigateMixin from "../../mixins/navigate-mixin";
|
import NavigateMixin from "../../mixins/navigate-mixin";
|
||||||
|
|
||||||
|
import(/* webpackChunkName: "panel-config-area-registry" */ "./area_registry/ha-config-area-registry");
|
||||||
import(/* webpackChunkName: "panel-config-automation" */ "./automation/ha-config-automation");
|
import(/* webpackChunkName: "panel-config-automation" */ "./automation/ha-config-automation");
|
||||||
import(/* webpackChunkName: "panel-config-cloud" */ "./cloud/ha-config-cloud");
|
import(/* webpackChunkName: "panel-config-cloud" */ "./cloud/ha-config-cloud");
|
||||||
import(/* webpackChunkName: "panel-config-config" */ "./config-entries/ha-config-entries");
|
import(/* webpackChunkName: "panel-config-config" */ "./config-entries/ha-config-entries");
|
||||||
@ -42,6 +43,19 @@ class HaPanelConfig extends EventsMixin(NavigateMixin(PolymerElement)) {
|
|||||||
>
|
>
|
||||||
</iron-media-query>
|
</iron-media-query>
|
||||||
|
|
||||||
|
<template
|
||||||
|
is="dom-if"
|
||||||
|
if='[[_equals(_routeData.page, "area_registry")]]'
|
||||||
|
restamp
|
||||||
|
>
|
||||||
|
<ha-config-area-registry
|
||||||
|
page-name="area_registry"
|
||||||
|
route="[[route]]"
|
||||||
|
hass="[[hass]]"
|
||||||
|
is-wide="[[isWide]]"
|
||||||
|
></ha-config-area-registry>
|
||||||
|
</template>
|
||||||
|
|
||||||
<template is="dom-if" if='[[_equals(_routeData.page, "core")]]' restamp>
|
<template is="dom-if" if='[[_equals(_routeData.page, "core")]]' restamp>
|
||||||
<ha-config-core
|
<ha-config-core
|
||||||
page-name="core"
|
page-name="core"
|
||||||
|
@ -527,6 +527,10 @@
|
|||||||
"config": {
|
"config": {
|
||||||
"header": "Configure Home Assistant",
|
"header": "Configure Home Assistant",
|
||||||
"introduction": "Here it is possible to configure your components and Home Assistant. Not everything is possible to configure from the UI yet, but we're working on it.",
|
"introduction": "Here it is possible to configure your components and Home Assistant. Not everything is possible to configure from the UI yet, but we're working on it.",
|
||||||
|
"area_registry": {
|
||||||
|
"caption": "Area Registry",
|
||||||
|
"description": "Overview of all areas in your home."
|
||||||
|
},
|
||||||
"core": {
|
"core": {
|
||||||
"caption": "General",
|
"caption": "General",
|
||||||
"description": "Validate your configuration file and control the server",
|
"description": "Validate your configuration file and control the server",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user