mirror of
https://github.com/home-assistant/frontend.git
synced 2026-04-02 17:03:45 +00:00
Compare commits
1 Commits
keyboard-i
...
gauge-fat
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fd2cac5af9 |
@@ -1,4 +1,3 @@
|
||||
/// <reference types="chromecast-caf-sender" />
|
||||
import { mdiTelevision } from "@mdi/js";
|
||||
import { css, html, LitElement, nothing } from "lit";
|
||||
import { customElement, state } from "lit/decorators";
|
||||
|
||||
@@ -207,7 +207,7 @@
|
||||
"tar": "7.5.13",
|
||||
"terser-webpack-plugin": "5.4.0",
|
||||
"ts-lit-plugin": "2.0.2",
|
||||
"typescript": "6.0.2",
|
||||
"typescript": "5.9.3",
|
||||
"typescript-eslint": "8.57.2",
|
||||
"vite-tsconfig-paths": "6.1.1",
|
||||
"vitest": "4.1.2",
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
/* eslint-disable no-console */
|
||||
/// <reference types="chromecast-caf-sender" />
|
||||
|
||||
import type { Auth } from "home-assistant-js-websocket";
|
||||
import { castApiAvailable } from "./cast_framework";
|
||||
|
||||
@@ -38,14 +38,6 @@ export interface HASSDomEvent<T> extends Event {
|
||||
detail: T;
|
||||
}
|
||||
|
||||
export type HASSDomTargetEvent<T extends EventTarget> = Event & {
|
||||
target: T;
|
||||
};
|
||||
|
||||
export type HASSDomCurrentTargetEvent<T extends EventTarget> = Event & {
|
||||
currentTarget: T;
|
||||
};
|
||||
|
||||
/**
|
||||
* Dispatches a custom event with an optional detail value.
|
||||
*
|
||||
|
||||
@@ -606,7 +606,13 @@ export class HaChartBase extends LitElement {
|
||||
id: "dataZoom",
|
||||
type: "inside",
|
||||
orient: "horizontal",
|
||||
filterMode: this._getDataZoomFilterMode() as any,
|
||||
// "boundaryFilter" is a custom mode added via axis-proxy-patch.ts.
|
||||
// It rescales the Y-axis to the visible data while keeping one point
|
||||
// just outside each boundary to avoid line gaps at the zoom edges.
|
||||
// Only use it for line charts — it causes issues with bar charts.
|
||||
filterMode: (ensureArray(this.data).every((s) => s.type === "line")
|
||||
? "boundaryFilter"
|
||||
: "filter") as any,
|
||||
xAxisIndex: 0,
|
||||
moveOnMouseMove: !this._isTouchDevice || this._isZoomed,
|
||||
preventDefaultMouseMove: !this._isTouchDevice || this._isZoomed,
|
||||
@@ -614,23 +620,6 @@ export class HaChartBase extends LitElement {
|
||||
};
|
||||
}
|
||||
|
||||
// "boundaryFilter" is a custom mode added via axis-proxy-patch.ts.
|
||||
// It rescales the Y-axis to the visible data while keeping one point
|
||||
// just outside each boundary to avoid line gaps at the zoom edges.
|
||||
// Use "filter" for bar charts since boundaryFilter causes rendering issues.
|
||||
// Use "weakFilter" for other types (e.g. custom/timeline) so bars
|
||||
// spanning the visible range boundary are kept.
|
||||
private _getDataZoomFilterMode(): string {
|
||||
const series = ensureArray(this.data);
|
||||
if (series.every((s) => s.type === "line")) {
|
||||
return "boundaryFilter";
|
||||
}
|
||||
if (series.some((s) => s.type === "bar")) {
|
||||
return "filter";
|
||||
}
|
||||
return "weakFilter";
|
||||
}
|
||||
|
||||
private _createOptions(): ECOption {
|
||||
let xAxis = this.options?.xAxis;
|
||||
if (xAxis) {
|
||||
|
||||
@@ -17,10 +17,6 @@ import memoizeOne from "memoize-one";
|
||||
import { STRINGS_SEPARATOR_DOT } from "../../common/const";
|
||||
import { restoreScroll } from "../../common/decorators/restore-scroll";
|
||||
import { fireEvent } from "../../common/dom/fire_event";
|
||||
import type {
|
||||
HASSDomCurrentTargetEvent,
|
||||
HASSDomTargetEvent,
|
||||
} from "../../common/dom/fire_event";
|
||||
import { stringCompare } from "../../common/string/compare";
|
||||
import type { LocalizeFunc } from "../../common/translations/localize";
|
||||
import { debounce } from "../../common/util/debounce";
|
||||
@@ -107,7 +103,6 @@ export interface DataTableRowData {
|
||||
export type SortableColumnContainer = Record<string, ClonedDataTableColumnData>;
|
||||
|
||||
const UNDEFINED_GROUP_KEY = "zzzzz_undefined";
|
||||
const AUTO_FOCUS_ALLOWED_ACTIVE_TAGS = ["BODY", "HTML", "HOME-ASSISTANT"];
|
||||
|
||||
@customElement("ha-data-table")
|
||||
export class HaDataTable extends LitElement {
|
||||
@@ -171,10 +166,6 @@ export class HaDataTable extends LitElement {
|
||||
|
||||
@query("slot[name='header']") private _header!: HTMLSlotElement;
|
||||
|
||||
@query(".mdc-data-table__header-row") private _headerRow?: HTMLDivElement;
|
||||
|
||||
@query("lit-virtualizer") private _scroller?: HTMLElement;
|
||||
|
||||
@state() private _collapsedGroups: string[] = [];
|
||||
|
||||
@state() private _lastSelectedRowId: string | null = null;
|
||||
@@ -189,8 +180,6 @@ export class HaDataTable extends LitElement {
|
||||
|
||||
private _lastUpdate = 0;
|
||||
|
||||
private _didAutoFocusScroller = false;
|
||||
|
||||
// @ts-ignore
|
||||
@restoreScroll(".scroller") private _savedScrollPos?: number;
|
||||
|
||||
@@ -253,26 +242,16 @@ export class HaDataTable extends LitElement {
|
||||
this.updateComplete.then(() => this._calcTableHeight());
|
||||
}
|
||||
|
||||
protected updated(changedProps: PropertyValues) {
|
||||
if (!this._headerRow) {
|
||||
protected updated() {
|
||||
const header = this.renderRoot.querySelector(".mdc-data-table__header-row");
|
||||
if (!header) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this._headerRow.scrollWidth > this._headerRow.clientWidth) {
|
||||
this.style.setProperty(
|
||||
"--table-row-width",
|
||||
`${this._headerRow.scrollWidth}px`
|
||||
);
|
||||
if (header.scrollWidth > header.clientWidth) {
|
||||
this.style.setProperty("--table-row-width", `${header.scrollWidth}px`);
|
||||
} else {
|
||||
this.style.removeProperty("--table-row-width");
|
||||
}
|
||||
|
||||
this._focusTableOnLoad();
|
||||
|
||||
// Refocus on toggle checkbox changes
|
||||
if (changedProps.has("selectable")) {
|
||||
this._focusScroller();
|
||||
}
|
||||
}
|
||||
|
||||
public willUpdate(properties: PropertyValues) {
|
||||
@@ -538,7 +517,6 @@ export class HaDataTable extends LitElement {
|
||||
<lit-virtualizer
|
||||
scroller
|
||||
class="mdc-data-table__content scroller ha-scrollbar"
|
||||
tabindex=${ifDefined(!this.autoHeight ? "0" : undefined)}
|
||||
@scroll=${this._saveScrollPos}
|
||||
.items=${this._groupData(
|
||||
this._filteredData,
|
||||
@@ -851,10 +829,8 @@ export class HaDataTable extends LitElement {
|
||||
): Promise<DataTableRowData[]> => filterData(data, columns, filter)
|
||||
);
|
||||
|
||||
private _handleHeaderClick(
|
||||
ev: HASSDomCurrentTargetEvent<HTMLElement & { columnId: string }>
|
||||
) {
|
||||
const columnId = ev.currentTarget.columnId;
|
||||
private _handleHeaderClick(ev: Event) {
|
||||
const columnId = (ev.currentTarget as any).columnId;
|
||||
if (!this.columns[columnId].sortable) {
|
||||
return;
|
||||
}
|
||||
@@ -872,12 +848,11 @@ export class HaDataTable extends LitElement {
|
||||
column: columnId,
|
||||
direction: this.sortDirection,
|
||||
});
|
||||
|
||||
this._focusScroller();
|
||||
}
|
||||
|
||||
private _handleHeaderRowCheckboxClick(ev: HASSDomTargetEvent<HaCheckbox>) {
|
||||
if (ev.target.checked) {
|
||||
private _handleHeaderRowCheckboxClick(ev: Event) {
|
||||
const checkbox = ev.target as HaCheckbox;
|
||||
if (checkbox.checked) {
|
||||
this.selectAll();
|
||||
} else {
|
||||
this._checkedRows = [];
|
||||
@@ -886,10 +861,9 @@ export class HaDataTable extends LitElement {
|
||||
this._lastSelectedRowId = null;
|
||||
}
|
||||
|
||||
private _handleRowCheckboxClicked = (
|
||||
ev: HASSDomCurrentTargetEvent<HaCheckbox & { rowId: string }>
|
||||
) => {
|
||||
const rowId = ev.currentTarget.rowId;
|
||||
private _handleRowCheckboxClicked = (ev: Event) => {
|
||||
const checkbox = ev.currentTarget as HaCheckbox;
|
||||
const rowId = (checkbox as any).rowId;
|
||||
|
||||
const groupedData = this._groupData(
|
||||
this._filteredData,
|
||||
@@ -926,7 +900,7 @@ export class HaDataTable extends LitElement {
|
||||
...this._selectRange(groupedData, lastSelectedRowIndex, rowIndex),
|
||||
];
|
||||
}
|
||||
} else if (!ev.currentTarget.checked) {
|
||||
} else if (!checkbox.checked) {
|
||||
if (!this._checkedRows.includes(rowId)) {
|
||||
this._checkedRows = [...this._checkedRows, rowId];
|
||||
}
|
||||
@@ -964,9 +938,7 @@ export class HaDataTable extends LitElement {
|
||||
return checkedRows;
|
||||
}
|
||||
|
||||
private _handleRowClick = (
|
||||
ev: HASSDomCurrentTargetEvent<HTMLElement & { rowId: string }>
|
||||
) => {
|
||||
private _handleRowClick = (ev: Event) => {
|
||||
if (
|
||||
ev
|
||||
.composedPath()
|
||||
@@ -982,13 +954,14 @@ export class HaDataTable extends LitElement {
|
||||
) {
|
||||
return;
|
||||
}
|
||||
const rowId = ev.currentTarget.rowId;
|
||||
const rowId = (ev.currentTarget as any).rowId;
|
||||
fireEvent(this, "row-click", { id: rowId }, { bubbles: false });
|
||||
};
|
||||
|
||||
private _setTitle(ev: HASSDomCurrentTargetEvent<HTMLElement>) {
|
||||
if (ev.currentTarget.scrollWidth > ev.currentTarget.offsetWidth) {
|
||||
ev.currentTarget.setAttribute("title", ev.currentTarget.innerText);
|
||||
private _setTitle(ev: Event) {
|
||||
const target = ev.currentTarget as HTMLElement;
|
||||
if (target.scrollWidth > target.offsetWidth) {
|
||||
target.setAttribute("title", target.innerText);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1010,27 +983,6 @@ export class HaDataTable extends LitElement {
|
||||
this._debounceSearch((ev.target as HTMLInputElement).value);
|
||||
}
|
||||
|
||||
private _focusTableOnLoad() {
|
||||
if (
|
||||
this._didAutoFocusScroller ||
|
||||
this.autoHeight ||
|
||||
(document.activeElement &&
|
||||
!AUTO_FOCUS_ALLOWED_ACTIVE_TAGS.includes(
|
||||
document.activeElement.tagName
|
||||
))
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._focusScroller();
|
||||
}
|
||||
|
||||
private _focusScroller(): void {
|
||||
this._scroller?.focus({
|
||||
preventScroll: true,
|
||||
});
|
||||
}
|
||||
|
||||
private async _calcTableHeight() {
|
||||
if (this.autoHeight) {
|
||||
return;
|
||||
@@ -1040,27 +992,23 @@ export class HaDataTable extends LitElement {
|
||||
}
|
||||
|
||||
@eventOptions({ passive: true })
|
||||
private _saveScrollPos(e: HASSDomTargetEvent<HTMLDivElement>) {
|
||||
this._savedScrollPos = e.target.scrollTop;
|
||||
private _saveScrollPos(e: Event) {
|
||||
this._savedScrollPos = (e.target as HTMLDivElement).scrollTop;
|
||||
|
||||
if (this._headerRow) {
|
||||
this._headerRow.scrollLeft = e.target.scrollLeft;
|
||||
}
|
||||
this.renderRoot.querySelector(".mdc-data-table__header-row")!.scrollLeft = (
|
||||
e.target as HTMLDivElement
|
||||
).scrollLeft;
|
||||
}
|
||||
|
||||
@eventOptions({ passive: true })
|
||||
private _scrollContent(e: HASSDomTargetEvent<HTMLDivElement>) {
|
||||
if (!this._scroller) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._scroller.scrollLeft = e.target.scrollLeft;
|
||||
private _scrollContent(e: Event) {
|
||||
this.renderRoot.querySelector("lit-virtualizer")!.scrollLeft = (
|
||||
e.target as HTMLDivElement
|
||||
).scrollLeft;
|
||||
}
|
||||
|
||||
private _collapseGroup = (
|
||||
ev: HASSDomCurrentTargetEvent<HTMLElement & { group: string }>
|
||||
) => {
|
||||
const groupName = ev.currentTarget.group;
|
||||
private _collapseGroup = (ev: Event) => {
|
||||
const groupName = (ev.currentTarget as any).group;
|
||||
if (this._collapsedGroups.includes(groupName)) {
|
||||
this._collapsedGroups = this._collapsedGroups.filter(
|
||||
(grp) => grp !== groupName
|
||||
@@ -1483,11 +1431,6 @@ export class HaDataTable extends LitElement {
|
||||
contain: size layout !important;
|
||||
overscroll-behavior: contain;
|
||||
}
|
||||
|
||||
lit-virtualizer:focus,
|
||||
lit-virtualizer:focus-visible {
|
||||
outline: none;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
|
||||
@@ -171,7 +171,7 @@ export class HaGauge extends LitElement {
|
||||
? svg`
|
||||
<path
|
||||
class="needle"
|
||||
d="M -36,-2 L -44,-1 A 1,1,0,0,0,-44,1 L -36,2 A 2,2,0,0,0,-36,-2 Z"
|
||||
d="M -34,-3 L -48,-1 A 1,1,0,0,0,-48,1 L -34,3 A 2,2,0,0,0,-34,-3 Z"
|
||||
|
||||
style=${styleMap({ transform: `rotate(${this._angle}deg)` })}
|
||||
/>
|
||||
@@ -243,19 +243,19 @@ export class HaGauge extends LitElement {
|
||||
.levels-base {
|
||||
fill: none;
|
||||
stroke: var(--primary-background-color);
|
||||
stroke-width: 6;
|
||||
stroke-width: 12;
|
||||
stroke-linecap: butt;
|
||||
}
|
||||
|
||||
.level {
|
||||
fill: none;
|
||||
stroke-width: 6;
|
||||
stroke-width: 12;
|
||||
stroke-linecap: butt;
|
||||
}
|
||||
|
||||
.value {
|
||||
fill: none;
|
||||
stroke-width: 6;
|
||||
stroke-width: 12;
|
||||
stroke: var(--gauge-color);
|
||||
stroke-linecap: butt;
|
||||
transition: stroke-dashoffset 1s ease 0s;
|
||||
|
||||
@@ -630,7 +630,7 @@ export class HaInput extends LitElement {
|
||||
}
|
||||
|
||||
wa-input::part(hint) {
|
||||
min-height: var(--ha-space-5);
|
||||
height: var(--ha-space-5);
|
||||
margin-block-start: 0;
|
||||
margin-inline-start: var(--ha-space-3);
|
||||
font-size: var(--ha-font-size-xs);
|
||||
@@ -641,7 +641,6 @@ export class HaInput extends LitElement {
|
||||
|
||||
wa-input.hint-hidden::part(hint) {
|
||||
height: 0;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.error {
|
||||
|
||||
@@ -826,16 +826,16 @@ const describeLegacyTrigger = (
|
||||
: trigger.entity_id;
|
||||
|
||||
let offsetChoice = "other";
|
||||
let offset = "";
|
||||
let offset: string | string[] = "";
|
||||
if (trigger.offset) {
|
||||
offsetChoice = trigger.offset.startsWith("-") ? "before" : "after";
|
||||
const parts = trigger.offset.startsWith("-")
|
||||
offset = trigger.offset.startsWith("-")
|
||||
? trigger.offset.substring(1).split(":")
|
||||
: trigger.offset.split(":");
|
||||
const duration = {
|
||||
hours: parts.length > 0 ? +parts[0] : 0,
|
||||
minutes: parts.length > 1 ? +parts[1] : 0,
|
||||
seconds: parts.length > 2 ? +parts[2] : 0,
|
||||
hours: offset.length > 0 ? +offset[0] : 0,
|
||||
minutes: offset.length > 1 ? +offset[1] : 0,
|
||||
seconds: offset.length > 2 ? +offset[2] : 0,
|
||||
};
|
||||
offset = formatDurationLong(hass.locale, duration);
|
||||
if (offset === "") {
|
||||
|
||||
@@ -20,7 +20,6 @@ export interface CoreFrontendSystemData {
|
||||
export interface HomeFrontendSystemData {
|
||||
favorite_entities?: string[];
|
||||
welcome_banner_dismissed?: boolean;
|
||||
hidden_summaries?: string[];
|
||||
}
|
||||
|
||||
declare global {
|
||||
|
||||
@@ -103,6 +103,7 @@ class DialogRestart extends LitElement {
|
||||
return nothing;
|
||||
}
|
||||
|
||||
const showReload = this.hass.userData?.showAdvanced;
|
||||
const showRebootShutdown = !!this._hostInfo;
|
||||
|
||||
const dialogTitle = this.hass.localize("ui.dialogs.restart.heading");
|
||||
@@ -134,24 +135,30 @@ class DialogRestart extends LitElement {
|
||||
`
|
||||
: html`
|
||||
<ha-md-list dialogInitialFocus>
|
||||
<ha-md-list-item
|
||||
type="button"
|
||||
@click=${this._reload}
|
||||
.disabled=${this._loadingBackupInfo}
|
||||
>
|
||||
<div slot="headline">
|
||||
${this.hass.localize("ui.dialogs.restart.reload.title")}
|
||||
</div>
|
||||
<div slot="supporting-text">
|
||||
${this.hass.localize(
|
||||
"ui.dialogs.restart.reload.description"
|
||||
)}
|
||||
</div>
|
||||
<div slot="start" class="icon-background reload">
|
||||
<ha-svg-icon .path=${mdiAutoFix}></ha-svg-icon>
|
||||
</div>
|
||||
<ha-icon-next slot="end"></ha-icon-next>
|
||||
</ha-md-list-item>
|
||||
${showReload
|
||||
? html`
|
||||
<ha-md-list-item
|
||||
type="button"
|
||||
@click=${this._reload}
|
||||
.disabled=${this._loadingBackupInfo}
|
||||
>
|
||||
<div slot="headline">
|
||||
${this.hass.localize(
|
||||
"ui.dialogs.restart.reload.title"
|
||||
)}
|
||||
</div>
|
||||
<div slot="supporting-text">
|
||||
${this.hass.localize(
|
||||
"ui.dialogs.restart.reload.description"
|
||||
)}
|
||||
</div>
|
||||
<div slot="start" class="icon-background reload">
|
||||
<ha-svg-icon .path=${mdiAutoFix}></ha-svg-icon>
|
||||
</div>
|
||||
<ha-icon-next slot="end"></ha-icon-next>
|
||||
</ha-md-list-item>
|
||||
`
|
||||
: nothing}
|
||||
<ha-md-list-item
|
||||
type="button"
|
||||
.action=${"restart"}
|
||||
|
||||
@@ -107,6 +107,19 @@ class HaPanelDevAction extends LitElement {
|
||||
this._yamlEditor?.setValue(this._serviceData)
|
||||
);
|
||||
}
|
||||
} else if (!this._serviceData?.action) {
|
||||
const domain = Object.keys(this.hass.services).sort()[0];
|
||||
const service = Object.keys(this.hass.services[domain]).sort()[0];
|
||||
this._serviceData = {
|
||||
action: `${domain}.${service}`,
|
||||
target: {},
|
||||
data: {},
|
||||
};
|
||||
if (this._yamlMode) {
|
||||
this.updateComplete.then(() =>
|
||||
this._yamlEditor?.setValue(this._serviceData)
|
||||
);
|
||||
}
|
||||
}
|
||||
this._checkUiSupported();
|
||||
}
|
||||
|
||||
@@ -176,14 +176,13 @@ export class HaConfigDevicePage extends LitElement {
|
||||
private _entities = memoizeOne(
|
||||
(
|
||||
deviceId: string,
|
||||
entities: EntityRegistryEntry[],
|
||||
devices: HomeAssistant["devices"]
|
||||
entities: EntityRegistryEntry[]
|
||||
): EntityRegistryStateEntry[] =>
|
||||
entities
|
||||
.filter((entity) => entity.device_id === deviceId)
|
||||
.map((entity) => ({
|
||||
...entity,
|
||||
stateName: this._computeEntityName(entity, devices),
|
||||
stateName: this._computeEntityName(entity),
|
||||
}))
|
||||
.sort((ent1, ent2) =>
|
||||
stringCompare(
|
||||
@@ -341,11 +340,7 @@ export class HaConfigDevicePage extends LitElement {
|
||||
this.entries,
|
||||
this.manifests
|
||||
);
|
||||
const entities = this._entities(
|
||||
this.deviceId,
|
||||
this._entityReg,
|
||||
this.hass.devices
|
||||
);
|
||||
const entities = this._entities(this.deviceId, this._entityReg);
|
||||
const entitiesByCategory = this._entitiesByCategory(entities);
|
||||
const batteryEntity = this._batteryEntity(entities);
|
||||
const batteryChargingEntity = this._batteryChargingEntity(entities);
|
||||
@@ -1149,11 +1144,7 @@ export class HaConfigDevicePage extends LitElement {
|
||||
});
|
||||
}
|
||||
|
||||
const entities = this._entities(
|
||||
this.deviceId,
|
||||
this._entityReg,
|
||||
this.hass.devices
|
||||
);
|
||||
const entities = this._entities(this.deviceId, this._entityReg);
|
||||
|
||||
const assistSatellite = entities.find(
|
||||
(ent) => computeDomain(ent.entity_id) === "assist_satellite"
|
||||
@@ -1280,13 +1271,10 @@ export class HaConfigDevicePage extends LitElement {
|
||||
}
|
||||
}
|
||||
|
||||
private _computeEntityName(
|
||||
entity: EntityRegistryEntry,
|
||||
devices: HomeAssistant["devices"]
|
||||
) {
|
||||
const device = devices[this.deviceId];
|
||||
private _computeEntityName(entity: EntityRegistryEntry) {
|
||||
const device = this.hass.devices[this.deviceId];
|
||||
return (
|
||||
computeEntityEntryName(entity, devices) ||
|
||||
computeEntityEntryName(entity, this.hass.devices) ||
|
||||
computeDeviceNameDisplay(device, this.hass)
|
||||
);
|
||||
}
|
||||
@@ -1305,11 +1293,9 @@ export class HaConfigDevicePage extends LitElement {
|
||||
|
||||
private _createScene() {
|
||||
const entities: SceneEntities = {};
|
||||
this._entities(this.deviceId, this._entityReg, this.hass.devices).forEach(
|
||||
(entity) => {
|
||||
entities[entity.entity_id] = "";
|
||||
}
|
||||
);
|
||||
this._entities(this.deviceId, this._entityReg).forEach((entity) => {
|
||||
entities[entity.entity_id] = "";
|
||||
});
|
||||
showSceneEditor({
|
||||
entities,
|
||||
});
|
||||
@@ -1374,11 +1360,9 @@ export class HaConfigDevicePage extends LitElement {
|
||||
}
|
||||
|
||||
private _resetEntityIds = () => {
|
||||
const entities = this._entities(
|
||||
this.deviceId,
|
||||
this._entityReg,
|
||||
this.hass.devices
|
||||
).map((e) => e.entity_id);
|
||||
const entities = this._entities(this.deviceId, this._entityReg).map(
|
||||
(e) => e.entity_id
|
||||
);
|
||||
regenerateEntityIds(this, this.hass, entities);
|
||||
};
|
||||
|
||||
@@ -1470,11 +1454,7 @@ export class HaConfigDevicePage extends LitElement {
|
||||
) {
|
||||
return;
|
||||
}
|
||||
const entities = this._entities(
|
||||
this.deviceId,
|
||||
this._entityReg,
|
||||
this.hass.devices
|
||||
);
|
||||
const entities = this._entities(this.deviceId, this._entityReg);
|
||||
|
||||
const updateProms = entities.map((entity) => {
|
||||
const name = entity.name || entity.stateName;
|
||||
|
||||
@@ -74,14 +74,19 @@ class HaConfigHardwareAll extends LitElement {
|
||||
);
|
||||
|
||||
private _data = memoizeOne(
|
||||
(hardware: HassioHardwareInfo): DataTableRowData[] =>
|
||||
hardware.devices.map((device) => ({
|
||||
...device,
|
||||
id: device.dev_path,
|
||||
attributes_string: Object.entries(device.attributes)
|
||||
.map(([key, value]) => `${key}: ${value}`)
|
||||
.join(" "),
|
||||
}))
|
||||
(showAdvanced: boolean, hardware: HassioHardwareInfo): DataTableRowData[] =>
|
||||
hardware.devices
|
||||
.filter(
|
||||
(device) =>
|
||||
showAdvanced || ["tty", "gpio", "input"].includes(device.subsystem)
|
||||
)
|
||||
.map((device) => ({
|
||||
...device,
|
||||
id: device.dev_path,
|
||||
attributes_string: Object.entries(device.attributes)
|
||||
.map(([key, value]) => `${key}: ${value}`)
|
||||
.join(" "),
|
||||
}))
|
||||
);
|
||||
|
||||
protected firstUpdated(): void {
|
||||
@@ -98,7 +103,12 @@ class HaConfigHardwareAll extends LitElement {
|
||||
.tabs=${hardwareTabs(this.hass)}
|
||||
clickable
|
||||
.columns=${this._columns(this.hass.localize)}
|
||||
.data=${this._hardware ? this._data(this._hardware) : []}
|
||||
.data=${this._hardware
|
||||
? this._data(
|
||||
this.hass.userData?.showAdvanced || false,
|
||||
this._hardware
|
||||
)
|
||||
: []}
|
||||
.noDataText=${this._error ||
|
||||
this.hass.localize("ui.panel.config.hardware.loading_system_data")}
|
||||
@row-click=${this._handleRowClicked}
|
||||
|
||||
@@ -250,7 +250,7 @@ export class DialogLovelaceDashboardDetail extends LitElement {
|
||||
}
|
||||
|
||||
private _fillUrlPath(title: string) {
|
||||
if (this._urlPathChanged || !title) {
|
||||
if ((this.hass.userData?.showAdvanced && this._urlPathChanged) || !title) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,50 +1,17 @@
|
||||
import { css, html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { styleMap } from "lit/directives/style-map";
|
||||
import { computeCssColor } from "../../../common/color/compute-color";
|
||||
import { fireEvent } from "../../../common/dom/fire_event";
|
||||
import "../../../components/entity/ha-entities-picker";
|
||||
import "../../../components/ha-alert";
|
||||
import "../../../components/ha-button";
|
||||
import "../../../components/ha-dialog-footer";
|
||||
import "../../../components/ha-dialog";
|
||||
import "../../../components/ha-icon";
|
||||
import "../../../components/ha-switch";
|
||||
import type { HomeFrontendSystemData } from "../../../data/frontend";
|
||||
import type { HassDialog } from "../../../dialogs/make-dialog-manager";
|
||||
import {
|
||||
getSummaryLabel,
|
||||
HOME_SUMMARIES_ICONS,
|
||||
type HomeSummary,
|
||||
} from "../../lovelace/strategies/home/helpers/home-summaries";
|
||||
import { haStyleDialog } from "../../../resources/styles";
|
||||
import type { HomeAssistant } from "../../../types";
|
||||
import type { EditHomeDialogParams } from "./show-dialog-edit-home";
|
||||
|
||||
interface SummaryInfo {
|
||||
key: string;
|
||||
icon: string;
|
||||
color: string;
|
||||
}
|
||||
|
||||
// Ordered to match dashboard rendering order
|
||||
const SUMMARY_ITEMS: SummaryInfo[] = [
|
||||
{ key: "light", icon: HOME_SUMMARIES_ICONS.light, color: "amber" },
|
||||
{ key: "climate", icon: HOME_SUMMARIES_ICONS.climate, color: "deep-orange" },
|
||||
{
|
||||
key: "security",
|
||||
icon: HOME_SUMMARIES_ICONS.security,
|
||||
color: "blue-grey",
|
||||
},
|
||||
{
|
||||
key: "media_players",
|
||||
icon: HOME_SUMMARIES_ICONS.media_players,
|
||||
color: "blue",
|
||||
},
|
||||
{ key: "weather", icon: "mdi:weather-partly-cloudy", color: "teal" },
|
||||
{ key: "energy", icon: HOME_SUMMARIES_ICONS.energy, color: "amber" },
|
||||
];
|
||||
|
||||
@customElement("dialog-edit-home")
|
||||
export class DialogEditHome
|
||||
extends LitElement
|
||||
@@ -83,8 +50,6 @@ export class DialogEditHome
|
||||
return nothing;
|
||||
}
|
||||
|
||||
const hiddenSummaries = new Set(this._config?.hidden_summaries || []);
|
||||
|
||||
return html`
|
||||
<ha-dialog
|
||||
.hass=${this.hass}
|
||||
@@ -114,33 +79,6 @@ export class DialogEditHome
|
||||
@value-changed=${this._favoriteEntitiesChanged}
|
||||
></ha-entities-picker>
|
||||
|
||||
<h3 class="section-header">
|
||||
${this.hass.localize("ui.panel.home.editor.summaries")}
|
||||
</h3>
|
||||
<p class="section-description">
|
||||
${this.hass.localize("ui.panel.home.editor.summaries_description")}
|
||||
</p>
|
||||
<div class="summary-toggles">
|
||||
${SUMMARY_ITEMS.map((item) => {
|
||||
const label = this._getSummaryLabel(item.key);
|
||||
const color = computeCssColor(item.color);
|
||||
return html`
|
||||
<label class="summary-toggle">
|
||||
<ha-icon
|
||||
.icon=${item.icon}
|
||||
style=${styleMap({ "--mdc-icon-size": "24px", color })}
|
||||
></ha-icon>
|
||||
<span class="summary-label">${label}</span>
|
||||
<ha-switch
|
||||
.checked=${!hiddenSummaries.has(item.key)}
|
||||
.summary=${item.key}
|
||||
@change=${this._summaryToggleChanged}
|
||||
></ha-switch>
|
||||
</label>
|
||||
`;
|
||||
})}
|
||||
</div>
|
||||
|
||||
<ha-alert alert-type="info">
|
||||
${this.hass.localize("ui.panel.home.editor.areas_hint", {
|
||||
areas_page: html`<a
|
||||
@@ -172,38 +110,6 @@ export class DialogEditHome
|
||||
`;
|
||||
}
|
||||
|
||||
private _getSummaryLabel(key: string): string {
|
||||
if (key === "weather") {
|
||||
return this.hass.localize(
|
||||
"ui.panel.lovelace.strategy.home.summary_list.weather"
|
||||
);
|
||||
}
|
||||
return getSummaryLabel(this.hass.localize, key as HomeSummary);
|
||||
}
|
||||
|
||||
private _summaryToggleChanged(ev: Event): void {
|
||||
const target = ev.target as HTMLElement & {
|
||||
checked: boolean;
|
||||
summary: string;
|
||||
};
|
||||
const summary = target.summary;
|
||||
const checked = target.checked;
|
||||
|
||||
const hiddenSummaries = new Set(this._config?.hidden_summaries || []);
|
||||
|
||||
if (checked) {
|
||||
hiddenSummaries.delete(summary);
|
||||
} else {
|
||||
hiddenSummaries.add(summary);
|
||||
}
|
||||
|
||||
this._config = {
|
||||
...this._config,
|
||||
hidden_summaries:
|
||||
hiddenSummaries.size > 0 ? [...hiddenSummaries] : undefined,
|
||||
};
|
||||
}
|
||||
|
||||
private _favoriteEntitiesChanged(ev: CustomEvent): void {
|
||||
const entities = ev.detail.value as string[];
|
||||
this._config = {
|
||||
@@ -242,36 +148,6 @@ export class DialogEditHome
|
||||
color: var(--secondary-text-color);
|
||||
}
|
||||
|
||||
.section-header {
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
margin: var(--ha-space-6) 0 var(--ha-space-1) 0;
|
||||
}
|
||||
|
||||
.section-description {
|
||||
margin: 0 0 var(--ha-space-2) 0;
|
||||
color: var(--secondary-text-color);
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.summary-toggles {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.summary-toggle {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--ha-space-3);
|
||||
padding: var(--ha-space-2) 0;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.summary-label {
|
||||
flex: 1;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
ha-entities-picker {
|
||||
display: block;
|
||||
}
|
||||
|
||||
@@ -318,7 +318,6 @@ class PanelHome extends LitElement {
|
||||
type: "home",
|
||||
favorite_entities: this._config.favorite_entities,
|
||||
home_panel: true,
|
||||
hidden_summaries: this._config.hidden_summaries,
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -187,15 +187,8 @@ export class HuiStatisticsGraphCard extends LitElement implements LovelaceCard {
|
||||
this._names = {};
|
||||
this._entities.forEach((config) => {
|
||||
const stateObj = this.hass!.states[config.entity];
|
||||
if (stateObj) {
|
||||
this._names[config.entity] =
|
||||
this.hass!.formatEntityName(stateObj, config.name) || config.entity;
|
||||
} else {
|
||||
this._names[config.entity] =
|
||||
(typeof config.name === "string" ? config.name : undefined) ||
|
||||
this._metadata?.[config.entity]?.name ||
|
||||
config.entity;
|
||||
}
|
||||
this._names[config.entity] =
|
||||
this.hass!.formatEntityName(stateObj, config.name) || config.entity;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -247,7 +247,7 @@ class HUIRoot extends LitElement {
|
||||
icon: mdiFileMultiple,
|
||||
key: "ui.panel.lovelace.editor.menu.manage_resources",
|
||||
overflowAction: this._handleManageResources,
|
||||
visible: this._editMode,
|
||||
visible: this._editMode && this.hass.userData?.showAdvanced,
|
||||
overflow: true,
|
||||
},
|
||||
{
|
||||
|
||||
@@ -17,7 +17,6 @@ export interface HomeDashboardStrategyConfig {
|
||||
type: "home";
|
||||
favorite_entities?: string[];
|
||||
home_panel?: boolean;
|
||||
hidden_summaries?: string[];
|
||||
}
|
||||
|
||||
@customElement("home-dashboard-strategy")
|
||||
@@ -95,7 +94,6 @@ export class HomeDashboardStrategy extends ReactiveElement {
|
||||
type: "home-overview",
|
||||
favorite_entities: config.favorite_entities,
|
||||
home_panel: config.home_panel,
|
||||
hidden_summaries: config.hidden_summaries,
|
||||
} satisfies HomeOverviewViewStrategyConfig,
|
||||
},
|
||||
...areaViews,
|
||||
|
||||
@@ -39,7 +39,6 @@ export interface HomeOverviewViewStrategyConfig {
|
||||
type: "home-overview";
|
||||
favorite_entities?: string[];
|
||||
home_panel?: boolean;
|
||||
hidden_summaries?: string[];
|
||||
}
|
||||
|
||||
const computeAreaCard = (
|
||||
@@ -255,8 +254,6 @@ export class HomeOverviewViewStrategy extends ReactiveElement {
|
||||
) ??
|
||||
false);
|
||||
|
||||
const hiddenSummaries = new Set(config.hidden_summaries || []);
|
||||
|
||||
// Build summary cards (used in both mobile section and sidebar)
|
||||
const summaryCards: LovelaceCardConfig[] = [
|
||||
// Repairs card - only visible to admins, hides when empty
|
||||
@@ -283,7 +280,6 @@ export class HomeOverviewViewStrategy extends ReactiveElement {
|
||||
hide_empty: true,
|
||||
} satisfies DiscoveredDevicesCardConfig,
|
||||
hasLights &&
|
||||
!hiddenSummaries.has("light") &&
|
||||
({
|
||||
type: "home-summary",
|
||||
summary: "light",
|
||||
@@ -293,7 +289,6 @@ export class HomeOverviewViewStrategy extends ReactiveElement {
|
||||
},
|
||||
} satisfies HomeSummaryCard),
|
||||
hasClimate &&
|
||||
!hiddenSummaries.has("climate") &&
|
||||
({
|
||||
type: "home-summary",
|
||||
summary: "climate",
|
||||
@@ -303,7 +298,6 @@ export class HomeOverviewViewStrategy extends ReactiveElement {
|
||||
},
|
||||
} satisfies HomeSummaryCard),
|
||||
hasSecurity &&
|
||||
!hiddenSummaries.has("security") &&
|
||||
({
|
||||
type: "home-summary",
|
||||
summary: "security",
|
||||
@@ -313,7 +307,6 @@ export class HomeOverviewViewStrategy extends ReactiveElement {
|
||||
},
|
||||
} satisfies HomeSummaryCard),
|
||||
hasMediaPlayers &&
|
||||
!hiddenSummaries.has("media_players") &&
|
||||
({
|
||||
type: "home-summary",
|
||||
summary: "media_players",
|
||||
@@ -323,7 +316,6 @@ export class HomeOverviewViewStrategy extends ReactiveElement {
|
||||
},
|
||||
} satisfies HomeSummaryCard),
|
||||
weatherEntity &&
|
||||
!hiddenSummaries.has("weather") &&
|
||||
({
|
||||
type: "tile",
|
||||
entity: weatherEntity,
|
||||
@@ -333,7 +325,6 @@ export class HomeOverviewViewStrategy extends ReactiveElement {
|
||||
state_content: ["temperature", "state"],
|
||||
} satisfies TileCardConfig),
|
||||
hasEnergy &&
|
||||
!hiddenSummaries.has("energy") &&
|
||||
({
|
||||
type: "home-summary",
|
||||
summary: "energy",
|
||||
|
||||
@@ -2462,9 +2462,7 @@
|
||||
"favorite_entities_helper": "Display your favorite entities. Home Assistant will still suggest based on commonly used up to 8 slots.",
|
||||
"save_failed": "Failed to save Overview page configuration",
|
||||
"areas_hint": "You can rearrange your floors and areas in the order that best represents your house on the {areas_page}.",
|
||||
"areas_page": "areas page",
|
||||
"summaries": "Summaries",
|
||||
"summaries_description": "Choose which summaries to show on your overview page."
|
||||
"areas_page": "areas page"
|
||||
},
|
||||
"new_overview_dialog": {
|
||||
"title": "Welcome to your new overview",
|
||||
@@ -5041,7 +5039,7 @@
|
||||
}
|
||||
},
|
||||
"event": {
|
||||
"label": "Manual event received",
|
||||
"label": "Manual event",
|
||||
"event_type": "Event type",
|
||||
"event_data": "Event data",
|
||||
"context_users": "Limit to events triggered by",
|
||||
@@ -5534,7 +5532,7 @@
|
||||
}
|
||||
},
|
||||
"event": {
|
||||
"label": "Fire manual event",
|
||||
"label": "Manual event",
|
||||
"event": "[%key:ui::panel::config::automation::editor::triggers::type::event::event_type%]",
|
||||
"event_data": "[%key:ui::panel::config::automation::editor::triggers::type::event::event_data%]",
|
||||
"description": {
|
||||
@@ -9310,7 +9308,7 @@
|
||||
"label_mode": "Label mode",
|
||||
"maximum": "Maximum",
|
||||
"manual": "Manual",
|
||||
"manual_description": "Need to add a community card or just want to manually write the YAML?",
|
||||
"manual_description": "Need to add a custom card or just want to manually write the YAML?",
|
||||
"minimum": "Minimum",
|
||||
"name": "Name",
|
||||
"paste": "Paste from clipboard",
|
||||
@@ -9333,7 +9331,7 @@
|
||||
"suggested_cards": "Suggested cards",
|
||||
"core_cards": "Core cards",
|
||||
"energy_cards": "Energy cards",
|
||||
"custom_cards": "Community cards",
|
||||
"custom_cards": "Custom cards",
|
||||
"round_temperature": "Round temperature",
|
||||
"features": "Features",
|
||||
"actions": "Actions",
|
||||
@@ -9647,12 +9645,12 @@
|
||||
},
|
||||
"generic": {
|
||||
"manual": "Manual",
|
||||
"manual_description": "Need to add a community badge or just want to manually write the YAML?",
|
||||
"manual_description": "Need to add a custom badge or just want to manually write the YAML?",
|
||||
"paste": "Paste from clipboard",
|
||||
"paste_description": "Paste a {type} badge from the clipboard",
|
||||
"suggested_badges": "Suggested badges",
|
||||
"other_badges": "Other badges",
|
||||
"custom_badges": "Community badges"
|
||||
"custom_badges": "Custom badges"
|
||||
}
|
||||
},
|
||||
"entities": {
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
"useDefineForClassFields": false,
|
||||
// Modules
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "bundler",
|
||||
"moduleResolution": "node",
|
||||
"resolveJsonModule": true,
|
||||
// Babel handles transpiling and no need for declaration files
|
||||
"noEmit": true,
|
||||
@@ -130,35 +130,7 @@
|
||||
],
|
||||
"@formatjs/intl-relativetimeformat/polyfill-force": [
|
||||
"./node_modules/@formatjs/intl-relativetimeformat/polyfill-force.js"
|
||||
],
|
||||
"@formatjs/intl-durationformat/src/types": [
|
||||
"./node_modules/@formatjs/intl-durationformat/src/types.js"
|
||||
],
|
||||
"@formatjs/intl-pluralrules/locale-data/en": [
|
||||
"./node_modules/@formatjs/intl-pluralrules/locale-data/en.js"
|
||||
],
|
||||
"@home-assistant/webawesome/dist/components/*": [
|
||||
"./node_modules/@home-assistant/webawesome/dist/components/*"
|
||||
],
|
||||
"@home-assistant/webawesome/dist/events/*": [
|
||||
"./node_modules/@home-assistant/webawesome/dist/events/*"
|
||||
],
|
||||
"@home-assistant/webawesome/dist/internal/*": [
|
||||
"./node_modules/@home-assistant/webawesome/dist/internal/*"
|
||||
],
|
||||
"@lit-labs/virtualizer/virtualize": [
|
||||
"./node_modules/@lit-labs/virtualizer/virtualize.js"
|
||||
],
|
||||
"@lit/reactive-element/reactive-controller": [
|
||||
"./node_modules/@lit/reactive-element/reactive-controller.js"
|
||||
],
|
||||
"echarts/types/src/*": ["./node_modules/echarts/types/src/*.d.ts"],
|
||||
"echarts/lib/*": ["./node_modules/echarts/lib/*"],
|
||||
"home-assistant-js-websocket/dist/*": [
|
||||
"./node_modules/home-assistant-js-websocket/dist/*"
|
||||
],
|
||||
"lit/directives/join": ["./node_modules/lit/directives/join.js"],
|
||||
"lit/directives/ref": ["./node_modules/lit/directives/ref.js"]
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
18
yarn.lock
18
yarn.lock
@@ -9002,7 +9002,7 @@ __metadata:
|
||||
terser-webpack-plugin: "npm:5.4.0"
|
||||
tinykeys: "npm:3.0.0"
|
||||
ts-lit-plugin: "npm:2.0.2"
|
||||
typescript: "npm:6.0.2"
|
||||
typescript: "npm:5.9.3"
|
||||
typescript-eslint: "npm:8.57.2"
|
||||
vite-tsconfig-paths: "npm:6.1.1"
|
||||
vitest: "npm:4.1.2"
|
||||
@@ -13894,13 +13894,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"typescript@npm:6.0.2":
|
||||
version: 6.0.2
|
||||
resolution: "typescript@npm:6.0.2"
|
||||
"typescript@npm:5.9.3":
|
||||
version: 5.9.3
|
||||
resolution: "typescript@npm:5.9.3"
|
||||
bin:
|
||||
tsc: bin/tsc
|
||||
tsserver: bin/tsserver
|
||||
checksum: 10/b9fc2fcee7ee8e5ca6f5138181964550531e18e2d20ecb71b802d4d495881e3444e0ef94cbb6e84eb2ba41e913f6feae3ca33cc722b32e6e6dfadb32d23b80e6
|
||||
checksum: 10/c089d9d3da2729fd4ac517f9b0e0485914c4b3c26f80dc0cffcb5de1719a17951e92425d55db59515c1a7ddab65808466debb864d0d56dcf43f27007d0709594
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
@@ -13914,13 +13914,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"typescript@patch:typescript@npm%3A6.0.2#optional!builtin<compat/typescript>":
|
||||
version: 6.0.2
|
||||
resolution: "typescript@patch:typescript@npm%3A6.0.2#optional!builtin<compat/typescript>::version=6.0.2&hash=5786d5"
|
||||
"typescript@patch:typescript@npm%3A5.9.3#optional!builtin<compat/typescript>":
|
||||
version: 5.9.3
|
||||
resolution: "typescript@patch:typescript@npm%3A5.9.3#optional!builtin<compat/typescript>::version=5.9.3&hash=5786d5"
|
||||
bin:
|
||||
tsc: bin/tsc
|
||||
tsserver: bin/tsserver
|
||||
checksum: 10/e5501dfcf8c5b6b9a61f6fa53decc40187cebe3a2b7b2bd51c615007ad4cf6d58298461fef63294f6be9d5f2f33019b2a2e2bf02d9cb7d7f6ec94372bf24ffa2
|
||||
checksum: 10/696e1b017bc2635f4e0c94eb4435357701008e2f272f553d06e35b494b8ddc60aa221145e286c28ace0c89ee32827a28c2040e3a69bdc108b1a5dc8fb40b72e3
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
|
||||
Reference in New Issue
Block a user