mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-25 18:26:35 +00:00
Ingress: offer to start addon on ingress page (#16458)
Co-authored-by: Joakim Sørensen <ludeeus@ludeeus.dev>
This commit is contained in:
parent
343708cdaa
commit
b15754a6a7
@ -114,11 +114,22 @@ class HassioAddonInfo extends LitElement {
|
|||||||
|
|
||||||
@state() private _error?: string;
|
@state() private _error?: string;
|
||||||
|
|
||||||
|
private _fetchDataTimeout?: number;
|
||||||
|
|
||||||
private _addonStoreInfo = memoizeOne(
|
private _addonStoreInfo = memoizeOne(
|
||||||
(slug: string, storeAddons: StoreAddon[]) =>
|
(slug: string, storeAddons: StoreAddon[]) =>
|
||||||
storeAddons.find((addon) => addon.slug === slug)
|
storeAddons.find((addon) => addon.slug === slug)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
public disconnectedCallback() {
|
||||||
|
super.disconnectedCallback();
|
||||||
|
|
||||||
|
if (this._fetchDataTimeout) {
|
||||||
|
clearInterval(this._fetchDataTimeout);
|
||||||
|
this._fetchDataTimeout = undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
const addonStoreInfo =
|
const addonStoreInfo =
|
||||||
!this.addon.detached && !this.addon.available
|
!this.addon.detached && !this.addon.available
|
||||||
@ -592,7 +603,10 @@ class HassioAddonInfo extends LitElement {
|
|||||||
</ha-progress-button>
|
</ha-progress-button>
|
||||||
`
|
`
|
||||||
: html`
|
: html`
|
||||||
<ha-progress-button @click=${this._startClicked}>
|
<ha-progress-button
|
||||||
|
@click=${this._startClicked}
|
||||||
|
.progress=${this.addon.state === "startup"}
|
||||||
|
>
|
||||||
${this.supervisor.localize("addon.dashboard.start")}
|
${this.supervisor.localize("addon.dashboard.start")}
|
||||||
</ha-progress-button>
|
</ha-progress-button>
|
||||||
`
|
`
|
||||||
@ -672,9 +686,36 @@ class HassioAddonInfo extends LitElement {
|
|||||||
super.updated(changedProps);
|
super.updated(changedProps);
|
||||||
if (changedProps.has("addon")) {
|
if (changedProps.has("addon")) {
|
||||||
this._loadData();
|
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> {
|
private async _loadData(): Promise<void> {
|
||||||
if ("state" in this.addon && this.addon.state === "started") {
|
if ("state" in this.addon && this.addon.state === "started") {
|
||||||
this._metrics = await fetchHassioStats(
|
this._metrics = await fetchHassioStats(
|
||||||
|
@ -16,6 +16,7 @@ import "../../../src/components/ha-icon-button";
|
|||||||
import {
|
import {
|
||||||
fetchHassioAddonInfo,
|
fetchHassioAddonInfo,
|
||||||
HassioAddonDetails,
|
HassioAddonDetails,
|
||||||
|
startHassioAddon,
|
||||||
} from "../../../src/data/hassio/addon";
|
} from "../../../src/data/hassio/addon";
|
||||||
import { extractApiErrorMessage } from "../../../src/data/hassio/common";
|
import { extractApiErrorMessage } from "../../../src/data/hassio/common";
|
||||||
import {
|
import {
|
||||||
@ -23,7 +24,10 @@ import {
|
|||||||
validateHassioSession,
|
validateHassioSession,
|
||||||
} from "../../../src/data/hassio/ingress";
|
} from "../../../src/data/hassio/ingress";
|
||||||
import { Supervisor } from "../../../src/data/supervisor/supervisor";
|
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-loading-screen";
|
||||||
import "../../../src/layouts/hass-subpage";
|
import "../../../src/layouts/hass-subpage";
|
||||||
import { HomeAssistant, Route } from "../../../src/types";
|
import { HomeAssistant, Route } from "../../../src/types";
|
||||||
@ -45,6 +49,8 @@ class HassioIngressView extends LitElement {
|
|||||||
|
|
||||||
private _sessionKeepAlive?: number;
|
private _sessionKeepAlive?: number;
|
||||||
|
|
||||||
|
private _fetchDataTimeout?: number;
|
||||||
|
|
||||||
public disconnectedCallback() {
|
public disconnectedCallback() {
|
||||||
super.disconnectedCallback();
|
super.disconnectedCallback();
|
||||||
|
|
||||||
@ -52,16 +58,21 @@ class HassioIngressView extends LitElement {
|
|||||||
clearInterval(this._sessionKeepAlive);
|
clearInterval(this._sessionKeepAlive);
|
||||||
this._sessionKeepAlive = undefined;
|
this._sessionKeepAlive = undefined;
|
||||||
}
|
}
|
||||||
|
if (this._fetchDataTimeout) {
|
||||||
|
clearInterval(this._fetchDataTimeout);
|
||||||
|
this._fetchDataTimeout = undefined;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
if (!this._addon) {
|
if (!this._addon) {
|
||||||
return html` <hass-loading-screen></hass-loading-screen> `;
|
return html`<hass-loading-screen></hass-loading-screen>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
const iframe = html`<iframe
|
const iframe = html`<iframe
|
||||||
title=${this._addon.name}
|
title=${this._addon.name}
|
||||||
src=${this._addon.ingress_url!}
|
src=${this._addon.ingress_url!}
|
||||||
|
@load=${this._checkLoaded}
|
||||||
>
|
>
|
||||||
</iframe>`;
|
</iframe>`;
|
||||||
|
|
||||||
@ -132,10 +143,10 @@ class HassioIngressView extends LitElement {
|
|||||||
return;
|
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 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) {
|
if (addon && addon !== oldAddon) {
|
||||||
this._fetchData(addon);
|
this._fetchData(addon);
|
||||||
@ -145,33 +156,23 @@ class HassioIngressView extends LitElement {
|
|||||||
private async _fetchData(addonSlug: string) {
|
private async _fetchData(addonSlug: string) {
|
||||||
const createSessionPromise = createHassioSession(this.hass);
|
const createSessionPromise = createHassioSession(this.hass);
|
||||||
|
|
||||||
let addon;
|
let addon: HassioAddonDetails;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
addon = await fetchHassioAddonInfo(this.hass, addonSlug);
|
addon = await fetchHassioAddonInfo(this.hass, addonSlug);
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
await showAlertDialog(this, {
|
await showAlertDialog(this, {
|
||||||
text: "Unable to fetch add-on info to start Ingress",
|
text: this.supervisor.localize("ingress.error_addon_info"),
|
||||||
title: "Supervisor",
|
title: "Supervisor",
|
||||||
});
|
});
|
||||||
await nextRender();
|
await nextRender();
|
||||||
history.back();
|
navigate("/hassio/store", { replace: true });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!addon.ingress_url) {
|
if (!addon.version) {
|
||||||
await showAlertDialog(this, {
|
await showAlertDialog(this, {
|
||||||
text: "Add-on does not support Ingress",
|
text: this.supervisor.localize("ingress.error_addon_not_installed"),
|
||||||
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",
|
|
||||||
title: addon.name,
|
title: addon.name,
|
||||||
});
|
});
|
||||||
await nextRender();
|
await nextRender();
|
||||||
@ -179,13 +180,74 @@ class HassioIngressView extends LitElement {
|
|||||||
return;
|
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 {
|
try {
|
||||||
session = await createSessionPromise;
|
session = await createSessionPromise;
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
|
if (this._sessionKeepAlive) {
|
||||||
|
clearInterval(this._sessionKeepAlive);
|
||||||
|
}
|
||||||
await showAlertDialog(this, {
|
await showAlertDialog(this, {
|
||||||
text: "Unable to create an Ingress session",
|
text: this.supervisor.localize("ingress.error_creating_session"),
|
||||||
title: addon.name,
|
title: addon.name,
|
||||||
});
|
});
|
||||||
await nextRender();
|
await nextRender();
|
||||||
@ -207,6 +269,31 @@ class HassioIngressView extends LitElement {
|
|||||||
this._addon = addon;
|
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 {
|
private _toggleMenu(): void {
|
||||||
fireEvent(this, "hass-toggle-menu");
|
fireEvent(this, "hass-toggle-menu");
|
||||||
}
|
}
|
||||||
|
@ -23,7 +23,13 @@ export type AddonStartup =
|
|||||||
| "services"
|
| "services"
|
||||||
| "application"
|
| "application"
|
||||||
| "once";
|
| "once";
|
||||||
export type AddonState = "started" | "stopped" | null;
|
export type AddonState =
|
||||||
|
| "startup"
|
||||||
|
| "started"
|
||||||
|
| "stopped"
|
||||||
|
| "unknown"
|
||||||
|
| "error"
|
||||||
|
| null;
|
||||||
export type AddonRepository = "core" | "local" | string;
|
export type AddonRepository = "core" | "local" | string;
|
||||||
|
|
||||||
interface AddonTranslations {
|
interface AddonTranslations {
|
||||||
|
@ -5834,10 +5834,20 @@
|
|||||||
"error": "[%key:ui::panel::my::error%]",
|
"error": "[%key:ui::panel::my::error%]",
|
||||||
"error_addon_not_found": "Add-on not found",
|
"error_addon_not_found": "Add-on not found",
|
||||||
"error_repository_not_found": "The required repository for this Add-on was 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_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"
|
"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": {
|
"system": {
|
||||||
"log": {
|
"log": {
|
||||||
"log_provider": "Log Provider",
|
"log_provider": "Log Provider",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user