Compare commits

..

23 Commits

Author SHA1 Message Date
Wendelin
76c21c5e78 fix prettier 2025-12-10 17:15:00 +01:00
Wendelin
b5afa9d457 Fixes 2025-12-10 17:09:04 +01:00
Wendelin
6f38e4fd75 Merge branch 'dev' of github.com:home-assistant/frontend into copilot/migrate-overflow-menus-to-ha-dropdown 2025-12-10 13:34:04 +01:00
copilot-swe-agent[bot]
44fcb72b79 Migrate row-level overflow menu from ha-md-menu to ha-dropdown in automation picker
Co-authored-by: wendevlin <12148533+wendevlin@users.noreply.github.com>
2025-12-09 13:09:46 +00:00
Wendelin
a88fcd41b5 Fix submenu 2025-12-09 14:03:19 +01:00
copilot-swe-agent[bot]
0a8c0c1c89 Complete scene-dashboard and script-picker migration - add @wa-select handlers and replace ha-sub-menu with native submenu
Co-authored-by: wendevlin <12148533+wendevlin@users.noreply.github.com>
2025-12-09 12:15:06 +00:00
copilot-swe-agent[bot]
6f140d30f5 Fix scene-dashboard and script-picker - remove .clickAction, @click, keep-open, has-overflow, replace ha-sub-menu with native submenu pattern
Co-authored-by: wendevlin <12148533+wendevlin@users.noreply.github.com>
2025-12-09 12:12:15 +00:00
copilot-swe-agent[bot]
baa84ea36c Fix multiple files - remove @click, .clickAction, has-overflow, replace ha-sub-menu, move class to button
Co-authored-by: wendevlin <12148533+wendevlin@users.noreply.github.com>
2025-12-09 12:03:58 +00:00
copilot-swe-agent[bot]
68033fa8b4 Fix ha-config-devices-dashboard.ts - replace .clickAction and ha-sub-menu with proper patterns
Co-authored-by: wendevlin <12148533+wendevlin@users.noreply.github.com>
2025-12-09 11:53:09 +00:00
copilot-swe-agent[bot]
5fd9bac9e5 Use data-action attribute instead of .action property for label bulk actions
Co-authored-by: wendevlin <12148533+wendevlin@users.noreply.github.com>
2025-12-09 11:46:54 +00:00
copilot-swe-agent[bot]
a1c7f91dd0 Replace ha-sub-menu with native ha-dropdown-item submenu pattern
Co-authored-by: wendevlin <12148533+wendevlin@users.noreply.github.com>
2025-12-09 11:42:58 +00:00
Wendelin
10538989f1 Merge branch 'dev' of github.com:home-assistant/frontend into copilot/migrate-overflow-menus-to-ha-dropdown 2025-12-09 12:33:13 +01:00
copilot-swe-agent[bot]
0b88a78ec0 Fix ha-automation-picker.ts - replace .clickAction with @wa-select handlers
Co-authored-by: wendevlin <12148533+wendevlin@users.noreply.github.com>
2025-12-05 13:30:08 +00:00
copilot-swe-agent[bot]
885f31a4b4 Fix type errors by re-adding ha-md-menu-item imports for row overflow menus
Co-authored-by: wendevlin <12148533+wendevlin@users.noreply.github.com>
2025-12-05 12:05:31 +00:00
copilot-swe-agent[bot]
e825a2b090 Migrate ha-button-menu to ha-dropdown in entities panel
Co-authored-by: wendevlin <12148533+wendevlin@users.noreply.github.com>
2025-12-05 12:01:26 +00:00
copilot-swe-agent[bot]
657f88595b Address code review feedback - remove unused methods and clean up warning class
Co-authored-by: wendevlin <12148533+wendevlin@users.noreply.github.com>
2025-12-05 11:58:09 +00:00
copilot-swe-agent[bot]
fdeca95215 Migrate ha-button-menu to ha-dropdown in devices dashboard
Co-authored-by: wendevlin <12148533+wendevlin@users.noreply.github.com>
2025-12-05 11:55:15 +00:00
copilot-swe-agent[bot]
13b60ed2ee Migrate ha-button-menu to ha-dropdown in script picker
Co-authored-by: wendevlin <12148533+wendevlin@users.noreply.github.com>
2025-12-05 11:51:53 +00:00
copilot-swe-agent[bot]
d74127001c Migrate ha-button-menu to ha-dropdown in automation picker
Co-authored-by: wendevlin <12148533+wendevlin@users.noreply.github.com>
2025-12-05 11:47:50 +00:00
copilot-swe-agent[bot]
eb9d1c119a Migrate ha-button-menu to ha-dropdown in updates and helpers panels
Co-authored-by: wendevlin <12148533+wendevlin@users.noreply.github.com>
2025-12-05 11:43:36 +00:00
copilot-swe-agent[bot]
cecf0a95c8 Migrate ha-button-menu to ha-dropdown in scene panel
Co-authored-by: wendevlin <12148533+wendevlin@users.noreply.github.com>
2025-12-05 11:39:22 +00:00
copilot-swe-agent[bot]
950a773f7e Migrate ha-button-menu to ha-dropdown in areas, dashboard, cloud, and network panels
Co-authored-by: wendevlin <12148533+wendevlin@users.noreply.github.com>
2025-12-05 11:33:16 +00:00
copilot-swe-agent[bot]
dddcd04ce9 Initial plan 2025-12-05 11:21:21 +00:00
353 changed files with 3100 additions and 3444 deletions

View File

@@ -1,4 +1,4 @@
import type { DeviceRegistryEntry } from "../../../src/data/device/device_registry"; import type { DeviceRegistryEntry } from "../../../src/data/device_registry";
import type { MockHomeAssistant } from "../../../src/fake_data/provide_hass"; import type { MockHomeAssistant } from "../../../src/fake_data/provide_hass";
export const mockDeviceRegistry = ( export const mockDeviceRegistry = (

View File

@@ -1,4 +1,4 @@
import type { EntityRegistryEntry } from "../../../src/data/entity/entity_registry"; import type { EntityRegistryEntry } from "../../../src/data/entity_registry";
import type { MockHomeAssistant } from "../../../src/fake_data/provide_hass"; import type { MockHomeAssistant } from "../../../src/fake_data/provide_hass";
export const mockEntityRegistry = ( export const mockEntityRegistry = (

View File

@@ -1,4 +1,4 @@
import type { LabelRegistryEntry } from "../../../src/data/label/label_registry"; import type { LabelRegistryEntry } from "../../../src/data/label_registry";
import type { MockHomeAssistant } from "../../../src/fake_data/provide_hass"; import type { MockHomeAssistant } from "../../../src/fake_data/provide_hass";
export const mockLabelRegistry = ( export const mockLabelRegistry = (

View File

@@ -142,7 +142,7 @@ export class DemoAutomationDescribeAction extends LitElement {
<div class="action"> <div class="action">
<span> <span>
${this._action ${this._action
? describeAction(this.hass, [], this._action) ? describeAction(this.hass, [], [], {}, this._action)
: "<invalid YAML>"} : "<invalid YAML>"}
</span> </span>
<ha-yaml-editor <ha-yaml-editor
@@ -155,7 +155,7 @@ export class DemoAutomationDescribeAction extends LitElement {
${ACTIONS.map( ${ACTIONS.map(
(conf) => html` (conf) => html`
<div class="action"> <div class="action">
<span>${describeAction(this.hass, [], conf as any)}</span> <span>${describeAction(this.hass, [], [], {}, conf as any)}</span>
<pre>${dump(conf)}</pre> <pre>${dump(conf)}</pre>
</div> </div>
` `

View File

@@ -11,11 +11,11 @@ import { computeInitialHaFormData } from "../../../../src/components/ha-form/com
import "../../../../src/components/ha-form/ha-form"; import "../../../../src/components/ha-form/ha-form";
import type { HaFormSchema } from "../../../../src/components/ha-form/types"; import type { HaFormSchema } from "../../../../src/components/ha-form/types";
import type { AreaRegistryEntry } from "../../../../src/data/area_registry"; import type { AreaRegistryEntry } from "../../../../src/data/area_registry";
import type { DeviceRegistryEntry } from "../../../../src/data/device/device_registry";
import { getEntity } from "../../../../src/fake_data/entity"; import { getEntity } from "../../../../src/fake_data/entity";
import { provideHass } from "../../../../src/fake_data/provide_hass"; import { provideHass } from "../../../../src/fake_data/provide_hass";
import type { HomeAssistant } from "../../../../src/types"; import type { HomeAssistant } from "../../../../src/types";
import "../../components/demo-black-white-row"; import "../../components/demo-black-white-row";
import type { DeviceRegistryEntry } from "../../../../src/data/device_registry";
const ENTITIES = [ const ENTITIES = [
getEntity("alarm_control_panel", "alarm", "disarmed", { getEntity("alarm_control_panel", "alarm", "disarmed", {

View File

@@ -13,9 +13,9 @@ import "../../../../src/components/ha-selector/ha-selector";
import "../../../../src/components/ha-settings-row"; import "../../../../src/components/ha-settings-row";
import type { AreaRegistryEntry } from "../../../../src/data/area_registry"; import type { AreaRegistryEntry } from "../../../../src/data/area_registry";
import type { BlueprintInput } from "../../../../src/data/blueprint"; import type { BlueprintInput } from "../../../../src/data/blueprint";
import type { DeviceRegistryEntry } from "../../../../src/data/device/device_registry"; import type { DeviceRegistryEntry } from "../../../../src/data/device_registry";
import type { FloorRegistryEntry } from "../../../../src/data/floor_registry"; import type { FloorRegistryEntry } from "../../../../src/data/floor_registry";
import type { LabelRegistryEntry } from "../../../../src/data/label/label_registry"; import type { LabelRegistryEntry } from "../../../../src/data/label_registry";
import { showDialog } from "../../../../src/dialogs/make-dialog-manager"; import { showDialog } from "../../../../src/dialogs/make-dialog-manager";
import { getEntity } from "../../../../src/fake_data/entity"; import { getEntity } from "../../../../src/fake_data/entity";
import { provideHass } from "../../../../src/fake_data/provide_hass"; import { provideHass } from "../../../../src/fake_data/provide_hass";

View File

@@ -6,8 +6,8 @@ import { customElement, property, state } from "lit/decorators";
import { classMap } from "lit/directives/class-map"; import { classMap } from "lit/directives/class-map";
import type { IntegrationManifest } from "../../../../src/data/integration"; import type { IntegrationManifest } from "../../../../src/data/integration";
import type { DeviceRegistryEntry } from "../../../../src/data/device/device_registry"; import type { DeviceRegistryEntry } from "../../../../src/data/device_registry";
import type { EntityRegistryEntry } from "../../../../src/data/entity/entity_registry"; import type { EntityRegistryEntry } from "../../../../src/data/entity_registry";
import { provideHass } from "../../../../src/fake_data/provide_hass"; import { provideHass } from "../../../../src/fake_data/provide_hass";
import "../../../../src/panels/config/integrations/ha-config-flow-card"; import "../../../../src/panels/config/integrations/ha-config-flow-card";
import type { import type {

View File

@@ -52,7 +52,7 @@
"@fullcalendar/list": "6.1.19", "@fullcalendar/list": "6.1.19",
"@fullcalendar/luxon3": "6.1.19", "@fullcalendar/luxon3": "6.1.19",
"@fullcalendar/timegrid": "6.1.19", "@fullcalendar/timegrid": "6.1.19",
"@home-assistant/webawesome": "3.0.0-ha.2", "@home-assistant/webawesome": "3.0.0-ha.1",
"@lezer/highlight": "1.2.3", "@lezer/highlight": "1.2.3",
"@lit-labs/motion": "1.0.9", "@lit-labs/motion": "1.0.9",
"@lit-labs/observers": "2.0.6", "@lit-labs/observers": "2.0.6",

View File

@@ -3,8 +3,8 @@ import {
DOMAIN_ATTRIBUTES_FORMATERS, DOMAIN_ATTRIBUTES_FORMATERS,
DOMAIN_ATTRIBUTES_UNITS, DOMAIN_ATTRIBUTES_UNITS,
TEMPERATURE_ATTRIBUTES, TEMPERATURE_ATTRIBUTES,
} from "../../data/entity/entity_attributes"; } from "../../data/entity_attributes";
import type { EntityRegistryDisplayEntry } from "../../data/entity/entity_registry"; import type { EntityRegistryDisplayEntry } from "../../data/entity_registry";
import type { FrontendLocaleData } from "../../data/translation"; import type { FrontendLocaleData } from "../../data/translation";
import type { WeatherEntity } from "../../data/weather"; import type { WeatherEntity } from "../../data/weather";
import { getWeatherUnit } from "../../data/weather"; import { getWeatherUnit } from "../../data/weather";

View File

@@ -1,12 +1,12 @@
import memoizeOne from "memoize-one"; import memoizeOne from "memoize-one";
import type { DeviceRegistryEntry } from "../../data/device/device_registry"; import type { DeviceRegistryEntry } from "../../data/device_registry";
import type { import type {
EntityRegistryDisplayEntry, EntityRegistryDisplayEntry,
EntityRegistryEntry, EntityRegistryEntry,
} from "../../data/entity/entity_registry"; } from "../../data/entity_registry";
import type { HomeAssistant } from "../../types"; import type { HomeAssistant } from "../../types";
import { getDuplicates } from "../string/get_duplicates";
import { computeStateName } from "./compute_state_name"; import { computeStateName } from "./compute_state_name";
import { getDuplicates } from "../string/get_duplicates";
export const computeDeviceName = ( export const computeDeviceName = (
device: DeviceRegistryEntry device: DeviceRegistryEntry

View File

@@ -2,7 +2,7 @@ import type { HassEntity } from "home-assistant-js-websocket";
import type { import type {
EntityRegistryDisplayEntry, EntityRegistryDisplayEntry,
EntityRegistryEntry, EntityRegistryEntry,
} from "../../data/entity/entity_registry"; } from "../../data/entity_registry";
import type { HomeAssistant } from "../../types"; import type { HomeAssistant } from "../../types";
import { computeDeviceName } from "./compute_device_name"; import { computeDeviceName } from "./compute_device_name";
import { computeStateName } from "./compute_state_name"; import { computeStateName } from "./compute_state_name";

View File

@@ -1,12 +1,12 @@
import type { HassConfig, HassEntity } from "home-assistant-js-websocket"; import type { HassConfig, HassEntity } from "home-assistant-js-websocket";
import { UNAVAILABLE, UNKNOWN } from "../../data/entity/entity"; import { UNAVAILABLE, UNKNOWN } from "../../data/entity";
import type { EntityRegistryDisplayEntry } from "../../data/entity/entity_registry"; import type { EntityRegistryDisplayEntry } from "../../data/entity_registry";
import type { FrontendLocaleData } from "../../data/translation"; import type { FrontendLocaleData } from "../../data/translation";
import { TimeZone } from "../../data/translation"; import { TimeZone } from "../../data/translation";
import type { HomeAssistant } from "../../types"; import type { HomeAssistant } from "../../types";
import { DURATION_UNITS, formatDuration } from "../datetime/format_duration";
import { formatDate } from "../datetime/format_date"; import { formatDate } from "../datetime/format_date";
import { formatDateTime } from "../datetime/format_date_time"; import { formatDateTime } from "../datetime/format_date_time";
import { DURATION_UNITS, formatDuration } from "../datetime/format_duration";
import { formatTime } from "../datetime/format_time"; import { formatTime } from "../datetime/format_time";
import { import {
formatNumber, formatNumber,

View File

@@ -1,5 +1,5 @@
import type { AreaRegistryEntry } from "../../../data/area_registry"; import type { AreaRegistryEntry } from "../../../data/area_registry";
import type { DeviceRegistryEntry } from "../../../data/device/device_registry"; import type { DeviceRegistryEntry } from "../../../data/device_registry";
import type { FloorRegistryEntry } from "../../../data/floor_registry"; import type { FloorRegistryEntry } from "../../../data/floor_registry";
import type { HomeAssistant } from "../../../types"; import type { HomeAssistant } from "../../../types";

View File

@@ -1,11 +1,11 @@
import type { HassEntity } from "home-assistant-js-websocket"; import type { HassEntity } from "home-assistant-js-websocket";
import type { AreaRegistryEntry } from "../../../data/area_registry"; import type { AreaRegistryEntry } from "../../../data/area_registry";
import type { DeviceRegistryEntry } from "../../../data/device/device_registry"; import type { DeviceRegistryEntry } from "../../../data/device_registry";
import type { import type {
EntityRegistryDisplayEntry, EntityRegistryDisplayEntry,
EntityRegistryEntry, EntityRegistryEntry,
ExtEntityRegistryEntry, ExtEntityRegistryEntry,
} from "../../../data/entity/entity_registry"; } from "../../../data/entity_registry";
import type { FloorRegistryEntry } from "../../../data/floor_registry"; import type { FloorRegistryEntry } from "../../../data/floor_registry";
import type { HomeAssistant } from "../../../types"; import type { HomeAssistant } from "../../../types";

View File

@@ -1,14 +1,14 @@
import type { ConfigEntry } from "../../data/config_entries"; import type { HomeAssistant } from "../../types";
import { deleteConfigEntry } from "../../data/config_entries";
import type { EntityRegistryEntry } from "../../data/entity/entity_registry";
import { removeEntityRegistryEntry } from "../../data/entity/entity_registry";
import { HELPERS_CRUD } from "../../data/helpers_crud";
import type { IntegrationManifest } from "../../data/integration"; import type { IntegrationManifest } from "../../data/integration";
import { computeDomain } from "./compute_domain";
import { HELPERS_CRUD } from "../../data/helpers_crud";
import type { Helper } from "../../panels/config/helpers/const"; import type { Helper } from "../../panels/config/helpers/const";
import { isHelperDomain } from "../../panels/config/helpers/const"; import { isHelperDomain } from "../../panels/config/helpers/const";
import type { HomeAssistant } from "../../types";
import { isComponentLoaded } from "../config/is_component_loaded"; import { isComponentLoaded } from "../config/is_component_loaded";
import { computeDomain } from "./compute_domain"; import type { EntityRegistryEntry } from "../../data/entity_registry";
import { removeEntityRegistryEntry } from "../../data/entity_registry";
import type { ConfigEntry } from "../../data/config_entries";
import { deleteConfigEntry } from "../../data/config_entries";
export const isDeletableEntity = ( export const isDeletableEntity = (
hass: HomeAssistant, hass: HomeAssistant,

View File

@@ -1,9 +1,9 @@
import type { HassEntity } from "home-assistant-js-websocket"; import type { HassEntity } from "home-assistant-js-websocket";
import { UNAVAILABLE_STATES } from "../../data/entity/entity";
import type { HomeAssistant } from "../../types";
import { stringCompare } from "../string/compare";
import { computeDomain } from "./compute_domain";
import { computeStateDomain } from "./compute_state_domain"; import { computeStateDomain } from "./compute_state_domain";
import { UNAVAILABLE_STATES } from "../../data/entity";
import type { HomeAssistant } from "../../types";
import { computeDomain } from "./compute_domain";
import { stringCompare } from "../string/compare";
export const FIXED_DOMAIN_STATES = { export const FIXED_DOMAIN_STATES = {
alarm_control_panel: [ alarm_control_panel: [

View File

@@ -1,7 +1,7 @@
import type { HassEntity } from "home-assistant-js-websocket"; import type { HassEntity } from "home-assistant-js-websocket";
import { isUnavailableState, UNAVAILABLE } from "../../data/entity/entity";
import type { HomeAssistant } from "../../types";
import { computeStateDomain } from "./compute_state_domain"; import { computeStateDomain } from "./compute_state_domain";
import { isUnavailableState, UNAVAILABLE } from "../../data/entity";
import type { HomeAssistant } from "../../types";
export const computeGroupEntitiesState = (states: HassEntity[]): string => { export const computeGroupEntitiesState = (states: HassEntity[]): string => {
if (!states.length) { if (!states.length) {

View File

@@ -1,5 +1,5 @@
import type { HassEntity } from "home-assistant-js-websocket"; import type { HassEntity } from "home-assistant-js-websocket";
import { isUnavailableState, OFF, UNAVAILABLE } from "../../data/entity/entity"; import { isUnavailableState, OFF, UNAVAILABLE } from "../../data/entity";
import { computeDomain } from "./compute_domain"; import { computeDomain } from "./compute_domain";
export function stateActive(stateObj: HassEntity, state?: string): boolean { export function stateActive(stateObj: HassEntity, state?: string): boolean {

View File

@@ -1,5 +1,5 @@
import type { HassEntity } from "home-assistant-js-websocket"; import type { HassEntity } from "home-assistant-js-websocket";
import { UNAVAILABLE } from "../../data/entity/entity"; import { UNAVAILABLE } from "../../data/entity";
import type { HomeAssistant } from "../../types"; import type { HomeAssistant } from "../../types";
import { DOMAINS_WITH_CARD } from "../const"; import { DOMAINS_WITH_CARD } from "../const";
import { canToggleState } from "./can_toggle_state"; import { canToggleState } from "./can_toggle_state";

View File

@@ -1,6 +1,6 @@
/** Return a color representing a state. */ /** Return a color representing a state. */
import type { HassEntity } from "home-assistant-js-websocket"; import type { HassEntity } from "home-assistant-js-websocket";
import { UNAVAILABLE } from "../../data/entity/entity"; import { UNAVAILABLE } from "../../data/entity";
import type { GroupEntity } from "../../data/group"; import type { GroupEntity } from "../../data/group";
import { computeGroupDomain } from "../../data/group"; import { computeGroupDomain } from "../../data/group";
import { computeCssVariable } from "../../resources/css-variables"; import { computeCssVariable } from "../../resources/css-variables";

View File

@@ -2,7 +2,7 @@ import type {
HassEntity, HassEntity,
HassEntityAttributeBase, HassEntityAttributeBase,
} from "home-assistant-js-websocket"; } from "home-assistant-js-websocket";
import type { EntityRegistryDisplayEntry } from "../../data/entity/entity_registry"; import type { EntityRegistryDisplayEntry } from "../../data/entity_registry";
import type { FrontendLocaleData } from "../../data/translation"; import type { FrontendLocaleData } from "../../data/translation";
import { NumberFormat } from "../../data/translation"; import { NumberFormat } from "../../data/translation";
import { round } from "./round"; import { round } from "./round";

View File

@@ -3,11 +3,11 @@ import { getGraphColorByIndex } from "../../common/color/colors";
import { hex2rgb, lab2hex, rgb2lab } from "../../common/color/convert-color"; import { hex2rgb, lab2hex, rgb2lab } from "../../common/color/convert-color";
import { labBrighten } from "../../common/color/lab"; import { labBrighten } from "../../common/color/lab";
import { computeDomain } from "../../common/entity/compute_domain"; import { computeDomain } from "../../common/entity/compute_domain";
import { stateColorProperties } from "../../common/entity/state_color";
import { UNAVAILABLE, UNKNOWN } from "../../data/entity";
import { computeCssValue } from "../../resources/css-variables";
import { computeStateDomain } from "../../common/entity/compute_state_domain"; import { computeStateDomain } from "../../common/entity/compute_state_domain";
import { FIXED_DOMAIN_STATES } from "../../common/entity/get_states"; import { FIXED_DOMAIN_STATES } from "../../common/entity/get_states";
import { stateColorProperties } from "../../common/entity/state_color";
import { UNAVAILABLE, UNKNOWN } from "../../data/entity/entity";
import { computeCssValue } from "../../resources/css-variables";
const DOMAIN_STATE_SHADES: Record<string, Record<string, number>> = { const DOMAIN_STATE_SHADES: Record<string, Record<string, number>> = {
media_player: { media_player: {

View File

@@ -6,7 +6,7 @@ import { computeCssColor } from "../../common/color/compute-color";
import { fireEvent } from "../../common/dom/fire_event"; import { fireEvent } from "../../common/dom/fire_event";
import { stopPropagation } from "../../common/dom/stop_propagation"; import { stopPropagation } from "../../common/dom/stop_propagation";
import { stringCompare } from "../../common/string/compare"; import { stringCompare } from "../../common/string/compare";
import type { LabelRegistryEntry } from "../../data/label/label_registry"; import type { LabelRegistryEntry } from "../../data/label_registry";
import "../chips/ha-chip-set"; import "../chips/ha-chip-set";
import "../ha-dropdown"; import "../ha-dropdown";
import "../ha-dropdown-item"; import "../ha-dropdown-item";

View File

@@ -1,9 +1,9 @@
import { customElement } from "lit/decorators"; import { customElement } from "lit/decorators";
import type { DeviceAction } from "../../data/device/device_automation"; import type { DeviceAction } from "../../data/device_automation";
import { import {
fetchDeviceActions, fetchDeviceActions,
localizeDeviceAutomationAction, localizeDeviceAutomationAction,
} from "../../data/device/device_automation"; } from "../../data/device_automation";
import { HaDeviceAutomationPicker } from "./ha-device-automation-picker"; import { HaDeviceAutomationPicker } from "./ha-device-automation-picker";
@customElement("ha-device-action-picker") @customElement("ha-device-action-picker")

View File

@@ -2,17 +2,17 @@ import { consume } from "@lit/context";
import { css, html, LitElement, nothing } from "lit"; import { css, html, LitElement, nothing } from "lit";
import { property, state } from "lit/decorators"; import { property, state } from "lit/decorators";
import { fireEvent } from "../../common/dom/fire_event"; import { fireEvent } from "../../common/dom/fire_event";
import { stopPropagation } from "../../common/dom/stop_propagation";
import { fullEntitiesContext } from "../../data/context"; import { fullEntitiesContext } from "../../data/context";
import type { DeviceAutomation } from "../../data/device/device_automation"; import type { DeviceAutomation } from "../../data/device_automation";
import { import {
deviceAutomationsEqual, deviceAutomationsEqual,
sortDeviceAutomations, sortDeviceAutomations,
} from "../../data/device/device_automation"; } from "../../data/device_automation";
import type { EntityRegistryEntry } from "../../data/entity/entity_registry"; import type { EntityRegistryEntry } from "../../data/entity_registry";
import type { HomeAssistant } from "../../types"; import type { HomeAssistant } from "../../types";
import "../ha-md-select";
import "../ha-md-select-option"; import "../ha-md-select-option";
import "../ha-md-select";
import { stopPropagation } from "../../common/dom/stop_propagation";
const NO_AUTOMATION_KEY = "NO_AUTOMATION"; const NO_AUTOMATION_KEY = "NO_AUTOMATION";
const UNKNOWN_AUTOMATION_KEY = "UNKNOWN_AUTOMATION"; const UNKNOWN_AUTOMATION_KEY = "UNKNOWN_AUTOMATION";

View File

@@ -1,9 +1,9 @@
import { customElement } from "lit/decorators"; import { customElement } from "lit/decorators";
import type { DeviceCondition } from "../../data/device/device_automation"; import type { DeviceCondition } from "../../data/device_automation";
import { import {
fetchDeviceConditions, fetchDeviceConditions,
localizeDeviceAutomationCondition, localizeDeviceAutomationCondition,
} from "../../data/device/device_automation"; } from "../../data/device_automation";
import { HaDeviceAutomationPicker } from "./ha-device-automation-picker"; import { HaDeviceAutomationPicker } from "./ha-device-automation-picker";
@customElement("ha-device-condition-picker") @customElement("ha-device-condition-picker")

View File

@@ -12,8 +12,8 @@ import {
deviceComboBoxKeys, deviceComboBoxKeys,
getDevices, getDevices,
type DevicePickerItem, type DevicePickerItem,
} from "../../data/device/device_picker"; type DeviceRegistryEntry,
import type { DeviceRegistryEntry } from "../../data/device/device_registry"; } from "../../data/device_registry";
import type { HomeAssistant } from "../../types"; import type { HomeAssistant } from "../../types";
import { brandsUrl } from "../../util/brands-url"; import { brandsUrl } from "../../util/brands-url";
import "../ha-generic-picker"; import "../ha-generic-picker";

View File

@@ -1,9 +1,9 @@
import { customElement } from "lit/decorators"; import { customElement } from "lit/decorators";
import type { DeviceTrigger } from "../../data/device/device_automation"; import type { DeviceTrigger } from "../../data/device_automation";
import { import {
fetchDeviceTriggers, fetchDeviceTriggers,
localizeDeviceAutomationTrigger, localizeDeviceAutomationTrigger,
} from "../../data/device/device_automation"; } from "../../data/device_automation";
import { HaDeviceAutomationPicker } from "./ha-device-automation-picker"; import { HaDeviceAutomationPicker } from "./ha-device-automation-picker";
@customElement("ha-device-trigger-picker") @customElement("ha-device-trigger-picker")

View File

@@ -4,7 +4,7 @@ import { customElement, property } from "lit/decorators";
import memoizeOne from "memoize-one"; import memoizeOne from "memoize-one";
import { fireEvent } from "../../common/dom/fire_event"; import { fireEvent } from "../../common/dom/fire_event";
import { isValidEntityId } from "../../common/entity/valid_entity_id"; import { isValidEntityId } from "../../common/entity/valid_entity_id";
import type { HaEntityPickerEntityFilterFunc } from "../../data/entity/entity"; import type { HaEntityPickerEntityFilterFunc } from "../../data/entity";
import type { HomeAssistant, ValueChangedEvent } from "../../types"; import type { HomeAssistant, ValueChangedEvent } from "../../types";
import "../ha-sortable"; import "../ha-sortable";
import "./ha-entity-picker"; import "./ha-entity-picker";

View File

@@ -7,12 +7,12 @@ import { fireEvent } from "../../common/dom/fire_event";
import { computeEntityNameList } from "../../common/entity/compute_entity_name_display"; import { computeEntityNameList } from "../../common/entity/compute_entity_name_display";
import { isValidEntityId } from "../../common/entity/valid_entity_id"; import { isValidEntityId } from "../../common/entity/valid_entity_id";
import { computeRTL } from "../../common/util/compute_rtl"; import { computeRTL } from "../../common/util/compute_rtl";
import type { HaEntityPickerEntityFilterFunc } from "../../data/entity/entity"; import type { HaEntityPickerEntityFilterFunc } from "../../data/entity";
import { import {
entityComboBoxKeys, entityComboBoxKeys,
getEntities, getEntities,
type EntityComboBoxItem, type EntityComboBoxItem,
} from "../../data/entity/entity_picker"; } from "../../data/entity_registry";
import { domainToName } from "../../data/integration"; import { domainToName } from "../../data/integration";
import { import {
isHelperDomain, isHelperDomain,

View File

@@ -6,11 +6,7 @@ import { customElement, property, state } from "lit/decorators";
import { STATES_OFF } from "../../common/const"; import { STATES_OFF } from "../../common/const";
import { computeStateDomain } from "../../common/entity/compute_state_domain"; import { computeStateDomain } from "../../common/entity/compute_state_domain";
import { computeStateName } from "../../common/entity/compute_state_name"; import { computeStateName } from "../../common/entity/compute_state_name";
import { import { UNAVAILABLE, UNKNOWN, isUnavailableState } from "../../data/entity";
UNAVAILABLE,
UNKNOWN,
isUnavailableState,
} from "../../data/entity/entity";
import { forwardHaptic } from "../../data/haptics"; import { forwardHaptic } from "../../data/haptics";
import type { HomeAssistant } from "../../types"; import type { HomeAssistant } from "../../types";
import "../ha-formfield"; import "../ha-formfield";

View File

@@ -14,12 +14,8 @@ import {
getNumberFormatOptions, getNumberFormatOptions,
isNumericState, isNumericState,
} from "../../common/number/format_number"; } from "../../common/number/format_number";
import { import { isUnavailableState, UNAVAILABLE, UNKNOWN } from "../../data/entity";
isUnavailableState, import type { EntityRegistryDisplayEntry } from "../../data/entity_registry";
UNAVAILABLE,
UNKNOWN,
} from "../../data/entity/entity";
import type { EntityRegistryDisplayEntry } from "../../data/entity/entity_registry";
import { timerTimeRemaining } from "../../data/timer"; import { timerTimeRemaining } from "../../data/timer";
import type { HomeAssistant } from "../../types"; import type { HomeAssistant } from "../../types";
import "../ha-label-badge"; import "../ha-label-badge";

View File

@@ -13,9 +13,9 @@ import { createAreaRegistryEntry } from "../data/area_registry";
import type { import type {
DeviceEntityDisplayLookup, DeviceEntityDisplayLookup,
DeviceRegistryEntry, DeviceRegistryEntry,
} from "../data/device/device_registry"; } from "../data/device_registry";
import { getDeviceEntityDisplayLookup } from "../data/device/device_registry"; import { getDeviceEntityDisplayLookup } from "../data/device_registry";
import type { EntityRegistryDisplayEntry } from "../data/entity/entity_registry"; import type { EntityRegistryDisplayEntry } from "../data/entity_registry";
import { showAlertDialog } from "../dialogs/generic/show-dialog-box"; import { showAlertDialog } from "../dialogs/generic/show-dialog-box";
import { showAreaRegistryDetailDialog } from "../panels/config/areas/show-dialog-area-registry-detail"; import { showAreaRegistryDetailDialog } from "../panels/config/areas/show-dialog-area-registry-detail";
import type { HomeAssistant, ValueChangedEvent } from "../types"; import type { HomeAssistant, ValueChangedEvent } from "../types";
@@ -296,6 +296,7 @@ export class HaAreaPicker extends LitElement {
secondary: floorName, secondary: floorName,
icon: area.icon || undefined, icon: area.icon || undefined,
icon_path: area.icon ? undefined : mdiTextureBox, icon_path: area.icon ? undefined : mdiTextureBox,
sorting_label: areaName,
search_labels: { search_labels: {
areaName: areaName || null, areaName: areaName || null,
floorName: floorName || null, floorName: floorName || null,

View File

@@ -3,15 +3,15 @@ import type { CSSResultGroup, PropertyValues } from "lit";
import { css, html, LitElement, nothing } from "lit"; import { css, html, LitElement, nothing } from "lit";
import { customElement, property, state } from "lit/decorators"; import { customElement, property, state } from "lit/decorators";
import { computeAttributeNameDisplay } from "../common/entity/compute_attribute_display"; import { computeAttributeNameDisplay } from "../common/entity/compute_attribute_display";
import { computeStateDomain } from "../common/entity/compute_state_domain";
import { import {
STATE_ATTRIBUTES, STATE_ATTRIBUTES,
STATE_ATTRIBUTES_DOMAIN_CLASS, STATE_ATTRIBUTES_DOMAIN_CLASS,
} from "../data/entity/entity_attributes"; } from "../data/entity_attributes";
import { haStyle } from "../resources/styles"; import { haStyle } from "../resources/styles";
import type { HomeAssistant } from "../types"; import type { HomeAssistant } from "../types";
import "./ha-attribute-value"; import "./ha-attribute-value";
import "./ha-expansion-panel"; import "./ha-expansion-panel";
import { computeStateDomain } from "../common/entity/compute_state_domain";
@customElement("ha-attributes") @customElement("ha-attributes")
class HaAttributes extends LitElement { class HaAttributes extends LitElement {

View File

@@ -52,9 +52,7 @@ export class HaAutomationRow extends LitElement {
<slot name="leading-icon"></slot> <slot name="leading-icon"></slot>
</div> </div>
<slot class="header" name="header"></slot> <slot class="header" name="header"></slot>
<div class="icons"> <slot name="icons"></slot>
<slot name="icons"></slot>
</div>
</div> </div>
`; `;
} }
@@ -120,9 +118,9 @@ export class HaAutomationRow extends LitElement {
} }
.row { .row {
display: flex; display: flex;
padding: 0 var(--ha-space-3); padding: var(--ha-space-0) var(--ha-space-2);
min-height: 48px; min-height: 48px;
align-items: flex-start; align-items: center;
cursor: pointer; cursor: pointer;
overflow: hidden; overflow: hidden;
font-weight: var(--ha-font-weight-medium); font-weight: var(--ha-font-weight-medium);
@@ -147,9 +145,6 @@ export class HaAutomationRow extends LitElement {
align-items: center; align-items: center;
transform: rotate(45deg); transform: rotate(45deg);
} }
.leading-icon-wrapper {
padding-top: var(--ha-space-3);
}
::slotted([slot="leading-icon"]) { ::slotted([slot="leading-icon"]) {
color: var(--ha-color-on-neutral-quiet); color: var(--ha-color-on-neutral-quiet);
} }
@@ -177,10 +172,6 @@ export class HaAutomationRow extends LitElement {
overflow-wrap: anywhere; overflow-wrap: anywhere;
margin: var(--ha-space-0) var(--ha-space-3); margin: var(--ha-space-0) var(--ha-space-3);
} }
.icons {
display: flex;
align-items: center;
}
:host([sort-selected]) .row { :host([sort-selected]) .row {
outline: solid; outline: solid;
outline-color: rgba(var(--rgb-accent-color), 0.6); outline-color: rgba(var(--rgb-accent-color), 0.6);

View File

@@ -3,7 +3,7 @@ import { css, html, LitElement, nothing } from "lit";
import { customElement, property } from "lit/decorators"; import { customElement, property } from "lit/decorators";
import type { ClimateEntity } from "../data/climate"; import type { ClimateEntity } from "../data/climate";
import { CLIMATE_PRESET_NONE } from "../data/climate"; import { CLIMATE_PRESET_NONE } from "../data/climate";
import { isUnavailableState, OFF } from "../data/entity/entity"; import { isUnavailableState, OFF } from "../data/entity";
import type { HomeAssistant } from "../types"; import type { HomeAssistant } from "../types";
@customElement("ha-climate-state") @customElement("ha-climate-state")

View File

@@ -9,14 +9,14 @@ import type { ConfigEntry, SubEntry } from "../data/config_entries";
import { getConfigEntry, getSubEntries } from "../data/config_entries"; import { getConfigEntry, getSubEntries } from "../data/config_entries";
import type { Agent } from "../data/conversation"; import type { Agent } from "../data/conversation";
import { listAgents } from "../data/conversation"; import { listAgents } from "../data/conversation";
import { getExtendedEntityRegistryEntry } from "../data/entity/entity_registry";
import { fetchIntegrationManifest } from "../data/integration"; import { fetchIntegrationManifest } from "../data/integration";
import { showOptionsFlowDialog } from "../dialogs/config-flow/show-dialog-options-flow"; import { showOptionsFlowDialog } from "../dialogs/config-flow/show-dialog-options-flow";
import { showSubConfigFlowDialog } from "../dialogs/config-flow/show-dialog-sub-config-flow";
import type { HomeAssistant } from "../types"; import type { HomeAssistant } from "../types";
import "./ha-list-item"; import "./ha-list-item";
import "./ha-select"; import "./ha-select";
import type { HaSelect } from "./ha-select"; import type { HaSelect } from "./ha-select";
import { getExtendedEntityRegistryEntry } from "../data/entity_registry";
import { showSubConfigFlowDialog } from "../dialogs/config-flow/show-dialog-sub-config-flow";
const NONE = "__NONE_OPTION__"; const NONE = "__NONE_OPTION__";

View File

@@ -47,7 +47,6 @@ export class HaDomainIcon extends LitElement {
if (icn) { if (icn) {
return html`<ha-icon .icon=${icn}></ha-icon>`; return html`<ha-icon .icon=${icn}></ha-icon>`;
} }
return this._renderFallback(); return this._renderFallback();
}); });

View File

@@ -1,9 +1,6 @@
import DropdownItem from "@home-assistant/webawesome/dist/components/dropdown-item/dropdown-item"; import DropdownItem from "@home-assistant/webawesome/dist/components/dropdown-item/dropdown-item";
import "@home-assistant/webawesome/dist/components/icon/icon"; import { css, type CSSResultGroup } from "lit";
import { css, type CSSResultGroup, html } from "lit";
import { customElement } from "lit/decorators"; import { customElement } from "lit/decorators";
import "./ha-svg-icon";
import { mdiCheckboxBlankOutline, mdiCheckboxMarked } from "@mdi/js";
/** /**
* Home Assistant dropdown item component * Home Assistant dropdown item component
@@ -17,16 +14,6 @@ import { mdiCheckboxBlankOutline, mdiCheckboxMarked } from "@mdi/js";
*/ */
@customElement("ha-dropdown-item") @customElement("ha-dropdown-item")
export class HaDropdownItem extends DropdownItem { export class HaDropdownItem extends DropdownItem {
protected renderCheckboxIcon() {
return html`
<ha-svg-icon
id="check"
part="checkmark"
.path=${this.checked ? mdiCheckboxMarked : mdiCheckboxBlankOutline}
></ha-svg-icon>
`;
}
static get styles(): CSSResultGroup { static get styles(): CSSResultGroup {
return [ return [
DropdownItem.styles, DropdownItem.styles,
@@ -35,10 +22,6 @@ export class HaDropdownItem extends DropdownItem {
min-height: var(--ha-space-10); min-height: var(--ha-space-10);
} }
#check {
visibility: visible;
}
#icon ::slotted(*) { #icon ::slotted(*) {
color: var(--ha-color-on-neutral-normal); color: var(--ha-color-on-neutral-normal);
} }

View File

@@ -10,8 +10,8 @@ import { computeCssColor } from "../common/color/compute-color";
import { fireEvent } from "../common/dom/fire_event"; import { fireEvent } from "../common/dom/fire_event";
import { navigate } from "../common/navigate"; import { navigate } from "../common/navigate";
import { stringCompare } from "../common/string/compare"; import { stringCompare } from "../common/string/compare";
import type { LabelRegistryEntry } from "../data/label/label_registry"; import type { LabelRegistryEntry } from "../data/label_registry";
import { subscribeLabelRegistry } from "../data/label/label_registry"; import { subscribeLabelRegistry } from "../data/label_registry";
import { SubscribeMixin } from "../mixins/subscribe-mixin"; import { SubscribeMixin } from "../mixins/subscribe-mixin";
import { haStyleScrollbar } from "../resources/styles"; import { haStyleScrollbar } from "../resources/styles";
import type { HomeAssistant } from "../types"; import type { HomeAssistant } from "../types";

View File

@@ -12,9 +12,9 @@ import { updateAreaRegistryEntry } from "../data/area_registry";
import type { import type {
DeviceEntityDisplayLookup, DeviceEntityDisplayLookup,
DeviceRegistryEntry, DeviceRegistryEntry,
} from "../data/device/device_registry"; } from "../data/device_registry";
import { getDeviceEntityDisplayLookup } from "../data/device/device_registry"; import { getDeviceEntityDisplayLookup } from "../data/device_registry";
import type { EntityRegistryDisplayEntry } from "../data/entity/entity_registry"; import type { EntityRegistryDisplayEntry } from "../data/entity_registry";
import { import {
createFloorRegistryEntry, createFloorRegistryEntry,
getFloorAreaLookup, getFloorAreaLookup,
@@ -291,6 +291,7 @@ export class HaFloorPicker extends LitElement {
id: floor.floor_id, id: floor.floor_id,
primary: floorName, primary: floorName,
floor: floor, floor: floor,
sorting_label: floor.level?.toString() || "zzzzz",
search_labels: { search_labels: {
floorName, floorName,
floor_id: floor.floor_id, floor_id: floor.floor_id,

View File

@@ -367,10 +367,7 @@ export class HaGenericPicker extends LitElement {
wa-popover::part(body) { wa-popover::part(body) {
width: max(var(--body-width), 250px); width: max(var(--body-width), 250px);
max-width: var( max-width: max(var(--body-width), 250px);
--ha-generic-picker-max-width,
max(var(--body-width), 250px)
);
max-height: 500px; max-height: 500px;
height: 70vh; height: 70vh;
overflow: hidden; overflow: hidden;

View File

@@ -1,7 +1,7 @@
import type { TemplateResult } from "lit"; import type { TemplateResult } from "lit";
import { css, html, LitElement } from "lit"; import { css, html, LitElement } from "lit";
import { customElement, property } from "lit/decorators"; import { customElement, property } from "lit/decorators";
import { isUnavailableState, OFF } from "../data/entity/entity"; import { isUnavailableState, OFF } from "../data/entity";
import type { HumidifierEntity } from "../data/humidifier"; import type { HumidifierEntity } from "../data/humidifier";
import type { HomeAssistant } from "../types"; import type { HomeAssistant } from "../types";

View File

@@ -11,12 +11,13 @@ import {
} from "lit/decorators"; } from "lit/decorators";
import memoizeOne from "memoize-one"; import memoizeOne from "memoize-one";
import { fireEvent } from "../common/dom/fire_event"; import { fireEvent } from "../common/dom/fire_event";
import { getLabels, labelComboBoxKeys } from "../data/label/label_picker"; import type { LabelRegistryEntry } from "../data/label_registry";
import { import {
createLabelRegistryEntry, createLabelRegistryEntry,
getLabels,
labelComboBoxKeys,
subscribeLabelRegistry, subscribeLabelRegistry,
type LabelRegistryEntry, } from "../data/label_registry";
} from "../data/label/label_registry";
import { showAlertDialog } from "../dialogs/generic/show-dialog-box"; import { showAlertDialog } from "../dialogs/generic/show-dialog-box";
import { SubscribeMixin } from "../mixins/subscribe-mixin"; import { SubscribeMixin } from "../mixins/subscribe-mixin";
import { showLabelDetailDialog } from "../panels/config/labels/show-dialog-label-detail"; import { showLabelDetailDialog } from "../panels/config/labels/show-dialog-label-detail";

View File

@@ -8,11 +8,11 @@ import memoizeOne from "memoize-one";
import { computeCssColor } from "../common/color/compute-color"; import { computeCssColor } from "../common/color/compute-color";
import { fireEvent } from "../common/dom/fire_event"; import { fireEvent } from "../common/dom/fire_event";
import { stringCompare } from "../common/string/compare"; import { stringCompare } from "../common/string/compare";
import type { LabelRegistryEntry } from "../data/label/label_registry"; import type { LabelRegistryEntry } from "../data/label_registry";
import { import {
subscribeLabelRegistry, subscribeLabelRegistry,
updateLabelRegistryEntry, updateLabelRegistryEntry,
} from "../data/label/label_registry"; } from "../data/label_registry";
import { SubscribeMixin } from "../mixins/subscribe-mixin"; import { SubscribeMixin } from "../mixins/subscribe-mixin";
import { showLabelDetailDialog } from "../panels/config/labels/show-dialog-label-detail"; import { showLabelDetailDialog } from "../panels/config/labels/show-dialog-label-detail";
import type { HomeAssistant, ValueChangedEvent } from "../types"; import type { HomeAssistant, ValueChangedEvent } from "../types";

View File

@@ -288,30 +288,13 @@ export class HaPickerComboBox extends ScrollableFadeMixin(LitElement) {
let items = [...this.getItems(this._search, this.selectedSection)]; let items = [...this.getItems(this._search, this.selectedSection)];
if (!this.sections?.length) { if (!this.sections?.length) {
items = items.sort((entityA, entityB) => { items = items.sort((entityA, entityB) =>
const sortLabelA = caseInsensitiveStringCompare(
typeof entityA === "string" ? entityA : entityA.sorting_label; (entityA as PickerComboBoxItem).sorting_label!,
const sortLabelB = (entityB as PickerComboBoxItem).sorting_label!,
typeof entityB === "string" ? entityB : entityB.sorting_label;
if (!sortLabelA || !sortLabelB) {
return 0;
}
if (!sortLabelB) {
return -1;
}
if (!sortLabelA) {
return 1;
}
return caseInsensitiveStringCompare(
sortLabelA,
sortLabelB,
this.hass?.locale.language ?? navigator.language this.hass?.locale.language ?? navigator.language
); )
}); );
} }
if (!items.length) { if (!items.length) {

View File

@@ -7,7 +7,7 @@ import { fullEntitiesContext } from "../../data/context";
import { import {
subscribeEntityRegistry, subscribeEntityRegistry,
type EntityRegistryEntry, type EntityRegistryEntry,
} from "../../data/entity/entity_registry"; } from "../../data/entity_registry";
import type { Action } from "../../data/script"; import type { Action } from "../../data/script";
import { migrateAutomationAction } from "../../data/script"; import { migrateAutomationAction } from "../../data/script";
import type { ActionSelector } from "../../data/selector"; import type { ActionSelector } from "../../data/selector";

View File

@@ -4,14 +4,14 @@ import { html, LitElement, nothing } from "lit";
import { customElement, property, state } from "lit/decorators"; import { customElement, property, state } from "lit/decorators";
import memoizeOne from "memoize-one"; import memoizeOne from "memoize-one";
import { ensureArray } from "../../common/array/ensure-array"; import { ensureArray } from "../../common/array/ensure-array";
import type { DeviceRegistryEntry } from "../../data/device_registry";
import { getDeviceIntegrationLookup } from "../../data/device_registry";
import { fireEvent } from "../../common/dom/fire_event"; import { fireEvent } from "../../common/dom/fire_event";
import type { EntitySources } from "../../data/entity_sources";
import { fetchEntitySourcesWithCache } from "../../data/entity_sources";
import type { AreaSelector } from "../../data/selector";
import type { ConfigEntry } from "../../data/config_entries"; import type { ConfigEntry } from "../../data/config_entries";
import { getConfigEntries } from "../../data/config_entries"; import { getConfigEntries } from "../../data/config_entries";
import type { DeviceRegistryEntry } from "../../data/device/device_registry";
import { getDeviceIntegrationLookup } from "../../data/device/device_registry";
import type { EntitySources } from "../../data/entity/entity_sources";
import { fetchEntitySourcesWithCache } from "../../data/entity/entity_sources";
import type { AreaSelector } from "../../data/selector";
import { import {
filterSelectorDevices, filterSelectorDevices,
filterSelectorEntities, filterSelectorEntities,

View File

@@ -5,13 +5,13 @@ import { customElement, property, state } from "lit/decorators";
import memoizeOne from "memoize-one"; import memoizeOne from "memoize-one";
import { ensureArray } from "../../common/array/ensure-array"; import { ensureArray } from "../../common/array/ensure-array";
import { fireEvent } from "../../common/dom/fire_event"; import { fireEvent } from "../../common/dom/fire_event";
import type { DeviceRegistryEntry } from "../../data/device_registry";
import { getDeviceIntegrationLookup } from "../../data/device_registry";
import type { EntitySources } from "../../data/entity_sources";
import { fetchEntitySourcesWithCache } from "../../data/entity_sources";
import type { DeviceSelector } from "../../data/selector";
import type { ConfigEntry } from "../../data/config_entries"; import type { ConfigEntry } from "../../data/config_entries";
import { getConfigEntries } from "../../data/config_entries"; import { getConfigEntries } from "../../data/config_entries";
import type { DeviceRegistryEntry } from "../../data/device/device_registry";
import { getDeviceIntegrationLookup } from "../../data/device/device_registry";
import type { EntitySources } from "../../data/entity/entity_sources";
import { fetchEntitySourcesWithCache } from "../../data/entity/entity_sources";
import type { DeviceSelector } from "../../data/selector";
import { import {
filterSelectorDevices, filterSelectorDevices,
filterSelectorEntities, filterSelectorEntities,

View File

@@ -4,12 +4,12 @@ import { html, LitElement, nothing } from "lit";
import { customElement, property, state } from "lit/decorators"; import { customElement, property, state } from "lit/decorators";
import { ensureArray } from "../../common/array/ensure-array"; import { ensureArray } from "../../common/array/ensure-array";
import { fireEvent } from "../../common/dom/fire_event"; import { fireEvent } from "../../common/dom/fire_event";
import type { EntitySources } from "../../data/entity/entity_sources"; import type { EntitySources } from "../../data/entity_sources";
import { fetchEntitySourcesWithCache } from "../../data/entity/entity_sources"; import { fetchEntitySourcesWithCache } from "../../data/entity_sources";
import type { EntitySelector } from "../../data/selector"; import type { EntitySelector } from "../../data/selector";
import { import {
computeCreateDomains,
filterSelectorEntities, filterSelectorEntities,
computeCreateDomains,
} from "../../data/selector"; } from "../../data/selector";
import type { HomeAssistant } from "../../types"; import type { HomeAssistant } from "../../types";
import "../entity/ha-entities-picker"; import "../entity/ha-entities-picker";

View File

@@ -4,14 +4,14 @@ import { html, LitElement, nothing } from "lit";
import { customElement, property, state } from "lit/decorators"; import { customElement, property, state } from "lit/decorators";
import memoizeOne from "memoize-one"; import memoizeOne from "memoize-one";
import { ensureArray } from "../../common/array/ensure-array"; import { ensureArray } from "../../common/array/ensure-array";
import type { DeviceRegistryEntry } from "../../data/device_registry";
import { getDeviceIntegrationLookup } from "../../data/device_registry";
import { fireEvent } from "../../common/dom/fire_event"; import { fireEvent } from "../../common/dom/fire_event";
import type { EntitySources } from "../../data/entity_sources";
import { fetchEntitySourcesWithCache } from "../../data/entity_sources";
import type { FloorSelector } from "../../data/selector";
import type { ConfigEntry } from "../../data/config_entries"; import type { ConfigEntry } from "../../data/config_entries";
import { getConfigEntries } from "../../data/config_entries"; import { getConfigEntries } from "../../data/config_entries";
import type { DeviceRegistryEntry } from "../../data/device/device_registry";
import { getDeviceIntegrationLookup } from "../../data/device/device_registry";
import type { EntitySources } from "../../data/entity/entity_sources";
import { fetchEntitySourcesWithCache } from "../../data/entity/entity_sources";
import type { FloorSelector } from "../../data/selector";
import { import {
filterSelectorDevices, filterSelectorDevices,
filterSelectorEntities, filterSelectorEntities,

View File

@@ -7,15 +7,15 @@ import { css, html, LitElement, nothing } from "lit";
import { customElement, property, state } from "lit/decorators"; import { customElement, property, state } from "lit/decorators";
import memoizeOne from "memoize-one"; import memoizeOne from "memoize-one";
import { ensureArray } from "../../common/array/ensure-array"; import { ensureArray } from "../../common/array/ensure-array";
import type { DeviceRegistryEntry } from "../../data/device/device_registry"; import type { DeviceRegistryEntry } from "../../data/device_registry";
import { getDeviceIntegrationLookup } from "../../data/device/device_registry"; import { getDeviceIntegrationLookup } from "../../data/device_registry";
import type { EntitySources } from "../../data/entity/entity_sources"; import type { EntitySources } from "../../data/entity_sources";
import { fetchEntitySourcesWithCache } from "../../data/entity/entity_sources"; import { fetchEntitySourcesWithCache } from "../../data/entity_sources";
import type { TargetSelector } from "../../data/selector"; import type { TargetSelector } from "../../data/selector";
import { import {
computeCreateDomains,
filterSelectorDevices, filterSelectorDevices,
filterSelectorEntities, filterSelectorEntities,
computeCreateDomains,
} from "../../data/selector"; } from "../../data/selector";
import type { HomeAssistant } from "../../types"; import type { HomeAssistant } from "../../types";
import "../ha-target-picker"; import "../ha-target-picker";

View File

@@ -22,10 +22,10 @@ interface ServiceComboBoxItem extends PickerComboBoxItem {
} }
const SEARCH_KEYS = [ const SEARCH_KEYS = [
{ name: "search_labels.name", weight: 10 }, { name: "name", weight: 10 },
{ name: "search_labels.description", weight: 8 }, { name: "description", weight: 8 },
{ name: "search_labels.domainName", weight: 6 }, { name: "domainName", weight: 6 },
{ name: "search_labels.serviceId", weight: 3 }, { name: "serviceId", weight: 3 },
]; ];
@customElement("ha-service-picker") @customElement("ha-service-picker")

View File

@@ -13,30 +13,32 @@ import memoizeOne from "memoize-one";
import { ensureArray } from "../common/array/ensure-array"; import { ensureArray } from "../common/array/ensure-array";
import { fireEvent } from "../common/dom/fire_event"; import { fireEvent } from "../common/dom/fire_event";
import { isValidEntityId } from "../common/entity/valid_entity_id"; import { isValidEntityId } from "../common/entity/valid_entity_id";
import { caseInsensitiveStringCompare } from "../common/string/compare";
import { computeRTL } from "../common/util/compute_rtl"; import { computeRTL } from "../common/util/compute_rtl";
import { import {
areaFloorComboBoxKeys, areaFloorComboBoxKeys,
getAreasAndFloors, getAreasAndFloors,
type AreaFloorValue, type AreaFloorValue,
type FloorComboBoxItem, type FloorComboBoxItem,
} from "../data/area_floor_picker"; } from "../data/area_floor";
import { getConfigEntries, type ConfigEntry } from "../data/config_entries"; import { getConfigEntries, type ConfigEntry } from "../data/config_entries";
import { labelsContext } from "../data/context"; import { labelsContext } from "../data/context";
import { import {
deviceComboBoxKeys, deviceComboBoxKeys,
getDevices, getDevices,
type DevicePickerItem, type DevicePickerItem,
} from "../data/device/device_picker"; } from "../data/device_registry";
import type { HaEntityPickerEntityFilterFunc } from "../data/entity/entity"; import type { HaEntityPickerEntityFilterFunc } from "../data/entity";
import { import {
entityComboBoxKeys, entityComboBoxKeys,
getEntities, getEntities,
type EntityComboBoxItem, type EntityComboBoxItem,
} from "../data/entity/entity_picker"; } from "../data/entity_registry";
import { domainToName } from "../data/integration"; import { domainToName } from "../data/integration";
import { getLabels, labelComboBoxKeys } from "../data/label/label_picker"; import {
import type { LabelRegistryEntry } from "../data/label/label_registry"; getLabels,
labelComboBoxKeys,
type LabelRegistryEntry,
} from "../data/label_registry";
import { import {
areaMeetsFilter, areaMeetsFilter,
deviceMeetsFilter, deviceMeetsFilter,
@@ -743,7 +745,7 @@ export class HaTargetPicker extends SubscribeMixin(LitElement) {
: undefined, : undefined,
undefined, undefined,
`entity${SEPARATOR}` `entity${SEPARATOR}`
).sort(this._sortBySortingLabel); );
if (searchTerm) { if (searchTerm) {
entityItems = this._filterGroup( entityItems = this._filterGroup(
@@ -776,7 +778,7 @@ export class HaTargetPicker extends SubscribeMixin(LitElement) {
: undefined, : undefined,
undefined, undefined,
`device${SEPARATOR}` `device${SEPARATOR}`
).sort(this._sortBySortingLabel); );
if (searchTerm) { if (searchTerm) {
deviceItems = this._filterGroup( deviceItems = this._filterGroup(
@@ -862,7 +864,7 @@ export class HaTargetPicker extends SubscribeMixin(LitElement) {
entityFilter, entityFilter,
targetValue?.label_id ? ensureArray(targetValue.label_id) : undefined, targetValue?.label_id ? ensureArray(targetValue.label_id) : undefined,
`label${SEPARATOR}` `label${SEPARATOR}`
).sort(this._sortBySortingLabel); );
if (searchTerm) { if (searchTerm) {
labels = this._filterGroup( labels = this._filterGroup(
@@ -962,7 +964,10 @@ export class HaTargetPicker extends SubscribeMixin(LitElement) {
let hasFloor = false; let hasFloor = false;
let rtl = false; let rtl = false;
let showEntityId = false; let showEntityId = false;
if (type === "area" || type === "floor") { if (type === "area" || type === "floor") {
item.id = item[type]?.[`${type}_id`];
rtl = computeRTL(this.hass); rtl = computeRTL(this.hass);
hasFloor = hasFloor =
type === "area" && !!(item as FloorComboBoxItem).area?.floor_id; type === "area" && !!(item as FloorComboBoxItem).area?.floor_id;
@@ -1068,13 +1073,6 @@ export class HaTargetPicker extends SubscribeMixin(LitElement) {
term: html`<b>${search}</b>`, term: html`<b>${search}</b>`,
}); });
private _sortBySortingLabel = (entityA, entityB) =>
caseInsensitiveStringCompare(
(entityA as PickerComboBoxItem).sorting_label!,
(entityB as PickerComboBoxItem).sorting_label!,
this.hass?.locale.language ?? navigator.language
);
static get styles(): CSSResultGroup { static get styles(): CSSResultGroup {
return css` return css`
.add-target-wrapper { .add-target-wrapper {

View File

@@ -13,7 +13,6 @@ export class HaToast extends Snackbar {
} }
.mdc-snackbar { .mdc-snackbar {
z-index: 10;
margin: 8px; margin: 8px;
right: calc(8px + var(--safe-area-inset-right)); right: calc(8px + var(--safe-area-inset-right));
bottom: calc(8px + var(--safe-area-inset-bottom)); bottom: calc(8px + var(--safe-area-inset-bottom));

View File

@@ -7,7 +7,7 @@ import { fireEvent } from "../../common/dom/fire_event";
import { computeDomain } from "../../common/entity/compute_domain"; import { computeDomain } from "../../common/entity/compute_domain";
import { computeStateName } from "../../common/entity/compute_state_name"; import { computeStateName } from "../../common/entity/compute_state_name";
import { supportsFeature } from "../../common/entity/supports-feature"; import { supportsFeature } from "../../common/entity/supports-feature";
import type { EntityRegistryDisplayEntry } from "../../data/entity/entity_registry"; import type { EntityRegistryDisplayEntry } from "../../data/entity_registry";
import { extractApiErrorMessage } from "../../data/hassio/common"; import { extractApiErrorMessage } from "../../data/hassio/common";
import { import {
type MediaPlayerEntity, type MediaPlayerEntity,

View File

@@ -17,7 +17,7 @@ import { until } from "lit/directives/until";
import { fireEvent } from "../../common/dom/fire_event"; import { fireEvent } from "../../common/dom/fire_event";
import { slugify } from "../../common/string/slugify"; import { slugify } from "../../common/string/slugify";
import { debounce } from "../../common/util/debounce"; import { debounce } from "../../common/util/debounce";
import { isUnavailableState } from "../../data/entity/entity"; import { isUnavailableState } from "../../data/entity";
import type { import type {
MediaPickedEvent, MediaPickedEvent,
MediaPlayerBrowseAction, MediaPlayerBrowseAction,

View File

@@ -1,5 +1,5 @@
import { fireEvent } from "../../../common/dom/fire_event"; import { fireEvent } from "../../../common/dom/fire_event";
import type { HaEntityPickerEntityFilterFunc } from "../../../data/entity/entity"; import type { HaEntityPickerEntityFilterFunc } from "../../../data/entity";
import type { TargetType } from "../../../data/target"; import type { TargetType } from "../../../data/target";
import type { HaDevicePickerDeviceFilterFunc } from "../../device/ha-device-picker"; import type { HaDevicePickerDeviceFilterFunc } from "../../device/ha-device-picker";

View File

@@ -1,6 +1,6 @@
import { css, html, LitElement, nothing } from "lit"; import { css, html, LitElement, nothing } from "lit";
import { customElement, property } from "lit/decorators"; import { customElement, property } from "lit/decorators";
import type { HaEntityPickerEntityFilterFunc } from "../../data/entity/entity"; import type { HaEntityPickerEntityFilterFunc } from "../../data/entity";
import type { TargetType, TargetTypeFloorless } from "../../data/target"; import type { TargetType, TargetTypeFloorless } from "../../data/target";
import type { HomeAssistant } from "../../types"; import type { HomeAssistant } from "../../types";
import type { HaDevicePickerDeviceFilterFunc } from "../device/ha-device-picker"; import type { HaDevicePickerDeviceFilterFunc } from "../device/ha-device-picker";

View File

@@ -23,11 +23,11 @@ import { computeRTL } from "../../common/util/compute_rtl";
import type { AreaRegistryEntry } from "../../data/area_registry"; 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/device_registry"; import type { DeviceRegistryEntry } from "../../data/device_registry";
import type { HaEntityPickerEntityFilterFunc } from "../../data/entity/entity"; import type { HaEntityPickerEntityFilterFunc } from "../../data/entity";
import type { FloorRegistryEntry } from "../../data/floor_registry"; import type { FloorRegistryEntry } from "../../data/floor_registry";
import { domainToName } from "../../data/integration"; import { domainToName } from "../../data/integration";
import type { LabelRegistryEntry } from "../../data/label/label_registry"; import type { LabelRegistryEntry } from "../../data/label_registry";
import { import {
areaMeetsFilter, areaMeetsFilter,
deviceMeetsFilter, deviceMeetsFilter,

View File

@@ -23,7 +23,7 @@ import { slugify } from "../../common/string/slugify";
import { getConfigEntry } from "../../data/config_entries"; import { getConfigEntry } from "../../data/config_entries";
import { labelsContext } from "../../data/context"; import { labelsContext } from "../../data/context";
import { domainToName } from "../../data/integration"; import { domainToName } from "../../data/integration";
import type { LabelRegistryEntry } from "../../data/label/label_registry"; import type { LabelRegistryEntry } from "../../data/label_registry";
import type { TargetType } from "../../data/target"; import type { TargetType } from "../../data/target";
import type { HomeAssistant } from "../../types"; import type { HomeAssistant } from "../../types";
import { brandsUrl } from "../../util/brands-url"; import { brandsUrl } from "../../util/brands-url";

View File

@@ -17,7 +17,6 @@ export class HaTraceBlueprintConfig extends LitElement {
return html` return html`
<ha-code-editor <ha-code-editor
.value=${dump(this.trace.blueprint_inputs || "").trimRight()} .value=${dump(this.trace.blueprint_inputs || "").trimRight()}
.hass=${this.hass}
read-only read-only
dir="ltr" dir="ltr"
></ha-code-editor> ></ha-code-editor>

View File

@@ -17,7 +17,6 @@ export class HaTraceConfig extends LitElement {
return html` return html`
<ha-code-editor <ha-code-editor
.value=${dump(this.trace.config).trimRight()} .value=${dump(this.trace.config).trimRight()}
.hass=${this.hass}
read-only read-only
dir="ltr" dir="ltr"
></ha-code-editor> ></ha-code-editor>

View File

@@ -1,16 +1,14 @@
import { consume } from "@lit/context";
import { dump } from "js-yaml"; import { dump } from "js-yaml";
import { consume } from "@lit/context";
import type { CSSResultGroup, TemplateResult } from "lit"; import type { CSSResultGroup, TemplateResult } from "lit";
import { css, html, LitElement, nothing } from "lit"; import { css, html, LitElement, nothing } from "lit";
import { customElement, property, state } from "lit/decorators"; import { customElement, property, state } from "lit/decorators";
import { classMap } from "lit/directives/class-map"; import { classMap } from "lit/directives/class-map";
import { formatDateTimeWithSeconds } from "../../common/datetime/format_date_time"; import { formatDateTimeWithSeconds } from "../../common/datetime/format_date_time";
import { describeCondition, describeTrigger } from "../../data/automation_i18n"; import "../ha-code-editor";
import { fullEntitiesContext, labelsContext } from "../../data/context"; import "../ha-icon-button";
import type { EntityRegistryEntry } from "../../data/entity/entity_registry"; import "./hat-logbook-note";
import type { LabelRegistryEntry } from "../../data/label/label_registry";
import type { LogbookEntry } from "../../data/logbook"; import type { LogbookEntry } from "../../data/logbook";
import { describeAction } from "../../data/script_i18n";
import type { import type {
ActionTraceStep, ActionTraceStep,
ChooseActionTraceStep, ChooseActionTraceStep,
@@ -18,12 +16,19 @@ import type {
} from "../../data/trace"; } from "../../data/trace";
import { getDataFromPath } from "../../data/trace"; import { getDataFromPath } from "../../data/trace";
import "../../panels/logbook/ha-logbook-renderer"; import "../../panels/logbook/ha-logbook-renderer";
import type { HomeAssistant } from "../../types";
import "../ha-code-editor";
import "../ha-icon-button";
import "./hat-logbook-note";
import type { NodeInfo } from "./hat-script-graph";
import { traceTabStyles } from "./trace-tab-styles"; import { traceTabStyles } from "./trace-tab-styles";
import type { HomeAssistant } from "../../types";
import type { NodeInfo } from "./hat-script-graph";
import { describeCondition, describeTrigger } from "../../data/automation_i18n";
import type { EntityRegistryEntry } from "../../data/entity_registry";
import type { LabelRegistryEntry } from "../../data/label_registry";
import type { FloorRegistryEntry } from "../../data/floor_registry";
import {
floorsContext,
fullEntitiesContext,
labelsContext,
} from "../../data/context";
import { describeAction } from "../../data/script_i18n";
const TRACE_PATH_TABS = [ const TRACE_PATH_TABS = [
"step_config", "step_config",
@@ -58,6 +63,10 @@ export class HaTracePathDetails extends LitElement {
@consume({ context: labelsContext, subscribe: true }) @consume({ context: labelsContext, subscribe: true })
_labelReg!: LabelRegistryEntry[]; _labelReg!: LabelRegistryEntry[];
@state()
@consume({ context: floorsContext, subscribe: true })
_floorReg!: Record<string, FloorRegistryEntry>;
protected render(): TemplateResult { protected render(): TemplateResult {
return html` return html`
<div class="padded-box trace-info"> <div class="padded-box trace-info">
@@ -184,6 +193,8 @@ export class HaTracePathDetails extends LitElement {
${describeAction( ${describeAction(
this.hass, this.hass,
this._entityReg, this._entityReg,
this._labelReg,
this._floorReg,
currentDetail currentDetail
)} )}
</h2>` </h2>`
@@ -267,7 +278,6 @@ export class HaTracePathDetails extends LitElement {
return config return config
? html`<ha-code-editor ? html`<ha-code-editor
.value=${dump(config).trimEnd()} .value=${dump(config).trimEnd()}
.hass=${this.hass}
read-only read-only
dir="ltr" dir="ltr"
></ha-code-editor>` ></ha-code-editor>`

View File

@@ -15,13 +15,19 @@ import { formatDateTimeWithSeconds } from "../../common/datetime/format_date_tim
import { relativeTime } from "../../common/datetime/relative_time"; import { relativeTime } from "../../common/datetime/relative_time";
import { fireEvent } from "../../common/dom/fire_event"; import { fireEvent } from "../../common/dom/fire_event";
import { toggleAttribute } from "../../common/dom/toggle_attribute"; import { toggleAttribute } from "../../common/dom/toggle_attribute";
import { fullEntitiesContext } from "../../data/context"; import {
import type { EntityRegistryEntry } from "../../data/entity/entity_registry"; floorsContext,
fullEntitiesContext,
labelsContext,
} from "../../data/context";
import type { EntityRegistryEntry } from "../../data/entity_registry";
import type { FloorRegistryEntry } from "../../data/floor_registry";
import type { LabelRegistryEntry } from "../../data/label_registry";
import type { LogbookEntry } from "../../data/logbook"; import type { LogbookEntry } from "../../data/logbook";
import type { import type {
ChooseAction, ChooseAction,
IfAction,
Option, Option,
IfAction,
ParallelAction, ParallelAction,
RepeatAction, RepeatAction,
SequenceAction, SequenceAction,
@@ -191,6 +197,8 @@ class ActionRenderer {
constructor( constructor(
private hass: HomeAssistant, private hass: HomeAssistant,
private entityReg: EntityRegistryEntry[], private entityReg: EntityRegistryEntry[],
private labelReg: LabelRegistryEntry[],
private floorReg: Record<string, FloorRegistryEntry>,
private entries: TemplateResult[], private entries: TemplateResult[],
private trace: AutomationTraceExtended, private trace: AutomationTraceExtended,
private logbookRenderer: LogbookRenderer, private logbookRenderer: LogbookRenderer,
@@ -305,7 +313,14 @@ class ActionRenderer {
this._renderEntry( this._renderEntry(
path, path,
describeAction(this.hass, this.entityReg, data, actionType), describeAction(
this.hass,
this.entityReg,
this.labelReg,
this.floorReg,
data,
actionType
),
undefined, undefined,
data.enabled === false data.enabled === false
); );
@@ -470,7 +485,13 @@ class ActionRenderer {
const name = const name =
repeatConfig.alias || repeatConfig.alias ||
describeAction(this.hass, this.entityReg, repeatConfig); describeAction(
this.hass,
this.entityReg,
this.labelReg,
this.floorReg,
repeatConfig
);
this._renderEntry(repeatPath, name, undefined, disabled); this._renderEntry(repeatPath, name, undefined, disabled);
@@ -564,7 +585,14 @@ class ActionRenderer {
this._renderEntry( this._renderEntry(
sequencePath, sequencePath,
sequenceConfig.alias || sequenceConfig.alias ||
describeAction(this.hass, this.entityReg, sequenceConfig, "sequence"), describeAction(
this.hass,
this.entityReg,
this.labelReg,
this.floorReg,
sequenceConfig,
"sequence"
),
undefined, undefined,
sequenceConfig.enabled === false sequenceConfig.enabled === false
); );
@@ -655,6 +683,14 @@ export class HaAutomationTracer extends LitElement {
@consume({ context: fullEntitiesContext, subscribe: true }) @consume({ context: fullEntitiesContext, subscribe: true })
_entityReg!: EntityRegistryEntry[]; _entityReg!: EntityRegistryEntry[];
@state()
@consume({ context: labelsContext, subscribe: true })
_labelReg!: LabelRegistryEntry[];
@state()
@consume({ context: floorsContext, subscribe: true })
_floorReg!: Record<string, FloorRegistryEntry>;
protected render() { protected render() {
if (!this.trace) { if (!this.trace) {
return nothing; return nothing;
@@ -671,6 +707,8 @@ export class HaAutomationTracer extends LitElement {
const actionRenderer = new ActionRenderer( const actionRenderer = new ActionRenderer(
this.hass, this.hass,
this._entityReg, this._entityReg,
this._labelReg,
this._floorReg,
entries, entries,
this.trace, this.trace,
logbookRenderer, logbookRenderer,

View File

@@ -13,7 +13,7 @@ import type {
import { supportsFeature } from "../common/entity/supports-feature"; import { supportsFeature } from "../common/entity/supports-feature";
import { showEnterCodeDialog } from "../dialogs/enter-code/show-enter-code-dialog"; import { showEnterCodeDialog } from "../dialogs/enter-code/show-enter-code-dialog";
import type { HomeAssistant } from "../types"; import type { HomeAssistant } from "../types";
import { getExtendedEntityRegistryEntry } from "./entity/entity_registry"; import { getExtendedEntityRegistryEntry } from "./entity_registry";
export const FORMAT_TEXT = "text"; export const FORMAT_TEXT = "text";
export const FORMAT_NUMBER = "number"; export const FORMAT_NUMBER = "number";

View File

@@ -11,9 +11,9 @@ import {
getDeviceEntityDisplayLookup, getDeviceEntityDisplayLookup,
type DeviceEntityDisplayLookup, type DeviceEntityDisplayLookup,
type DeviceRegistryEntry, type DeviceRegistryEntry,
} from "./device/device_registry"; } from "./device_registry";
import type { HaEntityPickerEntityFilterFunc } from "./entity/entity"; import type { HaEntityPickerEntityFilterFunc } from "./entity";
import type { EntityRegistryDisplayEntry } from "./entity/entity_registry"; import type { EntityRegistryDisplayEntry } from "./entity_registry";
import type { FloorRegistryEntry } from "./floor_registry"; import type { FloorRegistryEntry } from "./floor_registry";
export interface FloorComboBoxItem extends PickerComboBoxItem { export interface FloorComboBoxItem extends PickerComboBoxItem {

View File

@@ -1,9 +1,9 @@
import type { HomeAssistant } from "../types"; import type { HomeAssistant } from "../types";
import type { DeviceRegistryEntry } from "./device/device_registry"; import type { DeviceRegistryEntry } from "./device_registry";
import type { import type {
EntityRegistryDisplayEntry, EntityRegistryDisplayEntry,
EntityRegistryEntry, EntityRegistryEntry,
} from "./entity/entity_registry"; } from "./entity_registry";
import type { RegistryEntry } from "./registry"; import type { RegistryEntry } from "./registry";
export { subscribeAreaRegistry } from "./ws-area_registry"; export { subscribeAreaRegistry } from "./ws-area_registry";

View File

@@ -1,7 +1,7 @@
import type { HassEntity } from "home-assistant-js-websocket"; import type { HassEntity } from "home-assistant-js-websocket";
import { supportsFeature } from "../common/entity/supports-feature";
import type { HomeAssistant } from "../types"; import type { HomeAssistant } from "../types";
import { UNAVAILABLE } from "./entity/entity"; import { supportsFeature } from "../common/entity/supports-feature";
import { UNAVAILABLE } from "./entity";
export const enum AssistSatelliteEntityFeature { export const enum AssistSatelliteEntityFeature {
ANNOUNCE = 1, ANNOUNCE = 1,

View File

@@ -12,10 +12,7 @@ import type { Context, HomeAssistant } from "../types";
import type { BlueprintInput } from "./blueprint"; import type { BlueprintInput } from "./blueprint";
import type { ConditionDescription } from "./condition"; import type { ConditionDescription } from "./condition";
import { CONDITION_BUILDING_BLOCKS } from "./condition"; import { CONDITION_BUILDING_BLOCKS } from "./condition";
import type { import type { DeviceCondition, DeviceTrigger } from "./device_automation";
DeviceCondition,
DeviceTrigger,
} from "./device/device_automation";
import type { Action, Field, MODES } from "./script"; import type { Action, Field, MODES } from "./script";
import { migrateAutomationAction } from "./script"; import { migrateAutomationAction } from "./script";
import type { TriggerDescription } from "./trigger"; import type { TriggerDescription } from "./trigger";

View File

@@ -26,15 +26,12 @@ import type {
Trigger, Trigger,
} from "./automation"; } from "./automation";
import { getConditionDomain, getConditionObjectId } from "./condition"; import { getConditionDomain, getConditionObjectId } from "./condition";
import type { import type { DeviceCondition, DeviceTrigger } from "./device_automation";
DeviceCondition,
DeviceTrigger,
} from "./device/device_automation";
import { import {
localizeDeviceAutomationCondition, localizeDeviceAutomationCondition,
localizeDeviceAutomationTrigger, localizeDeviceAutomationTrigger,
} from "./device/device_automation"; } from "./device_automation";
import type { EntityRegistryEntry } from "./entity/entity_registry"; import type { EntityRegistryEntry } from "./entity_registry";
import type { FrontendLocaleData } from "./translation"; import type { FrontendLocaleData } from "./translation";
import { getTriggerDomain, getTriggerObjectId, isTriggerList } from "./trigger"; import { getTriggerDomain, getTriggerObjectId, isTriggerList } from "./trigger";

View File

@@ -2,7 +2,7 @@ import { getColorByIndex } from "../common/color/colors";
import { computeDomain } from "../common/entity/compute_domain"; import { computeDomain } from "../common/entity/compute_domain";
import { computeStateName } from "../common/entity/compute_state_name"; import { computeStateName } from "../common/entity/compute_state_name";
import type { HomeAssistant } from "../types"; import type { HomeAssistant } from "../types";
import { isUnavailableState } from "./entity/entity"; import { isUnavailableState } from "./entity";
export interface Calendar { export interface Calendar {
entity_id: string; entity_id: string;

View File

@@ -1,8 +1,8 @@
import { createContext } from "@lit/context"; import { createContext } from "@lit/context";
import type { HassConfig } from "home-assistant-js-websocket"; import type { HassConfig } from "home-assistant-js-websocket";
import type { HomeAssistant } from "../types"; import type { HomeAssistant } from "../types";
import type { EntityRegistryEntry } from "./entity/entity_registry"; import type { EntityRegistryEntry } from "./entity_registry";
import type { LabelRegistryEntry } from "./label/label_registry"; import type { LabelRegistryEntry } from "./label_registry";
export const connectionContext = export const connectionContext =
createContext<HomeAssistant["connection"]>("connection"); createContext<HomeAssistant["connection"]>("connection");

View File

@@ -5,7 +5,7 @@ import type {
import { stateActive } from "../common/entity/state_active"; import { stateActive } from "../common/entity/state_active";
import { supportsFeature } from "../common/entity/supports-feature"; import { supportsFeature } from "../common/entity/supports-feature";
import type { HomeAssistant } from "../types"; import type { HomeAssistant } from "../types";
import { UNAVAILABLE } from "./entity/entity"; import { UNAVAILABLE } from "./entity";
export const enum CoverEntityFeature { export const enum CoverEntityFeature {
OPEN = 1, OPEN = 1,

View File

@@ -1,182 +0,0 @@
import { computeAreaName } from "../../common/entity/compute_area_name";
import { computeDeviceNameDisplay } from "../../common/entity/compute_device_name";
import { computeDomain } from "../../common/entity/compute_domain";
import { getDeviceContext } from "../../common/entity/context/get_device_context";
import type { HaDevicePickerDeviceFilterFunc } from "../../components/device/ha-device-picker";
import type { PickerComboBoxItem } from "../../components/ha-picker-combo-box";
import type { FuseWeightedKey } from "../../resources/fuseMultiTerm";
import type { HomeAssistant } from "../../types";
import type { ConfigEntry } from "../config_entries";
import type { HaEntityPickerEntityFilterFunc } from "../entity/entity";
import { domainToName } from "../integration";
import {
getDeviceEntityDisplayLookup,
type DeviceEntityDisplayLookup,
} from "./device_registry";
export interface DevicePickerItem extends PickerComboBoxItem {
domain?: string;
domain_name?: string;
}
export const deviceComboBoxKeys: FuseWeightedKey[] = [
{
name: "search_labels.deviceName",
weight: 10,
},
{
name: "search_labels.areaName",
weight: 8,
},
{
name: "search_labels.domainName",
weight: 4,
},
{
name: "search_labels.domain",
weight: 4,
},
];
export const getDevices = (
hass: HomeAssistant,
configEntryLookup: Record<string, ConfigEntry>,
includeDomains?: string[],
excludeDomains?: string[],
includeDeviceClasses?: string[],
deviceFilter?: HaDevicePickerDeviceFilterFunc,
entityFilter?: HaEntityPickerEntityFilterFunc,
excludeDevices?: string[],
value?: string,
idPrefix = ""
): DevicePickerItem[] => {
const devices = Object.values(hass.devices);
const entities = Object.values(hass.entities);
let deviceEntityLookup: DeviceEntityDisplayLookup = {};
if (
includeDomains ||
excludeDomains ||
includeDeviceClasses ||
entityFilter
) {
deviceEntityLookup = getDeviceEntityDisplayLookup(entities);
}
let inputDevices = devices.filter(
(device) => device.id === value || !device.disabled_by
);
if (includeDomains) {
inputDevices = inputDevices.filter((device) => {
const devEntities = deviceEntityLookup[device.id];
if (!devEntities || !devEntities.length) {
return false;
}
return deviceEntityLookup[device.id].some((entity) =>
includeDomains.includes(computeDomain(entity.entity_id))
);
});
}
if (excludeDomains) {
inputDevices = inputDevices.filter((device) => {
const devEntities = deviceEntityLookup[device.id];
if (!devEntities || !devEntities.length) {
return true;
}
return entities.every(
(entity) => !excludeDomains.includes(computeDomain(entity.entity_id))
);
});
}
if (excludeDevices) {
inputDevices = inputDevices.filter(
(device) => !excludeDevices!.includes(device.id)
);
}
if (includeDeviceClasses) {
inputDevices = inputDevices.filter((device) => {
const devEntities = deviceEntityLookup[device.id];
if (!devEntities || !devEntities.length) {
return false;
}
return deviceEntityLookup[device.id].some((entity) => {
const stateObj = hass.states[entity.entity_id];
if (!stateObj) {
return false;
}
return (
stateObj.attributes.device_class &&
includeDeviceClasses.includes(stateObj.attributes.device_class)
);
});
});
}
if (entityFilter) {
inputDevices = inputDevices.filter((device) => {
const devEntities = deviceEntityLookup[device.id];
if (!devEntities || !devEntities.length) {
return false;
}
return devEntities.some((entity) => {
const stateObj = hass.states[entity.entity_id];
if (!stateObj) {
return false;
}
return entityFilter(stateObj);
});
});
}
if (deviceFilter) {
inputDevices = inputDevices.filter(
(device) =>
// We always want to include the device of the current value
device.id === value || deviceFilter!(device)
);
}
const outputDevices = inputDevices.map<DevicePickerItem>((device) => {
const deviceName = computeDeviceNameDisplay(
device,
hass,
deviceEntityLookup[device.id]
);
const { area } = getDeviceContext(device, hass);
const areaName = area ? computeAreaName(area) : undefined;
const configEntry = device.primary_config_entry
? configEntryLookup?.[device.primary_config_entry]
: undefined;
const domain = configEntry?.domain;
const domainName = domain ? domainToName(hass.localize, domain) : undefined;
const primary =
deviceName || hass.localize("ui.components.device-picker.unnamed_device");
return {
id: `${idPrefix}${device.id}`,
label: "",
primary,
secondary: areaName,
domain: configEntry?.domain,
domain_name: domainName,
search_labels: {
deviceName,
areaName: areaName || null,
domain: domain || null,
domainName: domainName || null,
},
sorting_label: [primary, areaName, domainName].filter(Boolean).join("_"),
};
});
return outputDevices;
};

View File

@@ -1,169 +0,0 @@
import { computeStateName } from "../../common/entity/compute_state_name";
import { caseInsensitiveStringCompare } from "../../common/string/compare";
import type { HomeAssistant } from "../../types";
import type { ConfigEntry } from "../config_entries";
import type {
EntityRegistryDisplayEntry,
EntityRegistryEntry,
} from "../entity/entity_registry";
import type { EntitySources } from "../entity/entity_sources";
import type { RegistryEntry } from "../registry";
export {
fetchDeviceRegistry,
subscribeDeviceRegistry,
} from "../ws-device_registry";
export interface DeviceRegistryEntry extends RegistryEntry {
id: string;
config_entries: string[];
config_entries_subentries: Record<string, (string | null)[]>;
connections: [string, string][];
identifiers: [string, string][];
manufacturer: string | null;
model: string | null;
model_id: string | null;
name: string | null;
labels: string[];
sw_version: string | null;
hw_version: string | null;
serial_number: string | null;
via_device_id: string | null;
area_id: string | null;
name_by_user: string | null;
entry_type: "service" | null;
disabled_by: "user" | "integration" | "config_entry" | null;
configuration_url: string | null;
primary_config_entry: string | null;
}
export type DeviceEntityDisplayLookup = Record<
string,
EntityRegistryDisplayEntry[]
>;
export type DeviceEntityLookup<
T extends EntityRegistryEntry | EntityRegistryDisplayEntry =
| EntityRegistryEntry
| EntityRegistryDisplayEntry,
> = Record<string, T[]>;
export interface DeviceRegistryEntryMutableParams {
area_id?: string | null;
name_by_user?: string | null;
disabled_by?: string | null;
labels?: string[];
}
export const fallbackDeviceName = (
hass: HomeAssistant,
entities: EntityRegistryEntry[] | EntityRegistryDisplayEntry[] | string[]
) => {
for (const entity of entities || []) {
const entityId = typeof entity === "string" ? entity : entity.entity_id;
const stateObj = hass.states[entityId];
if (stateObj) {
return computeStateName(stateObj);
}
}
return undefined;
};
export const devicesInArea = (devices: DeviceRegistryEntry[], areaId: string) =>
devices.filter((device) => device.area_id === areaId);
export const updateDeviceRegistryEntry = (
hass: HomeAssistant,
deviceId: string,
updates: Partial<DeviceRegistryEntryMutableParams>
) =>
hass.callWS<DeviceRegistryEntry>({
type: "config/device_registry/update",
device_id: deviceId,
...updates,
});
export const removeConfigEntryFromDevice = (
hass: HomeAssistant,
deviceId: string,
configEntryId: string
) =>
hass.callWS<DeviceRegistryEntry>({
type: "config/device_registry/remove_config_entry",
device_id: deviceId,
config_entry_id: configEntryId,
});
export const sortDeviceRegistryByName = (
entries: DeviceRegistryEntry[],
language: string
) =>
entries.sort((entry1, entry2) =>
caseInsensitiveStringCompare(entry1.name || "", entry2.name || "", language)
);
export const getDeviceEntityLookup = (
entities: (EntityRegistryEntry | EntityRegistryDisplayEntry)[]
): DeviceEntityLookup => {
const deviceEntityLookup: DeviceEntityLookup = {};
for (const entity of entities) {
if (!entity.device_id) {
continue;
}
if (!(entity.device_id in deviceEntityLookup)) {
deviceEntityLookup[entity.device_id] = [];
}
deviceEntityLookup[entity.device_id].push(entity);
}
return deviceEntityLookup;
};
export const getDeviceEntityDisplayLookup = (
entities: EntityRegistryDisplayEntry[]
): DeviceEntityDisplayLookup => {
const deviceEntityLookup: DeviceEntityDisplayLookup = {};
for (const entity of entities) {
if (!entity.device_id) {
continue;
}
if (!(entity.device_id in deviceEntityLookup)) {
deviceEntityLookup[entity.device_id] = [];
}
deviceEntityLookup[entity.device_id].push(entity);
}
return deviceEntityLookup;
};
export const getDeviceIntegrationLookup = (
entitySources: EntitySources,
entities: EntityRegistryDisplayEntry[] | EntityRegistryEntry[],
devices?: DeviceRegistryEntry[],
configEntries?: ConfigEntry[]
): Record<string, Set<string>> => {
const deviceIntegrations: Record<string, Set<string>> = {};
for (const entity of entities) {
const source = entitySources[entity.entity_id];
if (!source?.domain || entity.device_id === null) {
continue;
}
deviceIntegrations[entity.device_id!] =
deviceIntegrations[entity.device_id!] || new Set<string>();
deviceIntegrations[entity.device_id!].add(source.domain);
}
// Lookup devices that have no entities
if (devices && configEntries) {
for (const device of devices) {
for (const config_entry_id of device.config_entries) {
const entry = configEntries.find((e) => e.entry_id === config_entry_id);
if (entry?.domain) {
deviceIntegrations[device.id] =
deviceIntegrations[device.id] || new Set<string>();
deviceIntegrations[device.id].add(entry.domain);
}
}
}
}
return deviceIntegrations;
};

View File

@@ -1,14 +1,14 @@
import { computeStateName } from "../../common/entity/compute_state_name"; import { computeStateName } from "../common/entity/compute_state_name";
import type { HaFormSchema } from "../../components/ha-form/types"; import type { HaFormSchema } from "../components/ha-form/types";
import type { HomeAssistant } from "../../types"; import type { HomeAssistant } from "../types";
import type { BaseTrigger } from "../automation"; import type { BaseTrigger } from "./automation";
import { migrateAutomationTrigger } from "../automation"; import { migrateAutomationTrigger } from "./automation";
import type { EntityRegistryEntry } from "../entity/entity_registry"; import type { EntityRegistryEntry } from "./entity_registry";
import { import {
computeEntityRegistryName, computeEntityRegistryName,
entityRegistryByEntityId, entityRegistryByEntityId,
entityRegistryById, entityRegistryById,
} from "../entity/entity_registry"; } from "./entity_registry";
export interface DeviceAutomation { export interface DeviceAutomation {
alias?: string; alias?: string;

345
src/data/device_registry.ts Normal file
View File

@@ -0,0 +1,345 @@
import { computeAreaName } from "../common/entity/compute_area_name";
import { computeDeviceNameDisplay } from "../common/entity/compute_device_name";
import { computeDomain } from "../common/entity/compute_domain";
import { computeStateName } from "../common/entity/compute_state_name";
import { getDeviceContext } from "../common/entity/context/get_device_context";
import { caseInsensitiveStringCompare } from "../common/string/compare";
import type { HaDevicePickerDeviceFilterFunc } from "../components/device/ha-device-picker";
import type { PickerComboBoxItem } from "../components/ha-picker-combo-box";
import type { FuseWeightedKey } from "../resources/fuseMultiTerm";
import type { HomeAssistant } from "../types";
import type { ConfigEntry } from "./config_entries";
import type { HaEntityPickerEntityFilterFunc } from "./entity";
import type {
EntityRegistryDisplayEntry,
EntityRegistryEntry,
} from "./entity_registry";
import type { EntitySources } from "./entity_sources";
import { domainToName } from "./integration";
import type { RegistryEntry } from "./registry";
export {
fetchDeviceRegistry,
subscribeDeviceRegistry,
} from "./ws-device_registry";
export interface DeviceRegistryEntry extends RegistryEntry {
id: string;
config_entries: string[];
config_entries_subentries: Record<string, (string | null)[]>;
connections: [string, string][];
identifiers: [string, string][];
manufacturer: string | null;
model: string | null;
model_id: string | null;
name: string | null;
labels: string[];
sw_version: string | null;
hw_version: string | null;
serial_number: string | null;
via_device_id: string | null;
area_id: string | null;
name_by_user: string | null;
entry_type: "service" | null;
disabled_by: "user" | "integration" | "config_entry" | null;
configuration_url: string | null;
primary_config_entry: string | null;
}
export type DeviceEntityDisplayLookup = Record<
string,
EntityRegistryDisplayEntry[]
>;
export type DeviceEntityLookup<
T extends EntityRegistryEntry | EntityRegistryDisplayEntry =
| EntityRegistryEntry
| EntityRegistryDisplayEntry,
> = Record<string, T[]>;
export interface DeviceRegistryEntryMutableParams {
area_id?: string | null;
name_by_user?: string | null;
disabled_by?: string | null;
labels?: string[];
}
export const fallbackDeviceName = (
hass: HomeAssistant,
entities: EntityRegistryEntry[] | EntityRegistryDisplayEntry[] | string[]
) => {
for (const entity of entities || []) {
const entityId = typeof entity === "string" ? entity : entity.entity_id;
const stateObj = hass.states[entityId];
if (stateObj) {
return computeStateName(stateObj);
}
}
return undefined;
};
export const devicesInArea = (devices: DeviceRegistryEntry[], areaId: string) =>
devices.filter((device) => device.area_id === areaId);
export const updateDeviceRegistryEntry = (
hass: HomeAssistant,
deviceId: string,
updates: Partial<DeviceRegistryEntryMutableParams>
) =>
hass.callWS<DeviceRegistryEntry>({
type: "config/device_registry/update",
device_id: deviceId,
...updates,
});
export const removeConfigEntryFromDevice = (
hass: HomeAssistant,
deviceId: string,
configEntryId: string
) =>
hass.callWS<DeviceRegistryEntry>({
type: "config/device_registry/remove_config_entry",
device_id: deviceId,
config_entry_id: configEntryId,
});
export const sortDeviceRegistryByName = (
entries: DeviceRegistryEntry[],
language: string
) =>
entries.sort((entry1, entry2) =>
caseInsensitiveStringCompare(entry1.name || "", entry2.name || "", language)
);
export const getDeviceEntityLookup = (
entities: (EntityRegistryEntry | EntityRegistryDisplayEntry)[]
): DeviceEntityLookup => {
const deviceEntityLookup: DeviceEntityLookup = {};
for (const entity of entities) {
if (!entity.device_id) {
continue;
}
if (!(entity.device_id in deviceEntityLookup)) {
deviceEntityLookup[entity.device_id] = [];
}
deviceEntityLookup[entity.device_id].push(entity);
}
return deviceEntityLookup;
};
export const getDeviceEntityDisplayLookup = (
entities: EntityRegistryDisplayEntry[]
): DeviceEntityDisplayLookup => {
const deviceEntityLookup: DeviceEntityDisplayLookup = {};
for (const entity of entities) {
if (!entity.device_id) {
continue;
}
if (!(entity.device_id in deviceEntityLookup)) {
deviceEntityLookup[entity.device_id] = [];
}
deviceEntityLookup[entity.device_id].push(entity);
}
return deviceEntityLookup;
};
export const getDeviceIntegrationLookup = (
entitySources: EntitySources,
entities: EntityRegistryDisplayEntry[] | EntityRegistryEntry[],
devices?: DeviceRegistryEntry[],
configEntries?: ConfigEntry[]
): Record<string, Set<string>> => {
const deviceIntegrations: Record<string, Set<string>> = {};
for (const entity of entities) {
const source = entitySources[entity.entity_id];
if (!source?.domain || entity.device_id === null) {
continue;
}
deviceIntegrations[entity.device_id!] =
deviceIntegrations[entity.device_id!] || new Set<string>();
deviceIntegrations[entity.device_id!].add(source.domain);
}
// Lookup devices that have no entities
if (devices && configEntries) {
for (const device of devices) {
for (const config_entry_id of device.config_entries) {
const entry = configEntries.find((e) => e.entry_id === config_entry_id);
if (entry?.domain) {
deviceIntegrations[device.id] =
deviceIntegrations[device.id] || new Set<string>();
deviceIntegrations[device.id].add(entry.domain);
}
}
}
}
return deviceIntegrations;
};
export interface DevicePickerItem extends PickerComboBoxItem {
domain?: string;
domain_name?: string;
}
export const deviceComboBoxKeys: FuseWeightedKey[] = [
{
name: "search_labels.deviceName",
weight: 10,
},
{
name: "search_labels.areaName",
weight: 8,
},
{
name: "search_labels.domainName",
weight: 4,
},
{
name: "search_labels.domain",
weight: 4,
},
];
export const getDevices = (
hass: HomeAssistant,
configEntryLookup: Record<string, ConfigEntry>,
includeDomains?: string[],
excludeDomains?: string[],
includeDeviceClasses?: string[],
deviceFilter?: HaDevicePickerDeviceFilterFunc,
entityFilter?: HaEntityPickerEntityFilterFunc,
excludeDevices?: string[],
value?: string,
idPrefix = ""
): DevicePickerItem[] => {
const devices = Object.values(hass.devices);
const entities = Object.values(hass.entities);
let deviceEntityLookup: DeviceEntityDisplayLookup = {};
if (
includeDomains ||
excludeDomains ||
includeDeviceClasses ||
entityFilter
) {
deviceEntityLookup = getDeviceEntityDisplayLookup(entities);
}
let inputDevices = devices.filter(
(device) => device.id === value || !device.disabled_by
);
if (includeDomains) {
inputDevices = inputDevices.filter((device) => {
const devEntities = deviceEntityLookup[device.id];
if (!devEntities || !devEntities.length) {
return false;
}
return deviceEntityLookup[device.id].some((entity) =>
includeDomains.includes(computeDomain(entity.entity_id))
);
});
}
if (excludeDomains) {
inputDevices = inputDevices.filter((device) => {
const devEntities = deviceEntityLookup[device.id];
if (!devEntities || !devEntities.length) {
return true;
}
return entities.every(
(entity) => !excludeDomains.includes(computeDomain(entity.entity_id))
);
});
}
if (excludeDevices) {
inputDevices = inputDevices.filter(
(device) => !excludeDevices!.includes(device.id)
);
}
if (includeDeviceClasses) {
inputDevices = inputDevices.filter((device) => {
const devEntities = deviceEntityLookup[device.id];
if (!devEntities || !devEntities.length) {
return false;
}
return deviceEntityLookup[device.id].some((entity) => {
const stateObj = hass.states[entity.entity_id];
if (!stateObj) {
return false;
}
return (
stateObj.attributes.device_class &&
includeDeviceClasses.includes(stateObj.attributes.device_class)
);
});
});
}
if (entityFilter) {
inputDevices = inputDevices.filter((device) => {
const devEntities = deviceEntityLookup[device.id];
if (!devEntities || !devEntities.length) {
return false;
}
return devEntities.some((entity) => {
const stateObj = hass.states[entity.entity_id];
if (!stateObj) {
return false;
}
return entityFilter(stateObj);
});
});
}
if (deviceFilter) {
inputDevices = inputDevices.filter(
(device) =>
// We always want to include the device of the current value
device.id === value || deviceFilter!(device)
);
}
const outputDevices = inputDevices.map<DevicePickerItem>((device) => {
const deviceName = computeDeviceNameDisplay(
device,
hass,
deviceEntityLookup[device.id]
);
const { area } = getDeviceContext(device, hass);
const areaName = area ? computeAreaName(area) : undefined;
const configEntry = device.primary_config_entry
? configEntryLookup?.[device.primary_config_entry]
: undefined;
const domain = configEntry?.domain;
const domainName = domain ? domainToName(hass.localize, domain) : undefined;
return {
id: `${idPrefix}${device.id}`,
label: "",
primary:
deviceName ||
hass.localize("ui.components.device-picker.unnamed_device"),
secondary: areaName,
domain: configEntry?.domain,
domain_name: domainName,
search_labels: {
deviceName,
areaName: areaName || null,
domain: domain || null,
domainName: domainName || null,
},
sorting_label: deviceName || "zzz",
};
});
return outputDevices;
};

View File

@@ -1,5 +1,5 @@
import type { HassEntity } from "home-assistant-js-websocket"; import type { HassEntity } from "home-assistant-js-websocket";
import { arrayLiteralIncludes } from "../../common/array/literal-includes"; import { arrayLiteralIncludes } from "../common/array/literal-includes";
export const UNAVAILABLE = "unavailable"; export const UNAVAILABLE = "unavailable";
export const UNKNOWN = "unknown"; export const UNKNOWN = "unknown";

View File

@@ -1,155 +0,0 @@
import type { HassEntity } from "home-assistant-js-websocket";
import { computeDomain } from "../../common/entity/compute_domain";
import { computeEntityNameList } from "../../common/entity/compute_entity_name_display";
import { computeStateName } from "../../common/entity/compute_state_name";
import { computeRTL } from "../../common/util/compute_rtl";
import type { PickerComboBoxItem } from "../../components/ha-picker-combo-box";
import type { FuseWeightedKey } from "../../resources/fuseMultiTerm";
import type { HomeAssistant } from "../../types";
import { domainToName } from "../integration";
import type { HaEntityPickerEntityFilterFunc } from "./entity";
export interface EntityComboBoxItem extends PickerComboBoxItem {
domain_name?: string;
stateObj?: HassEntity;
}
export const entityComboBoxKeys: FuseWeightedKey[] = [
{
name: "search_labels.entityName",
weight: 10,
},
{
name: "search_labels.friendlyName",
weight: 8,
},
{
name: "search_labels.deviceName",
weight: 7,
},
{
name: "search_labels.areaName",
weight: 6,
},
{
name: "search_labels.domainName",
weight: 6,
},
{
name: "search_labels.entityId",
weight: 3,
},
];
export const getEntities = (
hass: HomeAssistant,
includeDomains?: string[],
excludeDomains?: string[],
entityFilter?: HaEntityPickerEntityFilterFunc,
includeDeviceClasses?: string[],
includeUnitOfMeasurement?: string[],
includeEntities?: string[],
excludeEntities?: string[],
value?: string,
idPrefix = ""
): EntityComboBoxItem[] => {
let items: EntityComboBoxItem[] = [];
let entityIds = Object.keys(hass.states);
if (includeEntities) {
entityIds = entityIds.filter((entityId) =>
includeEntities.includes(entityId)
);
}
if (excludeEntities) {
entityIds = entityIds.filter(
(entityId) => !excludeEntities.includes(entityId)
);
}
if (includeDomains) {
entityIds = entityIds.filter((eid) =>
includeDomains.includes(computeDomain(eid))
);
}
if (excludeDomains) {
entityIds = entityIds.filter(
(eid) => !excludeDomains.includes(computeDomain(eid))
);
}
items = entityIds.map<EntityComboBoxItem>((entityId) => {
const stateObj = hass.states[entityId];
const friendlyName = computeStateName(stateObj); // Keep this for search
const [entityName, deviceName, areaName] = computeEntityNameList(
stateObj,
[{ type: "entity" }, { type: "device" }, { type: "area" }],
hass.entities,
hass.devices,
hass.areas,
hass.floors
);
const domainName = domainToName(hass.localize, computeDomain(entityId));
const isRTL = computeRTL(hass);
const primary = entityName || deviceName || entityId;
const secondary = [areaName, entityName ? deviceName : undefined]
.filter(Boolean)
.join(isRTL ? " ◂ " : " ▸ ");
return {
id: `${idPrefix}${entityId}`,
primary: primary,
secondary: secondary,
domain_name: domainName,
sorting_label: [primary, secondary].filter(Boolean).join("_"),
search_labels: {
entityName: entityName || null,
deviceName: deviceName || null,
areaName: areaName || null,
domainName: domainName || null,
friendlyName: friendlyName || null,
entityId: entityId,
},
stateObj: stateObj,
};
});
if (includeDeviceClasses) {
items = items.filter(
(item) =>
// We always want to include the entity of the current value
item.id === value ||
(item.stateObj?.attributes.device_class &&
includeDeviceClasses.includes(item.stateObj.attributes.device_class))
);
}
if (includeUnitOfMeasurement) {
items = items.filter(
(item) =>
// We always want to include the entity of the current value
item.id === value ||
(item.stateObj?.attributes.unit_of_measurement &&
includeUnitOfMeasurement.includes(
item.stateObj.attributes.unit_of_measurement
))
);
}
if (entityFilter) {
items = items.filter(
(item) =>
// We always want to include the entity of the current value
item.id === value || (item.stateObj && entityFilter!(item.stateObj))
);
}
return items;
};

View File

@@ -1,5 +1,5 @@
import { formatDurationDigital } from "../../common/datetime/format_duration"; import { formatDurationDigital } from "../common/datetime/format_duration";
import type { FrontendLocaleData } from "../translation"; import type { FrontendLocaleData } from "./translation";
// These attributes are hidden from the more-info window for all entities. // These attributes are hidden from the more-info window for all entities.
export const STATE_ATTRIBUTES = [ export const STATE_ATTRIBUTES = [

View File

@@ -1,14 +1,20 @@
import type { Connection } from "home-assistant-js-websocket"; import type { Connection, HassEntity } from "home-assistant-js-websocket";
import { createCollection } from "home-assistant-js-websocket"; import { createCollection } from "home-assistant-js-websocket";
import type { Store } from "home-assistant-js-websocket/dist/store"; import type { Store } from "home-assistant-js-websocket/dist/store";
import memoizeOne from "memoize-one"; import memoizeOne from "memoize-one";
import { computeDomain } from "../../common/entity/compute_domain"; import { computeDomain } from "../common/entity/compute_domain";
import { computeStateName } from "../../common/entity/compute_state_name"; import { computeEntityNameList } from "../common/entity/compute_entity_name_display";
import { caseInsensitiveStringCompare } from "../../common/string/compare"; import { computeStateName } from "../common/entity/compute_state_name";
import { debounce } from "../../common/util/debounce"; import { caseInsensitiveStringCompare } from "../common/string/compare";
import type { HomeAssistant } from "../../types"; import { computeRTL } from "../common/util/compute_rtl";
import type { LightColor } from "../light"; import { debounce } from "../common/util/debounce";
import type { RegistryEntry } from "../registry"; import type { PickerComboBoxItem } from "../components/ha-picker-combo-box";
import type { FuseWeightedKey } from "../resources/fuseMultiTerm";
import type { HomeAssistant } from "../types";
import type { HaEntityPickerEntityFilterFunc } from "./entity";
import { domainToName } from "./integration";
import type { LightColor } from "./light";
import type { RegistryEntry } from "./registry";
type EntityCategory = "config" | "diagnostic"; type EntityCategory = "config" | "diagnostic";
@@ -324,3 +330,148 @@ export const getAutomaticEntityIds = (
type: "config/entity_registry/get_automatic_entity_ids", type: "config/entity_registry/get_automatic_entity_ids",
entity_ids, entity_ids,
}); });
export interface EntityComboBoxItem extends PickerComboBoxItem {
domain_name?: string;
stateObj?: HassEntity;
}
export const entityComboBoxKeys: FuseWeightedKey[] = [
{
name: "search_labels.entityName",
weight: 10,
},
{
name: "search_labels.friendlyName",
weight: 8,
},
{
name: "search_labels.deviceName",
weight: 7,
},
{
name: "search_labels.areaName",
weight: 6,
},
{
name: "search_labels.domainName",
weight: 6,
},
{
name: "search_labels.entityId",
weight: 3,
},
];
export const getEntities = (
hass: HomeAssistant,
includeDomains?: string[],
excludeDomains?: string[],
entityFilter?: HaEntityPickerEntityFilterFunc,
includeDeviceClasses?: string[],
includeUnitOfMeasurement?: string[],
includeEntities?: string[],
excludeEntities?: string[],
value?: string,
idPrefix = ""
): EntityComboBoxItem[] => {
let items: EntityComboBoxItem[] = [];
let entityIds = Object.keys(hass.states);
if (includeEntities) {
entityIds = entityIds.filter((entityId) =>
includeEntities.includes(entityId)
);
}
if (excludeEntities) {
entityIds = entityIds.filter(
(entityId) => !excludeEntities.includes(entityId)
);
}
if (includeDomains) {
entityIds = entityIds.filter((eid) =>
includeDomains.includes(computeDomain(eid))
);
}
if (excludeDomains) {
entityIds = entityIds.filter(
(eid) => !excludeDomains.includes(computeDomain(eid))
);
}
items = entityIds.map<EntityComboBoxItem>((entityId) => {
const stateObj = hass.states[entityId];
const friendlyName = computeStateName(stateObj); // Keep this for search
const [entityName, deviceName, areaName] = computeEntityNameList(
stateObj,
[{ type: "entity" }, { type: "device" }, { type: "area" }],
hass.entities,
hass.devices,
hass.areas,
hass.floors
);
const domainName = domainToName(hass.localize, computeDomain(entityId));
const isRTL = computeRTL(hass);
const primary = entityName || deviceName || entityId;
const secondary = [areaName, entityName ? deviceName : undefined]
.filter(Boolean)
.join(isRTL ? " ◂ " : " ▸ ");
return {
id: `${idPrefix}${entityId}`,
primary: primary,
secondary: secondary,
domain_name: domainName,
sorting_label: [deviceName, entityName].filter(Boolean).join("_"),
search_labels: {
entityName: entityName || null,
deviceName: deviceName || null,
areaName: areaName || null,
domainName: domainName || null,
friendlyName: friendlyName || null,
entityId: entityId,
},
stateObj: stateObj,
};
});
if (includeDeviceClasses) {
items = items.filter(
(item) =>
// We always want to include the entity of the current value
item.id === value ||
(item.stateObj?.attributes.device_class &&
includeDeviceClasses.includes(item.stateObj.attributes.device_class))
);
}
if (includeUnitOfMeasurement) {
items = items.filter(
(item) =>
// We always want to include the entity of the current value
item.id === value ||
(item.stateObj?.attributes.unit_of_measurement &&
includeUnitOfMeasurement.includes(
item.stateObj.attributes.unit_of_measurement
))
);
}
if (entityFilter) {
items = items.filter(
(item) =>
// We always want to include the entity of the current value
item.id === value || (item.stateObj && entityFilter!(item.stateObj))
);
}
return items;
};

View File

@@ -1,5 +1,5 @@
import { timeCachePromiseFunc } from "../../common/util/time-cache-function-promise"; import { timeCachePromiseFunc } from "../common/util/time-cache-function-promise";
import type { HomeAssistant } from "../../types"; import type { HomeAssistant } from "../types";
interface EntitySource { interface EntitySource {
domain: string; domain: string;

View File

@@ -56,11 +56,11 @@ import type { HomeAssistant } from "../types";
import type { import type {
EntityRegistryDisplayEntry, EntityRegistryDisplayEntry,
EntityRegistryEntry, EntityRegistryEntry,
} from "./entity/entity_registry"; } from "./entity_registry";
import { mdiHomeAssistant } from "../resources/home-assistant-logo-svg"; import { mdiHomeAssistant } from "../resources/home-assistant-logo-svg";
import { getConditionDomain, getConditionObjectId } from "./condition";
import { getTriggerDomain, getTriggerObjectId } from "./trigger"; import { getTriggerDomain, getTriggerObjectId } from "./trigger";
import { getConditionDomain, getConditionObjectId } from "./condition";
/** Icon to use when no icon specified for service. */ /** Icon to use when no icon specified for service. */
export const DEFAULT_SERVICE_ICON = mdiRoomService; export const DEFAULT_SERVICE_ICON = mdiRoomService;

View File

@@ -1,90 +0,0 @@
import type { Connection } from "home-assistant-js-websocket";
import { createCollection } from "home-assistant-js-websocket";
import type { Store } from "home-assistant-js-websocket/dist/store";
import { stringCompare } from "../../common/string/compare";
import { debounce } from "../../common/util/debounce";
import type { HomeAssistant } from "../../types";
import type { RegistryEntry } from "../registry";
export interface LabelRegistryEntry extends RegistryEntry {
label_id: string;
name: string;
icon: string | null;
color: string | null;
description: string | null;
}
export interface LabelRegistryEntryMutableParams {
name: string;
icon?: string | null;
color?: string | null;
description?: string | null;
}
export const fetchLabelRegistry = (conn: Connection) =>
conn
.sendMessagePromise({
type: "config/label_registry/list",
})
.then((labels) =>
(labels as LabelRegistryEntry[]).sort((ent1, ent2) =>
stringCompare(ent1.name, ent2.name)
)
);
export const subscribeLabelRegistryUpdates = (
conn: Connection,
store: Store<LabelRegistryEntry[]>
) =>
conn.subscribeEvents(
debounce(
() =>
fetchLabelRegistry(conn).then((labels: LabelRegistryEntry[]) =>
store.setState(labels, true)
),
500,
true
),
"label_registry_updated"
);
export const subscribeLabelRegistry = (
conn: Connection,
onChange: (labels: LabelRegistryEntry[]) => void
) =>
createCollection<LabelRegistryEntry[]>(
"_labelRegistry",
fetchLabelRegistry,
subscribeLabelRegistryUpdates,
conn,
onChange
);
export const createLabelRegistryEntry = (
hass: HomeAssistant,
values: LabelRegistryEntryMutableParams
) =>
hass.callWS<LabelRegistryEntry>({
type: "config/label_registry/create",
...values,
});
export const updateLabelRegistryEntry = (
hass: HomeAssistant,
labelId: string,
updates: Partial<LabelRegistryEntryMutableParams>
) =>
hass.callWS<LabelRegistryEntry>({
type: "config/label_registry/update",
label_id: labelId,
...updates,
});
export const deleteLabelRegistryEntry = (
hass: HomeAssistant,
labelId: string
) =>
hass.callWS({
type: "config/label_registry/delete",
label_id: labelId,
});

View File

@@ -1,17 +1,105 @@
import { mdiLabel } from "@mdi/js"; import { mdiLabel } from "@mdi/js";
import { computeDomain } from "../../common/entity/compute_domain"; import type { Connection } from "home-assistant-js-websocket";
import type { HaDevicePickerDeviceFilterFunc } from "../../components/device/ha-device-picker"; import { createCollection } from "home-assistant-js-websocket";
import type { PickerComboBoxItem } from "../../components/ha-picker-combo-box"; import type { Store } from "home-assistant-js-websocket/dist/store";
import type { FuseWeightedKey } from "../../resources/fuseMultiTerm"; import { computeDomain } from "../common/entity/compute_domain";
import type { HomeAssistant } from "../../types"; import { stringCompare } from "../common/string/compare";
import { debounce } from "../common/util/debounce";
import type { HaDevicePickerDeviceFilterFunc } from "../components/device/ha-device-picker";
import type { PickerComboBoxItem } from "../components/ha-picker-combo-box";
import type { FuseWeightedKey } from "../resources/fuseMultiTerm";
import type { HomeAssistant } from "../types";
import { import {
getDeviceEntityDisplayLookup, getDeviceEntityDisplayLookup,
type DeviceEntityDisplayLookup, type DeviceEntityDisplayLookup,
type DeviceRegistryEntry, type DeviceRegistryEntry,
} from "../device/device_registry"; } from "./device_registry";
import type { HaEntityPickerEntityFilterFunc } from "../entity/entity"; import type { HaEntityPickerEntityFilterFunc } from "./entity";
import type { EntityRegistryDisplayEntry } from "../entity/entity_registry"; import type { EntityRegistryDisplayEntry } from "./entity_registry";
import type { LabelRegistryEntry } from "./label_registry"; import type { RegistryEntry } from "./registry";
export interface LabelRegistryEntry extends RegistryEntry {
label_id: string;
name: string;
icon: string | null;
color: string | null;
description: string | null;
}
export interface LabelRegistryEntryMutableParams {
name: string;
icon?: string | null;
color?: string | null;
description?: string | null;
}
export const fetchLabelRegistry = (conn: Connection) =>
conn
.sendMessagePromise({
type: "config/label_registry/list",
})
.then((labels) =>
(labels as LabelRegistryEntry[]).sort((ent1, ent2) =>
stringCompare(ent1.name, ent2.name)
)
);
export const subscribeLabelRegistryUpdates = (
conn: Connection,
store: Store<LabelRegistryEntry[]>
) =>
conn.subscribeEvents(
debounce(
() =>
fetchLabelRegistry(conn).then((labels: LabelRegistryEntry[]) =>
store.setState(labels, true)
),
500,
true
),
"label_registry_updated"
);
export const subscribeLabelRegistry = (
conn: Connection,
onChange: (labels: LabelRegistryEntry[]) => void
) =>
createCollection<LabelRegistryEntry[]>(
"_labelRegistry",
fetchLabelRegistry,
subscribeLabelRegistryUpdates,
conn,
onChange
);
export const createLabelRegistryEntry = (
hass: HomeAssistant,
values: LabelRegistryEntryMutableParams
) =>
hass.callWS<LabelRegistryEntry>({
type: "config/label_registry/create",
...values,
});
export const updateLabelRegistryEntry = (
hass: HomeAssistant,
labelId: string,
updates: Partial<LabelRegistryEntryMutableParams>
) =>
hass.callWS<LabelRegistryEntry>({
type: "config/label_registry/update",
label_id: labelId,
...updates,
});
export const deleteLabelRegistryEntry = (
hass: HomeAssistant,
labelId: string
) =>
hass.callWS({
type: "config/label_registry/delete",
label_id: labelId,
});
export const labelComboBoxKeys: FuseWeightedKey[] = [ export const labelComboBoxKeys: FuseWeightedKey[] = [
{ {

View File

@@ -2,7 +2,7 @@ import type {
HassEntityAttributeBase, HassEntityAttributeBase,
HassEntityBase, HassEntityBase,
} from "home-assistant-js-websocket"; } from "home-assistant-js-websocket";
import { UNAVAILABLE } from "./entity/entity"; import { UNAVAILABLE } from "./entity";
export type LawnMowerEntityState = export type LawnMowerEntityState =
| "paused" | "paused"

View File

@@ -2,10 +2,10 @@ import type {
HassEntityAttributeBase, HassEntityAttributeBase,
HassEntityBase, HassEntityBase,
} from "home-assistant-js-websocket"; } from "home-assistant-js-websocket";
import { getExtendedEntityRegistryEntry } from "./entity_registry";
import { showEnterCodeDialog } from "../dialogs/enter-code/show-enter-code-dialog"; import { showEnterCodeDialog } from "../dialogs/enter-code/show-enter-code-dialog";
import type { HomeAssistant } from "../types"; import type { HomeAssistant } from "../types";
import { UNAVAILABLE } from "./entity/entity"; import { UNAVAILABLE } from "./entity";
import { getExtendedEntityRegistryEntry } from "./entity/entity_registry";
export const enum LockEntityFeature { export const enum LockEntityFeature {
OPEN = 1, OPEN = 1,

View File

@@ -9,7 +9,7 @@ import { computeStateDomain } from "../common/entity/compute_state_domain";
import { autoCaseNoun } from "../common/translations/auto_case_noun"; import { autoCaseNoun } from "../common/translations/auto_case_noun";
import type { LocalizeFunc } from "../common/translations/localize"; import type { LocalizeFunc } from "../common/translations/localize";
import type { HomeAssistant } from "../types"; import type { HomeAssistant } from "../types";
import { UNAVAILABLE, UNKNOWN } from "./entity/entity"; import { UNAVAILABLE, UNKNOWN } from "./entity";
import { isNumericEntity } from "./history"; import { isNumericEntity } from "./history";
const LOGBOOK_LOCALIZE_PATH = "ui.components.logbook.messages"; const LOGBOOK_LOCALIZE_PATH = "ui.components.logbook.messages";

View File

@@ -1,8 +1,8 @@
import type { UnsubscribeFunc } from "home-assistant-js-websocket"; import type { UnsubscribeFunc } from "home-assistant-js-websocket";
import { isComponentLoaded } from "../common/config/is_component_loaded";
import { navigate } from "../common/navigate"; import { navigate } from "../common/navigate";
import type { HomeAssistant } from "../types"; import type { HomeAssistant } from "../types";
import { subscribeDeviceRegistry } from "./device/device_registry"; import { subscribeDeviceRegistry } from "./device_registry";
import { isComponentLoaded } from "../common/config/is_component_loaded";
import { getThreadDataSetTLV, listThreadDataSets } from "./thread"; import { getThreadDataSetTLV, listThreadDataSets } from "./thread";
export enum NetworkType { export enum NetworkType {

View File

@@ -32,11 +32,11 @@ import type {
HassEntityAttributeBase, HassEntityAttributeBase,
HassEntityBase, HassEntityBase,
} from "home-assistant-js-websocket"; } from "home-assistant-js-websocket";
import { stateActive } from "../common/entity/state_active";
import { supportsFeature } from "../common/entity/supports-feature"; import { supportsFeature } from "../common/entity/supports-feature";
import { stateActive } from "../common/entity/state_active";
import type { MediaPlayerItemId } from "../components/media-player/ha-media-player-browse"; import type { MediaPlayerItemId } from "../components/media-player/ha-media-player-browse";
import type { HomeAssistant, TranslationDict } from "../types"; import type { HomeAssistant, TranslationDict } from "../types";
import { isUnavailableState } from "./entity/entity"; import { isUnavailableState } from "./entity";
import { isTTSMediaSource } from "./tts"; import { isTTSMediaSource } from "./tts";
interface MediaPlayerEntityAttributes extends HassEntityAttributeBase { interface MediaPlayerEntityAttributes extends HassEntityAttributeBase {

View File

@@ -1,5 +1,4 @@
import { html, nothing } from "lit"; import { html, nothing } from "lit";
import "../components/ha-expansion-panel";
import { import {
showAlertDialog, showAlertDialog,
showConfirmationDialog, showConfirmationDialog,
@@ -8,7 +7,8 @@ import type { HomeAssistant } from "../types";
import { import {
getAutomaticEntityIds, getAutomaticEntityIds,
updateEntityRegistryEntry, updateEntityRegistryEntry,
} from "./entity/entity_registry"; } from "./entity_registry";
import "../components/ha-expansion-panel";
export const regenerateEntityIds = async ( export const regenerateEntityIds = async (
element: HTMLElement, element: HTMLElement,

View File

@@ -1,15 +1,22 @@
import { ensureArray } from "../common/array/ensure-array"; import { ensureArray } from "../common/array/ensure-array";
import { formatNumericDuration } from "../common/datetime/format_duration"; import { formatNumericDuration } from "../common/datetime/format_duration";
import secondsToDuration from "../common/datetime/seconds_to_duration"; import secondsToDuration from "../common/datetime/seconds_to_duration";
import { computeDeviceNameDisplay } from "../common/entity/compute_device_name";
import { computeStateName } from "../common/entity/compute_state_name"; import { computeStateName } from "../common/entity/compute_state_name";
import { formatListWithAnds } from "../common/string/format-list"; import { formatListWithAnds } from "../common/string/format-list";
import { isTemplate } from "../common/string/has-template"; import { isTemplate } from "../common/string/has-template";
import type { HomeAssistant } from "../types"; import type { HomeAssistant } from "../types";
import type { Condition } from "./automation"; import type { Condition } from "./automation";
import { describeCondition } from "./automation_i18n"; import { describeCondition } from "./automation_i18n";
import { localizeDeviceAutomationAction } from "./device/device_automation"; import { localizeDeviceAutomationAction } from "./device_automation";
import type { EntityRegistryEntry } from "./entity/entity_registry"; import type { EntityRegistryEntry } from "./entity_registry";
import {
computeEntityRegistryName,
entityRegistryById,
} from "./entity_registry";
import type { FloorRegistryEntry } from "./floor_registry";
import { domainToName } from "./integration"; import { domainToName } from "./integration";
import type { LabelRegistryEntry } from "./label_registry";
import type { import type {
ActionType, ActionType,
ActionTypes, ActionTypes,
@@ -34,6 +41,8 @@ const actionTranslationBaseKey =
export const describeAction = <T extends ActionType>( export const describeAction = <T extends ActionType>(
hass: HomeAssistant, hass: HomeAssistant,
entityRegistry: EntityRegistryEntry[], entityRegistry: EntityRegistryEntry[],
labelRegistry: LabelRegistryEntry[],
floorRegistry: Record<string, FloorRegistryEntry>,
action: ActionTypes[T], action: ActionTypes[T],
actionType?: T, actionType?: T,
ignoreAlias = false ignoreAlias = false
@@ -42,6 +51,8 @@ export const describeAction = <T extends ActionType>(
const description = tryDescribeAction( const description = tryDescribeAction(
hass, hass,
entityRegistry, entityRegistry,
labelRegistry,
floorRegistry,
action, action,
actionType, actionType,
ignoreAlias ignoreAlias
@@ -64,6 +75,8 @@ export const describeAction = <T extends ActionType>(
const tryDescribeAction = <T extends ActionType>( const tryDescribeAction = <T extends ActionType>(
hass: HomeAssistant, hass: HomeAssistant,
entityRegistry: EntityRegistryEntry[], entityRegistry: EntityRegistryEntry[],
labelRegistry: LabelRegistryEntry[],
floorRegistry: Record<string, FloorRegistryEntry>,
action: ActionTypes[T], action: ActionTypes[T],
actionType?: T, actionType?: T,
ignoreAlias = false ignoreAlias = false
@@ -87,6 +100,107 @@ const tryDescribeAction = <T extends ActionType>(
{ name: "target" } { name: "target" }
) )
); );
} else if (targetOrData) {
for (const [key, name] of Object.entries({
area_id: "areas",
device_id: "devices",
entity_id: "entities",
floor_id: "floors",
label_id: "labels",
})) {
if (!(key in targetOrData)) {
continue;
}
const keyConf: string[] = ensureArray(targetOrData[key]) || [];
for (const targetThing of keyConf) {
if (isTemplate(targetThing)) {
targets.push(
hass.localize(
`${actionTranslationBaseKey}.service.description.target_template`,
{ name }
)
);
break;
} else if (key === "entity_id") {
if (targetThing.includes(".")) {
const state = hass.states[targetThing];
if (state) {
targets.push(computeStateName(state));
} else {
targets.push(targetThing);
}
} else {
const entityReg = entityRegistryById(entityRegistry)[targetThing];
if (entityReg) {
targets.push(
computeEntityRegistryName(hass, entityReg) || targetThing
);
} else if (targetThing === "all") {
targets.push(
hass.localize(
`${actionTranslationBaseKey}.service.description.target_every_entity`
)
);
} else {
targets.push(
hass.localize(
`${actionTranslationBaseKey}.service.description.target_unknown_entity`
)
);
}
}
} else if (key === "device_id") {
const device = hass.devices[targetThing];
if (device) {
targets.push(computeDeviceNameDisplay(device, hass));
} else {
targets.push(
hass.localize(
`${actionTranslationBaseKey}.service.description.target_unknown_device`
)
);
}
} else if (key === "area_id") {
const area = hass.areas[targetThing];
if (area?.name) {
targets.push(area.name);
} else {
targets.push(
hass.localize(
`${actionTranslationBaseKey}.service.description.target_unknown_area`
)
);
}
} else if (key === "floor_id") {
const floor = floorRegistry[targetThing] ?? undefined;
if (floor?.name) {
targets.push(floor.name);
} else {
targets.push(
hass.localize(
`${actionTranslationBaseKey}.service.description.target_unknown_floor`
)
);
}
} else if (key === "label_id") {
const label = labelRegistry.find(
(lbl) => lbl.label_id === targetThing
);
if (label?.name) {
targets.push(label.name);
} else {
targets.push(
hass.localize(
`${actionTranslationBaseKey}.service.description.target_unknown_label`
)
);
}
} else {
targets.push(targetThing);
}
}
}
} }
if ( if (
@@ -115,20 +229,26 @@ const tryDescribeAction = <T extends ActionType>(
if (config.metadata) { if (config.metadata) {
return hass.localize( return hass.localize(
`${actionTranslationBaseKey}.service.description.service_name_no_targets`, targets.length
? `${actionTranslationBaseKey}.service.description.service_name`
: `${actionTranslationBaseKey}.service.description.service_name_no_targets`,
{ {
domain: domainToName(hass.localize, domain), domain: domainToName(hass.localize, domain),
name: service || config.action, name: service || config.action,
targets: formatListWithAnds(hass.locale, targets),
} }
); );
} }
return hass.localize( return hass.localize(
`${actionTranslationBaseKey}.service.description.service_based_on_name_no_targets`, targets.length
? `${actionTranslationBaseKey}.service.description.service_based_on_name`
: `${actionTranslationBaseKey}.service.description.service_based_on_name_no_targets`,
{ {
name: service name: service
? `${domainToName(hass.localize, domain)}: ${service}` ? `${domainToName(hass.localize, domain)}: ${service}`
: config.action, : config.action,
targets: formatListWithAnds(hass.locale, targets),
} }
); );
} }

View File

@@ -3,7 +3,6 @@ import type {
HassServiceTarget, HassServiceTarget,
} from "home-assistant-js-websocket"; } from "home-assistant-js-websocket";
import { ensureArray } from "../common/array/ensure-array"; import { ensureArray } from "../common/array/ensure-array";
import type { EntityNameItem } from "../common/entity/compute_entity_name_display";
import { computeStateDomain } from "../common/entity/compute_state_domain"; import { computeStateDomain } from "../common/entity/compute_state_domain";
import { supportsFeature } from "../common/entity/supports-feature"; import { supportsFeature } from "../common/entity/supports-feature";
import { isHelperDomain } from "../panels/config/helpers/const"; import { isHelperDomain } from "../panels/config/helpers/const";
@@ -12,12 +11,13 @@ import type { HomeAssistant } from "../types";
import { import {
type DeviceRegistryEntry, type DeviceRegistryEntry,
getDeviceIntegrationLookup, getDeviceIntegrationLookup,
} from "./device/device_registry"; } from "./device_registry";
import type { import type {
EntityRegistryDisplayEntry, EntityRegistryDisplayEntry,
EntityRegistryEntry, EntityRegistryEntry,
} from "./entity/entity_registry"; } from "./entity_registry";
import type { EntitySources } from "./entity/entity_sources"; import type { EntitySources } from "./entity_sources";
import type { EntityNameItem } from "../common/entity/compute_entity_name_display";
export type Selector = export type Selector =
| ActionSelector | ActionSelector

View File

@@ -3,13 +3,14 @@ import { computeDomain } from "../common/entity/compute_domain";
import type { HaDevicePickerDeviceFilterFunc } from "../components/device/ha-device-picker"; import type { HaDevicePickerDeviceFilterFunc } from "../components/device/ha-device-picker";
import type { PickerComboBoxItem } from "../components/ha-picker-combo-box"; import type { PickerComboBoxItem } from "../components/ha-picker-combo-box";
import type { HomeAssistant } from "../types"; import type { HomeAssistant } from "../types";
import type { FloorComboBoxItem } from "./area_floor_picker"; import type { FloorComboBoxItem } from "./area_floor";
import type { AreaRegistryEntry } from "./area_registry"; import type { AreaRegistryEntry } from "./area_registry";
import type { DevicePickerItem } from "./device/device_picker"; import type { DevicePickerItem, DeviceRegistryEntry } from "./device_registry";
import type { DeviceRegistryEntry } from "./device/device_registry"; import type { HaEntityPickerEntityFilterFunc } from "./entity";
import type { HaEntityPickerEntityFilterFunc } from "./entity/entity"; import type {
import type { EntityComboBoxItem } from "./entity/entity_picker"; EntityComboBoxItem,
import type { EntityRegistryDisplayEntry } from "./entity/entity_registry"; EntityRegistryDisplayEntry,
} from "./entity_registry";
export const TARGET_SEPARATOR = "________"; export const TARGET_SEPARATOR = "________";

View File

@@ -1,8 +1,8 @@
import type { HomeAssistant, ServiceCallResponse } from "../types";
import { computeDomain } from "../common/entity/compute_domain"; import { computeDomain } from "../common/entity/compute_domain";
import { computeStateName } from "../common/entity/compute_state_name"; import { computeStateName } from "../common/entity/compute_state_name";
import { isUnavailableState } from "./entity";
import { stringCompare } from "../common/string/compare"; import { stringCompare } from "../common/string/compare";
import type { HomeAssistant, ServiceCallResponse } from "../types";
import { isUnavailableState } from "./entity/entity";
export interface TodoList { export interface TodoList {
entity_id: string; entity_id: string;

View File

@@ -4,7 +4,7 @@ import type {
HassEntityBase, HassEntityBase,
HassEvent, HassEvent,
} from "home-assistant-js-websocket"; } from "home-assistant-js-websocket";
import { BINARY_STATE_OFF, BINARY_STATE_ON } from "../common/const"; import { BINARY_STATE_ON, BINARY_STATE_OFF } from "../common/const";
import { computeDomain } from "../common/entity/compute_domain"; import { computeDomain } from "../common/entity/compute_domain";
import { computeStateDomain } from "../common/entity/compute_state_domain"; import { computeStateDomain } from "../common/entity/compute_state_domain";
import { supportsFeature } from "../common/entity/supports-feature"; import { supportsFeature } from "../common/entity/supports-feature";
@@ -13,7 +13,7 @@ import { caseInsensitiveStringCompare } from "../common/string/compare";
import { showAlertDialog } from "../dialogs/generic/show-dialog-box"; import { showAlertDialog } from "../dialogs/generic/show-dialog-box";
import type { HomeAssistant } from "../types"; import type { HomeAssistant } from "../types";
import { showToast } from "../util/toast"; import { showToast } from "../util/toast";
import type { EntitySources } from "./entity/entity_sources"; import type { EntitySources } from "./entity_sources";
export enum UpdateEntityFeature { export enum UpdateEntityFeature {
INSTALL = 1, INSTALL = 1,

View File

@@ -2,7 +2,7 @@ import type {
HassEntityAttributeBase, HassEntityAttributeBase,
HassEntityBase, HassEntityBase,
} from "home-assistant-js-websocket"; } from "home-assistant-js-websocket";
import { UNAVAILABLE } from "./entity/entity"; import { UNAVAILABLE } from "./entity";
export type VacuumEntityState = export type VacuumEntityState =
| "on" | "on"

View File

@@ -2,9 +2,9 @@ import type {
HassEntityAttributeBase, HassEntityAttributeBase,
HassEntityBase, HassEntityBase,
} from "home-assistant-js-websocket"; } from "home-assistant-js-websocket";
import { UNAVAILABLE } from "./entity";
import { stateActive } from "../common/entity/state_active"; import { stateActive } from "../common/entity/state_active";
import type { HomeAssistant } from "../types"; import type { HomeAssistant } from "../types";
import { UNAVAILABLE } from "./entity/entity";
export const enum ValveEntityFeature { export const enum ValveEntityFeature {
OPEN = 1, OPEN = 1,

View File

@@ -1,8 +1,8 @@
import type { Connection } from "home-assistant-js-websocket"; import type { Connection } from "home-assistant-js-websocket";
import { createCollection } from "home-assistant-js-websocket"; import { createCollection } from "home-assistant-js-websocket";
import type { Store } from "home-assistant-js-websocket/dist/store"; import type { Store } from "home-assistant-js-websocket/dist/store";
import type { DeviceRegistryEntry } from "./device_registry";
import { debounce } from "../common/util/debounce"; import { debounce } from "../common/util/debounce";
import type { DeviceRegistryEntry } from "./device/device_registry";
export const fetchDeviceRegistry = (conn: Connection) => export const fetchDeviceRegistry = (conn: Connection) =>
conn.sendMessagePromise<DeviceRegistryEntry[]>({ conn.sendMessagePromise<DeviceRegistryEntry[]>({

Some files were not shown because too many files have changed in this diff Show More