Compare commits

..

1 Commits

Author SHA1 Message Date
Paul Bottein
d0188404a7 Don't use tooltip on dashboard toolbar 2025-11-03 09:33:03 +01:00
23 changed files with 547 additions and 801 deletions

View File

@@ -36,14 +36,14 @@ jobs:
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@0499de31b99561a6d14a36a5f662c2a54f91beee # v4.31.2
uses: github/codeql-action/init@4e94bd11f71e507f7f87df81788dff88d1dacbfb # v4.31.0
with:
languages: ${{ matrix.language }}
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@0499de31b99561a6d14a36a5f662c2a54f91beee # v4.31.2
uses: github/codeql-action/autobuild@4e94bd11f71e507f7f87df81788dff88d1dacbfb # v4.31.0
# Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl
@@ -57,4 +57,4 @@ jobs:
# make release
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@0499de31b99561a6d14a36a5f662c2a54f91beee # v4.31.2
uses: github/codeql-action/analyze@4e94bd11f71e507f7f87df81788dff88d1dacbfb # v4.31.0

View File

@@ -39,7 +39,6 @@ const SENSOR_DEVICE_CLASSES = [
"pm1",
"pm10",
"pm25",
"pm4",
"power_factor",
"power",
"precipitation",

View File

@@ -81,7 +81,7 @@
"@material/mwc-top-app-bar": "0.27.0",
"@material/mwc-top-app-bar-fixed": "0.27.0",
"@material/top-app-bar": "=14.0.0-canary.53b3cad2f.0",
"@material/web": "2.4.1",
"@material/web": "2.4.0",
"@mdi/js": "7.4.47",
"@mdi/svg": "7.4.47",
"@replit/codemirror-indentation-markers": "6.5.3",
@@ -89,8 +89,8 @@
"@thomasloven/round-slider": "0.6.0",
"@tsparticles/engine": "3.9.1",
"@tsparticles/preset-links": "3.2.0",
"@vaadin/combo-box": "24.9.4",
"@vaadin/vaadin-themable-mixin": "24.9.4",
"@vaadin/combo-box": "24.9.2",
"@vaadin/vaadin-themable-mixin": "24.9.2",
"@vibrant/color": "4.0.0",
"@vue/web-component-wrapper": "1.3.0",
"@webcomponents/scoped-custom-element-registry": "0.0.10",
@@ -154,11 +154,11 @@
"@babel/preset-env": "7.28.5",
"@bundle-stats/plugin-webpack-filter": "4.21.5",
"@lokalise/node-api": "15.3.1",
"@octokit/auth-oauth-device": "8.0.3",
"@octokit/plugin-retry": "8.0.3",
"@octokit/auth-oauth-device": "8.0.2",
"@octokit/plugin-retry": "8.0.2",
"@octokit/rest": "22.0.0",
"@rsdoctor/rspack-plugin": "1.3.7",
"@rspack/core": "1.6.0",
"@rsdoctor/rspack-plugin": "1.3.6",
"@rspack/core": "1.5.8",
"@rspack/dev-server": "1.1.4",
"@types/babel__plugin-transform-runtime": "7.9.5",
"@types/chromecast-caf-receiver": "6.0.22",
@@ -178,7 +178,7 @@
"@types/tar": "6.1.13",
"@types/ua-parser-js": "0.7.39",
"@types/webspeechapi": "0.0.29",
"@vitest/coverage-v8": "4.0.5",
"@vitest/coverage-v8": "4.0.4",
"babel-loader": "10.0.0",
"babel-plugin-template-html-minifier": "4.1.0",
"browserslist-useragent-regexp": "4.1.3",
@@ -201,7 +201,7 @@
"gulp-rename": "2.1.0",
"html-minifier-terser": "7.2.0",
"husky": "9.1.7",
"jsdom": "27.1.0",
"jsdom": "27.0.1",
"jszip": "3.10.1",
"lint-staged": "16.2.6",
"lit-analyzer": "2.0.3",
@@ -213,13 +213,13 @@
"rspack-manifest-plugin": "5.1.0",
"serve": "14.2.5",
"sinon": "21.0.0",
"tar": "7.5.2",
"tar": "7.5.1",
"terser-webpack-plugin": "5.3.14",
"ts-lit-plugin": "2.0.2",
"typescript": "5.9.3",
"typescript-eslint": "8.46.2",
"vite-tsconfig-paths": "5.1.4",
"vitest": "4.0.5",
"vitest": "4.0.4",
"webpack-stats-plugin": "1.1.3",
"webpackbar": "7.0.0",
"workbox-build": "patch:workbox-build@npm%3A7.1.1#~/.yarn/patches/workbox-build-npm-7.1.1-a854f3faae.patch"

View File

@@ -214,7 +214,6 @@ const FIXED_DOMAIN_ATTRIBUTE_STATES = {
"pm1",
"pm10",
"pm25",
"pm4",
"power_factor",
"power",
"pressure",

View File

@@ -357,11 +357,7 @@ export class HaAreaPicker extends LitElement {
protected render(): TemplateResult {
const placeholder =
this.placeholder ??
this.hass.localize("ui.components.area-picker.placeholder");
const notFoundLabel = this.hass.localize(
"ui.components.area-picker.no_match"
);
this.placeholder ?? this.hass.localize("ui.components.area-picker.area");
const valueRenderer = this._computeValueRenderer(this.hass.areas);
@@ -371,7 +367,9 @@ export class HaAreaPicker extends LitElement {
.autofocus=${this.autofocus}
.label=${this.label}
.helper=${this.helper}
.notFoundLabel=${notFoundLabel}
.notFoundLabel=${this.hass.localize(
"ui.components.area-picker.no_match"
)}
.placeholder=${placeholder}
.value=${this.value}
.getItems=${this._getItems}

View File

@@ -59,7 +59,6 @@ export class HaButton extends Button {
line-height: 1;
transition: background-color 0.15s ease-in-out;
text-wrap: wrap;
}
:host([size="small"]) .button {

View File

@@ -3,6 +3,7 @@ import type { RenderItemFunction } from "@lit-labs/virtualizer/virtualize";
import { mdiPlaylistPlus } from "@mdi/js";
import { css, html, LitElement, nothing, type CSSResultGroup } from "lit";
import { customElement, property, query, state } from "lit/decorators";
import { ifDefined } from "lit/directives/if-defined";
import { tinykeys } from "tinykeys";
import { fireEvent } from "../common/dom/fire_event";
import type { HomeAssistant } from "../types";
@@ -106,6 +107,9 @@ export class HaGenericPicker extends LitElement {
protected render() {
return html`
${this.label
? html`<label ?disabled=${this.disabled}>${this.label}</label>`
: nothing}
<div class="container">
<div id="picker">
<slot name="field">
@@ -126,7 +130,7 @@ export class HaGenericPicker extends LitElement {
type="button"
class=${this._opened ? "opened" : ""}
compact
.label=${this.label}
aria-label=${ifDefined(this.label)}
@click=${this.open}
@clear=${this._clear}
.placeholder=${this.placeholder}

View File

@@ -8,7 +8,6 @@ import {
type TemplateResult,
} from "lit";
import { customElement, property, query } from "lit/decorators";
import { ifDefined } from "lit/directives/if-defined";
import { fireEvent } from "../common/dom/fire_event";
import "./ha-combo-box-item";
import type { HaComboBoxItem } from "./ha-combo-box-item";
@@ -34,8 +33,6 @@ export class HaPickerField extends LitElement {
@property() public placeholder?: string;
@property() public label?: string;
@property({ attribute: "hide-clear-icon", type: Boolean })
public hideClearIcon = false;
@@ -54,35 +51,15 @@ export class HaPickerField extends LitElement {
!!this.value && !this.required && !this.disabled && !this.hideClearIcon;
return html`
<ha-combo-box-item
.disabled=${this.disabled}
type="button"
compact
aria-label=${ifDefined(this.label)}
>
<ha-combo-box-item .disabled=${this.disabled} type="button" compact>
${this.value
? this.valueRenderer
? this.valueRenderer(this.value)
: html`<span slot="headline">${this.value}</span>`
: html`<slot name="headline">${this.value}</slot>`
: html`
${this.label
? html`
<span
slot="headline"
class="label ${this.placeholder
? "with-placeholder"
: ""}"
>${this.label}</span
>
`
: nothing}
${this.placeholder
? html`
<span slot="headline" class="placeholder">
${this.placeholder}
</span>
`
: nothing}
<span slot="headline" class="placeholder">
${this.placeholder}
</span>
`}
${showClearIcon
? html`
@@ -175,24 +152,9 @@ export class HaPickerField extends LitElement {
width: 32px;
}
.label {
padding: 0 8px;
color: var(--secondary-text-color);
line-height: var(--ha-line-height-normal);
font-size: var(--ha-font-size-m);
white-space: nowrap;
}
.label.with-placeholder {
line-height: var(--ha-line-height-condensed);
font-size: var(--ha-font-size-xs);
}
.placeholder {
color: var(--secondary-text-color);
padding: 0 8px;
line-height: var(--ha-line-height-normal);
font-size: var(--ha-font-size-m);
}
`,
];

View File

@@ -152,18 +152,10 @@ export class MoreInfoHistory extends LitElement {
}
}
private _setUpdateTimer() {
private _setRedrawTimer() {
// redraw the graph every minute to update the time axis
clearInterval(this._interval);
this._interval = window.setInterval(() => {
// If using statistics, refresh the data
if (this._statistics) {
this._fetchStatistics();
}
// If using history, redraw the graph to update the time axis
if (this._stateHistory) {
this._redrawGraph();
}
}, 1000 * 60);
this._interval = window.setInterval(() => this._redrawGraph(), 1000 * 60);
}
private async _getStatisticsMetaData(statisticIds: string[] | undefined) {
@@ -178,30 +170,6 @@ export class MoreInfoHistory extends LitElement {
return statisticsMetaData;
}
private async _fetchStatistics(): Promise<boolean> {
// Fire off the metadata and fetch at the same time
// to avoid waiting in sequence so the UI responds
// faster.
const _metadata = this._getStatisticsMetaData([this.entityId]);
const _statistics = fetchStatistics(
this.hass!,
subHours(new Date(), 24),
undefined,
[this.entityId],
"5minute",
undefined,
statTypes
);
const [metadata, statistics] = await Promise.all([_metadata, _statistics]);
if (metadata && Object.keys(metadata).length) {
this._metadata = metadata;
this._statistics = statistics;
this._statNames = { [this.entityId]: "" };
return true;
}
return false;
}
private async _getStateHistory(): Promise<void> {
if (
isComponentLoaded(this.hass, "recorder") &&
@@ -212,10 +180,27 @@ export class MoreInfoHistory extends LitElement {
// has not opted into statistics so there is no need to check as it
// requires another round-trip to the server.
if (stateObj && stateObj.attributes.state_class) {
const hasStatistics = await this._fetchStatistics();
if (hasStatistics) {
// Using statistics, set up refresh timer
this._setUpdateTimer();
// Fire off the metadata and fetch at the same time
// to avoid waiting in sequence so the UI responds
// faster.
const _metadata = this._getStatisticsMetaData([this.entityId]);
const _statistics = fetchStatistics(
this.hass!,
subHours(new Date(), 24),
undefined,
[this.entityId],
"5minute",
undefined,
statTypes
);
const [metadata, statistics] = await Promise.all([
_metadata,
_statistics,
]);
if (metadata && Object.keys(metadata).length) {
this._metadata = metadata;
this._statistics = statistics;
this._statNames = { [this.entityId]: "" };
return;
}
}
@@ -253,7 +238,7 @@ export class MoreInfoHistory extends LitElement {
this._error = err;
return undefined;
});
this._setUpdateTimer();
this._setRedrawTimer();
}
static styles = [

View File

@@ -97,9 +97,6 @@ export const ENTITY_COMPONENT_ICONS: Record<string, ComponentIcons> = {
pm25: {
default: "mdi:molecule",
},
pm4: {
default: "mdi:molecule",
},
power: {
default: "mdi:flash",
},
@@ -677,9 +674,6 @@ export const ENTITY_COMPONENT_ICONS: Record<string, ComponentIcons> = {
pm25: {
default: "mdi:molecule",
},
pm4: {
default: "mdi:molecule",
},
power: {
default: "mdi:flash",
},

View File

@@ -332,15 +332,6 @@ class DialogCalendarEventEditor extends LitElement {
private _allDayToggleChanged(ev) {
this._allDay = ev.target.checked;
// When switching to all-day mode, normalize dates to midnight so time portions don't interfere with date comparisons
if (this._allDay && this._dtstart && this._dtend) {
this._dtstart = new Date(
formatDate(this._dtstart, this._timeZone!) + "T00:00:00"
);
this._dtend = new Date(
formatDate(this._dtend, this._timeZone!) + "T00:00:00"
);
}
}
private _startDateChanged(ev: CustomEvent) {

View File

@@ -1,4 +1,3 @@
import { consume } from "@lit/context";
import {
mdiAppleKeyboardCommand,
mdiContentCopy,
@@ -14,7 +13,6 @@ import {
import { html, LitElement, nothing } from "lit";
import { customElement, property, query, state } from "lit/decorators";
import { keyed } from "lit/directives/keyed";
import { capitalizeFirstLetter } from "../../../../common/string/capitalize-first-letter";
import { fireEvent } from "../../../../common/dom/fire_event";
import { handleStructError } from "../../../../common/structs/handle-errors";
import type { LocalizeKeys } from "../../../../common/translations/localize";
@@ -22,16 +20,7 @@ import "../../../../components/ha-md-divider";
import "../../../../components/ha-md-menu-item";
import { ACTION_BUILDING_BLOCKS } from "../../../../data/action";
import type { ActionSidebarConfig } from "../../../../data/automation";
import {
floorsContext,
fullEntitiesContext,
labelsContext,
} from "../../../../data/context";
import type { EntityRegistryEntry } from "../../../../data/entity_registry";
import type { FloorRegistryEntry } from "../../../../data/floor_registry";
import type { LabelRegistryEntry } from "../../../../data/label_registry";
import type { RepeatAction } from "../../../../data/script";
import { describeAction } from "../../../../data/script_i18n";
import type { HomeAssistant } from "../../../../types";
import { isMac } from "../../../../util/is_mac";
import type HaAutomationConditionEditor from "../action/ha-automation-action-editor";
@@ -60,18 +49,6 @@ export default class HaAutomationSidebarAction extends LitElement {
@state() private _warnings?: string[];
@state()
@consume({ context: fullEntitiesContext, subscribe: true })
_entityReg!: EntityRegistryEntry[];
@state()
@consume({ context: labelsContext, subscribe: true })
_labelReg!: LabelRegistryEntry[];
@state()
@consume({ context: floorsContext, subscribe: true })
_floorReg!: Record<string, FloorRegistryEntry>;
@query(".sidebar-editor")
public editor?: HaAutomationConditionEditor;
@@ -102,20 +79,15 @@ export default class HaAutomationSidebarAction extends LitElement {
const isBuildingBlock = ACTION_BUILDING_BLOCKS.includes(type || "");
const title = capitalizeFirstLetter(
describeAction(
this.hass,
this._entityReg,
this._labelReg,
this._floorReg,
actionConfig
)
);
const subtitle = this.hass.localize(
"ui.panel.config.automation.editor.actions.action"
);
const title =
this.hass.localize(
`ui.panel.config.automation.editor.actions.type.${type}.label` as LocalizeKeys
) || type;
const description = isBuildingBlock
? this.hass.localize(
`ui.panel.config.automation.editor.actions.type.${type}.description.picker` as LocalizeKeys

View File

@@ -20,7 +20,6 @@ import { subscribeEntityRegistry } from "../../../data/entity_registry";
import type { UpdateEntity } from "../../../data/update";
import { SubscribeMixin } from "../../../mixins/subscribe-mixin";
import type { HomeAssistant } from "../../../types";
import "../../../components/ha-progress-ring";
@customElement("ha-config-updates")
class HaConfigUpdates extends SubscribeMixin(LitElement) {
@@ -57,29 +56,6 @@ class HaConfigUpdates extends SubscribeMixin(LitElement) {
this._entities?.find((entity) => entity.entity_id === entityId)
);
private _renderUpdateProgress(entity: UpdateEntity) {
if (entity.attributes.update_percentage != null) {
return html`<ha-progress-ring
size="small"
.value=${entity.attributes.update_percentage}
.label=${this.hass.localize(
"ui.panel.config.updates.update_in_progress"
)}
></ha-progress-ring>`;
}
if (entity.attributes.in_progress) {
return html`<ha-spinner
size="small"
.ariaLabel=${this.hass.localize(
"ui.panel.config.updates.update_in_progress"
)}
></ha-spinner>`;
}
return html`<ha-icon-next></ha-icon-next>`;
}
protected render() {
if (!this.updateEntities?.length) {
return nothing;
@@ -130,9 +106,13 @@ class HaConfigUpdates extends SubscribeMixin(LitElement) {
)}
></state-badge>
${this.narrow && entity.attributes.in_progress
? html`<div class="absolute">
${this._renderUpdateProgress(entity)}
</div>`
? html`<ha-spinner
class="absolute"
size="small"
.ariaLabel=${this.hass.localize(
"ui.panel.config.updates.update_in_progress"
)}
></ha-spinner>`
: nothing}
</div>
<span slot="headline"
@@ -148,9 +128,16 @@ class HaConfigUpdates extends SubscribeMixin(LitElement) {
: nothing}
</span>
${!this.narrow
? html`<div slot="end">
${this._renderUpdateProgress(entity)}
</div>`
? entity.attributes.in_progress
? html`<div slot="end">
<ha-spinner
size="small"
.ariaLabel=${this.hass.localize(
"ui.panel.config.updates.update_in_progress"
)}
></ha-spinner>
</div>`
: html`<ha-icon-next slot="end"></ha-icon-next>`
: nothing}
</ha-md-list-item>
`;
@@ -206,13 +193,13 @@ class HaConfigUpdates extends SubscribeMixin(LitElement) {
div[slot="start"] {
position: relative;
}
div.absolute {
ha-spinner.absolute {
position: absolute;
left: 6px;
top: 6px;
}
state-badge.updating {
opacity: 0.2;
opacity: 0.5;
}
`,
];

View File

@@ -82,7 +82,7 @@ class DialogLabelDetail
this.hass,
this._params.entry
? this._params.entry.name || this._params.entry.label_id
: this.hass!.localize("ui.dialogs.label-detail.new_label")
: this.hass!.localize("ui.panel.config.labels.detail.new_label")
)}
>
<div>
@@ -95,9 +95,11 @@ class DialogLabelDetail
.value=${this._name}
.configValue=${"name"}
@input=${this._input}
.label=${this.hass!.localize("ui.dialogs.label-detail.name")}
.label=${this.hass!.localize(
"ui.panel.config.labels.detail.name"
)}
.validationMessage=${this.hass!.localize(
"ui.dialogs.label-detail.required_error_msg"
"ui.panel.config.labels.detail.required_error_msg"
)}
required
></ha-textfield>
@@ -106,21 +108,25 @@ class DialogLabelDetail
.hass=${this.hass}
.configValue=${"icon"}
@value-changed=${this._valueChanged}
.label=${this.hass!.localize("ui.dialogs.label-detail.icon")}
.label=${this.hass!.localize(
"ui.panel.config.labels.detail.icon"
)}
></ha-icon-picker>
<ha-color-picker
.value=${this._color}
.configValue=${"color"}
.hass=${this.hass}
@value-changed=${this._valueChanged}
.label=${this.hass!.localize("ui.dialogs.label-detail.color")}
.label=${this.hass!.localize(
"ui.panel.config.labels.detail.color"
)}
></ha-color-picker>
<ha-textarea
.value=${this._description}
.configValue=${"description"}
@input=${this._input}
.label=${this.hass!.localize(
"ui.dialogs.label-detail.description"
"ui.panel.config.labels.detail.description"
)}
></ha-textarea>
</div>
@@ -134,7 +140,7 @@ class DialogLabelDetail
@click=${this._deleteEntry}
.disabled=${this._submitting}
>
${this.hass!.localize("ui.common.delete")}
${this.hass!.localize("ui.panel.config.labels.detail.delete")}
</ha-button>
`
: nothing}
@@ -144,8 +150,8 @@ class DialogLabelDetail
.disabled=${this._submitting || !this._name}
>
${this._params.entry
? this.hass!.localize("ui.common.update")
: this.hass!.localize("ui.common.create")}
? this.hass!.localize("ui.panel.config.labels.detail.update")
: this.hass!.localize("ui.panel.config.labels.detail.create")}
</ha-button>
</ha-dialog>
`;

View File

@@ -148,9 +148,7 @@ export class AssistPipelineDebug extends LitElement {
).pipeline_runs.reverse();
} catch (e: any) {
showAlertDialog(this, {
title: this.hass.localize(
"ui.panel.config.voice_assistants.debug.error.fetch_runs"
),
title: "Failed to fetch pipeline runs",
text: e.message,
});
return;
@@ -178,9 +176,7 @@ export class AssistPipelineDebug extends LitElement {
).events;
} catch (e: any) {
showAlertDialog(this, {
title: this.hass.localize(
"ui.panel.config.voice_assistants.debug.error.fetch_events"
),
title: "Failed to fetch events",
text: e.message,
});
return;

View File

@@ -30,26 +30,16 @@ export class AssistPipelineEvents extends LitElement {
const run = this._processEvents(this.events);
if (!run) {
if (this.events.length) {
return html`<ha-alert alert-type="error"
>${this.hass.localize(
"ui.panel.config.voice_assistants.debug.error.showing_run"
)}</ha-alert
>
return html`<ha-alert alert-type="error">Error showing run</ha-alert>
<ha-card>
<ha-expansion-panel>
<span slot="header"
>${this.hass.localize(
"ui.panel.config.voice_assistants.debug.raw"
)}</span
>
<span slot="header">Raw</span>
<pre>${JSON.stringify(this.events, null, 2)}</pre>
</ha-expansion-panel>
</ha-card>`;
}
return html`<ha-alert alert-type="warning"
>${this.hass.localize(
"ui.panel.config.voice_assistants.debug.no_events"
)}</ha-alert
>There were no events in this run.</ha-alert
>`;
}
return html`

View File

@@ -11,16 +11,31 @@ import type { HomeAssistant } from "../../../../types";
import { formatNumber } from "../../../../common/number/format_number";
import "../../../../components/ha-yaml-editor";
import { showAlertDialog } from "../../../../dialogs/generic/show-dialog-box";
import type { LocalizeKeys } from "../../../../common/translations/localize";
const RUN_DATA = ["pipeline", "language"];
const WAKE_WORD_DATA = ["engine"];
const RUN_DATA = {
pipeline: "Pipeline",
language: "Language",
};
const WAKE_WORD_DATA = {
engine: "Engine",
};
const STT_DATA = ["engine"];
const STT_DATA = {
engine: "Engine",
};
const INTENT_DATA = ["engine", "language", "intent_input"];
const INTENT_DATA = {
engine: "Engine",
language: "Language",
intent_input: "Input",
};
const TTS_DATA = ["engine", "language", "voice", "tts_input"];
const TTS_DATA = {
engine: "Engine",
language: "Language",
voice: "Voice",
tts_input: "Input",
};
const STAGES: Record<PipelineRun["stage"], number> = {
ready: 0,
@@ -87,32 +102,24 @@ const renderProgress = (
return html`${durationString}s ✅`;
};
const renderData = (
hass: HomeAssistant,
data: Record<string, any>,
keys: string[]
) =>
keys.map((key) => {
const label = hass.localize(
`ui.panel.config.voice_assistants.debug.stages.${key}` as LocalizeKeys
);
return html`
const renderData = (data: Record<string, any>, keys: Record<string, string>) =>
Object.entries(keys).map(
([key, label]) => html`
<div class="row">
<div>${label}</div>
<div>${data[key]}</div>
</div>
`;
});
`
);
const dataMinusKeysRender = (
hass: HomeAssistant,
data: Record<string, any>,
keys: string[]
keys: Record<string, string>
) => {
const result = {};
let render = false;
for (const key in data) {
if (keys.includes(key) || key === "done") {
if (key in keys || key === "done") {
continue;
}
render = true;
@@ -120,9 +127,7 @@ const dataMinusKeysRender = (
}
return render
? html`<ha-expansion-panel>
<span slot="header"
>${hass.localize("ui.panel.config.voice_assistants.debug.raw")}</span
>
<span slot="header">Raw</span>
<ha-yaml-editor readOnly autoUpdate .value=${result}></ha-yaml-editor>
</ha-expansion-panel>`
: "";
@@ -178,15 +183,11 @@ export class AssistPipelineDebug extends LitElement {
<ha-card>
<div class="card-content">
<div class="row heading">
<div>
${this.hass.localize(
"ui.panel.config.voice_assistants.debug.run"
)}
</div>
<div>Run</div>
<div>${this.pipelineRun.stage}</div>
</div>
${renderData(this.hass, this.pipelineRun.run, RUN_DATA)}
${renderData(this.pipelineRun.run, RUN_DATA)}
${messages.length > 0
? html`
<div class="messages">
@@ -208,39 +209,23 @@ export class AssistPipelineDebug extends LitElement {
<ha-card>
<div class="card-content">
<div class="row heading">
<span
>${this.hass.localize(
"ui.panel.config.voice_assistants.debug.stages.wake_word"
)}</span
>
<span>Wake word</span>
${renderProgress(this.hass, this.pipelineRun, "wake_word")}
</div>
${this.pipelineRun.wake_word
? html`
<div class="card-content">
${renderData(
this.hass,
this.pipelineRun.wake_word,
WAKE_WORD_DATA
)}
${renderData(this.pipelineRun.wake_word, STT_DATA)}
${this.pipelineRun.wake_word.wake_word_output
? html`<div class="row">
<div>
${this.hass.localize(
"ui.panel.config.voice_assistants.debug.stages.model"
)}
</div>
<div>Model</div>
<div>
${this.pipelineRun.wake_word.wake_word_output
.ww_id}
</div>
</div>
<div class="row">
<div>
${this.hass.localize(
"ui.panel.config.voice_assistants.debug.stages.timestamp"
)}
</div>
<div>Timestamp</div>
<div>
${this.pipelineRun.wake_word.wake_word_output
.timestamp}
@@ -248,7 +233,6 @@ export class AssistPipelineDebug extends LitElement {
</div>`
: ""}
${dataMinusKeysRender(
this.hass,
this.pipelineRun.wake_word,
WAKE_WORD_DATA
)}
@@ -265,11 +249,7 @@ export class AssistPipelineDebug extends LitElement {
<ha-card>
<div class="card-content">
<div class="row heading">
<span
>${this.hass.localize(
"ui.panel.config.voice_assistants.debug.stages.speech_to_text"
)}</span
>
<span>Speech-to-text</span>
${renderProgress(
this.hass,
this.pipelineRun,
@@ -280,30 +260,18 @@ export class AssistPipelineDebug extends LitElement {
${this.pipelineRun.stt
? html`
<div class="card-content">
${renderData(this.hass, this.pipelineRun.stt, STT_DATA)}
${renderData(this.pipelineRun.stt, STT_DATA)}
<div class="row">
<div>
${this.hass.localize(
"ui.panel.config.voice_assistants.debug.stages.language"
)}
</div>
<div>Language</div>
<div>${this.pipelineRun.stt.metadata.language}</div>
</div>
${this.pipelineRun.stt.stt_output
? html`<div class="row">
<div>
${this.hass.localize(
"ui.panel.config.voice_assistants.debug.stages.output"
)}
</div>
<div>Output</div>
<div>${this.pipelineRun.stt.stt_output.text}</div>
</div>`
: ""}
${dataMinusKeysRender(
this.hass,
this.pipelineRun.stt,
STT_DATA
)}
${dataMinusKeysRender(this.pipelineRun.stt, STT_DATA)}
</div>
`
: ""}
@@ -317,28 +285,16 @@ export class AssistPipelineDebug extends LitElement {
<ha-card>
<div class="card-content">
<div class="row heading">
<span
>${this.hass.localize(
"ui.panel.config.voice_assistants.debug.stages.natural_language_processing"
)}</span
>
<span>Natural Language Processing</span>
${renderProgress(this.hass, this.pipelineRun, "intent")}
</div>
${this.pipelineRun.intent
? html`
<div class="card-content">
${renderData(
this.hass,
this.pipelineRun.intent,
INTENT_DATA
)}
${renderData(this.pipelineRun.intent, INTENT_DATA)}
${this.pipelineRun.intent.intent_output
? html`<div class="row">
<div>
${this.hass.localize(
"ui.panel.config.voice_assistants.debug.stages.response_type"
)}
</div>
<div>Response type</div>
<div>
${this.pipelineRun.intent.intent_output
.response.response_type}
@@ -347,11 +303,7 @@ export class AssistPipelineDebug extends LitElement {
${this.pipelineRun.intent.intent_output.response
.response_type === "error"
? html`<div class="row">
<div>
${this.hass.localize(
"ui.panel.config.voice_assistants.debug.error.code"
)}
</div>
<div>Error code</div>
<div>
${this.pipelineRun.intent.intent_output
.response.data.code}
@@ -360,27 +312,18 @@ export class AssistPipelineDebug extends LitElement {
: ""}`
: ""}
<div class="row">
<div>
${this.hass.localize(
"ui.panel.config.voice_assistants.debug.stages.prefer_local"
)}
</div>
<div>Prefer handling locally</div>
<div>
${this.pipelineRun.intent.prefer_local_intents}
</div>
</div>
<div class="row">
<div>
${this.hass.localize(
"ui.panel.config.voice_assistants.debug.stages.processed_locally"
)}
</div>
<div>Processed locally</div>
<div>
${this.pipelineRun.intent.processed_locally}
</div>
</div>
${dataMinusKeysRender(
this.hass,
this.pipelineRun.intent,
INTENT_DATA
)}
@@ -397,22 +340,14 @@ export class AssistPipelineDebug extends LitElement {
<ha-card>
<div class="card-content">
<div class="row heading">
<span
>${this.hass.localize(
"ui.panel.config.voice_assistants.debug.stages.text_to_speech"
)}</span
>
<span>Text-to-speech</span>
${renderProgress(this.hass, this.pipelineRun, "tts")}
</div>
${this.pipelineRun.tts
? html`
<div class="card-content">
${renderData(this.hass, this.pipelineRun.tts, TTS_DATA)}
${dataMinusKeysRender(
this.hass,
this.pipelineRun.tts,
TTS_DATA
)}
${renderData(this.pipelineRun.tts, TTS_DATA)}
${dataMinusKeysRender(this.pipelineRun.tts, TTS_DATA)}
</div>
`
: ""}
@@ -426,13 +361,7 @@ export class AssistPipelineDebug extends LitElement {
? this._stopTTS
: this._playTTS}
>
${this._isPlaying
? this.hass.localize(
"ui.panel.config.voice_assistants.debug.stop_audio"
)
: this.hass.localize(
"ui.panel.config.voice_assistants.debug.play_audio"
)}
${this._isPlaying ? "Stop audio" : "Play audio"}
</ha-button>
</div>
`
@@ -443,11 +372,7 @@ export class AssistPipelineDebug extends LitElement {
${maybeRenderError(this.pipelineRun, "tts", lastRunStage)}
<ha-card>
<ha-expansion-panel>
<span slot="header"
>${this.hass.localize(
"ui.panel.config.voice_assistants.debug.raw"
)}</span
>
<span slot="header">Raw</span>
<ha-yaml-editor
read-only
auto-update
@@ -466,14 +391,7 @@ export class AssistPipelineDebug extends LitElement {
this._audioElement = new Audio(url);
this._audioElement.addEventListener("error", () => {
showAlertDialog(this, {
title: this.hass.localize(
"ui.panel.config.voice_assistants.debug.error.title"
),
text: this.hass.localize(
"ui.panel.config.voice_assistants.debug.error.playing_audio"
),
});
showAlertDialog(this, { title: "Error", text: "Error playing audio" });
});
this._audioElement.addEventListener("play", () => {

View File

@@ -80,44 +80,41 @@ export class HuiEnergyCompareCard
return html`
<ha-alert dismissable @alert-dismissed-clicked=${this._stopCompare}>
${this.hass.localize(
"ui.panel.lovelace.cards.energy.energy_compare.info",
{
start: html`<b
>${formatDate(
this._start!,
this.hass.locale,
this.hass.config
)}${dayDifference > 0
? ` -
${this.hass.localize("ui.panel.energy.compare.info", {
start: html`<b
>${formatDate(
this._start!,
this.hass.locale,
this.hass.config
)}${dayDifference > 0
? ` -
${formatDate(
this._end || endOfDay(new Date()),
this.hass.locale,
this.hass.config
)}`
: ""}</b
>`,
end: html`<b
>${formatDate(
this._startCompare,
this.hass.locale,
this.hass.config
)}${dayDifference > 0
? ` -
: ""}</b
>`,
end: html`<b
>${formatDate(
this._startCompare,
this.hass.locale,
this.hass.config
)}${dayDifference > 0
? ` -
${formatDate(this._endCompare, this.hass.locale, this.hass.config)}`
: ""}</b
>
<button class="link" @click=${this._changeCompareMode}>
(${this._compareMode === CompareMode.PREVIOUS
? this.hass.localize(
"ui.panel.lovelace.cards.energy.energy_compare.compare_previous_year"
)
: this.hass.localize(
"ui.panel.lovelace.cards.energy.energy_compare.compare_previous_period"
)})
</button>`,
}
)}
: ""}</b
>
<button class="link" @click=${this._changeCompareMode}>
(${this._compareMode === CompareMode.PREVIOUS
? this.hass.localize(
"ui.panel.energy.compare.compare_previous_year"
)
: this.hass.localize(
"ui.panel.energy.compare.compare_previous_period"
)})
</button>`,
})}
</ha-alert>
`;
}

View File

@@ -17,7 +17,6 @@ import type { HomeAssistant } from "../../../../types";
import { showSaveSuccessToast } from "../../../../util/toast-saved-success";
import "../../cards/hui-card";
import "../../sections/hui-section";
import { getViewType } from "../../views/get-view-type";
import { addCards, addSection } from "../config-util";
import type { LovelaceContainerPath } from "../lovelace-path";
import { parseLovelaceContainerPath } from "../lovelace-path";
@@ -67,9 +66,7 @@ export class HuiDialogSuggestCard extends LitElement {
const { viewIndex } = parseLovelaceContainerPath(this._params.path);
const viewConfig = this._params!.lovelaceConfig.views[viewIndex];
return (
!isStrategyView(viewConfig) && getViewType(viewConfig) === "sections"
);
return !isStrategyView(viewConfig) && viewConfig.type === "sections";
}
private _renderPreview() {

View File

@@ -322,7 +322,6 @@ class HUIRoot extends LitElement {
.path=${item.icon}
slot="trigger"
.label=${label}
hide-title
></ha-icon-button>
${item.subItems
.filter((subItem) => subItem.visible)
@@ -349,10 +348,8 @@ class HUIRoot extends LitElement {
.id="button-${index}"
.path=${item.icon}
@click=${item.buttonAction}
.label=${label}
></ha-icon-button>
<ha-tooltip placement="bottom" .for="button-${index}">
${label}
</ha-tooltip>
`;
result.push(button);
});

View File

@@ -824,7 +824,6 @@
"add_new": "Add new area…",
"no_areas": "You don't have any areas",
"no_match": "No matching areas found",
"placeholder": "Select an area",
"unassigned_areas": "Unassigned areas",
"failed_create_area": "Failed to create area."
},
@@ -1707,14 +1706,6 @@
"update": "[%key:ui::panel::config::devices::update%]",
"unknown_error": "[%key:ui::panel::config::devices::unknown_error%]"
},
"label-detail": {
"new_label": "New label",
"name": "Name",
"icon": "Icon",
"color": "Color",
"description": "Description",
"required_error_msg": "[%key:ui::panel::config::zone::detail::required_error_msg%]"
},
"voice-settings": {
"expose_header": "Expose",
"aliases_header": "Aliases",
@@ -2415,7 +2406,18 @@
"introduction": "Labels can help you organize your areas, devices, and entities. They can be used to filter in the UI, or use them as a target in automations.",
"introduction2": "Go to the area, device, or entity you want to add a label to, and press the edit button to assign labels to them.",
"confirm_remove_title": "Remove label?",
"confirm_remove": "Are you sure you want to remove label {label}? It will be removed from all areas, devices, and entities."
"confirm_remove": "Are you sure you want to remove label {label}? It will be removed from all areas, devices, and entities.",
"detail": {
"new_label": "New label",
"name": "Name",
"icon": "Icon",
"color": "Color",
"description": "Description",
"delete": "Delete",
"update": "Update",
"create": "Create",
"required_error_msg": "[%key:ui::panel::config::zone::detail::required_error_msg%]"
}
},
"areas": {
"caption": "Areas",
@@ -3627,36 +3629,6 @@
"older_run": "Older run",
"newer_run": "Newer run",
"start_debug_run": "Start debug run",
"error": {
"code": "Error code",
"fetch_events": "Failed to fetch events",
"fetch_runs": "Failed to fetch pipeline runs",
"playing_audio": "Error playing audio",
"showing_run": "Error showing run",
"title": "Error"
},
"no_events": "There were no events in this run.",
"play_audio": "Play audio",
"raw": "Raw",
"run": "Run",
"stages": {
"engine": "Engine",
"input": "Input",
"language": "Language",
"model": "Model",
"natural_language_processing": "Natural language processing",
"output": "Output",
"pipeline": "Pipeline",
"prefer_local": "Prefer handling locally",
"processed_locally": "Processed locally",
"response_type": "Response type",
"speech_to_text": "Speech-to-text",
"text_to_speech": "Text-to-speech",
"timestamp": "Timestamp",
"voice": "Voice",
"wake_word": "Wake word"
},
"stop_audio": "Stop audio",
"pipeline": {
"header": "Assist pipeline",
"run_text_pipeline": "Run text pipeline",
@@ -7116,11 +7088,6 @@
"card_indicates_energy_used": "This card indicates how much of the electricity consumed by your home was generated using non-fossil fuels like solar, wind, and nuclear. The higher, the better!",
"low_carbon_energy_consumed": "Low-carbon electricity consumed",
"low_carbon_energy_not_calculated": "Consumed low-carbon electricity couldn't be calculated"
},
"energy_compare": {
"info": "You are comparing the period {start} with the period {end}",
"compare_previous_year": "Compare previous year",
"compare_previous_period": "Compare previous period"
}
},
"heading": {
@@ -9369,6 +9336,11 @@
"energy": {
"download_data": "[%key:ui::panel::history::download_data%]",
"configure": "[%key:ui::dialogs::quick-bar::commands::navigation::energy%]",
"compare": {
"info": "You are comparing the period {start} with the period {end}",
"compare_previous_year": "Compare previous year",
"compare_previous_period": "Compare previous period"
},
"setup": {
"next": "Next",
"back": "Back",

View File

@@ -201,7 +201,6 @@ describe("getStates", () => {
"pm1",
"pm10",
"pm25",
"pm4",
"power_factor",
"power",
"pressure",
@@ -216,7 +215,7 @@ describe("getStates", () => {
"volume_flow_rate",
])
);
expect(result.length).toBe(35);
expect(result.length).toBe(34);
});
it("should return empty array for unknown attribute", () => {

714
yarn.lock

File diff suppressed because it is too large Load Diff