diff --git a/hassio/src/addon-store/hassio-addon-store.ts b/hassio/src/addon-store/hassio-addon-store.ts index 024d803be2..ab434eb8ca 100644 --- a/hassio/src/addon-store/hassio-addon-store.ts +++ b/hassio/src/addon-store/hassio-addon-store.ts @@ -92,11 +92,7 @@ export class HassioAddonStore extends LitElement { .route=${this.route} .header=${this.supervisor.localize("panel.store")} > - +
- + - + +
${this._cameras && this._cameras.length > 1 - ? html` + ? html` ; + stt?: PipelineSTTStartEvent["data"] & + Partial & { done: boolean }; intent?: PipelineIntentStartEvent["data"] & - Partial; - tts?: PipelineTTSStartEvent["data"] & Partial; + Partial & { done: boolean }; + tts?: PipelineTTSStartEvent["data"] & + Partial & { done: boolean }; } -export const runPipelineFromText = ( +export const runVoiceAssistantPipeline = ( hass: HomeAssistant, callback: (event: PipelineRun) => void, options: PipelineRunOptions @@ -139,17 +141,38 @@ export const runPipelineFromText = ( } if (updateEvent.type === "stt-start") { - run = { ...run, stage: "stt", stt: updateEvent.data }; + run = { + ...run, + stage: "stt", + stt: { ...updateEvent.data, done: false }, + }; } else if (updateEvent.type === "stt-end") { - run = { ...run, stt: { ...run.stt!, ...updateEvent.data } }; + run = { + ...run, + stt: { ...run.stt!, ...updateEvent.data, done: true }, + }; } else if (updateEvent.type === "intent-start") { - run = { ...run, stage: "intent", intent: updateEvent.data }; + run = { + ...run, + stage: "intent", + intent: { ...updateEvent.data, done: false }, + }; } else if (updateEvent.type === "intent-end") { - run = { ...run, intent: { ...run.intent!, ...updateEvent.data } }; + run = { + ...run, + intent: { ...run.intent!, ...updateEvent.data, done: true }, + }; } else if (updateEvent.type === "tts-start") { - run = { ...run, stage: "tts", tts: updateEvent.data }; + run = { + ...run, + stage: "tts", + tts: { ...updateEvent.data, done: false }, + }; } else if (updateEvent.type === "tts-end") { - run = { ...run, tts: { ...run.tts!, ...updateEvent.data } }; + run = { + ...run, + tts: { ...run.tts!, ...updateEvent.data, done: true }, + }; } else if (updateEvent.type === "run-end") { run = { ...run, stage: "done" }; unsubProm.then((unsub) => unsub()); diff --git a/src/dialogs/more-info/components/fan/ha-more-info-fan-speed.ts b/src/dialogs/more-info/components/fan/ha-more-info-fan-speed.ts index 4762796e1b..41fead80cf 100644 --- a/src/dialogs/more-info/components/fan/ha-more-info-fan-speed.ts +++ b/src/dialogs/more-info/components/fan/ha-more-info-fan-speed.ts @@ -3,6 +3,7 @@ import { customElement, property, state } from "lit/decorators"; import { styleMap } from "lit/directives/style-map"; import { computeAttributeNameDisplay } from "../../../../common/entity/compute_attribute_display"; import { computeStateDisplay } from "../../../../common/entity/compute_state_display"; +import { stateActive } from "../../../../common/entity/state_active"; import { stateColorCss } from "../../../../common/entity/state_color"; import "../../../../components/ha-control-select"; import type { ControlSelectOption } from "../../../../components/ha-control-select"; @@ -26,20 +27,25 @@ export class HaMoreInfoFanSpeed extends LitElement { @property({ attribute: false }) public stateObj!: FanEntity; - @state() value?: number; + @state() sliderValue?: number; + + @state() speedValue?: FanSpeed; protected updated(changedProp: Map): void { if (changedProp.has("stateObj")) { - this.value = - this.stateObj.attributes.percentage != null - ? Math.max(Math.round(this.stateObj.attributes.percentage), 1) - : undefined; + const percentage = stateActive(this.stateObj) + ? this.stateObj.attributes.percentage ?? 0 + : 0; + this.sliderValue = Math.max(Math.round(percentage), 0); + this.speedValue = fanPercentageToSpeed(this.stateObj, percentage); } } private _speedValueChanged(ev: CustomEvent) { const speed = (ev.detail as any).value as FanSpeed; + this.speedValue = speed; + const percentage = fanSpeedToPercentage(this.stateObj, speed); this.hass.callService("fan", "set_percentage", { @@ -52,6 +58,8 @@ export class HaMoreInfoFanSpeed extends LitElement { const value = (ev.detail as any).value; if (isNaN(value)) return; + this.sliderValue = value; + this.hass.callService("fan", "set_percentage", { entity_id: this.stateObj!.entity_id, percentage: value, @@ -88,16 +96,11 @@ export class HaMoreInfoFanSpeed extends LitElement { }) ).reverse(); - const speed = fanPercentageToSpeed( - this.stateObj, - this.stateObj.attributes.percentage ?? 0 - ); - return html` - ${stateObj.attributes.status || - this.hass.localize( - `component.vacuum.entity_component._.state.${stateObj.state}` + ${computeAttributeValueDisplay( + this.hass.localize, + stateObj, + this.hass.locale, + this.hass.entities, + "status" ) || - stateObj.state} + computeStateDisplay( + this.hass.localize, + stateObj, + this.hass.locale, + this.hass.entities + )}
diff --git a/src/dialogs/more-info/ha-more-info-dialog.ts b/src/dialogs/more-info/ha-more-info-dialog.ts index 8512a382e1..3bcc18b883 100644 --- a/src/dialogs/more-info/ha-more-info-dialog.ts +++ b/src/dialogs/more-info/ha-more-info-dialog.ts @@ -451,6 +451,7 @@ export class MoreInfoDialog extends LitElement { --dialog-surface-position: static; --dialog-content-position: static; --dialog-content-padding: 0; + --chart-base-position: static; } ha-header-bar { diff --git a/src/layouts/hass-subpage.ts b/src/layouts/hass-subpage.ts index a15eaa6eb1..7a342d00b8 100644 --- a/src/layouts/hass-subpage.ts +++ b/src/layouts/hass-subpage.ts @@ -99,6 +99,8 @@ class HassSubpage extends LitElement { display: block; height: 100%; background-color: var(--primary-background-color); + overflow: hidden; + position: relative; } :host([narrow]) { @@ -152,7 +154,7 @@ class HassSubpage extends LitElement { } #fab { - position: fixed; + position: absolute; right: calc(16px + env(safe-area-inset-right)); bottom: calc(16px + env(safe-area-inset-bottom)); z-index: 1; diff --git a/src/panels/config/automation/action/ha-automation-action-row.ts b/src/panels/config/automation/action/ha-automation-action-row.ts index b6474dc2ac..a85feebf28 100644 --- a/src/panels/config/automation/action/ha-automation-action-row.ts +++ b/src/panels/config/automation/action/ha-automation-action-row.ts @@ -182,8 +182,6 @@ export default class HaAutomationActionRow extends LitElement { : html` diff --git a/src/panels/config/automation/action/ha-automation-action.ts b/src/panels/config/automation/action/ha-automation-action.ts index 7ff8100b76..98b10bbffa 100644 --- a/src/panels/config/automation/action/ha-automation-action.ts +++ b/src/panels/config/automation/action/ha-automation-action.ts @@ -128,11 +128,7 @@ export default class HaAutomationAction extends LitElement { ` )}
- + diff --git a/src/panels/config/automation/condition/ha-automation-condition.ts b/src/panels/config/automation/condition/ha-automation-condition.ts index 942fe074ae..12b4fd3bbb 100644 --- a/src/panels/config/automation/condition/ha-automation-condition.ts +++ b/src/panels/config/automation/condition/ha-automation-condition.ts @@ -180,11 +180,7 @@ export default class HaAutomationCondition extends LitElement { ` )} - + ` : ""} - + ` : ""} - + diff --git a/src/panels/config/cloud/alexa/cloud-alexa.ts b/src/panels/config/cloud/alexa/cloud-alexa.ts index 8c666890dc..6c8dbda913 100644 --- a/src/panels/config/cloud/alexa/cloud-alexa.ts +++ b/src/panels/config/cloud/alexa/cloud-alexa.ts @@ -168,7 +168,6 @@ class CloudAlexa extends LitElement { ${!emptyFilter ? html`${iconButton}` : html` @@ -225,7 +224,7 @@ class CloudAlexa extends LitElement { .narrow=${this.narrow} .header=${this.hass!.localize("ui.panel.config.cloud.alexa.title")} > - + @@ -302,7 +301,7 @@ class CloudGoogleAssistant extends LitElement { .hass=${this.hass} .header=${this.hass!.localize("ui.panel.config.cloud.google.title")} .narrow=${this.narrow}> - + - + - + + - + ` : html` - + import("./areas/ha-config-areas"), }, - voice_assistant: { - tag: "assist-pipeline-debug", - load: () => - import( - "./integrations/integration-panels/voice_assistant/assist/assist-pipeline-debug" - ), + "voice-assistants": { + tag: "ha-config-voice-assistants", + load: () => import("./voice-assistants/ha-config-voice-assistants"), }, automation: { tag: "ha-config-automation", diff --git a/src/panels/config/integrations/ha-config-flow-card.ts b/src/panels/config/integrations/ha-config-flow-card.ts index d60e83c56c..d2005a10ff 100644 --- a/src/panels/config/integrations/ha-config-flow-card.ts +++ b/src/panels/config/integrations/ha-config-flow-card.ts @@ -60,7 +60,7 @@ export class HaConfigFlowCard extends LitElement { }` )} > - + ${disabledCount}` : ""} - + + - + ${router.server} ${router.extended_address === this._otbrInfo?.extended_address ? html` diff --git a/src/panels/config/integrations/integration-panels/voice_assistant/assist/assist-pipeline-debug.ts b/src/panels/config/integrations/integration-panels/voice_assistant/assist/assist-pipeline-debug.ts index c0dcd3d5af..49906ad2a9 100644 --- a/src/panels/config/integrations/integration-panels/voice_assistant/assist/assist-pipeline-debug.ts +++ b/src/panels/config/integrations/integration-panels/voice_assistant/assist/assist-pipeline-debug.ts @@ -1,20 +1,25 @@ -import { css, html, LitElement, PropertyValues, TemplateResult } from "lit"; +import { css, html, LitElement, TemplateResult } from "lit"; import { customElement, property, query, state } from "lit/decorators"; import "../../../../../../components/ha-button"; import { PipelineRun, - runPipelineFromText, + PipelineRunOptions, + runVoiceAssistantPipeline, } from "../../../../../../data/voice_assistant"; import "../../../../../../layouts/hass-subpage"; import "../../../../../../components/ha-formfield"; import "../../../../../../components/ha-checkbox"; import { haStyle } from "../../../../../../resources/styles"; import type { HomeAssistant } from "../../../../../../types"; -import { showPromptDialog } from "../../../../../../dialogs/generic/show-dialog-box"; +import { + showAlertDialog, + showPromptDialog, +} from "../../../../../../dialogs/generic/show-dialog-box"; import "./assist-render-pipeline-run"; import type { HaCheckbox } from "../../../../../../components/ha-checkbox"; import type { HaTextField } from "../../../../../../components/ha-textfield"; import "../../../../../../components/ha-textfield"; +import { fileDownload } from "../../../../../../util/file_download"; @customElement("assist-pipeline-debug") export class AssistPipelineDebug extends LitElement { @@ -24,8 +29,6 @@ export class AssistPipelineDebug extends LitElement { @state() private _pipelineRuns: PipelineRun[] = []; - @state() private _stopRecording?: () => void; - @query("#continue-conversation") private _continueConversationCheckbox!: HaCheckbox; @@ -36,6 +39,8 @@ export class AssistPipelineDebug extends LitElement { @state() private _finished = false; + @state() private _languageOverride?: string; + protected render(): TemplateResult { return html` Clear + + Download + ` - : ""} + : html` + + Set Language + + `}
@@ -81,6 +96,12 @@ export class AssistPipelineDebug extends LitElement { Send ` + : this._finished + ? html` + + Continue talking + + ` : html` { - if (this._continueConversationCheckbox.checked) { - this._runAudioPipeline(); - } else { - this._finished = true; - } - }); - audio.play(); - } else if (currentRun.stage === "error") { - this._finished = true; - } - } - private get conversationId(): string | null { return this._pipelineRuns.length === 0 ? null @@ -177,26 +151,19 @@ export class AssistPipelineDebug extends LitElement { return; } - let added = false; - runPipelineFromText( - this.hass, + await this._doRunPipeline( (run) => { - if (textfield && ["done", "error"].includes(run.stage)) { - textfield.value = ""; - } - - if (added) { - this._pipelineRuns = [run, ...this._pipelineRuns.slice(1)]; - } else { - this._pipelineRuns = [run, ...this._pipelineRuns]; - added = true; + if (["done", "error"].includes(run.stage)) { + this._finished = true; + if (textfield) { + textfield.value = ""; + } } }, { start_stage: "intent", end_stage: "intent", input: { text }, - conversation_id: this.conversationId, } ); } @@ -204,7 +171,13 @@ export class AssistPipelineDebug extends LitElement { private async _runAudioPipeline() { // @ts-ignore-next-line const context = new (window.AudioContext || window.webkitAudioContext)(); - const stream = await navigator.mediaDevices.getUserMedia({ audio: true }); + let stream: MediaStream; + try { + stream = await navigator.mediaDevices.getUserMedia({ audio: true }); + } catch (err) { + return; + } + await context.audioWorklet.addModule( new URL("./recorder.worklet.js", import.meta.url) ); @@ -213,47 +186,111 @@ export class AssistPipelineDebug extends LitElement { const recorder = new AudioWorkletNode(context, "recorder.worklet"); this.hass.connection.socket!.binaryType = "arraybuffer"; - this._stopRecording = () => { + + let run: PipelineRun | undefined; + + let stopRecording: (() => void) | undefined = () => { + stopRecording = undefined; + // We're currently STTing, so finish audio + if (run?.stage === "stt" && run.stt!.done === false) { + if (this._audioBuffer) { + for (const chunk of this._audioBuffer) { + this._sendAudioChunk(chunk); + } + } + // Send empty message to indicate we're done streaming. + this._sendAudioChunk(new Int16Array()); + } + this._audioBuffer = undefined; stream.getTracks()[0].stop(); context.close(); - this._stopRecording = undefined; - this._audioBuffer = undefined; - // Send empty message to indicate we're done streaming. - this._sendAudioChunk(new Int16Array()); }; this._audioBuffer = []; source.connect(recorder).connect(context.destination); recorder.port.onmessage = (e) => { + if (!stopRecording) { + return; + } if (this._audioBuffer) { this._audioBuffer.push(e.data); - return; + } else { + this._sendAudioChunk(e.data); } - if (this._pipelineRuns[0].stage !== "stt") { - return; - } - this._sendAudioChunk(e.data); }; - this._finished = false; - let added = false; - runPipelineFromText( - this.hass, - (run) => { - if (added) { - this._pipelineRuns = [run, ...this._pipelineRuns.slice(1)]; - } else { - this._pipelineRuns = [run, ...this._pipelineRuns]; - added = true; + await this._doRunPipeline( + (updatedRun) => { + run = updatedRun; + + // When we start STT stage, the WS has a binary handler + if (updatedRun.stage === "stt" && this._audioBuffer) { + // Send the buffer over the WS to the STT engine. + for (const buffer of this._audioBuffer) { + this._sendAudioChunk(buffer); + } + this._audioBuffer = undefined; + } + + // Stop recording if the server is done with STT stage + if (!["ready", "stt"].includes(updatedRun.stage) && stopRecording) { + stopRecording(); + } + + // Play audio when we're done. + if (updatedRun.stage === "done") { + const url = updatedRun.tts!.tts_output!.url; + const audio = new Audio(url); + audio.addEventListener("ended", () => { + if (this._continueConversationCheckbox.checked) { + this._runAudioPipeline(); + } else { + this._finished = true; + } + }); + audio.play(); + } else if (updatedRun.stage === "error") { + this._finished = true; } }, { start_stage: "stt", end_stage: "tts", - conversation_id: this.conversationId, } ); } + private async _doRunPipeline( + callback: (event: PipelineRun) => void, + options: PipelineRunOptions + ) { + this._finished = false; + let added = false; + try { + await runVoiceAssistantPipeline( + this.hass, + (updatedRun) => { + if (added) { + this._pipelineRuns = [updatedRun, ...this._pipelineRuns.slice(1)]; + } else { + this._pipelineRuns = [updatedRun, ...this._pipelineRuns]; + added = true; + } + callback(updatedRun); + }, + { + ...options, + language: this._languageOverride, + conversation_id: this.conversationId, + } + ); + } catch (err: any) { + await showAlertDialog(this, { + title: "Error starting pipeline", + text: err.message || err, + }); + } + } + private _sendAudioChunk(chunk: Int16Array) { // Turn into 8 bit so we can prefix our handler ID. const data = new Uint8Array(1 + chunk.length * 2); @@ -273,6 +310,27 @@ export class AssistPipelineDebug extends LitElement { this._pipelineRuns = []; } + private _downloadConversation() { + fileDownload( + `data:text/plain;charset=utf-8,${encodeURIComponent( + JSON.stringify(this._pipelineRuns, null, 2) + )}`, + `conversation.json` + ); + } + + private async _setLanguage() { + const language = await showPromptDialog(this, { + title: "Language override", + inputLabel: "Language", + inputType: "text", + confirmText: "Set", + }); + if (language) { + this._languageOverride = language; + } + } + static styles = [ haStyle, css` diff --git a/src/panels/config/integrations/integration-panels/voice_assistant/assist/assist-render-pipeline-run.ts b/src/panels/config/integrations/integration-panels/voice_assistant/assist/assist-render-pipeline-run.ts index f9940e0cb2..596b7f7560 100644 --- a/src/panels/config/integrations/integration-panels/voice_assistant/assist/assist-render-pipeline-run.ts +++ b/src/panels/config/integrations/integration-panels/voice_assistant/assist/assist-render-pipeline-run.ts @@ -50,9 +50,11 @@ const maybeRenderError = ( return ""; } - return html` - ${run.error!.message} (${run.error!.code}) - `; + return html` + + ${run.error!.message} (${run.error!.code}) + + `; }; const renderProgress = ( @@ -76,10 +78,9 @@ const renderProgress = ( } if (!finishEvent) { - return html``; + return html` + + `; } const duration = @@ -109,7 +110,7 @@ const dataMinusKeysRender = ( const result = {}; let render = false; for (const key in data) { - if (key in keys) { + if (key in keys || key === "done") { continue; } render = true; diff --git a/src/panels/config/logs/ha-config-logs.ts b/src/panels/config/logs/ha-config-logs.ts index a220344260..6788c9b639 100644 --- a/src/panels/config/logs/ha-config-logs.ts +++ b/src/panels/config/logs/ha-config-logs.ts @@ -115,7 +115,7 @@ export class HaConfigLogs extends LitElement { ${isComponentLoaded(this.hass, "hassio") && this.hass.userData?.showAdvanced ? html` - + ${this.hass.userData?.showAdvanced ? html` - + ` : this.hass.localize("ui.common.save")} - +
- + ` : ""} - + + ${this._hostInfo ? html` - + + import( + "../integrations/integration-panels/voice_assistant/assist/assist-pipeline-debug" + ), + }, + }, + }; + + protected updatePageEl(pageEl) { + pageEl.hass = this.hass; + pageEl.narrow = this.narrow; + pageEl.isWide = this.isWide; + pageEl.route = this.routeTail; + } +} + +declare global { + interface HTMLElementTagNameMap { + "ha-config-voice-assistants": HaConfigVoiceAssistants; + } +} diff --git a/src/panels/lovelace/cards/hui-tile-card.ts b/src/panels/lovelace/cards/hui-tile-card.ts index 45269255b3..5f2b43b83a 100644 --- a/src/panels/lovelace/cards/hui-tile-card.ts +++ b/src/panels/lovelace/cards/hui-tile-card.ts @@ -214,7 +214,7 @@ export class HuiTileCard extends LitElement implements LovelaceCard { } } - if (domain === "fan" && stateActive(stateObj)) { + if (domain === "fan") { const speedStateDisplay = computeFanSpeedStateDisplay( stateObj as FanEntity, this.hass!.locale @@ -231,12 +231,11 @@ export class HuiTileCard extends LitElement implements LovelaceCard { this.hass!.entities ); - if (domain === "cover" && stateActive(stateObj)) { + if (domain === "cover") { const positionStateDisplay = computeCoverPositionStateDisplay( stateObj as CoverEntity, this.hass!.locale ); - if (positionStateDisplay) { return `${stateDisplay} βΈ± ${positionStateDisplay}`; } diff --git a/src/panels/lovelace/components/hui-card-options.ts b/src/panels/lovelace/components/hui-card-options.ts index 7e34b16c70..18c32eafa4 100644 --- a/src/panels/lovelace/components/hui-card-options.ts +++ b/src/panels/lovelace/components/hui-card-options.ts @@ -80,7 +80,7 @@ export class HuiCardOptions extends LitElement { @click=${this._cardUp} ?disabled=${this.path![1] === 0} > - + - + + { class HuiFanSpeedTileFeature extends LitElement implements LovelaceTileFeature { @property({ attribute: false }) public hass?: HomeAssistant; - @property({ attribute: false }) public stateObj?: HassEntity; + @property({ attribute: false }) public stateObj?: FanEntity; @state() private _config?: FanSpeedTileFeatureConfig; @@ -79,6 +81,10 @@ class HuiFanSpeedTileFeature extends LitElement implements LovelaceTileFeature { const speedCount = computeFanSpeedCount(this.stateObj); + const percentage = stateActive(this.stateObj) + ? this.stateObj.attributes.percentage ?? 0 + : 0; + if (speedCount <= FAN_SPEED_COUNT_MAX_FOR_BUTTONS) { const options = FAN_SPEEDS[speedCount]!.map( (speed) => ({ @@ -88,10 +94,7 @@ class HuiFanSpeedTileFeature extends LitElement implements LovelaceTileFeature { }) ); - const speed = fanPercentageToSpeed( - this.stateObj, - this.stateObj.attributes.percentage ?? 0 - ); + const speed = fanPercentageToSpeed(this.stateObj, percentage); return html`
@@ -113,15 +116,12 @@ class HuiFanSpeedTileFeature extends LitElement implements LovelaceTileFeature { `; } - const percentage = - this.stateObj.attributes.percentage != null - ? Math.max(Math.round(this.stateObj.attributes.percentage), 0) - : undefined; + const value = Math.max(Math.round(percentage), 0); return html`
+ + ${ this.narrow ? html` diff --git a/src/panels/profile/ha-pick-dashboard-row.ts b/src/panels/profile/ha-pick-dashboard-row.ts index 39354f25bc..7b88eb9dfa 100644 --- a/src/panels/profile/ha-pick-dashboard-row.ts +++ b/src/panels/profile/ha-pick-dashboard-row.ts @@ -37,6 +37,7 @@ class HaPickDashboardRow extends LitElement { .disabled=${!this._dashboards?.length} .value=${this.hass.defaultPanel} @selected=${this._dashboardChanged} + naturalMenuWidth > ${this.hass.localize( diff --git a/src/panels/profile/ha-pick-first-weekday-row.ts b/src/panels/profile/ha-pick-first-weekday-row.ts index c9142efb57..6a615cc433 100644 --- a/src/panels/profile/ha-pick-first-weekday-row.ts +++ b/src/panels/profile/ha-pick-first-weekday-row.ts @@ -30,6 +30,7 @@ class FirstWeekdayRow extends LitElement { .disabled=${this.hass.locale === undefined} .value=${this.hass.locale.first_weekday} @selected=${this._handleFormatSelection} + naturalMenuWidth > ${[ FirstWeekday.language, diff --git a/src/panels/profile/ha-pick-language-row.ts b/src/panels/profile/ha-pick-language-row.ts index c2e7ba86f6..7bab38abdc 100644 --- a/src/panels/profile/ha-pick-language-row.ts +++ b/src/panels/profile/ha-pick-language-row.ts @@ -39,6 +39,7 @@ export class HaPickLanguageRow extends LitElement { )} .value=${this.hass.locale.language} @selected=${this._languageSelectionChanged} + naturalMenuWidth > ${this._languages.map( (language) => html` ${Object.values(NumberFormat).map((format) => { const formattedNumber = formatNumber(1234567.89, { diff --git a/src/panels/profile/ha-pick-theme-row.ts b/src/panels/profile/ha-pick-theme-row.ts index 0dcdaca9dd..1677ff7e15 100644 --- a/src/panels/profile/ha-pick-theme-row.ts +++ b/src/panels/profile/ha-pick-theme-row.ts @@ -23,6 +23,9 @@ import { import { HomeAssistant } from "../../types"; import { documentationUrl } from "../../util/documentation-url"; +const BACKEND_SELECTED_THEME = "Backend-selected"; +const DEFAULT_THEME = "default"; + @customElement("ha-pick-theme-row") export class HaPickThemeRow extends LitElement { @property({ attribute: false }) public hass!: HomeAssistant; @@ -65,16 +68,24 @@ export class HaPickThemeRow extends LitElement { + + ${this.hass.localize("ui.panel.profile.themes.backend-selected")} + + + ${this.hass.localize("ui.panel.profile.themes.default")} + ${this._themeNames.map( - (theme) => - html`${theme}` + (theme) => html` + ${theme} + ` )} - ${curTheme === "default" || this._supportsModeSelection(curTheme) + ${curTheme === DEFAULT_THEME || this._supportsModeSelection(curTheme) ? html`
- ${curTheme === "default" + ${curTheme === DEFAULT_THEME ? html`
${Object.values(TimeFormat).map((format) => { const formattedTime = formatTime(date, { diff --git a/src/translations/en.json b/src/translations/en.json index 1255298cec..8b8e01ef28 100755 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -4711,7 +4711,9 @@ }, "primary_color": "Primary color", "accent_color": "Accent color", - "reset": "Reset" + "reset": "Reset", + "backend-selected": "Use backend preferred theme", + "default": "Default" }, "dashboard": { "header": "Dashboard", diff --git a/src/util/legacy-support.js b/src/util/legacy-support.js index c94ffffdb0..869ecbb4f7 100644 --- a/src/util/legacy-support.js +++ b/src/util/legacy-support.js @@ -16,7 +16,9 @@ const handler = { console.warn(message); document .querySelector("home-assistant") - .dispatchEvent(new CustomEvent("write_log", { detail: { message } })); + .dispatchEvent( + new CustomEvent("write_log", { detail: { message, level: "warning" } }) + ); return Reflect.get(target, prop, receiver); }, apply: function (target, thisArg, argumentsList) { @@ -24,7 +26,9 @@ const handler = { console.warn(message); document .querySelector("home-assistant") - .dispatchEvent(new CustomEvent("write_log", { detail: { message } })); + .dispatchEvent( + new CustomEvent("write_log", { detail: { message, level: "warning" } }) + ); return Reflect.apply(target, thisArg, argumentsList); }, }; diff --git a/yarn.lock b/yarn.lock index 38cd20991c..9804be8337 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5179,10 +5179,10 @@ __metadata: languageName: node linkType: hard -"@webcomponents/scoped-custom-element-registry@npm:0.0.8": - version: 0.0.8 - resolution: "@webcomponents/scoped-custom-element-registry@npm:0.0.8" - checksum: b758bd34723834eedeabe53decadeaa3a655a0fb6b264f1ad55d9fb617f1cff44c72f7722e78cfb953a82c06f85c1e640929d65e775df85202c34419091d2cdf +"@webcomponents/scoped-custom-element-registry@npm:0.0.9": + version: 0.0.9 + resolution: "@webcomponents/scoped-custom-element-registry@npm:0.0.9" + checksum: 02595d7b184a04fab16b12599f80a3e405ed9cee347e42079423bf4b7d4c794201d1553d818c0663d83c8b047a77eec7b32318267927673bd4218eb0f75a1b5e languageName: node linkType: hard @@ -5193,10 +5193,10 @@ __metadata: languageName: node linkType: hard -"@webcomponents/webcomponentsjs@npm:2.7.0": - version: 2.7.0 - resolution: "@webcomponents/webcomponentsjs@npm:2.7.0" - checksum: df60a5faf79d85eba334a42f7bffb101f0e01a8ee8cc3705f17c67c70247415c4440ad6e8fdd3c74dc5be097a436c4e4831824bef039a12b69f05e84a07187b7 +"@webcomponents/webcomponentsjs@npm:2.8.0": + version: 2.8.0 + resolution: "@webcomponents/webcomponentsjs@npm:2.8.0" + checksum: 186373c0308a35abf4e97228b1cc5a5dc5c694701ccef2046baa1598636b08be5ec16baff2770584fef1aa241837fc36316f0b415396202eb444b36aa1a01117 languageName: node linkType: hard @@ -9535,8 +9535,8 @@ __metadata: "@vue/web-component-wrapper": 1.3.0 "@web/dev-server": 0.1.37 "@web/dev-server-rollup": 0.4.0 - "@webcomponents/scoped-custom-element-registry": 0.0.8 - "@webcomponents/webcomponentsjs": 2.7.0 + "@webcomponents/scoped-custom-element-registry": 0.0.9 + "@webcomponents/webcomponentsjs": 2.8.0 app-datepicker: 5.1.1 babel-loader: 9.1.2 babel-plugin-template-html-minifier: 4.1.0 @@ -9633,7 +9633,7 @@ __metadata: vue2-daterange-picker: 0.6.8 webpack: =5.72.1 webpack-cli: 5.0.1 - webpack-dev-server: 4.13.1 + webpack-dev-server: 4.13.2 webpack-manifest-plugin: 5.0.0 webpackbar: 5.0.2 weekstart: 2.0.0 @@ -15863,9 +15863,9 @@ __metadata: languageName: node linkType: hard -"webpack-dev-server@npm:4.13.1": - version: 4.13.1 - resolution: "webpack-dev-server@npm:4.13.1" +"webpack-dev-server@npm:4.13.2": + version: 4.13.2 + resolution: "webpack-dev-server@npm:4.13.2" dependencies: "@types/bonjour": ^3.5.9 "@types/connect-history-api-fallback": ^1.3.5 @@ -15906,7 +15906,7 @@ __metadata: optional: true bin: webpack-dev-server: bin/webpack-dev-server.js - checksum: f70611544b7d964a31eb3d934d7c2b376b97e6927a89e03b2e21cfa5812bb639625cd18fd350de1604ba6c455b324135523a894032f28c69d90d90682e4f3b7d + checksum: 9bf573abf05b0e0f1e8219820f6264e25a0f8ee6aebed3c0d0449c24a37f88b575972e0a2bec426112ee37d48c8f5090e7754aa1873206d3c9b6344a54718232 languageName: node linkType: hard