mirror of
				https://github.com/home-assistant/frontend.git
				synced 2025-11-04 00:19:47 +00:00 
			
		
		
		
	Compare commits
	
		
			2 Commits
		
	
	
		
			20251029.1
			...
			ha-wa-dial
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					1aa55b3071 | ||
| 
						 | 
					aa21eff508 | 
@@ -16,9 +16,9 @@ import {
 | 
			
		||||
} from "../../../../src/common/auth/token_storage";
 | 
			
		||||
import { atLeastVersion } from "../../../../src/common/config/version";
 | 
			
		||||
import { toggleAttribute } from "../../../../src/common/dom/toggle_attribute";
 | 
			
		||||
import "../../../../src/components/ha-button";
 | 
			
		||||
import "../../../../src/components/ha-icon";
 | 
			
		||||
import "../../../../src/components/ha-list";
 | 
			
		||||
import "../../../../src/components/ha-button";
 | 
			
		||||
import "../../../../src/components/ha-list-item";
 | 
			
		||||
import "../../../../src/components/ha-svg-icon";
 | 
			
		||||
import {
 | 
			
		||||
@@ -28,6 +28,7 @@ import {
 | 
			
		||||
import { isStrategyDashboard } from "../../../../src/data/lovelace/config/types";
 | 
			
		||||
import type { LovelaceViewConfig } from "../../../../src/data/lovelace/config/view";
 | 
			
		||||
import "../../../../src/layouts/hass-loading-screen";
 | 
			
		||||
import { generateDefaultViewConfig } from "../../../../src/panels/lovelace/common/generate-lovelace-config";
 | 
			
		||||
import "./hc-layout";
 | 
			
		||||
 | 
			
		||||
@customElement("hc-cast")
 | 
			
		||||
@@ -95,9 +96,7 @@ class HcCast extends LitElement {
 | 
			
		||||
                <ha-list @action=${this._handlePickView} activatable>
 | 
			
		||||
                  ${(
 | 
			
		||||
                    this.lovelaceViews ?? [
 | 
			
		||||
                      {
 | 
			
		||||
                        title: "Home",
 | 
			
		||||
                      },
 | 
			
		||||
                      generateDefaultViewConfig({}, {}, {}, {}, () => ""),
 | 
			
		||||
                    ]
 | 
			
		||||
                  ).map(
 | 
			
		||||
                    (view, idx) => html`
 | 
			
		||||
 
 | 
			
		||||
@@ -5,14 +5,14 @@ subtitle: Dialogs provide important prompts in a user flow.
 | 
			
		||||
 | 
			
		||||
# Material Design 3
 | 
			
		||||
 | 
			
		||||
Our dialogs are based on the latest version of Material Design. Please note that we have made some well-considered adjustments to these guidelines. Specs and guidelines can be found on its [website](https://m3.material.io/components/dialogs/overview).
 | 
			
		||||
Our dialogs are based on the latest version of Material Design. Please note that we have made some well-considered adjustments to these guideliness. Specs and guidelines can be found on its [website](https://m3.material.io/components/dialogs/overview).
 | 
			
		||||
 | 
			
		||||
# Guidelines
 | 
			
		||||
 | 
			
		||||
## Design
 | 
			
		||||
 | 
			
		||||
- Dialogs have a max width of 560px. Alert and confirmation dialogs have a fixed width of 320px. If you need more width, consider a dedicated page instead.
 | 
			
		||||
- The close X-icon is on the top left, on all screen sizes. Except for alert and confirmation dialogs, they only have buttons and no X-icon. This is different compared to the Material guidelines.
 | 
			
		||||
- Dialogs have a max width of 560px. Alert and confirmation dialogs got a fixed width of 320px. If you need more width, consider a dedicated page instead.
 | 
			
		||||
- The close X-icon is on the top left, on all screen sizes. Except for alert and confirmation dialogs, they only have buttons and no X-icon. This is different compared to the Material guideliness.
 | 
			
		||||
- Dialogs can't be closed with ESC or clicked outside of the dialog when there is a form that the user needs to fill out. Instead it will animate "no" by a little shake.
 | 
			
		||||
- Extra icon buttons are on the top right, for example help, settings and expand dialog. More than 2 icon buttons, they will be in an overflow menu.
 | 
			
		||||
- The submit button is grouped with a cancel button at the bottom right, on all screen sizes. Fullscreen mobile dialogs have them sticky at the bottom.
 | 
			
		||||
@@ -26,7 +26,7 @@ Our dialogs are based on the latest version of Material Design. Please note that
 | 
			
		||||
 | 
			
		||||
- A best practice is to always use a title, even if it is optional by Material guidelines.
 | 
			
		||||
- People mainly read the title and a button. Put the most important information in those two.
 | 
			
		||||
- Try to avoid user generated content in the title, this could make the title unreadably long.
 | 
			
		||||
- Try to avoid user generated content in the title, this could make the title unreadable long.
 | 
			
		||||
- If users become unsure, they read the description. Make sure this explains what will happen.
 | 
			
		||||
- Strive for minimalism.
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -53,7 +53,7 @@
 | 
			
		||||
    "@fullcalendar/luxon3": "6.1.19",
 | 
			
		||||
    "@fullcalendar/timegrid": "6.1.19",
 | 
			
		||||
    "@home-assistant/webawesome": "3.0.0-beta.6.ha.6",
 | 
			
		||||
    "@lezer/highlight": "1.2.3",
 | 
			
		||||
    "@lezer/highlight": "1.2.2",
 | 
			
		||||
    "@lit-labs/motion": "1.0.9",
 | 
			
		||||
    "@lit-labs/observers": "2.0.6",
 | 
			
		||||
    "@lit-labs/virtualizer": "2.1.1",
 | 
			
		||||
@@ -235,8 +235,5 @@
 | 
			
		||||
    "tslib": "2.8.1",
 | 
			
		||||
    "@material/mwc-list@^0.27.0": "patch:@material/mwc-list@npm%3A0.27.0#~/.yarn/patches/@material-mwc-list-npm-0.27.0-5344fc9de4.patch"
 | 
			
		||||
  },
 | 
			
		||||
  "packageManager": "yarn@4.10.3",
 | 
			
		||||
  "volta": {
 | 
			
		||||
    "node": "22.21.1"
 | 
			
		||||
  }
 | 
			
		||||
  "packageManager": "yarn@4.10.3"
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
 | 
			
		||||
 | 
			
		||||
[project]
 | 
			
		||||
name         = "home-assistant-frontend"
 | 
			
		||||
version      = "20251029.1"
 | 
			
		||||
version      = "20250924.0"
 | 
			
		||||
license      = "Apache-2.0"
 | 
			
		||||
license-files = ["LICENSE*"]
 | 
			
		||||
description  = "The Home Assistant frontend"
 | 
			
		||||
 
 | 
			
		||||
@@ -1,116 +0,0 @@
 | 
			
		||||
export interface SwipeGestureResult {
 | 
			
		||||
  velocity: number;
 | 
			
		||||
  delta: number;
 | 
			
		||||
  isSwipe: boolean;
 | 
			
		||||
  isDownwardSwipe: boolean;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface SwipeGestureConfig {
 | 
			
		||||
  velocitySwipeThreshold?: number;
 | 
			
		||||
  movementTimeThreshold?: number;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const VELOCITY_SWIPE_THRESHOLD = 0.5; // px/ms
 | 
			
		||||
const MOVEMENT_TIME_THRESHOLD = 100; // ms
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Recognizes swipe gestures and calculates velocity for touch interactions.
 | 
			
		||||
 * Tracks touch movement and provides velocity-based and position-based gesture detection.
 | 
			
		||||
 */
 | 
			
		||||
export class SwipeGestureRecognizer {
 | 
			
		||||
  private _startY = 0;
 | 
			
		||||
 | 
			
		||||
  private _delta = 0;
 | 
			
		||||
 | 
			
		||||
  private _startTime = 0;
 | 
			
		||||
 | 
			
		||||
  private _lastY = 0;
 | 
			
		||||
 | 
			
		||||
  private _lastTime = 0;
 | 
			
		||||
 | 
			
		||||
  private _velocityThreshold: number;
 | 
			
		||||
 | 
			
		||||
  private _movementTimeThreshold: number;
 | 
			
		||||
 | 
			
		||||
  constructor(config: SwipeGestureConfig = {}) {
 | 
			
		||||
    this._velocityThreshold =
 | 
			
		||||
      config.velocitySwipeThreshold ?? VELOCITY_SWIPE_THRESHOLD; // px/ms
 | 
			
		||||
    this._movementTimeThreshold =
 | 
			
		||||
      config.movementTimeThreshold ?? MOVEMENT_TIME_THRESHOLD; // ms
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Initialize gesture tracking with starting touch position
 | 
			
		||||
   */
 | 
			
		||||
  public start(clientY: number): void {
 | 
			
		||||
    const now = Date.now();
 | 
			
		||||
    this._startY = clientY;
 | 
			
		||||
    this._startTime = now;
 | 
			
		||||
    this._lastY = clientY;
 | 
			
		||||
    this._lastTime = now;
 | 
			
		||||
    this._delta = 0;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Update gesture state during movement
 | 
			
		||||
   * Returns the current delta (negative when dragging down)
 | 
			
		||||
   */
 | 
			
		||||
  public move(clientY: number): number {
 | 
			
		||||
    const now = Date.now();
 | 
			
		||||
    this._delta = this._startY - clientY;
 | 
			
		||||
    this._lastY = clientY;
 | 
			
		||||
    this._lastTime = now;
 | 
			
		||||
    return this._delta;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Calculate final gesture result when touch ends
 | 
			
		||||
   */
 | 
			
		||||
  public end(): SwipeGestureResult {
 | 
			
		||||
    const velocity = this.getVelocity();
 | 
			
		||||
    const hasSignificantVelocity = Math.abs(velocity) > this._velocityThreshold;
 | 
			
		||||
 | 
			
		||||
    return {
 | 
			
		||||
      velocity,
 | 
			
		||||
      delta: this._delta,
 | 
			
		||||
      isSwipe: hasSignificantVelocity,
 | 
			
		||||
      isDownwardSwipe: velocity > 0,
 | 
			
		||||
    };
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Get current drag delta (negative when dragging down)
 | 
			
		||||
   */
 | 
			
		||||
  public getDelta(): number {
 | 
			
		||||
    return this._delta;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Calculate velocity based on recent movement
 | 
			
		||||
   * Returns 0 if no recent movement detected
 | 
			
		||||
   * Positive velocity means downward swipe
 | 
			
		||||
   */
 | 
			
		||||
  public getVelocity(): number {
 | 
			
		||||
    const now = Date.now();
 | 
			
		||||
    const timeSinceLastMove = now - this._lastTime;
 | 
			
		||||
 | 
			
		||||
    // Only consider velocity if the last movement was recent
 | 
			
		||||
    if (timeSinceLastMove >= this._movementTimeThreshold) {
 | 
			
		||||
      return 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const timeDelta = this._lastTime - this._startTime;
 | 
			
		||||
    return timeDelta > 0 ? (this._lastY - this._startY) / timeDelta : 0;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * Reset all tracking state
 | 
			
		||||
   */
 | 
			
		||||
  public reset(): void {
 | 
			
		||||
    this._startY = 0;
 | 
			
		||||
    this._delta = 0;
 | 
			
		||||
    this._startTime = 0;
 | 
			
		||||
    this._lastY = 0;
 | 
			
		||||
    this._lastTime = 0;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@@ -35,7 +35,6 @@ export const MIN_TIME_BETWEEN_UPDATES = 60 * 5 * 1000;
 | 
			
		||||
const LEGEND_OVERFLOW_LIMIT = 10;
 | 
			
		||||
const LEGEND_OVERFLOW_LIMIT_MOBILE = 6;
 | 
			
		||||
const DOUBLE_TAP_TIME = 300;
 | 
			
		||||
const RESIZE_ANIMATION_DURATION = 250;
 | 
			
		||||
 | 
			
		||||
export type CustomLegendOption = ECOption["legend"] & {
 | 
			
		||||
  type: "custom";
 | 
			
		||||
@@ -206,15 +205,6 @@ export class HaChartBase extends LitElement {
 | 
			
		||||
    }
 | 
			
		||||
    if (changedProps.has("options")) {
 | 
			
		||||
      chartOptions = { ...chartOptions, ...this._createOptions() };
 | 
			
		||||
      if (
 | 
			
		||||
        this._compareCustomLegendOptions(
 | 
			
		||||
          changedProps.get("options"),
 | 
			
		||||
          this.options
 | 
			
		||||
        )
 | 
			
		||||
      ) {
 | 
			
		||||
        // custom legend changes may require a resize to layout properly
 | 
			
		||||
        this._shouldResizeChart = true;
 | 
			
		||||
      }
 | 
			
		||||
    } else if (this._isTouchDevice && changedProps.has("_isZoomed")) {
 | 
			
		||||
      chartOptions.dataZoom = this._getDataZoomConfig();
 | 
			
		||||
    }
 | 
			
		||||
@@ -306,7 +296,7 @@ export class HaChartBase extends LitElement {
 | 
			
		||||
          itemStyle = {
 | 
			
		||||
            color: dataset?.color as string,
 | 
			
		||||
            ...(dataset?.itemStyle as { borderColor?: string }),
 | 
			
		||||
            ...itemStyle,
 | 
			
		||||
            itemStyle,
 | 
			
		||||
          };
 | 
			
		||||
          const color = itemStyle?.color as string;
 | 
			
		||||
          const borderColor = itemStyle?.borderColor as string;
 | 
			
		||||
@@ -518,7 +508,6 @@ export class HaChartBase extends LitElement {
 | 
			
		||||
        );
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
    this.requestUpdate("_hiddenDatasets");
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private _getDataZoomConfig(): DataZoomComponentOption | undefined {
 | 
			
		||||
@@ -969,31 +958,11 @@ export class HaChartBase extends LitElement {
 | 
			
		||||
 | 
			
		||||
  private _handleChartRenderFinished = () => {
 | 
			
		||||
    if (this._shouldResizeChart) {
 | 
			
		||||
      this.chart?.resize({
 | 
			
		||||
        animation: this._reducedMotion
 | 
			
		||||
          ? undefined
 | 
			
		||||
          : { duration: RESIZE_ANIMATION_DURATION },
 | 
			
		||||
      });
 | 
			
		||||
      this.chart?.resize();
 | 
			
		||||
      this._shouldResizeChart = false;
 | 
			
		||||
    }
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  private _compareCustomLegendOptions(
 | 
			
		||||
    oldOptions: ECOption | undefined,
 | 
			
		||||
    newOptions: ECOption | undefined
 | 
			
		||||
  ): boolean {
 | 
			
		||||
    const oldLegends = ensureArray(
 | 
			
		||||
      oldOptions?.legend || []
 | 
			
		||||
    ) as LegendComponentOption[];
 | 
			
		||||
    const newLegends = ensureArray(
 | 
			
		||||
      newOptions?.legend || []
 | 
			
		||||
    ) as LegendComponentOption[];
 | 
			
		||||
    return (
 | 
			
		||||
      oldLegends.some((l) => l.show && l.type === "custom") !==
 | 
			
		||||
      newLegends.some((l) => l.show && l.type === "custom")
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  static styles = css`
 | 
			
		||||
    :host {
 | 
			
		||||
      display: block;
 | 
			
		||||
 
 | 
			
		||||
@@ -1 +0,0 @@
 | 
			
		||||
export const ANY_STATE_VALUE = "__ANY_STATE_IGNORE_ATTRIBUTES__";
 | 
			
		||||
@@ -147,7 +147,7 @@ class HaEntitiesPicker extends LitElement {
 | 
			
		||||
          .createDomains=${this.createDomains}
 | 
			
		||||
          .required=${this.required && !currentEntities.length}
 | 
			
		||||
          @value-changed=${this._addEntity}
 | 
			
		||||
          .addButton=${currentEntities.length > 0}
 | 
			
		||||
          add-button
 | 
			
		||||
        ></ha-entity-picker>
 | 
			
		||||
      </div>
 | 
			
		||||
    `;
 | 
			
		||||
 
 | 
			
		||||
@@ -312,7 +312,7 @@ export class HaEntityNamePicker extends LitElement {
 | 
			
		||||
  private _toValue = memoizeOne(
 | 
			
		||||
    (items: EntityNameItem[]): typeof this.value => {
 | 
			
		||||
      if (items.length === 0) {
 | 
			
		||||
        return undefined;
 | 
			
		||||
        return "";
 | 
			
		||||
      }
 | 
			
		||||
      if (items.length === 1) {
 | 
			
		||||
        const item = items[0];
 | 
			
		||||
 
 | 
			
		||||
@@ -4,7 +4,6 @@ import { customElement, property } from "lit/decorators";
 | 
			
		||||
import { keyed } from "lit/directives/keyed";
 | 
			
		||||
import { repeat } from "lit/directives/repeat";
 | 
			
		||||
import { fireEvent } from "../../common/dom/fire_event";
 | 
			
		||||
import { ANY_STATE_VALUE } from "./const";
 | 
			
		||||
import { ensureArray } from "../../common/array/ensure-array";
 | 
			
		||||
import type { HomeAssistant } from "../../types";
 | 
			
		||||
import "./ha-entity-state-picker";
 | 
			
		||||
@@ -58,7 +57,6 @@ export class HaEntityStatesPicker extends LitElement {
 | 
			
		||||
 | 
			
		||||
    const value = this.value || [];
 | 
			
		||||
    const hide = [...(this.hideStates || []), ...value];
 | 
			
		||||
    const hideValue = value.includes(ANY_STATE_VALUE);
 | 
			
		||||
 | 
			
		||||
    return html`
 | 
			
		||||
      ${repeat(
 | 
			
		||||
@@ -86,7 +84,7 @@ export class HaEntityStatesPicker extends LitElement {
 | 
			
		||||
        `
 | 
			
		||||
      )}
 | 
			
		||||
      <div>
 | 
			
		||||
        ${(this.disabled && value.length) || hideValue
 | 
			
		||||
        ${this.disabled && value.length
 | 
			
		||||
          ? nothing
 | 
			
		||||
          : keyed(
 | 
			
		||||
              value.length,
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,6 @@
 | 
			
		||||
import "@home-assistant/webawesome/dist/components/drawer/drawer";
 | 
			
		||||
import { css, html, LitElement, type PropertyValues } from "lit";
 | 
			
		||||
import { customElement, property, query, state } from "lit/decorators";
 | 
			
		||||
import { SwipeGestureRecognizer } from "../common/util/swipe-gesture-recognizer";
 | 
			
		||||
import { customElement, property, state } from "lit/decorators";
 | 
			
		||||
import { haStyleScrollbar } from "../resources/styles";
 | 
			
		||||
 | 
			
		||||
export const BOTTOM_SHEET_ANIMATION_DURATION_MS = 300;
 | 
			
		||||
@@ -15,12 +14,6 @@ export class HaBottomSheet extends LitElement {
 | 
			
		||||
 | 
			
		||||
  @state() private _drawerOpen = false;
 | 
			
		||||
 | 
			
		||||
  @query("#drawer") private _drawer!: HTMLElement;
 | 
			
		||||
 | 
			
		||||
  private _gestureRecognizer = new SwipeGestureRecognizer();
 | 
			
		||||
 | 
			
		||||
  private _isDragging = false;
 | 
			
		||||
 | 
			
		||||
  private _handleAfterHide() {
 | 
			
		||||
    this.open = false;
 | 
			
		||||
    const ev = new Event("closed", {
 | 
			
		||||
@@ -40,132 +33,19 @@ export class HaBottomSheet extends LitElement {
 | 
			
		||||
  render() {
 | 
			
		||||
    return html`
 | 
			
		||||
      <wa-drawer
 | 
			
		||||
        id="drawer"
 | 
			
		||||
        placement="bottom"
 | 
			
		||||
        .open=${this._drawerOpen}
 | 
			
		||||
        @wa-after-hide=${this._handleAfterHide}
 | 
			
		||||
        without-header
 | 
			
		||||
        @touchstart=${this._handleTouchStart}
 | 
			
		||||
      >
 | 
			
		||||
        <slot name="header"></slot>
 | 
			
		||||
        <div id="body" class="body ha-scrollbar">
 | 
			
		||||
        <div class="body ha-scrollbar">
 | 
			
		||||
          <slot></slot>
 | 
			
		||||
        </div>
 | 
			
		||||
      </wa-drawer>
 | 
			
		||||
    `;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private _handleTouchStart = (ev: TouchEvent) => {
 | 
			
		||||
    // Check if any element inside drawer in the composed path has scrollTop > 0
 | 
			
		||||
    for (const path of ev.composedPath()) {
 | 
			
		||||
      const el = path as HTMLElement;
 | 
			
		||||
      if (el === this._drawer) {
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
      if (el.scrollTop > 0) {
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    this._startResizing(ev.touches[0].clientY);
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  private _startResizing(clientY: number) {
 | 
			
		||||
    // register event listeners for drag handling
 | 
			
		||||
    document.addEventListener("touchmove", this._handleTouchMove, {
 | 
			
		||||
      passive: false,
 | 
			
		||||
    });
 | 
			
		||||
    document.addEventListener("touchend", this._handleTouchEnd);
 | 
			
		||||
    document.addEventListener("touchcancel", this._handleTouchEnd);
 | 
			
		||||
 | 
			
		||||
    this._gestureRecognizer.start(clientY);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private _handleTouchMove = (ev: TouchEvent) => {
 | 
			
		||||
    const currentY = ev.touches[0].clientY;
 | 
			
		||||
    const delta = this._gestureRecognizer.move(currentY);
 | 
			
		||||
 | 
			
		||||
    if (delta < 0) {
 | 
			
		||||
      ev.preventDefault();
 | 
			
		||||
      this._isDragging = true;
 | 
			
		||||
      requestAnimationFrame(() => {
 | 
			
		||||
        if (this._isDragging) {
 | 
			
		||||
          this.style.setProperty(
 | 
			
		||||
            "--dialog-transform",
 | 
			
		||||
            `translateY(${delta * -1}px)`
 | 
			
		||||
          );
 | 
			
		||||
        }
 | 
			
		||||
      });
 | 
			
		||||
    }
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  private _animateSnapBack() {
 | 
			
		||||
    // Add transition for smooth animation
 | 
			
		||||
    this.style.setProperty(
 | 
			
		||||
      "--dialog-transition",
 | 
			
		||||
      `transform ${BOTTOM_SHEET_ANIMATION_DURATION_MS}ms ease-out`
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    // Reset transform to snap back
 | 
			
		||||
    this.style.removeProperty("--dialog-transform");
 | 
			
		||||
 | 
			
		||||
    // Remove transition after animation completes
 | 
			
		||||
    setTimeout(() => {
 | 
			
		||||
      this.style.removeProperty("--dialog-transition");
 | 
			
		||||
    }, BOTTOM_SHEET_ANIMATION_DURATION_MS);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private _handleTouchEnd = () => {
 | 
			
		||||
    this._unregisterResizeHandlers();
 | 
			
		||||
 | 
			
		||||
    this._isDragging = false;
 | 
			
		||||
 | 
			
		||||
    const result = this._gestureRecognizer.end();
 | 
			
		||||
 | 
			
		||||
    // If velocity exceeds threshold, use velocity direction to determine action
 | 
			
		||||
    if (result.isSwipe) {
 | 
			
		||||
      if (result.isDownwardSwipe) {
 | 
			
		||||
        // Downward swipe - close the bottom sheet
 | 
			
		||||
        this._drawerOpen = false;
 | 
			
		||||
      } else {
 | 
			
		||||
        // Upward swipe - keep open and animate back
 | 
			
		||||
        this._animateSnapBack();
 | 
			
		||||
      }
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // If velocity is below threshold, use position-based logic
 | 
			
		||||
    // Get the drawer height to calculate 50% threshold
 | 
			
		||||
    const drawerBody = this._drawer.shadowRoot?.querySelector(
 | 
			
		||||
      '[part="body"]'
 | 
			
		||||
    ) as HTMLElement;
 | 
			
		||||
    const drawerHeight = drawerBody?.offsetHeight || 0;
 | 
			
		||||
 | 
			
		||||
    // delta is negative when dragging down
 | 
			
		||||
    // Close if dragged down past 50% of the drawer height
 | 
			
		||||
    if (
 | 
			
		||||
      drawerHeight > 0 &&
 | 
			
		||||
      result.delta < 0 &&
 | 
			
		||||
      Math.abs(result.delta) > drawerHeight * 0.5
 | 
			
		||||
    ) {
 | 
			
		||||
      this._drawerOpen = false;
 | 
			
		||||
    } else {
 | 
			
		||||
      this._animateSnapBack();
 | 
			
		||||
    }
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  private _unregisterResizeHandlers = () => {
 | 
			
		||||
    document.removeEventListener("touchmove", this._handleTouchMove);
 | 
			
		||||
    document.removeEventListener("touchend", this._handleTouchEnd);
 | 
			
		||||
    document.removeEventListener("touchcancel", this._handleTouchEnd);
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  disconnectedCallback() {
 | 
			
		||||
    super.disconnectedCallback();
 | 
			
		||||
    this._unregisterResizeHandlers();
 | 
			
		||||
    this._isDragging = false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  static styles = [
 | 
			
		||||
    haStyleScrollbar,
 | 
			
		||||
    css`
 | 
			
		||||
@@ -179,8 +59,6 @@ export class HaBottomSheet extends LitElement {
 | 
			
		||||
      wa-drawer::part(dialog) {
 | 
			
		||||
        max-height: var(--ha-bottom-sheet-max-height, 90vh);
 | 
			
		||||
        align-items: center;
 | 
			
		||||
        transform: var(--dialog-transform);
 | 
			
		||||
        transition: var(--dialog-transition);
 | 
			
		||||
      }
 | 
			
		||||
      wa-drawer::part(body) {
 | 
			
		||||
        max-width: var(--ha-bottom-sheet-max-width);
 | 
			
		||||
@@ -212,11 +90,6 @@ export class HaBottomSheet extends LitElement {
 | 
			
		||||
        max-width: 100%;
 | 
			
		||||
        display: flex;
 | 
			
		||||
        flex-direction: column;
 | 
			
		||||
        padding: var(
 | 
			
		||||
          --ha-bottom-sheet-padding,
 | 
			
		||||
          0 var(--safe-area-inset-right) var(--safe-area-inset-bottom)
 | 
			
		||||
            var(--safe-area-inset-left)
 | 
			
		||||
        );
 | 
			
		||||
      }
 | 
			
		||||
    `,
 | 
			
		||||
  ];
 | 
			
		||||
 
 | 
			
		||||
@@ -148,7 +148,7 @@ export class HaForm extends LitElement implements HaFormElement {
 | 
			
		||||
                  .value=${getValue(this.data, item)}
 | 
			
		||||
                  .label=${this._computeLabel(item, this.data)}
 | 
			
		||||
                  .disabled=${item.disabled || this.disabled || false}
 | 
			
		||||
                  .placeholder=${item.required ? undefined : item.default}
 | 
			
		||||
                  .placeholder=${item.required ? "" : item.default}
 | 
			
		||||
                  .helper=${this._computeHelper(item)}
 | 
			
		||||
                  .localizeValue=${this.localizeValue}
 | 
			
		||||
                  .required=${item.required || false}
 | 
			
		||||
 
 | 
			
		||||
@@ -16,10 +16,14 @@ import memoizeOne from "memoize-one";
 | 
			
		||||
import { computeCssColor } from "../../common/color/compute-color";
 | 
			
		||||
import { hex2rgb } from "../../common/color/convert-color";
 | 
			
		||||
import { fireEvent } from "../../common/dom/fire_event";
 | 
			
		||||
import { computeDeviceNameDisplay } from "../../common/entity/compute_device_name";
 | 
			
		||||
import { computeDomain } from "../../common/entity/compute_domain";
 | 
			
		||||
import { computeStateName } from "../../common/entity/compute_state_name";
 | 
			
		||||
import { slugify } from "../../common/string/slugify";
 | 
			
		||||
import {
 | 
			
		||||
  computeDeviceName,
 | 
			
		||||
  computeDeviceNameDisplay,
 | 
			
		||||
} from "../../common/entity/compute_device_name";
 | 
			
		||||
import { computeDomain } from "../../common/entity/compute_domain";
 | 
			
		||||
import { computeEntityName } from "../../common/entity/compute_entity_name";
 | 
			
		||||
import { getEntityContext } from "../../common/entity/context/get_entity_context";
 | 
			
		||||
import { getConfigEntry } from "../../data/config_entries";
 | 
			
		||||
import { labelsContext } from "../../data/context";
 | 
			
		||||
import { domainToName } from "../../data/integration";
 | 
			
		||||
@@ -168,10 +172,23 @@ export class HaTargetPickerValueChip extends LitElement {
 | 
			
		||||
    if (type === "entity") {
 | 
			
		||||
      this._setDomainName(computeDomain(itemId));
 | 
			
		||||
 | 
			
		||||
      const stateObj = this.hass.states[itemId];
 | 
			
		||||
      const stateObject = this.hass.states[itemId];
 | 
			
		||||
      const entityName = computeEntityName(
 | 
			
		||||
        stateObject,
 | 
			
		||||
        this.hass.entities,
 | 
			
		||||
        this.hass.devices
 | 
			
		||||
      );
 | 
			
		||||
      const { device } = getEntityContext(
 | 
			
		||||
        stateObject,
 | 
			
		||||
        this.hass.entities,
 | 
			
		||||
        this.hass.devices,
 | 
			
		||||
        this.hass.areas,
 | 
			
		||||
        this.hass.floors
 | 
			
		||||
      );
 | 
			
		||||
      const deviceName = device ? computeDeviceName(device) : undefined;
 | 
			
		||||
      return {
 | 
			
		||||
        name: computeStateName(stateObj) || itemId,
 | 
			
		||||
        stateObject: stateObj,
 | 
			
		||||
        name: entityName || deviceName || itemId,
 | 
			
		||||
        stateObject,
 | 
			
		||||
      };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -435,9 +435,9 @@ export const convertStatisticsToHistory = (
 | 
			
		||||
  Object.entries(orderedStatistics).forEach(([key, value]) => {
 | 
			
		||||
    const entityHistoryStates: EntityHistoryState[] = value.map((e) => ({
 | 
			
		||||
      s: e.mean != null ? e.mean.toString() : e.state!.toString(),
 | 
			
		||||
      lc: e.end / 1000,
 | 
			
		||||
      lc: e.start / 1000,
 | 
			
		||||
      a: {},
 | 
			
		||||
      lu: e.end / 1000,
 | 
			
		||||
      lu: e.start / 1000,
 | 
			
		||||
    }));
 | 
			
		||||
    statsHistoryStates[key] = entityHistoryStates;
 | 
			
		||||
  });
 | 
			
		||||
 
 | 
			
		||||
@@ -484,7 +484,7 @@ class DataEntryFlowDialog extends LitElement {
 | 
			
		||||
        this._unsubDataEntryFlowProgress = undefined;
 | 
			
		||||
      }
 | 
			
		||||
      if (_step.next_flow[0] === "config_flow") {
 | 
			
		||||
        showConfigFlowDialog(this, {
 | 
			
		||||
        showConfigFlowDialog(this._params!.dialogParentElement!, {
 | 
			
		||||
          continueFlowId: _step.next_flow[1],
 | 
			
		||||
          carryOverDevices: this._devices(
 | 
			
		||||
            this._params!.flowConfig.showDevices,
 | 
			
		||||
@@ -496,23 +496,32 @@ class DataEntryFlowDialog extends LitElement {
 | 
			
		||||
        });
 | 
			
		||||
      } else if (_step.next_flow[0] === "options_flow") {
 | 
			
		||||
        if (_step.type === "create_entry") {
 | 
			
		||||
          showOptionsFlowDialog(this, _step.result!, {
 | 
			
		||||
            continueFlowId: _step.next_flow[1],
 | 
			
		||||
            navigateToResult: this._params!.navigateToResult,
 | 
			
		||||
            dialogClosedCallback: this._params!.dialogClosedCallback,
 | 
			
		||||
          });
 | 
			
		||||
          showOptionsFlowDialog(
 | 
			
		||||
            this._params!.dialogParentElement!,
 | 
			
		||||
            _step.result!,
 | 
			
		||||
            {
 | 
			
		||||
              continueFlowId: _step.next_flow[1],
 | 
			
		||||
              navigateToResult: this._params!.navigateToResult,
 | 
			
		||||
              dialogClosedCallback: this._params!.dialogClosedCallback,
 | 
			
		||||
            }
 | 
			
		||||
          );
 | 
			
		||||
        }
 | 
			
		||||
      } else if (_step.next_flow[0] === "config_subentries_flow") {
 | 
			
		||||
        if (_step.type === "create_entry") {
 | 
			
		||||
          showSubConfigFlowDialog(this, _step.result!, _step.next_flow[0], {
 | 
			
		||||
            continueFlowId: _step.next_flow[1],
 | 
			
		||||
            navigateToResult: this._params!.navigateToResult,
 | 
			
		||||
            dialogClosedCallback: this._params!.dialogClosedCallback,
 | 
			
		||||
          });
 | 
			
		||||
          showSubConfigFlowDialog(
 | 
			
		||||
            this._params!.dialogParentElement!,
 | 
			
		||||
            _step.result!,
 | 
			
		||||
            _step.next_flow[0],
 | 
			
		||||
            {
 | 
			
		||||
              continueFlowId: _step.next_flow[1],
 | 
			
		||||
              navigateToResult: this._params!.navigateToResult,
 | 
			
		||||
              dialogClosedCallback: this._params!.dialogClosedCallback,
 | 
			
		||||
            }
 | 
			
		||||
          );
 | 
			
		||||
        }
 | 
			
		||||
      } else {
 | 
			
		||||
        this.closeDialog();
 | 
			
		||||
        showAlertDialog(this, {
 | 
			
		||||
        showAlertDialog(this._params!.dialogParentElement!, {
 | 
			
		||||
          text: this.hass.localize(
 | 
			
		||||
            "ui.panel.config.integrations.config_flow.error",
 | 
			
		||||
            { error: `Unsupported next flow type: ${_step.next_flow[0]}` }
 | 
			
		||||
 
 | 
			
		||||
@@ -1,15 +1,15 @@
 | 
			
		||||
import { mdiAlertOutline } from "@mdi/js";
 | 
			
		||||
import { mdiAlertOutline, mdiClose } from "@mdi/js";
 | 
			
		||||
import { css, html, LitElement, nothing } from "lit";
 | 
			
		||||
import { customElement, property, query, state } from "lit/decorators";
 | 
			
		||||
import { ifDefined } from "lit/directives/if-defined";
 | 
			
		||||
import { fireEvent } from "../../common/dom/fire_event";
 | 
			
		||||
import "../../components/ha-button";
 | 
			
		||||
import "../../components/ha-dialog-footer";
 | 
			
		||||
import "../../components/ha-dialog-header";
 | 
			
		||||
import "../../components/ha-md-dialog";
 | 
			
		||||
import type { HaMdDialog } from "../../components/ha-md-dialog";
 | 
			
		||||
import "../../components/ha-svg-icon";
 | 
			
		||||
import "../../components/ha-textfield";
 | 
			
		||||
import type { HaTextField } from "../../components/ha-textfield";
 | 
			
		||||
import "../../components/ha-wa-dialog";
 | 
			
		||||
import type { HomeAssistant } from "../../types";
 | 
			
		||||
import type { DialogBoxParams } from "./show-dialog-box";
 | 
			
		||||
 | 
			
		||||
@@ -19,12 +19,12 @@ class DialogBox extends LitElement {
 | 
			
		||||
 | 
			
		||||
  @state() private _params?: DialogBoxParams;
 | 
			
		||||
 | 
			
		||||
  @state() private _open = false;
 | 
			
		||||
 | 
			
		||||
  @state() private _closeState?: "canceled" | "confirmed";
 | 
			
		||||
 | 
			
		||||
  @query("ha-textfield") private _textField?: HaTextField;
 | 
			
		||||
 | 
			
		||||
  @query("ha-md-dialog") private _dialog?: HaMdDialog;
 | 
			
		||||
 | 
			
		||||
  private _closePromise?: Promise<void>;
 | 
			
		||||
 | 
			
		||||
  private _closeResolve?: () => void;
 | 
			
		||||
@@ -34,6 +34,7 @@ class DialogBox extends LitElement {
 | 
			
		||||
      await this._closePromise;
 | 
			
		||||
    }
 | 
			
		||||
    this._params = params;
 | 
			
		||||
    this._open = true;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public closeDialog(): boolean {
 | 
			
		||||
@@ -60,16 +61,25 @@ class DialogBox extends LitElement {
 | 
			
		||||
        this.hass.localize("ui.dialogs.generic.default_confirmation_title"));
 | 
			
		||||
 | 
			
		||||
    return html`
 | 
			
		||||
      <ha-md-dialog
 | 
			
		||||
        open
 | 
			
		||||
        .disableCancelAction=${confirmPrompt}
 | 
			
		||||
      <ha-wa-dialog
 | 
			
		||||
        .hass=${this.hass}
 | 
			
		||||
        .open=${this._open}
 | 
			
		||||
        ?prevent-scrim-close=${confirmPrompt}
 | 
			
		||||
        @closed=${this._dialogClosed}
 | 
			
		||||
        type="alert"
 | 
			
		||||
        aria-labelledby="dialog-box-title"
 | 
			
		||||
        aria-describedby="dialog-box-description"
 | 
			
		||||
      >
 | 
			
		||||
        <div slot="headline">
 | 
			
		||||
          <span .title=${dialogTitle} id="dialog-box-title">
 | 
			
		||||
        <ha-dialog-header slot="header">
 | 
			
		||||
          ${!confirmPrompt
 | 
			
		||||
            ? html`<slot name="headerNavigationIcon" slot="navigationIcon">
 | 
			
		||||
                <ha-icon-button
 | 
			
		||||
                  data-dialog="close"
 | 
			
		||||
                  .label=${this.hass?.localize("ui.common.close") ?? "Close"}
 | 
			
		||||
                  .path=${mdiClose}
 | 
			
		||||
                ></ha-icon-button
 | 
			
		||||
              ></slot>`
 | 
			
		||||
            : nothing}
 | 
			
		||||
          <span slot="title" id="dialog-box-title">
 | 
			
		||||
            ${this._params.warning
 | 
			
		||||
              ? html`<ha-svg-icon
 | 
			
		||||
                  .path=${mdiAlertOutline}
 | 
			
		||||
@@ -78,13 +88,13 @@ class DialogBox extends LitElement {
 | 
			
		||||
              : nothing}
 | 
			
		||||
            ${dialogTitle}
 | 
			
		||||
          </span>
 | 
			
		||||
        </div>
 | 
			
		||||
        <div slot="content" id="dialog-box-description">
 | 
			
		||||
        </ha-dialog-header>
 | 
			
		||||
        <div id="dialog-box-description">
 | 
			
		||||
          ${this._params.text ? html` <p>${this._params.text}</p> ` : ""}
 | 
			
		||||
          ${this._params.prompt
 | 
			
		||||
            ? html`
 | 
			
		||||
                <ha-textfield
 | 
			
		||||
                  dialogInitialFocus
 | 
			
		||||
                  autofocus
 | 
			
		||||
                  value=${ifDefined(this._params.defaultValue)}
 | 
			
		||||
                  .placeholder=${this._params.placeholder}
 | 
			
		||||
                  .label=${this._params.inputLabel
 | 
			
		||||
@@ -99,10 +109,11 @@ class DialogBox extends LitElement {
 | 
			
		||||
              `
 | 
			
		||||
            : ""}
 | 
			
		||||
        </div>
 | 
			
		||||
        <div slot="actions">
 | 
			
		||||
        <ha-dialog-footer slot="footer">
 | 
			
		||||
          ${confirmPrompt
 | 
			
		||||
            ? html`
 | 
			
		||||
                <ha-button
 | 
			
		||||
                  slot="secondaryAction"
 | 
			
		||||
                  @click=${this._dismiss}
 | 
			
		||||
                  ?autofocus=${!this._params.prompt && this._params.destructive}
 | 
			
		||||
                  appearance="plain"
 | 
			
		||||
@@ -114,6 +125,7 @@ class DialogBox extends LitElement {
 | 
			
		||||
              `
 | 
			
		||||
            : nothing}
 | 
			
		||||
          <ha-button
 | 
			
		||||
            slot="primaryAction"
 | 
			
		||||
            @click=${this._confirm}
 | 
			
		||||
            ?autofocus=${!this._params.prompt && !this._params.destructive}
 | 
			
		||||
            variant=${this._params.destructive ? "danger" : "brand"}
 | 
			
		||||
@@ -122,8 +134,8 @@ class DialogBox extends LitElement {
 | 
			
		||||
              ? this._params.confirmText
 | 
			
		||||
              : this.hass.localize("ui.common.ok")}
 | 
			
		||||
          </ha-button>
 | 
			
		||||
        </div>
 | 
			
		||||
      </ha-md-dialog>
 | 
			
		||||
        </ha-dialog-footer>
 | 
			
		||||
      </ha-wa-dialog>
 | 
			
		||||
    `;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@@ -148,8 +160,7 @@ class DialogBox extends LitElement {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private _closeDialog() {
 | 
			
		||||
    fireEvent(this, "dialog-closed", { dialog: this.localName });
 | 
			
		||||
    this._dialog?.close();
 | 
			
		||||
    this._open = false;
 | 
			
		||||
    this._closePromise = new Promise((resolve) => {
 | 
			
		||||
      this._closeResolve = resolve;
 | 
			
		||||
    });
 | 
			
		||||
@@ -162,6 +173,7 @@ class DialogBox extends LitElement {
 | 
			
		||||
    }
 | 
			
		||||
    this._closeState = undefined;
 | 
			
		||||
    this._params = undefined;
 | 
			
		||||
    this._open = false;
 | 
			
		||||
    this._closeResolve?.();
 | 
			
		||||
    this._closeResolve = undefined;
 | 
			
		||||
  }
 | 
			
		||||
 
 | 
			
		||||
@@ -143,14 +143,9 @@ class DialogCalendarEventDetail extends LitElement {
 | 
			
		||||
      this.hass.locale.time_zone,
 | 
			
		||||
      this.hass.config.time_zone
 | 
			
		||||
    );
 | 
			
		||||
    // For all-day events (date-only strings), parse without timezone to avoid offset issues
 | 
			
		||||
    const start = isDate(this._data!.dtstart)
 | 
			
		||||
      ? new Date(this._data!.dtstart + "T00:00:00")
 | 
			
		||||
      : new TZDate(this._data!.dtstart, timeZone);
 | 
			
		||||
    const endValue = isDate(this._data!.dtend)
 | 
			
		||||
      ? new Date(this._data!.dtend + "T00:00:00")
 | 
			
		||||
      : new TZDate(this._data!.dtend, timeZone);
 | 
			
		||||
    // All day event end dates are exclusive in iCalendar format, subtract one day for display
 | 
			
		||||
    const start = new TZDate(this._data!.dtstart, timeZone);
 | 
			
		||||
    const endValue = new TZDate(this._data!.dtend, timeZone);
 | 
			
		||||
    // All day events should be displayed as a day earlier
 | 
			
		||||
    const end = isDate(this._data.dtend) ? addDays(endValue, -1) : endValue;
 | 
			
		||||
    // The range can be shortened when the start and end are on the same day.
 | 
			
		||||
    if (isSameDay(start, end)) {
 | 
			
		||||
 
 | 
			
		||||
@@ -15,7 +15,6 @@ import {
 | 
			
		||||
  getFloors,
 | 
			
		||||
} from "../../lovelace/strategies/areas/helpers/areas-strategy-helper";
 | 
			
		||||
import { getHomeStructure } from "../../lovelace/strategies/home/helpers/home-structure";
 | 
			
		||||
import { floorDefaultIcon } from "../../../components/ha-floor-icon";
 | 
			
		||||
 | 
			
		||||
export interface ClimateViewStrategyConfig {
 | 
			
		||||
  type: "climate";
 | 
			
		||||
@@ -153,7 +152,6 @@ export class ClimateViewStrategy extends ReactiveElement {
 | 
			
		||||
              floorCount > 1
 | 
			
		||||
                ? floor.name
 | 
			
		||||
                : hass.localize("ui.panel.lovelace.strategy.home.areas"),
 | 
			
		||||
            icon: floor.icon || floorDefaultIcon(floor),
 | 
			
		||||
          },
 | 
			
		||||
        ],
 | 
			
		||||
      };
 | 
			
		||||
 
 | 
			
		||||
@@ -3,7 +3,6 @@ import {
 | 
			
		||||
  mdiContentCopy,
 | 
			
		||||
  mdiContentCut,
 | 
			
		||||
  mdiDelete,
 | 
			
		||||
  mdiIdentifier,
 | 
			
		||||
  mdiPlayCircleOutline,
 | 
			
		||||
  mdiPlaylistEdit,
 | 
			
		||||
  mdiPlusCircleMultipleOutline,
 | 
			
		||||
@@ -41,8 +40,6 @@ export default class HaAutomationSidebarTrigger extends LitElement {
 | 
			
		||||
  @property({ type: Number, attribute: "sidebar-key" })
 | 
			
		||||
  public sidebarKey?: number;
 | 
			
		||||
 | 
			
		||||
  @state() private _requestShowId = false;
 | 
			
		||||
 | 
			
		||||
  @state() private _warnings?: string[];
 | 
			
		||||
 | 
			
		||||
  @query(".sidebar-editor")
 | 
			
		||||
@@ -50,7 +47,6 @@ export default class HaAutomationSidebarTrigger extends LitElement {
 | 
			
		||||
 | 
			
		||||
  protected willUpdate(changedProperties) {
 | 
			
		||||
    if (changedProperties.has("config")) {
 | 
			
		||||
      this._requestShowId = false;
 | 
			
		||||
      this._warnings = undefined;
 | 
			
		||||
      if (this.config) {
 | 
			
		||||
        this.yamlMode = this.config.yamlMode;
 | 
			
		||||
@@ -105,24 +101,6 @@ export default class HaAutomationSidebarTrigger extends LitElement {
 | 
			
		||||
          </div>
 | 
			
		||||
        </ha-md-menu-item>
 | 
			
		||||
 | 
			
		||||
        ${!this.yamlMode &&
 | 
			
		||||
        !("id" in this.config.config) &&
 | 
			
		||||
        !this._requestShowId
 | 
			
		||||
          ? html`<ha-md-menu-item
 | 
			
		||||
              slot="menu-items"
 | 
			
		||||
              .clickAction=${this._showTriggerId}
 | 
			
		||||
              .disabled=${this.disabled || type === "list"}
 | 
			
		||||
            >
 | 
			
		||||
              <ha-svg-icon slot="start" .path=${mdiIdentifier}></ha-svg-icon>
 | 
			
		||||
              <div class="overflow-label">
 | 
			
		||||
                ${this.hass.localize(
 | 
			
		||||
                  "ui.panel.config.automation.editor.triggers.edit_id"
 | 
			
		||||
                )}
 | 
			
		||||
                <span class="shortcut-placeholder ${isMac ? "mac" : ""}"></span>
 | 
			
		||||
              </div>
 | 
			
		||||
            </ha-md-menu-item>`
 | 
			
		||||
          : nothing}
 | 
			
		||||
 | 
			
		||||
        <ha-md-divider
 | 
			
		||||
          slot="menu-items"
 | 
			
		||||
          role="separator"
 | 
			
		||||
@@ -272,7 +250,6 @@ export default class HaAutomationSidebarTrigger extends LitElement {
 | 
			
		||||
            @value-changed=${this._valueChangedSidebar}
 | 
			
		||||
            @yaml-changed=${this._yamlChangedSidebar}
 | 
			
		||||
            .uiSupported=${this.config.uiSupported}
 | 
			
		||||
            .showId=${this._requestShowId}
 | 
			
		||||
            .yamlMode=${this.yamlMode}
 | 
			
		||||
            .disabled=${this.disabled}
 | 
			
		||||
            @ui-mode-not-available=${this._handleUiModeNotAvailable}
 | 
			
		||||
@@ -315,10 +292,6 @@ export default class HaAutomationSidebarTrigger extends LitElement {
 | 
			
		||||
    fireEvent(this, "toggle-yaml-mode");
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  private _showTriggerId = () => {
 | 
			
		||||
    this._requestShowId = true;
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  static styles = [sidebarEditorStyles, overflowStyles];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -29,8 +29,6 @@ export default class HaAutomationTriggerEditor extends LitElement {
 | 
			
		||||
 | 
			
		||||
  @property({ type: Boolean, attribute: "sidebar" }) public inSidebar = false;
 | 
			
		||||
 | 
			
		||||
  @property({ type: Boolean, attribute: "show-id" }) public showId = false;
 | 
			
		||||
 | 
			
		||||
  @query("ha-yaml-editor") public yamlEditor?: HaYamlEditor;
 | 
			
		||||
 | 
			
		||||
  protected render() {
 | 
			
		||||
@@ -38,8 +36,6 @@ export default class HaAutomationTriggerEditor extends LitElement {
 | 
			
		||||
 | 
			
		||||
    const yamlMode = this.yamlMode || !this.uiSupported;
 | 
			
		||||
 | 
			
		||||
    const showId = "id" in this.trigger || this.showId;
 | 
			
		||||
 | 
			
		||||
    return html`
 | 
			
		||||
      <div
 | 
			
		||||
        class=${classMap({
 | 
			
		||||
@@ -74,15 +70,20 @@ export default class HaAutomationTriggerEditor extends LitElement {
 | 
			
		||||
              ></ha-yaml-editor>
 | 
			
		||||
            `
 | 
			
		||||
          : html`
 | 
			
		||||
              ${showId && !isTriggerList(this.trigger)
 | 
			
		||||
              ${!isTriggerList(this.trigger)
 | 
			
		||||
                ? html`
 | 
			
		||||
                    <ha-textfield
 | 
			
		||||
                      .label=${this.hass.localize(
 | 
			
		||||
                      .label=${`${this.hass.localize(
 | 
			
		||||
                        "ui.panel.config.automation.editor.triggers.id"
 | 
			
		||||
                      )}
 | 
			
		||||
                      )} (${this.hass.localize(
 | 
			
		||||
                        "ui.panel.config.automation.editor.triggers.optional"
 | 
			
		||||
                      )})`}
 | 
			
		||||
                      .value=${this.trigger.id || ""}
 | 
			
		||||
                      .disabled=${this.disabled}
 | 
			
		||||
                      @change=${this._idChanged}
 | 
			
		||||
                      .helper=${this.hass.localize(
 | 
			
		||||
                        "ui.panel.config.automation.editor.triggers.id_helper"
 | 
			
		||||
                      )}
 | 
			
		||||
                    ></ha-textfield>
 | 
			
		||||
                  `
 | 
			
		||||
                : nothing}
 | 
			
		||||
 
 | 
			
		||||
@@ -19,7 +19,6 @@ import { ensureArray } from "../../../../../common/array/ensure-array";
 | 
			
		||||
import { fireEvent } from "../../../../../common/dom/fire_event";
 | 
			
		||||
import { hasTemplate } from "../../../../../common/string/has-template";
 | 
			
		||||
import type { StateTrigger } from "../../../../../data/automation";
 | 
			
		||||
import { ANY_STATE_VALUE } from "../../../../../components/entity/const";
 | 
			
		||||
import type { HomeAssistant } from "../../../../../types";
 | 
			
		||||
import { baseTriggerStruct, forDictStruct } from "../../structs";
 | 
			
		||||
import type { TriggerElement } from "../ha-automation-trigger-row";
 | 
			
		||||
@@ -37,12 +36,14 @@ const stateTriggerStruct = assign(
 | 
			
		||||
    trigger: literal("state"),
 | 
			
		||||
    entity_id: optional(union([string(), array(string())])),
 | 
			
		||||
    attribute: optional(string()),
 | 
			
		||||
    from: optional(union([nullable(string()), array(string())])),
 | 
			
		||||
    to: optional(union([nullable(string()), array(string())])),
 | 
			
		||||
    from: optional(nullable(string())),
 | 
			
		||||
    to: optional(nullable(string())),
 | 
			
		||||
    for: optional(union([number(), string(), forDictStruct])),
 | 
			
		||||
  })
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
const ANY_STATE_VALUE = "__ANY_STATE_IGNORE_ATTRIBUTES__";
 | 
			
		||||
 | 
			
		||||
@customElement("ha-automation-trigger-state")
 | 
			
		||||
export class HaStateTrigger extends LitElement implements TriggerElement {
 | 
			
		||||
  @property({ attribute: false }) public hass!: HomeAssistant;
 | 
			
		||||
@@ -56,12 +57,7 @@ export class HaStateTrigger extends LitElement implements TriggerElement {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private _schema = memoizeOne(
 | 
			
		||||
    (
 | 
			
		||||
      localize: LocalizeFunc,
 | 
			
		||||
      attribute: string | undefined,
 | 
			
		||||
      hideInFrom: string[],
 | 
			
		||||
      hideInTo: string[]
 | 
			
		||||
    ) =>
 | 
			
		||||
    (localize: LocalizeFunc, attribute) =>
 | 
			
		||||
      [
 | 
			
		||||
        {
 | 
			
		||||
          name: "entity_id",
 | 
			
		||||
@@ -135,7 +131,6 @@ export class HaStateTrigger extends LitElement implements TriggerElement {
 | 
			
		||||
          },
 | 
			
		||||
          selector: {
 | 
			
		||||
            state: {
 | 
			
		||||
              multiple: true,
 | 
			
		||||
              extra_options: (attribute
 | 
			
		||||
                ? []
 | 
			
		||||
                : [
 | 
			
		||||
@@ -147,7 +142,6 @@ export class HaStateTrigger extends LitElement implements TriggerElement {
 | 
			
		||||
                    },
 | 
			
		||||
                  ]) as any,
 | 
			
		||||
              attribute: attribute,
 | 
			
		||||
              hide_states: hideInFrom,
 | 
			
		||||
            },
 | 
			
		||||
          },
 | 
			
		||||
        },
 | 
			
		||||
@@ -158,7 +152,6 @@ export class HaStateTrigger extends LitElement implements TriggerElement {
 | 
			
		||||
          },
 | 
			
		||||
          selector: {
 | 
			
		||||
            state: {
 | 
			
		||||
              multiple: true,
 | 
			
		||||
              extra_options: (attribute
 | 
			
		||||
                ? []
 | 
			
		||||
                : [
 | 
			
		||||
@@ -170,7 +163,6 @@ export class HaStateTrigger extends LitElement implements TriggerElement {
 | 
			
		||||
                    },
 | 
			
		||||
                  ]) as any,
 | 
			
		||||
              attribute: attribute,
 | 
			
		||||
              hide_states: hideInTo,
 | 
			
		||||
            },
 | 
			
		||||
          },
 | 
			
		||||
        },
 | 
			
		||||
@@ -215,15 +207,13 @@ export class HaStateTrigger extends LitElement implements TriggerElement {
 | 
			
		||||
      entity_id: ensureArray(this.trigger.entity_id),
 | 
			
		||||
      for: trgFor,
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    data.to = this._normalizeStates(this.trigger.to, data.attribute);
 | 
			
		||||
    data.from = this._normalizeStates(this.trigger.from, data.attribute);
 | 
			
		||||
    const schema = this._schema(
 | 
			
		||||
      this.hass.localize,
 | 
			
		||||
      this.trigger.attribute,
 | 
			
		||||
      data.to,
 | 
			
		||||
      data.from
 | 
			
		||||
    );
 | 
			
		||||
    if (!data.attribute && data.to === null) {
 | 
			
		||||
      data.to = ANY_STATE_VALUE;
 | 
			
		||||
    }
 | 
			
		||||
    if (!data.attribute && data.from === null) {
 | 
			
		||||
      data.from = ANY_STATE_VALUE;
 | 
			
		||||
    }
 | 
			
		||||
    const schema = this._schema(this.hass.localize, this.trigger.attribute);
 | 
			
		||||
 | 
			
		||||
    return html`
 | 
			
		||||
      <ha-form
 | 
			
		||||
@@ -241,60 +231,22 @@ export class HaStateTrigger extends LitElement implements TriggerElement {
 | 
			
		||||
    ev.stopPropagation();
 | 
			
		||||
    const newTrigger = ev.detail.value;
 | 
			
		||||
 | 
			
		||||
    newTrigger.to = this._applyAnyStateExclusive(
 | 
			
		||||
      newTrigger.to,
 | 
			
		||||
      newTrigger.attribute
 | 
			
		||||
    );
 | 
			
		||||
    if (Array.isArray(newTrigger.to) && newTrigger.to.length === 0) {
 | 
			
		||||
      delete newTrigger.to;
 | 
			
		||||
    if (newTrigger.to === ANY_STATE_VALUE) {
 | 
			
		||||
      newTrigger.to = newTrigger.attribute ? undefined : null;
 | 
			
		||||
    }
 | 
			
		||||
    newTrigger.from = this._applyAnyStateExclusive(
 | 
			
		||||
      newTrigger.from,
 | 
			
		||||
      newTrigger.attribute
 | 
			
		||||
    );
 | 
			
		||||
    if (Array.isArray(newTrigger.from) && newTrigger.from.length === 0) {
 | 
			
		||||
      delete newTrigger.from;
 | 
			
		||||
    if (newTrigger.from === ANY_STATE_VALUE) {
 | 
			
		||||
      newTrigger.from = newTrigger.attribute ? undefined : null;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    Object.keys(newTrigger).forEach((key) => {
 | 
			
		||||
      const val = newTrigger[key];
 | 
			
		||||
      if (val === undefined || val === "") {
 | 
			
		||||
        delete newTrigger[key];
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
    Object.keys(newTrigger).forEach((key) =>
 | 
			
		||||
      newTrigger[key] === undefined || newTrigger[key] === ""
 | 
			
		||||
        ? delete newTrigger[key]
 | 
			
		||||
        : {}
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    fireEvent(this, "value-changed", { value: newTrigger });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private _applyAnyStateExclusive(
 | 
			
		||||
    val: string | string[] | null | undefined,
 | 
			
		||||
    attribute?: string
 | 
			
		||||
  ): string | string[] | null | undefined {
 | 
			
		||||
    const anyStateSelected = Array.isArray(val)
 | 
			
		||||
      ? val.includes(ANY_STATE_VALUE)
 | 
			
		||||
      : val === ANY_STATE_VALUE;
 | 
			
		||||
    if (anyStateSelected) {
 | 
			
		||||
      // Any state is exclusive: null if no attribute, undefined if attribute
 | 
			
		||||
      return attribute ? undefined : null;
 | 
			
		||||
    }
 | 
			
		||||
    return val;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private _normalizeStates(
 | 
			
		||||
    value: string | string[] | null | undefined,
 | 
			
		||||
    attribute?: string
 | 
			
		||||
  ): string[] {
 | 
			
		||||
    // If no attribute is selected and backend value is null,
 | 
			
		||||
    // expose it as the special ANY state option in the UI.
 | 
			
		||||
    if (!attribute && value === null) {
 | 
			
		||||
      return [ANY_STATE_VALUE];
 | 
			
		||||
    }
 | 
			
		||||
    if (value === undefined || value === null) {
 | 
			
		||||
      return [];
 | 
			
		||||
    }
 | 
			
		||||
    return ensureArray(value);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private _computeLabelCallback = (
 | 
			
		||||
    schema: SchemaUnion<ReturnType<typeof this._schema>>
 | 
			
		||||
  ): string =>
 | 
			
		||||
 
 | 
			
		||||
@@ -228,7 +228,7 @@ export class HaDeviceEntitiesCard extends LitElement {
 | 
			
		||||
    addEntitiesToLovelaceView(
 | 
			
		||||
      this,
 | 
			
		||||
      this.hass,
 | 
			
		||||
      computeCards(this.hass, entities, {
 | 
			
		||||
      computeCards(this.hass.states, entities, {
 | 
			
		||||
        title: this.deviceName,
 | 
			
		||||
      }),
 | 
			
		||||
      computeSection(entities, {
 | 
			
		||||
 
 | 
			
		||||
@@ -5,7 +5,8 @@ import { fireEvent } from "../../../../common/dom/fire_event";
 | 
			
		||||
import { computeDeviceNameDisplay } from "../../../../common/entity/compute_device_name";
 | 
			
		||||
import "../../../../components/ha-alert";
 | 
			
		||||
import "../../../../components/ha-area-picker";
 | 
			
		||||
import "../../../../components/ha-dialog";
 | 
			
		||||
import "../../../../components/ha-wa-dialog";
 | 
			
		||||
import "../../../../components/ha-dialog-footer";
 | 
			
		||||
import "../../../../components/ha-button";
 | 
			
		||||
import "../../../../components/ha-labels-picker";
 | 
			
		||||
import type { HaSwitch } from "../../../../components/ha-switch";
 | 
			
		||||
@@ -19,6 +20,8 @@ import type { DeviceRegistryDetailDialogParams } from "./show-dialog-device-regi
 | 
			
		||||
class DialogDeviceRegistryDetail extends LitElement {
 | 
			
		||||
  @property({ attribute: false }) public hass!: HomeAssistant;
 | 
			
		||||
 | 
			
		||||
  @state() private _open = false;
 | 
			
		||||
 | 
			
		||||
  @state() private _nameByUser!: string;
 | 
			
		||||
 | 
			
		||||
  @state() private _error?: string;
 | 
			
		||||
@@ -42,10 +45,15 @@ class DialogDeviceRegistryDetail extends LitElement {
 | 
			
		||||
    this._areaId = this._params.device.area_id || "";
 | 
			
		||||
    this._labels = this._params.device.labels || [];
 | 
			
		||||
    this._disabledBy = this._params.device.disabled_by;
 | 
			
		||||
    this._open = true;
 | 
			
		||||
    await this.updateComplete;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public closeDialog(): void {
 | 
			
		||||
    this._open = false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private _dialogClosed(): void {
 | 
			
		||||
    this._error = "";
 | 
			
		||||
    this._params = undefined;
 | 
			
		||||
    fireEvent(this, "dialog-closed", { dialog: this.localName });
 | 
			
		||||
@@ -57,10 +65,12 @@ class DialogDeviceRegistryDetail extends LitElement {
 | 
			
		||||
    }
 | 
			
		||||
    const device = this._params.device;
 | 
			
		||||
    return html`
 | 
			
		||||
      <ha-dialog
 | 
			
		||||
        open
 | 
			
		||||
        @closed=${this.closeDialog}
 | 
			
		||||
        .heading=${computeDeviceNameDisplay(device, this.hass)}
 | 
			
		||||
      <ha-wa-dialog
 | 
			
		||||
        .hass=${this.hass}
 | 
			
		||||
        .open=${this._open}
 | 
			
		||||
        header-title=${computeDeviceNameDisplay(device, this.hass)}
 | 
			
		||||
        prevent-scrim-close
 | 
			
		||||
        @closed=${this._dialogClosed}
 | 
			
		||||
      >
 | 
			
		||||
        <div>
 | 
			
		||||
          ${this._error
 | 
			
		||||
@@ -68,6 +78,7 @@ class DialogDeviceRegistryDetail extends LitElement {
 | 
			
		||||
            : ""}
 | 
			
		||||
          <div class="form">
 | 
			
		||||
            <ha-textfield
 | 
			
		||||
              autofocus
 | 
			
		||||
              .value=${this._nameByUser}
 | 
			
		||||
              @input=${this._nameChanged}
 | 
			
		||||
              .label=${this.hass.localize(
 | 
			
		||||
@@ -75,7 +86,6 @@ class DialogDeviceRegistryDetail extends LitElement {
 | 
			
		||||
              )}
 | 
			
		||||
              .placeholder=${device.name || ""}
 | 
			
		||||
              .disabled=${this._submitting}
 | 
			
		||||
              dialogInitialFocus
 | 
			
		||||
            ></ha-textfield>
 | 
			
		||||
            <ha-area-picker
 | 
			
		||||
              .hass=${this.hass}
 | 
			
		||||
@@ -131,22 +141,25 @@ class DialogDeviceRegistryDetail extends LitElement {
 | 
			
		||||
            </div>
 | 
			
		||||
          </div>
 | 
			
		||||
        </div>
 | 
			
		||||
        <ha-button
 | 
			
		||||
          slot="secondaryAction"
 | 
			
		||||
          @click=${this.closeDialog}
 | 
			
		||||
          .disabled=${this._submitting}
 | 
			
		||||
          appearance="plain"
 | 
			
		||||
        >
 | 
			
		||||
          ${this.hass.localize("ui.common.cancel")}
 | 
			
		||||
        </ha-button>
 | 
			
		||||
        <ha-button
 | 
			
		||||
          slot="primaryAction"
 | 
			
		||||
          @click=${this._updateEntry}
 | 
			
		||||
          .disabled=${this._submitting}
 | 
			
		||||
        >
 | 
			
		||||
          ${this.hass.localize("ui.dialogs.device-registry-detail.update")}
 | 
			
		||||
        </ha-button>
 | 
			
		||||
      </ha-dialog>
 | 
			
		||||
 | 
			
		||||
        <ha-dialog-footer slot="footer">
 | 
			
		||||
          <ha-button
 | 
			
		||||
            slot="secondaryAction"
 | 
			
		||||
            @click=${this.closeDialog}
 | 
			
		||||
            .disabled=${this._submitting}
 | 
			
		||||
            appearance="plain"
 | 
			
		||||
          >
 | 
			
		||||
            ${this.hass.localize("ui.common.cancel")}
 | 
			
		||||
          </ha-button>
 | 
			
		||||
          <ha-button
 | 
			
		||||
            slot="primaryAction"
 | 
			
		||||
            @click=${this._updateEntry}
 | 
			
		||||
            .disabled=${this._submitting}
 | 
			
		||||
          >
 | 
			
		||||
            ${this.hass.localize("ui.dialogs.device-registry-detail.update")}
 | 
			
		||||
          </ha-button>
 | 
			
		||||
        </ha-dialog-footer>
 | 
			
		||||
      </ha-wa-dialog>
 | 
			
		||||
    `;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -280,11 +280,10 @@ ${type === "object"
 | 
			
		||||
 | 
			
		||||
        .content.horizontal {
 | 
			
		||||
          --code-mirror-max-height: calc(
 | 
			
		||||
            100vh - var(--header-height) -
 | 
			
		||||
              (var(--ha-line-height-normal) * var(--ha-font-size-m) * 3) -
 | 
			
		||||
              (max(16px, var(--safe-area-inset-top)) * 2) -
 | 
			
		||||
            100vh - var(--header-height) - (var(--ha-line-height-normal) * 3) -
 | 
			
		||||
              (1em * 2) - (max(16px, var(--safe-area-inset-top)) * 2) -
 | 
			
		||||
              (max(16px, var(--safe-area-inset-bottom)) * 2) -
 | 
			
		||||
              (var(--ha-card-border-width, 1px) * 3) - (1em * 2) - 192px
 | 
			
		||||
              (var(--ha-card-border-width, 1px) * 2) - 179px
 | 
			
		||||
          );
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -313,14 +313,9 @@ class HaPanelHistory extends LitElement {
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const statsStartDate = new Date(this._startDate);
 | 
			
		||||
    // History uses the end datapoint of the statistic, so if we want the
 | 
			
		||||
    // graph to start at 7AM, need to fetch the statistic from 6AM.
 | 
			
		||||
    statsStartDate.setHours(statsStartDate.getHours() - 1);
 | 
			
		||||
 | 
			
		||||
    const statistics = await fetchStatistics(
 | 
			
		||||
      this.hass!,
 | 
			
		||||
      statsStartDate,
 | 
			
		||||
      this._startDate,
 | 
			
		||||
      this._endDate,
 | 
			
		||||
      statisticIds,
 | 
			
		||||
      "hour",
 | 
			
		||||
 
 | 
			
		||||
@@ -5,7 +5,6 @@ import {
 | 
			
		||||
  generateEntityFilter,
 | 
			
		||||
  type EntityFilter,
 | 
			
		||||
} from "../../../common/entity/entity_filter";
 | 
			
		||||
import { floorDefaultIcon } from "../../../components/ha-floor-icon";
 | 
			
		||||
import type { LovelaceCardConfig } from "../../../data/lovelace/config/card";
 | 
			
		||||
import type { LovelaceSectionRawConfig } from "../../../data/lovelace/config/section";
 | 
			
		||||
import type { LovelaceViewConfig } from "../../../data/lovelace/config/view";
 | 
			
		||||
@@ -99,7 +98,6 @@ export class LightViewStrategy extends ReactiveElement {
 | 
			
		||||
              floorCount > 1
 | 
			
		||||
                ? floor.name
 | 
			
		||||
                : hass.localize("ui.panel.lovelace.strategy.home.areas"),
 | 
			
		||||
            icon: floor.icon || floorDefaultIcon(floor),
 | 
			
		||||
          },
 | 
			
		||||
        ],
 | 
			
		||||
      };
 | 
			
		||||
 
 | 
			
		||||
@@ -1,126 +0,0 @@
 | 
			
		||||
import { html, LitElement, nothing } from "lit";
 | 
			
		||||
import { customElement, property, state } from "lit/decorators";
 | 
			
		||||
import { computeDomain } from "../../../common/entity/compute_domain";
 | 
			
		||||
import { supportsFeature } from "../../../common/entity/supports-feature";
 | 
			
		||||
import "../../../components/ha-control-number-buttons";
 | 
			
		||||
import { isUnavailableState } from "../../../data/entity";
 | 
			
		||||
import {
 | 
			
		||||
  MediaPlayerEntityFeature,
 | 
			
		||||
  type MediaPlayerEntity,
 | 
			
		||||
} from "../../../data/media-player";
 | 
			
		||||
import type { HomeAssistant } from "../../../types";
 | 
			
		||||
import type { LovelaceCardFeature, LovelaceCardFeatureEditor } from "../types";
 | 
			
		||||
import { cardFeatureStyles } from "./common/card-feature-styles";
 | 
			
		||||
import type {
 | 
			
		||||
  LovelaceCardFeatureContext,
 | 
			
		||||
  MediaPlayerVolumeButtonsCardFeatureConfig,
 | 
			
		||||
} from "./types";
 | 
			
		||||
import { clamp } from "../../../common/number/clamp";
 | 
			
		||||
 | 
			
		||||
export const supportsMediaPlayerVolumeButtonsCardFeature = (
 | 
			
		||||
  hass: HomeAssistant,
 | 
			
		||||
  context: LovelaceCardFeatureContext
 | 
			
		||||
) => {
 | 
			
		||||
  const stateObj = context.entity_id
 | 
			
		||||
    ? hass.states[context.entity_id]
 | 
			
		||||
    : undefined;
 | 
			
		||||
  if (!stateObj) return false;
 | 
			
		||||
  const domain = computeDomain(stateObj.entity_id);
 | 
			
		||||
  return (
 | 
			
		||||
    domain === "media_player" &&
 | 
			
		||||
    supportsFeature(stateObj, MediaPlayerEntityFeature.VOLUME_SET)
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@customElement("hui-media-player-volume-buttons-card-feature")
 | 
			
		||||
class HuiMediaPlayerVolumeButtonsCardFeature
 | 
			
		||||
  extends LitElement
 | 
			
		||||
  implements LovelaceCardFeature
 | 
			
		||||
{
 | 
			
		||||
  @property({ attribute: false }) public hass?: HomeAssistant;
 | 
			
		||||
 | 
			
		||||
  @property({ attribute: false }) public context?: LovelaceCardFeatureContext;
 | 
			
		||||
 | 
			
		||||
  @state() private _config?: MediaPlayerVolumeButtonsCardFeatureConfig;
 | 
			
		||||
 | 
			
		||||
  private get _stateObj() {
 | 
			
		||||
    if (!this.hass || !this.context || !this.context.entity_id) {
 | 
			
		||||
      return undefined;
 | 
			
		||||
    }
 | 
			
		||||
    return this.hass.states[this.context.entity_id] as
 | 
			
		||||
      | MediaPlayerEntity
 | 
			
		||||
      | undefined;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  static getStubConfig(): MediaPlayerVolumeButtonsCardFeatureConfig {
 | 
			
		||||
    return {
 | 
			
		||||
      type: "media-player-volume-buttons",
 | 
			
		||||
      step: 5,
 | 
			
		||||
    };
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public static async getConfigElement(): Promise<LovelaceCardFeatureEditor> {
 | 
			
		||||
    await import(
 | 
			
		||||
      "../editor/config-elements/hui-media-player-volume-buttons-card-feature-editor"
 | 
			
		||||
    );
 | 
			
		||||
    return document.createElement(
 | 
			
		||||
      "hui-media-player-volume-buttons-card-feature-editor"
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public setConfig(config: MediaPlayerVolumeButtonsCardFeatureConfig): void {
 | 
			
		||||
    if (!config) {
 | 
			
		||||
      throw new Error("Invalid configuration");
 | 
			
		||||
    }
 | 
			
		||||
    this._config = config;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  protected render() {
 | 
			
		||||
    if (
 | 
			
		||||
      !this._config ||
 | 
			
		||||
      !this.hass ||
 | 
			
		||||
      !this.context ||
 | 
			
		||||
      !this._stateObj ||
 | 
			
		||||
      !supportsMediaPlayerVolumeButtonsCardFeature(this.hass, this.context)
 | 
			
		||||
    ) {
 | 
			
		||||
      return nothing;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const position =
 | 
			
		||||
      this._stateObj.attributes.volume_level != null
 | 
			
		||||
        ? Math.round(this._stateObj.attributes.volume_level * 100)
 | 
			
		||||
        : undefined;
 | 
			
		||||
 | 
			
		||||
    return html`
 | 
			
		||||
      <ha-control-number-buttons
 | 
			
		||||
        .disabled=${!this._stateObj || isUnavailableState(this._stateObj.state)}
 | 
			
		||||
        .locale=${this.hass.locale}
 | 
			
		||||
        min="0"
 | 
			
		||||
        max="100"
 | 
			
		||||
        .step=${this._config.step ?? 5}
 | 
			
		||||
        .value=${position}
 | 
			
		||||
        unit="%"
 | 
			
		||||
        @value-changed=${this._valueChanged}
 | 
			
		||||
      ></ha-control-number-buttons>
 | 
			
		||||
    `;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private _valueChanged(ev: CustomEvent) {
 | 
			
		||||
    ev.stopPropagation();
 | 
			
		||||
 | 
			
		||||
    this.hass!.callService("media_player", "volume_set", {
 | 
			
		||||
      entity_id: this._stateObj!.entity_id,
 | 
			
		||||
      volume_level: clamp(ev.detail.value, 0, 100) / 100,
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  static get styles() {
 | 
			
		||||
    return cardFeatureStyles;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
declare global {
 | 
			
		||||
  interface HTMLElementTagNameMap {
 | 
			
		||||
    "hui-media-player-volume-buttons-card-feature": HuiMediaPlayerVolumeButtonsCardFeature;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@@ -94,7 +94,7 @@ class HuiHistoryChartCardFeature
 | 
			
		||||
    }
 | 
			
		||||
    if (!this._coordinates) {
 | 
			
		||||
      return html`
 | 
			
		||||
        <div class="container loading">
 | 
			
		||||
        <div class="container">
 | 
			
		||||
          <ha-spinner size="small"></ha-spinner>
 | 
			
		||||
        </div>
 | 
			
		||||
      `;
 | 
			
		||||
@@ -153,14 +153,6 @@ class HuiHistoryChartCardFeature
 | 
			
		||||
      align-items: flex-end;
 | 
			
		||||
      pointer-events: none !important;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .container.loading {
 | 
			
		||||
      width: 100%;
 | 
			
		||||
      display: flex;
 | 
			
		||||
      justify-content: center;
 | 
			
		||||
      align-items: center;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    hui-graph-base {
 | 
			
		||||
      width: 100%;
 | 
			
		||||
      --accent-color: var(--feature-color);
 | 
			
		||||
 
 | 
			
		||||
@@ -50,11 +50,6 @@ export interface MediaPlayerVolumeSliderCardFeatureConfig {
 | 
			
		||||
  type: "media-player-volume-slider";
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface MediaPlayerVolumeButtonsCardFeatureConfig {
 | 
			
		||||
  type: "media-player-volume-buttons";
 | 
			
		||||
  step?: number;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface FanDirectionCardFeatureConfig {
 | 
			
		||||
  type: "fan-direction";
 | 
			
		||||
}
 | 
			
		||||
@@ -257,7 +252,6 @@ export type LovelaceCardFeatureConfig =
 | 
			
		||||
  | LockCommandsCardFeatureConfig
 | 
			
		||||
  | LockOpenDoorCardFeatureConfig
 | 
			
		||||
  | MediaPlayerPlaybackCardFeatureConfig
 | 
			
		||||
  | MediaPlayerVolumeButtonsCardFeatureConfig
 | 
			
		||||
  | MediaPlayerVolumeSliderCardFeatureConfig
 | 
			
		||||
  | NumericInputCardFeatureConfig
 | 
			
		||||
  | SelectOptionsCardFeatureConfig
 | 
			
		||||
 
 | 
			
		||||
@@ -35,8 +35,6 @@ import { measureTextWidth } from "../../../../util/text";
 | 
			
		||||
import "../../../../components/ha-icon-button";
 | 
			
		||||
import { storage } from "../../../../common/decorators/storage";
 | 
			
		||||
import { listenMediaQuery } from "../../../../common/dom/media_query";
 | 
			
		||||
import { getEnergyColor } from "./common/color";
 | 
			
		||||
import type { CustomLegendOption } from "../../../../components/chart/ha-chart-base";
 | 
			
		||||
 | 
			
		||||
@customElement("hui-energy-devices-graph-card")
 | 
			
		||||
export class HuiEnergyDevicesGraphCard
 | 
			
		||||
@@ -51,8 +49,6 @@ export class HuiEnergyDevicesGraphCard
 | 
			
		||||
 | 
			
		||||
  @state() private _data?: EnergyData;
 | 
			
		||||
 | 
			
		||||
  @state() private _legendData: NonNullable<CustomLegendOption["data"]> = [];
 | 
			
		||||
 | 
			
		||||
  @state()
 | 
			
		||||
  @storage({
 | 
			
		||||
    key: "energy-devices-graph-chart-type",
 | 
			
		||||
@@ -61,14 +57,6 @@ export class HuiEnergyDevicesGraphCard
 | 
			
		||||
  })
 | 
			
		||||
  private _chartType: "bar" | "pie" = "bar";
 | 
			
		||||
 | 
			
		||||
  @state()
 | 
			
		||||
  @storage({
 | 
			
		||||
    key: "energy-devices-pie-hidden-stats",
 | 
			
		||||
    state: true,
 | 
			
		||||
    subscribe: false,
 | 
			
		||||
  })
 | 
			
		||||
  private _hiddenStats: string[] = [];
 | 
			
		||||
 | 
			
		||||
  @state() private _isMobile = false;
 | 
			
		||||
 | 
			
		||||
  private _compoundStats: string[] = [];
 | 
			
		||||
@@ -133,16 +121,10 @@ export class HuiEnergyDevicesGraphCard
 | 
			
		||||
          <ha-chart-base
 | 
			
		||||
            .hass=${this.hass}
 | 
			
		||||
            .data=${this._chartData}
 | 
			
		||||
            .options=${this._createOptions(
 | 
			
		||||
              this._chartData,
 | 
			
		||||
              this._chartType,
 | 
			
		||||
              this._legendData
 | 
			
		||||
            )}
 | 
			
		||||
            .height=${`${Math.max(300, (this._legendData?.length || 0) * 28 + 50)}px`}
 | 
			
		||||
            .extraComponents=${[PieChart]}
 | 
			
		||||
            .options=${this._createOptions(this._chartData, this._chartType)}
 | 
			
		||||
            .height=${`${Math.max(300, (this._chartData[0]?.data?.length || 0) * 28 + 50)}px`}
 | 
			
		||||
            @chart-click=${this._handleChartClick}
 | 
			
		||||
            @dataset-hidden=${this._datasetHidden}
 | 
			
		||||
            @dataset-unhidden=${this._datasetUnhidden}
 | 
			
		||||
            .extraComponents=${[PieChart]}
 | 
			
		||||
          ></ha-chart-base>
 | 
			
		||||
        </div>
 | 
			
		||||
      </ha-card>
 | 
			
		||||
@@ -163,8 +145,7 @@ export class HuiEnergyDevicesGraphCard
 | 
			
		||||
  private _createOptions = memoizeOne(
 | 
			
		||||
    (
 | 
			
		||||
      data: (BarSeriesOption | PieSeriesOption)[],
 | 
			
		||||
      chartType: "bar" | "pie",
 | 
			
		||||
      legendData: typeof this._legendData
 | 
			
		||||
      chartType: "bar" | "pie"
 | 
			
		||||
    ): ECOption => {
 | 
			
		||||
      const options: ECOption = {
 | 
			
		||||
        grid: {
 | 
			
		||||
@@ -180,7 +161,6 @@ export class HuiEnergyDevicesGraphCard
 | 
			
		||||
        },
 | 
			
		||||
        xAxis: { show: false },
 | 
			
		||||
        yAxis: { show: false },
 | 
			
		||||
        legend: { type: "custom", show: false },
 | 
			
		||||
      };
 | 
			
		||||
      if (chartType === "bar") {
 | 
			
		||||
        options.xAxis = {
 | 
			
		||||
@@ -211,18 +191,6 @@ export class HuiEnergyDevicesGraphCard
 | 
			
		||||
            ),
 | 
			
		||||
          },
 | 
			
		||||
        };
 | 
			
		||||
      } else {
 | 
			
		||||
        options.legend = {
 | 
			
		||||
          type: "custom",
 | 
			
		||||
          show: true,
 | 
			
		||||
          data: legendData,
 | 
			
		||||
          selected: legendData
 | 
			
		||||
            .filter((d) => d.id && this._hiddenStats.includes(d.id))
 | 
			
		||||
            .reduce((acc, d) => {
 | 
			
		||||
              acc[d.id!] = false;
 | 
			
		||||
              return acc;
 | 
			
		||||
            }, {}),
 | 
			
		||||
        };
 | 
			
		||||
      }
 | 
			
		||||
      return options;
 | 
			
		||||
    }
 | 
			
		||||
@@ -386,12 +354,23 @@ export class HuiEnergyDevicesGraphCard
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    if (this._chartType === "pie") {
 | 
			
		||||
      const { summedData, compareSummedData } = getSummedData(energyData);
 | 
			
		||||
      const { consumption, compareConsumption } = computeConsumptionData(
 | 
			
		||||
        summedData,
 | 
			
		||||
        compareSummedData
 | 
			
		||||
    chartData.sort((a: any, b: any) => b.value[0] - a.value[0]);
 | 
			
		||||
    if (compareData) {
 | 
			
		||||
      datasets[1].data = chartData.map((d) =>
 | 
			
		||||
        chartDataCompare.find((d2) => (d2 as any).id === d.id)
 | 
			
		||||
      ) as typeof chartDataCompare;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    datasets.forEach((dataset) => {
 | 
			
		||||
      dataset.data!.length = Math.min(
 | 
			
		||||
        this._config?.max_devices || Infinity,
 | 
			
		||||
        dataset.data!.length
 | 
			
		||||
      );
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    if (this._chartType === "pie") {
 | 
			
		||||
      const { summedData } = getSummedData(energyData);
 | 
			
		||||
      const { consumption } = computeConsumptionData(summedData);
 | 
			
		||||
      const totalUsed = consumption.total.used_total;
 | 
			
		||||
      const showUntracked =
 | 
			
		||||
        "from_grid" in summedData ||
 | 
			
		||||
@@ -401,47 +380,6 @@ export class HuiEnergyDevicesGraphCard
 | 
			
		||||
        ? totalUsed -
 | 
			
		||||
          chartData.reduce((acc: number, d: any) => acc + d.value[0], 0)
 | 
			
		||||
        : 0;
 | 
			
		||||
      if (untracked > 0) {
 | 
			
		||||
        const color = getEnergyColor(
 | 
			
		||||
          computedStyle,
 | 
			
		||||
          this.hass.themes.darkMode,
 | 
			
		||||
          false,
 | 
			
		||||
          false,
 | 
			
		||||
          "--history-unknown-color"
 | 
			
		||||
        );
 | 
			
		||||
        chartData.push({
 | 
			
		||||
          id: "untracked",
 | 
			
		||||
          value: [untracked, "untracked"] as any,
 | 
			
		||||
          name: this.hass.localize(
 | 
			
		||||
            "ui.panel.lovelace.cards.energy.energy_devices_graph.untracked_consumption"
 | 
			
		||||
          ),
 | 
			
		||||
          itemStyle: {
 | 
			
		||||
            color: color + "7F",
 | 
			
		||||
            borderColor: color,
 | 
			
		||||
          },
 | 
			
		||||
        });
 | 
			
		||||
        if (compareData) {
 | 
			
		||||
          const compareUntracked =
 | 
			
		||||
            compareConsumption!.total.used_total -
 | 
			
		||||
            chartDataCompare.reduce(
 | 
			
		||||
              (acc: number, d: any) => acc + d.value[0],
 | 
			
		||||
              0
 | 
			
		||||
            );
 | 
			
		||||
          if (compareUntracked > 0) {
 | 
			
		||||
            chartDataCompare.push({
 | 
			
		||||
              id: "untracked",
 | 
			
		||||
              value: [compareUntracked, "untracked"] as any,
 | 
			
		||||
              name: this.hass.localize(
 | 
			
		||||
                "ui.panel.lovelace.cards.energy.energy_devices_graph.untracked_consumption"
 | 
			
		||||
              ),
 | 
			
		||||
              itemStyle: {
 | 
			
		||||
                color: color + "32",
 | 
			
		||||
                borderColor: color + "7F",
 | 
			
		||||
              },
 | 
			
		||||
            });
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      datasets.push({
 | 
			
		||||
        type: "pie",
 | 
			
		||||
        radius: ["0%", compareData ? "30%" : "40%"],
 | 
			
		||||
@@ -463,36 +401,17 @@ export class HuiEnergyDevicesGraphCard
 | 
			
		||||
          color: "rgba(0, 0, 0, 0)",
 | 
			
		||||
        },
 | 
			
		||||
        tooltip: {
 | 
			
		||||
          show: false,
 | 
			
		||||
          formatter: () =>
 | 
			
		||||
            untracked > 0
 | 
			
		||||
              ? this.hass.localize(
 | 
			
		||||
                  "ui.panel.lovelace.cards.energy.energy_devices_graph.includes_untracked",
 | 
			
		||||
                  { num: formatNumber(untracked, this.hass.locale) }
 | 
			
		||||
                )
 | 
			
		||||
              : "",
 | 
			
		||||
        },
 | 
			
		||||
      });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    chartData.sort((a: any, b: any) => b.value[0] - a.value[0]);
 | 
			
		||||
    if (
 | 
			
		||||
      this._config?.max_devices &&
 | 
			
		||||
      chartData.length > this._config.max_devices
 | 
			
		||||
    ) {
 | 
			
		||||
      chartData.splice(this._config.max_devices);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    this._legendData = chartData.map((d) => ({
 | 
			
		||||
      ...d,
 | 
			
		||||
      name: this._getDeviceName(d.name),
 | 
			
		||||
    }));
 | 
			
		||||
    // filter out hidden stats in place
 | 
			
		||||
    for (let i = chartData.length - 1; i >= 0; i--) {
 | 
			
		||||
      if (this._hiddenStats.includes((chartData[i] as any).id)) {
 | 
			
		||||
        chartData.splice(i, 1);
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (compareData) {
 | 
			
		||||
      datasets[1].data = chartData.map((d) =>
 | 
			
		||||
        chartDataCompare.find((d2) => (d2 as any).id === d.id)
 | 
			
		||||
      ) as typeof chartDataCompare;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    this._chartData = datasets;
 | 
			
		||||
    await this.updateComplete;
 | 
			
		||||
  }
 | 
			
		||||
@@ -521,18 +440,6 @@ export class HuiEnergyDevicesGraphCard
 | 
			
		||||
    this._getStatistics(this._data!);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private _datasetHidden(ev: CustomEvent<{ id: string }>) {
 | 
			
		||||
    this._hiddenStats = [...this._hiddenStats, ev.detail.id];
 | 
			
		||||
    this._getStatistics(this._data!);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private _datasetUnhidden(ev: CustomEvent<{ id: string }>) {
 | 
			
		||||
    this._hiddenStats = this._hiddenStats.filter(
 | 
			
		||||
      (stat) => stat !== ev.detail.id
 | 
			
		||||
    );
 | 
			
		||||
    this._getStatistics(this._data!);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  static styles = css`
 | 
			
		||||
    .card-header {
 | 
			
		||||
      display: flex;
 | 
			
		||||
 
 | 
			
		||||
@@ -137,7 +137,6 @@ export class HuiCalendarCard extends LitElement implements LovelaceCard {
 | 
			
		||||
          class=${classMap({
 | 
			
		||||
            "is-grid": this.layout === "grid",
 | 
			
		||||
            "is-panel": this.layout === "panel",
 | 
			
		||||
            "has-title": !!this._config.title,
 | 
			
		||||
          })}
 | 
			
		||||
          .narrow=${this._narrow}
 | 
			
		||||
          .events=${this._events}
 | 
			
		||||
@@ -230,7 +229,6 @@ export class HuiCalendarCard extends LitElement implements LovelaceCard {
 | 
			
		||||
      padding: 0 8px 8px;
 | 
			
		||||
      box-sizing: border-box;
 | 
			
		||||
      height: 100%;
 | 
			
		||||
      overflow: hidden;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .header {
 | 
			
		||||
@@ -241,25 +239,15 @@ export class HuiCalendarCard extends LitElement implements LovelaceCard {
 | 
			
		||||
      padding-left: 8px;
 | 
			
		||||
      padding-inline-start: 8px;
 | 
			
		||||
      direction: var(--direction);
 | 
			
		||||
      white-space: nowrap;
 | 
			
		||||
      text-overflow: ellipsis;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ha-full-calendar {
 | 
			
		||||
      --calendar-height: 400px;
 | 
			
		||||
      height: var(--calendar-height);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ha-full-calendar.is-grid,
 | 
			
		||||
    ha-full-calendar.is-panel {
 | 
			
		||||
      --calendar-height: calc(100% - 16px);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    ha-full-calendar.is-grid.has-title,
 | 
			
		||||
    ha-full-calendar.is-panel.has-title {
 | 
			
		||||
      --calendar-height: calc(
 | 
			
		||||
        100% - var(--ha-card-header-font-size, var(--ha-font-size-2xl)) - 22px
 | 
			
		||||
      );
 | 
			
		||||
      height: calc(100% - 16px);
 | 
			
		||||
    }
 | 
			
		||||
  `;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -5,6 +5,7 @@ import { classMap } from "lit/directives/class-map";
 | 
			
		||||
import { ifDefined } from "lit/directives/if-defined";
 | 
			
		||||
import { applyThemesOnElement } from "../../../common/dom/apply_themes_on_element";
 | 
			
		||||
import { computeDomain } from "../../../common/entity/compute_domain";
 | 
			
		||||
import { computeStateName } from "../../../common/entity/compute_state_name";
 | 
			
		||||
import "../../../components/entity/state-badge";
 | 
			
		||||
import "../../../components/ha-card";
 | 
			
		||||
import "../../../components/ha-icon";
 | 
			
		||||
@@ -18,7 +19,6 @@ import type {
 | 
			
		||||
import { SENSOR_DEVICE_CLASS_TIMESTAMP } from "../../../data/sensor";
 | 
			
		||||
import type { HomeAssistant } from "../../../types";
 | 
			
		||||
import { actionHandler } from "../common/directives/action-handler-directive";
 | 
			
		||||
import { computeLovelaceEntityName } from "../common/entity/compute-lovelace-entity-name";
 | 
			
		||||
import { findEntities } from "../common/find-entities";
 | 
			
		||||
import { handleAction } from "../common/handle-action";
 | 
			
		||||
import { hasAction, hasAnyAction } from "../common/has-action";
 | 
			
		||||
@@ -252,11 +252,7 @@ export class HuiGlanceCard extends LitElement implements LovelaceCard {
 | 
			
		||||
      </div>`;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const name = computeLovelaceEntityName(
 | 
			
		||||
      this.hass!,
 | 
			
		||||
      stateObj,
 | 
			
		||||
      entityConf.name
 | 
			
		||||
    );
 | 
			
		||||
    const name = entityConf.name ?? computeStateName(stateObj);
 | 
			
		||||
 | 
			
		||||
    return html`
 | 
			
		||||
      <div
 | 
			
		||||
 
 | 
			
		||||
@@ -3,27 +3,25 @@ import { LitElement, css, html, nothing } from "lit";
 | 
			
		||||
import { customElement, property, state } from "lit/decorators";
 | 
			
		||||
import { classMap } from "lit/directives/class-map";
 | 
			
		||||
import { isComponentLoaded } from "../../../common/config/is_component_loaded";
 | 
			
		||||
import { createSearchParam } from "../../../common/url/search-params";
 | 
			
		||||
import "../../../components/chart/state-history-charts";
 | 
			
		||||
import "../../../components/ha-alert";
 | 
			
		||||
import "../../../components/ha-card";
 | 
			
		||||
import "../../../components/ha-icon-next";
 | 
			
		||||
import {
 | 
			
		||||
  computeHistory,
 | 
			
		||||
  convertStatisticsToHistory,
 | 
			
		||||
  mergeHistoryResults,
 | 
			
		||||
  subscribeHistoryStatesTimeWindow,
 | 
			
		||||
  type HistoryResult,
 | 
			
		||||
  convertStatisticsToHistory,
 | 
			
		||||
  mergeHistoryResults,
 | 
			
		||||
} from "../../../data/history";
 | 
			
		||||
import { fetchStatistics } from "../../../data/recorder";
 | 
			
		||||
import { getSensorNumericDeviceClasses } from "../../../data/sensor";
 | 
			
		||||
import type { HomeAssistant } from "../../../types";
 | 
			
		||||
import { computeLovelaceEntityName } from "../common/entity/compute-lovelace-entity-name";
 | 
			
		||||
import { hasConfigOrEntitiesChanged } from "../common/has-changed";
 | 
			
		||||
import { processConfigEntities } from "../common/process-config-entities";
 | 
			
		||||
import type { EntityConfig } from "../entity-rows/types";
 | 
			
		||||
import type { LovelaceCard, LovelaceGridOptions } from "../types";
 | 
			
		||||
import type { HistoryGraphCardConfig } from "./types";
 | 
			
		||||
import { createSearchParam } from "../../../common/url/search-params";
 | 
			
		||||
import { fetchStatistics } from "../../../data/recorder";
 | 
			
		||||
 | 
			
		||||
export const DEFAULT_HOURS_TO_SHOW = 24;
 | 
			
		||||
 | 
			
		||||
@@ -53,8 +51,6 @@ export class HuiHistoryGraphCard extends LitElement implements LovelaceCard {
 | 
			
		||||
 | 
			
		||||
  private _entityIds: string[] = [];
 | 
			
		||||
 | 
			
		||||
  private _entities: EntityConfig[] = [];
 | 
			
		||||
 | 
			
		||||
  private _hoursToShow = DEFAULT_HOURS_TO_SHOW;
 | 
			
		||||
 | 
			
		||||
  private _interval?: number;
 | 
			
		||||
@@ -84,35 +80,21 @@ export class HuiHistoryGraphCard extends LitElement implements LovelaceCard {
 | 
			
		||||
      throw new Error("You must include at least one entity");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    this._entities = config.entities
 | 
			
		||||
    const configEntities = config.entities
 | 
			
		||||
      ? processConfigEntities(config.entities)
 | 
			
		||||
      : [];
 | 
			
		||||
    this._entityIds = this._entities.map((entity) => entity.entity);
 | 
			
		||||
 | 
			
		||||
    this._entityIds = [];
 | 
			
		||||
    configEntities.forEach((entity) => {
 | 
			
		||||
      this._entityIds.push(entity.entity);
 | 
			
		||||
      if (entity.name) {
 | 
			
		||||
        this._names[entity.entity] = entity.name;
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    this._hoursToShow = config.hours_to_show || DEFAULT_HOURS_TO_SHOW;
 | 
			
		||||
 | 
			
		||||
    this._config = config;
 | 
			
		||||
    this._computeNames();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private _computeNames() {
 | 
			
		||||
    if (!this.hass || !this._config) {
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
    this._names = {};
 | 
			
		||||
    this._entities.forEach((entity) => {
 | 
			
		||||
      const stateObj = this.hass!.states[entity.entity];
 | 
			
		||||
      this._names[entity.entity] = stateObj
 | 
			
		||||
        ? computeLovelaceEntityName(this.hass!, stateObj, entity.name)
 | 
			
		||||
        : entity.entity;
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public willUpdate(changedProps: PropertyValues) {
 | 
			
		||||
    super.willUpdate(changedProps);
 | 
			
		||||
    if (changedProps.has("hass")) {
 | 
			
		||||
      this._computeNames();
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public connectedCallback() {
 | 
			
		||||
@@ -180,7 +162,7 @@ export class HuiHistoryGraphCard extends LitElement implements LovelaceCard {
 | 
			
		||||
  private async _fetchStatistics(sensorNumericDeviceClasses: string[]) {
 | 
			
		||||
    const now = new Date();
 | 
			
		||||
    const start = new Date();
 | 
			
		||||
    start.setHours(start.getHours() - this._hoursToShow - 1);
 | 
			
		||||
    start.setHours(start.getHours() - this._hoursToShow);
 | 
			
		||||
 | 
			
		||||
    const statistics = await fetchStatistics(
 | 
			
		||||
      this.hass!,
 | 
			
		||||
 
 | 
			
		||||
@@ -14,11 +14,7 @@ import { findEntities } from "../common/find-entities";
 | 
			
		||||
import { processConfigEntities } from "../common/process-config-entities";
 | 
			
		||||
import "../components/hui-warning";
 | 
			
		||||
import type { EntityConfig } from "../entity-rows/types";
 | 
			
		||||
import type {
 | 
			
		||||
  LovelaceCard,
 | 
			
		||||
  LovelaceCardEditor,
 | 
			
		||||
  LovelaceGridOptions,
 | 
			
		||||
} from "../types";
 | 
			
		||||
import type { LovelaceCard, LovelaceCardEditor } from "../types";
 | 
			
		||||
import type { LogbookCardConfig } from "./types";
 | 
			
		||||
import { resolveEntityIDs } from "../../../data/selector";
 | 
			
		||||
import { ensureArray } from "../../../common/array/ensure-array";
 | 
			
		||||
@@ -68,15 +64,6 @@ export class HuiLogbookCard extends LitElement implements LovelaceCard {
 | 
			
		||||
    return 9 + (this._config?.title ? 1 : 0);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public getGridOptions(): LovelaceGridOptions {
 | 
			
		||||
    return {
 | 
			
		||||
      rows: 6,
 | 
			
		||||
      columns: 12,
 | 
			
		||||
      min_columns: 6,
 | 
			
		||||
      min_rows: this._config?.title ? 4 : 3,
 | 
			
		||||
    };
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public validateTarget(
 | 
			
		||||
    config: LogbookCardConfig
 | 
			
		||||
  ): HassServiceTarget | undefined {
 | 
			
		||||
@@ -202,10 +189,6 @@ export class HuiLogbookCard extends LitElement implements LovelaceCard {
 | 
			
		||||
      >
 | 
			
		||||
        <div class="content">
 | 
			
		||||
          <ha-logbook
 | 
			
		||||
            class=${classMap({
 | 
			
		||||
              "is-grid": this.layout === "grid",
 | 
			
		||||
              "is-panel": this.layout === "panel",
 | 
			
		||||
            })}
 | 
			
		||||
            .hass=${this.hass}
 | 
			
		||||
            .time=${this._time}
 | 
			
		||||
            .entityIds=${this._getEntityIds()}
 | 
			
		||||
@@ -229,7 +212,6 @@ export class HuiLogbookCard extends LitElement implements LovelaceCard {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        .content {
 | 
			
		||||
          height: 100%;
 | 
			
		||||
          padding: 0 16px 16px;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@@ -242,11 +224,6 @@ export class HuiLogbookCard extends LitElement implements LovelaceCard {
 | 
			
		||||
          display: block;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        ha-logbook.is-grid,
 | 
			
		||||
        ha-logbook.is-panel {
 | 
			
		||||
          height: 100%;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        :host([ispanel]) .content,
 | 
			
		||||
        :host([ispanel]) ha-logbook {
 | 
			
		||||
          height: 100%;
 | 
			
		||||
 
 | 
			
		||||
@@ -35,12 +35,20 @@ import {
 | 
			
		||||
  hasConfigOrEntitiesChanged,
 | 
			
		||||
} from "../common/has-changed";
 | 
			
		||||
import { processConfigEntities } from "../common/process-config-entities";
 | 
			
		||||
import type { EntityConfig } from "../entity-rows/types";
 | 
			
		||||
import type { LovelaceCard, LovelaceGridOptions } from "../types";
 | 
			
		||||
import type { MapCardConfig, MapEntityConfig } from "./types";
 | 
			
		||||
import type { MapCardConfig } from "./types";
 | 
			
		||||
 | 
			
		||||
export const DEFAULT_HOURS_TO_SHOW = 0;
 | 
			
		||||
export const DEFAULT_ZOOM = 14;
 | 
			
		||||
 | 
			
		||||
interface MapEntityConfig extends EntityConfig {
 | 
			
		||||
  label_mode?: "state" | "attribute" | "name";
 | 
			
		||||
  attribute?: string;
 | 
			
		||||
  unit?: string;
 | 
			
		||||
  focus?: boolean;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
interface GeoEntity {
 | 
			
		||||
  entity_id: string;
 | 
			
		||||
  label_mode?: "state" | "attribute" | "name" | "icon";
 | 
			
		||||
 
 | 
			
		||||
@@ -20,15 +20,14 @@ import {
 | 
			
		||||
} from "../../../data/recorder";
 | 
			
		||||
import type { HomeAssistant } from "../../../types";
 | 
			
		||||
import { computeCardSize } from "../common/compute-card-size";
 | 
			
		||||
import { computeLovelaceEntityName } from "../common/entity/compute-lovelace-entity-name";
 | 
			
		||||
import { findEntities } from "../common/find-entities";
 | 
			
		||||
import { hasConfigOrEntityChanged } from "../common/has-changed";
 | 
			
		||||
import { createHeaderFooterElement } from "../create-element/create-header-footer-element";
 | 
			
		||||
import type {
 | 
			
		||||
  LovelaceCard,
 | 
			
		||||
  LovelaceCardEditor,
 | 
			
		||||
  LovelaceGridOptions,
 | 
			
		||||
  LovelaceHeaderFooter,
 | 
			
		||||
  LovelaceGridOptions,
 | 
			
		||||
} from "../types";
 | 
			
		||||
import type { HuiErrorCard } from "./hui-error-card";
 | 
			
		||||
import type { EntityCardConfig, StatisticCardConfig } from "./types";
 | 
			
		||||
@@ -181,9 +180,7 @@ export class HuiStatisticCard extends LitElement implements LovelaceCard {
 | 
			
		||||
 | 
			
		||||
    const stateObj = this.hass.states[this._config.entity];
 | 
			
		||||
    const name =
 | 
			
		||||
      (this._config.name
 | 
			
		||||
        ? computeLovelaceEntityName(this.hass, stateObj, this._config.name)
 | 
			
		||||
        : "") ||
 | 
			
		||||
      this._config.name ||
 | 
			
		||||
      getStatisticLabel(this.hass, this._config.entity, this._metadata);
 | 
			
		||||
 | 
			
		||||
    return html`
 | 
			
		||||
 
 | 
			
		||||
@@ -1,11 +1,15 @@
 | 
			
		||||
import { differenceInDays, subHours } from "date-fns";
 | 
			
		||||
import type { HassEntity, UnsubscribeFunc } from "home-assistant-js-websocket";
 | 
			
		||||
import { subHours, differenceInDays } from "date-fns";
 | 
			
		||||
import type { PropertyValues } from "lit";
 | 
			
		||||
import { css, html, LitElement, nothing } from "lit";
 | 
			
		||||
import { customElement, property, state } from "lit/decorators";
 | 
			
		||||
import { classMap } from "lit/directives/class-map";
 | 
			
		||||
import "../../../components/ha-card";
 | 
			
		||||
import { getEnergyDataCollection } from "../../../data/energy";
 | 
			
		||||
import {
 | 
			
		||||
  getSuggestedMax,
 | 
			
		||||
  getSuggestedPeriod,
 | 
			
		||||
} from "./energy/common/energy-chart-options";
 | 
			
		||||
import type {
 | 
			
		||||
  Statistics,
 | 
			
		||||
  StatisticsMetaData,
 | 
			
		||||
@@ -17,16 +21,10 @@ import {
 | 
			
		||||
  getStatisticMetadata,
 | 
			
		||||
} from "../../../data/recorder";
 | 
			
		||||
import type { HomeAssistant } from "../../../types";
 | 
			
		||||
import { computeLovelaceEntityName } from "../common/entity/compute-lovelace-entity-name";
 | 
			
		||||
import { findEntities } from "../common/find-entities";
 | 
			
		||||
import { hasConfigOrEntitiesChanged } from "../common/has-changed";
 | 
			
		||||
import { processConfigEntities } from "../common/process-config-entities";
 | 
			
		||||
import type { EntityConfig } from "../entity-rows/types";
 | 
			
		||||
import type { LovelaceCard, LovelaceGridOptions } from "../types";
 | 
			
		||||
import {
 | 
			
		||||
  getSuggestedMax,
 | 
			
		||||
  getSuggestedPeriod,
 | 
			
		||||
} from "./energy/common/energy-chart-options";
 | 
			
		||||
import type { StatisticsGraphCardConfig } from "./types";
 | 
			
		||||
 | 
			
		||||
export const DEFAULT_DAYS_TO_SHOW = 30;
 | 
			
		||||
@@ -69,9 +67,7 @@ export class HuiStatisticsGraphCard extends LitElement implements LovelaceCard {
 | 
			
		||||
 | 
			
		||||
  @state() private _unit?: string;
 | 
			
		||||
 | 
			
		||||
  private _entities: EntityConfig[] = [];
 | 
			
		||||
 | 
			
		||||
  private _entityIds: string[] = [];
 | 
			
		||||
  private _entities: string[] = [];
 | 
			
		||||
 | 
			
		||||
  private _names: Record<string, string> = {};
 | 
			
		||||
 | 
			
		||||
@@ -152,10 +148,17 @@ export class HuiStatisticsGraphCard extends LitElement implements LovelaceCard {
 | 
			
		||||
      throw new Error("You must include at least one entity");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    this._entities = config.entities
 | 
			
		||||
    const configEntities = config.entities
 | 
			
		||||
      ? processConfigEntities(config.entities, false)
 | 
			
		||||
      : [];
 | 
			
		||||
    this._entityIds = this._entities.map((ent) => ent.entity);
 | 
			
		||||
 | 
			
		||||
    this._entities = [];
 | 
			
		||||
    configEntities.forEach((entity) => {
 | 
			
		||||
      this._entities.push(entity.entity);
 | 
			
		||||
      if (entity.name) {
 | 
			
		||||
        this._names[entity.entity] = entity.name;
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    if (typeof config.stat_types === "string") {
 | 
			
		||||
      this._statTypes = [config.stat_types];
 | 
			
		||||
@@ -165,20 +168,6 @@ export class HuiStatisticsGraphCard extends LitElement implements LovelaceCard {
 | 
			
		||||
      this._statTypes = config.stat_types;
 | 
			
		||||
    }
 | 
			
		||||
    this._config = config;
 | 
			
		||||
    this._computeNames();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private _computeNames() {
 | 
			
		||||
    if (!this.hass || !this._config) {
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
    this._names = {};
 | 
			
		||||
    this._entities.forEach((config) => {
 | 
			
		||||
      const stateObj = this.hass!.states[config.entity];
 | 
			
		||||
      this._names[config.entity] = stateObj
 | 
			
		||||
        ? computeLovelaceEntityName(this.hass!, stateObj, config.name)
 | 
			
		||||
        : config.entity;
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  protected shouldUpdate(changedProps: PropertyValues): boolean {
 | 
			
		||||
@@ -220,10 +209,6 @@ export class HuiStatisticsGraphCard extends LitElement implements LovelaceCard {
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (changedProps.has("hass")) {
 | 
			
		||||
      this._computeNames();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (
 | 
			
		||||
      changedProps.has("_config") &&
 | 
			
		||||
      oldConfig?.entities !== this._config.entities
 | 
			
		||||
@@ -247,7 +232,7 @@ export class HuiStatisticsGraphCard extends LitElement implements LovelaceCard {
 | 
			
		||||
    clearInterval(this._interval);
 | 
			
		||||
    this._interval = 0; // block concurrent calls
 | 
			
		||||
    if (fetchMetadata) {
 | 
			
		||||
      await this._getStatisticsMetaData(this._entityIds);
 | 
			
		||||
      await this._getStatisticsMetaData(this._entities);
 | 
			
		||||
    }
 | 
			
		||||
    await this._getStatistics();
 | 
			
		||||
    // statistics are created every hour
 | 
			
		||||
@@ -359,7 +344,7 @@ export class HuiStatisticsGraphCard extends LitElement implements LovelaceCard {
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      if (!unitClass && this._metadata) {
 | 
			
		||||
        const metadata = this._metadata[this._entityIds[0]];
 | 
			
		||||
        const metadata = this._metadata[this._entities[0]];
 | 
			
		||||
        unitClass = metadata?.unit_class;
 | 
			
		||||
        this._unit = unitClass
 | 
			
		||||
          ? getDisplayUnit(this.hass!, metadata.statistic_id, metadata) ||
 | 
			
		||||
@@ -371,15 +356,14 @@ export class HuiStatisticsGraphCard extends LitElement implements LovelaceCard {
 | 
			
		||||
        this.hass!,
 | 
			
		||||
        startDate,
 | 
			
		||||
        endDate,
 | 
			
		||||
        this._entityIds,
 | 
			
		||||
        this._entities,
 | 
			
		||||
        this._period,
 | 
			
		||||
        unitconfig,
 | 
			
		||||
        this._statTypes
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
      this._statistics = {};
 | 
			
		||||
      this._entities.forEach((entity) => {
 | 
			
		||||
        const id = entity.entity;
 | 
			
		||||
      this._entities.forEach((id) => {
 | 
			
		||||
        if (id in statistics) {
 | 
			
		||||
          this._statistics![id] = statistics[id];
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -5,7 +5,6 @@ import type { EnergySourceByType } from "../../../data/energy";
 | 
			
		||||
import type { ActionConfig } from "../../../data/lovelace/config/action";
 | 
			
		||||
import type { LovelaceCardConfig } from "../../../data/lovelace/config/card";
 | 
			
		||||
import type { Statistic, StatisticType } from "../../../data/recorder";
 | 
			
		||||
import type { MediaSelectorValue } from "../../../data/selector";
 | 
			
		||||
import type { TimeFormat } from "../../../data/translation";
 | 
			
		||||
import type { ForecastType } from "../../../data/weather";
 | 
			
		||||
import type {
 | 
			
		||||
@@ -30,6 +29,7 @@ import type {
 | 
			
		||||
import type { LovelaceHeaderFooterConfig } from "../header-footer/types";
 | 
			
		||||
import type { LovelaceHeadingBadgeConfig } from "../heading-badges/types";
 | 
			
		||||
import type { HomeSummary } from "../strategies/home/helpers/home-summaries";
 | 
			
		||||
import type { MediaSelectorValue } from "../../../data/selector";
 | 
			
		||||
 | 
			
		||||
export type AlarmPanelCardConfigState =
 | 
			
		||||
  | "arm_away"
 | 
			
		||||
@@ -347,15 +347,7 @@ export interface LogbookCardConfig extends LovelaceCardConfig {
 | 
			
		||||
  theme?: string;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface MapEntityConfig extends EntityConfig {
 | 
			
		||||
  label_mode?: "state" | "attribute" | "name";
 | 
			
		||||
  attribute?: string;
 | 
			
		||||
  unit?: string;
 | 
			
		||||
  focus?: boolean;
 | 
			
		||||
  name?: string;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface GeoLocationSourceConfig {
 | 
			
		||||
interface GeoLocationSourceConfig {
 | 
			
		||||
  source: string;
 | 
			
		||||
  label_mode?: "name" | "state" | "attribute" | "icon";
 | 
			
		||||
  attribute?: string;
 | 
			
		||||
@@ -370,7 +362,7 @@ export interface MapCardConfig extends LovelaceCardConfig {
 | 
			
		||||
  auto_fit?: boolean;
 | 
			
		||||
  fit_zones?: boolean;
 | 
			
		||||
  default_zoom?: number;
 | 
			
		||||
  entities?: (MapEntityConfig | string)[];
 | 
			
		||||
  entities?: (EntityConfig | string)[];
 | 
			
		||||
  hours_to_show?: number;
 | 
			
		||||
  geo_location_sources?: (GeoLocationSourceConfig | string)[];
 | 
			
		||||
  dark_mode?: boolean;
 | 
			
		||||
@@ -442,7 +434,7 @@ export interface StatisticsGraphCardConfig extends EnergyCardBaseConfig {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface StatisticCardConfig extends LovelaceCardConfig {
 | 
			
		||||
  name?: string | EntityNameItem | EntityNameItem[];
 | 
			
		||||
  name?: string;
 | 
			
		||||
  entities: (EntityConfig | string)[];
 | 
			
		||||
  period:
 | 
			
		||||
    | {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,29 +1,28 @@
 | 
			
		||||
import type { HassEntity } from "home-assistant-js-websocket";
 | 
			
		||||
import { ensureArray } from "../../../../common/array/ensure-array";
 | 
			
		||||
import type { EntityNameItem } from "../../../../common/entity/compute_entity_name_display";
 | 
			
		||||
import { computeStateName } from "../../../../common/entity/compute_state_name";
 | 
			
		||||
import {
 | 
			
		||||
  DEFAULT_ENTITY_NAME,
 | 
			
		||||
  type EntityNameItem,
 | 
			
		||||
} from "../../../../common/entity/compute_entity_name_display";
 | 
			
		||||
import type { HomeAssistant } from "../../../../types";
 | 
			
		||||
import { ensureArray } from "../../../../common/array/ensure-array";
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Computes the display name for an entity in Lovelace (cards and badges).
 | 
			
		||||
 *
 | 
			
		||||
 * @param hass - The Home Assistant instance
 | 
			
		||||
 * @param stateObj - The entity state object
 | 
			
		||||
 * @param config - The name configuration (string for override, or EntityNameItem[] for structured naming)
 | 
			
		||||
 * @param nameConfig - The name configuration (string for override, or EntityNameItem[] for structured naming)
 | 
			
		||||
 * @returns The computed entity name
 | 
			
		||||
 */
 | 
			
		||||
export const computeLovelaceEntityName = (
 | 
			
		||||
  hass: HomeAssistant,
 | 
			
		||||
  stateObj: HassEntity | undefined,
 | 
			
		||||
  config: string | EntityNameItem | EntityNameItem[] | undefined
 | 
			
		||||
  nameConfig: string | EntityNameItem | EntityNameItem[] | undefined
 | 
			
		||||
): string => {
 | 
			
		||||
  // If no config is provided, fall back to the default state name
 | 
			
		||||
  if (!config) {
 | 
			
		||||
    return stateObj ? computeStateName(stateObj) : "";
 | 
			
		||||
  }
 | 
			
		||||
  if (typeof config === "string") {
 | 
			
		||||
    return config;
 | 
			
		||||
  if (typeof nameConfig === "string") {
 | 
			
		||||
    return nameConfig;
 | 
			
		||||
  }
 | 
			
		||||
  const config = nameConfig || DEFAULT_ENTITY_NAME;
 | 
			
		||||
  if (stateObj) {
 | 
			
		||||
    return hass.formatEntityName(stateObj, config);
 | 
			
		||||
  }
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
import type { HassEntities, HassEntity } from "home-assistant-js-websocket";
 | 
			
		||||
import { ASSIST_ENTITIES, SENSOR_ENTITIES } from "../../../common/const";
 | 
			
		||||
import { SENSOR_ENTITIES, ASSIST_ENTITIES } from "../../../common/const";
 | 
			
		||||
import { computeDomain } from "../../../common/entity/compute_domain";
 | 
			
		||||
import { computeStateDomain } from "../../../common/entity/compute_state_domain";
 | 
			
		||||
import { computeStateName } from "../../../common/entity/compute_state_name";
 | 
			
		||||
@@ -14,14 +14,12 @@ import type {
 | 
			
		||||
  GridSourceTypeEnergyPreference,
 | 
			
		||||
} from "../../../data/energy";
 | 
			
		||||
import { domainToName } from "../../../data/integration";
 | 
			
		||||
import type { LovelaceBadgeConfig } from "../../../data/lovelace/config/badge";
 | 
			
		||||
import type { LovelaceCardConfig } from "../../../data/lovelace/config/card";
 | 
			
		||||
import type { LovelaceSectionConfig } from "../../../data/lovelace/config/section";
 | 
			
		||||
import type { LovelaceViewConfig } from "../../../data/lovelace/config/view";
 | 
			
		||||
import { computeUserInitials } from "../../../data/user";
 | 
			
		||||
import type { HomeAssistant } from "../../../types";
 | 
			
		||||
import { HELPER_DOMAINS } from "../../config/helpers/const";
 | 
			
		||||
import type { EntityBadgeConfig } from "../badges/types";
 | 
			
		||||
import type {
 | 
			
		||||
  AlarmPanelCardConfig,
 | 
			
		||||
  EntitiesCardConfig,
 | 
			
		||||
@@ -33,7 +31,8 @@ import type {
 | 
			
		||||
} from "../cards/types";
 | 
			
		||||
import type { EntityConfig } from "../entity-rows/types";
 | 
			
		||||
import type { ButtonsHeaderFooterConfig } from "../header-footer/types";
 | 
			
		||||
import { computeLovelaceEntityName } from "./entity/compute-lovelace-entity-name";
 | 
			
		||||
import type { LovelaceBadgeConfig } from "../../../data/lovelace/config/badge";
 | 
			
		||||
import type { EntityBadgeConfig } from "../badges/types";
 | 
			
		||||
 | 
			
		||||
const HIDE_DOMAIN = new Set([
 | 
			
		||||
  "ai_task",
 | 
			
		||||
@@ -126,13 +125,13 @@ export const computeSection = (
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
export const computeCards = (
 | 
			
		||||
  hass: HomeAssistant,
 | 
			
		||||
  states: HassEntities,
 | 
			
		||||
  entityIds: string[],
 | 
			
		||||
  entityCardOptions: Partial<EntitiesCardConfig>,
 | 
			
		||||
  renderFooterEntities = true
 | 
			
		||||
): LovelaceCardConfig[] => {
 | 
			
		||||
  const cards: LovelaceCardConfig[] = [];
 | 
			
		||||
  const states = hass.states;
 | 
			
		||||
 | 
			
		||||
  // For entity card
 | 
			
		||||
  const entitiesConf: (string | EntityConfig)[] = [];
 | 
			
		||||
 | 
			
		||||
@@ -271,23 +270,19 @@ export const computeCards = (
 | 
			
		||||
        ? states[a]
 | 
			
		||||
          ? computeStateName(states[a])
 | 
			
		||||
          : ""
 | 
			
		||||
        : states[a.entity]
 | 
			
		||||
          ? computeLovelaceEntityName(hass, states[a.entity], a.name)
 | 
			
		||||
          : "",
 | 
			
		||||
        : a.name || "",
 | 
			
		||||
      typeof b === "string"
 | 
			
		||||
        ? states[b]
 | 
			
		||||
          ? computeStateName(states[b])
 | 
			
		||||
          : ""
 | 
			
		||||
        : states[b.entity]
 | 
			
		||||
          ? computeLovelaceEntityName(hass, states[b.entity], b.name)
 | 
			
		||||
          : ""
 | 
			
		||||
        : b.name || ""
 | 
			
		||||
    );
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  // If we ended up with footer entities but no normal entities,
 | 
			
		||||
  // render the footer entities as normal entities.
 | 
			
		||||
  if (entitiesConf.length === 0 && footerEntities.length > 0) {
 | 
			
		||||
    return computeCards(hass, entityIds, entityCardOptions, false);
 | 
			
		||||
    return computeCards(states, entityIds, entityCardOptions, false);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (entitiesConf.length > 0 || footerEntities.length > 0) {
 | 
			
		||||
@@ -365,14 +360,14 @@ const computeDefaultViewStates = (
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const generateViewConfig = (
 | 
			
		||||
  hass: HomeAssistant,
 | 
			
		||||
  localize: LocalizeFunc,
 | 
			
		||||
  path: string,
 | 
			
		||||
  title: string | undefined,
 | 
			
		||||
  icon: string | undefined,
 | 
			
		||||
  entities: HassEntities
 | 
			
		||||
): LovelaceViewConfig => {
 | 
			
		||||
  const ungroupedEntitites: Record<string, string[]> = {};
 | 
			
		||||
  const { localize } = hass;
 | 
			
		||||
 | 
			
		||||
  // Organize ungrouped entities in ungrouped things
 | 
			
		||||
  for (const entityId of Object.keys(entities)) {
 | 
			
		||||
    const state = entities[entityId];
 | 
			
		||||
@@ -475,7 +470,7 @@ export const generateViewConfig = (
 | 
			
		||||
    .forEach((domain) => {
 | 
			
		||||
      cards.push(
 | 
			
		||||
        ...computeCards(
 | 
			
		||||
          hass,
 | 
			
		||||
          entities,
 | 
			
		||||
          ungroupedEntitites[domain].sort((a, b) =>
 | 
			
		||||
            stringCompare(
 | 
			
		||||
              computeStateName(entities[a]),
 | 
			
		||||
@@ -503,17 +498,16 @@ export const generateViewConfig = (
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export const generateDefaultViewConfig = (
 | 
			
		||||
  hass: HomeAssistant,
 | 
			
		||||
  areaEntries: HomeAssistant["areas"],
 | 
			
		||||
  deviceEntries: HomeAssistant["devices"],
 | 
			
		||||
  entityEntries: HomeAssistant["entities"],
 | 
			
		||||
  entities: HassEntities,
 | 
			
		||||
  localize: LocalizeFunc,
 | 
			
		||||
  energyPrefs?: EnergyPreferences,
 | 
			
		||||
  areasPrefs?: AreasDisplayValue,
 | 
			
		||||
  hideEntitiesWithoutAreas?: boolean,
 | 
			
		||||
  hideEnergy?: boolean
 | 
			
		||||
): LovelaceViewConfig => {
 | 
			
		||||
  const entities = hass.states;
 | 
			
		||||
  const areaEntries = hass.areas;
 | 
			
		||||
  const deviceEntries = hass.devices;
 | 
			
		||||
  const entityEntries = hass.entities;
 | 
			
		||||
  const states = computeDefaultViewStates(entities, entityEntries);
 | 
			
		||||
  const path = "default_view";
 | 
			
		||||
  const title = "Home";
 | 
			
		||||
@@ -555,7 +549,7 @@ export const generateDefaultViewConfig = (
 | 
			
		||||
 | 
			
		||||
  for (const groupEntity of splittedByGroups.groups) {
 | 
			
		||||
    groupCards.push(
 | 
			
		||||
      ...computeCards(hass, groupEntity.attributes.entity_id, {
 | 
			
		||||
      ...computeCards(entities, groupEntity.attributes.entity_id, {
 | 
			
		||||
        title: computeStateName(groupEntity),
 | 
			
		||||
        show_header_toggle: groupEntity.attributes.control !== "hidden",
 | 
			
		||||
      })
 | 
			
		||||
@@ -563,7 +557,7 @@ export const generateDefaultViewConfig = (
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  const config = generateViewConfig(
 | 
			
		||||
    hass,
 | 
			
		||||
    localize,
 | 
			
		||||
    path,
 | 
			
		||||
    title,
 | 
			
		||||
    icon,
 | 
			
		||||
@@ -581,7 +575,7 @@ export const generateDefaultViewConfig = (
 | 
			
		||||
    const area = areaEntries[areaId];
 | 
			
		||||
    areaCards.push(
 | 
			
		||||
      ...computeCards(
 | 
			
		||||
        hass,
 | 
			
		||||
        entities,
 | 
			
		||||
        areaEntities.map((entity) => entity.entity_id),
 | 
			
		||||
        {
 | 
			
		||||
          title: area.name,
 | 
			
		||||
@@ -607,7 +601,7 @@ export const generateDefaultViewConfig = (
 | 
			
		||||
    const device = deviceEntries[deviceId];
 | 
			
		||||
    deviceCards.push(
 | 
			
		||||
      ...computeCards(
 | 
			
		||||
        hass,
 | 
			
		||||
        entities,
 | 
			
		||||
        deviceEntities.map((entity) => entity.entity_id),
 | 
			
		||||
        {
 | 
			
		||||
          title:
 | 
			
		||||
 
 | 
			
		||||
@@ -2,12 +2,8 @@
 | 
			
		||||
import { isValidEntityId } from "../../../common/entity/valid_entity_id";
 | 
			
		||||
import type { EntityConfig, LovelaceRowConfig } from "../entity-rows/types";
 | 
			
		||||
 | 
			
		||||
interface BaseEntityConfig {
 | 
			
		||||
  type: string;
 | 
			
		||||
  entity: string;
 | 
			
		||||
}
 | 
			
		||||
export const processConfigEntities = <
 | 
			
		||||
  T extends BaseEntityConfig | LovelaceRowConfig,
 | 
			
		||||
  T extends EntityConfig | LovelaceRowConfig,
 | 
			
		||||
>(
 | 
			
		||||
  entities: (T | string)[],
 | 
			
		||||
  checkEntityId = true
 | 
			
		||||
 
 | 
			
		||||
@@ -45,13 +45,14 @@ export class HuiEntityEditor extends LitElement {
 | 
			
		||||
      this.hass.devices
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    const name = this.hass.formatEntityName(
 | 
			
		||||
      stateObj,
 | 
			
		||||
      useDeviceName ? { type: "device" } : { type: "entity" }
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    const isRTL = computeRTL(this.hass);
 | 
			
		||||
 | 
			
		||||
    const primary =
 | 
			
		||||
      this.hass.formatEntityName(
 | 
			
		||||
        stateObj,
 | 
			
		||||
        useDeviceName ? { type: "device" } : { type: "entity" }
 | 
			
		||||
      ) || item.entity;
 | 
			
		||||
    const primary = item.name || name || item.entity;
 | 
			
		||||
 | 
			
		||||
    const secondary = this.hass.formatEntityName(
 | 
			
		||||
      stateObj,
 | 
			
		||||
 
 | 
			
		||||
@@ -4,19 +4,19 @@ import { customElement, property } from "lit/decorators";
 | 
			
		||||
import { classMap } from "lit/directives/class-map";
 | 
			
		||||
import { ifDefined } from "lit/directives/if-defined";
 | 
			
		||||
import { DOMAINS_INPUT_ROW } from "../../../common/const";
 | 
			
		||||
import { stopPropagation } from "../../../common/dom/stop_propagation";
 | 
			
		||||
import { toggleAttribute } from "../../../common/dom/toggle_attribute";
 | 
			
		||||
import { computeDomain } from "../../../common/entity/compute_domain";
 | 
			
		||||
import { computeStateName } from "../../../common/entity/compute_state_name";
 | 
			
		||||
import "../../../components/entity/state-badge";
 | 
			
		||||
import "../../../components/ha-relative-time";
 | 
			
		||||
import type { ActionHandlerEvent } from "../../../data/lovelace/action_handler";
 | 
			
		||||
import type { HomeAssistant } from "../../../types";
 | 
			
		||||
import type { EntitiesCardEntityConfig } from "../cards/types";
 | 
			
		||||
import { actionHandler } from "../common/directives/action-handler-directive";
 | 
			
		||||
import { computeLovelaceEntityName } from "../common/entity/compute-lovelace-entity-name";
 | 
			
		||||
import { handleAction } from "../common/handle-action";
 | 
			
		||||
import { hasAction, hasAnyAction } from "../common/has-action";
 | 
			
		||||
import { createEntityNotFoundWarning } from "./hui-warning";
 | 
			
		||||
import { stopPropagation } from "../../../common/dom/stop_propagation";
 | 
			
		||||
 | 
			
		||||
@customElement("hui-generic-entity-row")
 | 
			
		||||
export class HuiGenericEntityRow extends LitElement {
 | 
			
		||||
@@ -59,11 +59,7 @@ export class HuiGenericEntityRow extends LitElement {
 | 
			
		||||
    const pointer = hasAnyAction(this.config);
 | 
			
		||||
 | 
			
		||||
    const hasSecondary = this.secondaryText || this.config.secondary_info;
 | 
			
		||||
    const name = computeLovelaceEntityName(
 | 
			
		||||
      this.hass,
 | 
			
		||||
      stateObj,
 | 
			
		||||
      this.config.name
 | 
			
		||||
    );
 | 
			
		||||
    const name = this.config.name ?? computeStateName(stateObj);
 | 
			
		||||
 | 
			
		||||
    return html`
 | 
			
		||||
      <div
 | 
			
		||||
@@ -91,7 +87,7 @@ export class HuiGenericEntityRow extends LitElement {
 | 
			
		||||
              class="info ${classMap({ "text-content": !hasSecondary })}"
 | 
			
		||||
              .title=${name}
 | 
			
		||||
            >
 | 
			
		||||
              ${name}
 | 
			
		||||
              ${this.config.name || computeStateName(stateObj)}
 | 
			
		||||
              ${hasSecondary
 | 
			
		||||
                ? html`
 | 
			
		||||
                    <div class="secondary">
 | 
			
		||||
 
 | 
			
		||||
@@ -23,7 +23,6 @@ import "../card-features/hui-light-color-temp-card-feature";
 | 
			
		||||
import "../card-features/hui-lock-commands-card-feature";
 | 
			
		||||
import "../card-features/hui-lock-open-door-card-feature";
 | 
			
		||||
import "../card-features/hui-media-player-playback-card-feature";
 | 
			
		||||
import "../card-features/hui-media-player-volume-buttons-card-feature";
 | 
			
		||||
import "../card-features/hui-media-player-volume-slider-card-feature";
 | 
			
		||||
import "../card-features/hui-numeric-input-card-feature";
 | 
			
		||||
import "../card-features/hui-select-options-card-feature";
 | 
			
		||||
@@ -73,7 +72,6 @@ const TYPES = new Set<LovelaceCardFeatureConfig["type"]>([
 | 
			
		||||
  "lock-commands",
 | 
			
		||||
  "lock-open-door",
 | 
			
		||||
  "media-player-playback",
 | 
			
		||||
  "media-player-volume-buttons",
 | 
			
		||||
  "media-player-volume-slider",
 | 
			
		||||
  "numeric-input",
 | 
			
		||||
  "select-options",
 | 
			
		||||
 
 | 
			
		||||
@@ -296,7 +296,11 @@ export class HuiCreateDialogCard
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private _suggestCards(): void {
 | 
			
		||||
    const cardConfig = computeCards(this.hass, this._selectedEntities, {});
 | 
			
		||||
    const cardConfig = computeCards(
 | 
			
		||||
      this.hass.states,
 | 
			
		||||
      this._selectedEntities,
 | 
			
		||||
      {}
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    let sectionOptions: Partial<LovelaceSectionConfig> = {};
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -4,6 +4,7 @@ import { customElement, property, state } from "lit/decorators";
 | 
			
		||||
import memoizeOne from "memoize-one";
 | 
			
		||||
import { array, assert, assign, object, optional, string } from "superstruct";
 | 
			
		||||
import { fireEvent } from "../../../../common/dom/fire_event";
 | 
			
		||||
import { DEFAULT_ENTITY_NAME } from "../../../../common/entity/compute_entity_name_display";
 | 
			
		||||
import { supportsFeature } from "../../../../common/entity/supports-feature";
 | 
			
		||||
import type { LocalizeFunc } from "../../../../common/translations/localize";
 | 
			
		||||
import "../../../../components/ha-form/ha-form";
 | 
			
		||||
@@ -64,7 +65,9 @@ export class HuiAlarmPanelCardEditor
 | 
			
		||||
        {
 | 
			
		||||
          name: "name",
 | 
			
		||||
          selector: {
 | 
			
		||||
            entity_name: {},
 | 
			
		||||
            entity_name: {
 | 
			
		||||
              default_name: DEFAULT_ENTITY_NAME,
 | 
			
		||||
            },
 | 
			
		||||
          },
 | 
			
		||||
          context: { entity: "entity" },
 | 
			
		||||
        },
 | 
			
		||||
 
 | 
			
		||||
@@ -5,6 +5,7 @@ import { customElement, property, state } from "lit/decorators";
 | 
			
		||||
import memoizeOne from "memoize-one";
 | 
			
		||||
import { assert, assign, boolean, object, optional, string } from "superstruct";
 | 
			
		||||
import { fireEvent } from "../../../../common/dom/fire_event";
 | 
			
		||||
import { DEFAULT_ENTITY_NAME } from "../../../../common/entity/compute_entity_name_display";
 | 
			
		||||
import "../../../../components/ha-form/ha-form";
 | 
			
		||||
import type {
 | 
			
		||||
  HaFormSchema,
 | 
			
		||||
@@ -72,7 +73,7 @@ export class HuiButtonCardEditor
 | 
			
		||||
        {
 | 
			
		||||
          name: "name",
 | 
			
		||||
          selector: {
 | 
			
		||||
            entity_name: {},
 | 
			
		||||
            entity_name: { default_name: DEFAULT_ENTITY_NAME },
 | 
			
		||||
          },
 | 
			
		||||
          context: { entity: "entity" },
 | 
			
		||||
        },
 | 
			
		||||
 
 | 
			
		||||
@@ -48,7 +48,6 @@ import { supportsLightColorTempCardFeature } from "../../card-features/hui-light
 | 
			
		||||
import { supportsLockCommandsCardFeature } from "../../card-features/hui-lock-commands-card-feature";
 | 
			
		||||
import { supportsLockOpenDoorCardFeature } from "../../card-features/hui-lock-open-door-card-feature";
 | 
			
		||||
import { supportsMediaPlayerPlaybackCardFeature } from "../../card-features/hui-media-player-playback-card-feature";
 | 
			
		||||
import { supportsMediaPlayerVolumeButtonsCardFeature } from "../../card-features/hui-media-player-volume-buttons-card-feature";
 | 
			
		||||
import { supportsMediaPlayerVolumeSliderCardFeature } from "../../card-features/hui-media-player-volume-slider-card-feature";
 | 
			
		||||
import { supportsNumericInputCardFeature } from "../../card-features/hui-numeric-input-card-feature";
 | 
			
		||||
import { supportsSelectOptionsCardFeature } from "../../card-features/hui-select-options-card-feature";
 | 
			
		||||
@@ -103,7 +102,6 @@ const UI_FEATURE_TYPES = [
 | 
			
		||||
  "lock-commands",
 | 
			
		||||
  "lock-open-door",
 | 
			
		||||
  "media-player-playback",
 | 
			
		||||
  "media-player-volume-buttons",
 | 
			
		||||
  "media-player-volume-slider",
 | 
			
		||||
  "numeric-input",
 | 
			
		||||
  "select-options",
 | 
			
		||||
@@ -133,7 +131,6 @@ const EDITABLES_FEATURE_TYPES = new Set<UiFeatureTypes>([
 | 
			
		||||
  "fan-preset-modes",
 | 
			
		||||
  "humidifier-modes",
 | 
			
		||||
  "lawn-mower-commands",
 | 
			
		||||
  "media-player-volume-buttons",
 | 
			
		||||
  "numeric-input",
 | 
			
		||||
  "select-options",
 | 
			
		||||
  "trend-graph",
 | 
			
		||||
@@ -174,7 +171,6 @@ const SUPPORTS_FEATURE_TYPES: Record<
 | 
			
		||||
  "lock-commands": supportsLockCommandsCardFeature,
 | 
			
		||||
  "lock-open-door": supportsLockOpenDoorCardFeature,
 | 
			
		||||
  "media-player-playback": supportsMediaPlayerPlaybackCardFeature,
 | 
			
		||||
  "media-player-volume-buttons": supportsMediaPlayerVolumeButtonsCardFeature,
 | 
			
		||||
  "media-player-volume-slider": supportsMediaPlayerVolumeSliderCardFeature,
 | 
			
		||||
  "numeric-input": supportsNumericInputCardFeature,
 | 
			
		||||
  "select-options": supportsSelectOptionsCardFeature,
 | 
			
		||||
 
 | 
			
		||||
@@ -13,6 +13,7 @@ import {
 | 
			
		||||
  string,
 | 
			
		||||
  union,
 | 
			
		||||
} from "superstruct";
 | 
			
		||||
import { DEFAULT_ENTITY_NAME } from "../../../../common/entity/compute_entity_name_display";
 | 
			
		||||
import { fireEvent } from "../../../../common/dom/fire_event";
 | 
			
		||||
import type { LocalizeFunc } from "../../../../common/translations/localize";
 | 
			
		||||
import "../../../../components/ha-form/ha-form";
 | 
			
		||||
@@ -85,7 +86,9 @@ export class HuiEntityBadgeEditor
 | 
			
		||||
            {
 | 
			
		||||
              name: "name",
 | 
			
		||||
              selector: {
 | 
			
		||||
                entity_name: {},
 | 
			
		||||
                entity_name: {
 | 
			
		||||
                  default_name: DEFAULT_ENTITY_NAME,
 | 
			
		||||
                },
 | 
			
		||||
              },
 | 
			
		||||
              context: { entity: "entity" },
 | 
			
		||||
            },
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,5 @@
 | 
			
		||||
import { assert, assign, boolean, object, optional, string } from "superstruct";
 | 
			
		||||
import { DEFAULT_ENTITY_NAME } from "../../../../common/entity/compute_entity_name_display";
 | 
			
		||||
import type { LocalizeFunc } from "../../../../common/translations/localize";
 | 
			
		||||
import type { HaFormSchema } from "../../../../components/ha-form/types";
 | 
			
		||||
import { headerFooterConfigStructs } from "../../header-footer/structs";
 | 
			
		||||
@@ -25,7 +26,9 @@ const SCHEMA = [
 | 
			
		||||
  {
 | 
			
		||||
    name: "name",
 | 
			
		||||
    selector: {
 | 
			
		||||
      entity_name: {},
 | 
			
		||||
      entity_name: {
 | 
			
		||||
        default_name: DEFAULT_ENTITY_NAME,
 | 
			
		||||
      },
 | 
			
		||||
    },
 | 
			
		||||
    context: { entity: "entity" },
 | 
			
		||||
  },
 | 
			
		||||
 
 | 
			
		||||
@@ -14,6 +14,7 @@ import {
 | 
			
		||||
  string,
 | 
			
		||||
} from "superstruct";
 | 
			
		||||
import { fireEvent } from "../../../../common/dom/fire_event";
 | 
			
		||||
import { DEFAULT_ENTITY_NAME } from "../../../../common/entity/compute_entity_name_display";
 | 
			
		||||
import "../../../../components/ha-form/ha-form";
 | 
			
		||||
import type { SchemaUnion } from "../../../../components/ha-form/types";
 | 
			
		||||
import { NON_NUMERIC_ATTRIBUTES } from "../../../../data/entity_attributes";
 | 
			
		||||
@@ -101,7 +102,9 @@ export class HuiGaugeCardEditor
 | 
			
		||||
        {
 | 
			
		||||
          name: "name",
 | 
			
		||||
          selector: {
 | 
			
		||||
            entity_name: {},
 | 
			
		||||
            entity_name: {
 | 
			
		||||
              default_name: DEFAULT_ENTITY_NAME,
 | 
			
		||||
            },
 | 
			
		||||
          },
 | 
			
		||||
          context: { entity: "entity" },
 | 
			
		||||
        },
 | 
			
		||||
 
 | 
			
		||||
@@ -46,18 +46,20 @@ export class HuiGenericEntityRowEditor
 | 
			
		||||
    return [
 | 
			
		||||
      { name: "entity", required: true, selector: { entity: {} } },
 | 
			
		||||
      {
 | 
			
		||||
        name: "name",
 | 
			
		||||
        selector: { entity_name: {} },
 | 
			
		||||
        context: { entity: "entity" },
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        name: "icon",
 | 
			
		||||
        selector: {
 | 
			
		||||
          icon: {},
 | 
			
		||||
        },
 | 
			
		||||
        context: {
 | 
			
		||||
          icon_entity: "entity",
 | 
			
		||||
        },
 | 
			
		||||
        type: "grid",
 | 
			
		||||
        name: "",
 | 
			
		||||
        schema: [
 | 
			
		||||
          { name: "name", selector: { text: {} } },
 | 
			
		||||
          {
 | 
			
		||||
            name: "icon",
 | 
			
		||||
            selector: {
 | 
			
		||||
              icon: {},
 | 
			
		||||
            },
 | 
			
		||||
            context: {
 | 
			
		||||
              icon_entity: "entity",
 | 
			
		||||
            },
 | 
			
		||||
          },
 | 
			
		||||
        ],
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        name: "secondary_info",
 | 
			
		||||
 
 | 
			
		||||
@@ -11,20 +11,20 @@ import {
 | 
			
		||||
  string,
 | 
			
		||||
  union,
 | 
			
		||||
} from "superstruct";
 | 
			
		||||
import type { HASSDomEvent } from "../../../../common/dom/fire_event";
 | 
			
		||||
import { fireEvent } from "../../../../common/dom/fire_event";
 | 
			
		||||
import "../../../../components/ha-form/ha-form";
 | 
			
		||||
import "../hui-sub-element-editor";
 | 
			
		||||
import type { EditDetailElementEvent, SubElementEditorConfig } from "../types";
 | 
			
		||||
import type { HASSDomEvent } from "../../../../common/dom/fire_event";
 | 
			
		||||
import type { SchemaUnion } from "../../../../components/ha-form/types";
 | 
			
		||||
import type { HomeAssistant } from "../../../../types";
 | 
			
		||||
import type { ConfigEntity, GlanceCardConfig } from "../../cards/types";
 | 
			
		||||
import "../../components/hui-entity-editor";
 | 
			
		||||
import type { EntityConfig } from "../../entity-rows/types";
 | 
			
		||||
import type { LovelaceCardEditor } from "../../types";
 | 
			
		||||
import "../hui-sub-element-editor";
 | 
			
		||||
import { processEditorEntities } from "../process-editor-entities";
 | 
			
		||||
import { baseLovelaceCardConfig } from "../structs/base-card-struct";
 | 
			
		||||
import { entitiesConfigStruct } from "../structs/entities-struct";
 | 
			
		||||
import type { EditDetailElementEvent, SubElementEditorConfig } from "../types";
 | 
			
		||||
import type { EntityConfig } from "../../entity-rows/types";
 | 
			
		||||
 | 
			
		||||
const cardConfigStruct = assign(
 | 
			
		||||
  baseLovelaceCardConfig,
 | 
			
		||||
@@ -42,17 +42,11 @@ const cardConfigStruct = assign(
 | 
			
		||||
 | 
			
		||||
const SUB_SCHEMA = [
 | 
			
		||||
  { name: "entity", selector: { entity: {} }, required: true },
 | 
			
		||||
  {
 | 
			
		||||
    name: "name",
 | 
			
		||||
    selector: { entity_name: {} },
 | 
			
		||||
    context: {
 | 
			
		||||
      entity: "entity",
 | 
			
		||||
    },
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    type: "grid",
 | 
			
		||||
    name: "",
 | 
			
		||||
    schema: [
 | 
			
		||||
      { name: "name", selector: { text: {} } },
 | 
			
		||||
      {
 | 
			
		||||
        name: "icon",
 | 
			
		||||
        selector: {
 | 
			
		||||
 
 | 
			
		||||
@@ -45,13 +45,7 @@ const cardConfigStruct = assign(
 | 
			
		||||
 | 
			
		||||
const SUB_SCHEMA = [
 | 
			
		||||
  { name: "entity", selector: { entity: {} }, required: true },
 | 
			
		||||
  {
 | 
			
		||||
    name: "name",
 | 
			
		||||
    selector: { entity_name: {} },
 | 
			
		||||
    context: {
 | 
			
		||||
      entity: "entity",
 | 
			
		||||
    },
 | 
			
		||||
  },
 | 
			
		||||
  { name: "name", selector: { text: {} } },
 | 
			
		||||
] as const;
 | 
			
		||||
 | 
			
		||||
@customElement("hui-history-graph-card-editor")
 | 
			
		||||
 
 | 
			
		||||
@@ -14,6 +14,7 @@ import {
 | 
			
		||||
} from "superstruct";
 | 
			
		||||
import type { HASSDomEvent } from "../../../../common/dom/fire_event";
 | 
			
		||||
import { fireEvent } from "../../../../common/dom/fire_event";
 | 
			
		||||
import { DEFAULT_ENTITY_NAME } from "../../../../common/entity/compute_entity_name_display";
 | 
			
		||||
import "../../../../components/ha-expansion-panel";
 | 
			
		||||
import "../../../../components/ha-form/ha-form";
 | 
			
		||||
import type {
 | 
			
		||||
@@ -60,7 +61,9 @@ const SCHEMA = [
 | 
			
		||||
  {
 | 
			
		||||
    name: "name",
 | 
			
		||||
    selector: {
 | 
			
		||||
      entity_name: {},
 | 
			
		||||
      entity_name: {
 | 
			
		||||
        default_name: DEFAULT_ENTITY_NAME,
 | 
			
		||||
      },
 | 
			
		||||
    },
 | 
			
		||||
    context: { entity: "entity" },
 | 
			
		||||
  },
 | 
			
		||||
 
 | 
			
		||||
@@ -1,9 +1,10 @@
 | 
			
		||||
import { mdiGestureTap } from "@mdi/js";
 | 
			
		||||
import type { CSSResultGroup } from "lit";
 | 
			
		||||
import { html, LitElement, nothing } from "lit";
 | 
			
		||||
import { customElement, property, state } from "lit/decorators";
 | 
			
		||||
import { assert, assign, object, optional, string } from "superstruct";
 | 
			
		||||
import { mdiGestureTap } from "@mdi/js";
 | 
			
		||||
import { fireEvent } from "../../../../common/dom/fire_event";
 | 
			
		||||
import { DEFAULT_ENTITY_NAME } from "../../../../common/entity/compute_entity_name_display";
 | 
			
		||||
import "../../../../components/ha-form/ha-form";
 | 
			
		||||
import type { SchemaUnion } from "../../../../components/ha-form/types";
 | 
			
		||||
import type { HomeAssistant } from "../../../../types";
 | 
			
		||||
@@ -36,7 +37,9 @@ const SCHEMA = [
 | 
			
		||||
  {
 | 
			
		||||
    name: "name",
 | 
			
		||||
    selector: {
 | 
			
		||||
      entity_name: {},
 | 
			
		||||
      entity_name: {
 | 
			
		||||
        default_name: DEFAULT_ENTITY_NAME,
 | 
			
		||||
      },
 | 
			
		||||
    },
 | 
			
		||||
    context: { entity: "entity" },
 | 
			
		||||
  },
 | 
			
		||||
 
 | 
			
		||||
@@ -2,7 +2,6 @@ import { mdiPalette } from "@mdi/js";
 | 
			
		||||
import type { CSSResultGroup } from "lit";
 | 
			
		||||
import { css, html, LitElement, nothing } from "lit";
 | 
			
		||||
import { customElement, property, state } from "lit/decorators";
 | 
			
		||||
import memoizeOne from "memoize-one";
 | 
			
		||||
import {
 | 
			
		||||
  array,
 | 
			
		||||
  assert,
 | 
			
		||||
@@ -14,19 +13,19 @@ import {
 | 
			
		||||
  string,
 | 
			
		||||
  union,
 | 
			
		||||
} from "superstruct";
 | 
			
		||||
import memoizeOne from "memoize-one";
 | 
			
		||||
import { fireEvent } from "../../../../common/dom/fire_event";
 | 
			
		||||
import { computeDomain } from "../../../../common/entity/compute_domain";
 | 
			
		||||
import { hasLocation } from "../../../../common/entity/has_location";
 | 
			
		||||
import type { LocalizeFunc } from "../../../../common/translations/localize";
 | 
			
		||||
import { computeDomain } from "../../../../common/entity/compute_domain";
 | 
			
		||||
import "../../../../components/ha-form/ha-form";
 | 
			
		||||
import type { SchemaUnion } from "../../../../components/ha-form/types";
 | 
			
		||||
import "../../../../components/ha-formfield";
 | 
			
		||||
import "../../../../components/ha-selector/ha-selector-select";
 | 
			
		||||
import "../../../../components/ha-switch";
 | 
			
		||||
import type { SelectSelector } from "../../../../data/selector";
 | 
			
		||||
import "../../../../components/ha-formfield";
 | 
			
		||||
import "../../../../components/ha-switch";
 | 
			
		||||
import "../../../../components/ha-selector/ha-selector-select";
 | 
			
		||||
import type { HomeAssistant, ValueChangedEvent } from "../../../../types";
 | 
			
		||||
import { DEFAULT_HOURS_TO_SHOW, DEFAULT_ZOOM } from "../../cards/hui-map-card";
 | 
			
		||||
import type { MapCardConfig, MapEntityConfig } from "../../cards/types";
 | 
			
		||||
import type { MapCardConfig } from "../../cards/types";
 | 
			
		||||
import "../../components/hui-entity-editor";
 | 
			
		||||
import type { EntityConfig } from "../../entity-rows/types";
 | 
			
		||||
import type { LovelaceCardEditor } from "../../types";
 | 
			
		||||
@@ -34,6 +33,7 @@ import { processEditorEntities } from "../process-editor-entities";
 | 
			
		||||
import { baseLovelaceCardConfig } from "../structs/base-card-struct";
 | 
			
		||||
import type { EntitiesEditorEvent } from "../types";
 | 
			
		||||
import { configElementStyle } from "./config-elements-style";
 | 
			
		||||
import type { LocalizeFunc } from "../../../../common/translations/localize";
 | 
			
		||||
 | 
			
		||||
export const mapEntitiesConfigStruct = union([
 | 
			
		||||
  object({
 | 
			
		||||
@@ -223,9 +223,7 @@ export class HuiMapCardEditor extends LitElement implements LovelaceCardEditor {
 | 
			
		||||
    })
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
  private _entitiesValueChanged(
 | 
			
		||||
    ev: EntitiesEditorEvent<MapEntityConfig>
 | 
			
		||||
  ): void {
 | 
			
		||||
  private _entitiesValueChanged(ev: EntitiesEditorEvent): void {
 | 
			
		||||
    if (ev.detail && ev.detail.entities) {
 | 
			
		||||
      this._config = { ...this._config!, entities: ev.detail.entities };
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -2,6 +2,7 @@ import { html, LitElement, nothing } from "lit";
 | 
			
		||||
import { customElement, property, state } from "lit/decorators";
 | 
			
		||||
import { assert, assign, object, optional, string } from "superstruct";
 | 
			
		||||
import { fireEvent } from "../../../../common/dom/fire_event";
 | 
			
		||||
import { DEFAULT_ENTITY_NAME } from "../../../../common/entity/compute_entity_name_display";
 | 
			
		||||
import "../../../../components/ha-form/ha-form";
 | 
			
		||||
import type {
 | 
			
		||||
  HaFormSchema,
 | 
			
		||||
@@ -32,7 +33,9 @@ const SCHEMA = [
 | 
			
		||||
  {
 | 
			
		||||
    name: "name",
 | 
			
		||||
    selector: {
 | 
			
		||||
      entity_name: {},
 | 
			
		||||
      entity_name: {
 | 
			
		||||
        default_name: DEFAULT_ENTITY_NAME,
 | 
			
		||||
      },
 | 
			
		||||
    },
 | 
			
		||||
    context: { entity: "entity" },
 | 
			
		||||
  },
 | 
			
		||||
 
 | 
			
		||||
@@ -1,86 +0,0 @@
 | 
			
		||||
import { html, LitElement, nothing } from "lit";
 | 
			
		||||
import { customElement, property, state } from "lit/decorators";
 | 
			
		||||
import memoizeOne from "memoize-one";
 | 
			
		||||
import { fireEvent } from "../../../../common/dom/fire_event";
 | 
			
		||||
import "../../../../components/ha-form/ha-form";
 | 
			
		||||
import type { SchemaUnion } from "../../../../components/ha-form/types";
 | 
			
		||||
import type { HomeAssistant } from "../../../../types";
 | 
			
		||||
import type {
 | 
			
		||||
  LovelaceCardFeatureContext,
 | 
			
		||||
  MediaPlayerVolumeButtonsCardFeatureConfig,
 | 
			
		||||
} from "../../card-features/types";
 | 
			
		||||
import type { LovelaceCardFeatureEditor } from "../../types";
 | 
			
		||||
 | 
			
		||||
@customElement("hui-media-player-volume-buttons-card-feature-editor")
 | 
			
		||||
export class HuiMediaPlayerVolumeButtonsCardFeatureEditor
 | 
			
		||||
  extends LitElement
 | 
			
		||||
  implements LovelaceCardFeatureEditor
 | 
			
		||||
{
 | 
			
		||||
  @property({ attribute: false }) public hass?: HomeAssistant;
 | 
			
		||||
 | 
			
		||||
  @property({ attribute: false }) public context?: LovelaceCardFeatureContext;
 | 
			
		||||
 | 
			
		||||
  @state() private _config?: MediaPlayerVolumeButtonsCardFeatureConfig;
 | 
			
		||||
 | 
			
		||||
  public setConfig(config: MediaPlayerVolumeButtonsCardFeatureConfig): void {
 | 
			
		||||
    this._config = config;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private _schema = memoizeOne(
 | 
			
		||||
    () =>
 | 
			
		||||
      [
 | 
			
		||||
        {
 | 
			
		||||
          name: "step",
 | 
			
		||||
          selector: {
 | 
			
		||||
            number: {
 | 
			
		||||
              mode: "slider",
 | 
			
		||||
              step: 1,
 | 
			
		||||
              min: 1,
 | 
			
		||||
              max: 100,
 | 
			
		||||
              unit_of_measurement: "%",
 | 
			
		||||
            },
 | 
			
		||||
          },
 | 
			
		||||
        },
 | 
			
		||||
      ] as const
 | 
			
		||||
  );
 | 
			
		||||
 | 
			
		||||
  protected render() {
 | 
			
		||||
    if (!this.hass || !this._config) {
 | 
			
		||||
      return nothing;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const data: MediaPlayerVolumeButtonsCardFeatureConfig = {
 | 
			
		||||
      type: "media-player-volume-buttons",
 | 
			
		||||
      step: this._config.step ?? 5,
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    const schema = this._schema();
 | 
			
		||||
 | 
			
		||||
    return html`
 | 
			
		||||
      <ha-form
 | 
			
		||||
        .hass=${this.hass}
 | 
			
		||||
        .data=${data}
 | 
			
		||||
        .schema=${schema}
 | 
			
		||||
        .computeLabel=${this._computeLabelCallback}
 | 
			
		||||
        @value-changed=${this._valueChanged}
 | 
			
		||||
      ></ha-form>
 | 
			
		||||
    `;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private _valueChanged(ev: CustomEvent): void {
 | 
			
		||||
    fireEvent(this, "config-changed", { config: ev.detail.value });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private _computeLabelCallback = (
 | 
			
		||||
    schema: SchemaUnion<ReturnType<typeof this._schema>>
 | 
			
		||||
  ) =>
 | 
			
		||||
    this.hass!.localize(
 | 
			
		||||
      `ui.panel.lovelace.editor.features.types.media-player-volume-buttons.${schema.name}`
 | 
			
		||||
    );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
declare global {
 | 
			
		||||
  interface HTMLElementTagNameMap {
 | 
			
		||||
    "hui-media-player-volume-buttons-card-feature-editor": HuiMediaPlayerVolumeButtonsCardFeatureEditor;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,8 +1,8 @@
 | 
			
		||||
import memoizeOne from "memoize-one";
 | 
			
		||||
import { mdiGestureTap } from "@mdi/js";
 | 
			
		||||
import type { CSSResultGroup } from "lit";
 | 
			
		||||
import { html, LitElement, nothing } from "lit";
 | 
			
		||||
import { customElement, property, state } from "lit/decorators";
 | 
			
		||||
import memoizeOne from "memoize-one";
 | 
			
		||||
import {
 | 
			
		||||
  assert,
 | 
			
		||||
  assign,
 | 
			
		||||
@@ -15,6 +15,7 @@ import {
 | 
			
		||||
} from "superstruct";
 | 
			
		||||
import { fireEvent } from "../../../../common/dom/fire_event";
 | 
			
		||||
import { computeDomain } from "../../../../common/entity/compute_domain";
 | 
			
		||||
import { DEFAULT_ENTITY_NAME } from "../../../../common/entity/compute_entity_name_display";
 | 
			
		||||
import type { LocalizeFunc } from "../../../../common/translations/localize";
 | 
			
		||||
import "../../../../components/ha-form/ha-form";
 | 
			
		||||
import type {
 | 
			
		||||
@@ -70,7 +71,9 @@ export class HuiPictureEntityCardEditor
 | 
			
		||||
        {
 | 
			
		||||
          name: "name",
 | 
			
		||||
          selector: {
 | 
			
		||||
            entity_name: {},
 | 
			
		||||
            entity_name: {
 | 
			
		||||
              default_name: DEFAULT_ENTITY_NAME,
 | 
			
		||||
            },
 | 
			
		||||
          },
 | 
			
		||||
          context: { entity: "entity" },
 | 
			
		||||
        },
 | 
			
		||||
 
 | 
			
		||||
@@ -2,6 +2,7 @@ import { html, LitElement, nothing } from "lit";
 | 
			
		||||
import { customElement, property, state } from "lit/decorators";
 | 
			
		||||
import { assert, assign, object, optional, string } from "superstruct";
 | 
			
		||||
import { fireEvent } from "../../../../common/dom/fire_event";
 | 
			
		||||
import { DEFAULT_ENTITY_NAME } from "../../../../common/entity/compute_entity_name_display";
 | 
			
		||||
import "../../../../components/ha-form/ha-form";
 | 
			
		||||
import type { SchemaUnion } from "../../../../components/ha-form/types";
 | 
			
		||||
import type { HomeAssistant } from "../../../../types";
 | 
			
		||||
@@ -24,7 +25,9 @@ const SCHEMA = [
 | 
			
		||||
  {
 | 
			
		||||
    name: "name",
 | 
			
		||||
    selector: {
 | 
			
		||||
      entity_name: {},
 | 
			
		||||
      entity_name: {
 | 
			
		||||
        default_name: DEFAULT_ENTITY_NAME,
 | 
			
		||||
      },
 | 
			
		||||
    },
 | 
			
		||||
    context: { entity: "entity" },
 | 
			
		||||
  },
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,7 @@
 | 
			
		||||
import memoizeOne from "memoize-one";
 | 
			
		||||
import type { CSSResultGroup } from "lit";
 | 
			
		||||
import { html, LitElement, nothing } from "lit";
 | 
			
		||||
import { customElement, property, state } from "lit/decorators";
 | 
			
		||||
import memoizeOne from "memoize-one";
 | 
			
		||||
import {
 | 
			
		||||
  assert,
 | 
			
		||||
  assign,
 | 
			
		||||
@@ -12,17 +12,18 @@ import {
 | 
			
		||||
  string,
 | 
			
		||||
  union,
 | 
			
		||||
} from "superstruct";
 | 
			
		||||
import { fireEvent } from "../../../../common/dom/fire_event";
 | 
			
		||||
import { DEFAULT_ENTITY_NAME } from "../../../../common/entity/compute_entity_name_display";
 | 
			
		||||
import type { LocalizeFunc } from "../../../../common/translations/localize";
 | 
			
		||||
import { fireEvent } from "../../../../common/dom/fire_event";
 | 
			
		||||
import "../../../../components/ha-form/ha-form";
 | 
			
		||||
import type { SchemaUnion } from "../../../../components/ha-form/types";
 | 
			
		||||
import type { HomeAssistant } from "../../../../types";
 | 
			
		||||
import { DEFAULT_HOURS_TO_SHOW } from "../../cards/hui-sensor-card";
 | 
			
		||||
import type { SensorCardConfig } from "../../cards/types";
 | 
			
		||||
import type { LovelaceCardEditor } from "../../types";
 | 
			
		||||
import { baseLovelaceCardConfig } from "../structs/base-card-struct";
 | 
			
		||||
import { entityNameStruct } from "../structs/entity-name-struct";
 | 
			
		||||
import { configElementStyle } from "./config-elements-style";
 | 
			
		||||
import { DEFAULT_HOURS_TO_SHOW } from "../../cards/hui-sensor-card";
 | 
			
		||||
 | 
			
		||||
const cardConfigStruct = assign(
 | 
			
		||||
  baseLovelaceCardConfig,
 | 
			
		||||
@@ -70,7 +71,9 @@ export class HuiSensorCardEditor
 | 
			
		||||
        {
 | 
			
		||||
          name: "name",
 | 
			
		||||
          selector: {
 | 
			
		||||
            entity_name: {},
 | 
			
		||||
            entity_name: {
 | 
			
		||||
              default_name: DEFAULT_ENTITY_NAME,
 | 
			
		||||
            },
 | 
			
		||||
          },
 | 
			
		||||
          context: { entity: "entity" },
 | 
			
		||||
        },
 | 
			
		||||
 
 | 
			
		||||
@@ -21,13 +21,12 @@ import type { StatisticCardConfig } from "../../cards/types";
 | 
			
		||||
import { headerFooterConfigStructs } from "../../header-footer/structs";
 | 
			
		||||
import type { LovelaceCardEditor } from "../../types";
 | 
			
		||||
import { baseLovelaceCardConfig } from "../structs/base-card-struct";
 | 
			
		||||
import { entityNameStruct } from "../structs/entity-name-struct";
 | 
			
		||||
 | 
			
		||||
const cardConfigStruct = assign(
 | 
			
		||||
  baseLovelaceCardConfig,
 | 
			
		||||
  object({
 | 
			
		||||
    entity: optional(string()),
 | 
			
		||||
    name: optional(entityNameStruct),
 | 
			
		||||
    name: optional(string()),
 | 
			
		||||
    icon: optional(string()),
 | 
			
		||||
    unit: optional(string()),
 | 
			
		||||
    stat_type: optional(string()),
 | 
			
		||||
@@ -145,15 +144,11 @@ export class HuiStatisticCardEditor
 | 
			
		||||
                }
 | 
			
		||||
              : { object: {} },
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          name: "name",
 | 
			
		||||
          selector: { entity_name: {} },
 | 
			
		||||
          context: { entity: "entity" },
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          type: "grid",
 | 
			
		||||
          name: "",
 | 
			
		||||
          schema: [
 | 
			
		||||
            { name: "name", selector: { text: {} } },
 | 
			
		||||
            {
 | 
			
		||||
              name: "icon",
 | 
			
		||||
              selector: {
 | 
			
		||||
 
 | 
			
		||||
@@ -14,7 +14,7 @@ import {
 | 
			
		||||
} from "superstruct";
 | 
			
		||||
import type { HASSDomEvent } from "../../../../common/dom/fire_event";
 | 
			
		||||
import { fireEvent } from "../../../../common/dom/fire_event";
 | 
			
		||||
import { computeDomain } from "../../../../common/entity/compute_domain";
 | 
			
		||||
import { DEFAULT_ENTITY_NAME } from "../../../../common/entity/compute_entity_name_display";
 | 
			
		||||
import "../../../../components/ha-expansion-panel";
 | 
			
		||||
import "../../../../components/ha-form/ha-form";
 | 
			
		||||
import type {
 | 
			
		||||
@@ -35,6 +35,7 @@ import type { EditDetailElementEvent, EditSubElementEvent } from "../types";
 | 
			
		||||
import { configElementStyle } from "./config-elements-style";
 | 
			
		||||
import "./hui-card-features-editor";
 | 
			
		||||
import type { FeatureType } from "./hui-card-features-editor";
 | 
			
		||||
import { computeDomain } from "../../../../common/entity/compute_domain";
 | 
			
		||||
 | 
			
		||||
const COMPATIBLE_FEATURES_TYPES: Record<string, FeatureType[]> = {
 | 
			
		||||
  climate: [
 | 
			
		||||
@@ -88,7 +89,9 @@ export class HuiThermostatCardEditor
 | 
			
		||||
        {
 | 
			
		||||
          name: "name",
 | 
			
		||||
          selector: {
 | 
			
		||||
            entity_name: {},
 | 
			
		||||
            entity_name: {
 | 
			
		||||
              default_name: DEFAULT_ENTITY_NAME,
 | 
			
		||||
            },
 | 
			
		||||
          },
 | 
			
		||||
          context: { entity: "entity" },
 | 
			
		||||
        },
 | 
			
		||||
 
 | 
			
		||||
@@ -16,6 +16,7 @@ import {
 | 
			
		||||
} from "superstruct";
 | 
			
		||||
import type { HASSDomEvent } from "../../../../common/dom/fire_event";
 | 
			
		||||
import { fireEvent } from "../../../../common/dom/fire_event";
 | 
			
		||||
import { DEFAULT_ENTITY_NAME } from "../../../../common/entity/compute_entity_name_display";
 | 
			
		||||
import type { LocalizeFunc } from "../../../../common/translations/localize";
 | 
			
		||||
import { orderProperties } from "../../../../common/util/order-properties";
 | 
			
		||||
import "../../../../components/ha-expansion-panel";
 | 
			
		||||
@@ -101,7 +102,9 @@ export class HuiTileCardEditor
 | 
			
		||||
            {
 | 
			
		||||
              name: "name",
 | 
			
		||||
              selector: {
 | 
			
		||||
                entity_name: {},
 | 
			
		||||
                entity_name: {
 | 
			
		||||
                  default_name: DEFAULT_ENTITY_NAME,
 | 
			
		||||
                },
 | 
			
		||||
              },
 | 
			
		||||
              context: { entity: "entity" },
 | 
			
		||||
            },
 | 
			
		||||
 
 | 
			
		||||
@@ -12,6 +12,7 @@ import {
 | 
			
		||||
  string,
 | 
			
		||||
} from "superstruct";
 | 
			
		||||
import { fireEvent } from "../../../../common/dom/fire_event";
 | 
			
		||||
import { DEFAULT_ENTITY_NAME } from "../../../../common/entity/compute_entity_name_display";
 | 
			
		||||
import { supportsFeature } from "../../../../common/entity/supports-feature";
 | 
			
		||||
import type { LocalizeFunc } from "../../../../common/translations/localize";
 | 
			
		||||
import "../../../../components/ha-form/ha-form";
 | 
			
		||||
@@ -152,7 +153,9 @@ export class HuiWeatherForecastCardEditor
 | 
			
		||||
        {
 | 
			
		||||
          name: "name",
 | 
			
		||||
          selector: {
 | 
			
		||||
            entity_name: {},
 | 
			
		||||
            entity_name: {
 | 
			
		||||
              default_name: DEFAULT_ENTITY_NAME,
 | 
			
		||||
            },
 | 
			
		||||
          },
 | 
			
		||||
          context: { entity: "entity" },
 | 
			
		||||
        },
 | 
			
		||||
 
 | 
			
		||||
@@ -13,6 +13,7 @@ import {
 | 
			
		||||
  union,
 | 
			
		||||
} from "superstruct";
 | 
			
		||||
import { fireEvent } from "../../../../common/dom/fire_event";
 | 
			
		||||
import { DEFAULT_ENTITY_NAME } from "../../../../common/entity/compute_entity_name_display";
 | 
			
		||||
import type { LocalizeFunc } from "../../../../common/translations/localize";
 | 
			
		||||
import "../../../../components/ha-expansion-panel";
 | 
			
		||||
import "../../../../components/ha-form/ha-form";
 | 
			
		||||
@@ -93,7 +94,9 @@ export class HuiHeadingEntityEditor
 | 
			
		||||
                {
 | 
			
		||||
                  name: "name",
 | 
			
		||||
                  selector: {
 | 
			
		||||
                    entity_name: {},
 | 
			
		||||
                    entity_name: {
 | 
			
		||||
                      default_name: DEFAULT_ENTITY_NAME,
 | 
			
		||||
                    },
 | 
			
		||||
                  },
 | 
			
		||||
                  context: { entity: "entity" },
 | 
			
		||||
                },
 | 
			
		||||
 
 | 
			
		||||
@@ -4,12 +4,11 @@ import {
 | 
			
		||||
  actionConfigStruct,
 | 
			
		||||
  actionConfigStructConfirmation,
 | 
			
		||||
} from "./action-struct";
 | 
			
		||||
import { entityNameStruct } from "./entity-name-struct";
 | 
			
		||||
 | 
			
		||||
export const entitiesConfigStruct = union([
 | 
			
		||||
  object({
 | 
			
		||||
    entity: string(),
 | 
			
		||||
    name: optional(entityNameStruct),
 | 
			
		||||
    name: optional(string()),
 | 
			
		||||
    icon: optional(string()),
 | 
			
		||||
    image: optional(string()),
 | 
			
		||||
    secondary_info: optional(string()),
 | 
			
		||||
 
 | 
			
		||||
@@ -43,10 +43,9 @@ export interface ConfigError {
 | 
			
		||||
  message: string;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export interface EntitiesEditorEvent<T extends EntityConfig = EntityConfig>
 | 
			
		||||
  extends CustomEvent {
 | 
			
		||||
export interface EntitiesEditorEvent extends CustomEvent {
 | 
			
		||||
  detail: {
 | 
			
		||||
    entities?: T[];
 | 
			
		||||
    entities?: EntityConfig[];
 | 
			
		||||
    item?: any;
 | 
			
		||||
  };
 | 
			
		||||
  target: EventTarget | null;
 | 
			
		||||
 
 | 
			
		||||
@@ -111,7 +111,11 @@ export class HuiUnusedEntities extends LitElement {
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private _addToLovelaceView(): void {
 | 
			
		||||
    const cardConfig = computeCards(this.hass, this._selectedEntities, {});
 | 
			
		||||
    const cardConfig = computeCards(
 | 
			
		||||
      this.hass.states,
 | 
			
		||||
      this._selectedEntities,
 | 
			
		||||
      {}
 | 
			
		||||
    );
 | 
			
		||||
    const sectionConfig = computeSection(this._selectedEntities, {});
 | 
			
		||||
 | 
			
		||||
    if (this.lovelace.config.views.length === 1) {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,17 +1,17 @@
 | 
			
		||||
import { format } from "date-fns";
 | 
			
		||||
import type { PropertyValues, TemplateResult } from "lit";
 | 
			
		||||
import { css, html, LitElement, nothing } from "lit";
 | 
			
		||||
import { customElement, property, state } from "lit/decorators";
 | 
			
		||||
import "../../../components/ha-date-input";
 | 
			
		||||
import "../../../components/ha-time-input";
 | 
			
		||||
import { setDateTimeValue } from "../../../data/datetime";
 | 
			
		||||
import { format } from "date-fns";
 | 
			
		||||
import { isUnavailableState, UNAVAILABLE } from "../../../data/entity";
 | 
			
		||||
import { setDateTimeValue } from "../../../data/datetime";
 | 
			
		||||
import type { HomeAssistant } from "../../../types";
 | 
			
		||||
import { hasConfigOrEntityChanged } from "../common/has-changed";
 | 
			
		||||
import "../components/hui-generic-entity-row";
 | 
			
		||||
import { createEntityNotFoundWarning } from "../components/hui-warning";
 | 
			
		||||
import type { EntityConfig, LovelaceRow } from "./types";
 | 
			
		||||
import { computeLovelaceEntityName } from "../common/entity/compute-lovelace-entity-name";
 | 
			
		||||
import "../../../components/ha-time-input";
 | 
			
		||||
import { computeStateName } from "../../../common/entity/compute_state_name";
 | 
			
		||||
 | 
			
		||||
@customElement("hui-datetime-entity-row")
 | 
			
		||||
class HuiInputDatetimeEntityRow extends LitElement implements LovelaceRow {
 | 
			
		||||
@@ -53,12 +53,6 @@ class HuiInputDatetimeEntityRow extends LitElement implements LovelaceRow {
 | 
			
		||||
    const time = dateObj ? format(dateObj, "HH:mm:ss") : undefined;
 | 
			
		||||
    const date = dateObj ? format(dateObj, "yyyy-MM-dd") : undefined;
 | 
			
		||||
 | 
			
		||||
    const name = computeLovelaceEntityName(
 | 
			
		||||
      this.hass!,
 | 
			
		||||
      stateObj,
 | 
			
		||||
      this._config.name
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    return html`
 | 
			
		||||
      <hui-generic-entity-row
 | 
			
		||||
        .hass=${this.hass}
 | 
			
		||||
@@ -67,7 +61,7 @@ class HuiInputDatetimeEntityRow extends LitElement implements LovelaceRow {
 | 
			
		||||
      >
 | 
			
		||||
        <div>
 | 
			
		||||
          <ha-date-input
 | 
			
		||||
            .label=${name}
 | 
			
		||||
            .label=${this._config.name || computeStateName(stateObj)}
 | 
			
		||||
            .locale=${this.hass.locale}
 | 
			
		||||
            .value=${date}
 | 
			
		||||
            .disabled=${unavailable}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,7 @@
 | 
			
		||||
import type { PropertyValues } from "lit";
 | 
			
		||||
import { css, html, LitElement, nothing } from "lit";
 | 
			
		||||
import { customElement, property, state } from "lit/decorators";
 | 
			
		||||
import { computeStateName } from "../../../common/entity/compute_state_name";
 | 
			
		||||
import "../../../components/ha-date-input";
 | 
			
		||||
import "../../../components/ha-time-input";
 | 
			
		||||
import { isUnavailableState, UNKNOWN } from "../../../data/entity";
 | 
			
		||||
@@ -9,7 +10,6 @@ import {
 | 
			
		||||
  stateToIsoDateString,
 | 
			
		||||
} from "../../../data/input_datetime";
 | 
			
		||||
import type { HomeAssistant } from "../../../types";
 | 
			
		||||
import { computeLovelaceEntityName } from "../common/entity/compute-lovelace-entity-name";
 | 
			
		||||
import { hasConfigOrEntityChanged } from "../common/has-changed";
 | 
			
		||||
import "../components/hui-generic-entity-row";
 | 
			
		||||
import { createEntityNotFoundWarning } from "../components/hui-warning";
 | 
			
		||||
@@ -47,11 +47,7 @@ class HuiInputDatetimeEntityRow extends LitElement implements LovelaceRow {
 | 
			
		||||
      `;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const name = computeLovelaceEntityName(
 | 
			
		||||
      this.hass!,
 | 
			
		||||
      stateObj,
 | 
			
		||||
      this._config.name
 | 
			
		||||
    );
 | 
			
		||||
    const name = this._config.name || computeStateName(stateObj);
 | 
			
		||||
 | 
			
		||||
    return html`
 | 
			
		||||
      <hui-generic-entity-row
 | 
			
		||||
 
 | 
			
		||||
@@ -2,6 +2,7 @@ import type { PropertyValues } from "lit";
 | 
			
		||||
import { css, html, LitElement, nothing } from "lit";
 | 
			
		||||
import { customElement, property, state } from "lit/decorators";
 | 
			
		||||
import { stopPropagation } from "../../../common/dom/stop_propagation";
 | 
			
		||||
import { computeStateName } from "../../../common/entity/compute_state_name";
 | 
			
		||||
import "../../../components/ha-list-item";
 | 
			
		||||
import "../../../components/ha-select";
 | 
			
		||||
import { UNAVAILABLE } from "../../../data/entity";
 | 
			
		||||
@@ -10,7 +11,6 @@ import type { InputSelectEntity } from "../../../data/input_select";
 | 
			
		||||
import { setInputSelectOption } from "../../../data/input_select";
 | 
			
		||||
import type { HomeAssistant } from "../../../types";
 | 
			
		||||
import type { EntitiesCardEntityConfig } from "../cards/types";
 | 
			
		||||
import { computeLovelaceEntityName } from "../common/entity/compute-lovelace-entity-name";
 | 
			
		||||
import { hasConfigOrEntityChanged } from "../common/has-changed";
 | 
			
		||||
import "../components/hui-generic-entity-row";
 | 
			
		||||
import { createEntityNotFoundWarning } from "../components/hui-warning";
 | 
			
		||||
@@ -51,12 +51,6 @@ class HuiInputSelectEntityRow extends LitElement implements LovelaceRow {
 | 
			
		||||
      `;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const name = computeLovelaceEntityName(
 | 
			
		||||
      this.hass!,
 | 
			
		||||
      stateObj,
 | 
			
		||||
      this._config.name
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    return html`
 | 
			
		||||
      <hui-generic-entity-row
 | 
			
		||||
        .hass=${this.hass}
 | 
			
		||||
@@ -64,7 +58,7 @@ class HuiInputSelectEntityRow extends LitElement implements LovelaceRow {
 | 
			
		||||
        hide-name
 | 
			
		||||
      >
 | 
			
		||||
        <ha-select
 | 
			
		||||
          .label=${name}
 | 
			
		||||
          .label=${this._config.name || computeStateName(stateObj)}
 | 
			
		||||
          .value=${stateObj.state}
 | 
			
		||||
          .options=${stateObj.attributes.options}
 | 
			
		||||
          .disabled=${
 | 
			
		||||
 
 | 
			
		||||
@@ -1,11 +1,11 @@
 | 
			
		||||
import type { PropertyValues } from "lit";
 | 
			
		||||
import { css, html, LitElement, nothing } from "lit";
 | 
			
		||||
import { customElement, property, state } from "lit/decorators";
 | 
			
		||||
import { computeStateName } from "../../../common/entity/compute_state_name";
 | 
			
		||||
import "../../../components/ha-textfield";
 | 
			
		||||
import { isUnavailableState, UNAVAILABLE } from "../../../data/entity";
 | 
			
		||||
import { setValue } from "../../../data/input_text";
 | 
			
		||||
import type { HomeAssistant } from "../../../types";
 | 
			
		||||
import { computeLovelaceEntityName } from "../common/entity/compute-lovelace-entity-name";
 | 
			
		||||
import { hasConfigOrEntityChanged } from "../common/has-changed";
 | 
			
		||||
import "../components/hui-generic-entity-row";
 | 
			
		||||
import { createEntityNotFoundWarning } from "../components/hui-warning";
 | 
			
		||||
@@ -43,12 +43,6 @@ class HuiInputTextEntityRow extends LitElement implements LovelaceRow {
 | 
			
		||||
      `;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const name = computeLovelaceEntityName(
 | 
			
		||||
      this.hass!,
 | 
			
		||||
      stateObj,
 | 
			
		||||
      this._config.name
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    return html`
 | 
			
		||||
      <hui-generic-entity-row
 | 
			
		||||
        .hass=${this.hass}
 | 
			
		||||
@@ -56,7 +50,7 @@ class HuiInputTextEntityRow extends LitElement implements LovelaceRow {
 | 
			
		||||
        hide-name
 | 
			
		||||
      >
 | 
			
		||||
        <ha-textfield
 | 
			
		||||
          .label=${name}
 | 
			
		||||
          .label=${this._config.name || computeStateName(stateObj)}
 | 
			
		||||
          .disabled=${stateObj.state === UNAVAILABLE}
 | 
			
		||||
          .value=${stateObj.state}
 | 
			
		||||
          .minlength=${stateObj.attributes.min}
 | 
			
		||||
 
 | 
			
		||||
@@ -2,6 +2,7 @@ import type { PropertyValues } from "lit";
 | 
			
		||||
import { LitElement, css, html, nothing } from "lit";
 | 
			
		||||
import { customElement, property, state } from "lit/decorators";
 | 
			
		||||
import { stopPropagation } from "../../../common/dom/stop_propagation";
 | 
			
		||||
import { computeStateName } from "../../../common/entity/compute_state_name";
 | 
			
		||||
import "../../../components/ha-list-item";
 | 
			
		||||
import "../../../components/ha-select";
 | 
			
		||||
import { UNAVAILABLE } from "../../../data/entity";
 | 
			
		||||
@@ -10,7 +11,6 @@ import type { SelectEntity } from "../../../data/select";
 | 
			
		||||
import { setSelectOption } from "../../../data/select";
 | 
			
		||||
import type { HomeAssistant } from "../../../types";
 | 
			
		||||
import type { EntitiesCardEntityConfig } from "../cards/types";
 | 
			
		||||
import { computeLovelaceEntityName } from "../common/entity/compute-lovelace-entity-name";
 | 
			
		||||
import { hasConfigOrEntityChanged } from "../common/has-changed";
 | 
			
		||||
import "../components/hui-generic-entity-row";
 | 
			
		||||
import { createEntityNotFoundWarning } from "../components/hui-warning";
 | 
			
		||||
@@ -51,12 +51,6 @@ class HuiSelectEntityRow extends LitElement implements LovelaceRow {
 | 
			
		||||
      `;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const name = computeLovelaceEntityName(
 | 
			
		||||
      this.hass!,
 | 
			
		||||
      stateObj,
 | 
			
		||||
      this._config.name
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    return html`
 | 
			
		||||
      <hui-generic-entity-row
 | 
			
		||||
        .hass=${this.hass}
 | 
			
		||||
@@ -64,7 +58,7 @@ class HuiSelectEntityRow extends LitElement implements LovelaceRow {
 | 
			
		||||
        hide-name
 | 
			
		||||
      >
 | 
			
		||||
        <ha-select
 | 
			
		||||
          .label=${name}
 | 
			
		||||
          .label=${this._config.name || computeStateName(stateObj)}
 | 
			
		||||
          .value=${stateObj.state}
 | 
			
		||||
          .options=${stateObj.attributes.options}
 | 
			
		||||
          .disabled=${stateObj.state === UNAVAILABLE}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,7 @@
 | 
			
		||||
import type { PropertyValues } from "lit";
 | 
			
		||||
import { css, html, LitElement, nothing } from "lit";
 | 
			
		||||
import { customElement, property, state } from "lit/decorators";
 | 
			
		||||
import { computeStateName } from "../../../common/entity/compute_state_name";
 | 
			
		||||
import "../../../components/ha-textfield";
 | 
			
		||||
import { isUnavailableState, UNAVAILABLE } from "../../../data/entity";
 | 
			
		||||
import type { TextEntity } from "../../../data/text";
 | 
			
		||||
@@ -10,7 +11,6 @@ import { hasConfigOrEntityChanged } from "../common/has-changed";
 | 
			
		||||
import "../components/hui-generic-entity-row";
 | 
			
		||||
import { createEntityNotFoundWarning } from "../components/hui-warning";
 | 
			
		||||
import type { EntityConfig, LovelaceRow } from "./types";
 | 
			
		||||
import { computeLovelaceEntityName } from "../common/entity/compute-lovelace-entity-name";
 | 
			
		||||
 | 
			
		||||
@customElement("hui-text-entity-row")
 | 
			
		||||
class HuiTextEntityRow extends LitElement implements LovelaceRow {
 | 
			
		||||
@@ -46,12 +46,6 @@ class HuiTextEntityRow extends LitElement implements LovelaceRow {
 | 
			
		||||
      `;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    const name = computeLovelaceEntityName(
 | 
			
		||||
      this.hass!,
 | 
			
		||||
      stateObj,
 | 
			
		||||
      this._config.name
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    return html`
 | 
			
		||||
      <hui-generic-entity-row
 | 
			
		||||
        .hass=${this.hass}
 | 
			
		||||
@@ -59,7 +53,7 @@ class HuiTextEntityRow extends LitElement implements LovelaceRow {
 | 
			
		||||
        hide-name
 | 
			
		||||
      >
 | 
			
		||||
        <ha-textfield
 | 
			
		||||
          .label=${name}
 | 
			
		||||
          .label=${this._config.name || computeStateName(stateObj)}
 | 
			
		||||
          .disabled=${stateObj.state === UNAVAILABLE}
 | 
			
		||||
          .value=${stateObj.state}
 | 
			
		||||
          .minlength=${stateObj.attributes.min}
 | 
			
		||||
 
 | 
			
		||||
@@ -3,6 +3,7 @@ import { LitElement, css, html, nothing } from "lit";
 | 
			
		||||
import { customElement, property, state } from "lit/decorators";
 | 
			
		||||
import { classMap } from "lit/directives/class-map";
 | 
			
		||||
import { ifDefined } from "lit/directives/if-defined";
 | 
			
		||||
import { computeStateName } from "../../../common/entity/compute_state_name";
 | 
			
		||||
import { isUnavailableState } from "../../../data/entity";
 | 
			
		||||
import type { ActionHandlerEvent } from "../../../data/lovelace/action_handler";
 | 
			
		||||
import type { ForecastEvent, WeatherEntity } from "../../../data/weather";
 | 
			
		||||
@@ -23,7 +24,6 @@ import { hasConfigOrEntityChanged } from "../common/has-changed";
 | 
			
		||||
import "../components/hui-generic-entity-row";
 | 
			
		||||
import { createEntityNotFoundWarning } from "../components/hui-warning";
 | 
			
		||||
import type { LovelaceRow } from "./types";
 | 
			
		||||
import { computeLovelaceEntityName } from "../common/entity/compute-lovelace-entity-name";
 | 
			
		||||
 | 
			
		||||
@customElement("hui-weather-entity-row")
 | 
			
		||||
class HuiWeatherEntityRow extends LitElement implements LovelaceRow {
 | 
			
		||||
@@ -119,12 +119,6 @@ class HuiWeatherEntityRow extends LitElement implements LovelaceRow {
 | 
			
		||||
    const forecastData = getForecast(stateObj.attributes, this._forecastEvent);
 | 
			
		||||
    const forecast = forecastData?.forecast;
 | 
			
		||||
 | 
			
		||||
    const name = computeLovelaceEntityName(
 | 
			
		||||
      this.hass!,
 | 
			
		||||
      stateObj,
 | 
			
		||||
      this._config.name
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    return html`
 | 
			
		||||
      <div
 | 
			
		||||
        class="icon-image ${classMap({
 | 
			
		||||
@@ -161,7 +155,7 @@ class HuiWeatherEntityRow extends LitElement implements LovelaceRow {
 | 
			
		||||
          hasDoubleClick: hasAction(this._config!.double_tap_action),
 | 
			
		||||
        })}
 | 
			
		||||
      >
 | 
			
		||||
        ${name}
 | 
			
		||||
        ${this._config.name || computeStateName(stateObj)}
 | 
			
		||||
        ${hasSecondary
 | 
			
		||||
          ? html`
 | 
			
		||||
              <div class="secondary">
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,3 @@
 | 
			
		||||
import type { EntityNameItem } from "../../../common/entity/compute_entity_name_display";
 | 
			
		||||
import type {
 | 
			
		||||
  ActionConfig,
 | 
			
		||||
  ConfirmationRestrictionConfig,
 | 
			
		||||
@@ -11,7 +10,7 @@ import type { TimestampRenderingFormat } from "../components/types";
 | 
			
		||||
export interface EntityConfig {
 | 
			
		||||
  entity: string;
 | 
			
		||||
  type?: string;
 | 
			
		||||
  name?: string | EntityNameItem | EntityNameItem[];
 | 
			
		||||
  name?: string;
 | 
			
		||||
  icon?: string;
 | 
			
		||||
  image?: string;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -321,8 +321,6 @@ class HUIRoot extends LitElement {
 | 
			
		||||
                .id="button-${index}"
 | 
			
		||||
                .path=${item.icon}
 | 
			
		||||
                slot="trigger"
 | 
			
		||||
                .label=${label}
 | 
			
		||||
                hide-title
 | 
			
		||||
              ></ha-icon-button>
 | 
			
		||||
              ${item.subItems
 | 
			
		||||
                .filter((subItem) => subItem.visible)
 | 
			
		||||
@@ -342,6 +340,9 @@ class HUIRoot extends LitElement {
 | 
			
		||||
                  `
 | 
			
		||||
                )}
 | 
			
		||||
            </ha-button-menu>
 | 
			
		||||
            <ha-tooltip placement="bottom" .for="button-${index}">
 | 
			
		||||
              ${label}
 | 
			
		||||
            </ha-tooltip>
 | 
			
		||||
          `
 | 
			
		||||
        : html`
 | 
			
		||||
            <ha-icon-button
 | 
			
		||||
 
 | 
			
		||||
@@ -2,15 +2,15 @@ import { css, html, LitElement, nothing } from "lit";
 | 
			
		||||
import { customElement, state } from "lit/decorators";
 | 
			
		||||
import { DOMAINS_TOGGLE } from "../../../common/const";
 | 
			
		||||
import { computeDomain } from "../../../common/entity/compute_domain";
 | 
			
		||||
import "../../../components/ha-button";
 | 
			
		||||
import { computeStateName } from "../../../common/entity/compute_state_name";
 | 
			
		||||
import "../../../components/ha-state-icon";
 | 
			
		||||
import type { ActionHandlerEvent } from "../../../data/lovelace/action_handler";
 | 
			
		||||
import "../../../components/ha-button";
 | 
			
		||||
import type { HomeAssistant } from "../../../types";
 | 
			
		||||
import { actionHandler } from "../common/directives/action-handler-directive";
 | 
			
		||||
import { computeLovelaceEntityName } from "../common/entity/compute-lovelace-entity-name";
 | 
			
		||||
import { handleAction } from "../common/handle-action";
 | 
			
		||||
import { hasAction } from "../common/has-action";
 | 
			
		||||
import type { ButtonRowConfig, LovelaceRow } from "../entity-rows/types";
 | 
			
		||||
import type { ActionHandlerEvent } from "../../../data/lovelace/action_handler";
 | 
			
		||||
 | 
			
		||||
@customElement("hui-button-row")
 | 
			
		||||
export class HuiButtonRow extends LitElement implements LovelaceRow {
 | 
			
		||||
@@ -49,11 +49,8 @@ export class HuiButtonRow extends LitElement implements LovelaceRow {
 | 
			
		||||
        ? this.hass.states[this._config.entity]
 | 
			
		||||
        : undefined;
 | 
			
		||||
 | 
			
		||||
    const name = computeLovelaceEntityName(
 | 
			
		||||
      this.hass!,
 | 
			
		||||
      stateObj,
 | 
			
		||||
      this._config.name
 | 
			
		||||
    );
 | 
			
		||||
    const name =
 | 
			
		||||
      this._config.name ?? (stateObj ? computeStateName(stateObj) : "");
 | 
			
		||||
 | 
			
		||||
    return html`
 | 
			
		||||
      <ha-state-icon
 | 
			
		||||
 
 | 
			
		||||
@@ -168,17 +168,9 @@ export class HomeAreaViewStrategy extends ReactiveElement {
 | 
			
		||||
 | 
			
		||||
    const summaryEntities = Object.values(entitiesBySummary).flat();
 | 
			
		||||
 | 
			
		||||
    // Automations section
 | 
			
		||||
    const automationFilter = generateEntityFilter(hass, {
 | 
			
		||||
      domain: "automation",
 | 
			
		||||
      entity_category: "none",
 | 
			
		||||
    });
 | 
			
		||||
    const automations = areaEntities.filter(automationFilter);
 | 
			
		||||
 | 
			
		||||
    // Rest of entities grouped by device
 | 
			
		||||
    const otherEntities = areaEntities.filter(
 | 
			
		||||
      (entityId) =>
 | 
			
		||||
        !summaryEntities.includes(entityId) && !automations.includes(entityId)
 | 
			
		||||
      (entityId) => !summaryEntities.includes(entityId)
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    const entitiesByDevice: Record<string, string[]> = {};
 | 
			
		||||
@@ -303,21 +295,6 @@ export class HomeAreaViewStrategy extends ReactiveElement {
 | 
			
		||||
      sections.push(...deviceSections);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Show automations last, if they exist
 | 
			
		||||
    if (automations.length > 0) {
 | 
			
		||||
      sections.push({
 | 
			
		||||
        type: "grid",
 | 
			
		||||
        cards: [
 | 
			
		||||
          computeHeadingCard(
 | 
			
		||||
            hass.localize("ui.panel.lovelace.strategy.home.automations"),
 | 
			
		||||
            "mdi:robot",
 | 
			
		||||
            "/config/automation/dashboard"
 | 
			
		||||
          ),
 | 
			
		||||
          ...automations.map(computeTileCard),
 | 
			
		||||
        ],
 | 
			
		||||
      });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Allow between 2 and 3 columns (the max should be set to define the width of the header)
 | 
			
		||||
    const maxColumns = clamp(sections.length, 2, 3);
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -21,7 +21,6 @@ import type {
 | 
			
		||||
import { getAreas, getFloors } from "../areas/helpers/areas-strategy-helper";
 | 
			
		||||
import type { CommonControlSectionStrategyConfig } from "../usage_prediction/common-controls-section-strategy";
 | 
			
		||||
import { getHomeStructure } from "./helpers/home-structure";
 | 
			
		||||
import { floorDefaultIcon } from "../../../../components/ha-floor-icon";
 | 
			
		||||
 | 
			
		||||
export interface HomeMainViewStrategyConfig {
 | 
			
		||||
  type: "home-main";
 | 
			
		||||
@@ -93,7 +92,6 @@ export class HomeMainViewStrategy extends ReactiveElement {
 | 
			
		||||
                  ? floor.name
 | 
			
		||||
                  : hass.localize("ui.panel.lovelace.strategy.home.areas"),
 | 
			
		||||
              heading_style: "title",
 | 
			
		||||
              icon: floor.icon || floorDefaultIcon(floor),
 | 
			
		||||
            },
 | 
			
		||||
            ...cards,
 | 
			
		||||
          ],
 | 
			
		||||
@@ -217,9 +215,7 @@ export class HomeMainViewStrategy extends ReactiveElement {
 | 
			
		||||
      column_span: maxColumns,
 | 
			
		||||
      cards: [],
 | 
			
		||||
    };
 | 
			
		||||
    const weatherEntity = Object.keys(hass.states)
 | 
			
		||||
      .filter(weatherFilter)
 | 
			
		||||
      .sort()[0];
 | 
			
		||||
    const weatherEntity = Object.keys(hass.states).find(weatherFilter);
 | 
			
		||||
 | 
			
		||||
    if (weatherEntity) {
 | 
			
		||||
      widgetSection.cards!.push(
 | 
			
		||||
 
 | 
			
		||||
@@ -4,7 +4,6 @@ import {
 | 
			
		||||
  findEntities,
 | 
			
		||||
  generateEntityFilter,
 | 
			
		||||
} from "../../../../common/entity/entity_filter";
 | 
			
		||||
import { floorDefaultIcon } from "../../../../components/ha-floor-icon";
 | 
			
		||||
import type { LovelaceCardConfig } from "../../../../data/lovelace/config/card";
 | 
			
		||||
import type { LovelaceSectionRawConfig } from "../../../../data/lovelace/config/section";
 | 
			
		||||
import type { LovelaceViewConfig } from "../../../../data/lovelace/config/view";
 | 
			
		||||
@@ -97,7 +96,6 @@ export class HomeMMediaPlayersViewStrategy extends ReactiveElement {
 | 
			
		||||
              floorCount > 1
 | 
			
		||||
                ? floor.name
 | 
			
		||||
                : hass.localize("ui.panel.lovelace.strategy.home.areas"),
 | 
			
		||||
            icon: floor.icon || floorDefaultIcon(floor),
 | 
			
		||||
          },
 | 
			
		||||
        ],
 | 
			
		||||
      };
 | 
			
		||||
 
 | 
			
		||||
@@ -44,7 +44,10 @@ export class OriginalStatesViewStrategy extends ReactiveElement {
 | 
			
		||||
    // User can override default view. If they didn't, we will add one
 | 
			
		||||
    // that contains all entities.
 | 
			
		||||
    const view = generateDefaultViewConfig(
 | 
			
		||||
      hass,
 | 
			
		||||
      hass.areas,
 | 
			
		||||
      hass.devices,
 | 
			
		||||
      hass.entities,
 | 
			
		||||
      hass.states,
 | 
			
		||||
      localize,
 | 
			
		||||
      energyPrefs,
 | 
			
		||||
      config.areas,
 | 
			
		||||
 
 | 
			
		||||
@@ -5,7 +5,6 @@ import {
 | 
			
		||||
  generateEntityFilter,
 | 
			
		||||
  type EntityFilter,
 | 
			
		||||
} from "../../../common/entity/entity_filter";
 | 
			
		||||
import { floorDefaultIcon } from "../../../components/ha-floor-icon";
 | 
			
		||||
import type { LovelaceCardConfig } from "../../../data/lovelace/config/card";
 | 
			
		||||
import type { LovelaceSectionRawConfig } from "../../../data/lovelace/config/section";
 | 
			
		||||
import type { LovelaceViewConfig } from "../../../data/lovelace/config/view";
 | 
			
		||||
@@ -141,7 +140,6 @@ export class SafetyViewStrategy extends ReactiveElement {
 | 
			
		||||
              floorCount > 1
 | 
			
		||||
                ? floor.name
 | 
			
		||||
                : hass.localize("ui.panel.lovelace.strategy.home.areas"),
 | 
			
		||||
            icon: floor.icon || floorDefaultIcon(floor),
 | 
			
		||||
          },
 | 
			
		||||
        ],
 | 
			
		||||
      };
 | 
			
		||||
 
 | 
			
		||||
@@ -255,10 +255,6 @@ class DialogTodoItemEditor extends LitElement {
 | 
			
		||||
 | 
			
		||||
  // Parse a date in the browser timezone
 | 
			
		||||
  private _parseDate(dateStr: string): Date {
 | 
			
		||||
    // If it's a date-only string (no 'T'), parse as midnight in browser time to avoid offset issues
 | 
			
		||||
    if (!dateStr.includes("T")) {
 | 
			
		||||
      return new Date(dateStr + "T00:00:00");
 | 
			
		||||
    }
 | 
			
		||||
    const tzDate = new TZDate(dateStr, this._timeZone!);
 | 
			
		||||
    return new Date(tzDate.getTime());
 | 
			
		||||
  }
 | 
			
		||||
 
 | 
			
		||||
@@ -3945,6 +3945,7 @@
 | 
			
		||||
              "add": "Add trigger",
 | 
			
		||||
              "empty_search": "No triggers found for {term}",
 | 
			
		||||
              "id": "Trigger ID",
 | 
			
		||||
              "id_helper": "Helps identify each run based on which trigger fired.",
 | 
			
		||||
              "optional": "Optional",
 | 
			
		||||
              "edit_id": "Edit ID",
 | 
			
		||||
              "duplicate": "[%key:ui::common::duplicate%]",
 | 
			
		||||
@@ -6942,8 +6943,7 @@
 | 
			
		||||
            "areas": "Areas",
 | 
			
		||||
            "other_areas": "Other areas",
 | 
			
		||||
            "unamed_device": "Unnamed device",
 | 
			
		||||
            "others": "Others",
 | 
			
		||||
            "automations": "Automations"
 | 
			
		||||
            "others": "Others"
 | 
			
		||||
          },
 | 
			
		||||
          "common_controls": {
 | 
			
		||||
            "not_loaded": "Usage Prediction integration is not loaded.",
 | 
			
		||||
@@ -7075,10 +7075,10 @@
 | 
			
		||||
            "energy_devices_graph": {
 | 
			
		||||
              "energy_usage": "Energy usage",
 | 
			
		||||
              "previous_energy_usage": "Previous energy usage",
 | 
			
		||||
              "total_energy_usage": "Total",
 | 
			
		||||
              "total_energy_usage": "Total energy usage",
 | 
			
		||||
              "change_chart_type": "Change chart type",
 | 
			
		||||
              "untracked_consumption": "Untracked consumption",
 | 
			
		||||
              "untracked": "untracked"
 | 
			
		||||
              "untracked": "untracked",
 | 
			
		||||
              "includes_untracked": "Includes {num} kWh of untracked energy"
 | 
			
		||||
            },
 | 
			
		||||
            "energy_devices_detail_graph": {
 | 
			
		||||
              "untracked_consumption": "Untracked consumption",
 | 
			
		||||
@@ -8154,10 +8154,6 @@
 | 
			
		||||
              "media-player-playback": {
 | 
			
		||||
                "label": "Media player playback controls"
 | 
			
		||||
              },
 | 
			
		||||
              "media-player-volume-buttons": {
 | 
			
		||||
                "label": "Media player volume buttons",
 | 
			
		||||
                "step": "Step size"
 | 
			
		||||
              },
 | 
			
		||||
              "media-player-volume-slider": {
 | 
			
		||||
                "label": "Media player volume slider"
 | 
			
		||||
              },
 | 
			
		||||
 
 | 
			
		||||
@@ -1,71 +0,0 @@
 | 
			
		||||
import { expect, test } from "vitest";
 | 
			
		||||
import { TZDate } from "@date-fns/tz";
 | 
			
		||||
import { isDate } from "../../../src/common/string/is_date";
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * These tests verify that all-day event dates are correctly identified
 | 
			
		||||
 * and can be distinguished from datetime strings. This is critical for
 | 
			
		||||
 * proper date display in the calendar event detail dialog.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
test("isDate correctly identifies date-only strings", () => {
 | 
			
		||||
  // Valid date-only strings (all-day events)
 | 
			
		||||
  expect(isDate("2025-10-10")).toBe(true);
 | 
			
		||||
  expect(isDate("2007-06-28")).toBe(true);
 | 
			
		||||
  expect(isDate("2025-12-31")).toBe(true);
 | 
			
		||||
 | 
			
		||||
  // DateTime strings should not be identified as dates
 | 
			
		||||
  expect(isDate("2025-10-10T00:00:00")).toBe(false);
 | 
			
		||||
  expect(isDate("2025-10-10T14:30:00")).toBe(false);
 | 
			
		||||
  expect(isDate("2025-10-10T14:30:00Z")).toBe(false);
 | 
			
		||||
  expect(isDate("2025-10-10T14:30:00+00:00")).toBe(false);
 | 
			
		||||
  expect(isDate("2025-10-10T14:30:00-08:00")).toBe(false);
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
test("Date parsing for all-day events", () => {
 | 
			
		||||
  // Verify that date-only strings can be parsed as local dates
 | 
			
		||||
  const dateStr = "2025-10-10";
 | 
			
		||||
  const parsed = new Date(dateStr + "T00:00:00");
 | 
			
		||||
 | 
			
		||||
  expect(parsed.getFullYear()).toBe(2025);
 | 
			
		||||
  expect(parsed.getMonth()).toBe(9); // October (0-indexed)
 | 
			
		||||
  expect(parsed.getDate()).toBe(10);
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
test("Timed events respect timezone conversion", () => {
 | 
			
		||||
  // Verify that datetime strings with timezone info are properly converted with TZDate
 | 
			
		||||
  const datetimeStr = "2025-10-10T14:30:00-07:00"; // 2:30 PM Pacific time
 | 
			
		||||
  const timeZone = "America/Los_Angeles"; // UTC-7 (PDT) in October
 | 
			
		||||
 | 
			
		||||
  // This should NOT be identified as a date-only string
 | 
			
		||||
  expect(isDate(datetimeStr)).toBe(false);
 | 
			
		||||
 | 
			
		||||
  // Timed events should use TZDate which respects timezone
 | 
			
		||||
  const tzDate = new TZDate(datetimeStr, timeZone);
 | 
			
		||||
 | 
			
		||||
  // The date should be October 10, 2:30 PM in LA timezone
 | 
			
		||||
  expect(tzDate.getFullYear()).toBe(2025);
 | 
			
		||||
  expect(tzDate.getMonth()).toBe(9); // October (0-indexed)
 | 
			
		||||
  expect(tzDate.getDate()).toBe(10);
 | 
			
		||||
  expect(tzDate.getHours()).toBe(14);
 | 
			
		||||
  expect(tzDate.getMinutes()).toBe(30);
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
test("Timed events display different day due to timezone offset", () => {
 | 
			
		||||
  // An event at 1 AM UTC on October 10 should display as October 9 in Pacific time
 | 
			
		||||
  const utcDatetimeStr = "2025-10-10T01:00:00Z";
 | 
			
		||||
  const timeZone = "America/Los_Angeles"; // UTC-7 (PDT) in October
 | 
			
		||||
 | 
			
		||||
  // This should NOT be identified as a date-only string
 | 
			
		||||
  expect(isDate(utcDatetimeStr)).toBe(false);
 | 
			
		||||
 | 
			
		||||
  // Parse the UTC datetime in Pacific timezone
 | 
			
		||||
  const tzDate = new TZDate(utcDatetimeStr, timeZone);
 | 
			
		||||
 | 
			
		||||
  // Due to the -7 hour offset, 1 AM UTC becomes 6 PM on the previous day in Pacific
 | 
			
		||||
  expect(tzDate.getFullYear()).toBe(2025);
 | 
			
		||||
  expect(tzDate.getMonth()).toBe(9); // October (0-indexed)
 | 
			
		||||
  expect(tzDate.getDate()).toBe(9); // Previous day
 | 
			
		||||
  expect(tzDate.getHours()).toBe(18); // 6 PM
 | 
			
		||||
  expect(tzDate.getMinutes()).toBe(0);
 | 
			
		||||
});
 | 
			
		||||
@@ -1,4 +1,5 @@
 | 
			
		||||
import { describe, expect, it, vi } from "vitest";
 | 
			
		||||
import { DEFAULT_ENTITY_NAME } from "../../../../../src/common/entity/compute_entity_name_display";
 | 
			
		||||
import { computeLovelaceEntityName } from "../../../../../src/panels/lovelace/common/entity/compute-lovelace-entity-name";
 | 
			
		||||
import type { HomeAssistant } from "../../../../../src/types";
 | 
			
		||||
import { mockStateObj } from "../../../../common/entity/context/context-mock";
 | 
			
		||||
@@ -22,32 +23,30 @@ describe("computeLovelaceEntityName", () => {
 | 
			
		||||
    expect(mockFormatEntityName).not.toHaveBeenCalled();
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  it("return state name when nameConfig is empty string", () => {
 | 
			
		||||
  it("returns empty string when nameConfig is empty string", () => {
 | 
			
		||||
    const mockFormatEntityName = vi.fn();
 | 
			
		||||
    const hass = createMockHass(mockFormatEntityName);
 | 
			
		||||
    const stateObj = mockStateObj({
 | 
			
		||||
      entity_id: "light.kitchen",
 | 
			
		||||
      attributes: { friendly_name: "Kitchen Light" },
 | 
			
		||||
    });
 | 
			
		||||
    const stateObj = mockStateObj({ entity_id: "light.kitchen" });
 | 
			
		||||
 | 
			
		||||
    const result = computeLovelaceEntityName(hass, stateObj, "");
 | 
			
		||||
 | 
			
		||||
    expect(result).toBe("Kitchen Light");
 | 
			
		||||
    expect(result).toBe("");
 | 
			
		||||
    expect(mockFormatEntityName).not.toHaveBeenCalled();
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  it("return state name when nameConfig is undefined", () => {
 | 
			
		||||
  it("calls formatEntityName with DEFAULT_ENTITY_NAME when nameConfig is undefined", () => {
 | 
			
		||||
    const mockFormatEntityName = vi.fn(() => "Formatted Name");
 | 
			
		||||
    const hass = createMockHass(mockFormatEntityName);
 | 
			
		||||
    const stateObj = mockStateObj({
 | 
			
		||||
      entity_id: "light.kitchen",
 | 
			
		||||
      attributes: { friendly_name: "Kitchen Light" },
 | 
			
		||||
    });
 | 
			
		||||
    const stateObj = mockStateObj({ entity_id: "light.kitchen" });
 | 
			
		||||
 | 
			
		||||
    const result = computeLovelaceEntityName(hass, stateObj, undefined);
 | 
			
		||||
 | 
			
		||||
    expect(result).toBe("Kitchen Light");
 | 
			
		||||
    expect(mockFormatEntityName).not.toHaveBeenCalled();
 | 
			
		||||
    expect(result).toBe("Formatted Name");
 | 
			
		||||
    expect(mockFormatEntityName).toHaveBeenCalledTimes(1);
 | 
			
		||||
    expect(mockFormatEntityName).toHaveBeenCalledWith(
 | 
			
		||||
      stateObj,
 | 
			
		||||
      DEFAULT_ENTITY_NAME
 | 
			
		||||
    );
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  it("calls formatEntityName with EntityNameItem config", () => {
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										10
									
								
								yarn.lock
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								yarn.lock
									
									
									
									
									
								
							@@ -2277,12 +2277,12 @@ __metadata:
 | 
			
		||||
  languageName: node
 | 
			
		||||
  linkType: hard
 | 
			
		||||
 | 
			
		||||
"@lezer/highlight@npm:1.2.3, @lezer/highlight@npm:^1.0.0":
 | 
			
		||||
  version: 1.2.3
 | 
			
		||||
  resolution: "@lezer/highlight@npm:1.2.3"
 | 
			
		||||
"@lezer/highlight@npm:1.2.2, @lezer/highlight@npm:^1.0.0":
 | 
			
		||||
  version: 1.2.2
 | 
			
		||||
  resolution: "@lezer/highlight@npm:1.2.2"
 | 
			
		||||
  dependencies:
 | 
			
		||||
    "@lezer/common": "npm:^1.3.0"
 | 
			
		||||
  checksum: 10/8f787d464f8a036f117a0b23e73ac034d224a57d72501c6559089098a28f127c9e495b90ac7d132acc86199e0b64d4c038f75f9293a37c7c61add52fa1acdb4e
 | 
			
		||||
  checksum: 10/73cb339de042b354cbc0b9e83978a91d2448435edae865a192cfc50d536e0b7d2e3cd563aabeb59eb6c86b0c38b3edc6f2871da8482c5dd8dca4a0899e743f7f
 | 
			
		||||
  languageName: node
 | 
			
		||||
  linkType: hard
 | 
			
		||||
 | 
			
		||||
@@ -9235,7 +9235,7 @@ __metadata:
 | 
			
		||||
    "@fullcalendar/luxon3": "npm:6.1.19"
 | 
			
		||||
    "@fullcalendar/timegrid": "npm:6.1.19"
 | 
			
		||||
    "@home-assistant/webawesome": "npm:3.0.0-beta.6.ha.6"
 | 
			
		||||
    "@lezer/highlight": "npm:1.2.3"
 | 
			
		||||
    "@lezer/highlight": "npm:1.2.2"
 | 
			
		||||
    "@lit-labs/motion": "npm:1.0.9"
 | 
			
		||||
    "@lit-labs/observers": "npm:2.0.6"
 | 
			
		||||
    "@lit-labs/virtualizer": "npm:2.1.1"
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user