Compare commits

...

11 Commits

Author SHA1 Message Date
Simon Lamon 608cdf168e Move live condition test inline 2026-06-05 20:53:18 +00:00
karwosts d45b5d20a2 Fix hui-editor search (#52453) 2026-06-05 20:27:21 +02:00
elcaptain ad593a6733 added Brand Personality (#52451)
* added Brand Personality

as a result of https://github.com/OpenHomeFoundation/roadmap/issues/124

* fixed formatting
2026-06-05 20:26:15 +02:00
Aidan Timson 18c084b4da Add maintenance my redirect (#52442)
Add maintenance My redirect
2026-06-05 20:21:43 +02:00
renovate[bot] d761a68bee Update yarn monorepo to v4.16.0 (#52449) 2026-06-05 14:52:54 +01:00
Petar Petrov 937abc7e86 Use context instead of hass in domain state display components (#52448) 2026-06-05 14:11:58 +01:00
Petar Petrov 6d3979be17 Use context instead of hass in cover and valve control rows (#52446) 2026-06-05 14:11:01 +01:00
Jan-Philipp Benecke ef44a2d682 Refactor developer tools to use top bar component (#52316)
* Refactor developer tools to use top bar component

* Format
2026-06-05 15:43:03 +03:00
renovate[bot] 378c5a3c9d Update rspack monorepo to v2.0.6 (#52447)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-06-05 15:31:29 +03:00
renovate[bot] aaea886d51 Update dependency idb-keyval to v6.2.5 (#52445)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-06-05 15:30:56 +03:00
Petar Petrov a040dca639 Use consumeLocalize instead of hass in localize-only leaf components (#52443) 2026-06-05 15:13:09 +03:00
46 changed files with 1428 additions and 1346 deletions
-940
View File
File diff suppressed because one or more lines are too long
+944
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -13,4 +13,4 @@ nodeLinker: node-modules
npmMinimalAgeGate: 3d
yarnPath: .yarn/releases/yarn-4.15.0.cjs
yarnPath: .yarn/releases/yarn-4.16.0.cjs
@@ -0,0 +1,48 @@
---
title: "Brand Personality"
---
# Brand Personality
These five traits describe who Home Assistant is, not how it speaks. Tone of voice the playfulness, the informality, the warmth, etc should flow naturally from these, and will help guide writers on how to bring the brand personality to life.
The first four traits are relational: they describe how Home Assistant behaves toward its users.
_Welcoming_ is about how we receive them.\
_Candid_ is about how we communicate with them.\
_Supportive_ is about how we help them.\
_Generous_ is about how/what we give to them.\
If any of these feel similar, its because theyre all expressions of the same underlying character, just in different moments of the user relationship.
_Independent_ is different. Its foundational: it describes who Home Assistant is at its core.\
And its because of that independence that the other four traits feel genuine rather than performed. A corporate brand can try to be welcoming, candid, supportive, and generous,
but without independence, those traits will always be managed and moderated.
## Welcoming
**Warm and open, kind, friendly, approachable, accommodating**\
_But not: people pleasing, appeasing, sycophantic, ingratiating_\
Home Assistant feels like a knowledgeable friend, not a product. We meet you at your own level, never talk down to you, and make you feel valued regardless of your technical ability. This isnt performative, its expressed naturally in the small things: how errors are explained, documentation is written, and how the community talks to newcomers.
## Candid
**Direct, honest, transparent, unpretentious**\
_But not: unfriendly, rude, blunt, unempathetic_\
Home Assistant says what it means. We dont hide complexity behind false simplicity, fall back on marketing fluff, or pretend limitations dont exist. We respect users enough to be straight with them: about what Home Assistant can do, what it can't, and why it works the way it does. This is what builds real trust with users.
## Supportive
**Helpful, guiding, encouraging, empathetic, genuine**\
_But not: directive, hollow, condescending, over-bearing_\
Home Assistant always has your back. Whether youre just starting out or deep into a complex setup, we steer you forward without taking over. Our support is genuine: practical, patient, and there when its needed most. Because Home Assistant wants every user to succeed in building a smart home with privacy, choice, and sustainability at its heart.
## Generous
**Empowering, trusting, giving, sharing**\
_But not: overwhelming, indiscriminate, patronizing, controlling_\
Home Assistant gives you everything you need today, and as your setup evolves. There are no strings attached: no artificial limits, features locked behind tiers, or deceptive patterns designed to tie you to a closed platform. We trust users with control, access, and transparency. This generosity is reciprocal: the time, knowledge, and care our community gives freely is what keeps Home Assistant thriving and truly open.
## Independent
**Principled, liberated, confident, imperfect**\
_But not: conceited, obstinate, erratic, insular_\
Home Assistant doesnt feel the need to behave like an established tech brand or follow corporate rules. With no shareholders or VCs to answer to, we can say what we think, do things our own way, and not take ourselves too seriously. This freedom of spirit comes from the confidence of knowing our own values, and that our community shares them.
+3 -3
View File
@@ -93,7 +93,7 @@
"gulp-zopfli-green": "7.0.0",
"hls.js": "1.6.16",
"home-assistant-js-websocket": "9.6.0",
"idb-keyval": "6.2.4",
"idb-keyval": "6.2.5",
"intl-messageformat": "11.2.7",
"js-yaml": "4.2.0",
"leaflet": "1.9.4",
@@ -137,7 +137,7 @@
"@octokit/plugin-retry": "8.1.0",
"@octokit/rest": "22.0.1",
"@rsdoctor/rspack-plugin": "1.5.12",
"@rspack/core": "2.0.5",
"@rspack/core": "2.0.6",
"@rspack/dev-server": "2.0.3",
"@types/chromecast-caf-receiver": "6.0.26",
"@types/chromecast-caf-sender": "1.0.11",
@@ -212,7 +212,7 @@
"@material/mwc-list@^0.27.0": "patch:@material/mwc-list@npm%3A0.27.0#~/.yarn/patches/@material-mwc-list-npm-0.27.0-5344fc9de4.patch",
"glob@^10.2.2": "^10.5.0"
},
"packageManager": "yarn@4.15.0",
"packageManager": "yarn@4.16.0",
"volta": {
"node": "24.16.0"
}
@@ -32,10 +32,10 @@ export class HaAutomationRowLiveTest extends LitElement {
static styles = css`
:host {
position: absolute;
top: -5px;
inset-inline-end: -6px;
display: inline-block;
display: inline-flex;
align-items: center;
vertical-align: middle;
margin-inline-start: var(--ha-space-1);
}
#indicator {
width: 10px;
+9 -6
View File
@@ -1,12 +1,15 @@
import { LitElement, html, nothing } from "lit";
import { customElement, property } from "lit/decorators";
import { customElement, property, state } from "lit/decorators";
import { fireEvent } from "../common/dom/fire_event";
import type { HomeAssistant } from "../types";
import { consumeLocalize } from "../common/decorators/consume-context-entry";
import type { LocalizeFunc } from "../common/translations/localize";
import "./input/ha-input-multi";
@customElement("ha-aliases-editor")
class AliasesEditor extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;
@state()
@consumeLocalize()
private _localize!: LocalizeFunc;
@property({ type: Array }) public aliases!: string[];
@@ -25,9 +28,9 @@ class AliasesEditor extends LitElement {
.disabled=${this.disabled}
.sortable=${this.sortable}
update-on-blur
.label=${this.hass!.localize("ui.dialogs.aliases.label")}
.removeLabel=${this.hass!.localize("ui.dialogs.aliases.remove")}
.addLabel=${this.hass!.localize("ui.dialogs.aliases.add")}
.label=${this._localize("ui.dialogs.aliases.label")}
.removeLabel=${this._localize("ui.dialogs.aliases.remove")}
.addLabel=${this._localize("ui.dialogs.aliases.add")}
item-index
@value-changed=${this._aliasesChanged}
>
+12 -5
View File
@@ -1,12 +1,16 @@
import { consume } from "@lit/context";
import type { ContextType } from "@lit/context";
import type { HassEntity } from "home-assistant-js-websocket";
import { css, html, LitElement, nothing } from "lit";
import { customElement, property } from "lit/decorators";
import { customElement, property, state } from "lit/decorators";
import { until } from "lit/directives/until";
import type { HomeAssistant } from "../types";
import { formattersContext } from "../data/context";
@customElement("ha-attribute-value")
class HaAttributeValue extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;
@state()
@consume({ context: formattersContext, subscribe: true })
private _formatters?: ContextType<typeof formattersContext>;
@property({ attribute: false }) public stateObj?: HassEntity;
@@ -53,7 +57,7 @@ class HaAttributeValue extends LitElement {
}
if (this.hideUnit) {
const parts = this.hass.formatEntityAttributeValueToParts(
const parts = this._formatters!.formatEntityAttributeValueToParts(
this.stateObj!,
this.attribute
);
@@ -63,7 +67,10 @@ class HaAttributeValue extends LitElement {
.join("");
}
return this.hass.formatEntityAttributeValue(this.stateObj!, this.attribute);
return this._formatters!.formatEntityAttributeValue(
this.stateObj!,
this.attribute
);
}
static styles = css`
-1
View File
@@ -80,7 +80,6 @@ class HaAttributes extends LitElement {
</div>
<div class="value">
<ha-attribute-value
.hass=${this.hass}
.attribute=${attribute}
.stateObj=${this.stateObj}
></ha-attribute-value>
+34 -20
View File
@@ -1,14 +1,22 @@
import { consume } from "@lit/context";
import type { ContextType } from "@lit/context";
import type { TemplateResult } from "lit";
import { css, html, LitElement, nothing } from "lit";
import { customElement, property } from "lit/decorators";
import { customElement, property, state } from "lit/decorators";
import { consumeLocalize } from "../common/decorators/consume-context-entry";
import type { LocalizeFunc } from "../common/translations/localize";
import type { ClimateEntity } from "../data/climate";
import { CLIMATE_PRESET_NONE } from "../data/climate";
import { formattersContext } from "../data/context";
import { OFF, UNAVAILABLE, UNKNOWN } from "../data/entity/entity";
import type { HomeAssistant } from "../types";
@customElement("ha-climate-state")
class HaClimateState extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;
@state()
@consume({ context: formattersContext, subscribe: true })
private _formatters?: ContextType<typeof formattersContext>;
@state() @consumeLocalize() private _localize!: LocalizeFunc;
@property({ attribute: false }) public stateObj!: ClimateEntity;
@@ -24,7 +32,7 @@ class HaClimateState extends LitElement {
${this.stateObj.attributes.preset_mode &&
this.stateObj.attributes.preset_mode !== CLIMATE_PRESET_NONE
? html`-
${this.hass.formatEntityAttributeValue(
${this._formatters!.formatEntityAttributeValue(
this.stateObj,
"preset_mode"
)}`
@@ -37,7 +45,7 @@ class HaClimateState extends LitElement {
${currentStatus && !noValue
? html`
<div class="current">
${this.hass.localize("ui.card.climate.currently")}:
${this._localize("ui.card.climate.currently")}:
<div class="unit">${currentStatus}</div>
</div>
`
@@ -45,32 +53,32 @@ class HaClimateState extends LitElement {
}
private _computeCurrentStatus(): string | undefined {
if (!this.hass || !this.stateObj) {
if (!this._formatters || !this.stateObj) {
return undefined;
}
if (
this.stateObj.attributes.current_temperature != null &&
this.stateObj.attributes.current_humidity != null
) {
return `${this.hass.formatEntityAttributeValue(
return `${this._formatters.formatEntityAttributeValue(
this.stateObj,
"current_temperature"
)}/
${this.hass.formatEntityAttributeValue(
${this._formatters.formatEntityAttributeValue(
this.stateObj,
"current_humidity"
)}`;
}
if (this.stateObj.attributes.current_temperature != null) {
return this.hass.formatEntityAttributeValue(
return this._formatters.formatEntityAttributeValue(
this.stateObj,
"current_temperature"
);
}
if (this.stateObj.attributes.current_humidity != null) {
return this.hass.formatEntityAttributeValue(
return this._formatters.formatEntityAttributeValue(
this.stateObj,
"current_humidity"
);
@@ -80,7 +88,7 @@ class HaClimateState extends LitElement {
}
private _computeTarget(): string {
if (!this.hass || !this.stateObj) {
if (!this._formatters || !this.stateObj) {
return "";
}
@@ -88,33 +96,39 @@ class HaClimateState extends LitElement {
this.stateObj.attributes.target_temp_low != null &&
this.stateObj.attributes.target_temp_high != null
) {
return `${this.hass.formatEntityAttributeValue(
return `${this._formatters.formatEntityAttributeValue(
this.stateObj,
"target_temp_low"
)}-${this.hass.formatEntityAttributeValue(
)}-${this._formatters.formatEntityAttributeValue(
this.stateObj,
"target_temp_high"
)}`;
}
if (this.stateObj.attributes.temperature != null) {
return this.hass.formatEntityAttributeValue(this.stateObj, "temperature");
return this._formatters.formatEntityAttributeValue(
this.stateObj,
"temperature"
);
}
if (
this.stateObj.attributes.target_humidity_low != null &&
this.stateObj.attributes.target_humidity_high != null
) {
return `${this.hass.formatEntityAttributeValue(
return `${this._formatters.formatEntityAttributeValue(
this.stateObj,
"target_humidity_low"
)}-${this.hass.formatEntityAttributeValue(
)}-${this._formatters.formatEntityAttributeValue(
this.stateObj,
"target_humidity_high"
)}`;
}
if (this.stateObj.attributes.humidity != null) {
return this.hass.formatEntityAttributeValue(this.stateObj, "humidity");
return this._formatters.formatEntityAttributeValue(
this.stateObj,
"humidity"
);
}
return "";
@@ -125,13 +139,13 @@ class HaClimateState extends LitElement {
this.stateObj.state === UNAVAILABLE ||
this.stateObj.state === UNKNOWN
) {
return this.hass.localize(`state.default.${this.stateObj.state}`);
return this._localize(`state.default.${this.stateObj.state}`);
}
const stateString = this.hass.formatEntityState(this.stateObj);
const stateString = this._formatters!.formatEntityState(this.stateObj);
if (this.stateObj.attributes.hvac_action && this.stateObj.state !== OFF) {
const actionString = this.hass.formatEntityAttributeValue(
const actionString = this._formatters!.formatEntityAttributeValue(
this.stateObj,
"hvac_action"
);
+15 -9
View File
@@ -1,17 +1,23 @@
import { consume, type ContextType } from "@lit/context";
import { mdiStop } from "@mdi/js";
import { css, html, LitElement, nothing } from "lit";
import { customElement, property } from "lit/decorators";
import { customElement, property, state } from "lit/decorators";
import { classMap } from "lit/directives/class-map";
import { consumeLocalize } from "../common/decorators/consume-context-entry";
import { computeCloseIcon, computeOpenIcon } from "../common/entity/cover_icon";
import { supportsFeature } from "../common/entity/supports-feature";
import type { LocalizeFunc } from "../common/translations/localize";
import { apiContext } from "../data/context";
import type { CoverEntity } from "../data/cover";
import { canClose, canOpen, canStop, CoverEntityFeature } from "../data/cover";
import type { HomeAssistant } from "../types";
import "./ha-icon-button";
@customElement("ha-cover-controls")
class HaCoverControls extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;
@state() @consumeLocalize() private _localize!: LocalizeFunc;
@consume({ context: apiContext, subscribe: true })
private _api!: ContextType<typeof apiContext>;
@property({ attribute: false }) public stateObj!: CoverEntity;
@@ -26,7 +32,7 @@ class HaCoverControls extends LitElement {
class=${classMap({
hidden: !supportsFeature(this.stateObj, CoverEntityFeature.OPEN),
})}
.label=${this.hass.localize("ui.card.cover.open_cover")}
.label=${this._localize("ui.card.cover.open_cover")}
@click=${this._onOpenTap}
.disabled=${!canOpen(this.stateObj)}
.path=${computeOpenIcon(this.stateObj)}
@@ -36,7 +42,7 @@ class HaCoverControls extends LitElement {
class=${classMap({
hidden: !supportsFeature(this.stateObj, CoverEntityFeature.STOP),
})}
.label=${this.hass.localize("ui.card.cover.stop_cover")}
.label=${this._localize("ui.card.cover.stop_cover")}
.path=${mdiStop}
@click=${this._onStopTap}
.disabled=${!canStop(this.stateObj)}
@@ -45,7 +51,7 @@ class HaCoverControls extends LitElement {
class=${classMap({
hidden: !supportsFeature(this.stateObj, CoverEntityFeature.CLOSE),
})}
.label=${this.hass.localize("ui.card.cover.close_cover")}
.label=${this._localize("ui.card.cover.close_cover")}
@click=${this._onCloseTap}
.disabled=${!canClose(this.stateObj)}
.path=${computeCloseIcon(this.stateObj)}
@@ -57,21 +63,21 @@ class HaCoverControls extends LitElement {
private _onOpenTap(ev): void {
ev.stopPropagation();
this.hass.callService("cover", "open_cover", {
this._api.callService("cover", "open_cover", {
entity_id: this.stateObj.entity_id,
});
}
private _onCloseTap(ev): void {
ev.stopPropagation();
this.hass.callService("cover", "close_cover", {
this._api.callService("cover", "close_cover", {
entity_id: this.stateObj.entity_id,
});
}
private _onStopTap(ev): void {
ev.stopPropagation();
this.hass.callService("cover", "stop_cover", {
this._api.callService("cover", "stop_cover", {
entity_id: this.stateObj.entity_id,
});
}
+15 -9
View File
@@ -1,8 +1,12 @@
import { consume, type ContextType } from "@lit/context";
import { mdiArrowBottomLeft, mdiArrowTopRight, mdiStop } from "@mdi/js";
import { css, html, LitElement, nothing } from "lit";
import { customElement, property } from "lit/decorators";
import { customElement, property, state } from "lit/decorators";
import { classMap } from "lit/directives/class-map";
import { consumeLocalize } from "../common/decorators/consume-context-entry";
import { supportsFeature } from "../common/entity/supports-feature";
import type { LocalizeFunc } from "../common/translations/localize";
import { apiContext } from "../data/context";
import type { CoverEntity } from "../data/cover";
import {
canCloseTilt,
@@ -10,12 +14,14 @@ import {
canStopTilt,
CoverEntityFeature,
} from "../data/cover";
import type { HomeAssistant } from "../types";
import "./ha-icon-button";
@customElement("ha-cover-tilt-controls")
class HaCoverTiltControls extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;
@state() @consumeLocalize() private _localize!: LocalizeFunc;
@consume({ context: apiContext, subscribe: true })
private _api!: ContextType<typeof apiContext>;
@property({ attribute: false }) stateObj!: CoverEntity;
@@ -31,7 +37,7 @@ class HaCoverTiltControls extends LitElement {
CoverEntityFeature.OPEN_TILT
),
})}
.label=${this.hass.localize("ui.card.cover.open_tilt_cover")}
.label=${this._localize("ui.card.cover.open_tilt_cover")}
.path=${mdiArrowTopRight}
@click=${this._onOpenTiltTap}
.disabled=${!canOpenTilt(this.stateObj)}
@@ -43,7 +49,7 @@ class HaCoverTiltControls extends LitElement {
CoverEntityFeature.STOP_TILT
),
})}
.label=${this.hass.localize("ui.card.cover.stop_cover")}
.label=${this._localize("ui.card.cover.stop_cover")}
.path=${mdiStop}
@click=${this._onStopTiltTap}
.disabled=${!canStopTilt(this.stateObj)}
@@ -55,7 +61,7 @@ class HaCoverTiltControls extends LitElement {
CoverEntityFeature.CLOSE_TILT
),
})}
.label=${this.hass.localize("ui.card.cover.close_tilt_cover")}
.label=${this._localize("ui.card.cover.close_tilt_cover")}
.path=${mdiArrowBottomLeft}
@click=${this._onCloseTiltTap}
.disabled=${!canCloseTilt(this.stateObj)}
@@ -64,21 +70,21 @@ class HaCoverTiltControls extends LitElement {
private _onOpenTiltTap(ev): void {
ev.stopPropagation();
this.hass.callService("cover", "open_cover_tilt", {
this._api.callService("cover", "open_cover_tilt", {
entity_id: this.stateObj.entity_id,
});
}
private _onCloseTiltTap(ev): void {
ev.stopPropagation();
this.hass.callService("cover", "close_cover_tilt", {
this._api.callService("cover", "close_cover_tilt", {
entity_id: this.stateObj.entity_id,
});
}
private _onStopTiltTap(ev): void {
ev.stopPropagation();
this.hass.callService("cover", "stop_cover_tilt", {
this._api.callService("cover", "stop_cover_tilt", {
entity_id: this.stateObj.entity_id,
});
}
+10 -11
View File
@@ -8,13 +8,12 @@ import { conditionalClamp } from "../common/number/clamp";
import type { CardGridSize } from "../panels/lovelace/common/compute-card-grid-size";
import { DEFAULT_GRID_SIZE } from "../panels/lovelace/common/compute-card-grid-size";
import "../panels/lovelace/editor/card-editor/ha-grid-layout-slider";
import type { HomeAssistant } from "../types";
import "./ha-icon-button";
import { consumeLocalize } from "../common/decorators/consume-context-entry";
import type { LocalizeFunc } from "../common/translations/localize";
@customElement("ha-grid-size-picker")
export class HaGridSizeEditor extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;
@property({ attribute: false }) public value?: CardGridSize;
@property({ attribute: false }) public rows = 8;
@@ -35,6 +34,10 @@ export class HaGridSizeEditor extends LitElement {
@state() public _localValue?: CardGridSize = { rows: 1, columns: 1 };
@state()
@consumeLocalize()
private _localize!: LocalizeFunc;
protected willUpdate(changedProperties: PropertyValues<this>) {
if (changedProperties.has("value")) {
this._localValue = this.value;
@@ -62,9 +65,7 @@ export class HaGridSizeEditor extends LitElement {
return html`
<div class="grid">
<ha-grid-layout-slider
aria-label=${this.hass.localize(
"ui.components.grid-size-picker.columns"
)}
aria-label=${this._localize("ui.components.grid-size-picker.columns")}
id="columns"
.min=${columnMin}
.max=${columnMax}
@@ -78,9 +79,7 @@ export class HaGridSizeEditor extends LitElement {
></ha-grid-layout-slider>
<ha-grid-layout-slider
aria-label=${this.hass.localize(
"ui.components.grid-size-picker.rows"
)}
aria-label=${this._localize("ui.components.grid-size-picker.rows")}
id="rows"
.min=${rowMin}
.max=${rowMax}
@@ -98,10 +97,10 @@ export class HaGridSizeEditor extends LitElement {
@click=${this._reset}
class="reset"
.path=${mdiRestore}
label=${this.hass.localize(
label=${this._localize(
"ui.components.grid-size-picker.reset_default"
)}
title=${this.hass.localize(
title=${this._localize(
"ui.components.grid-size-picker.reset_default"
)}
>
+20 -12
View File
@@ -1,13 +1,21 @@
import { consume } from "@lit/context";
import type { ContextType } from "@lit/context";
import type { TemplateResult } from "lit";
import { css, html, LitElement } from "lit";
import { customElement, property } from "lit/decorators";
import { customElement, property, state } from "lit/decorators";
import { consumeLocalize } from "../common/decorators/consume-context-entry";
import type { LocalizeFunc } from "../common/translations/localize";
import { formattersContext } from "../data/context";
import { OFF, UNAVAILABLE, UNKNOWN } from "../data/entity/entity";
import type { HumidifierEntity } from "../data/humidifier";
import type { HomeAssistant } from "../types";
@customElement("ha-humidifier-state")
class HaHumidifierState extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;
@state()
@consume({ context: formattersContext, subscribe: true })
private _formatters?: ContextType<typeof formattersContext>;
@state() @consumeLocalize() private _localize!: LocalizeFunc;
@property({ attribute: false }) public stateObj!: HumidifierEntity;
@@ -22,7 +30,7 @@ class HaHumidifierState extends LitElement {
${this._localizeState()}
${this.stateObj.attributes.mode
? html`-
${this.hass.formatEntityAttributeValue(
${this._formatters!.formatEntityAttributeValue(
this.stateObj,
"mode"
)}`
@@ -34,19 +42,19 @@ class HaHumidifierState extends LitElement {
${currentStatus && !noValue
? html`<div class="current">
${this.hass.localize("ui.card.humidifier.currently")}:
${this._localize("ui.card.humidifier.currently")}:
<div class="unit">${currentStatus}</div>
</div>`
: ""}`;
}
private _computeCurrentStatus(): string | undefined {
if (!this.hass || !this.stateObj) {
if (!this._formatters || !this.stateObj) {
return undefined;
}
if (this.stateObj.attributes.current_humidity != null) {
return `${this.hass.formatEntityAttributeValue(
return `${this._formatters.formatEntityAttributeValue(
this.stateObj,
"current_humidity"
)}`;
@@ -56,12 +64,12 @@ class HaHumidifierState extends LitElement {
}
private _computeTarget(): string {
if (!this.hass || !this.stateObj) {
if (!this._formatters || !this.stateObj) {
return "";
}
if (this.stateObj.attributes.humidity != null) {
return `${this.hass.formatEntityAttributeValue(
return `${this._formatters.formatEntityAttributeValue(
this.stateObj,
"humidity"
)}`;
@@ -75,13 +83,13 @@ class HaHumidifierState extends LitElement {
this.stateObj.state === UNAVAILABLE ||
this.stateObj.state === UNKNOWN
) {
return this.hass.localize(`state.default.${this.stateObj.state}`);
return this._localize(`state.default.${this.stateObj.state}`);
}
const stateString = this.hass.formatEntityState(this.stateObj);
const stateString = this._formatters!.formatEntityState(this.stateObj);
if (this.stateObj.attributes.action && this.stateObj.state !== OFF) {
const actionString = this.hass.formatEntityAttributeValue(
const actionString = this._formatters!.formatEntityAttributeValue(
this.stateObj,
"action"
);
+7 -4
View File
@@ -3,13 +3,12 @@ import type { TemplateResult } from "lit";
import { html, LitElement } from "lit";
import { customElement, property, state } from "lit/decorators";
import { mainWindow } from "../common/dom/get_main_window";
import type { HomeAssistant } from "../types";
import "./ha-icon-button";
import { consumeLocalize } from "../common/decorators/consume-context-entry";
import type { LocalizeFunc } from "../common/translations/localize";
@customElement("ha-icon-button-arrow-next")
export class HaIconButtonArrowNext extends LitElement {
@property({ attribute: false }) public hass?: HomeAssistant;
@property({ type: Boolean }) public disabled = false;
@property() public label?: string;
@@ -17,11 +16,15 @@ export class HaIconButtonArrowNext extends LitElement {
@state() private _icon =
mainWindow.document.dir === "rtl" ? mdiArrowLeft : mdiArrowRight;
@state()
@consumeLocalize()
private _localize!: LocalizeFunc;
protected render(): TemplateResult {
return html`
<ha-icon-button
.disabled=${this.disabled}
.label=${this.label || this.hass?.localize("ui.common.next") || "Next"}
.label=${this.label || this._localize("ui.common.next") || "Next"}
.path=${this._icon}
></ha-icon-button>
`;
+7 -4
View File
@@ -3,13 +3,12 @@ import type { TemplateResult } from "lit";
import { html, LitElement } from "lit";
import { customElement, property, state } from "lit/decorators";
import { mainWindow } from "../common/dom/get_main_window";
import type { HomeAssistant } from "../types";
import "./ha-icon-button";
import { consumeLocalize } from "../common/decorators/consume-context-entry";
import type { LocalizeFunc } from "../common/translations/localize";
@customElement("ha-icon-button-next")
export class HaIconButtonNext extends LitElement {
@property({ attribute: false }) public hass?: HomeAssistant;
@property({ type: Boolean }) public disabled = false;
@property() public label?: string;
@@ -25,11 +24,15 @@ export class HaIconButtonNext extends LitElement {
@state() private _icon =
mainWindow.document.dir === "rtl" ? mdiChevronLeft : mdiChevronRight;
@state()
@consumeLocalize()
private _localize!: LocalizeFunc;
protected render(): TemplateResult {
return html`
<ha-icon-button
.disabled=${this.disabled}
.label=${this.label || this.hass?.localize("ui.common.next") || "Next"}
.label=${this.label || this._localize("ui.common.next") || "Next"}
.path=${this._icon}
.href=${this.href}
.target=${this.target}
+7 -4
View File
@@ -3,13 +3,12 @@ import type { TemplateResult } from "lit";
import { html, LitElement } from "lit";
import { customElement, property, state } from "lit/decorators";
import { mainWindow } from "../common/dom/get_main_window";
import type { HomeAssistant } from "../types";
import "./ha-icon-button";
import { consumeLocalize } from "../common/decorators/consume-context-entry";
import type { LocalizeFunc } from "../common/translations/localize";
@customElement("ha-icon-button-prev")
export class HaIconButtonPrev extends LitElement {
@property({ attribute: false }) public hass?: HomeAssistant;
@property({ type: Boolean }) public disabled = false;
@property() public label?: string;
@@ -25,11 +24,15 @@ export class HaIconButtonPrev extends LitElement {
@state() private _icon =
mainWindow.document.dir === "rtl" ? mdiChevronRight : mdiChevronLeft;
@state()
@consumeLocalize()
private _localize!: LocalizeFunc;
protected render(): TemplateResult {
return html`
<ha-icon-button
.disabled=${this.disabled}
.label=${this.label || this.hass?.localize("ui.common.back") || "Back"}
.label=${this.label || this._localize("ui.common.back") || "Back"}
.path=${this._icon}
.href=${this.href}
.target=${this.target}
+11 -10
View File
@@ -10,8 +10,9 @@ import type {
NetworkConfig,
} from "../data/network";
import { haStyle } from "../resources/styles";
import type { HomeAssistant } from "../types";
import "./ha-checkbox";
import { consumeLocalize } from "../common/decorators/consume-context-entry";
import type { LocalizeFunc } from "../common/translations/localize";
import type { HaCheckbox } from "./ha-checkbox";
import "./ha-settings-row";
import "./ha-svg-icon";
@@ -41,12 +42,14 @@ declare global {
}
@customElement("ha-network")
export class HaNetwork extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;
@property({ attribute: false }) public networkConfig?: NetworkConfig;
@state() private _expanded?: boolean;
@state()
@consumeLocalize()
private _localize!: LocalizeFunc;
protected render() {
if (this.networkConfig === undefined) {
return nothing;
@@ -57,14 +60,14 @@ export class HaNetwork extends LitElement {
@change=${this._handleAutoConfigureCheckboxClick}
.checked=${!configured_adapters.length}
.hint=${!configured_adapters.length
? this.hass.localize(
? this._localize(
"ui.panel.config.network.adapter.auto_configure_manual_hint"
)
: ""}
>
${this.hass.localize("ui.panel.config.network.adapter.auto_configure")}
${this._localize("ui.panel.config.network.adapter.auto_configure")}
<div class="description">
${this.hass.localize("ui.panel.config.network.adapter.detected")}:
${this._localize("ui.panel.config.network.adapter.detected")}:
${format_auto_detected_interfaces(this.networkConfig.adapters)}
</div>
</ha-checkbox>
@@ -77,13 +80,11 @@ export class HaNetwork extends LitElement {
.checked=${configured_adapters.includes(adapter.name)}
.adapter=${adapter.name}
>
${this.hass.localize(
"ui.panel.config.network.adapter.adapter"
)}:
${this._localize("ui.panel.config.network.adapter.adapter")}:
${adapter.name}
${adapter.default
? html`<ha-svg-icon .path=${mdiStar}></ha-svg-icon>
(${this.hass.localize("ui.common.default")})`
(${this._localize("ui.common.default")})`
: nothing}
<div class="description">
${format_addresses([...adapter.ipv4, ...adapter.ipv6])}
+93 -4
View File
@@ -1,6 +1,6 @@
import type { CSSResultGroup, PropertyValues } from "lit";
import { LitElement, css, html, nothing } from "lit";
import { customElement, property, query } from "lit/decorators";
import { customElement, property, query, state } from "lit/decorators";
import { classMap } from "lit/directives/class-map";
const PASSIVE_EVENT_OPTIONS = { passive: true } as const;
@@ -8,6 +8,10 @@ const PASSIVE_EVENT_OPTIONS = { passive: true } as const;
export const haTopAppBarFixedStyles = css`
:host {
display: block;
--total-top-app-bar-height: calc(
var(--header-height, 0px) + var(--sub-row-height, 0px)
);
--sub-row-height: 0px;
}
.top-app-bar {
@@ -37,14 +41,37 @@ export const haTopAppBarFixedStyles = css`
}
.row {
display: flex;
align-items: center;
box-sizing: border-box;
display: flex;
width: 100%;
align-items: center;
height: var(--header-height);
border-bottom: var(--app-header-border-bottom);
}
.top-app-bar.has-sub-row .row {
border-bottom: 0;
}
.sub-row {
box-sizing: border-box;
display: block;
width: 100%;
overflow: hidden;
border-bottom: var(--app-header-border-bottom);
}
.sub-row slot,
.sub-row ::slotted(*) {
box-sizing: border-box;
display: block;
width: 100%;
}
.sub-row[hidden] {
display: none;
}
.section {
display: flex;
align-items: center;
@@ -86,8 +113,14 @@ export const haTopAppBarFixedStyles = css`
}
.top-app-bar-fixed-adjust {
height: calc(
100vh - var(--total-top-app-bar-height, 0px) - var(
--safe-area-inset-top,
0px
) - var(--safe-area-inset-bottom, 0px)
);
padding-top: calc(
var(--header-height, 0px) + var(--safe-area-inset-top, 0px)
var(--total-top-app-bar-height, 0px) + var(--safe-area-inset-top, 0px)
);
padding-bottom: var(--safe-area-inset-bottom);
padding-right: var(--safe-area-inset-right);
@@ -106,8 +139,14 @@ export class HaTopAppBarFixed extends LitElement {
@query(".top-app-bar") protected _barElement!: HTMLElement;
@query(".sub-row") protected _subRowElement?: HTMLElement;
@state() private _hasSubRow = false;
private _scrollTarget?: HTMLElement | Window;
private _subRowResizeObserver?: ResizeObserver;
@property({ attribute: false })
public get scrollTarget(): HTMLElement | Window {
return this._scrollTarget || window;
@@ -137,6 +176,8 @@ export class HaTopAppBarFixed extends LitElement {
super.connectedCallback();
if (this.hasUpdated) {
this._observeSubRowHeight();
this._updateSubRowHeight();
this._updateBarPosition();
this._registerListeners();
this._syncScrollState();
@@ -153,6 +194,7 @@ export class HaTopAppBarFixed extends LitElement {
<header
class="top-app-bar ${classMap({
"pane-header": paneHeader,
"has-sub-row": this._hasSubRow,
})}"
>
<div class="row">
@@ -176,6 +218,9 @@ export class HaTopAppBarFixed extends LitElement {
<slot name="actionItems"></slot>
</section>
</div>
<div class="sub-row" ?hidden=${!this._hasSubRow}>
<slot name="subRow" @slotchange=${this._subRowSlotChanged}></slot>
</div>
</header>
`;
}
@@ -188,13 +233,23 @@ export class HaTopAppBarFixed extends LitElement {
protected firstUpdated(changedProperties: PropertyValues<this>) {
super.firstUpdated(changedProperties);
this._observeSubRowHeight();
this._updateSubRowHeight();
this._updateBarPosition();
this._registerListeners();
this._syncScrollState();
}
protected override updated(changedProperties: PropertyValues) {
super.updated(changedProperties);
if (changedProperties.has("_hasSubRow")) {
this._updateSubRowHeight();
}
}
override disconnectedCallback() {
super.disconnectedCallback();
this._unobserveSubRowHeight();
this._unregisterListeners();
}
@@ -225,6 +280,40 @@ export class HaTopAppBarFixed extends LitElement {
this.scrollTarget.removeEventListener("scroll", this._syncScrollState);
}
private _observeSubRowHeight() {
if (
this._subRowResizeObserver ||
!this._subRowElement ||
typeof ResizeObserver === "undefined"
) {
return;
}
this._subRowResizeObserver = new ResizeObserver(this._updateSubRowHeight);
this._subRowResizeObserver.observe(this._subRowElement);
}
private _unobserveSubRowHeight() {
this._subRowResizeObserver?.disconnect();
this._subRowResizeObserver = undefined;
}
private _subRowSlotChanged = (ev: Event) => {
const slot = ev.currentTarget as HTMLSlotElement;
this._hasSubRow = slot
.assignedNodes({ flatten: true })
.some(
(node) =>
node.nodeType !== Node.TEXT_NODE || Boolean(node.textContent?.trim())
);
};
private _updateSubRowHeight = () => {
const subRowHeight = this._hasSubRow
? this._subRowElement?.offsetHeight || 0
: 0;
this.style.setProperty("--sub-row-height", `${subRowHeight}px`);
};
static override styles: CSSResultGroup = haTopAppBarFixedStyles;
}
@@ -108,9 +108,9 @@ export class HaTwoPaneTopAppBarFixed extends HaTopAppBarFixed {
css`
.shadow-container {
position: absolute;
top: calc(-1 * var(--header-height));
top: calc(-1 * var(--total-top-app-bar-height));
width: 100%;
height: var(--header-height);
height: var(--total-top-app-bar-height);
z-index: 1;
transition: box-shadow 200ms linear;
}
@@ -131,7 +131,7 @@ export class HaTwoPaneTopAppBarFixed extends HaTopAppBarFixed {
.top-app-bar-fixed-adjust--pane {
display: flex;
height: calc(
100vh - var(--header-height, 0px) - var(
100vh - var(--total-top-app-bar-height, 0px) - var(
--safe-area-inset-top,
0px
) - var(--safe-area-inset-bottom, 0px)
+15 -9
View File
@@ -1,16 +1,22 @@
import { consume, type ContextType } from "@lit/context";
import { mdiStop, mdiValveClosed, mdiValveOpen } from "@mdi/js";
import { LitElement, html, css, nothing } from "lit";
import { customElement, property } from "lit/decorators";
import { customElement, property, state } from "lit/decorators";
import { classMap } from "lit/directives/class-map";
import { consumeLocalize } from "../common/decorators/consume-context-entry";
import { supportsFeature } from "../common/entity/supports-feature";
import type { LocalizeFunc } from "../common/translations/localize";
import { apiContext } from "../data/context";
import type { ValveEntity } from "../data/valve";
import { ValveEntityFeature, canClose, canOpen, canStop } from "../data/valve";
import type { HomeAssistant } from "../types";
import "./ha-icon-button";
@customElement("ha-valve-controls")
class HaValveControls extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;
@state() @consumeLocalize() private _localize!: LocalizeFunc;
@consume({ context: apiContext, subscribe: true })
private _api!: ContextType<typeof apiContext>;
@property({ attribute: false }) public stateObj!: ValveEntity;
@@ -25,7 +31,7 @@ class HaValveControls extends LitElement {
class=${classMap({
hidden: !supportsFeature(this.stateObj, ValveEntityFeature.OPEN),
})}
.label=${this.hass.localize("ui.card.valve.open_valve")}
.label=${this._localize("ui.card.valve.open_valve")}
@click=${this._onOpenTap}
.disabled=${!canOpen(this.stateObj)}
.path=${mdiValveOpen}
@@ -35,7 +41,7 @@ class HaValveControls extends LitElement {
class=${classMap({
hidden: !supportsFeature(this.stateObj, ValveEntityFeature.STOP),
})}
.label=${this.hass.localize("ui.card.valve.stop_valve")}
.label=${this._localize("ui.card.valve.stop_valve")}
@click=${this._onStopTap}
.disabled=${!canStop(this.stateObj)}
.path=${mdiStop}
@@ -44,7 +50,7 @@ class HaValveControls extends LitElement {
class=${classMap({
hidden: !supportsFeature(this.stateObj, ValveEntityFeature.CLOSE),
})}
.label=${this.hass.localize("ui.card.valve.close_valve")}
.label=${this._localize("ui.card.valve.close_valve")}
@click=${this._onCloseTap}
.disabled=${!canClose(this.stateObj)}
.path=${mdiValveClosed}
@@ -56,21 +62,21 @@ class HaValveControls extends LitElement {
private _onOpenTap(ev): void {
ev.stopPropagation();
this.hass.callService("valve", "open_valve", {
this._api.callService("valve", "open_valve", {
entity_id: this.stateObj.entity_id,
});
}
private _onCloseTap(ev): void {
ev.stopPropagation();
this.hass.callService("valve", "close_valve", {
this._api.callService("valve", "close_valve", {
entity_id: this.stateObj.entity_id,
});
}
private _onStopTap(ev): void {
ev.stopPropagation();
this.hass.callService("valve", "stop_valve", {
this._api.callService("valve", "stop_valve", {
entity_id: this.stateObj.entity_id,
});
}
@@ -123,8 +123,7 @@ class EntityPreviewRow extends LitElement {
const climateDomains = ["climate", "water_heater"];
if (climateDomains.includes(domain)) {
return html`
<ha-climate-state .hass=${this.hass} .stateObj=${stateObj}>
</ha-climate-state>
<ha-climate-state .stateObj=${stateObj}> </ha-climate-state>
`;
}
@@ -133,15 +132,11 @@ class EntityPreviewRow extends LitElement {
${isTiltOnly(stateObj)
? html`
<ha-cover-tilt-controls
.hass=${this.hass}
.stateObj=${stateObj}
></ha-cover-tilt-controls>
`
: html`
<ha-cover-controls
.hass=${this.hass}
.stateObj=${stateObj}
></ha-cover-controls>
<ha-cover-controls .stateObj=${stateObj}></ha-cover-controls>
`}
`;
}
@@ -216,8 +211,7 @@ class EntityPreviewRow extends LitElement {
if (domain === "humidifier") {
return html`
<ha-humidifier-state .hass=${this.hass} .stateObj=${stateObj}>
</ha-humidifier-state>
<ha-humidifier-state .stateObj=${stateObj}> </ha-humidifier-state>
`;
}
@@ -190,7 +190,6 @@ class HaMoreInfoDetails extends LitElement {
</div>
<div class="value">
<ha-attribute-value
.hass=${this.hass}
.attribute=${attribute}
.stateObj=${this._stateObj}
></ha-attribute-value>
@@ -132,7 +132,6 @@ export class HuiNotificationDrawer extends KeyboardShortcutMixin(LitElement) {
</div>
<ha-icon-button-prev
slot="actionItems"
.hass=${this.hass}
@click=${this.closeDialog}
.label=${this.hass.localize("ui.notification_drawer.close")}
>
@@ -206,7 +206,6 @@ class DialogAreaDetail
)}
</p>
<ha-aliases-editor
.hass=${this.hass}
.aliases=${this._aliases}
@value-changed=${this._aliasesChanged}
></ha-aliases-editor>
@@ -229,7 +229,6 @@ class DialogFloorDetail extends LitElement {
)}
</p>
<ha-aliases-editor
.hass=${this.hass}
.aliases=${this._aliases}
@value-changed=${this._aliasesChanged}
></ha-aliases-editor>
@@ -217,19 +217,11 @@ export default class HaAutomationConditionRow extends LitElement {
.hass=${this.hass}
.condition=${this.condition.condition}
></ha-condition-icon>
${this.optionsInSidebar && this.condition.condition !== "trigger"
? html`<ha-automation-row-live-test
.state=${this._liveTestResult.state}
.label=${this.hass.localize(
`ui.panel.config.automation.editor.conditions.live_test_state.${this._liveTestResult.state}`
)}
></ha-automation-row-live-test>`
: nothing}
</div>
${this.optionsInSidebar &&
this.condition.condition !== "trigger" &&
this._liveTestResult.message
? html`<ha-tooltip for="condition-icon" slot="leading-icon"
? html`<ha-tooltip for="condition-live-test" slot="leading-icon"
>${this._liveTestResult.message}</ha-tooltip
>`
: nothing}
@@ -245,6 +237,15 @@ export default class HaAutomationConditionRow extends LitElement {
this.condition.condition !== "device"
)
: nothing}
${this.optionsInSidebar && this.condition.condition !== "trigger"
? html`<ha-automation-row-live-test
id="condition-live-test"
.state=${this._liveTestResult.state}
.label=${this.hass.localize(
`ui.panel.config.automation.editor.conditions.live_test_state.${this._liveTestResult.state}`
)}
></ha-automation-row-live-test>`
: nothing}
${this.condition.note?.trim()
? html`
<ha-svg-icon
@@ -2,20 +2,49 @@ import { mdiDotsVertical } from "@mdi/js";
import type { CSSResultGroup, TemplateResult, PropertyValues } from "lit";
import { css, html, LitElement } from "lit";
import { customElement, property } from "lit/decorators";
import { classMap } from "lit/directives/class-map";
import { navigate } from "../../../common/navigate";
import "../../../components/ha-dropdown";
import "../../../components/ha-dropdown-item";
import "../../../components/ha-icon-button";
import "../../../components/ha-icon-button-arrow-prev";
import "../../../components/ha-menu-button";
import "../../../components/ha-tab-group";
import "../../../components/ha-tab-group-tab";
import { haStyle } from "../../../resources/styles";
import "../../../components/ha-top-app-bar-fixed";
import type { HomeAssistant, Route } from "../../../types";
import "./developer-tools-router";
import type { HaDropdownSelectEvent } from "../../../components/ha-dropdown";
const DEVELOPER_TOOLS_TABS = [
{
panel: "yaml",
translationKey: "ui.panel.config.developer-tools.tabs.yaml.title",
},
{
panel: "state",
translationKey: "ui.panel.config.developer-tools.tabs.states.title",
},
{
panel: "action",
translationKey: "ui.panel.config.developer-tools.tabs.actions.title",
},
{
panel: "template",
translationKey: "ui.panel.config.developer-tools.tabs.templates.title",
},
{
panel: "event",
translationKey: "ui.panel.config.developer-tools.tabs.events.title",
},
{
panel: "statistics",
translationKey: "ui.panel.config.developer-tools.tabs.statistics.title",
},
{
panel: "assist",
translationKey: "ui.panel.config.developer-tools.tabs.assist.tab",
},
] as const;
@customElement("ha-panel-developer-tools")
class PanelDeveloperTools extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;
@@ -32,94 +61,47 @@ class PanelDeveloperTools extends LitElement {
protected render(): TemplateResult {
const page = this._page;
return html`
<div class="header ${classMap({ narrow: this.narrow })}">
<div class="toolbar">
<ha-icon-button-arrow-prev
slot="navigationIcon"
@click=${this._handleBack}
></ha-icon-button-arrow-prev>
<div class="main-title">
${this.hass.localize(
"ui.panel.config.dashboard.developer_tools.main"
)}
</div>
<ha-dropdown slot="actionItems" @wa-select=${this._handleMenuAction}>
<ha-icon-button
slot="trigger"
.label=${this.hass.localize("ui.common.menu")}
.path=${mdiDotsVertical}
></ha-icon-button>
<ha-dropdown-item value="debug">
${this.hass.localize(
"ui.panel.config.developer-tools.tabs.debug.title"
)}
</ha-dropdown-item>
</ha-dropdown>
<ha-top-app-bar-fixed .narrow=${this.narrow}>
<ha-icon-button-arrow-prev
slot="navigationIcon"
@click=${this._handleBack}
></ha-icon-button-arrow-prev>
<div slot="title">
${this.hass.localize(
"ui.panel.config.dashboard.developer_tools.main"
)}
</div>
<ha-tab-group @wa-tab-show=${this._handlePageSelected}>
<ha-tab-group-tab slot="nav" panel="yaml" .active=${page === "yaml"}>
<ha-dropdown slot="actionItems" @wa-select=${this._handleMenuAction}>
<ha-icon-button
slot="trigger"
.label=${this.hass.localize("ui.common.menu")}
.path=${mdiDotsVertical}
></ha-icon-button>
<ha-dropdown-item value="debug">
${this.hass.localize(
"ui.panel.config.developer-tools.tabs.yaml.title"
"ui.panel.config.developer-tools.tabs.debug.title"
)}
</ha-tab-group-tab>
<ha-tab-group-tab
slot="nav"
panel="state"
.active=${page === "state"}
>
${this.hass.localize(
"ui.panel.config.developer-tools.tabs.states.title"
)}
</ha-tab-group-tab>
<ha-tab-group-tab
slot="nav"
panel="action"
.active=${page === "action"}
>
${this.hass.localize(
"ui.panel.config.developer-tools.tabs.actions.title"
)}
</ha-tab-group-tab>
<ha-tab-group-tab
slot="nav"
panel="template"
.active=${page === "template"}
>
${this.hass.localize(
"ui.panel.config.developer-tools.tabs.templates.title"
)}
</ha-tab-group-tab>
<ha-tab-group-tab
slot="nav"
panel="event"
.active=${page === "event"}
>
${this.hass.localize(
"ui.panel.config.developer-tools.tabs.events.title"
)}
</ha-tab-group-tab>
<ha-tab-group-tab
slot="nav"
panel="statistics"
.active=${page === "statistics"}
>
${this.hass.localize(
"ui.panel.config.developer-tools.tabs.statistics.title"
)}
</ha-tab-group-tab>
<ha-tab-group-tab
slot="nav"
panel="assist"
.active=${page === "assist"}
>Assist</ha-tab-group-tab
>
</ha-dropdown-item>
</ha-dropdown>
<ha-tab-group @wa-tab-show=${this._handlePageSelected} slot="subRow">
${DEVELOPER_TOOLS_TABS.map(
(tab) => html`
<ha-tab-group-tab
slot="nav"
panel=${tab.panel}
.active=${page === tab.panel}
>
${this.hass.localize(tab.translationKey)}
</ha-tab-group-tab>
`
)}
</ha-tab-group>
</div>
<developer-tools-router
.route=${this.route}
.narrow=${this.narrow}
.hass=${this.hass}
></developer-tools-router>
<developer-tools-router
.route=${this.route}
.narrow=${this.narrow}
.hass=${this.hass}
></developer-tools-router>
</ha-top-app-bar-fixed>
`;
}
@@ -150,90 +132,17 @@ class PanelDeveloperTools extends LitElement {
navigate("/config");
}
static get styles(): CSSResultGroup {
return [
haStyle,
css`
:host {
color: var(--primary-text-color);
display: flex;
min-height: 100vh;
}
.header {
position: fixed;
top: 0;
z-index: 4;
background-color: var(--app-header-background-color);
width: calc(
var(--ha-top-app-bar-width, 100%) - var(
--safe-area-inset-right,
0px
)
);
padding-top: var(--safe-area-inset-top);
padding-right: var(--safe-area-inset-right);
color: var(--app-header-text-color, white);
-webkit-backdrop-filter: var(--app-header-backdrop-filter, none);
backdrop-filter: var(--app-header-backdrop-filter, none);
}
:host([narrow]) .header {
width: calc(
var(--ha-top-app-bar-width, 100%) - var(
--safe-area-inset-left,
0px
) - var(--safe-area-inset-right, 0px)
);
padding-left: var(--safe-area-inset-left);
}
.toolbar {
height: var(--header-height);
display: flex;
align-items: center;
font-size: var(--ha-font-size-xl);
padding: var(--ha-space-2) var(--ha-space-3);
font-weight: var(--ha-font-weight-normal);
box-sizing: border-box;
}
:host([narrow]) .toolbar {
padding: var(--ha-space-1);
}
.main-title {
margin-inline-start: var(--ha-space-6);
line-height: var(--ha-line-height-normal);
flex-grow: 1;
}
.narrow .main-title {
margin-inline-start: var(--ha-space-2);
}
developer-tools-router {
display: block;
padding-top: calc(
var(--header-height) + 52px + var(--safe-area-inset-top, 0px)
);
padding-bottom: var(--safe-area-inset-bottom);
padding-right: var(--safe-area-inset-right);
flex: 1 1 100%;
max-width: calc(100% - var(--safe-area-inset-right, 0px));
}
:host([narrow]) developer-tools-router {
padding-left: var(--safe-area-inset-left);
max-width: calc(
100% - var(--safe-area-inset-left, 0px) - var(
--safe-area-inset-right,
0px
)
);
}
ha-tab-group {
--ha-tab-active-text-color: var(--app-header-text-color, white);
--ha-tab-indicator-color: var(--app-header-text-color, white);
--ha-tab-track-color: transparent;
border-bottom: var(--app-header-border-bottom, none);
}
`,
];
}
static readonly styles: CSSResultGroup = css`
developer-tools-router {
display: block;
height: 100%;
}
ha-tab-group {
--ha-tab-active-text-color: var(--app-header-text-color, white);
--ha-tab-indicator-color: var(--app-header-text-color, white);
--ha-tab-track-color: transparent;
}
`;
}
declare global {
@@ -602,11 +602,6 @@ class HaPanelDevState extends LitElement {
return [
haStyle,
css`
:host {
display: block;
height: 100%;
}
:host {
-ms-user-select: initial;
-webkit-user-select: initial;
@@ -46,7 +46,6 @@ class ConfigNetwork extends LitElement {
</p>
<ha-network
@network-config-changed=${this._configChanged}
.hass=${this.hass}
.networkConfig=${this._networkConfig}
></ha-network>
</div>
@@ -304,7 +304,6 @@ export class EntityVoiceSettings extends SubscribeMixin(LitElement) {
></ha-switch>
</ha-md-list-item>
<ha-aliases-editor
.hass=${this.hass}
.aliases=${(this._aliases ?? this.entry.aliases).filter(
(a): a is string => a !== null
)}
@@ -181,7 +181,6 @@ export class HuiEntityCard extends LitElement implements LovelaceCard {
? stateObj.attributes[this._config.attribute!] !== undefined
? html`<ha-attribute-value
hide-unit
.hass=${this.hass}
.stateObj=${stateObj}
.attribute=${this._config.attribute!}
>
@@ -133,7 +133,6 @@ export class HuiCardLayoutEditor extends LitElement {
"max-width": `${(this.sectionConfig.column_span ?? 1) * 250 + 40}px`,
})}
.columns=${gridTotalColumns}
.hass=${this.hass}
.value=${gridValue}
.isDefault=${this._isDefault(configOptions)}
@value-changed=${this._gridSizeChanged}
@@ -239,17 +239,9 @@ export class HaCardConditionEditor extends LitElement {
<ha-svg-icon
.path=${ICON_CONDITION[condition.condition]}
></ha-svg-icon>
${hideLiveTest
? nothing
: html`<ha-automation-row-live-test
.state=${this._liveTestResult.state}
.label=${this.hass.localize(
`ui.panel.lovelace.editor.condition-editor.live_test_state.${this._liveTestResult.state}`
)}
></ha-automation-row-live-test>`}
</div>
${!hideLiveTest && this._liveTestResult.message
? html`<ha-tooltip for="condition-icon" slot="leading-icon"
? html`<ha-tooltip for="condition-live-test" slot="leading-icon"
>${this._liveTestResult.message}</ha-tooltip
>`
: nothing}
@@ -257,6 +249,15 @@ export class HaCardConditionEditor extends LitElement {
${this.hass.localize(
`ui.panel.lovelace.editor.condition-editor.condition.${condition.condition}.label`
) || condition.condition}
${!hideLiveTest
? html`<ha-automation-row-live-test
id="condition-live-test"
.state=${this._liveTestResult.state}
.label=${this.hass.localize(
`ui.panel.lovelace.editor.condition-editor.live_test_state.${this._liveTestResult.state}`
)}
></ha-automation-row-live-test>`
: nothing}
</h3>
<ha-automation-row-event-chip
.show=${this._testingResult !== undefined}
@@ -43,8 +43,7 @@ class HuiClimateEntityRow extends LitElement implements LovelaceRow {
return html`
<hui-generic-entity-row .hass=${this.hass} .config=${this._config}>
<ha-climate-state .hass=${this.hass} .stateObj=${stateObj}>
</ha-climate-state>
<ha-climate-state .stateObj=${stateObj}> </ha-climate-state>
</hui-generic-entity-row>
`;
}
@@ -48,15 +48,11 @@ class HuiCoverEntityRow extends LitElement implements LovelaceRow {
${isTiltOnly(stateObj)
? html`
<ha-cover-tilt-controls
.hass=${this.hass}
.stateObj=${stateObj}
></ha-cover-tilt-controls>
`
: html`
<ha-cover-controls
.hass=${this.hass}
.stateObj=${stateObj}
></ha-cover-controls>
<ha-cover-controls .stateObj=${stateObj}></ha-cover-controls>
`}
</hui-generic-entity-row>
`;
@@ -45,8 +45,7 @@ class HuiHumidifierEntityRow extends LitElement implements LovelaceRow {
return html`
<hui-generic-entity-row .hass=${this.hass} .config=${this._config}>
<ha-humidifier-state .hass=${this.hass} .stateObj=${stateObj}>
</ha-humidifier-state>
<ha-humidifier-state .stateObj=${stateObj}> </ha-humidifier-state>
</hui-generic-entity-row>
`;
}
+3
View File
@@ -136,7 +136,10 @@ class LovelaceFullConfigEditor extends LitElement {
}
ha-yaml-editor {
display: flex;
flex-direction: column;
height: 100%;
min-height: 0;
}
.save-button {
-1
View File
@@ -522,7 +522,6 @@ class HUIRoot extends LitElement {
@click=${this._editView}
></ha-icon-button>
<ha-icon-button-arrow-next
.hass=${this.hass}
.label=${this.hass!.localize(
"ui.panel.lovelace.editor.edit_view.move_right"
)}
@@ -69,7 +69,6 @@ class HuiAttributeRow extends LitElement implements LovelaceRow {
<ha-attribute-value
.hideUnit=${this._config.suffix !== undefined &&
this._config.suffix !== ""}
.hass=${this.hass}
.stateObj=${stateObj}
.attribute=${this._config.attribute}
>
+3
View File
@@ -296,6 +296,9 @@ export const getMyRedirects = (): Redirects => ({
component: "history",
redirect: "/history",
},
maintenance: {
redirect: "/maintenance",
},
overview: {
redirect: "/home/overview",
},
+1 -4
View File
@@ -23,10 +23,7 @@ class StateCardClimate extends LitElement {
.stateObj=${this.stateObj}
.inDialog=${this.inDialog}
></state-info>
<ha-climate-state
.hass=${this.hass}
.stateObj=${this.stateObj}
></ha-climate-state>
<ha-climate-state .stateObj=${this.stateObj}></ha-climate-state>
</div>
`;
}
-2
View File
@@ -26,12 +26,10 @@ class StateCardCover extends LitElement {
.inDialog=${this.inDialog}
></state-info>
<ha-cover-controls
.hass=${this.hass}
.hidden=${isTiltOnly(this.stateObj)}
.stateObj=${this.stateObj}
></ha-cover-controls>
<ha-cover-tilt-controls
.hass=${this.hass}
.hidden=${!isTiltOnly(this.stateObj)}
.stateObj=${this.stateObj}
></ha-cover-tilt-controls>
+1 -4
View File
@@ -25,10 +25,7 @@ class StateCardHumidifier extends LitElement {
.inDialog=${this.inDialog}
>
</state-info>
<ha-humidifier-state
.hass=${this.hass}
.stateObj=${this.stateObj}
></ha-humidifier-state>
<ha-humidifier-state .stateObj=${this.stateObj}></ha-humidifier-state>
</div>
`;
}
+1
View File
@@ -3832,6 +3832,7 @@
"developer-tools": {
"tabs": {
"assist": {
"tab": "Assist",
"title": "Sentences parser",
"description": "Enter sentences and see how they will be parsed by Home Assistant. Each line will be processed as an individual sentence. Intents will not be executed on your instance.",
"parse_sentences": "Parse sentences",
+55 -55
View File
@@ -3591,51 +3591,51 @@ __metadata:
languageName: node
linkType: hard
"@rspack/binding-darwin-arm64@npm:2.0.5":
version: 2.0.5
resolution: "@rspack/binding-darwin-arm64@npm:2.0.5"
"@rspack/binding-darwin-arm64@npm:2.0.6":
version: 2.0.6
resolution: "@rspack/binding-darwin-arm64@npm:2.0.6"
conditions: os=darwin & cpu=arm64
languageName: node
linkType: hard
"@rspack/binding-darwin-x64@npm:2.0.5":
version: 2.0.5
resolution: "@rspack/binding-darwin-x64@npm:2.0.5"
"@rspack/binding-darwin-x64@npm:2.0.6":
version: 2.0.6
resolution: "@rspack/binding-darwin-x64@npm:2.0.6"
conditions: os=darwin & cpu=x64
languageName: node
linkType: hard
"@rspack/binding-linux-arm64-gnu@npm:2.0.5":
version: 2.0.5
resolution: "@rspack/binding-linux-arm64-gnu@npm:2.0.5"
"@rspack/binding-linux-arm64-gnu@npm:2.0.6":
version: 2.0.6
resolution: "@rspack/binding-linux-arm64-gnu@npm:2.0.6"
conditions: os=linux & cpu=arm64 & libc=glibc
languageName: node
linkType: hard
"@rspack/binding-linux-arm64-musl@npm:2.0.5":
version: 2.0.5
resolution: "@rspack/binding-linux-arm64-musl@npm:2.0.5"
"@rspack/binding-linux-arm64-musl@npm:2.0.6":
version: 2.0.6
resolution: "@rspack/binding-linux-arm64-musl@npm:2.0.6"
conditions: os=linux & cpu=arm64 & libc=musl
languageName: node
linkType: hard
"@rspack/binding-linux-x64-gnu@npm:2.0.5":
version: 2.0.5
resolution: "@rspack/binding-linux-x64-gnu@npm:2.0.5"
"@rspack/binding-linux-x64-gnu@npm:2.0.6":
version: 2.0.6
resolution: "@rspack/binding-linux-x64-gnu@npm:2.0.6"
conditions: os=linux & cpu=x64 & libc=glibc
languageName: node
linkType: hard
"@rspack/binding-linux-x64-musl@npm:2.0.5":
version: 2.0.5
resolution: "@rspack/binding-linux-x64-musl@npm:2.0.5"
"@rspack/binding-linux-x64-musl@npm:2.0.6":
version: 2.0.6
resolution: "@rspack/binding-linux-x64-musl@npm:2.0.6"
conditions: os=linux & cpu=x64 & libc=musl
languageName: node
linkType: hard
"@rspack/binding-wasm32-wasi@npm:2.0.5":
version: 2.0.5
resolution: "@rspack/binding-wasm32-wasi@npm:2.0.5"
"@rspack/binding-wasm32-wasi@npm:2.0.6":
version: 2.0.6
resolution: "@rspack/binding-wasm32-wasi@npm:2.0.6"
dependencies:
"@emnapi/core": "npm:1.10.0"
"@emnapi/runtime": "npm:1.10.0"
@@ -3644,41 +3644,41 @@ __metadata:
languageName: node
linkType: hard
"@rspack/binding-win32-arm64-msvc@npm:2.0.5":
version: 2.0.5
resolution: "@rspack/binding-win32-arm64-msvc@npm:2.0.5"
"@rspack/binding-win32-arm64-msvc@npm:2.0.6":
version: 2.0.6
resolution: "@rspack/binding-win32-arm64-msvc@npm:2.0.6"
conditions: os=win32 & cpu=arm64
languageName: node
linkType: hard
"@rspack/binding-win32-ia32-msvc@npm:2.0.5":
version: 2.0.5
resolution: "@rspack/binding-win32-ia32-msvc@npm:2.0.5"
"@rspack/binding-win32-ia32-msvc@npm:2.0.6":
version: 2.0.6
resolution: "@rspack/binding-win32-ia32-msvc@npm:2.0.6"
conditions: os=win32 & cpu=ia32
languageName: node
linkType: hard
"@rspack/binding-win32-x64-msvc@npm:2.0.5":
version: 2.0.5
resolution: "@rspack/binding-win32-x64-msvc@npm:2.0.5"
"@rspack/binding-win32-x64-msvc@npm:2.0.6":
version: 2.0.6
resolution: "@rspack/binding-win32-x64-msvc@npm:2.0.6"
conditions: os=win32 & cpu=x64
languageName: node
linkType: hard
"@rspack/binding@npm:2.0.5":
version: 2.0.5
resolution: "@rspack/binding@npm:2.0.5"
"@rspack/binding@npm:2.0.6":
version: 2.0.6
resolution: "@rspack/binding@npm:2.0.6"
dependencies:
"@rspack/binding-darwin-arm64": "npm:2.0.5"
"@rspack/binding-darwin-x64": "npm:2.0.5"
"@rspack/binding-linux-arm64-gnu": "npm:2.0.5"
"@rspack/binding-linux-arm64-musl": "npm:2.0.5"
"@rspack/binding-linux-x64-gnu": "npm:2.0.5"
"@rspack/binding-linux-x64-musl": "npm:2.0.5"
"@rspack/binding-wasm32-wasi": "npm:2.0.5"
"@rspack/binding-win32-arm64-msvc": "npm:2.0.5"
"@rspack/binding-win32-ia32-msvc": "npm:2.0.5"
"@rspack/binding-win32-x64-msvc": "npm:2.0.5"
"@rspack/binding-darwin-arm64": "npm:2.0.6"
"@rspack/binding-darwin-x64": "npm:2.0.6"
"@rspack/binding-linux-arm64-gnu": "npm:2.0.6"
"@rspack/binding-linux-arm64-musl": "npm:2.0.6"
"@rspack/binding-linux-x64-gnu": "npm:2.0.6"
"@rspack/binding-linux-x64-musl": "npm:2.0.6"
"@rspack/binding-wasm32-wasi": "npm:2.0.6"
"@rspack/binding-win32-arm64-msvc": "npm:2.0.6"
"@rspack/binding-win32-ia32-msvc": "npm:2.0.6"
"@rspack/binding-win32-x64-msvc": "npm:2.0.6"
dependenciesMeta:
"@rspack/binding-darwin-arm64":
optional: true
@@ -3700,15 +3700,15 @@ __metadata:
optional: true
"@rspack/binding-win32-x64-msvc":
optional: true
checksum: 10/95b4fa9daf1935e2ca3d1bd08b12c7f26ef0fffb09d7764bcb11fed1d4ecedc4dd79cbfbb37519121e81c06b97ade27f2d7a33ed0ed51e3b7d650b37b8b33c9e
checksum: 10/c2e5245abab3257d02f5d98947fad26c8de1b18bb17362734035cfbdd725d9c6c78432372bdff985b32fa4062059d7210e9f5ea7314ae3080805b64f616fe348
languageName: node
linkType: hard
"@rspack/core@npm:2.0.5":
version: 2.0.5
resolution: "@rspack/core@npm:2.0.5"
"@rspack/core@npm:2.0.6":
version: 2.0.6
resolution: "@rspack/core@npm:2.0.6"
dependencies:
"@rspack/binding": "npm:2.0.5"
"@rspack/binding": "npm:2.0.6"
peerDependencies:
"@module-federation/runtime-tools": ^0.24.1 || ^2.0.0
"@swc/helpers": ^0.5.23
@@ -3717,7 +3717,7 @@ __metadata:
optional: true
"@swc/helpers":
optional: true
checksum: 10/b955a12bd04e1e3bef954f8a3cfd48c7302d114fee4d13dc7c6f46645bb18d377a4df7f86520bbf44519ed7bb315bd119e98768bfd7d851b1329ba9ee51ce1ac
checksum: 10/d2417690e8135342179bc9e5035e16fe827522b4c0babef029a21ff5903cd56c09b86f08924527bd7d3e66f178f1f678ce099199cac8c1a137b18c5d8892e613
languageName: node
linkType: hard
@@ -8483,7 +8483,7 @@ __metadata:
"@octokit/rest": "npm:22.0.1"
"@replit/codemirror-indentation-markers": "npm:6.5.3"
"@rsdoctor/rspack-plugin": "npm:1.5.12"
"@rspack/core": "npm:2.0.5"
"@rspack/core": "npm:2.0.6"
"@rspack/dev-server": "npm:2.0.3"
"@swc/helpers": "npm:0.5.23"
"@thomasloven/round-slider": "npm:0.6.0"
@@ -8548,7 +8548,7 @@ __metadata:
home-assistant-js-websocket: "npm:9.6.0"
html-minifier-terser: "npm:7.2.0"
husky: "npm:9.1.7"
idb-keyval: "npm:6.2.4"
idb-keyval: "npm:6.2.5"
intl-messageformat: "npm:11.2.7"
js-yaml: "npm:4.2.0"
jsdom: "npm:29.1.1"
@@ -8762,10 +8762,10 @@ __metadata:
languageName: node
linkType: hard
"idb-keyval@npm:6.2.4":
version: 6.2.4
resolution: "idb-keyval@npm:6.2.4"
checksum: 10/b1bc874eb582c6bed89dd40a07fe5ca593238b37cded9c604e0cb74b396d2b8caa850519af4467e5ca1b4628682a6102150299db69a393702d0a0718945bc5ec
"idb-keyval@npm:6.2.5":
version: 6.2.5
resolution: "idb-keyval@npm:6.2.5"
checksum: 10/ac645882b3258ff07347d085baab91b871bac7be4f46ff8e20a7c036c2df35d3f695a30050009f27237b99045203568f2a842a35295a48f9b815959ee51a347e
languageName: node
linkType: hard