From 5fa001219536fcdc3253708e7d570eee9819158e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joakim=20S=C3=B8rensen?= Date: Thu, 3 Sep 2020 16:38:59 +0200 Subject: [PATCH] hassio-addon-info feedback (#6734) * hassio-addon-info feedback * lint * init config validation * better error * Finish * sort imports * Use startup type for watchdog * Only show error if issue with config * Adjust --- .../src/addon-view/info/hassio-addon-info.ts | 127 +++++++++++++----- src/data/hassio/addon.ts | 14 ++ src/data/hassio/common.ts | 8 ++ 3 files changed, 118 insertions(+), 31 deletions(-) diff --git a/hassio/src/addon-view/info/hassio-addon-info.ts b/hassio/src/addon-view/info/hassio-addon-info.ts index 28343352c7..a17c7cc86c 100644 --- a/hassio/src/addon-view/info/hassio-addon-info.ts +++ b/hassio/src/addon-view/info/hassio-addon-info.ts @@ -38,15 +38,22 @@ import "../../../../src/components/ha-svg-icon"; import "../../../../src/components/ha-switch"; import { fetchHassioAddonChangelog, + fetchHassioAddonInfo, HassioAddonDetails, HassioAddonSetOptionParams, HassioAddonSetSecurityParams, installHassioAddon, setHassioAddonOption, setHassioAddonSecurity, + startHassioAddon, uninstallHassioAddon, + validateHassioAddonOption, } from "../../../../src/data/hassio/addon"; -import { showConfirmationDialog } from "../../../../src/dialogs/generic/show-dialog-box"; +import { extractApiErrorMessage } from "../../../../src/data/hassio/common"; +import { + showAlertDialog, + showConfirmationDialog, +} from "../../../../src/dialogs/generic/show-dialog-box"; import { haStyle } from "../../../../src/resources/styles"; import { HomeAssistant } from "../../../../src/types"; import "../../components/hassio-card-content"; @@ -126,8 +133,6 @@ class HassioAddonInfo extends LitElement { @internalProperty() private _error?: string; - @property({ type: Boolean }) private _installing = false; - protected render(): TemplateResult { return html` ${this._computeUpdateAvailable @@ -400,7 +405,7 @@ class HassioAddonInfo extends LitElement { > - ${this.hass.userData?.showAdvanced + ${this.addon.startup !== "once" ? html` @@ -498,12 +503,9 @@ class HassioAddonInfo extends LitElement { ` : html` - + Start - + `} ${this._computeShowWebUI ? html` @@ -527,12 +529,12 @@ class HassioAddonInfo extends LitElement { ` : ""} - Uninstall - + ${this.addon.build ? html` Install @@ -662,7 +663,9 @@ class HassioAddonInfo extends LitElement { }; fireEvent(this, "hass-api-called", eventdata); } catch (err) { - this._error = `Failed to set addon option, ${err.body?.message || err}`; + this._error = `Failed to set addon option, ${extractApiErrorMessage( + err + )}`; } } @@ -680,7 +683,9 @@ class HassioAddonInfo extends LitElement { }; fireEvent(this, "hass-api-called", eventdata); } catch (err) { - this._error = `Failed to set addon option, ${err.body?.message || err}`; + this._error = `Failed to set addon option, ${extractApiErrorMessage( + err + )}`; } } @@ -698,7 +703,9 @@ class HassioAddonInfo extends LitElement { }; fireEvent(this, "hass-api-called", eventdata); } catch (err) { - this._error = `Failed to set addon option, ${err.body?.message || err}`; + this._error = `Failed to set addon option, ${extractApiErrorMessage( + err + )}`; } } @@ -716,9 +723,9 @@ class HassioAddonInfo extends LitElement { }; fireEvent(this, "hass-api-called", eventdata); } catch (err) { - this._error = `Failed to set addon security option, ${ - err.body?.message || err - }`; + this._error = `Failed to set addon security option, ${extractApiErrorMessage( + err + )}`; } } @@ -736,12 +743,13 @@ class HassioAddonInfo extends LitElement { }; fireEvent(this, "hass-api-called", eventdata); } catch (err) { - this._error = `Failed to set addon option, ${err.body?.message || err}`; + this._error = `Failed to set addon option, ${extractApiErrorMessage( + err + )}`; } } private async _openChangelog(): Promise { - this._error = undefined; try { const content = await fetchHassioAddonChangelog( this.hass, @@ -752,15 +760,17 @@ class HassioAddonInfo extends LitElement { content, }); } catch (err) { - this._error = `Failed to get addon changelog, ${ - err.body?.message || err - }`; + showAlertDialog(this, { + title: "Failed to get addon changelog", + text: extractApiErrorMessage(err), + }); } } - private async _installClicked(): Promise { - this._error = undefined; - this._installing = true; + private async _installClicked(ev: CustomEvent): Promise { + const button = ev.target as any; + button.progress = true; + try { await installHassioAddon(this.hass, this.addon.slug); const eventdata = { @@ -770,12 +780,62 @@ class HassioAddonInfo extends LitElement { }; fireEvent(this, "hass-api-called", eventdata); } catch (err) { - this._error = `Failed to install addon, ${err.body?.message || err}`; + showAlertDialog(this, { + title: "Failed to install addon", + text: extractApiErrorMessage(err), + }); } - this._installing = false; + button.progress = false; } - private async _uninstallClicked(): Promise { + private async _startClicked(ev: CustomEvent): Promise { + const button = ev.currentTarget as any; + button.progress = true; + try { + const validate = await validateHassioAddonOption( + this.hass, + this.addon.slug + ); + if (!validate.data.valid) { + await showConfirmationDialog(this, { + title: "Failed to start addon - configruation validation faled!", + text: validate.data.message.split(" Got ")[0], + confirm: () => this._openConfiguration(), + confirmText: "Go to configruation", + dismissText: "Cancel", + }); + button.progress = false; + return; + } + } catch (err) { + showAlertDialog(this, { + title: "Failed to validate addon configuration", + text: extractApiErrorMessage(err), + }); + button.progress = false; + return; + } + + try { + await startHassioAddon(this.hass, this.addon.slug); + this.addon = await fetchHassioAddonInfo(this.hass, this.addon.slug); + } catch (err) { + showAlertDialog(this, { + title: "Failed to start addon", + text: extractApiErrorMessage(err), + }); + } + button.progress = false; + } + + private _openConfiguration(): void { + navigate(this, `/hassio/addon/${this.addon.slug}/config`); + } + + private async _uninstallClicked(ev: CustomEvent): Promise { + const button = ev.target as any; + button.progress = true; + const confirmed = await showConfirmationDialog(this, { title: this.addon.name, text: "Are you sure you want to uninstall this add-on?", @@ -784,6 +844,7 @@ class HassioAddonInfo extends LitElement { }); if (!confirmed) { + button.progress = false; return; } @@ -797,8 +858,12 @@ class HassioAddonInfo extends LitElement { }; fireEvent(this, "hass-api-called", eventdata); } catch (err) { - this._error = `Failed to uninstall addon, ${err.body?.message || err}`; + showAlertDialog(this, { + title: "Failed to uninstall addon", + text: extractApiErrorMessage(err), + }); } + button.progress = false; } static get styles(): CSSResult[] { diff --git a/src/data/hassio/addon.ts b/src/data/hassio/addon.ts index baa2e95f29..d500ad4311 100644 --- a/src/data/hassio/addon.ts +++ b/src/data/hassio/addon.ts @@ -51,6 +51,7 @@ export interface HassioAddonDetails extends HassioAddonInfo { changelog: boolean; hassio_api: boolean; hassio_role: "default" | "homeassistant" | "manager" | "admin"; + startup: "initialize" | "system" | "services" | "application" | "once"; homeassistant_api: boolean; auth_api: boolean; full_access: boolean; @@ -158,6 +159,19 @@ export const setHassioAddonOption = async ( ); }; +export const validateHassioAddonOption = async ( + hass: HomeAssistant, + slug: string +) => { + return await hass.callApi< + HassioResponse<{ message: string; valid: boolean }> + >("POST", `hassio/addons/${slug}/options/validate`); +}; + +export const startHassioAddon = async (hass: HomeAssistant, slug: string) => { + return hass.callApi("POST", `hassio/addons/${slug}/start`); +}; + export const setHassioAddonSecurity = async ( hass: HomeAssistant, slug: string, diff --git a/src/data/hassio/common.ts b/src/data/hassio/common.ts index b96d410c51..b3681d72a8 100644 --- a/src/data/hassio/common.ts +++ b/src/data/hassio/common.ts @@ -5,3 +5,11 @@ export interface HassioResponse { export const hassioApiResultExtractor = (response: HassioResponse) => response.data; + +export const extractApiErrorMessage = (error: any): string => { + return typeof error === "object" + ? typeof error.body === "object" + ? error.body.message || "Unkown error, see logs" + : error.body || "Unkown error, see logs" + : error; +};