mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-25 18:26:35 +00:00
Add UI for setting an area on entity level (#7837)
This commit is contained in:
parent
7ceb6eb50d
commit
fe31d15d27
@ -137,8 +137,7 @@ export class DialogHassioNetwork extends LitElement
|
|||||||
)}
|
)}
|
||||||
${this._interface?.type === "wireless"
|
${this._interface?.type === "wireless"
|
||||||
? html`
|
? html`
|
||||||
<ha-expansion-panel outlined>
|
<ha-expansion-panel header="Wi-Fi" outlined>
|
||||||
<span slot="title">Wi-Fi</span>
|
|
||||||
${this._interface?.wifi?.ssid
|
${this._interface?.wifi?.ssid
|
||||||
? html`<p>Connected to: ${this._interface?.wifi?.ssid}</p>`
|
? html`<p>Connected to: ${this._interface?.wifi?.ssid}</p>`
|
||||||
: ""}
|
: ""}
|
||||||
@ -281,8 +280,10 @@ export class DialogHassioNetwork extends LitElement
|
|||||||
|
|
||||||
private _renderIPConfiguration(version: string) {
|
private _renderIPConfiguration(version: string) {
|
||||||
return html`
|
return html`
|
||||||
<ha-expansion-panel outlined>
|
<ha-expansion-panel
|
||||||
<span slot="title">IPv${version.charAt(version.length - 1)}</span>
|
.header=${`IPv${version.charAt(version.length - 1)}`}
|
||||||
|
outlined
|
||||||
|
>
|
||||||
<div class="radio-row">
|
<div class="radio-row">
|
||||||
<ha-formfield label="DHCP">
|
<ha-formfield label="DHCP">
|
||||||
<ha-radio
|
<ha-radio
|
||||||
@ -591,6 +592,7 @@ export class DialogHassioNetwork extends LitElement
|
|||||||
}
|
}
|
||||||
|
|
||||||
ha-expansion-panel {
|
ha-expansion-panel {
|
||||||
|
--expansion-panel-summary-padding: 0 16px;
|
||||||
margin: 4px 0;
|
margin: 4px 0;
|
||||||
}
|
}
|
||||||
paper-input {
|
paper-input {
|
||||||
|
@ -28,6 +28,7 @@ import {
|
|||||||
import { SubscribeMixin } from "../mixins/subscribe-mixin";
|
import { SubscribeMixin } from "../mixins/subscribe-mixin";
|
||||||
import { PolymerChangedEvent } from "../polymer-types";
|
import { PolymerChangedEvent } from "../polymer-types";
|
||||||
import { HomeAssistant } from "../types";
|
import { HomeAssistant } from "../types";
|
||||||
|
import memoizeOne from "memoize-one";
|
||||||
|
|
||||||
const rowRenderer = (
|
const rowRenderer = (
|
||||||
root: HTMLElement,
|
root: HTMLElement,
|
||||||
@ -68,6 +69,8 @@ export class HaAreaPicker extends SubscribeMixin(LitElement) {
|
|||||||
|
|
||||||
@property() public value?: string;
|
@property() public value?: string;
|
||||||
|
|
||||||
|
@property() public placeholder?: string;
|
||||||
|
|
||||||
@property() public _areas?: AreaRegistryEntry[];
|
@property() public _areas?: AreaRegistryEntry[];
|
||||||
|
|
||||||
@property({ type: Boolean, attribute: "no-add" })
|
@property({ type: Boolean, attribute: "no-add" })
|
||||||
@ -110,6 +113,9 @@ export class HaAreaPicker extends SubscribeMixin(LitElement) {
|
|||||||
.label=${this.label === undefined && this.hass
|
.label=${this.label === undefined && this.hass
|
||||||
? this.hass.localize("ui.components.area-picker.area")
|
? this.hass.localize("ui.components.area-picker.area")
|
||||||
: this.label}
|
: this.label}
|
||||||
|
.placeholder=${this.placeholder
|
||||||
|
? this._area(this.placeholder)?.name
|
||||||
|
: undefined}
|
||||||
class="input"
|
class="input"
|
||||||
autocapitalize="none"
|
autocapitalize="none"
|
||||||
autocomplete="off"
|
autocomplete="off"
|
||||||
@ -151,6 +157,12 @@ export class HaAreaPicker extends SubscribeMixin(LitElement) {
|
|||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _area = memoizeOne((areaId: string):
|
||||||
|
| AreaRegistryEntry
|
||||||
|
| undefined => {
|
||||||
|
return this._areas?.find((area) => area.area_id === areaId);
|
||||||
|
});
|
||||||
|
|
||||||
private _clearValue(ev: Event) {
|
private _clearValue(ev: Event) {
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
this._setValue("");
|
this._setValue("");
|
||||||
|
@ -19,12 +19,14 @@ class HaExpansionPanel extends LitElement {
|
|||||||
|
|
||||||
@property({ type: Boolean, reflect: true }) outlined = false;
|
@property({ type: Boolean, reflect: true }) outlined = false;
|
||||||
|
|
||||||
|
@property() header?: string;
|
||||||
|
|
||||||
@query(".container") private _container!: HTMLDivElement;
|
@query(".container") private _container!: HTMLDivElement;
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
return html`
|
return html`
|
||||||
<div class="summary" @click=${this._toggleContainer}>
|
<div class="summary" @click=${this._toggleContainer}>
|
||||||
<slot name="title"></slot>
|
<slot name="header">${this.header}</slot>
|
||||||
<ha-svg-icon
|
<ha-svg-icon
|
||||||
.path=${mdiChevronDown}
|
.path=${mdiChevronDown}
|
||||||
class="summary-icon ${classMap({ expanded: this.expanded })}"
|
class="summary-icon ${classMap({ expanded: this.expanded })}"
|
||||||
@ -76,7 +78,7 @@ class HaExpansionPanel extends LitElement {
|
|||||||
|
|
||||||
.summary {
|
.summary {
|
||||||
display: flex;
|
display: flex;
|
||||||
padding: var(--expansion-panel-summary-padding, 0px 16px);
|
padding: var(--expansion-panel-summary-padding, 0);
|
||||||
min-height: 48px;
|
min-height: 48px;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
@ -10,6 +10,7 @@ export interface EntityRegistryEntry {
|
|||||||
platform: string;
|
platform: string;
|
||||||
config_entry_id?: string;
|
config_entry_id?: string;
|
||||||
device_id?: string;
|
device_id?: string;
|
||||||
|
area_id?: string;
|
||||||
disabled_by: string | null;
|
disabled_by: string | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -29,6 +30,7 @@ export interface UpdateEntityRegistryEntryResult {
|
|||||||
export interface EntityRegistryEntryUpdateParams {
|
export interface EntityRegistryEntryUpdateParams {
|
||||||
name?: string | null;
|
name?: string | null;
|
||||||
icon?: string | null;
|
icon?: string | null;
|
||||||
|
area_id?: string | null;
|
||||||
disabled_by?: string | null;
|
disabled_by?: string | null;
|
||||||
new_entity_id?: string;
|
new_entity_id?: string;
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,6 @@ import "@polymer/paper-input/paper-input";
|
|||||||
import type { PaperInputElement } from "@polymer/paper-input/paper-input";
|
import type { PaperInputElement } from "@polymer/paper-input/paper-input";
|
||||||
import "../../../components/ha-circular-progress";
|
import "../../../components/ha-circular-progress";
|
||||||
import {
|
import {
|
||||||
css,
|
|
||||||
CSSResult,
|
CSSResult,
|
||||||
customElement,
|
customElement,
|
||||||
html,
|
html,
|
||||||
@ -97,12 +96,11 @@ class DialogImportBlueprint extends LitElement {
|
|||||||
)}
|
)}
|
||||||
></paper-input>
|
></paper-input>
|
||||||
`}
|
`}
|
||||||
<ha-expansion-panel>
|
<ha-expansion-panel
|
||||||
<span slot="title"
|
.header=${this.hass.localize(
|
||||||
>${this.hass.localize(
|
"ui.panel.config.blueprint.add.raw_blueprint"
|
||||||
"ui.panel.config.blueprint.add.raw_blueprint"
|
)}
|
||||||
)}</span
|
>
|
||||||
>
|
|
||||||
<pre>${this._result.raw_data}</pre>
|
<pre>${this._result.raw_data}</pre>
|
||||||
</ha-expansion-panel>`
|
</ha-expansion-panel>`
|
||||||
: html`${this.hass.localize(
|
: html`${this.hass.localize(
|
||||||
@ -201,15 +199,8 @@ class DialogImportBlueprint extends LitElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static get styles(): CSSResult[] {
|
static get styles(): CSSResult {
|
||||||
return [
|
return haStyleDialog;
|
||||||
haStyleDialog,
|
|
||||||
css`
|
|
||||||
ha-expansion-panel {
|
|
||||||
--expansion-panel-summary-padding: 0;
|
|
||||||
}
|
|
||||||
`,
|
|
||||||
];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -12,7 +12,7 @@ import {
|
|||||||
computeDeviceName,
|
computeDeviceName,
|
||||||
DeviceRegistryEntry,
|
DeviceRegistryEntry,
|
||||||
} from "../../../../data/device_registry";
|
} from "../../../../data/device_registry";
|
||||||
import { loadDeviceRegistryDetailDialog } from "../../../../dialogs/device-registry-detail/show-dialog-device-registry-detail";
|
import { loadDeviceRegistryDetailDialog } from "../device-registry-detail/show-dialog-device-registry-detail";
|
||||||
import { HomeAssistant } from "../../../../types";
|
import { HomeAssistant } from "../../../../types";
|
||||||
|
|
||||||
@customElement("ha-device-info-card")
|
@customElement("ha-device-info-card")
|
||||||
|
@ -3,8 +3,8 @@ import "@polymer/paper-dropdown-menu/paper-dropdown-menu";
|
|||||||
import "@polymer/paper-input/paper-input";
|
import "@polymer/paper-input/paper-input";
|
||||||
import "@polymer/paper-item/paper-item";
|
import "@polymer/paper-item/paper-item";
|
||||||
import "@polymer/paper-listbox/paper-listbox";
|
import "@polymer/paper-listbox/paper-listbox";
|
||||||
import "../../components/ha-dialog";
|
import "../../../../components/ha-dialog";
|
||||||
import "../../components/ha-area-picker";
|
import "../../../../components/ha-area-picker";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
CSSResult,
|
CSSResult,
|
||||||
@ -18,11 +18,11 @@ import {
|
|||||||
} from "lit-element";
|
} from "lit-element";
|
||||||
|
|
||||||
import { DeviceRegistryDetailDialogParams } from "./show-dialog-device-registry-detail";
|
import { DeviceRegistryDetailDialogParams } from "./show-dialog-device-registry-detail";
|
||||||
import { HomeAssistant } from "../../types";
|
import { HomeAssistant } from "../../../../types";
|
||||||
import { PolymerChangedEvent } from "../../polymer-types";
|
import { PolymerChangedEvent } from "../../../../polymer-types";
|
||||||
import { computeDeviceName } from "../../data/device_registry";
|
import { computeDeviceName } from "../../../../data/device_registry";
|
||||||
import { fireEvent } from "../../common/dom/fire_event";
|
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||||
import { haStyleDialog } from "../../resources/styles";
|
import { haStyleDialog } from "../../../../resources/styles";
|
||||||
|
|
||||||
@customElement("dialog-device-registry-detail")
|
@customElement("dialog-device-registry-detail")
|
||||||
class DialogDeviceRegistryDetail extends LitElement {
|
class DialogDeviceRegistryDetail extends LitElement {
|
@ -1,8 +1,8 @@
|
|||||||
import { fireEvent } from "../../common/dom/fire_event";
|
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||||
import {
|
import {
|
||||||
DeviceRegistryEntry,
|
DeviceRegistryEntry,
|
||||||
DeviceRegistryEntryMutableParams,
|
DeviceRegistryEntryMutableParams,
|
||||||
} from "../../data/device_registry";
|
} from "../../../../data/device_registry";
|
||||||
|
|
||||||
export interface DeviceRegistryDetailDialogParams {
|
export interface DeviceRegistryDetailDialogParams {
|
||||||
device: DeviceRegistryEntry;
|
device: DeviceRegistryEntry;
|
@ -35,7 +35,7 @@ import { findRelated, RelatedResult } from "../../../data/search";
|
|||||||
import {
|
import {
|
||||||
loadDeviceRegistryDetailDialog,
|
loadDeviceRegistryDetailDialog,
|
||||||
showDeviceRegistryDetailDialog,
|
showDeviceRegistryDetailDialog,
|
||||||
} from "../../../dialogs/device-registry-detail/show-dialog-device-registry-detail";
|
} from "./device-registry-detail/show-dialog-device-registry-detail";
|
||||||
import { showConfirmationDialog } from "../../../dialogs/generic/show-dialog-box";
|
import { showConfirmationDialog } from "../../../dialogs/generic/show-dialog-box";
|
||||||
import "../../../layouts/hass-error-screen";
|
import "../../../layouts/hass-error-screen";
|
||||||
import "../../../layouts/hass-tabs-subpage";
|
import "../../../layouts/hass-tabs-subpage";
|
||||||
|
@ -20,9 +20,16 @@ import {
|
|||||||
import { showAlertDialog } from "../../../dialogs/generic/show-dialog-box";
|
import { showAlertDialog } from "../../../dialogs/generic/show-dialog-box";
|
||||||
import type { PolymerChangedEvent } from "../../../polymer-types";
|
import type { PolymerChangedEvent } from "../../../polymer-types";
|
||||||
import type { HomeAssistant } from "../../../types";
|
import type { HomeAssistant } from "../../../types";
|
||||||
|
import "../../../components/ha-area-picker";
|
||||||
|
import { UnsubscribeFunc } from "home-assistant-js-websocket";
|
||||||
|
import {
|
||||||
|
DeviceRegistryEntry,
|
||||||
|
subscribeDeviceRegistry,
|
||||||
|
} from "../../../data/device_registry";
|
||||||
|
import { SubscribeMixin } from "../../../mixins/subscribe-mixin";
|
||||||
|
|
||||||
@customElement("ha-registry-basic-editor")
|
@customElement("ha-registry-basic-editor")
|
||||||
export class HaEntityRegistryBasicEditor extends LitElement {
|
export class HaEntityRegistryBasicEditor extends SubscribeMixin(LitElement) {
|
||||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
@property() public entry!: ExtEntityRegistryEntry;
|
@property() public entry!: ExtEntityRegistryEntry;
|
||||||
@ -31,16 +38,26 @@ export class HaEntityRegistryBasicEditor extends LitElement {
|
|||||||
|
|
||||||
@internalProperty() private _entityId!: string;
|
@internalProperty() private _entityId!: string;
|
||||||
|
|
||||||
|
@internalProperty() private _areaId?: string;
|
||||||
|
|
||||||
@internalProperty() private _disabledBy!: string | null;
|
@internalProperty() private _disabledBy!: string | null;
|
||||||
|
|
||||||
|
private _deviceLookup?: Record<string, DeviceRegistryEntry>;
|
||||||
|
|
||||||
|
@internalProperty() private _device?: DeviceRegistryEntry;
|
||||||
|
|
||||||
@internalProperty() private _submitting?: boolean;
|
@internalProperty() private _submitting?: boolean;
|
||||||
|
|
||||||
public async updateEntry(): Promise<void> {
|
public async updateEntry(): Promise<void> {
|
||||||
this._submitting = true;
|
this._submitting = true;
|
||||||
const params: Partial<EntityRegistryEntryUpdateParams> = {
|
const params: Partial<EntityRegistryEntryUpdateParams> = {
|
||||||
new_entity_id: this._entityId.trim(),
|
new_entity_id: this._entityId.trim(),
|
||||||
|
area_id: this._areaId || null,
|
||||||
};
|
};
|
||||||
if (this._disabledBy === null || this._disabledBy === "user") {
|
if (
|
||||||
|
this.entry.disabled_by !== this._disabledBy &&
|
||||||
|
(this._disabledBy === null || this._disabledBy === "user")
|
||||||
|
) {
|
||||||
params.disabled_by = this._disabledBy;
|
params.disabled_by = this._disabledBy;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
@ -70,6 +87,20 @@ export class HaEntityRegistryBasicEditor extends LitElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public hassSubscribe(): UnsubscribeFunc[] {
|
||||||
|
return [
|
||||||
|
subscribeDeviceRegistry(this.hass.connection!, (devices) => {
|
||||||
|
this._deviceLookup = {};
|
||||||
|
for (const device of devices) {
|
||||||
|
this._deviceLookup[device.id] = device;
|
||||||
|
}
|
||||||
|
if (!this._device && this.entry.device_id) {
|
||||||
|
this._device = this._deviceLookup[this.entry.device_id];
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
protected updated(changedProperties: PropertyValues) {
|
protected updated(changedProperties: PropertyValues) {
|
||||||
super.updated(changedProperties);
|
super.updated(changedProperties);
|
||||||
if (!changedProperties.has("entry")) {
|
if (!changedProperties.has("entry")) {
|
||||||
@ -79,6 +110,11 @@ export class HaEntityRegistryBasicEditor extends LitElement {
|
|||||||
this._origEntityId = this.entry.entity_id;
|
this._origEntityId = this.entry.entity_id;
|
||||||
this._entityId = this.entry.entity_id;
|
this._entityId = this.entry.entity_id;
|
||||||
this._disabledBy = this.entry.disabled_by;
|
this._disabledBy = this.entry.disabled_by;
|
||||||
|
this._areaId = this.entry.area_id;
|
||||||
|
this._device =
|
||||||
|
this.entry.device_id && this._deviceLookup
|
||||||
|
? this._deviceLookup[this.entry.device_id]
|
||||||
|
: undefined;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -105,6 +141,12 @@ export class HaEntityRegistryBasicEditor extends LitElement {
|
|||||||
.invalid=${invalidDomainUpdate}
|
.invalid=${invalidDomainUpdate}
|
||||||
.disabled=${this._submitting}
|
.disabled=${this._submitting}
|
||||||
></paper-input>
|
></paper-input>
|
||||||
|
<ha-area-picker
|
||||||
|
.hass=${this.hass}
|
||||||
|
.value=${this._areaId}
|
||||||
|
.placeholder=${this._device?.area_id}
|
||||||
|
@value-changed=${this._areaPicked}
|
||||||
|
></ha-area-picker>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<ha-switch
|
<ha-switch
|
||||||
.checked=${!this._disabledBy}
|
.checked=${!this._disabledBy}
|
||||||
@ -139,6 +181,10 @@ export class HaEntityRegistryBasicEditor extends LitElement {
|
|||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _areaPicked(ev: CustomEvent) {
|
||||||
|
this._areaId = ev.detail.value;
|
||||||
|
}
|
||||||
|
|
||||||
private _entityIdChanged(ev: PolymerChangedEvent<string>): void {
|
private _entityIdChanged(ev: PolymerChangedEvent<string>): void {
|
||||||
this._entityId = ev.detail.value;
|
this._entityId = ev.detail.value;
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import "@material/mwc-button/mwc-button";
|
import "@material/mwc-button/mwc-button";
|
||||||
import "@polymer/paper-input/paper-input";
|
import "@polymer/paper-input/paper-input";
|
||||||
import { HassEntity } from "home-assistant-js-websocket";
|
import { HassEntity, UnsubscribeFunc } from "home-assistant-js-websocket";
|
||||||
import {
|
import {
|
||||||
css,
|
css,
|
||||||
CSSResult,
|
CSSResult,
|
||||||
@ -31,9 +31,18 @@ import type { PolymerChangedEvent } from "../../../polymer-types";
|
|||||||
import { haStyle } from "../../../resources/styles";
|
import { haStyle } from "../../../resources/styles";
|
||||||
import type { HomeAssistant } from "../../../types";
|
import type { HomeAssistant } from "../../../types";
|
||||||
import { domainIcon } from "../../../common/entity/domain_icon";
|
import { domainIcon } from "../../../common/entity/domain_icon";
|
||||||
|
import "../../../components/ha-area-picker";
|
||||||
|
import {
|
||||||
|
DeviceRegistryEntry,
|
||||||
|
subscribeDeviceRegistry,
|
||||||
|
updateDeviceRegistryEntry,
|
||||||
|
} from "../../../data/device_registry";
|
||||||
|
import { SubscribeMixin } from "../../../mixins/subscribe-mixin";
|
||||||
|
import "../../../components/ha-expansion-panel";
|
||||||
|
import { showDeviceRegistryDetailDialog } from "../devices/device-registry-detail/show-dialog-device-registry-detail";
|
||||||
|
|
||||||
@customElement("entity-registry-settings")
|
@customElement("entity-registry-settings")
|
||||||
export class EntityRegistrySettings extends LitElement {
|
export class EntityRegistrySettings extends SubscribeMixin(LitElement) {
|
||||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
@property() public entry!: ExtEntityRegistryEntry;
|
@property() public entry!: ExtEntityRegistryEntry;
|
||||||
@ -44,14 +53,34 @@ export class EntityRegistrySettings extends LitElement {
|
|||||||
|
|
||||||
@internalProperty() private _entityId!: string;
|
@internalProperty() private _entityId!: string;
|
||||||
|
|
||||||
|
@internalProperty() private _areaId?: string | null;
|
||||||
|
|
||||||
@internalProperty() private _disabledBy!: string | null;
|
@internalProperty() private _disabledBy!: string | null;
|
||||||
|
|
||||||
|
private _deviceLookup?: Record<string, DeviceRegistryEntry>;
|
||||||
|
|
||||||
|
@internalProperty() private _device?: DeviceRegistryEntry;
|
||||||
|
|
||||||
@internalProperty() private _error?: string;
|
@internalProperty() private _error?: string;
|
||||||
|
|
||||||
@internalProperty() private _submitting?: boolean;
|
@internalProperty() private _submitting?: boolean;
|
||||||
|
|
||||||
private _origEntityId!: string;
|
private _origEntityId!: string;
|
||||||
|
|
||||||
|
public hassSubscribe(): UnsubscribeFunc[] {
|
||||||
|
return [
|
||||||
|
subscribeDeviceRegistry(this.hass.connection!, (devices) => {
|
||||||
|
this._deviceLookup = {};
|
||||||
|
for (const device of devices) {
|
||||||
|
this._deviceLookup[device.id] = device;
|
||||||
|
}
|
||||||
|
if (this.entry.device_id) {
|
||||||
|
this._device = this._deviceLookup[this.entry.device_id];
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
protected updated(changedProperties: PropertyValues) {
|
protected updated(changedProperties: PropertyValues) {
|
||||||
super.updated(changedProperties);
|
super.updated(changedProperties);
|
||||||
if (changedProperties.has("entry")) {
|
if (changedProperties.has("entry")) {
|
||||||
@ -59,8 +88,13 @@ export class EntityRegistrySettings extends LitElement {
|
|||||||
this._name = this.entry.name || "";
|
this._name = this.entry.name || "";
|
||||||
this._icon = this.entry.icon || "";
|
this._icon = this.entry.icon || "";
|
||||||
this._origEntityId = this.entry.entity_id;
|
this._origEntityId = this.entry.entity_id;
|
||||||
|
this._areaId = this.entry.area_id;
|
||||||
this._entityId = this.entry.entity_id;
|
this._entityId = this.entry.entity_id;
|
||||||
this._disabledBy = this.entry.disabled_by;
|
this._disabledBy = this.entry.disabled_by;
|
||||||
|
this._device =
|
||||||
|
this.entry.device_id && this._deviceLookup
|
||||||
|
? this._deviceLookup[this.entry.device_id]
|
||||||
|
: undefined;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -117,6 +151,13 @@ export class EntityRegistrySettings extends LitElement {
|
|||||||
.invalid=${invalidDomainUpdate}
|
.invalid=${invalidDomainUpdate}
|
||||||
.disabled=${this._submitting}
|
.disabled=${this._submitting}
|
||||||
></paper-input>
|
></paper-input>
|
||||||
|
${!this.entry.device_id
|
||||||
|
? html`<ha-area-picker
|
||||||
|
.hass=${this.hass}
|
||||||
|
.value=${this._areaId}
|
||||||
|
@value-changed=${this._areaPicked}
|
||||||
|
></ha-area-picker>`
|
||||||
|
: ""}
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<ha-switch
|
<ha-switch
|
||||||
.checked=${!this._disabledBy}
|
.checked=${!this._disabledBy}
|
||||||
@ -148,6 +189,31 @@ export class EntityRegistrySettings extends LitElement {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
${this.entry.device_id
|
||||||
|
? html`<ha-expansion-panel .header=${"Advanced"}>
|
||||||
|
<p>
|
||||||
|
By default the entities of a device are in the same area as the
|
||||||
|
device. If you change the area of this entity, it will no longer
|
||||||
|
follow the area of the device.
|
||||||
|
</p>
|
||||||
|
${this._areaId
|
||||||
|
? html`<mwc-button @click=${this._clearArea}
|
||||||
|
>Follow device area</mwc-button
|
||||||
|
>`
|
||||||
|
: this._device
|
||||||
|
? html`<mwc-button @click=${this._openDeviceSettings}
|
||||||
|
>Change device area</mwc-button
|
||||||
|
>`
|
||||||
|
: ""}
|
||||||
|
<ha-area-picker
|
||||||
|
.hass=${this.hass}
|
||||||
|
.value=${this._areaId}
|
||||||
|
.placeholder=${this._device?.area_id}
|
||||||
|
@value-changed=${this._areaPicked}
|
||||||
|
></ha-area-picker
|
||||||
|
></ha-expansion-panel>`
|
||||||
|
: ""}
|
||||||
</div>
|
</div>
|
||||||
<div class="buttons">
|
<div class="buttons">
|
||||||
<mwc-button
|
<mwc-button
|
||||||
@ -183,14 +249,37 @@ export class EntityRegistrySettings extends LitElement {
|
|||||||
this._entityId = ev.detail.value;
|
this._entityId = ev.detail.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _areaPicked(ev: CustomEvent) {
|
||||||
|
this._error = undefined;
|
||||||
|
this._areaId = ev.detail.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _clearArea() {
|
||||||
|
this._error = undefined;
|
||||||
|
this._areaId = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _openDeviceSettings() {
|
||||||
|
showDeviceRegistryDetailDialog(this, {
|
||||||
|
device: this._device!,
|
||||||
|
updateEntry: async (updates) => {
|
||||||
|
await updateDeviceRegistryEntry(this.hass, this._device!.id, updates);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private async _updateEntry(): Promise<void> {
|
private async _updateEntry(): Promise<void> {
|
||||||
this._submitting = true;
|
this._submitting = true;
|
||||||
const params: Partial<EntityRegistryEntryUpdateParams> = {
|
const params: Partial<EntityRegistryEntryUpdateParams> = {
|
||||||
name: this._name.trim() || null,
|
name: this._name.trim() || null,
|
||||||
icon: this._icon.trim() || null,
|
icon: this._icon.trim() || null,
|
||||||
|
area_id: this._areaId || null,
|
||||||
new_entity_id: this._entityId.trim(),
|
new_entity_id: this._entityId.trim(),
|
||||||
};
|
};
|
||||||
if (this._disabledBy === null || this._disabledBy === "user") {
|
if (
|
||||||
|
this.entry.disabled_by !== this._disabledBy &&
|
||||||
|
(this._disabledBy === null || this._disabledBy === "user")
|
||||||
|
) {
|
||||||
params.disabled_by = this._disabledBy;
|
params.disabled_by = this._disabledBy;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
|
@ -62,6 +62,14 @@ import {
|
|||||||
} from "./show-dialog-entity-editor";
|
} from "./show-dialog-entity-editor";
|
||||||
import { haStyle } from "../../../resources/styles";
|
import { haStyle } from "../../../resources/styles";
|
||||||
import { UNAVAILABLE } from "../../../data/entity";
|
import { UNAVAILABLE } from "../../../data/entity";
|
||||||
|
import {
|
||||||
|
DeviceRegistryEntry,
|
||||||
|
subscribeDeviceRegistry,
|
||||||
|
} from "../../../data/device_registry";
|
||||||
|
import {
|
||||||
|
AreaRegistryEntry,
|
||||||
|
subscribeAreaRegistry,
|
||||||
|
} from "../../../data/area_registry";
|
||||||
|
|
||||||
export interface StateEntity extends EntityRegistryEntry {
|
export interface StateEntity extends EntityRegistryEntry {
|
||||||
readonly?: boolean;
|
readonly?: boolean;
|
||||||
@ -73,6 +81,7 @@ export interface EntityRow extends StateEntity {
|
|||||||
unavailable: boolean;
|
unavailable: boolean;
|
||||||
restored: boolean;
|
restored: boolean;
|
||||||
status: string;
|
status: string;
|
||||||
|
area?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
@customElement("ha-config-entities")
|
@customElement("ha-config-entities")
|
||||||
@ -87,6 +96,10 @@ export class HaConfigEntities extends SubscribeMixin(LitElement) {
|
|||||||
|
|
||||||
@internalProperty() private _entities?: EntityRegistryEntry[];
|
@internalProperty() private _entities?: EntityRegistryEntry[];
|
||||||
|
|
||||||
|
@internalProperty() private _devices?: DeviceRegistryEntry[];
|
||||||
|
|
||||||
|
@internalProperty() private _areas: AreaRegistryEntry[] = [];
|
||||||
|
|
||||||
@internalProperty() private _stateEntities: StateEntity[] = [];
|
@internalProperty() private _stateEntities: StateEntity[] = [];
|
||||||
|
|
||||||
@property() public _entries?: ConfigEntry[];
|
@property() public _entries?: ConfigEntry[];
|
||||||
@ -201,6 +214,15 @@ export class HaConfigEntities extends SubscribeMixin(LitElement) {
|
|||||||
template: (platform) =>
|
template: (platform) =>
|
||||||
this.hass.localize(`component.${platform}.title`) || platform,
|
this.hass.localize(`component.${platform}.title`) || platform,
|
||||||
},
|
},
|
||||||
|
area: {
|
||||||
|
title: this.hass.localize(
|
||||||
|
"ui.panel.config.entities.picker.headers.area"
|
||||||
|
),
|
||||||
|
sortable: true,
|
||||||
|
hidden: narrow,
|
||||||
|
filterable: true,
|
||||||
|
width: "15%",
|
||||||
|
},
|
||||||
status: {
|
status: {
|
||||||
title: this.hass.localize(
|
title: this.hass.localize(
|
||||||
"ui.panel.config.entities.picker.headers.status"
|
"ui.panel.config.entities.picker.headers.status"
|
||||||
@ -255,6 +277,8 @@ export class HaConfigEntities extends SubscribeMixin(LitElement) {
|
|||||||
private _filteredEntities = memoize(
|
private _filteredEntities = memoize(
|
||||||
(
|
(
|
||||||
entities: EntityRegistryEntry[],
|
entities: EntityRegistryEntry[],
|
||||||
|
devices: DeviceRegistryEntry[] | undefined,
|
||||||
|
areas: AreaRegistryEntry[] | undefined,
|
||||||
stateEntities: StateEntity[],
|
stateEntities: StateEntity[],
|
||||||
filters: URLSearchParams,
|
filters: URLSearchParams,
|
||||||
showDisabled: boolean,
|
showDisabled: boolean,
|
||||||
@ -262,21 +286,42 @@ export class HaConfigEntities extends SubscribeMixin(LitElement) {
|
|||||||
showReadOnly: boolean
|
showReadOnly: boolean
|
||||||
): EntityRow[] => {
|
): EntityRow[] => {
|
||||||
const result: EntityRow[] = [];
|
const result: EntityRow[] = [];
|
||||||
|
|
||||||
// If nothing gets filtered, this is our correct count of entities
|
// If nothing gets filtered, this is our correct count of entities
|
||||||
let startLength = entities.length + stateEntities.length;
|
let startLength = entities.length + stateEntities.length;
|
||||||
|
|
||||||
entities = showReadOnly ? entities.concat(stateEntities) : entities;
|
const areaLookup: { [areaId: string]: AreaRegistryEntry } = {};
|
||||||
|
const deviceLookup: { [deviceId: string]: DeviceRegistryEntry } = {};
|
||||||
|
|
||||||
|
if (areas) {
|
||||||
|
for (const area of areas) {
|
||||||
|
areaLookup[area.area_id] = area;
|
||||||
|
}
|
||||||
|
if (devices) {
|
||||||
|
for (const device of devices) {
|
||||||
|
deviceLookup[device.id] = device;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
entities.forEach((entity) => {
|
||||||
|
return entity;
|
||||||
|
});
|
||||||
|
|
||||||
|
let filteredEntities = showReadOnly
|
||||||
|
? entities.concat(stateEntities)
|
||||||
|
: entities;
|
||||||
|
|
||||||
filters.forEach((value, key) => {
|
filters.forEach((value, key) => {
|
||||||
switch (key) {
|
switch (key) {
|
||||||
case "config_entry":
|
case "config_entry":
|
||||||
entities = entities.filter(
|
filteredEntities = filteredEntities.filter(
|
||||||
(entity) => entity.config_entry_id === value
|
(entity) => entity.config_entry_id === value
|
||||||
);
|
);
|
||||||
// If we have an active filter and `showReadOnly` is true, the length of `entities` is correct.
|
// If we have an active filter and `showReadOnly` is true, the length of `entities` is correct.
|
||||||
// If however, the read-only entities were not added before, we need to check how many would
|
// If however, the read-only entities were not added before, we need to check how many would
|
||||||
// have matched the active filter and add that number to the count.
|
// have matched the active filter and add that number to the count.
|
||||||
startLength = entities.length;
|
startLength = filteredEntities.length;
|
||||||
if (!showReadOnly) {
|
if (!showReadOnly) {
|
||||||
startLength += stateEntities.filter(
|
startLength += stateEntities.filter(
|
||||||
(entity) => entity.config_entry_id === value
|
(entity) => entity.config_entry_id === value
|
||||||
@ -287,13 +332,17 @@ export class HaConfigEntities extends SubscribeMixin(LitElement) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (!showDisabled) {
|
if (!showDisabled) {
|
||||||
entities = entities.filter((entity) => !entity.disabled_by);
|
filteredEntities = filteredEntities.filter(
|
||||||
|
(entity) => !entity.disabled_by
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const entry of entities) {
|
for (const entry of filteredEntities) {
|
||||||
const entity = this.hass.states[entry.entity_id];
|
const entity = this.hass.states[entry.entity_id];
|
||||||
const unavailable = entity?.state === UNAVAILABLE;
|
const unavailable = entity?.state === UNAVAILABLE;
|
||||||
const restored = entity?.attributes.restored;
|
const restored = entity?.attributes.restored;
|
||||||
|
const areaId = entry.area_id ?? deviceLookup[entry.device_id!]?.area_id;
|
||||||
|
const area = areaId ? areaLookup[areaId] : undefined;
|
||||||
|
|
||||||
if (!showUnavailable && unavailable) {
|
if (!showUnavailable && unavailable) {
|
||||||
continue;
|
continue;
|
||||||
@ -309,6 +358,7 @@ export class HaConfigEntities extends SubscribeMixin(LitElement) {
|
|||||||
this.hass.localize("state.default.unavailable"),
|
this.hass.localize("state.default.unavailable"),
|
||||||
unavailable,
|
unavailable,
|
||||||
restored,
|
restored,
|
||||||
|
area: area ? area.name : undefined,
|
||||||
status: restored
|
status: restored
|
||||||
? this.hass.localize(
|
? this.hass.localize(
|
||||||
"ui.panel.config.entities.picker.status.restored"
|
"ui.panel.config.entities.picker.status.restored"
|
||||||
@ -345,6 +395,12 @@ export class HaConfigEntities extends SubscribeMixin(LitElement) {
|
|||||||
subscribeEntityRegistry(this.hass.connection!, (entities) => {
|
subscribeEntityRegistry(this.hass.connection!, (entities) => {
|
||||||
this._entities = entities;
|
this._entities = entities;
|
||||||
}),
|
}),
|
||||||
|
subscribeDeviceRegistry(this.hass.connection!, (devices) => {
|
||||||
|
this._devices = devices;
|
||||||
|
}),
|
||||||
|
subscribeAreaRegistry(this.hass.connection, (areas) => {
|
||||||
|
this._areas = areas;
|
||||||
|
}),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -372,6 +428,8 @@ export class HaConfigEntities extends SubscribeMixin(LitElement) {
|
|||||||
|
|
||||||
const entityData = this._filteredEntities(
|
const entityData = this._filteredEntities(
|
||||||
this._entities,
|
this._entities,
|
||||||
|
this._devices,
|
||||||
|
this._areas,
|
||||||
this._stateEntities,
|
this._stateEntities,
|
||||||
this._searchParms,
|
this._searchParms,
|
||||||
this._showDisabled,
|
this._showDisabled,
|
||||||
|
@ -1830,6 +1830,7 @@
|
|||||||
"name": "Name",
|
"name": "Name",
|
||||||
"entity_id": "Entity ID",
|
"entity_id": "Entity ID",
|
||||||
"integration": "Integration",
|
"integration": "Integration",
|
||||||
|
"area": "Area",
|
||||||
"status": "Status"
|
"status": "Status"
|
||||||
},
|
},
|
||||||
"selected": "{number} selected",
|
"selected": "{number} selected",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user