mirror of
https://github.com/home-assistant/frontend.git
synced 2025-11-08 02:19:43 +00:00
Handle unknown items in target picker (#27795)
* Handle unknown items in target picker * Update ha-target-picker-item-row.ts * update colors * fallback to domain icons
This commit is contained in:
@@ -6,6 +6,7 @@ import {
|
|||||||
mdiLabel,
|
mdiLabel,
|
||||||
mdiTextureBox,
|
mdiTextureBox,
|
||||||
} from "@mdi/js";
|
} from "@mdi/js";
|
||||||
|
import type { HassEntity } from "home-assistant-js-websocket";
|
||||||
import { css, html, LitElement, nothing, type PropertyValues } from "lit";
|
import { css, html, LitElement, nothing, type PropertyValues } from "lit";
|
||||||
import { customElement, property, query, state } from "lit/decorators";
|
import { customElement, property, query, state } from "lit/decorators";
|
||||||
import memoizeOne from "memoize-one";
|
import memoizeOne from "memoize-one";
|
||||||
@@ -19,9 +20,12 @@ import { computeDomain } from "../../common/entity/compute_domain";
|
|||||||
import { computeEntityName } from "../../common/entity/compute_entity_name";
|
import { computeEntityName } from "../../common/entity/compute_entity_name";
|
||||||
import { getEntityContext } from "../../common/entity/context/get_entity_context";
|
import { getEntityContext } from "../../common/entity/context/get_entity_context";
|
||||||
import { computeRTL } from "../../common/util/compute_rtl";
|
import { computeRTL } from "../../common/util/compute_rtl";
|
||||||
|
import type { AreaRegistryEntry } from "../../data/area_registry";
|
||||||
import { getConfigEntry } from "../../data/config_entries";
|
import { getConfigEntry } from "../../data/config_entries";
|
||||||
import { labelsContext } from "../../data/context";
|
import { labelsContext } from "../../data/context";
|
||||||
|
import type { DeviceRegistryEntry } from "../../data/device_registry";
|
||||||
import type { HaEntityPickerEntityFilterFunc } from "../../data/entity";
|
import type { HaEntityPickerEntityFilterFunc } from "../../data/entity";
|
||||||
|
import type { FloorRegistryEntry } from "../../data/floor_registry";
|
||||||
import { domainToName } from "../../data/integration";
|
import { domainToName } from "../../data/integration";
|
||||||
import type { LabelRegistryEntry } from "../../data/label_registry";
|
import type { LabelRegistryEntry } from "../../data/label_registry";
|
||||||
import {
|
import {
|
||||||
@@ -111,10 +115,10 @@ export class HaTargetPickerItemRow extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected render() {
|
protected render() {
|
||||||
const { name, context, iconPath, fallbackIconPath, stateObject } =
|
const { name, context, iconPath, fallbackIconPath, stateObject, notFound } =
|
||||||
this._itemData(this.type, this.itemId);
|
this._itemData(this.type, this.itemId);
|
||||||
|
|
||||||
const showEntities = this.type !== "entity";
|
const showEntities = this.type !== "entity" && !notFound;
|
||||||
|
|
||||||
const entries = this.parentEntries || this._entries;
|
const entries = this.parentEntries || this._entries;
|
||||||
|
|
||||||
@@ -128,7 +132,7 @@ export class HaTargetPickerItemRow extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<ha-md-list-item type="text">
|
<ha-md-list-item type="text" class=${notFound ? "error" : ""}>
|
||||||
<div class="icon" slot="start">
|
<div class="icon" slot="start">
|
||||||
${this.subEntry
|
${this.subEntry
|
||||||
? html`
|
? html`
|
||||||
@@ -148,11 +152,15 @@ export class HaTargetPickerItemRow extends LitElement {
|
|||||||
/>`
|
/>`
|
||||||
: fallbackIconPath
|
: fallbackIconPath
|
||||||
? html`<ha-svg-icon .path=${fallbackIconPath}></ha-svg-icon>`
|
? html`<ha-svg-icon .path=${fallbackIconPath}></ha-svg-icon>`
|
||||||
: stateObject
|
: this.type === "entity"
|
||||||
? html`
|
? html`
|
||||||
<ha-state-icon
|
<ha-state-icon
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.stateObj=${stateObject}
|
.stateObj=${stateObject ||
|
||||||
|
({
|
||||||
|
entity_id: this.itemId,
|
||||||
|
attributes: {},
|
||||||
|
} as HassEntity)}
|
||||||
>
|
>
|
||||||
</ha-state-icon>
|
</ha-state-icon>
|
||||||
`
|
`
|
||||||
@@ -160,8 +168,14 @@ export class HaTargetPickerItemRow extends LitElement {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div slot="headline">${name}</div>
|
<div slot="headline">${name}</div>
|
||||||
${context && !this.hideContext
|
${notFound || (context && !this.hideContext)
|
||||||
? html`<span slot="supporting-text">${context}</span>`
|
? html`<span slot="supporting-text"
|
||||||
|
>${notFound
|
||||||
|
? this.hass.localize(
|
||||||
|
`ui.components.target-picker.${this.type}_not_found`
|
||||||
|
)
|
||||||
|
: context}</span
|
||||||
|
>`
|
||||||
: nothing}
|
: nothing}
|
||||||
${this._domainName && this.subEntry
|
${this._domainName && this.subEntry
|
||||||
? html`<span slot="supporting-text" class="domain"
|
? html`<span slot="supporting-text" class="domain"
|
||||||
@@ -474,26 +488,28 @@ export class HaTargetPickerItemRow extends LitElement {
|
|||||||
|
|
||||||
private _itemData = memoizeOne((type: TargetType, item: string) => {
|
private _itemData = memoizeOne((type: TargetType, item: string) => {
|
||||||
if (type === "floor") {
|
if (type === "floor") {
|
||||||
const floor = this.hass.floors?.[item];
|
const floor: FloorRegistryEntry | undefined = this.hass.floors?.[item];
|
||||||
return {
|
return {
|
||||||
name: floor?.name || item,
|
name: floor?.name || item,
|
||||||
iconPath: floor?.icon,
|
iconPath: floor?.icon,
|
||||||
fallbackIconPath: floor ? floorDefaultIconPath(floor) : mdiHome,
|
fallbackIconPath: floor ? floorDefaultIconPath(floor) : mdiHome,
|
||||||
|
notFound: !floor,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
if (type === "area") {
|
if (type === "area") {
|
||||||
const area = this.hass.areas?.[item];
|
const area: AreaRegistryEntry | undefined = this.hass.areas?.[item];
|
||||||
return {
|
return {
|
||||||
name: area?.name || item,
|
name: area?.name || item,
|
||||||
context: area.floor_id && this.hass.floors?.[area.floor_id]?.name,
|
context: area?.floor_id && this.hass.floors?.[area.floor_id]?.name,
|
||||||
iconPath: area?.icon,
|
iconPath: area?.icon,
|
||||||
fallbackIconPath: mdiTextureBox,
|
fallbackIconPath: mdiTextureBox,
|
||||||
|
notFound: !area,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
if (type === "device") {
|
if (type === "device") {
|
||||||
const device = this.hass.devices?.[item];
|
const device: DeviceRegistryEntry | undefined = this.hass.devices?.[item];
|
||||||
|
|
||||||
if (device.primary_config_entry) {
|
if (device?.primary_config_entry) {
|
||||||
this._getDeviceDomain(device.primary_config_entry);
|
this._getDeviceDomain(device.primary_config_entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -501,24 +517,25 @@ export class HaTargetPickerItemRow extends LitElement {
|
|||||||
name: device ? computeDeviceNameDisplay(device, this.hass) : item,
|
name: device ? computeDeviceNameDisplay(device, this.hass) : item,
|
||||||
context: device?.area_id && this.hass.areas?.[device.area_id]?.name,
|
context: device?.area_id && this.hass.areas?.[device.area_id]?.name,
|
||||||
fallbackIconPath: mdiDevices,
|
fallbackIconPath: mdiDevices,
|
||||||
|
notFound: !device,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
if (type === "entity") {
|
if (type === "entity") {
|
||||||
this._setDomainName(computeDomain(item));
|
this._setDomainName(computeDomain(item));
|
||||||
|
|
||||||
const stateObject = this.hass.states[item];
|
const stateObject: HassEntity | undefined = this.hass.states[item];
|
||||||
const entityName = computeEntityName(
|
const entityName = stateObject
|
||||||
stateObject,
|
? computeEntityName(stateObject, this.hass.entities, this.hass.devices)
|
||||||
this.hass.entities,
|
: item;
|
||||||
this.hass.devices
|
const { area, device } = stateObject
|
||||||
);
|
? getEntityContext(
|
||||||
const { area, device } = getEntityContext(
|
stateObject,
|
||||||
stateObject,
|
this.hass.entities,
|
||||||
this.hass.entities,
|
this.hass.devices,
|
||||||
this.hass.devices,
|
this.hass.areas,
|
||||||
this.hass.areas,
|
this.hass.floors
|
||||||
this.hass.floors
|
)
|
||||||
);
|
: { area: undefined, device: undefined };
|
||||||
const deviceName = device ? computeDeviceName(device) : undefined;
|
const deviceName = device ? computeDeviceName(device) : undefined;
|
||||||
const areaName = area ? computeAreaName(area) : undefined;
|
const areaName = area ? computeAreaName(area) : undefined;
|
||||||
const context = [areaName, entityName ? deviceName : undefined]
|
const context = [areaName, entityName ? deviceName : undefined]
|
||||||
@@ -528,15 +545,19 @@ export class HaTargetPickerItemRow extends LitElement {
|
|||||||
name: entityName || deviceName || item,
|
name: entityName || deviceName || item,
|
||||||
context,
|
context,
|
||||||
stateObject,
|
stateObject,
|
||||||
|
notFound: !stateObject,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// type label
|
// type label
|
||||||
const label = this._labelRegistry.find((lab) => lab.label_id === item);
|
const label: LabelRegistryEntry | undefined = this._labelRegistry.find(
|
||||||
|
(lab) => lab.label_id === item
|
||||||
|
);
|
||||||
return {
|
return {
|
||||||
name: label?.name || item,
|
name: label?.name || item,
|
||||||
iconPath: label?.icon,
|
iconPath: label?.icon,
|
||||||
fallbackIconPath: mdiLabel,
|
fallbackIconPath: mdiLabel,
|
||||||
|
notFound: !label,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -597,11 +618,20 @@ export class HaTargetPickerItemRow extends LitElement {
|
|||||||
border-radius: var(--ha-card-border-radius, var(--ha-border-radius-lg));
|
border-radius: var(--ha-card-border-radius, var(--ha-border-radius-lg));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.error {
|
||||||
|
background: var(--ha-color-fill-warning-quiet-resting);
|
||||||
|
}
|
||||||
|
|
||||||
|
.error [slot="supporting-text"] {
|
||||||
|
color: var(--ha-color-on-warning-normal);
|
||||||
|
}
|
||||||
|
|
||||||
state-badge {
|
state-badge {
|
||||||
color: var(--ha-color-on-neutral-quiet);
|
color: var(--ha-color-on-neutral-quiet);
|
||||||
}
|
}
|
||||||
|
|
||||||
.icon {
|
.icon {
|
||||||
|
width: 24px;
|
||||||
display: flex;
|
display: flex;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -695,6 +695,11 @@
|
|||||||
"remove_device_id": "Remove device",
|
"remove_device_id": "Remove device",
|
||||||
"remove_entity_id": "Remove entity",
|
"remove_entity_id": "Remove entity",
|
||||||
"remove_label_id": "Remove label",
|
"remove_label_id": "Remove label",
|
||||||
|
"floor_not_found": "Floor not found",
|
||||||
|
"area_not_found": "Area not found",
|
||||||
|
"device_not_found": "Device not found",
|
||||||
|
"entity_not_found": "Entity not found",
|
||||||
|
"label_not_found": "Label not found",
|
||||||
"devices_count": "{count} {count, plural,\n one {device}\n other {devices}\n}",
|
"devices_count": "{count} {count, plural,\n one {device}\n other {devices}\n}",
|
||||||
"entities_count": "{count} {count, plural,\n one {entity}\n other {entities}\n}",
|
"entities_count": "{count} {count, plural,\n one {entity}\n other {entities}\n}",
|
||||||
"target_details": "Target details",
|
"target_details": "Target details",
|
||||||
|
|||||||
Reference in New Issue
Block a user