20241127.2 (#23104)

This commit is contained in:
Bram Kragten 2024-12-02 21:39:46 +01:00 committed by GitHub
commit 1ebd13027c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 133 additions and 51 deletions

View File

@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project]
name = "home-assistant-frontend"
version = "20241127.1"
version = "20241127.2"
license = {text = "Apache-2.0"}
description = "The Home Assistant frontend"
readme = "README.md"

View File

@ -226,6 +226,25 @@ export const fetchHassioLogsFollow = async (
signal
);
export const fetchHassioLogsFollowSkip = async (
hass: HomeAssistant,
provider: string,
signal: AbortSignal,
cursor: string,
skipLines: number,
lines = 100,
boot = 0
) =>
hass.callApiRaw(
"GET",
`hassio/${provider.includes("_") ? `addons/${provider}` : provider}/logs${boot !== 0 ? `/boots/${boot}` : ""}/follow`,
undefined,
{
Range: `entries=${cursor}:${skipLines}:${lines}`,
},
signal
);
export const getHassioLogDownloadUrl = (provider: string) =>
`/api/hassio/${
provider.includes("_") ? `addons/${provider}` : provider

View File

@ -37,6 +37,8 @@ export class HaVoiceAssistantSetupStepLocal extends LitElement {
@state() private _detailState?: string;
@state() private _error?: string;
@state() private _localTts?: EntityRegistryDisplayEntry[];
@state() private _localStt?: EntityRegistryDisplayEntry[];
@ -62,6 +64,7 @@ export class HaVoiceAssistantSetupStepLocal extends LitElement {
alt="Casita Home Assistant error logo"
/>
<h1>Failed to install add-ons</h1>
<p>${this._error}</p>
<p>
We could not automatically install a local TTS and STT provider
for you. Read the documentation to learn how to install them.
@ -179,8 +182,9 @@ export class HaVoiceAssistantSetupStepLocal extends LitElement {
}
this._detailState = "Creating assistant";
await this._findEntitiesAndCreatePipeline();
} catch (e) {
} catch (e: any) {
this._state = "ERROR";
this._error = e.message;
}
}
@ -199,11 +203,13 @@ export class HaVoiceAssistantSetupStepLocal extends LitElement {
private async _setupConfigEntry(addon: string) {
const configFlow = await createConfigFlow(this.hass, "wyoming");
const step = await handleConfigFlowStep(this.hass, configFlow.flow_id, {
host: `core_${addon}`,
host: `core-${addon}`,
port: addon === "piper" ? 10200 : 10300,
});
if (step.type !== "create_entry") {
throw new Error("Failed to create entry");
throw new Error(
`Failed to create entry for ${addon}${"errors" in step ? `: ${step.errors.base}` : ""}`
);
}
}
@ -321,7 +327,7 @@ export class HaVoiceAssistantSetupStepLocal extends LitElement {
this._findLocalEntities();
if (!this._localTts?.length || !this._localStt?.length) {
if (tryNo > 3) {
throw new Error("Timeout searching for local TTS and STT entities");
throw new Error("Could not find local TTS and STT entities");
}
await new Promise<void>((resolve) => {
setTimeout(resolve, 2000);

View File

@ -134,12 +134,13 @@ export class HaVoiceCommandDialog extends LitElement {
const controlHA = !this._pipeline
? false
: this.hass.states[this._pipeline?.conversation_engine]
: this._pipeline.prefer_local_intents ||
(this.hass.states[this._pipeline.conversation_engine]
? supportsFeature(
this.hass.states[this._pipeline?.conversation_engine],
this.hass.states[this._pipeline.conversation_engine],
ConversationEntityFeature.CONTROL
)
: true;
: true);
const supportsMicrophone = AudioRecorder.isSupported;
const supportsSTT = this._pipeline?.stt_engine;

View File

@ -50,6 +50,7 @@ import {
fetchHassioBoots,
fetchHassioLogs,
fetchHassioLogsFollow,
fetchHassioLogsFollowSkip,
fetchHassioLogsLegacy,
getHassioLogDownloadLinesUrl,
getHassioLogDownloadUrl,
@ -428,13 +429,21 @@ class ErrorLogCard extends LitElement {
}
}
private async _loadLogs(): Promise<void> {
private async _loadLogs(retry = false): Promise<void> {
this._error = undefined;
this._loadingState = "loading";
this._numberOfLines = retry ? (this._numberOfLines ?? 0) : 0;
if (!retry) {
this._loadingPrevState = undefined;
this._firstCursor = undefined;
this._numberOfLines = 0;
this._ansiToHtmlElement?.clear();
}
const streamLogs =
this._streamSupported &&
isComponentLoaded(this.hass, "hassio") &&
this.provider;
try {
if (this._logStreamAborter) {
@ -442,17 +451,14 @@ class ErrorLogCard extends LitElement {
this._logStreamAborter = undefined;
}
if (
this._streamSupported &&
isComponentLoaded(this.hass, "hassio") &&
this.provider
) {
if (streamLogs) {
this._logStreamAborter = new AbortController();
if (!retry) {
// check if there are any logs at all
const testResponse = await fetchHassioLogs(
this.hass,
this.provider,
this.provider!,
`entries=:-1:`,
this._boot
);
@ -460,14 +466,29 @@ class ErrorLogCard extends LitElement {
if (!testLogs.trim()) {
this._loadingState = "empty";
}
}
const response = await fetchHassioLogsFollow(
let response: Response;
if (retry && this._firstCursor) {
response = await fetchHassioLogsFollowSkip(
this.hass,
this.provider,
this.provider!,
this._logStreamAborter.signal,
this._firstCursor,
this._numberOfLines,
NUMBER_OF_LINES,
this._boot
);
} else {
response = await fetchHassioLogsFollow(
this.hass,
this.provider!,
this._logStreamAborter.signal,
NUMBER_OF_LINES,
this._boot
);
}
if (response.headers.has("X-First-Cursor")) {
this._firstCursor = response.headers.get("X-First-Cursor")!;
@ -524,7 +545,7 @@ class ErrorLogCard extends LitElement {
if (!this._downloadSupported) {
const downloadUrl = getHassioLogDownloadLinesUrl(
this.provider,
this.provider!,
this._numberOfLines,
this._boot
);
@ -532,6 +553,9 @@ class ErrorLogCard extends LitElement {
this._logsFileLink = signedUrl.path;
});
}
// first chunk loads successfully, reset retry param
retry = false;
}
}
} else {
@ -554,6 +578,13 @@ class ErrorLogCard extends LitElement {
if (err.name === "AbortError") {
return;
}
// The stream can fail if the connection is lost or firefox service worker intercept the connection
if (!retry && streamLogs) {
this._loadLogs(true);
return;
}
this._error = (this.localizeFunc || this.hass.localize)(
"ui.panel.config.logs.failed_get_logs",
{
@ -590,9 +621,10 @@ class ErrorLogCard extends LitElement {
private _handleConnectionStatus = (ev: HASSDomEvent<ConnectionStatus>) => {
if (ev.detail === "disconnected" && this._logStreamAborter) {
this._logStreamAborter.abort();
this._loadingState = "loading";
}
if (ev.detail === "connected") {
this._loadLogs();
this._loadLogs(true);
}
};

View File

@ -7,7 +7,9 @@ import {
mdiContentSave,
mdiDelete,
mdiDotsVertical,
mdiEye,
mdiInformationOutline,
mdiMotionPlayOutline,
mdiPlay,
mdiTag,
} from "@mdi/js";
@ -204,6 +206,14 @@ export class HaSceneEditor extends SubscribeMixin(
}
);
public connectedCallback() {
super.connectedCallback();
if (!this.sceneId) {
this._mode = "live";
this._subscribeEvents();
}
}
public disconnectedCallback() {
super.disconnectedCallback();
if (this._unsubscribeEvents) {
@ -387,15 +397,22 @@ export class HaSceneEditor extends SubscribeMixin(
alert-type="info"
.narrow=${this.narrow}
.title=${this.hass.localize(
`ui.panel.config.scene.editor.${this._mode === "live" ? "live_preview" : "review_mode"}`
`ui.panel.config.scene.editor.${this._mode === "live" ? "live_edit" : "review_mode"}`
)}
>
${this.hass.localize(
`ui.panel.config.scene.editor.${this._mode === "live" ? "live_preview_detail" : "review_mode_detail"}`
`ui.panel.config.scene.editor.${this._mode === "live" ? "live_edit_detail" : "review_mode_detail"}`
)}
<span slot="icon">
<ha-svg-icon
.path=${this._mode === "live"
? mdiMotionPlayOutline
: mdiEye}
></ha-svg-icon>
</span>
<ha-button slot="action" @click=${this._toggleLiveMode}>
${this.hass.localize(
`ui.panel.config.scene.editor.${this._mode === "live" ? "back_to_review_mode" : "live_preview"}`
`ui.panel.config.scene.editor.${this._mode === "live" ? "switch_to_review_mode" : "live_edit"}`
)}
</ha-button>
</ha-alert>
@ -542,6 +559,7 @@ export class HaSceneEditor extends SubscribeMixin(
}
return html`
<ha-list-item
class="entity"
hasMeta
.graphic=${this._mode === "live"
? "icon"
@ -759,13 +777,15 @@ export class HaSceneEditor extends SubscribeMixin(
text: this.hass.localize(
"ui.panel.config.scene.editor.enter_live_mode_unsaved"
),
confirmText: this.hass!.localize("ui.common.continue"),
destructive: true,
confirmText: this.hass!.localize(
"ui.panel.config.scene.editor.save_before_live"
),
dismissText: this.hass!.localize("ui.common.cancel"),
});
if (!result) {
return;
}
await this._saveScene();
}
this._entities.forEach((entity) => this._storeState(entity));
@ -1309,6 +1329,9 @@ export class HaSceneEditor extends SubscribeMixin(
li[role="separator"] {
border-bottom-color: var(--divider-color);
}
ha-list-item.entity {
padding-right: 28px;
}
`,
];
}

View File

@ -68,7 +68,7 @@ export class HuiHistoryGraphCardEditor
{
name: "hours_to_show",
default: DEFAULT_HOURS_TO_SHOW,
selector: { number: { min: 1, mode: "box" } },
selector: { number: { min: 0, step: "any", mode: "box" } },
},
],
},

View File

@ -3878,11 +3878,12 @@
},
"editor": {
"review_mode": "Review Mode",
"review_mode_detail": "You can adjust the scene's details and remove devices or entities. To fully edit, switch to Live Preview, which will apply the scene.",
"live_preview": "Live Preview",
"live_preview_detail": "In Live Preview, all changes to this scene are applied in real-time to your devices and entities.",
"enter_live_mode_unsaved": "You have unsaved changes to this scene. Continuing to live preview will apply the saved scene, which may overwrite your unsaved changes. Consider if you would like to save the scene first before activating it.",
"back_to_review_mode": "Back to review mode",
"review_mode_detail": "You can adjust the scene's details and remove devices or entities. To fully edit, switch to Live Edit, which will apply the scene.",
"live_edit": "Live Edit",
"live_edit_detail": "In Live Edit, all changes to this scene are applied in real-time to your devices and entities.",
"enter_live_mode_unsaved": "Before proceeding to Live Edit, please save your current changes.",
"save_before_live": "Save and Live Edit",
"switch_to_review_mode": "Switch to review mode",
"default_name": "New scene",
"load_error_not_editable": "Only scenes in scenes.yaml are editable.",
"load_error_unknown": "Error loading scene ({err_no}).",