Ingress: offer to start addon on ingress page (#16458)

Co-authored-by: Joakim Sørensen <ludeeus@ludeeus.dev>
This commit is contained in:
Bram Kragten 2023-06-27 18:49:24 +02:00 committed by GitHub
parent 343708cdaa
commit b15754a6a7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 168 additions and 24 deletions

View File

@ -114,11 +114,22 @@ class HassioAddonInfo extends LitElement {
@state() private _error?: string;
private _fetchDataTimeout?: number;
private _addonStoreInfo = memoizeOne(
(slug: string, storeAddons: StoreAddon[]) =>
storeAddons.find((addon) => addon.slug === slug)
);
public disconnectedCallback() {
super.disconnectedCallback();
if (this._fetchDataTimeout) {
clearInterval(this._fetchDataTimeout);
this._fetchDataTimeout = undefined;
}
}
protected render(): TemplateResult {
const addonStoreInfo =
!this.addon.detached && !this.addon.available
@ -592,7 +603,10 @@ class HassioAddonInfo extends LitElement {
</ha-progress-button>
`
: html`
<ha-progress-button @click=${this._startClicked}>
<ha-progress-button
@click=${this._startClicked}
.progress=${this.addon.state === "startup"}
>
${this.supervisor.localize("addon.dashboard.start")}
</ha-progress-button>
`
@ -672,9 +686,36 @@ class HassioAddonInfo extends LitElement {
super.updated(changedProps);
if (changedProps.has("addon")) {
this._loadData();
if (
!this._fetchDataTimeout &&
this.addon &&
"state" in this.addon &&
this.addon.state === "startup"
) {
// Addon is starting up, wait for it to start
this._scheduleDataUpdate();
}
}
}
private _scheduleDataUpdate() {
this._fetchDataTimeout = window.setTimeout(async () => {
const addon = await fetchHassioAddonInfo(this.hass, this.addon.slug);
if (addon.state !== "startup") {
this._fetchDataTimeout = undefined;
this.addon = addon;
const eventdata = {
success: true,
response: undefined,
path: "start",
};
fireEvent(this, "hass-api-called", eventdata);
} else {
this._scheduleDataUpdate();
}
}, 500);
}
private async _loadData(): Promise<void> {
if ("state" in this.addon && this.addon.state === "started") {
this._metrics = await fetchHassioStats(

View File

@ -16,6 +16,7 @@ import "../../../src/components/ha-icon-button";
import {
fetchHassioAddonInfo,
HassioAddonDetails,
startHassioAddon,
} from "../../../src/data/hassio/addon";
import { extractApiErrorMessage } from "../../../src/data/hassio/common";
import {
@ -23,7 +24,10 @@ import {
validateHassioSession,
} from "../../../src/data/hassio/ingress";
import { Supervisor } from "../../../src/data/supervisor/supervisor";
import { showAlertDialog } from "../../../src/dialogs/generic/show-dialog-box";
import {
showAlertDialog,
showConfirmationDialog,
} from "../../../src/dialogs/generic/show-dialog-box";
import "../../../src/layouts/hass-loading-screen";
import "../../../src/layouts/hass-subpage";
import { HomeAssistant, Route } from "../../../src/types";
@ -45,6 +49,8 @@ class HassioIngressView extends LitElement {
private _sessionKeepAlive?: number;
private _fetchDataTimeout?: number;
public disconnectedCallback() {
super.disconnectedCallback();
@ -52,16 +58,21 @@ class HassioIngressView extends LitElement {
clearInterval(this._sessionKeepAlive);
this._sessionKeepAlive = undefined;
}
if (this._fetchDataTimeout) {
clearInterval(this._fetchDataTimeout);
this._fetchDataTimeout = undefined;
}
}
protected render(): TemplateResult {
if (!this._addon) {
return html` <hass-loading-screen></hass-loading-screen> `;
return html`<hass-loading-screen></hass-loading-screen>`;
}
const iframe = html`<iframe
title=${this._addon.name}
src=${this._addon.ingress_url!}
@load=${this._checkLoaded}
>
</iframe>`;
@ -132,10 +143,10 @@ class HassioIngressView extends LitElement {
return;
}
const addon = this.route.path.substr(1);
const addon = this.route.path.substring(1);
const oldRoute = changedProps.get("route") as this["route"] | undefined;
const oldAddon = oldRoute ? oldRoute.path.substr(1) : undefined;
const oldAddon = oldRoute ? oldRoute.path.substring(1) : undefined;
if (addon && addon !== oldAddon) {
this._fetchData(addon);
@ -145,33 +156,23 @@ class HassioIngressView extends LitElement {
private async _fetchData(addonSlug: string) {
const createSessionPromise = createHassioSession(this.hass);
let addon;
let addon: HassioAddonDetails;
try {
addon = await fetchHassioAddonInfo(this.hass, addonSlug);
} catch (err: any) {
await showAlertDialog(this, {
text: "Unable to fetch add-on info to start Ingress",
text: this.supervisor.localize("ingress.error_addon_info"),
title: "Supervisor",
});
await nextRender();
history.back();
navigate("/hassio/store", { replace: true });
return;
}
if (!addon.ingress_url) {
if (!addon.version) {
await showAlertDialog(this, {
text: "Add-on does not support Ingress",
title: addon.name,
});
await nextRender();
history.back();
return;
}
if (addon.state !== "started") {
await showAlertDialog(this, {
text: "Add-on is not running. Please start it first",
text: this.supervisor.localize("ingress.error_addon_not_installed"),
title: addon.name,
});
await nextRender();
@ -179,13 +180,74 @@ class HassioIngressView extends LitElement {
return;
}
let session;
if (!addon.ingress_url) {
await showAlertDialog(this, {
text: this.supervisor.localize("ingress.error_addon_not_supported"),
title: addon.name,
});
await nextRender();
history.back();
return;
}
if (!addon.state || !["startup", "started"].includes(addon.state)) {
const confirm = await showConfirmationDialog(this, {
text: this.supervisor.localize("ingress.error_addon_not_running"),
title: addon.name,
confirmText: this.supervisor.localize("ingress.start_addon"),
dismissText: this.supervisor.localize("common.no"),
});
if (confirm) {
try {
await startHassioAddon(this.hass, addonSlug);
fireEvent(this, "supervisor-collection-refresh", {
collection: "addon",
});
this._fetchData(addonSlug);
return;
} catch (e) {
await showAlertDialog(this, {
text: this.supervisor.localize("ingress.error_starting_addon"),
title: addon.name,
});
await nextRender();
navigate(`/hassio/addon/${addon.slug}/logs`, { replace: true });
return;
}
} else {
await nextRender();
navigate(`/hassio/addon/${addon.slug}/info`, { replace: true });
return;
}
}
if (addon.state === "startup") {
// Addon is starting up, wait for it to start
this._fetchDataTimeout = window.setTimeout(() => {
this._fetchData(addonSlug);
}, 500);
return;
}
if (addon.state !== "started") {
return;
}
if (this._fetchDataTimeout) {
clearInterval(this._fetchDataTimeout);
this._fetchDataTimeout = undefined;
}
let session: string;
try {
session = await createSessionPromise;
} catch (err: any) {
if (this._sessionKeepAlive) {
clearInterval(this._sessionKeepAlive);
}
await showAlertDialog(this, {
text: "Unable to create an Ingress session",
text: this.supervisor.localize("ingress.error_creating_session"),
title: addon.name,
});
await nextRender();
@ -207,6 +269,31 @@ class HassioIngressView extends LitElement {
this._addon = addon;
}
private _checkLoaded(ev): void {
if (!this._addon) {
return;
}
if (ev.target.contentDocument.body.textContent === "502: Bad Gateway") {
showConfirmationDialog(this, {
text: this.supervisor.localize("ingress.error_addon_not_ready"),
title: this._addon.name,
confirmText: this.supervisor.localize("ingress.retry"),
dismissText: this.supervisor.localize("common.no"),
confirm: async () => {
const addon = this._addon;
this._addon = undefined;
await Promise.all([
this.updateComplete,
new Promise((resolve) => {
setTimeout(resolve, 500);
}),
]);
this._addon = addon;
},
});
}
}
private _toggleMenu(): void {
fireEvent(this, "hass-toggle-menu");
}

View File

@ -23,7 +23,13 @@ export type AddonStartup =
| "services"
| "application"
| "once";
export type AddonState = "started" | "stopped" | null;
export type AddonState =
| "startup"
| "started"
| "stopped"
| "unknown"
| "error"
| null;
export type AddonRepository = "core" | "local" | string;
interface AddonTranslations {

View File

@ -5834,10 +5834,20 @@
"error": "[%key:ui::panel::my::error%]",
"error_addon_not_found": "Add-on not found",
"error_repository_not_found": "The required repository for this Add-on was not found",
"error_addon_not_started": "The requested add-on is not running. Please start it first",
"error_addon_not_installed": "The requested add-on is not installed. Please install it first",
"error_addon_no_ingress": "The requested add-on does not support ingress"
},
"ingress": {
"error_addon_info": "Unable to fetch add-on info to start Ingress",
"error_addon_not_installed": "The add-on is not installed. Please install it first",
"error_addon_not_supported": "This add-on does not support Ingress",
"error_addon_not_running": "Add-on is not running. Do you want to start it now?",
"start_addon": "Start add-on",
"error_starting_addon": "Error starting the add-on",
"error_creating_session": "Unable to create an Ingress session",
"error_addon_not_ready": "The add-on seems to not be ready, it might still be starting. Do you want to try again?",
"retry": "Retry"
},
"system": {
"log": {
"log_provider": "Log Provider",