Compare commits

..

14 Commits

Author SHA1 Message Date
Aidan Timson b574aaa084 Type feature event handlers (#53461)
* Type feature event handlers

* Address event handler review feedback

Co-authored-by: timmo001 <28114703+timmo001@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
2026-08-04 08:28:13 +03:00
dependabot[bot] c1dca9b377 Bump postcss from 8.5.19 to 8.5.25 (#53465)
Bumps [postcss](https://github.com/postcss/postcss) from 8.5.19 to 8.5.25.
- [Release notes](https://github.com/postcss/postcss/releases)
- [Changelog](https://github.com/postcss/postcss/blob/main/CHANGELOG.md)
- [Commits](https://github.com/postcss/postcss/compare/8.5.19...8.5.25)

---
updated-dependencies:
- dependency-name: postcss
  dependency-version: 8.5.25
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-04 08:25:14 +03:00
dependabot[bot] c9963b7f92 Bump ip-address from 10.2.0 to 10.4.0 (#53470)
Bumps [ip-address](https://github.com/beaugunderson/ip-address) from 10.2.0 to 10.4.0.
- [Release notes](https://github.com/beaugunderson/ip-address/releases)
- [Commits](https://github.com/beaugunderson/ip-address/compare/v10.2.0...v10.4.0)

---
updated-dependencies:
- dependency-name: ip-address
  dependency-version: 10.4.0
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-08-04 08:24:52 +03:00
karwosts 42954152dc Fix slow loading of entity rows in device page (#53471)
Fix lazy loading of entity rows in device page
2026-08-04 08:24:19 +03:00
Petar Petrov f5a28977c2 Fix energy default period being saved under the wrong storage key (#53462) 2026-08-03 19:55:41 +02:00
renovate[bot] 9b1f407ff4 Update Node.js to v24.19.0 (#53463)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-08-03 19:54:38 +02:00
Aidan Timson 277822e51c Type application event contracts (#53459)
* Type application event contracts

* Remove redundant currentTarget casts

Co-authored-by: timmo001 <28114703+timmo001@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
2026-08-03 14:57:11 +00:00
Aidan Timson 4a2bca6d76 Type component event handlers (#53458)
* Type component event handlers

* Fix event handler types per review feedback

Co-authored-by: timmo001 <28114703+timmo001@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
2026-08-03 17:47:02 +03:00
Aidan Timson 8e47b5369e Apply minimum release age to all dependencies (#53464) 2026-08-03 14:26:32 +00:00
Petar Petrov 759ab155c7 Show progress on the Zigbee backup button while the backup is created (#53422) 2026-08-03 15:06:53 +01:00
karwosts 8de939a696 Fix unsaved-mixin race condition (#53435) 2026-08-03 17:04:42 +03:00
Petar Petrov 5c3d74502b Stop the statistics graph card editor looping while typing a name (#53434) 2026-08-03 16:30:06 +03:00
Aidan Timson 0bf67b603c Type and Lit skills (#53457)
Document frontend type and Lit guidance
2026-08-03 16:28:49 +03:00
renovate[bot] 01915f2e5a Update dependency lint-staged to v17.3.0 (#53456)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-08-03 14:53:15 +03:00
123 changed files with 755 additions and 451 deletions
+39
View File
@@ -0,0 +1,39 @@
---
name: ha-frontend-lit
description: Home Assistant frontend Lit conventions. Use when working with reactive properties, internal state, DOM queries, lifecycle methods, or render-derived state.
---
# HA Frontend Lit
Use this skill when implementing or reviewing Lit component state, DOM access, lifecycle methods, or rendering behavior. Cross-load `ha-frontend-types` for Home Assistant data contracts, assertions, and lifecycle parameter types.
## Reactive Fields
This project currently uses Lit's TypeScript experimental decorators with `useDefineForClassFields: false`. Match existing declarations and do not introduce standard-decorator `accessor` syntax unless the project changes decorator mode.
- Use `@property()` for public reactive API and `@state()` for private reactive state.
- Prefer inferred types for initialized reactive fields when inference preserves the intended type; annotate when widening or an external contract requires it.
## DOM Queries
Prefer Lit's `@query()` or `@queryAll()` decorators for fixed selectors in the component's render root.
- Type the decorated field with the narrowest useful DOM or component interface.
- Keep the field optional when it may be absent at the point of access, including conditional rendering or pre-render lifecycle access.
- Use a definite assignment assertion only when every call site runs after the node is guaranteed to exist.
- The optional second argument to `@query()`, as in `@query("#target", true)`, caches the first query result. Use it only when later renders cannot replace the queried node.
- Use a direct query when the selector is dynamic or the target is outside the component's render root. Before querying a child, consider whether the required value belongs in parent state or data flow.
## Render-Derived State
- Prefer render-local values for inexpensive structures used only by that render.
- Assign a render-local value once when repeated evaluation is non-trivial or a local name improves clarity.
- Keep purely presentational derivations in `render()`. Use stored state or `willUpdate()` when the value must participate in lifecycle work, reflection, CSS, or non-render consumers.
- Use `memoizeOne` for pure, argument-derived transforms when stable input identity avoids meaningful repeated work. Keep inputs explicit and limited, and do not add caching without a credible benefit over computing the value directly.
## References
- [Reactive properties](https://lit.dev/docs/components/properties/)
- [Decorators](https://lit.dev/docs/components/decorators/)
- [Shadow DOM queries](https://lit.dev/docs/components/shadow-dom/#query)
- [Reactive update cycle](https://lit.dev/docs/components/lifecycle/)
+44
View File
@@ -0,0 +1,44 @@
---
name: ha-frontend-types
description: Home Assistant frontend TypeScript conventions. Use when defining or reviewing backend data contracts, optional schemas, shared types, assertions, or Lit lifecycle types.
---
# HA Frontend Types
Use this skill for Home Assistant-specific TypeScript contracts and type choices.
## Home Assistant Data Contracts
Verify data contracts against the source that owns them, such as Home Assistant Core, Supervisor, a WebSocket handler, or an exported component type. Do not shape a type around assumptions made by its current frontend consumers.
- Match required, optional, nullable, and defaulted fields to the producer and runtime contract. Preserve optional configuration fields when omission is supported and has defined behavior.
- Use distinct request and response types when their wire shapes differ.
- When changing a shared contract, check affected consumers, tests, fixtures, and mocks.
## Reuse Home Assistant Contracts
- Reuse the canonical Home Assistant type when one exists. Define shared contract types in the data or API module that owns them.
- Reuse types exported by components and helpers rather than reconstructing their payloads. For event types, follow `ha-frontend-events`.
- When one domain contract is used across modules, define and export it from the module that owns that contract.
Prefer an existing owning contract. Introduce a frontend-specific type when the frontend shape or boundary genuinely differs.
## Assertions
Prefer accurate types and runtime narrowing. Use assertions or TypeScript suppressions at boundaries where the runtime invariant is understood but cannot be expressed cleanly; keep them narrow and explain non-obvious invariants.
## Lit Lifecycle Types
For Lit lifecycle methods that receive changed properties, use `PropertyValues<this>` when the method only needs public reactive properties:
```ts
protected willUpdate(changedProperties: PropertyValues<this>) {
// ...
}
```
Use unparameterized `PropertyValues` when the method inspects private or protected reactive properties, which are not keys of `this`. Do not add assertions solely to retain `PropertyValues<this>`.
## Enforced Baseline
Use `import type` for type-only imports. This is enforced by the repository ESLint configuration.
+1 -1
View File
@@ -1 +1 @@
24.18.1
24.19.0
+2
View File
@@ -41,6 +41,8 @@ Detailed guidance lives in project skills under `.agents/skills/`. Load the matc
- `ha-frontend-contexts`: Lit contexts, `hass` migration, and rerender-sensitive state access.
- `ha-frontend-components`: dialogs, forms, alerts, shortcuts, tooltips, panels, and Lovelace cards.
- `ha-frontend-events`: event handler typing, custom event dispatch, and event-map declarations.
- `ha-frontend-types`: backend data contracts, optional schemas, shared types, assertions, and lifecycle types.
- `ha-frontend-lit`: reactive fields, DOM queries, lifecycle behavior, and render-derived state.
- `ha-frontend-styling`: theme variables, spacing tokens, responsive layout, RTL, and view transitions.
- `ha-frontend-testing`: lint, typecheck, Vitest, Playwright e2e dev servers, and benchmarks.
- `ha-frontend-user-facing-text`: localization, terminology, sentence case, and Home Assistant text style.
@@ -23,7 +23,7 @@ export class DemoHaProgressButton extends LitElement {
<ha-progress-button @click=${this._clickedFail}>
Fail
</ha-progress-button>
<ha-progress-button size="small" @click=${this._clickedSuccess}>
<ha-progress-button size="s" @click=${this._clickedSuccess}>
small
</ha-progress-button>
<ha-progress-button
+2 -2
View File
@@ -196,7 +196,7 @@
"jsdom": "30.0.1",
"jszip": "3.10.1",
"license-checker-rseidelsohn": "5.0.1",
"lint-staged": "17.2.0",
"lint-staged": "17.3.0",
"lit-analyzer": "2.0.3",
"lodash.merge": "4.6.2",
"lodash.template": "4.18.1",
@@ -230,6 +230,6 @@
},
"packageManager": "yarn@4.18.0",
"volta": {
"node": "24.18.1"
"node": "24.19.0"
}
}
+2 -2
View File
@@ -8,10 +8,10 @@
":prConcurrentLimit10",
":semanticCommitsDisabled",
"group:monorepos",
"group:recommended",
"security:minimumReleaseAgeNpm"
"group:recommended"
],
"enabledManagers": ["npm", "nvm", "custom.regex"],
"minimumReleaseAge": "3 days",
"postUpdateOptions": ["yarnDedupeHighest"],
"lockFileMaintenance": {
"description": ["Run after patch releases but before next beta"],
@@ -18,6 +18,8 @@ export class HaProgressButton extends LitElement {
@property() appearance: Appearance = "accent";
@property() size: "xs" | "s" | "m" | "l" | "xl" = "m";
@property({ attribute: false }) public iconPath?: string;
@property() variant: "brand" | "danger" | "neutral" | "warning" | "success" =
@@ -32,6 +34,7 @@ export class HaProgressButton extends LitElement {
return html`
<ha-button
.appearance=${appearance}
.size=${this.size}
.disabled=${this.disabled}
.loading=${this.progress}
.variant=${
@@ -118,6 +121,12 @@ export class HaProgressButton extends LitElement {
width: 100%;
}
/* The icon lives in this shadow root, so callers cannot size it themselves. */
ha-button[size="xs"] ha-svg-icon[slot="start"],
ha-button[size="s"] ha-svg-icon[slot="start"] {
--mdc-icon-size: 16px;
}
/* Fade the content out rather than hiding it, so the button keeps its
accessible name while the result icon covers it. */
ha-button.result::part(start),
+11 -4
View File
@@ -26,7 +26,10 @@ import { styleMap } from "lit/directives/style-map";
import { ensureArray } from "../../common/array/ensure-array";
import { getAllGraphColors } from "../../common/color/colors";
import { transform } from "../../common/decorators/transform";
import type { HASSDomEvent } from "../../common/dom/fire_event";
import type {
HASSDomCurrentTargetEvent,
HASSDomEvent,
} from "../../common/dom/fire_event";
import { fireEvent } from "../../common/dom/fire_event";
import { listenMediaQuery } from "../../common/dom/media_query";
import { afterNextRender } from "../../common/util/render-status";
@@ -1216,7 +1219,9 @@ export class HaChartBase extends LitElement {
}
// Long-press to solo on touch/pen devices (500ms, consistent with action-handler-directive)
private _legendPointerDown(ev: PointerEvent) {
private _legendPointerDown(
ev: PointerEvent & HASSDomCurrentTargetEvent<HTMLElement>
) {
// Mouse uses Ctrl/Cmd+click instead
if (ev.pointerType === "mouse") {
return;
@@ -1246,7 +1251,9 @@ export class HaChartBase extends LitElement {
}
}
private _toggleDataset(ev: MouseEvent) {
private _toggleDataset(
ev: MouseEvent & HASSDomCurrentTargetEvent<HTMLElement>
) {
ev.stopPropagation();
if (!this.chart) {
return;
@@ -1268,7 +1275,7 @@ export class HaChartBase extends LitElement {
this._handleDatasetToggle(id);
}
private _labelClick(ev: MouseEvent) {
private _labelClick(ev: MouseEvent & HASSDomCurrentTargetEvent<HTMLElement>) {
ev.stopPropagation();
if (!this.chart) {
return;
+8 -7
View File
@@ -2,13 +2,10 @@ import { customElement, property, state } from "lit/decorators";
import { LitElement, html, css } from "lit";
import type { EChartsType } from "echarts/core";
import type { SankeySeriesOption } from "echarts/types/dist/echarts";
import type {
CallbackDataParams,
ECElementEvent,
} from "echarts/types/src/util/types";
import type { CallbackDataParams } from "echarts/types/src/util/types";
import memoizeOne from "memoize-one";
import { ResizeController } from "@lit-labs/observers/resize-controller";
import { fireEvent } from "../../common/dom/fire_event";
import { fireEvent, type HASSDomEvent } from "../../common/dom/fire_event";
import SankeyChart from "../../resources/echarts/components/sankey/install";
import type { HomeAssistant } from "../../types";
import type { HaECOption } from "../../resources/echarts/echarts";
@@ -121,11 +118,15 @@ export class HaSankeyChart extends LitElement {
return null;
};
private _handleChartSankeyRoam = (ev: CustomEvent) => {
private _handleChartSankeyRoam = (
ev: HASSDomEvent<HASSDomEvents["chart-sankeyroam"]>
) => {
this._currentZoom = ev.detail.zoom;
};
private _handleChartClick = (ev: CustomEvent<ECElementEvent>) => {
private _handleChartClick = (
ev: HASSDomEvent<HASSDomEvents["chart-click"]>
) => {
const detail = ev.detail;
// Only handle node clicks (not links)
if (detail.dataType !== "node") {
@@ -216,11 +216,13 @@ export class StateHistoryChartLine extends LitElement {
})}`;
};
private _datasetHidden(ev: CustomEvent) {
private _datasetHidden(ev: HASSDomEvent<HASSDomEvents["dataset-hidden"]>) {
this._hiddenStats.add(ev.detail.id);
}
private _datasetUnhidden(ev: CustomEvent) {
private _datasetUnhidden(
ev: HASSDomEvent<HASSDomEvents["dataset-unhidden"]>
) {
this._hiddenStats.delete(ev.detail.id);
}
@@ -229,7 +231,7 @@ export class StateHistoryChartLine extends LitElement {
chartBase.zoom(start, end, true);
}
private _handleDataZoom(ev: CustomEvent) {
private _handleDataZoom(ev: HASSDomEvent<HASSDomEvents["chart-zoom"]>) {
fireEvent(this, "chart-zoom-with-index", {
start: ev.detail.start ?? 0,
end: ev.detail.end ?? 100,
@@ -4,7 +4,6 @@ import { customElement, property, state } from "lit/decorators";
import type {
CustomSeriesOption,
CustomSeriesRenderItem,
ECElementEvent,
TooltipPositionCallbackParams,
} from "echarts/types/dist/shared";
import { formatDateTimeWithSeconds } from "../../common/datetime/format_date_time";
@@ -21,7 +20,7 @@ import echarts from "../../resources/echarts/echarts";
import { luminosity } from "../../common/color/rgb";
import { hex2rgb } from "../../common/color/convert-color";
import { measureTextWidth } from "../../util/text";
import { fireEvent } from "../../common/dom/fire_event";
import { fireEvent, type HASSDomEvent } from "../../common/dom/fire_event";
@customElement("state-history-chart-timeline")
export class StateHistoryChartTimeline extends LitElement {
@@ -271,7 +270,7 @@ export class StateHistoryChartTimeline extends LitElement {
chartBase.zoom(start, end, true);
}
private _handleDataZoom(ev: CustomEvent) {
private _handleDataZoom(ev: HASSDomEvent<HASSDomEvents["chart-zoom"]>) {
fireEvent(this, "chart-zoom-with-index", {
start: ev.detail.start ?? 0,
end: ev.detail.end ?? 100,
@@ -385,7 +384,9 @@ export class StateHistoryChartTimeline extends LitElement {
this._chartData = datasets;
}
private _handleChartClick(e: CustomEvent<ECElementEvent>): void {
private _handleChartClick(
e: HASSDomEvent<HASSDomEvents["chart-click"]>
): void {
if (e.detail.targetType === "axisLabel") {
const dataset = this._chartData[e.detail.dataIndex];
if (dataset) {
+7 -3
View File
@@ -11,6 +11,10 @@ import {
} from "lit/decorators";
import { isComponentLoaded } from "../../common/config/is_component_loaded";
import { restoreScroll } from "../../common/decorators/restore-scroll";
import type {
HASSDomEvent,
HASSDomTargetEvent,
} from "../../common/dom/fire_event";
import type {
HistoryResult,
LineChartUnit,
@@ -316,13 +320,13 @@ export class StateHistoryCharts extends LitElement {
}
}
private _yWidthChanged(e: CustomEvent<HASSDomEvents["y-width-changed"]>) {
private _yWidthChanged(e: HASSDomEvent<HASSDomEvents["y-width-changed"]>) {
this._childYWidths[e.detail.chartIndex] = e.detail.value;
this._maxYWidth = Math.max(...Object.values(this._childYWidths), 0);
}
private _handleTimelineSync(
e: CustomEvent<HASSDomEvents["chart-zoom-with-index"]>
e: HASSDomEvent<HASSDomEvents["chart-zoom-with-index"]>
) {
if (!this.syncCharts || this._isSyncing) {
return;
@@ -384,7 +388,7 @@ export class StateHistoryCharts extends LitElement {
}
@eventOptions({ passive: true })
private _saveScrollPos(e: Event) {
private _saveScrollPos(e: HASSDomTargetEvent<HTMLDivElement>) {
this._savedScrollPos = (e.target as HTMLDivElement).scrollTop;
}
+4 -2
View File
@@ -204,12 +204,14 @@ export class StatisticsChart extends LitElement {
`;
}
private _datasetHidden(ev: CustomEvent) {
private _datasetHidden(ev: HASSDomEvent<HASSDomEvents["dataset-hidden"]>) {
this._hiddenStats.add(ev.detail.id);
this.requestUpdate("_hiddenStats");
}
private _datasetUnhidden(ev: CustomEvent) {
private _datasetUnhidden(
ev: HASSDomEvent<HASSDomEvents["dataset-unhidden"]>
) {
this._hiddenStats.delete(ev.detail.id);
this.requestUpdate("_hiddenStats");
}
@@ -15,6 +15,10 @@ import {
} from "../../common/datetime/format_date";
import { transform } from "../../common/decorators/transform";
import { fireEvent } from "../../common/dom/fire_event";
import type {
HASSDomEvent,
HASSDomTargetEvent,
} from "../../common/dom/fire_event";
import { configContext, internationalizationContext } from "../../data/context";
import { TimeZone } from "../../data/translation";
import { MobileAwareMixin } from "../../mixins/mobile-aware-mixin";
@@ -307,7 +311,7 @@ export class DateRangePicker extends MobileAwareMixin(LitElement) {
});
}
private _focusChanged(ev: CustomEvent<Date>) {
private _focusChanged(ev: HASSDomEvent<Date>) {
const date = ev.detail;
this._pickerMonth = formatDateMonth(
date,
@@ -322,13 +326,19 @@ export class DateRangePicker extends MobileAwareMixin(LitElement) {
this._focusDate = undefined;
}
private _handleChange(ev: CustomEvent) {
private _handleChange(
ev: HASSDomTargetEvent<HTMLElementTagNameMap["calendar-range"]>
) {
const dateElement = ev.target as HTMLElementTagNameMap["calendar-range"];
this._dateValue = dateElement.value;
this._focusDate = undefined;
}
private _clickDateRangeChip(ev: Event) {
private _clickDateRangeChip(
ev: HASSDomTargetEvent<
HaFilterChip & { index: number; range: [Date, Date] }
>
) {
const chip = ev.target as HaFilterChip & {
index: number;
range: [Date, Date];
@@ -336,7 +346,7 @@ export class DateRangePicker extends MobileAwareMixin(LitElement) {
this._saveDateRangePreset(chip.range, chip.index);
}
private _setDateRange(ev: CustomEvent<ActionDetail>) {
private _setDateRange(ev: HASSDomEvent<ActionDetail>) {
const dateRange: [Date, Date] = Object.values(this.ranges!)[
ev.detail.index
];
@@ -355,7 +365,9 @@ export class DateRangePicker extends MobileAwareMixin(LitElement) {
});
}
private _handleChangeTime(ev: ValueChangedEvent<string>) {
private _handleChangeTime(
ev: ValueChangedEvent<string> & HASSDomTargetEvent<HaBaseTimeInput>
) {
ev.stopPropagation();
const time = ev.detail.value;
const target = ev.target as HaBaseTimeInput;
@@ -13,6 +13,10 @@ import {
} from "../../common/datetime/format_date";
import { valueToDate } from "../../common/datetime/value_to_date";
import { transform } from "../../common/decorators/transform";
import type {
HASSDomEvent,
HASSDomTargetEvent,
} from "../../common/dom/fire_event";
import { configContext, internationalizationContext } from "../../data/context";
import { DialogMixin } from "../../dialogs/dialog-mixin";
import type { HomeAssistantConfig } from "../../types";
@@ -167,7 +171,7 @@ export class HaDialogDatePicker extends DialogMixin<DatePickerDialogParams>(
</ha-adaptive-popover>`;
}
private _valueChanged(ev: Event) {
private _valueChanged(ev: HASSDomTargetEvent<CalendarDate>) {
const dateElement = ev.target as CalendarDate;
if (dateElement.value) {
this._updateValue(dateElement.value);
@@ -198,7 +202,7 @@ export class HaDialogDatePicker extends DialogMixin<DatePickerDialogParams>(
}
}
private _focusChanged(ev: CustomEvent<Date>) {
private _focusChanged(ev: HASSDomEvent<Date>) {
const date = ev.detail;
this._pickerMonth = formatDateMonth(
date,
@@ -7,6 +7,10 @@ import { repeat } from "lit/directives/repeat";
import memoizeOne from "memoize-one";
import { consumeLocalize } from "../common/decorators/consume-context-entry";
import { fireEvent } from "../common/dom/fire_event";
import type {
HASSDomCurrentTargetEvent,
HASSDomEvent,
} from "../common/dom/fire_event";
import { computeFloorName } from "../common/entity/compute_floor_name";
import { getAreaContext } from "../common/entity/context/get_area_context";
import type { LocalizeFunc } from "../common/translations/localize";
@@ -191,7 +195,7 @@ export class HaAreasFloorsDisplayEditor extends LitElement {
}
);
private _floorMoved(ev: CustomEvent<HASSDomEvents["item-moved"]>) {
private _floorMoved(ev: HASSDomEvent<HASSDomEvents["item-moved"]>) {
ev.stopPropagation();
const newIndex = ev.detail.newIndex;
const oldIndex = ev.detail.oldIndex;
@@ -215,7 +219,12 @@ export class HaAreasFloorsDisplayEditor extends LitElement {
fireEvent(this, "value-changed", { value: newValue });
}
private async _areaDisplayChanged(ev: ValueChangedEvent<DisplayValue>) {
private async _areaDisplayChanged(
ev: ValueChangedEvent<DisplayValue> &
HASSDomCurrentTargetEvent<
HTMLElementTagNameMap["ha-items-display-editor"] & { floorId?: string }
>
) {
ev.stopPropagation();
const value = ev.detail.value;
const currentFloorId = (ev.currentTarget as any).floorId;
+2 -1
View File
@@ -2,6 +2,7 @@ import type { TemplateResult } from "lit";
import { css, html, LitElement } from "lit";
import { customElement, property, query } from "lit/decorators";
import { fireEvent } from "../../common/dom/fire_event";
import type { HASSDomTargetEvent } from "../../common/dom/fire_event";
import "../ha-checkbox";
import type { HaCheckbox } from "../ha-checkbox";
import type {
@@ -43,7 +44,7 @@ export class HaFormBoolean extends LitElement implements HaFormElement {
`;
}
private _valueChanged(ev: Event) {
private _valueChanged(ev: HASSDomTargetEvent<HaCheckbox>) {
fireEvent(this, "value-changed", {
value: (ev.target as HaCheckbox).checked,
});
+2 -1
View File
@@ -2,6 +2,7 @@ import type { PropertyValues, TemplateResult } from "lit";
import { css, html, LitElement, nothing } from "lit";
import { customElement, property, query } from "lit/decorators";
import { fireEvent } from "../../common/dom/fire_event";
import type { HASSDomTargetEvent } from "../../common/dom/fire_event";
import type { LocalizeFunc } from "../../common/translations/localize";
import "../input/ha-input";
import type { HaInput } from "../input/ha-input";
@@ -70,7 +71,7 @@ export class HaFormFloat extends LitElement implements HaFormElement {
}
}
private _handleInput(ev: InputEvent) {
private _handleInput(ev: InputEvent & HASSDomTargetEvent<HaInput>) {
const source = ev.target as HaInput;
const rawValue = (source.value ?? "").replace(",", ".");
+5 -2
View File
@@ -2,6 +2,7 @@ import type { PropertyValues, TemplateResult } from "lit";
import { css, html, LitElement, nothing } from "lit";
import { customElement, property, query } from "lit/decorators";
import { fireEvent } from "../../common/dom/fire_event";
import type { HASSDomTargetEvent } from "../../common/dom/fire_event";
import type { LocalizeFunc } from "../../common/translations/localize";
import "../ha-checkbox";
import type { HaCheckbox } from "../ha-checkbox";
@@ -153,7 +154,7 @@ export class HaFormInteger extends LitElement implements HaFormElement {
);
}
private _handleCheckboxChange(ev: Event) {
private _handleCheckboxChange(ev: HASSDomTargetEvent<HaCheckbox>) {
const checked = (ev.target as HaCheckbox).checked;
let value: HaFormIntegerData | undefined;
if (checked) {
@@ -178,7 +179,9 @@ export class HaFormInteger extends LitElement implements HaFormElement {
});
}
private _valueChanged(ev: InputEvent) {
private _valueChanged(
ev: InputEvent & HASSDomTargetEvent<HaInput | HTMLInputElement>
) {
const source = ev.target as HaInput | HTMLInputElement;
const rawValue = source.value;
@@ -2,6 +2,7 @@ import type { PropertyValues, TemplateResult } from "lit";
import { css, html, LitElement } from "lit";
import { customElement, property, query } from "lit/decorators";
import { fireEvent } from "../../common/dom/fire_event";
import type { HASSDomTargetEvent } from "../../common/dom/fire_event";
import "../ha-check-list-item";
import "../ha-checkbox";
import type { HaCheckbox } from "../ha-checkbox";
@@ -140,7 +141,7 @@ export class HaFormMultiSelect extends LitElement implements HaFormElement {
}
}
private _valueChanged(ev: CustomEvent): void {
private _valueChanged(ev: HASSDomTargetEvent<HaCheckbox>): void {
const { value, checked } = ev.target as HaCheckbox;
this._handleValueChanged(value, checked);
}
+2 -2
View File
@@ -4,7 +4,7 @@ import { customElement, property } from "lit/decorators";
import memoizeOne from "memoize-one";
import { fireEvent } from "../../common/dom/fire_event";
import type { SelectSelector } from "../../data/selector";
import type { HomeAssistant } from "../../types";
import type { HomeAssistant, ValueChangedEvent } from "../../types";
import "../ha-selector/ha-selector-select";
import type {
HaFormElement,
@@ -78,7 +78,7 @@ export class HaFormSelect extends LitElement implements HaFormElement {
`;
}
private _valueChanged(ev: CustomEvent) {
private _valueChanged(ev: ValueChangedEvent<string>) {
ev.stopPropagation();
let value: HaFormSelectData | undefined = ev.detail.value;
+2 -1
View File
@@ -2,6 +2,7 @@ import type { PropertyValues, TemplateResult } from "lit";
import { LitElement, css, html, nothing } from "lit";
import { customElement, property, query } from "lit/decorators";
import { fireEvent } from "../../common/dom/fire_event";
import type { HASSDomTargetEvent } from "../../common/dom/fire_event";
import type { LocalizeFunc } from "../../common/translations/localize";
import "../ha-icon-button";
import "../input/ha-input";
@@ -79,7 +80,7 @@ export class HaFormString extends LitElement implements HaFormElement {
}
}
protected _valueChanged(ev: Event): void {
protected _valueChanged(ev: HASSDomTargetEvent<HaInput>): void {
let value: string | undefined = (ev.target as HaInput).value;
if (this.data === value) {
return;
+15 -5
View File
@@ -3,11 +3,19 @@ import { css, html, LitElement, nothing } from "lit";
import { customElement, property, query } from "lit/decorators";
import { dynamicElement } from "../../common/dom/dynamic-element-directive";
import { fireEvent } from "../../common/dom/fire_event";
import type { HomeAssistant } from "../../types";
import type { HomeAssistant, ValueChangedEvent } from "../../types";
import "../ha-alert";
import "../ha-selector/ha-selector";
import { getHiddenFields } from "./conditions";
import type { HaFormDataContainer, HaFormElement, HaFormSchema } from "./types";
import type {
HaFormData,
HaFormDataContainer,
HaFormElement,
HaFormSchema,
} from "./types";
type HaFormDataChangedEvent = ValueChangedEvent<HaFormData>;
type HaFormDataContainerChangedEvent = ValueChangedEvent<HaFormDataContainer>;
const LOAD_ELEMENTS = {
boolean: () => import("./ha-form-boolean"),
@@ -261,16 +269,18 @@ export class HaForm extends LitElement implements HaFormElement {
}
protected addValueChangedListener(element: Element | ShadowRoot) {
element.addEventListener("value-changed", (ev) => {
element.addEventListener("value-changed", (ev: Event) => {
ev.stopPropagation();
const schema = (ev.target as HaFormElement).schema as HaFormSchema;
if (ev.target === this) return;
const changeEv = ev as
HaFormDataChangedEvent | HaFormDataContainerChangedEvent;
const newValue =
!schema.name || ("flatten" in schema && schema.flatten)
? ev.detail.value
: { [schema.name]: ev.detail.value };
? (changeEv.detail.value as HaFormDataContainer)
: { [schema.name]: changeEv.detail.value as HaFormData };
this.data = {
...this.data,
+7 -2
View File
@@ -3,6 +3,7 @@ import type { CSSResultGroup, TemplateResult } from "lit";
import { css, html, LitElement, nothing } from "lit";
import { customElement, property, state } from "lit/decorators";
import { fireEvent } from "../common/dom/fire_event";
import type { HASSDomCurrentTargetEvent } from "../common/dom/fire_event";
import type {
Adapter,
IPv4ConfiguredAddress,
@@ -101,7 +102,9 @@ export class HaNetwork extends LitElement {
`;
}
private _handleAutoConfigureCheckboxClick(ev: Event) {
private _handleAutoConfigureCheckboxClick(
ev: HASSDomCurrentTargetEvent<HaCheckbox>
) {
const checkbox = ev.currentTarget as HaCheckbox;
if (this.networkConfig === undefined) {
return;
@@ -127,7 +130,9 @@ export class HaNetwork extends LitElement {
});
}
private _handleAdapterCheckboxClick(ev: Event) {
private _handleAdapterCheckboxClick(
ev: HASSDomCurrentTargetEvent<HaCheckbox>
) {
const checkbox = ev.currentTarget as HaCheckbox;
const adapter_name = checkbox.id;
if (this.networkConfig === undefined) {
@@ -5,7 +5,7 @@ import { getAppKey } from "../data/notify_html5";
import { showPromptDialog } from "../dialogs/generic/show-dialog-box";
import type { HaSwitch } from "./ha-switch";
import type { HomeAssistant } from "../types";
import { fireEvent } from "../common/dom/fire_event";
import { fireEvent, type HASSDomTargetEvent } from "../common/dom/fire_event";
import "./ha-switch";
export const pushSupported =
@@ -55,7 +55,7 @@ class HaPushNotificationsToggle extends LitElement {
}
}
private _handlePushChange(ev: Event) {
private _handlePushChange(ev: HASSDomTargetEvent<HaSwitch>) {
// Somehow this is triggered on Safari on page load causing
// it to get into a loop and crash the page.
if (!pushSupported) return;
+5 -2
View File
@@ -4,6 +4,7 @@ import { customElement, property } from "lit/decorators";
import { classMap } from "lit/directives/class-map";
import { styleMap } from "lit/directives/style-map";
import { fireEvent } from "../common/dom/fire_event";
import type { HASSDomCurrentTargetEvent } from "../common/dom/fire_event";
import "./ha-tooltip";
export interface Segment {
@@ -120,7 +121,9 @@ class HaSegmentedBar extends LitElement {
`;
}
private _handleSegmentClick(ev: Event): void {
private _handleSegmentClick(
ev: HASSDomCurrentTargetEvent<HTMLElement>
): void {
const target = ev.currentTarget as HTMLElement;
const index = Number(target.dataset.index);
const segment = this.segments[index];
@@ -129,7 +132,7 @@ class HaSegmentedBar extends LitElement {
}
}
private _handleLegendClick(ev: Event): void {
private _handleLegendClick(ev: HASSDomCurrentTargetEvent<HTMLElement>): void {
const target = ev.currentTarget as HTMLElement;
const index = Number(target.dataset.index);
const segment = this.segments[index];
+2 -1
View File
@@ -4,6 +4,7 @@ import { ifDefined } from "lit/directives/if-defined";
import memoizeOne from "memoize-one";
import { fireEvent } from "../common/dom/fire_event";
import "./ha-dropdown";
import type { HaDropdownSelectEvent } from "./ha-dropdown";
import "./ha-dropdown-item";
import "./ha-input-helper-text";
import "./ha-picker-field";
@@ -169,7 +170,7 @@ export class HaSelect extends LitElement {
: nothing;
}
private _handleSelect(ev: CustomEvent<{ item: { value: string | number } }>) {
private _handleSelect(ev: HaDropdownSelectEvent<string | number>) {
ev.stopPropagation();
const value = ev.detail.item.value;
if (value === this.value) {
+3 -3
View File
@@ -17,7 +17,7 @@ import { stringCompare } from "../common/string/compare";
import { computeRTL } from "../common/util/compute_rtl";
import { throttle } from "../common/util/throttle";
import { subscribeFrontendUserData } from "../data/frontend";
import type { ActionHandlerDetail } from "../data/lovelace/action_handler";
import type { ActionHandlerEvent } from "../data/lovelace/action_handler";
import {
FIXED_PANELS,
getDefaultPanelUrlPath,
@@ -634,7 +634,7 @@ class HaSidebar extends SubscribeMixin(ScrollableFadeMixin(LitElement)) {
});
}
private _handleAction(ev: CustomEvent<ActionHandlerDetail>) {
private _handleAction(ev: ActionHandlerEvent) {
if (ev.detail.action !== "hold") {
return;
}
@@ -646,7 +646,7 @@ class HaSidebar extends SubscribeMixin(ScrollableFadeMixin(LitElement)) {
fireEvent(this, "hass-show-notifications");
}
private _toggleSidebar(ev: CustomEvent) {
private _toggleSidebar(ev: ActionHandlerEvent) {
if (ev.detail.action !== "tap") {
return;
}
+4 -1
View File
@@ -2,6 +2,7 @@ import type { CSSResultGroup, PropertyValues } from "lit";
import { LitElement, css, html, nothing } from "lit";
import { customElement, property, query, state } from "lit/decorators";
import { classMap } from "lit/directives/class-map";
import type { HASSDomCurrentTargetEvent } from "../common/dom/fire_event";
import { goBack } from "../common/navigate";
import { haStyleScrollbar } from "../resources/styles";
import "./ha-icon-button-arrow-prev";
@@ -324,7 +325,9 @@ export class HaTopAppBarFixed extends LitElement {
this._subRowResizeObserver = undefined;
}
private _subRowSlotChanged = (ev: Event) => {
private _subRowSlotChanged = (
ev: HASSDomCurrentTargetEvent<HTMLSlotElement>
) => {
const slot = ev.currentTarget as HTMLSlotElement;
this._hasSubRow = slot
.assignedNodes({ flatten: true })
+2 -1
View File
@@ -2,6 +2,7 @@ import { consume, type ContextType } from "@lit/context";
import { mdiContentCopy, mdiEye, mdiEyeOff } from "@mdi/js";
import { css, html, LitElement, nothing } from "lit";
import { customElement, property, query, state } from "lit/decorators";
import type { HASSDomCurrentTargetEvent } from "../../common/dom/fire_event";
import { copyToClipboard } from "../../common/util/copy-clipboard";
import { internationalizationContext } from "../../data/context";
import { showToast } from "../../util/toast";
@@ -111,7 +112,7 @@ export class HaInputCopy extends LitElement {
`;
}
private _focusInput(ev: Event) {
private _focusInput(ev: HASSDomCurrentTargetEvent<HaInput>) {
const inputElement = ev.currentTarget as HaInput;
inputElement.select();
}
+2 -1
View File
@@ -2,6 +2,7 @@ import { HasSlotController } from "@home-assistant/webawesome/dist/internal/slot
import type { CSSResultGroup, TemplateResult } from "lit";
import { css, html, LitElement, nothing } from "lit";
import { customElement, property, state } from "lit/decorators";
import type { HASSDomTargetEvent } from "../../common/dom/fire_event";
/**
* @element ha-row-item
@@ -56,7 +57,7 @@ export class HaRowItem extends LitElement {
@state() private _hasEnd = false;
private _onSlotChange(name: "start" | "end") {
return (ev: Event) => {
return (ev: HASSDomTargetEvent<HTMLSlotElement>) => {
const slot = ev.target as HTMLSlotElement;
const hasContent = slot
.assignedNodes({ flatten: true })
+2 -1
View File
@@ -1,6 +1,7 @@
import { css, html, LitElement, nothing, svg } from "lit";
import { customElement, property, state } from "lit/decorators";
import { classMap } from "lit/directives/class-map";
import type { HASSDomTargetEvent } from "../../common/dom/fire_event";
import { BRANCH_HEIGHT, SPACING } from "./hat-graph-const";
interface BranchConfig {
@@ -31,7 +32,7 @@ export class HatGraphBranch extends LitElement {
private _maxHeight = 0;
private _updateBranches(ev: Event) {
private _updateBranches(ev: HASSDomTargetEvent<HTMLSlotElement>) {
let total_width = 0;
const heights: number[] = [];
const branches: BranchConfig[] = [];
+1 -1
View File
@@ -14,7 +14,7 @@ declare global {
}
interface GlobalEventHandlersEventMap {
"connection-status": HASSDomEvent<ConnectionStatus>;
"connection-status": HASSDomEvent<HASSDomEvents["connection-status"]>;
}
}
+1 -1
View File
@@ -207,7 +207,7 @@ declare global {
"hass-related-context": RelatedContextItem | undefined;
}
interface HTMLElementEventMap {
"hass-related-context": HASSDomEvent<RelatedContextItem | undefined>;
"hass-related-context": HASSDomEvent<HASSDomEvents["hass-related-context"]>;
}
}
+14 -2
View File
@@ -793,6 +793,17 @@ const findEnergyDataCollection = (
return (hass.connection as any)[key];
};
// The last-picked preset is remembered per energy collection, so each dashboard
// reopens on its own default period. Derived from the connection key so the read
// and write sides cannot drift apart.
export const getEnergyDefaultPeriodStorageKey = (
hass: HomeAssistant,
collectionKey?: string
): string => {
const [key] = convertCollectionKeyToConnection(hass, collectionKey);
return `energy-default-period-${key}`;
};
// When does the collection's day period need to roll over to the next day?
// With `midnightRollover` (the real-time "Now" view) it rolls over right at
// midnight. Otherwise it waits an hour, until the new day's first hourly
@@ -892,8 +903,9 @@ export const getEnergyDataCollection = (
// The real-time "Now" view always tracks today; it shows live data even
// before today's first statistic exists, so it never falls back to yesterday.
const preferredPeriod =
(localStorage.getItem(`energy-default-period-${key}`) as DateRange) ||
"today";
(localStorage.getItem(
getEnergyDefaultPeriodStorageKey(hass, options.key)
) as DateRange) || "today";
const period =
preferredPeriod === "today" && hour === "0" && !midnightRollover
? "yesterday"
+1 -1
View File
@@ -24,7 +24,7 @@ declare global {
}
interface GlobalEventHandlersEventMap {
haptic: HASSDomEvent<HapticType>;
haptic: HASSDomEvent<HASSDomEvents["haptic"]>;
}
}
@@ -2,6 +2,7 @@ import type { CSSResultGroup } from "lit";
import { css, html, LitElement, nothing } from "lit";
import { customElement, property, state } from "lit/decorators";
import { fireEvent } from "../../common/dom/fire_event";
import type { HASSDomTargetEvent } from "../../common/dom/fire_event";
import "../../components/ha-dialog";
import "../../components/ha-dialog-footer";
import "../../components/ha-formfield";
@@ -161,18 +162,18 @@ class DialogConfigEntrySystemOptions extends DirtyStateProviderMixin<SystemOptio
`;
}
private _disableNewEntitiesChanged(ev: Event): void {
private _disableNewEntitiesChanged(ev: HASSDomTargetEvent<HaSwitch>): void {
this._error = undefined;
this._disableNewEntities = !(ev.target as HaSwitch).checked;
this._disableNewEntities = !ev.target.checked;
this._updateDirtyState({
disableNewEntities: this._disableNewEntities,
disablePolling: this._disablePolling,
});
}
private _disablePollingChanged(ev: Event): void {
private _disablePollingChanged(ev: HASSDomTargetEvent<HaSwitch>): void {
this._error = undefined;
this._disablePolling = !(ev.target as HaSwitch).checked;
this._disablePolling = !ev.target.checked;
this._updateDirtyState({
disableNewEntities: this._disableNewEntities,
disablePolling: this._disablePolling,
@@ -70,8 +70,10 @@ declare global {
}
// for add event listener
interface HTMLElementEventMap {
"flow-update": HASSDomEvent<FlowUpdateEvent>;
"flow-step-footer-state-changed": HASSDomEvent<FlowStepFooterStateChangedEvent>;
"flow-update": HASSDomEvent<HASSDomEvents["flow-update"]>;
"flow-step-footer-state-changed": HASSDomEvent<
HASSDomEvents["flow-step-footer-state-changed"]
>;
}
}
@@ -741,7 +743,7 @@ class DataEntryFlowDialog extends DirtyStateProviderMixin<
};
private _handleFooterStateChanged = (
ev: HASSDomEvent<FlowStepFooterStateChangedEvent>
ev: HASSDomEvent<HASSDomEvents["flow-step-footer-state-changed"]>
) => {
if (ev.detail.loading !== undefined) {
this._formStepLoading = ev.detail.loading;
+2 -2
View File
@@ -5,7 +5,7 @@ import { customElement, property, state } from "lit/decorators";
import { createRef, ref } from "lit/directives/ref";
import memoizeOne from "memoize-one";
import { dynamicElement } from "../../common/dom/dynamic-element-directive";
import { fireEvent } from "../../common/dom/fire_event";
import { fireEvent, type HASSDomEvent } from "../../common/dom/fire_event";
import { isNavigationClick } from "../../common/dom/is-navigation-click";
import "../../components/ha-alert";
import { computeInitialHaFormData } from "../../components/ha-form/compute-initial-ha-form-data";
@@ -153,7 +153,7 @@ class StepFlowForm extends LitElement {
`;
}
private _setError(ev: CustomEvent) {
private _setError(ev: HASSDomEvent<DataEntryFlowStepForm["errors"]>) {
this._previewErrors = ev.detail;
}
@@ -2,9 +2,11 @@ import { consume } from "@lit/context";
import { css, html, LitElement, nothing } from "lit";
import { customElement, property, state } from "lit/decorators";
import { consumeLocalize } from "../../../common/decorators/consume-context-entry";
import type { HASSDomCurrentTargetEvent } from "../../../common/dom/fire_event";
import { slugify } from "../../../common/string/slugify";
import type { LocalizeFunc } from "../../../common/translations/localize";
import "../../../components/buttons/ha-progress-button";
import type { HaProgressButton } from "../../../components/buttons/ha-progress-button";
import "../../../components/ha-camera-stream";
import type { CameraEntity } from "../../../data/camera";
import { apiContext } from "../../../data/context";
@@ -66,8 +68,10 @@ class MoreInfoCamera extends LitElement {
`;
}
private async _downloadSnapshot(ev: CustomEvent) {
const button = ev.currentTarget as any;
private async _downloadSnapshot(
ev: HASSDomCurrentTargetEvent<HaProgressButton>
) {
const button = ev.currentTarget;
this._waiting = true;
try {
@@ -3,8 +3,10 @@ import type { HassEntity } from "home-assistant-js-websocket";
import { css, html, LitElement, nothing } from "lit";
import { customElement, property, state } from "lit/decorators";
import { consumeLocalize } from "../../../common/decorators/consume-context-entry";
import type { HASSDomCurrentTargetEvent } from "../../../common/dom/fire_event";
import type { LocalizeFunc } from "../../../common/translations/localize";
import "../../../components/ha-button";
import type { HaButton } from "../../../components/ha-button";
import { apiContext } from "../../../data/context";
import { UNAVAILABLE } from "../../../data/entity/entity";
import type { HomeAssistantApi } from "../../../types";
@@ -67,8 +69,10 @@ class MoreInfoCounter extends LitElement {
`;
}
private _handleActionClick(e: MouseEvent): void {
const action = (e.currentTarget as any).action;
private _handleActionClick(
e: MouseEvent & HASSDomCurrentTargetEvent<HaButton & { action: string }>
): void {
const action = e.currentTarget.action;
this._api.callService("counter", action, {
entity_id: this.stateObj!.entity_id,
});
@@ -17,6 +17,7 @@ import { classMap } from "lit/directives/class-map";
import { ifDefined } from "lit/directives/if-defined";
import { consumeLocalize } from "../../../common/decorators/consume-context-entry";
import { fireEvent } from "../../../common/dom/fire_event";
import type { HASSDomTargetEvent } from "../../../common/dom/fire_event";
import { stateActive } from "../../../common/entity/state_active";
import { supportsFeature } from "../../../common/entity/supports-feature";
import type { LocalizeFunc } from "../../../common/translations/localize";
@@ -872,12 +873,14 @@ class MoreInfoMediaPlayer extends LitElement {
});
}
private async _handleMediaSeekChanged(e: Event): Promise<void> {
private async _handleMediaSeekChanged(
e: HASSDomTargetEvent<HaSlider>
): Promise<void> {
if (!this.stateObj) {
return;
}
const newValue = (e.target as any).value;
const newValue = e.target.value;
this._api.callService("media_player", "media_seek", {
entity_id: this.stateObj.entity_id,
seek_position: newValue,
@@ -19,7 +19,7 @@ import {
hasRequiredScriptFields,
requiredScriptFieldsFilled,
} from "../../../data/script";
import type { HomeAssistant } from "../../../types";
import type { HomeAssistant, ValueChangedEvent } from "../../../types";
import "../components/ha-more-info-state-header";
@customElement("more-info-script")
@@ -209,7 +209,9 @@ class MoreInfoScript extends LitElement {
});
}
private _scriptDataChanged(ev: CustomEvent): void {
private _scriptDataChanged(
ev: ValueChangedEvent<Record<string, unknown>>
): void {
this._scriptData = { ...this._scriptData, ...ev.detail.value };
}
@@ -3,8 +3,10 @@ import type { PropertyValues } from "lit";
import { css, html, LitElement, nothing } from "lit";
import { customElement, property, state } from "lit/decorators";
import { consumeLocalize } from "../../../common/decorators/consume-context-entry";
import type { HASSDomCurrentTargetEvent } from "../../../common/dom/fire_event";
import type { LocalizeFunc } from "../../../common/translations/localize";
import "../../../components/ha-button";
import type { HaButton } from "../../../components/ha-button";
import "../../../components/ha-duration-input";
import type { HaDurationData } from "../../../components/ha-duration-input";
import { apiContext } from "../../../data/context";
@@ -137,8 +139,10 @@ class MoreInfoTimer extends LitElement {
});
}
private _handleActionClick(e: MouseEvent): void {
const action = (e.currentTarget as any).action;
private _handleActionClick(
e: MouseEvent & HASSDomCurrentTargetEvent<HaButton & { action: string }>
): void {
const action = e.currentTarget.action;
this._api.callService("timer", action, {
entity_id: this.stateObj!.entity_id,
});
+10 -4
View File
@@ -10,6 +10,7 @@ import { LitElement, css, html, nothing } from "lit";
import { customElement, property, state } from "lit/decorators";
import { isComponentLoaded } from "../../common/config/is_component_loaded";
import { fireEvent } from "../../common/dom/fire_event";
import type { HASSDomCurrentTargetEvent } from "../../common/dom/fire_event";
import "../../components/animation/ha-fade-in";
import "../../components/ha-adaptive-dialog";
import "../../components/ha-alert";
@@ -334,13 +335,18 @@ class DialogRestart extends LitElement {
}
};
private async _handleAction(ev: Event) {
private async _handleAction(
ev: HASSDomCurrentTargetEvent<
HaListItemButton & {
action: "restart" | "reboot" | "shutdown" | "restart-safe-mode";
}
>
) {
if (this._loadingBackupInfo) {
return;
}
this._loadingBackupInfo = true;
const action = (ev.currentTarget as HaListItemButton & { action: string })
.action as "restart" | "reboot" | "shutdown" | "restart-safe-mode";
const action = ev.currentTarget.action;
const backupState = await this._loadBackupState();
@@ -369,7 +375,7 @@ class DialogRestart extends LitElement {
this._dialogOpen = false;
let actionFunc;
let actionFunc: () => Promise<void>;
if (["restart", "restart-safe-mode"].includes(action)) {
const serviceData =
@@ -3,7 +3,7 @@ import type { CSSResultGroup, PropertyValues } from "lit";
import { css, 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 { fireEvent, type HASSDomEvent } from "../../common/dom/fire_event";
import { computeDomain } from "../../common/entity/compute_domain";
import { formatLanguageCode } from "../../common/language/format_language";
import "../../components/chips/ha-assist-chip";
@@ -18,7 +18,7 @@ import { getLanguageScores } from "../../data/conversation";
import { UNAVAILABLE } from "../../data/entity/entity";
import type { EntityRegistryDisplayEntry } from "../../data/entity/entity_registry";
import { haStyleDialog } from "../../resources/styles";
import type { HomeAssistant } from "../../types";
import type { HomeAssistant, ValueChangedEvent } from "../../types";
import type { VoiceAssistantSetupDialogParams } from "./show-voice-assistant-setup-dialog";
import "./voice-assistant-setup-step-area";
import "./voice-assistant-setup-step-change-wake-word";
@@ -337,7 +337,7 @@ export class HaVoiceAssistantSetupDialog extends LitElement {
this._language = ev.detail.item.value;
}
private _languageChanged(ev: CustomEvent) {
private _languageChanged(ev: ValueChangedEvent<string | undefined>) {
if (!ev.detail.value) {
return;
}
@@ -351,7 +351,7 @@ export class HaVoiceAssistantSetupDialog extends LitElement {
this._step = this._previousSteps.pop()!;
}
private async _goToNextStep(ev?: CustomEvent) {
private async _goToNextStep(ev?: HASSDomEvent<HASSDomEvents["next-step"]>) {
if (ev?.detail?.updateConfig) {
await this._fetchAssistConfiguration();
}
@@ -1,6 +1,7 @@
import { css, html, LitElement } from "lit";
import { customElement, property } from "lit/decorators";
import { fireEvent } from "../../common/dom/fire_event";
import type { HASSDomCurrentTargetEvent } from "../../common/dom/fire_event";
import "../../components/item/ha-list-item-button";
import type { HaListItemButton } from "../../components/item/ha-list-item-button";
import "../../components/list/ha-list-base";
@@ -50,14 +51,14 @@ export class HaVoiceAssistantSetupStepChangeWakeWord extends LitElement {
</ha-list-base>`;
}
private async _wakeWordPicked(ev: Event) {
private async _wakeWordPicked(
ev: HASSDomCurrentTargetEvent<HaListItemButton & { value: string }>
) {
if (!this.assistEntityId) {
return;
}
const wakeWordId = (
ev.currentTarget as HaListItemButton & { value: string }
).value;
const wakeWordId = ev.currentTarget.value;
await setWakeWords(this.hass, this.assistEntityId, [wakeWordId]);
this._nextStep();
@@ -19,7 +19,7 @@ import type { LanguageScore, LanguageScores } from "../../data/conversation";
import { getLanguageScores, listAgents } from "../../data/conversation";
import { listSTTEngines } from "../../data/stt";
import { listTTSEngines, listTTSVoices } from "../../data/tts";
import type { HomeAssistant } from "../../types";
import type { HomeAssistant, ValueChangedEvent } from "../../types";
import { documentationUrl } from "../../util/documentation-url";
import { AssistantSetupStyles } from "./styles";
import { STEP } from "./voice-assistant-setup-dialog";
@@ -265,8 +265,8 @@ export class HaVoiceAssistantSetupStepPipeline extends LitElement {
}
private async _createCloudPipeline(useLanguage: boolean): Promise<boolean> {
let cloudTtsEntityId;
let cloudSttEntityId;
let cloudTtsEntityId: string | undefined;
let cloudSttEntityId: string | undefined;
for (const entity of Object.values(this.hass.entities)) {
if (entity.platform === "cloud") {
const domain = computeDomain(entity.entity_id);
@@ -327,7 +327,7 @@ export class HaVoiceAssistantSetupStepPipeline extends LitElement {
const ttsVoices = await listTTSVoices(
this.hass,
cloudTtsEntityId,
cloudTtsEntityId!,
ttsEngine.supported_languages[0]
);
@@ -357,9 +357,9 @@ export class HaVoiceAssistantSetupStepPipeline extends LitElement {
language: (this.language || this.hass.config.language).split("-")[0],
conversation_engine: "conversation.home_assistant",
conversation_language: agent.supported_languages[0],
stt_engine: cloudSttEntityId,
stt_engine: cloudSttEntityId!,
stt_language: sttEngine.supported_languages[0],
tts_engine: cloudTtsEntityId,
tts_engine: cloudTtsEntityId!,
tts_language: ttsEngine.supported_languages[0],
tts_voice: ttsVoices.voices![0].voice_id,
wake_word_entity: null,
@@ -380,7 +380,9 @@ export class HaVoiceAssistantSetupStepPipeline extends LitElement {
}
}
private _valueChanged(ev: CustomEvent) {
private _valueChanged(
ev: ValueChangedEvent<(typeof OPTIONS)[number] | undefined>
) {
this._value = ev.detail.value;
}
@@ -410,7 +412,7 @@ export class HaVoiceAssistantSetupStepPipeline extends LitElement {
fireEvent(this, "next-step", { step: STEP.LOCAL, option: this._value });
}
private _languageChanged(ev: CustomEvent) {
private _languageChanged(ev: ValueChangedEvent<string | undefined>) {
if (!ev.detail.value) {
return;
}
+9 -1
View File
@@ -121,6 +121,14 @@ declare global {
}
interface GlobalEventHandlersEventMap {
"matter-commission-finish": HASSDomEvent<MatterCommissionFinish>;
"improv-discovered-device": HASSDomEvent<
HASSDomEvents["improv-discovered-device"]
>;
"improv-device-setup-done": HASSDomEvent<
HASSDomEvents["improv-device-setup-done"]
>;
"matter-commission-finish": HASSDomEvent<
HASSDomEvents["matter-commission-finish"]
>;
}
}
+2 -1
View File
@@ -3,6 +3,7 @@ import { css, html, LitElement } from "lit";
import { customElement, eventOptions, property } from "lit/decorators";
import { classMap } from "lit/directives/class-map";
import { restoreScroll } from "../common/decorators/restore-scroll";
import type { HASSDomTargetEvent } from "../common/dom/fire_event";
import { goBack } from "../common/navigate";
import { sanitizeNavigationPath } from "../common/url/sanitize-navigation-path";
import "../components/ha-icon-button-arrow-prev";
@@ -74,7 +75,7 @@ class HassSubpage extends LitElement {
}
@eventOptions({ passive: true })
private _saveScrollPos(e: Event) {
private _saveScrollPos(e: HASSDomTargetEvent<HTMLDivElement>) {
this._savedScrollPos = (e.target as HTMLDivElement).scrollTop;
}
+4 -2
View File
@@ -17,7 +17,7 @@ import { LitElement, css, html, nothing } from "lit";
import { customElement, property, query, state } from "lit/decorators";
import { classMap } from "lit/directives/class-map";
import { canShowPage } from "../common/config/can_show_page";
import { fireEvent } from "../common/dom/fire_event";
import { fireEvent, type HASSDomTargetEvent } from "../common/dom/fire_event";
import type { LocalizeFunc } from "../common/translations/localize";
import "../components/chips/ha-assist-chip";
import "../components/data-table/ha-data-table";
@@ -740,7 +740,9 @@ export class HaTabsSubpageDataTable extends KeyboardShortcutMixin(LitElement) {
this._dataTable.clearSelection();
};
private _handleSearchChange(ev: InputEvent) {
private _handleSearchChange(
ev: InputEvent & HASSDomTargetEvent<HaInputSearch>
) {
const target = ev.target as HaInputSearch;
if (this.filter === target.value) {
return;
+2 -1
View File
@@ -12,6 +12,7 @@ import { classMap } from "lit/directives/class-map";
import memoizeOne from "memoize-one";
import { canShowPage } from "../common/config/can_show_page";
import { restoreScroll } from "../common/decorators/restore-scroll";
import type { HASSDomTargetEvent } from "../common/dom/fire_event";
import { isNavigationClick } from "../common/dom/is-navigation-click";
import { goBack, navigate } from "../common/navigate";
import type { LocalizeFunc } from "../common/translations/localize";
@@ -232,7 +233,7 @@ export class HassTabsSubpage extends LitElement {
}
@eventOptions({ passive: true })
private _saveScrollPos(e: Event) {
private _saveScrollPos(e: HASSDomTargetEvent<HTMLDivElement>) {
this._savedScrollPos = (e.target as HTMLDivElement).scrollTop;
}
+1 -1
View File
@@ -36,7 +36,7 @@ export const PreventUnsavedMixin = <T extends Constructor<LitElement>>(
protected willUpdate(changedProperties: PropertyValues<this>): void {
super.willUpdate(changedProperties);
if (this.isDirtyState) {
if (this.isDirtyState && this.isConnected) {
window.addEventListener("click", this._handleClick, true);
window.addEventListener("beforeunload", this._handleUnload);
} else {
+10 -6
View File
@@ -37,7 +37,7 @@ import { subscribeUser } from "../data/ws-user";
import { makeDialogManager } from "../dialogs/make-dialog-manager";
import { litLocalizeLiteMixin } from "../mixins/lit-localize-lite-mixin";
import { HassElement } from "../state/hass-element";
import type { HomeAssistant } from "../types";
import type { HomeAssistant, ValueChangedEvent } from "../types";
import { storeState } from "../util/ha-pref-storage";
import { registerServiceWorker } from "../util/register-service-worker";
import "../components/progress/ha-progress-bar";
@@ -80,8 +80,8 @@ declare global {
}
interface GlobalEventHandlersEventMap {
"onboarding-step": HASSDomEvent<OnboardingEvent>;
"onboarding-progress": HASSDomEvent<OnboardingProgressEvent>;
"onboarding-step": HASSDomEvent<HASSDomEvents["onboarding-step"]>;
"onboarding-progress": HASSDomEvent<HASSDomEvents["onboarding-progress"]>;
}
}
@@ -332,7 +332,9 @@ class HaOnboarding extends litLocalizeLiteMixin(HassElement) {
}
}
private _handleProgress(ev: HASSDomEvent<OnboardingProgressEvent>) {
private _handleProgress(
ev: HASSDomEvent<HASSDomEvents["onboarding-progress"]>
) {
const stepSize = 100 / this._steps!.length;
if (ev.detail.increase) {
this._progress += ev.detail.increase * stepSize;
@@ -345,7 +347,9 @@ class HaOnboarding extends litLocalizeLiteMixin(HassElement) {
}
}
private async _handleStepDone(ev: HASSDomEvent<OnboardingEvent>) {
private async _handleStepDone(
ev: HASSDomEvent<HASSDomEvents["onboarding-step"]>
) {
const stepResult = ev.detail;
this._steps = this._steps!.map((step) =>
step.step === stepResult.type ? { ...step, done: true } : step
@@ -479,7 +483,7 @@ class HaOnboarding extends litLocalizeLiteMixin(HassElement) {
});
}
private _languageChanged(ev: CustomEvent) {
private _languageChanged(ev: ValueChangedEvent<string>) {
const language = ev.detail.value;
this.language = language;
if (this.hass) {
+4 -2
View File
@@ -2,7 +2,7 @@ import { mdiOpenInNew } from "@mdi/js";
import type { CSSResultGroup, TemplateResult, PropertyValues } from "lit";
import { css, html, LitElement } from "lit";
import { customElement, property, state } from "lit/decorators";
import { fireEvent } from "../common/dom/fire_event";
import { fireEvent, type HASSDomEvent } from "../common/dom/fire_event";
import type { LocalizeFunc } from "../common/translations/localize";
import "../components/ha-analytics";
import "../components/ha-button";
@@ -65,7 +65,9 @@ class OnboardingAnalytics extends LitElement {
});
}
private _preferencesChanged(event: CustomEvent): void {
private _preferencesChanged(
event: HASSDomEvent<HASSDomEvents["analytics-preferences-changed"]>
): void {
this._analyticsDetails = {
...this._analyticsDetails!,
preferences: event.detail.preferences,
+4 -2
View File
@@ -19,7 +19,7 @@ import type { ConfigUpdateValues } from "../data/core";
import { saveCoreConfig } from "../data/core";
import { countryCurrency } from "../data/currency";
import { onboardCoreConfigStep } from "../data/onboarding";
import type { HomeAssistant } from "../types";
import type { HomeAssistant, ValueChangedEvent } from "../types";
import { getLocalLanguage } from "../util/common-translation";
import "./onboarding-location";
@@ -132,7 +132,9 @@ class OnboardingCoreConfig extends LitElement {
});
}
private _handleFormChanged(ev: CustomEvent) {
private _handleFormChanged(
ev: ValueChangedEvent<{ country?: string; time_zone?: string }>
) {
const value = ev.detail.value as { country?: string; time_zone?: string };
this._country = value.country || undefined;
if (value.time_zone) {
+4 -1
View File
@@ -2,6 +2,7 @@ import type { TemplateResult, PropertyValues } from "lit";
import { css, html, LitElement, nothing } from "lit";
import { customElement, property, state } from "lit/decorators";
import { storage } from "../common/decorators/storage";
import type { HASSDomEvent } from "../common/dom/fire_event";
import { navigate } from "../common/navigate";
import type { LocalizeFunc } from "../common/translations/localize";
import { removeSearchParam } from "../common/url/search-params";
@@ -280,7 +281,9 @@ class OnboardingRestoreBackup extends LitElement {
setTimeout(() => this._loadBackupInfo(), delay);
}
private async _backupUploaded(ev: CustomEvent) {
private async _backupUploaded(
ev: HASSDomEvent<HASSDomEvents["backup-uploaded"]>
) {
this._backupId = ev.detail.backupId;
await this._loadBackupInfo();
}
@@ -2,6 +2,10 @@ import { css, html, LitElement, nothing, type CSSResultGroup } from "lit";
import { customElement, property, query, state } from "lit/decorators";
import { formatDateTimeWithBrowserDefaults } from "../../common/datetime/format_date_time";
import { fireEvent } from "../../common/dom/fire_event";
import type {
HASSDomCurrentTargetEvent,
HASSDomTargetEvent,
} from "../../common/dom/fire_event";
import type { LocalizeFunc } from "../../common/translations/localize";
import "../../components/buttons/ha-progress-button";
import type { HaProgressButton } from "../../components/buttons/ha-progress-button";
@@ -9,6 +13,7 @@ import "../../components/ha-alert";
import "../../components/ha-button";
import "../../components/ha-icon-button-arrow-prev";
import "../../components/input/ha-input";
import type { HaInput } from "../../components/input/ha-input";
import "../../components/item/ha-row-item";
import {
getPreferredAgentForDownload,
@@ -19,6 +24,7 @@ import { restoreOnboardingBackup } from "../../data/backup_onboarding";
import "../../panels/config/backup/components/ha-backup-data-picker";
import "../../panels/config/backup/components/ha-backup-formfield-label";
import { onBoardingStyles } from "../styles";
import type { ValueChangedEvent } from "../../types";
@customElement("onboarding-restore-backup-restore")
class OnboardingRestoreBackupRestore extends LitElement {
@@ -247,16 +253,20 @@ class OnboardingRestoreBackupRestore extends LitElement {
fireEvent(this, "sign-out");
}
private _selectedBackupChanged(ev: CustomEvent) {
private _selectedBackupChanged(ev: ValueChangedEvent<BackupData>) {
ev.stopPropagation();
this._selectedData = ev.detail.value;
}
private _encryptionKeyChanged(ev): void {
private _encryptionKeyChanged(
ev: HASSDomTargetEvent<Omit<HaInput, "value"> & { value: string }>
): void {
this._encryptionKey = ev.target.value;
}
private async _startRestore(ev: CustomEvent): Promise<void> {
private async _startRestore(
ev: HASSDomCurrentTargetEvent<HaProgressButton>
): Promise<void> {
const agentId = Object.keys(this.backup.agents)[0];
const backupProtected = this.backup.agents[agentId].protected;
@@ -270,7 +280,7 @@ class OnboardingRestoreBackupRestore extends LitElement {
}
this._loading = true;
const button = ev.currentTarget as HaProgressButton;
const button = ev.currentTarget;
this._error = undefined;
this._encryptionKeyWrong = false;
@@ -6,14 +6,20 @@ import type { ByWeekday, Options, WeekdayStr } from "rrule";
import { RRule, Weekday } from "rrule";
import { formatDate, formatTime } from "../../common/datetime/calc_date";
import { firstWeekdayIndex } from "../../common/datetime/first_weekday";
import type {
HASSDomCurrentTargetEvent,
HASSDomTargetEvent,
} from "../../common/dom/fire_event";
import type { LocalizeKeys } from "../../common/translations/localize";
import "../../components/chips/ha-chip-set";
import "../../components/chips/ha-filter-chip";
import type { HaFilterChip } from "../../components/chips/ha-filter-chip";
import "../../components/ha-date-input";
import "../../components/ha-select";
import type { HaSelectSelectEvent } from "../../components/ha-select";
import "../../components/input/ha-input";
import type { HomeAssistant } from "../../types";
import type { HaInput } from "../../components/input/ha-input";
import type { HomeAssistant, ValueChangedEvent } from "../../types";
import type {
MonthlyRepeatItem,
RepeatEnd,
@@ -321,8 +327,8 @@ export class RecurrenceRuleEditor extends LitElement {
`;
}
private _onIntervalChange(e: Event) {
this._interval = (e.target! as any).value;
private _onIntervalChange(e: HASSDomTargetEvent<HaInput>) {
this._interval = Number(e.target.value);
}
private _onRepeatSelected(e: HaSelectSelectEvent<RepeatFrequency>) {
@@ -351,9 +357,12 @@ export class RecurrenceRuleEditor extends LitElement {
this._monthday = selectedItem.bymonthday;
}
private _onWeekdayToggle(e: MouseEvent) {
const target = e.currentTarget as any;
const value = target.value as WeekdayStr;
private _onWeekdayToggle(
e: MouseEvent &
HASSDomCurrentTargetEvent<HaFilterChip & { value: WeekdayStr }>
) {
const target = e.currentTarget;
const value = target.value;
if (this._weekday.has(value)) {
this._weekday.delete(value);
} else {
@@ -385,11 +394,11 @@ export class RecurrenceRuleEditor extends LitElement {
e.stopPropagation();
}
private _onCountChange(e: Event) {
this._count = (e.target! as any).value;
private _onCountChange(e: HASSDomTargetEvent<HaInput>) {
this._count = Number(e.target.value);
}
private _onUntilChange(e: CustomEvent) {
private _onUntilChange(e: ValueChangedEvent<string>) {
e.stopPropagation();
this._untilDay = new Date(
new TZDate(e.detail.value + "T00:00:00", this.timezone).getTime()
@@ -436,7 +445,7 @@ export class RecurrenceRuleEditor extends LitElement {
);
// rrule.js can't compute some UNTIL variations so we compute that ourself. Must be
// in the same format as dtstart.
let newUntilValue;
let newUntilValue: string;
if (this.allDay) {
// For all-day events, only use the date part
newUntilValue = until.toISOString().split("T")[0].replace(/-/g, "");
@@ -3,7 +3,6 @@ import type { CSSResultGroup } from "lit";
import { css, html, LitElement, nothing } from "lit";
import { customElement, property, state } from "lit/decorators";
import { fireEvent } from "../../../common/dom/fire_event";
import type { HASSDomTargetEvent } from "../../../common/dom/fire_event";
import { DirtyStateProviderMixin } from "../../../mixins/dirty-state-provider-mixin";
import "../../../components/entity/ha-entity-picker";
import type { HaEntityPicker } from "../../../components/entity/ha-entity-picker";
@@ -456,7 +455,7 @@ class DialogAreaDetail
return deviceReg && deviceReg.area_id === areaId;
};
private _nameChanged(ev: InputEvent & HASSDomTargetEvent<HaInput>) {
private _nameChanged(ev: InputEvent) {
this._error = undefined;
this._name = (ev.target as HaInput).value ?? "";
this._updateDirtyState(this._currentState());
@@ -480,9 +479,7 @@ class DialogAreaDetail
this._updateDirtyState(this._currentState());
}
private _pictureChanged(
ev: ValueChangedEvent<string | null> & HASSDomTargetEvent<HaPictureUpload>
) {
private _pictureChanged(ev: ValueChangedEvent<string | null>) {
this._error = undefined;
this._picture = (ev.target as HaPictureUpload).value;
this._updateDirtyState(this._currentState());
@@ -493,9 +490,7 @@ class DialogAreaDetail
this._updateDirtyState(this._currentState());
}
private _sensorChanged(
ev: ValueChangedEvent<string> & HASSDomTargetEvent<HaEntityPicker>
): void {
private _sensorChanged(ev: CustomEvent): void {
const deviceClass = (ev.target as HaEntityPicker).includeDeviceClasses![0];
const key = `_${deviceClass}Entity`;
this[key] = ev.detail.value || null;
@@ -5,7 +5,6 @@ import { customElement, property, state } from "lit/decorators";
import { repeat } from "lit/directives/repeat";
import memoizeOne from "memoize-one";
import { fireEvent } from "../../../common/dom/fire_event";
import type { HASSDomTargetEvent } from "../../../common/dom/fire_event";
import "../../../components/chips/ha-chip-set";
import "../../../components/chips/ha-input-chip";
import "../../../components/ha-alert";
@@ -337,13 +336,13 @@ class DialogFloorDetail extends DirtyStateProviderMixin<FloorFormState>()(
this._updateDirtyState(this._currentState());
}
private _nameChanged(ev: InputEvent & HASSDomTargetEvent<HaInput>) {
private _nameChanged(ev: InputEvent) {
this._error = undefined;
this._name = (ev.target as HaInput).value ?? "";
this._updateDirtyState(this._currentState());
}
private _levelChanged(ev: InputEvent & HASSDomTargetEvent<HaInput>) {
private _levelChanged(ev: InputEvent) {
this._error = undefined;
this._level =
(ev.target as HaInput).value === ""
@@ -2,7 +2,6 @@ import { mdiClose, mdiOpenInNew } from "@mdi/js";
import { css, html, LitElement, nothing } from "lit";
import { customElement, property, query, state } from "lit/decorators";
import { fireEvent } from "../../../common/dom/fire_event";
import type { HASSDomTargetEvent } from "../../../common/dom/fire_event";
import { withViewTransition } from "../../../common/util/view-transition";
import "../../../components/ha-alert";
import "../../../components/ha-button";
@@ -280,7 +279,7 @@ class DialogImportBlueprint extends DirtyStateProviderMixin<BlueprintImportState
});
}
private _inputChanged(ev: HASSDomTargetEvent<HaInput>) {
private _inputChanged(ev: Event) {
this._updateDirtyState({
value: (ev.target as HaInput).value ?? "",
hasResult: !!this._result,
@@ -13,10 +13,7 @@ import { LitElement, html } from "lit";
import { customElement, property, state } from "lit/decorators";
import memoizeOne from "memoize-one";
import { storage } from "../../../common/decorators/storage";
import type {
HASSDomCurrentTargetEvent,
HASSDomEvent,
} from "../../../common/dom/fire_event";
import type { HASSDomEvent } from "../../../common/dom/fire_event";
import { fireEvent } from "../../../common/dom/fire_event";
import { computeStateName } from "../../../common/entity/compute_state_name";
import { navigate } from "../../../common/navigate";
@@ -486,7 +483,7 @@ class HaBlueprintOverview extends LitElement {
this._createNew(blueprint);
}
private _handleUsageClick = (ev: HASSDomCurrentTargetEvent<HTMLElement>) => {
private _handleUsageClick = (ev: Event) => {
ev.stopPropagation();
ev.preventDefault();
const target = ev.currentTarget as HTMLElement | null;
@@ -2,7 +2,6 @@ import type { CSSResultGroup } from "lit";
import { css, html, LitElement, nothing } from "lit";
import { customElement, property, state } from "lit/decorators";
import { fireEvent } from "../../../../common/dom/fire_event";
import type { HASSDomEvent } from "../../../../common/dom/fire_event";
import "../../../../components/entity/ha-statistic-picker";
import "../../../../components/ha-button";
import "../../../../components/ha-dialog";
@@ -341,7 +340,7 @@ export class DialogEnergyBatterySettings
}
private _handlePowerConfigChanged(
ev: HASSDomEvent<HASSDomEvents["power-config-changed"]>
ev: CustomEvent<{ powerType: PowerType; powerConfig: PowerConfig }>
) {
this._powerType = ev.detail.powerType;
this._powerConfig = ev.detail.powerConfig;
@@ -3,7 +3,6 @@ import type { CSSResultGroup } from "lit";
import { css, html, LitElement, nothing } from "lit";
import { customElement, state } from "lit/decorators";
import { fireEvent } from "../../../../common/dom/fire_event";
import type { HASSDomCurrentTargetEvent } from "../../../../common/dom/fire_event";
import type { LocalizeKeys } from "../../../../common/translations/localize";
import "../../../../components/ha-alert";
import "../../../../components/ha-button";
@@ -215,7 +214,7 @@ export class DialogEnergyCustomise
`;
}
private _toggleCard = (ev: HASSDomCurrentTargetEvent<HaSwitch>): void => {
private _toggleCard = (ev: Event): void => {
const target = ev.currentTarget as HaSwitch;
const cardKey = target.dataset.cardKey;
if (!cardKey) {
@@ -2,7 +2,6 @@ import type { CSSResultGroup } from "lit";
import { css, html, LitElement, nothing } from "lit";
import { customElement, property, state } from "lit/decorators";
import { fireEvent } from "../../../../common/dom/fire_event";
import type { HASSDomCurrentTargetEvent } from "../../../../common/dom/fire_event";
import "../../../../components/entity/ha-entity-picker";
import "../../../../components/entity/ha-statistic-picker";
import "../../../../components/ha-button";
@@ -354,7 +353,7 @@ export class DialogEnergyGasSettings
`;
}
private _handleCostChanged(ev: HASSDomCurrentTargetEvent<HaRadioGroup>) {
private _handleCostChanged(ev: Event) {
this._costs = (ev.currentTarget as HaRadioGroup).value as CostType;
this._updateFormDirtyState();
}
@@ -2,10 +2,6 @@ import type { CSSResultGroup } from "lit";
import { css, html, LitElement, nothing } from "lit";
import { customElement, property, state } from "lit/decorators";
import { fireEvent } from "../../../../common/dom/fire_event";
import type {
HASSDomCurrentTargetEvent,
HASSDomEvent,
} from "../../../../common/dom/fire_event";
import "../../../../components/entity/ha-entity-picker";
import "../../../../components/entity/ha-statistic-picker";
import "../../../../components/ha-button";
@@ -597,9 +593,7 @@ export class DialogEnergyGridSettings
this._updateFormDirtyState();
}
private _handleImportCostTypeChanged(
ev: HASSDomCurrentTargetEvent<HaRadioGroup>
) {
private _handleImportCostTypeChanged(ev: Event) {
this._importCostType = (ev.currentTarget as HaRadioGroup).value as CostType;
// Clear other cost fields when switching types
this._source = {
@@ -611,9 +605,7 @@ export class DialogEnergyGridSettings
this._updateFormDirtyState();
}
private _handleExportCostTypeChanged(
ev: HASSDomCurrentTargetEvent<HaRadioGroup>
) {
private _handleExportCostTypeChanged(ev: Event) {
this._exportCostType = (ev.currentTarget as HaRadioGroup).value as CostType;
// Clear other cost fields when switching types
this._source = {
@@ -638,7 +630,7 @@ export class DialogEnergyGridSettings
this._updateFormDirtyState();
}
private _numberCostChanged(ev: HASSDomCurrentTargetEvent<HTMLInputElement>) {
private _numberCostChanged(ev: Event) {
const input = ev.currentTarget as HTMLInputElement;
const value = input.value ? parseFloat(input.value) : null;
this._source = { ...this._source!, number_energy_price: value };
@@ -661,9 +653,7 @@ export class DialogEnergyGridSettings
this._updateFormDirtyState();
}
private _numberCompensationChanged(
ev: HASSDomCurrentTargetEvent<HTMLInputElement>
) {
private _numberCompensationChanged(ev: Event) {
const input = ev.currentTarget as HTMLInputElement;
const value = input.value ? parseFloat(input.value) : null;
this._source = { ...this._source!, number_energy_price_export: value };
@@ -671,7 +661,7 @@ export class DialogEnergyGridSettings
}
private _handlePowerConfigChanged(
ev: HASSDomEvent<HASSDomEvents["power-config-changed"]>
ev: CustomEvent<{ powerType: PowerType; powerConfig: PowerConfig }>
) {
this._powerType = ev.detail.powerType;
this._powerConfig = ev.detail.powerConfig;
@@ -3,7 +3,6 @@ import type { CSSResultGroup } from "lit";
import { css, html, LitElement, nothing } from "lit";
import { customElement, property, state } from "lit/decorators";
import { fireEvent } from "../../../../common/dom/fire_event";
import type { HASSDomCurrentTargetEvent } from "../../../../common/dom/fire_event";
import "../../../../components/entity/ha-statistic-picker";
import "../../../../components/ha-button";
import "../../../../components/ha-checkbox";
@@ -291,7 +290,7 @@ export class DialogEnergySolarSettings
);
}
private _handleForecastChanged(ev: HASSDomCurrentTargetEvent<HaRadioGroup>) {
private _handleForecastChanged(ev: Event) {
this._forecast = (ev.currentTarget as HaRadioGroup).value === "true";
this._updateFormDirtyState();
}
@@ -2,7 +2,6 @@ import type { CSSResultGroup } from "lit";
import { css, html, LitElement, nothing } from "lit";
import { customElement, property, state } from "lit/decorators";
import { fireEvent } from "../../../../common/dom/fire_event";
import type { HASSDomCurrentTargetEvent } from "../../../../common/dom/fire_event";
import "../../../../components/entity/ha-entity-picker";
import "../../../../components/entity/ha-statistic-picker";
import "../../../../components/ha-button";
@@ -290,7 +289,7 @@ export class DialogEnergyWaterSettings
`;
}
private _handleCostChanged(ev: HASSDomCurrentTargetEvent<HaRadioGroup>) {
private _handleCostChanged(ev: Event) {
this._costs = (ev.currentTarget as HaRadioGroup).value as CostType;
this._updateFormDirtyState();
}
@@ -3,7 +3,6 @@ import type { CSSResultGroup, PropertyValues, TemplateResult } from "lit";
import { css, html, LitElement, nothing } from "lit";
import { customElement, property, state } from "lit/decorators";
import { fireEvent } from "../../../../common/dom/fire_event";
import type { HASSDomCurrentTargetEvent } from "../../../../common/dom/fire_event";
import { stopPropagation } from "../../../../common/dom/stop_propagation";
import type { LocalizeKeys } from "../../../../common/translations/localize";
import "../../../../components/entity/ha-statistic-picker";
@@ -235,7 +234,7 @@ export class HaEnergyPowerConfig extends LitElement {
fireEvent(this, "hass-more-info", { entityId: this.helperEntityId! });
}
private _handlePowerTypeChanged(ev: HASSDomCurrentTargetEvent<HaRadioGroup>) {
private _handlePowerTypeChanged(ev: Event) {
const newPowerType = (ev.currentTarget as HaRadioGroup).value as PowerType;
// Clear power config when switching types
fireEvent(this, "power-config-changed", {
@@ -3,7 +3,6 @@ import type { CSSResultGroup, TemplateResult } from "lit";
import { LitElement, css, html, nothing } from "lit";
import { customElement, property, state } from "lit/decorators";
import memoizeOne from "memoize-one";
import type { HASSDomCurrentTargetEvent } from "../../../../../common/dom/fire_event";
import { computeDeviceName } from "../../../../../common/entity/compute_device_name";
import "../../../../../components/ha-alert";
import "../../../../../components/ha-button";
@@ -452,9 +451,7 @@ export class BluetoothAdapterInfoPage extends LitElement {
return this._formatModeLabel(scannerState.current_mode);
}
private async _handleEnable(
ev: HASSDomCurrentTargetEvent<HTMLElement & { entry: ConfigEntry }>
) {
private async _handleEnable(ev: Event) {
const button = ev.currentTarget as HTMLElement & { entry: ConfigEntry };
const entryId = button.entry.entry_id;
try {
@@ -477,9 +474,7 @@ export class BluetoothAdapterInfoPage extends LitElement {
}
}
private _openOptionFlow(
ev: HASSDomCurrentTargetEvent<HTMLElement & { entry: ConfigEntry }>
) {
private _openOptionFlow(ev: Event) {
ev.preventDefault();
ev.stopPropagation();
const button = ev.currentTarget as HTMLElement & { entry: ConfigEntry };
@@ -3,7 +3,6 @@ import type { CSSResultGroup } from "lit";
import { LitElement, css, html, nothing } from "lit";
import { customElement, property, state } from "lit/decorators";
import { fireEvent } from "../../../../../common/dom/fire_event";
import type { HASSDomCurrentTargetEvent } from "../../../../../common/dom/fire_event";
import "../../../../../components/ha-alert";
import "../../../../../components/ha-button";
import "../../../../../components/ha-dialog-footer";
@@ -212,9 +211,7 @@ class DialogMatterLockManage extends LitElement {
`;
}
private _handleUserClick(
ev: HASSDomCurrentTargetEvent<HTMLElement & { user: MatterLockUser }>
): void {
private _handleUserClick(ev: Event): void {
// Ignore clicks that originated from the delete button
const path = ev.composedPath();
if (path.some((el) => (el as HTMLElement).tagName === "HA-ICON-BUTTON")) {
@@ -224,9 +221,7 @@ class DialogMatterLockManage extends LitElement {
this._editUser(user);
}
private _handleDeleteUserClick(
ev: HASSDomCurrentTargetEvent<HTMLElement & { user: MatterLockUser }>
): void {
private _handleDeleteUserClick(ev: Event): void {
ev.preventDefault();
ev.stopPropagation();
const user = (ev.currentTarget as any).user as MatterLockUser;
@@ -11,7 +11,10 @@ import { addGroup, fetchGroupableDevices } from "../../../../../data/zha";
import "../../../../../layouts/hass-subpage";
import type { HomeAssistant } from "../../../../../types";
import "./zha-device-endpoint-list";
import type { ZHADeviceEndpointList } from "./zha-device-endpoint-list";
import type {
DeviceEndpointSelectionChangedEvent,
ZHADeviceEndpointList,
} from "./zha-device-endpoint-list";
@customElement("zha-add-group-page")
export class ZHAAddGroupPage extends LitElement {
@@ -128,7 +131,7 @@ export class ZHAAddGroupPage extends LitElement {
}
private _handleAddSelectionChanged(
ev: HASSDomEvent<HASSDomEvents["selection-changed"]>
ev: HASSDomEvent<DeviceEndpointSelectionChangedEvent>
): void {
this._selectedDevicesToAdd = ev.detail.value;
}
@@ -14,11 +14,14 @@ import type { CSSResultGroup, PropertyValues, TemplateResult } from "lit";
import { css, html, LitElement, nothing } from "lit";
import { customElement, property, state } from "lit/decorators";
import { isComponentLoaded } from "../../../../../common/config/is_component_loaded";
import type { HASSDomCurrentTargetEvent } from "../../../../../common/dom/fire_event";
import { navigate } from "../../../../../common/navigate";
import { animationStyles } from "../../../../../resources/theme/animations.globals";
import "../../../../../components/ha-alert";
import "../../../../../components/ha-button";
import "../../../../../components/ha-card";
import "../../../../../components/buttons/ha-progress-button";
import type { HaProgressButton } from "../../../../../components/buttons/ha-progress-button";
import "../../../../../components/ha-icon-next";
import "../../../../../components/ha-md-list";
@@ -67,6 +70,8 @@ class ZHAConfigDashboard extends LitElement {
@state() private _error?: string;
@state() private _generatingBackup = false;
protected firstUpdated(changedProperties: PropertyValues<this>) {
super.firstUpdated(changedProperties);
if (!this.hass) {
@@ -355,17 +360,18 @@ class ZHAConfigDashboard extends LitElement {
"ui.panel.config.zha.configuration_page.download_backup_description"
)}
</span>
<ha-button
<ha-progress-button
appearance="plain"
slot="end"
size="s"
.iconPath=${mdiDownload}
.progress=${this._generatingBackup}
@click=${this._createAndDownloadBackup}
>
<ha-svg-icon .path=${mdiDownload} slot="start"></ha-svg-icon>
${this.hass.localize(
"ui.panel.config.zha.configuration_page.download_backup_action"
)}
</ha-button>
</ha-progress-button>
</ha-md-list-item>
<ha-md-list-item>
<span slot="headline">
@@ -408,30 +414,32 @@ class ZHAConfigDashboard extends LitElement {
this._configuration = await fetchZHAConfiguration(this.hass!);
}
private async _createAndDownloadBackup(): Promise<void> {
private async _createAndDownloadBackup(
ev: HASSDomCurrentTargetEvent<HaProgressButton>
): Promise<void> {
// Captured up front: currentTarget is null once the first await resolves.
const button = ev.currentTarget;
let backup_and_metadata: ZHANetworkBackupAndMetadata;
// Reading the backup from the coordinator can take 5-30 seconds.
this._generatingBackup = true;
try {
backup_and_metadata = await createZHANetworkBackup(this.hass!);
} catch (err: any) {
button.actionError();
showAlertDialog(this, {
title: "Failed to create backup",
title: this.hass.localize(
"ui.panel.config.zha.configuration_page.backup_failed"
),
text: err.message,
warning: true,
});
return;
} finally {
this._generatingBackup = false;
}
if (!backup_and_metadata.is_complete) {
await showAlertDialog(this, {
title: "Backup is incomplete",
text: "A backup has been created but it is incomplete and cannot be restored. This is a coordinator firmware limitation.",
});
}
const backupJSON: string =
"data:text/plain;charset=utf-8," +
encodeURIComponent(JSON.stringify(backup_and_metadata.backup, null, 4));
const backupTime: Date = new Date(
Date.parse(backup_and_metadata.backup.backup_time)
);
@@ -441,7 +449,23 @@ class ZHAConfigDashboard extends LitElement {
basename = `Incomplete ${basename}`;
}
fileDownload(backupJSON, `${basename}.json`);
const blob = new Blob(
[JSON.stringify(backup_and_metadata.backup, null, 4)],
{ type: "application/json" }
);
fileDownload(URL.createObjectURL(blob), `${basename}.json`);
button.actionSuccess();
if (!backup_and_metadata.is_complete) {
showAlertDialog(this, {
title: this.hass.localize(
"ui.panel.config.zha.configuration_page.backup_incomplete_title"
),
text: this.hass.localize(
"ui.panel.config.zha.configuration_page.backup_incomplete_text"
),
});
}
}
private _openOptionFlow() {
@@ -517,10 +541,6 @@ class ZHAConfigDashboard extends LitElement {
--md-item-overflow: visible;
}
ha-button[size="s"] ha-svg-icon {
--mdc-icon-size: 16px;
}
.network-status div.heading {
display: flex;
align-items: center;
@@ -4,15 +4,10 @@ import type { CSSResultGroup, TemplateResult } from "lit";
import { css, html, LitElement, nothing } from "lit";
import { customElement, property, query, state } from "lit/decorators";
import { repeat } from "lit/directives/repeat";
import type {
HASSDomCurrentTargetEvent,
HASSDomEvent,
} from "../../../../../common/dom/fire_event";
import "../../../../../components/ha-card";
import "../../../../../components/ha-icon-button";
import "../../../../../components/ha-list";
import "../../../../../components/input/ha-input-search";
import type { HaInputSearch } from "../../../../../components/input/ha-input-search";
import "../../../../../components/item/ha-list-item-base";
import "../../../../../components/item/ha-list-item-option";
import type { HaListItemOption } from "../../../../../components/item/ha-list-item-option";
@@ -274,16 +269,11 @@ export class ZHADeviceEndpointList extends LitElement {
.join(" · ");
}
private _handleFilterChanged(
ev: HASSDomCurrentTargetEvent<HaInputSearch>
): void {
this._filter = ev.currentTarget.value ?? "";
private _handleFilterChanged(ev: Event): void {
this._filter = (ev.currentTarget as HTMLInputElement).value;
}
private _handleItemSelected(
ev: HASSDomEvent<HASSDomEvents["ha-list-item-selected"]> &
HASSDomCurrentTargetEvent<HaListSelectable>
): void {
private _handleItemSelected(ev: CustomEvent<number>): void {
const list = ev.currentTarget as HaListSelectable;
let selectedDeviceIds = this._selectedDeviceIds;
@@ -300,10 +290,7 @@ export class ZHADeviceEndpointList extends LitElement {
}
}
private _handleItemDeselected(
ev: HASSDomEvent<HASSDomEvents["ha-list-item-deselected"]> &
HASSDomCurrentTargetEvent<HaListSelectable>
): void {
private _handleItemDeselected(ev: CustomEvent<number>): void {
const list = ev.currentTarget as HaListSelectable;
let selectedDeviceIds = this._selectedDeviceIds;
@@ -19,7 +19,10 @@ import "../../../../../layouts/hass-subpage";
import type { HomeAssistant } from "../../../../../types";
import { formatAsPaddedHex } from "./functions";
import "./zha-device-endpoint-list";
import type { ZHADeviceEndpointList } from "./zha-device-endpoint-list";
import type {
DeviceEndpointSelectionChangedEvent,
ZHADeviceEndpointList,
} from "./zha-device-endpoint-list";
import { showZHAAddGroupMembersDialog } from "./show-dialog-zha-add-group-members";
@customElement("zha-group-page")
@@ -199,7 +202,7 @@ export class ZHAGroupPage extends LitElement {
}
private _handleRemoveSelectionChanged(
ev: HASSDomEvent<HASSDomEvents["selection-changed"]>
ev: HASSDomEvent<DeviceEndpointSelectionChangedEvent>
): void {
this._selectedDevicesToRemove = ev.detail.value;
}
@@ -1,16 +1,13 @@
import type { CSSResultGroup, PropertyValues, TemplateResult } from "lit";
import { css, html, LitElement, nothing } from "lit";
import { customElement, property, state } from "lit/decorators";
import type { HASSDomCurrentTargetEvent } from "../../../../../common/dom/fire_event";
import "../../../../../components/buttons/ha-progress-button";
import "../../../../../components/ha-card";
import "../../../../../components/ha-md-list";
import "../../../../../components/ha-md-list-item";
import "../../../../../components/ha-select";
import "../../../../../components/input/ha-input";
import type { HaInput } from "../../../../../components/input/ha-input";
import "../../../../../components/ha-switch";
import type { HaSwitch } from "../../../../../components/ha-switch";
import type { ZHAConfiguration } from "../../../../../data/zha";
import {
fetchZHAConfiguration,
@@ -21,8 +18,6 @@ import { haStyle } from "../../../../../resources/styles";
import type { HomeAssistant, Route } from "../../../../../types";
const PREDEFINED_TIMEOUTS = [1800, 3600, 7200, 21600, 43200, 86400];
type ZHASwitchChangeEvent = HASSDomCurrentTargetEvent<HaSwitch>;
type ZHAInputChangeEvent = HASSDomCurrentTargetEvent<HaInput>;
@customElement("zha-options-page")
class ZHAOptionsPage extends LitElement {
@@ -350,54 +345,53 @@ class ZHAOptionsPage extends LitElement {
`;
}
private _enableIdentifyOnJoinChanged(ev: ZHASwitchChangeEvent): void {
this._configuration!.data.zha_options.enable_identify_on_join =
ev.currentTarget.checked;
private _enableIdentifyOnJoinChanged(ev: Event): void {
const checked = (ev.target as HTMLInputElement).checked;
this._configuration!.data.zha_options.enable_identify_on_join = checked;
this.requestUpdate();
}
private _enhancedLightTransitionChanged(ev: ZHASwitchChangeEvent): void {
this._configuration!.data.zha_options.enhanced_light_transition =
ev.currentTarget.checked;
private _enhancedLightTransitionChanged(ev: Event): void {
const checked = (ev.target as HTMLInputElement).checked;
this._configuration!.data.zha_options.enhanced_light_transition = checked;
this.requestUpdate();
}
private _lightTransitioningFlagChanged(ev: ZHASwitchChangeEvent): void {
this._configuration!.data.zha_options.light_transitioning_flag =
ev.currentTarget.checked;
private _lightTransitioningFlagChanged(ev: Event): void {
const checked = (ev.target as HTMLInputElement).checked;
this._configuration!.data.zha_options.light_transitioning_flag = checked;
this.requestUpdate();
}
private _groupMembersAssumeStateChanged(ev: ZHASwitchChangeEvent): void {
this._configuration!.data.zha_options.group_members_assume_state =
ev.currentTarget.checked;
private _groupMembersAssumeStateChanged(ev: Event): void {
const checked = (ev.target as HTMLInputElement).checked;
this._configuration!.data.zha_options.group_members_assume_state = checked;
this.requestUpdate();
}
private _enableMainsStartupPollingChanged(ev: ZHASwitchChangeEvent): void {
private _enableMainsStartupPollingChanged(ev: Event): void {
const checked = (ev.target as HTMLInputElement).checked;
this._configuration!.data.zha_options.enable_mains_startup_polling =
ev.currentTarget.checked;
checked;
this.requestUpdate();
}
private _defaultLightTransitionChanged(ev: ZHAInputChangeEvent): void {
this._configuration!.data.zha_options.default_light_transition = Number(
ev.currentTarget.value
);
private _defaultLightTransitionChanged(ev: Event): void {
const value = Number((ev.target as HTMLInputElement).value);
this._configuration!.data.zha_options.default_light_transition = value;
this.requestUpdate();
}
private _customMainsSecondsChanged(ev: ZHAInputChangeEvent): void {
this._configuration!.data.zha_options.consider_unavailable_mains = Number(
ev.currentTarget.value
);
private _customMainsSecondsChanged(ev: Event): void {
const seconds = Number((ev.target as HTMLInputElement).value);
this._configuration!.data.zha_options.consider_unavailable_mains = seconds;
this.requestUpdate();
}
private _customBatterySecondsChanged(ev: ZHAInputChangeEvent): void {
this._configuration!.data.zha_options.consider_unavailable_battery = Number(
ev.currentTarget.value
);
private _customBatterySecondsChanged(ev: Event): void {
const seconds = Number((ev.target as HTMLInputElement).value);
this._configuration!.data.zha_options.consider_unavailable_battery =
seconds;
this.requestUpdate();
}
@@ -425,15 +419,7 @@ class ZHAOptionsPage extends LitElement {
this.requestUpdate();
}
private async _updateConfiguration(
ev: HASSDomCurrentTargetEvent<
HTMLElement & {
progress: boolean;
actionSuccess: () => void;
actionError: () => void;
}
>
): Promise<void> {
private async _updateConfiguration(ev: Event): Promise<void> {
const button = ev.currentTarget as HTMLElement & {
progress: boolean;
actionSuccess: () => void;
@@ -2,7 +2,6 @@ import type { CSSResultGroup } from "lit";
import { LitElement, css, html, nothing } from "lit";
import { customElement, property, state } from "lit/decorators";
import { fireEvent } from "../../../../../common/dom/fire_event";
import type { HASSDomCurrentTargetEvent } from "../../../../../common/dom/fire_event";
import type { LocalizeKeys } from "../../../../../common/translations/localize";
import "../../../../../components/ha-alert";
import "../../../../../components/ha-button";
@@ -479,9 +478,7 @@ class DialogZwaveCredentialUserEdit extends DirtyStateProviderMixin<CredentialFo
}
}
private _handleCredentialTypeChanged(
ev: HASSDomCurrentTargetEvent<HaRadioGroup>
): void {
private _handleCredentialTypeChanged(ev: Event): void {
this._credentialType = (ev.currentTarget as HaRadioGroup)
.value as ZwaveCredentialType;
// Switching types invalidates any data tied to the previous type's
@@ -2,13 +2,11 @@ import { mdiCloseCircle } from "@mdi/js";
import { LitElement, css, html, nothing } from "lit";
import { customElement, property, state } from "lit/decorators";
import { fireEvent } from "../../../../../common/dom/fire_event";
import type { HASSDomCurrentTargetEvent } from "../../../../../common/dom/fire_event";
import "../../../../../components/ha-button";
import "../../../../../components/ha-select";
import type { HaSelectSelectEvent } from "../../../../../components/ha-select";
import "../../../../../components/ha-spinner";
import "../../../../../components/input/ha-input";
import type { HaInput } from "../../../../../components/input/ha-input";
import {
getZwaveNodeRawConfigParameter,
setZwaveNodeRawConfigParameter,
@@ -131,9 +129,9 @@ class ZWaveJSCustomParam extends LitElement {
return parsed;
}
private _customParamNumberChanged(ev: HASSDomCurrentTargetEvent<HaInput>) {
private _customParamNumberChanged(ev: Event) {
this._customParamNumber = this._tryParseNumber(
ev.currentTarget.value ?? ""
(ev.target as HTMLInputElement).value
);
}
@@ -141,8 +139,8 @@ class ZWaveJSCustomParam extends LitElement {
this._valueSize = this._tryParseNumber(ev.detail.value) ?? 1;
}
private _customValueChanged(ev: HASSDomCurrentTargetEvent<HaInput>) {
this._value = this._tryParseNumber(ev.currentTarget.value ?? "");
private _customValueChanged(ev: Event) {
this._value = this._tryParseNumber((ev.target as HTMLInputElement).value);
}
private _customValueFormatChanged(ev: HaSelectSelectEvent) {
+1 -4
View File
@@ -4,7 +4,6 @@ import { css, 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 type { HASSDomCurrentTargetEvent } from "../../../common/dom/fire_event";
import type { LocalizeFunc } from "../../../common/translations/localize";
import "../../../components/buttons/ha-call-service-button";
import "../../../components/ha-alert";
@@ -284,9 +283,7 @@ export class SystemLogCard extends LitElement {
fileDownload(signedUrl.path, logFileName);
}
private _openLog(
ev: HASSDomCurrentTargetEvent<HTMLElement & { logItem: LoggedError }>
): void {
private _openLog(ev: Event): void {
const item = (ev.currentTarget as any).logItem;
showSystemLogDetailDialog(this, { item });
}
@@ -2,7 +2,6 @@ import type { PropertyValues } from "lit";
import { css, html, LitElement, nothing } from "lit";
import { customElement, property, query, state } from "lit/decorators";
import { isComponentLoaded } from "../../../common/config/is_component_loaded";
import type { HASSDomCurrentTargetEvent } from "../../../common/dom/fire_event";
import { isIPAddress } from "../../../common/string/is_ip_address";
import "../../../components/ha-alert";
import "../../../components/ha-button";
@@ -364,12 +363,12 @@ class ConfigUrlForm extends SubscribeMixin(LitElement) {
);
}
private _toggleCloud(ev: HASSDomCurrentTargetEvent<HaSwitch>) {
private _toggleCloud(ev: Event) {
this._cloudChecked = (ev.currentTarget as HaSwitch).checked;
this._showCustomExternalUrl = !this._cloudChecked;
}
private _toggleInternalAutomatic(ev: HASSDomCurrentTargetEvent<HaSwitch>) {
private _toggleInternalAutomatic(ev: Event) {
this._showCustomInternalUrl = !(ev.currentTarget as HaSwitch).checked;
}
@@ -2,10 +2,6 @@ import { mdiDeleteOutline, mdiMenuDown, mdiPlus, mdiWifi } from "@mdi/js";
import { css, type CSSResultGroup, html, LitElement, nothing } from "lit";
import { customElement, property, state } from "lit/decorators";
import { cache } from "lit/directives/cache";
import type {
HASSDomCurrentTargetEvent,
HASSDomTargetEvent,
} from "../../../common/dom/fire_event";
import "../../../components/ha-alert";
import "../../../components/ha-button";
import "../../../components/ha-card";
@@ -634,9 +630,7 @@ export class HassioNetwork extends LitElement {
this._interface = { ...this._interfaces[this._curTabIndex] };
}
private _handleRadioValueChanged(
ev: HASSDomCurrentTargetEvent<HaRadioGroup & { version: "ipv4" | "ipv6" }>
): void {
private _handleRadioValueChanged(ev: Event): void {
const source = ev.currentTarget as HaRadioGroup;
const value = source.value as "disabled" | "auto" | "static";
const version = (source as any).version as "ipv4" | "ipv6";
@@ -651,9 +645,7 @@ export class HassioNetwork extends LitElement {
this.requestUpdate("_interface");
}
private _handleRadioValueChangedAp(
ev: HASSDomCurrentTargetEvent<HaRadioGroup>
): void {
private _handleRadioValueChangedAp(ev: Event): void {
const source = ev.currentTarget as HaRadioGroup;
const value = source.value as "open" | "wep" | "wpa-psk";
this._wifiConfiguration!.auth = value;
@@ -661,11 +653,7 @@ export class HassioNetwork extends LitElement {
this.requestUpdate("_wifiConfiguration");
}
private _handleInputValueChanged(
ev: HASSDomTargetEvent<
HaInput & { version: "ipv4" | "ipv6"; index: number }
>
): void {
private _handleInputValueChanged(ev: Event): void {
const source = ev.target as HaInput;
const value = source.value;
const version = (ev.target as any).version as "ipv4" | "ipv6";
@@ -703,7 +691,7 @@ export class HassioNetwork extends LitElement {
}
}
private _handleInputValueChangedWifi(ev: HASSDomTargetEvent<HaInput>): void {
private _handleInputValueChangedWifi(ev: Event): void {
const source = ev.target as HaInput;
const value = source.value;
const id = source.id;
@@ -720,9 +708,7 @@ export class HassioNetwork extends LitElement {
this._wifiConfiguration![id] = value;
}
private _addAddress(
ev: HASSDomTargetEvent<HTMLElement & { version: "ipv4" | "ipv6" }>
): void {
private _addAddress(ev: Event): void {
const version = (ev.target as any).version as "ipv4" | "ipv6";
this._interface![version]!.address!.push(
version === "ipv4" ? "0.0.0.0/24" : "::/64"
@@ -731,11 +717,7 @@ export class HassioNetwork extends LitElement {
this.requestUpdate("_interface");
}
private _removeAddress(
ev: HASSDomTargetEvent<
HTMLElement & { index: number; version: "ipv4" | "ipv6" }
>
): void {
private _removeAddress(ev: Event): void {
const source = ev.target as any;
const index = source.index as number;
const version = source.version as "ipv4" | "ipv6";
@@ -770,11 +752,7 @@ export class HassioNetwork extends LitElement {
this.requestUpdate("_interface");
}
private _removeNameserver(
ev: HASSDomTargetEvent<
HTMLElement & { index: number; version: "ipv4" | "ipv6" }
>
): void {
private _removeNameserver(ev: Event): void {
const source = ev.target as any;
const index = source.index as number;
const version = source.version as "ipv4" | "ipv6";
@@ -11,7 +11,6 @@ import { css, html, LitElement, nothing } from "lit";
import { customElement, property, state } from "lit/decorators";
import { classMap } from "lit/directives/class-map";
import { fireEvent } from "../../../../common/dom/fire_event";
import type { HASSDomCurrentTargetEvent } from "../../../../common/dom/fire_event";
import { computeAreaName } from "../../../../common/entity/compute_area_name";
import { computeDeviceName } from "../../../../common/entity/compute_device_name";
import { computeEntityEntryName } from "../../../../common/entity/compute_entity_name";
@@ -261,9 +260,7 @@ class HaPanelDevStateRenderer extends LitElement {
return output;
}
private _copyEntity = async (
ev: HASSDomCurrentTargetEvent<HTMLElement & { entity: HassEntity }>
) => {
private _copyEntity = async (ev: Event) => {
ev.preventDefault();
const entity = (ev.currentTarget as HTMLElement & { entity: HassEntity })
.entity;
@@ -273,18 +270,14 @@ class HaPanelDevStateRenderer extends LitElement {
});
};
private _entityMoreInfo(
ev: HASSDomCurrentTargetEvent<HTMLElement & { entity: HassEntity }>
) {
private _entityMoreInfo(ev: Event) {
ev.preventDefault();
const entity = (ev.currentTarget as HTMLElement & { entity: HassEntity })
.entity;
fireEvent(this, "hass-more-info", { entityId: entity.entity_id });
}
private _entitySelected(
ev: HASSDomCurrentTargetEvent<HTMLElement & { entity: HassEntity }>
) {
private _entitySelected(ev: Event) {
ev.preventDefault();
const entity = (ev.currentTarget as HTMLElement & { entity: HassEntity })
.entity;
@@ -16,11 +16,7 @@ import { consume, type ContextType } from "@lit/context";
import { css, type CSSResultGroup, html, LitElement, nothing } from "lit";
import { customElement, property, query, state } from "lit/decorators";
import memoizeOne from "memoize-one";
import type {
HASSDomCurrentTargetEvent,
HASSDomEvent,
HASSDomTargetEvent,
} from "../../../../common/dom/fire_event";
import type { HASSDomEvent } from "../../../../common/dom/fire_event";
import { fireEvent } from "../../../../common/dom/fire_event";
import { computeAreaName } from "../../../../common/entity/compute_area_name";
import {
@@ -600,9 +596,7 @@ class HaPanelDevStatistics extends KeyboardShortcutMixin(LitElement) {
`;
}
private _handleSearchChange(
ev: InputEvent & HASSDomTargetEvent<HaInputSearch>
) {
private _handleSearchChange(ev: InputEvent) {
if (this.filter === (ev.target as HaInputSearch).value) {
return;
}
@@ -712,9 +706,7 @@ class HaPanelDevStatistics extends KeyboardShortcutMixin(LitElement) {
);
}
private _showStatisticsAdjustSumDialog(
ev: HASSDomCurrentTargetEvent<HTMLElement & { statistic: StatisticData }>
) {
private _showStatisticsAdjustSumDialog(ev: Event) {
ev.stopPropagation();
showStatisticsAdjustSumDialog(this, {
statistic: (
@@ -790,11 +782,7 @@ class HaPanelDevStatistics extends KeyboardShortcutMixin(LitElement) {
});
};
private _fixIssue = async (
ev: HASSDomCurrentTargetEvent<
HTMLElement & { data: StatisticsValidationResult[] }
>
) => {
private _fixIssue = async (ev: Event) => {
const issues = (
ev.currentTarget as HTMLElement & { data: StatisticsValidationResult[] }
).data.sort(
@@ -9,7 +9,6 @@ import type { UnsubscribeFunc } from "home-assistant-js-websocket";
import type { CSSResultGroup } from "lit";
import { css, html, LitElement, nothing } from "lit";
import { customElement, property, state } from "lit/decorators";
import type { HASSDomTargetEvent } from "../../../../common/dom/fire_event";
import { stopPropagation } from "../../../../common/dom/stop_propagation";
import type { LocalizeKeys } from "../../../../common/translations/localize";
import { debounce } from "../../../../common/util/debounce";
@@ -420,7 +419,7 @@ ${
`;
}
private _splitRepositioned(ev: HASSDomTargetEvent<HaSplitPanel>) {
private _splitRepositioned(ev: Event) {
this._splitPosition = (ev.target as HaSplitPanel).position;
this._storeSplitPosition();
}
@@ -296,6 +296,10 @@ export class HuiStatisticsGraphCard extends LitElement implements LovelaceCard {
await this._getStatisticsMetaData(this._entityIds);
}
await this._getStatistics();
if (!this.isConnected) {
this._interval = undefined;
return;
}
// statistics are created every hour
if (!this._config?.energy_date_selection) {
this._interval = window.setInterval(
+1 -1
View File
@@ -147,7 +147,7 @@ export class HuiTileCard extends LitElement implements LovelaceCard {
handleAction(this, this.hass!, this._config!, ev.detail.action!);
}
private _handleIconAction(ev: CustomEvent) {
private _handleIconAction(ev: ActionHandlerEvent) {
ev.stopPropagation();
const config = {
entity: this._config!.entity,
@@ -64,6 +64,7 @@ import {
CompareMode,
downloadEnergyData,
getEnergyDataCollection,
getEnergyDefaultPeriodStorageKey,
} from "../../../data/energy";
import { SubscribeMixin } from "../../../mixins/subscribe-mixin";
import type { HomeAssistant } from "../../../types";
@@ -544,7 +545,7 @@ export class HuiEnergyPeriodSelector extends SubscribeMixin(LitElement) {
private _presetSelected(ev) {
localStorage.setItem(
`energy-default-period-_${this.collectionKey || "energy"}`,
getEnergyDefaultPeriodStorageKey(this.hass, this.collectionKey),
RANGE_KEYS[ev.detail.index]
);
}
+12 -4
View File
@@ -4,6 +4,10 @@ import { customElement, property, state } from "lit/decorators";
import { classMap } from "lit/directives/class-map";
import { styleMap } from "lit/directives/style-map";
import { STATES_OFF } from "../../../common/const";
import type {
HASSDomCurrentTargetEvent,
HASSDomTargetEvent,
} from "../../../common/dom/fire_event";
import { computeDomain } from "../../../common/entity/compute_domain";
import parseAspectRatio from "../../../common/util/parse-aspect-ratio";
import "../../../components/ha-camera-stream";
@@ -378,9 +382,11 @@ export class HuiImage extends LitElement {
this._loadState = LoadState.Error;
}
private async _onImageLoad(ev: Event): Promise<void> {
private async _onImageLoad(
ev: HASSDomTargetEvent<HTMLImageElement>
): Promise<void> {
this._loadState = LoadState.Loaded;
const imgEl = ev.target as HTMLImageElement;
const imgEl = ev.target;
if (this._ratio && this._ratio.w > 0 && this._ratio.h > 0) {
this._loadedImageSrc = imgEl.src;
}
@@ -388,9 +394,11 @@ export class HuiImage extends LitElement {
this._lastImageHeight = imgEl.offsetHeight;
}
private async _onVideoLoad(ev: Event): Promise<void> {
private async _onVideoLoad(
ev: HASSDomCurrentTargetEvent<HaCameraStream>
): Promise<void> {
this._loadState = LoadState.Loaded;
const videoEl = ev.currentTarget as HaCameraStream;
const videoEl = ev.currentTarget;
await this.updateComplete;
this._lastImageHeight = videoEl.offsetHeight;
}
@@ -15,7 +15,7 @@ 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 type { HomeAssistant, ValueChangedEvent } from "../../../../../types";
import type { ImageElementConfig } from "../../../elements/types";
import type { LovelacePictureElementEditor } from "../../../types";
import { ACTION_RELATED_CONTEXT } from "../../../components/hui-action-editor";
@@ -159,7 +159,7 @@ export class HuiImageElementEditor
: {}),
}));
private _valueChanged(ev: CustomEvent): void {
private _valueChanged(ev: ValueChangedEvent<ImageElementConfig>): void {
fireEvent(this, "config-changed", { config: ev.detail.value });
}
@@ -20,7 +20,7 @@ import "../../../../components/ha-card";
import "../../../../components/ha-form/ha-form";
import "../../../../components/ha-icon";
import "../../../../components/ha-switch";
import type { HomeAssistant } from "../../../../types";
import type { HomeAssistant, ValueChangedEvent } from "../../../../types";
import {
PREVIEW_CLICK_CALLBACK,
type PictureElementsCardConfig,
@@ -29,6 +29,7 @@ import type { LovelaceCardEditor } from "../../types";
import "../hui-sub-element-editor";
import { baseLovelaceCardConfig } from "../structs/base-card-struct";
import type { EditDetailElementEvent, SubElementEditorConfig } from "../types";
import type { UIConfigChangedEvent } from "../hui-element-editor";
import { configElementStyle } from "./config-elements-style";
import "../hui-picture-elements-card-row-editor";
import type { LovelaceElementConfig } from "../../elements/types";
@@ -228,7 +229,7 @@ export class HuiPictureElementsCardEditor
: {}),
}));
private _formChanged(ev: CustomEvent): void {
private _formChanged(ev: ValueChangedEvent<PictureElementsCardConfig>): void {
ev.stopPropagation();
if (!this._config || !this.hass) {
return;
@@ -261,7 +262,9 @@ export class HuiPictureElementsCardEditor
}
}
private _handleSubElementChanged(ev: CustomEvent): void {
private _handleSubElementChanged(
ev: UIConfigChangedEvent<LovelaceElementConfig>
): void {
ev.stopPropagation();
if (!this._config || !this.hass) {
return;
@@ -500,13 +500,20 @@ export class HuiStatisticsGraphCardEditor
// update card config with updated entity config
const index = this._subElementEditorConfig!.index!;
const oldEntityConfig = this._config!.entities[index];
const oldEntityId =
typeof oldEntityConfig === "string"
? oldEntityConfig
: oldEntityConfig?.entity;
const newEntities = [...this._config!.entities];
newEntities[index] = newEntityConfig;
let config = this._config!;
config = { ...config, entities: newEntities };
// remove inappropriate stat options dependently on entities
config = await this._cleanConfig(config);
// only a different statistic can invalidate the stat options
if (newEntityConfig.entity !== oldEntityId) {
config = await this._cleanConfig(config);
}
// normalize a generated yaml code
config = this._orderProperties(config);
this._config = config;
@@ -12,7 +12,10 @@ class EntityRowDirective extends Directive {
private _name?: string;
private _lastHass?: HomeAssistant;
render(entityId: string, name: string, hass: HomeAssistant) {
this._lastHass = hass;
if (
!this._element ||
(this._entityId !== entityId &&
@@ -22,6 +25,15 @@ class EntityRowDirective extends Directive {
entity: entityId,
name,
} as LovelaceRowConfig);
this._element.addEventListener(
"ll-upgrade",
() => {
if ("hass" in this._element!) {
this._element!.hass = this._lastHass;
}
},
{ once: true }
);
} else if (this._entityId !== entityId || this._name !== name) {
(this._element as LovelaceRow).setConfig?.({
entity: entityId,
@@ -12,7 +12,12 @@ import { LitElement, css, html, nothing } from "lit";
import { customElement, property, query, state } from "lit/decorators";
import { classMap } from "lit/directives/class-map";
import { until } from "lit/directives/until";
import { fireEvent, type HASSDomEvent } from "../../common/dom/fire_event";
import { fireEvent } from "../../common/dom/fire_event";
import type {
HASSDomCurrentTargetEvent,
HASSDomEvent,
HASSDomTargetEvent,
} from "../../common/dom/fire_event";
import { computeDomain } from "../../common/entity/compute_domain";
import { computeEntityPickerDisplay } from "../../common/entity/compute_entity_name_display";
import { supportsFeature } from "../../common/entity/supports-feature";
@@ -582,14 +587,15 @@ export class BarMediaPlayer extends SubscribeMixin(LitElement) {
this._volumeSlider.value = this._volumeValue;
}
private _handleControlClick(e: MouseEvent): void {
const action = (e.currentTarget! as HTMLElement).getAttribute("action")!;
private _handleControlClick(
e: MouseEvent & HASSDomCurrentTargetEvent<HTMLElement>
): void {
const action = e.currentTarget.getAttribute("action")!;
if (!this._browserPlayer) {
handleMediaControlClick(
this.hass!,
this._stateObj!,
(e.currentTarget as HTMLElement).getAttribute("action")!
e.currentTarget.getAttribute("action")!
);
return;
}
@@ -600,12 +606,12 @@ export class BarMediaPlayer extends SubscribeMixin(LitElement) {
}
}
private _handleMediaSeekChanged(e: Event): void {
private _handleMediaSeekChanged(e: HASSDomTargetEvent<HaSlider>): void {
if (this.entityId === BROWSER_PLAYER || !this._stateObj) {
return;
}
const newValue = (e.target as HaSlider).value;
const newValue = e.target.value;
this.hass.callService("media_player", "media_seek", {
entity_id: this._stateObj.entity_id,
seek_position: newValue,
@@ -16,7 +16,7 @@ import type {
} from "../../data/data_entry_flow";
import { DirtyStateProviderMixin } from "../../mixins/dirty-state-provider-mixin";
import { haStyleDialog } from "../../resources/styles";
import type { HomeAssistant } from "../../types";
import type { HomeAssistant, ValueChangedEvent } from "../../types";
let instance = 0;
@@ -229,7 +229,7 @@ class HaMfaModuleSetupFlow extends DirtyStateProviderMixin<
});
}
private _stepDataChanged(ev: CustomEvent) {
private _stepDataChanged(ev: ValueChangedEvent<Record<string, unknown>>) {
this._stepData = ev.detail.value;
this._updateDirtyState(this._stepData);
}
@@ -2,6 +2,7 @@ import type { TemplateResult } from "lit";
import { html, LitElement } from "lit";
import { customElement, property } from "lit/decorators";
import { fireEvent } from "../../common/dom/fire_event";
import type { HASSDomTargetEvent } from "../../common/dom/fire_event";
import "../../components/ha-switch";
import type { HaSwitch } from "../../components/ha-switch";
import "../../components/item/ha-row-item";
@@ -33,8 +34,8 @@ class HaEnableShortcutsRow extends LitElement {
`;
}
private async _checkedChanged(ev: Event) {
const enabled = (ev.target as HaSwitch).checked;
private async _checkedChanged(ev: HASSDomTargetEvent<HaSwitch>) {
const enabled = ev.target.checked;
if (enabled === this.hass.enableShortcuts) {
return;
}
+3 -2
View File
@@ -2,6 +2,7 @@ import type { TemplateResult } from "lit";
import { html, LitElement } from "lit";
import { customElement, property } from "lit/decorators";
import { fireEvent } from "../../common/dom/fire_event";
import type { HASSDomTargetEvent } from "../../common/dom/fire_event";
import "../../components/ha-switch";
import type { HaSwitch } from "../../components/ha-switch";
import "../../components/item/ha-row-item";
@@ -31,8 +32,8 @@ class HaForcedNarrowRow extends LitElement {
`;
}
private async _checkedChanged(ev: Event) {
const newValue = (ev.target as HaSwitch).checked;
private async _checkedChanged(ev: HASSDomTargetEvent<HaSwitch>) {
const newValue = ev.target.checked;
if (newValue === (this.hass.dockedSidebar === "always_hidden")) {
return;
}
@@ -3,6 +3,7 @@ import type { CSSResultGroup, TemplateResult } from "lit";
import { css, html, LitElement, nothing } from "lit";
import { customElement, property, state } from "lit/decorators";
import { fireEvent } from "../../common/dom/fire_event";
import type { HASSDomCurrentTargetEvent } from "../../common/dom/fire_event";
import { copyToClipboard } from "../../common/util/copy-clipboard";
import { withViewTransition } from "../../common/util/view-transition";
import "../../components/ha-alert";
@@ -11,6 +12,7 @@ import "../../components/ha-dialog";
import "../../components/ha-dialog-footer";
import "../../components/ha-svg-icon";
import "../../components/input/ha-input";
import type { HaInput } from "../../components/input/ha-input";
import { DirtyStateProviderMixin } from "../../mixins/dirty-state-provider-mixin";
import type { HomeAssistant } from "../../types";
import { showToast } from "../../util/toast";
@@ -190,8 +192,10 @@ export class HaLongLivedAccessTokenDialog extends DirtyStateProviderMixin<string
`;
}
private _nameChanged(ev: Event) {
this._name = (ev.currentTarget as HTMLInputElement).value;
private _nameChanged(
ev: HASSDomCurrentTargetEvent<Omit<HaInput, "value"> & { value: string }>
) {
this._name = ev.currentTarget.value;
this._errorMessage = undefined;
this._updateDirtyState(this._name);
}
@@ -5,9 +5,11 @@ import { customElement, property } from "lit/decorators";
import memoizeOne from "memoize-one";
import { relativeTime } from "../../common/datetime/relative_time";
import { fireEvent } from "../../common/dom/fire_event";
import type { HASSDomCurrentTargetEvent } from "../../common/dom/fire_event";
import "../../components/ha-button";
import "../../components/ha-card";
import "../../components/ha-icon-button";
import type { HaIconButton } from "../../components/ha-icon-button";
import "../../components/ha-settings-row";
import type { RefreshToken } from "../../data/refresh_token";
import {
@@ -110,8 +112,10 @@ class HaLongLivedTokens extends LitElement {
});
}
private async _deleteToken(ev: Event): Promise<void> {
const token = (ev.currentTarget as any).token;
private async _deleteToken(
ev: HASSDomCurrentTargetEvent<HaIconButton & { token: RefreshToken }>
): Promise<void> {
const token = ev.currentTarget.token;
if (
!(await showConfirmationDialog(this, {
title: this.hass.localize(

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