diff --git a/hassio/src/dialogs/network/dialog-hassio-network.ts b/hassio/src/dialogs/network/dialog-hassio-network.ts new file mode 100644 index 0000000000..4eea315d9c --- /dev/null +++ b/hassio/src/dialogs/network/dialog-hassio-network.ts @@ -0,0 +1,328 @@ +import "@material/mwc-button/mwc-button"; +import "@material/mwc-icon-button"; +import "@material/mwc-tab-bar"; +import "@material/mwc-tab"; +import { PaperInputElement } from "@polymer/paper-input/paper-input"; +import { mdiClose } from "@mdi/js"; +import { + css, + CSSResult, + customElement, + html, + LitElement, + property, + internalProperty, + TemplateResult, +} from "lit-element"; +import { cache } from "lit-html/directives/cache"; + +import { + updateNetworkInterface, + NetworkInterface, +} from "../../../../src/data/hassio/network"; +import { fireEvent } from "../../../../src/common/dom/fire_event"; +import { HassioNetworkDialogParams } from "./show-dialog-network"; +import { haStyleDialog } from "../../../../src/resources/styles"; +import { + showAlertDialog, + showConfirmationDialog, +} from "../../../../src/dialogs/generic/show-dialog-box"; +import type { HomeAssistant } from "../../../../src/types"; +import type { HaRadio } from "../../../../src/components/ha-radio"; +import { HassDialog } from "../../../../src/dialogs/make-dialog-manager"; + +import "../../../../src/components/ha-circular-progress"; +import "../../../../src/components/ha-dialog"; +import "../../../../src/components/ha-formfield"; +import "../../../../src/components/ha-header-bar"; +import "../../../../src/components/ha-radio"; +import "../../../../src/components/ha-related-items"; +import "../../../../src/components/ha-svg-icon"; + +@customElement("dialog-hassio-network") +export class DialogHassioNetwork extends LitElement implements HassDialog { + @property({ attribute: false }) public hass!: HomeAssistant; + + @internalProperty() private _prosessing = false; + + @internalProperty() private _params?: HassioNetworkDialogParams; + + @internalProperty() private _network!: { + interface: string; + data: NetworkInterface; + }[]; + + @internalProperty() private _curTabIndex = 0; + + @internalProperty() private _device?: { + interface: string; + data: NetworkInterface; + }; + + @internalProperty() private _dirty = false; + + public async showDialog(params: HassioNetworkDialogParams): Promise { + this._params = params; + this._dirty = false; + this._curTabIndex = 0; + this._network = Object.keys(params.network?.interfaces) + .map((device) => ({ + interface: device, + data: params.network.interfaces[device], + })) + .sort((a, b) => { + return a.data.primary > b.data.primary ? -1 : 1; + }); + this._device = this._network[this._curTabIndex]; + this._device.data.nameservers = String(this._device.data.nameservers); + await this.updateComplete; + } + + public closeDialog(): void { + this._params = undefined; + this._prosessing = false; + fireEvent(this, "dialog-closed", { dialog: this.localName }); + } + + protected render(): TemplateResult { + if (!this._params || !this._network) { + return html``; + } + + return html` + +
+ + + Network settings + + + + + + ${this._network.length > 1 + ? html` ${this._network.map( + (device) => + html` + ` + )} + ` + : ""} +
+ ${cache(this._renderTab())} +
+ `; + } + + private _renderTab() { + return html`
+ + + + + + + + + ${this._device!.data.method !== "dhcp" + ? html` + + + NB!: If you are changing IP or gateway addresses, you might lose + the connection.` + : ""} +
+
+ + + ${this._prosessing + ? html`` + : "Update"} + +
`; + } + + private async _updateNetwork() { + this._prosessing = true; + let options: Partial = { + method: this._device!.data.method, + }; + if (options.method !== "dhcp") { + options = { + ...options, + address: this._device!.data.ip_address, + gateway: this._device!.data.gateway, + dns: String(this._device!.data.nameservers).split(","), + }; + } + try { + await updateNetworkInterface(this.hass, this._device!.interface, options); + } catch (err) { + showAlertDialog(this, { + title: "Failed to change network settings", + text: + typeof err === "object" ? err.body.message || "Unkown error" : err, + }); + this._prosessing = false; + return; + } + this._params?.loadData(); + this.closeDialog(); + } + + private async _handleTabActivated(ev: CustomEvent): Promise { + if (this._dirty) { + const confirm = await showConfirmationDialog(this, { + text: + "You have unsaved changes, these will get lost if you change tabs, do you want to continue?", + confirmText: "yes", + dismissText: "no", + }); + if (!confirm) { + this.requestUpdate("_device"); + return; + } + } + this._curTabIndex = ev.detail.index; + this._device = this._network[ev.detail.index]; + this._device.data.nameservers = String(this._device.data.nameservers); + } + + private _handleRadioValueChanged(ev: CustomEvent): void { + const value = (ev.target as HaRadio).value as "dhcp" | "static"; + + if (!value || !this._device || this._device!.data.method === value) { + return; + } + + this._dirty = true; + + this._device!.data.method = value; + this.requestUpdate("_device"); + } + + private _handleInputValueChanged(ev: CustomEvent): void { + const value: string | null | undefined = (ev.target as PaperInputElement) + .value; + const id = (ev.target as PaperInputElement).id; + + if (!value || !this._device || this._device.data[id] === value) { + return; + } + + this._dirty = true; + + this._device.data[id] = value; + } + + static get styles(): CSSResult[] { + return [ + haStyleDialog, + css` + ha-header-bar { + --mdc-theme-on-primary: var(--primary-text-color); + --mdc-theme-primary: var(--mdc-theme-surface); + flex-shrink: 0; + } + + mwc-tab-bar { + border-bottom: 1px solid + var(--mdc-dialog-scroll-divider-color, rgba(0, 0, 0, 0.12)); + } + + ha-dialog { + --dialog-content-position: static; + --dialog-content-padding: 0; + --dialog-z-index: 6; + } + + @media all and (min-width: 451px) and (min-height: 501px) { + .container { + width: 400px; + } + } + + .content { + display: block; + padding: 20px 24px; + } + + /* overrule the ha-style-dialog max-height on small screens */ + @media all and (max-width: 450px), all and (max-height: 500px) { + ha-header-bar { + --mdc-theme-primary: var(--app-header-background-color); + --mdc-theme-on-primary: var(--app-header-text-color, white); + } + } + + mwc-button.warning { + --mdc-theme-primary: var(--error-color); + } + + :host([rtl]) app-toolbar { + direction: rtl; + text-align: right; + } + .container { + padding: 20px 24px; + } + .form { + margin-bottom: 53px; + } + .buttons { + position: absolute; + bottom: 0; + width: 100%; + box-sizing: border-box; + border-top: 1px solid + var(--mdc-dialog-scroll-divider-color, rgba(0, 0, 0, 0.12)); + display: flex; + justify-content: space-between; + padding: 8px; + padding-bottom: max(env(safe-area-inset-bottom), 8px); + background-color: var(--mdc-theme-surface, #fff); + } + `, + ]; + } +} + +declare global { + interface HTMLElementTagNameMap { + "dialog-hassio-network": DialogHassioNetwork; + } +} diff --git a/hassio/src/dialogs/network/show-dialog-network.ts b/hassio/src/dialogs/network/show-dialog-network.ts new file mode 100644 index 0000000000..0f59f8a545 --- /dev/null +++ b/hassio/src/dialogs/network/show-dialog-network.ts @@ -0,0 +1,22 @@ +import { fireEvent } from "../../../../src/common/dom/fire_event"; +import { NetworkInfo } from "../../../../src/data/hassio/network"; +import "./dialog-hassio-network"; + +export interface HassioNetworkDialogParams { + network: NetworkInfo; + loadData: () => Promise; +} + +export const showNetworkDialog = ( + element: HTMLElement, + dialogParams: HassioNetworkDialogParams +): void => { + fireEvent(element, "show-dialog", { + dialogTag: "dialog-hassio-network", + dialogImport: () => + import( + /* webpackChunkName: "dialog-hassio-network" */ "./dialog-hassio-network" + ), + dialogParams, + }); +}; diff --git a/hassio/src/system/hassio-host-info.ts b/hassio/src/system/hassio-host-info.ts index 3a53925ddb..544e896adc 100644 --- a/hassio/src/system/hassio-host-info.ts +++ b/hassio/src/system/hassio-host-info.ts @@ -1,18 +1,23 @@ import "@material/mwc-button"; +import "@material/mwc-list/mwc-list-item"; +import { ActionDetail } from "@material/mwc-list/mwc-list-foundation"; +import { mdiDotsVertical } from "@mdi/js"; +import { safeDump } from "js-yaml"; +import memoizeOne from "memoize-one"; import { css, CSSResult, customElement, html, + internalProperty, LitElement, property, - internalProperty, TemplateResult, } from "lit-element"; -import "../../../src/components/buttons/ha-call-api-button"; -import { fetchHassioHardwareInfo } from "../../../src/data/hassio/hardware"; + import { changeHostOptions, + configSyncOS, fetchHassioHostInfo, HassioHassOSInfo, HassioHostInfo as HassioHostInfoType, @@ -20,16 +25,26 @@ import { shutdownHost, updateOS, } from "../../../src/data/hassio/host"; +import { fetchHassioHardwareInfo } from "../../../src/data/hassio/hardware"; +import { + fetchNetworkInfo, + NetworkInfo, +} from "../../../src/data/hassio/network"; import { HassioInfo } from "../../../src/data/hassio/supervisor"; +import { hassioStyle } from "../resources/hassio-style"; +import { haStyle } from "../../../src/resources/styles"; +import { HomeAssistant } from "../../../src/types"; import { showAlertDialog, showConfirmationDialog, showPromptDialog, } from "../../../src/dialogs/generic/show-dialog-box"; -import { haStyle } from "../../../src/resources/styles"; -import { HomeAssistant } from "../../../src/types"; import { showHassioMarkdownDialog } from "../dialogs/markdown/show-dialog-hassio-markdown"; -import { hassioStyle } from "../resources/hassio-style"; +import { showNetworkDialog } from "../dialogs/network/show-dialog-network"; + +import "../../../src/components/ha-button-menu"; +import "../../../src/components/ha-card"; +import "../../../src/components/ha-settings-row"; @customElement("hassio-host-info") class HassioHostInfo extends LitElement { @@ -41,86 +56,125 @@ class HassioHostInfo extends LitElement { @property({ attribute: false }) public hassOsInfo!: HassioHassOSInfo; - @internalProperty() private _errors?: string; + @internalProperty() public _networkInfo?: NetworkInfo; public render(): TemplateResult | void { + const primaryIpAddress = this._primaryIpAddress(this._networkInfo!); return html` - +
-

Host system

- - - - - - - - - - - ${!this.hostInfo.features.includes("hassos") - ? html` - - - ` - : ""} - ${this.hostInfo.deployment - ? html` - - - - - ` - : ""} - -
Hostname${this.hostInfo.hostname}
System${this.hostInfo.operating_system}
Docker version${this.hassioInfo.docker}
Deployment${this.hostInfo.deployment}
- - Hardware - ${this.hostInfo.features.includes("hostname") - ? html` + ? html` + + Hostname + + + ${this.hostInfo.hostname} + - Change hostname - ` + ` : ""} - ${this._errors - ? html`
Error: ${this._errors}
` + + + IP address + + + ${primaryIpAddress} + + + + + + + Operating system + + + ${this.hostInfo.operating_system} + + ${this.hostInfo.version !== this.hostInfo.version_latest && + this.hostInfo.features.includes("hassos") + ? html` + + + ` + : ""} + + ${!this.hostInfo.features.includes("hassos") + ? html` + + Docker version + + + ${this.hassioInfo.docker} + + ` + : ""} + ${this.hostInfo.deployment + ? html` + + Deployment + + + ${this.hostInfo.deployment} + + ` : ""}
${this.hostInfo.features.includes("reboot") ? html` - Reboot + ` : ""} ${this.hostInfo.features.includes("shutdown") ? html` - Shutdown - ` - : ""} - ${this.hostInfo.features.includes("hassos") - ? html` - Import from USB + ` : ""} - ${this.hostInfo.version !== this.hostInfo.version_latest - ? html` Update ` - : ""} + + + + + + + Hardware + + ${this.hostInfo.features.includes("hassos") + ? html` + Import from USB + ` + : ""} +
`; @@ -133,72 +187,96 @@ class HassioHostInfo extends LitElement { css` ha-card { height: 100%; + justify-content: space-between; + flex-direction: column; + display: flex; + } + .card-actions { + height: 48px; + border-top: none; + display: flex; + justify-content: space-between; + align-items: center; + } + ha-settings-row { + padding: 0; + height: 54px; width: 100%; } - .card-content { - color: var(--primary-text-color); - box-sizing: border-box; - height: calc(100% - 47px); + ha-settings-row[three-line] { + height: 74px; } - .info { - width: 100%; - } - .info td:nth-child(2) { - text-align: right; - } - .errors { - color: var(--error-color); - margin-top: 16px; - } - mwc-button.info { - max-width: calc(50% - 12px); - } - table.info { - margin-bottom: 10px; + ha-settings-row[three-line] > div { + white-space: normal; + color: var(--secondary-text-color); } + .warning { --mdc-theme-primary: var(--error-color); } + + ha-button-menu { + color: var(--secondary-text-color); + --mdc-menu-min-width: 200px; + } + @media (min-width: 563px) { + paper-listbox { + max-height: 150px; + overflow: auto; + } + } + paper-item { + cursor: pointer; + min-height: 35px; + } + mwc-list-item ha-svg-icon { + color: var(--secondary-text-color); + } `, ]; } protected firstUpdated(): void { - this.addEventListener("hass-api-called", (ev) => this._apiCalled(ev)); + this._loadData(); } - private _apiCalled(ev): void { - if (ev.detail.success) { - this._errors = undefined; - return; + private _primaryIpAddress = memoizeOne((network_info: NetworkInfo) => { + if (!network_info) { + return ""; } + return Object.keys(network_info?.interfaces) + .map((device) => network_info.interfaces[device]) + .find((device) => device.primary)?.ip_address; + }); - const response = ev.detail.response; - - this._errors = - typeof response.body === "object" - ? response.body.message || "Unknown error" - : response.body; + private async _handleMenuAction(ev: CustomEvent) { + switch (ev.detail.index) { + case 0: + await this._showHardware(); + break; + case 1: + await this._importFromUSB(); + break; + } } private async _showHardware(): Promise { try { - const content = this._objectToMarkdown( - await fetchHassioHardwareInfo(this.hass) - ); + const content = await fetchHassioHardwareInfo(this.hass); showHassioMarkdownDialog(this, { title: "Hardware", - content, + content: `
${safeDump(content, { indent: 2 })}
`, }); } catch (err) { - showHassioMarkdownDialog(this, { - title: "Hardware", - content: "Error getting hardware info", + showAlertDialog(this, { + title: "Failed to get Hardware list", + text: + typeof err === "object" ? err.body?.message || "Unkown error" : err, }); } } - private async _rebootHost(): Promise { + private async _hostReboot(): Promise { const confirmed = await showConfirmationDialog(this, { title: "Reboot", text: "Are you sure you want to reboot the host?", @@ -215,12 +293,13 @@ class HassioHostInfo extends LitElement { } catch (err) { showAlertDialog(this, { title: "Failed to reboot", - text: err.body.message, + text: + typeof err === "object" ? err.body?.message || "Unkown error" : err, }); } } - private async _shutdownHost(): Promise { + private async _hostShutdown(): Promise { const confirmed = await showConfirmationDialog(this, { title: "Shutdown", text: "Are you sure you want to shutdown the host?", @@ -237,12 +316,13 @@ class HassioHostInfo extends LitElement { } catch (err) { showAlertDialog(this, { title: "Failed to shutdown", - text: err.body.message, + text: + typeof err === "object" ? err.body?.message || "Unkown error" : err, }); } } - private async _updateOS(): Promise { + private async _osUpdate(): Promise { const confirmed = await showConfirmationDialog(this, { title: "Update", text: "Are you sure you want to update the OS?", @@ -259,30 +339,17 @@ class HassioHostInfo extends LitElement { } catch (err) { showAlertDialog(this, { title: "Failed to update", - text: err.body.message, + text: + typeof err === "object" ? err.body?.message || "Unkown error" : err, }); } } - private _objectToMarkdown(obj, indent = ""): string { - let data = ""; - Object.keys(obj).forEach((key) => { - if (typeof obj[key] !== "object") { - data += `${indent}- ${key}: ${obj[key]}\n`; - } else { - data += `${indent}- ${key}:\n`; - if (Array.isArray(obj[key])) { - if (obj[key].length) { - data += - `${indent} - ` + obj[key].join(`\n${indent} - `) + "\n"; - } - } else { - data += this._objectToMarkdown(obj[key], ` ${indent}`); - } - } + private async _changeNetworkClicked(): Promise { + showNetworkDialog(this, { + network: this._networkInfo!, + loadData: () => this._loadData(), }); - - return data; } private async _changeHostnameClicked(): Promise { @@ -301,11 +368,29 @@ class HassioHostInfo extends LitElement { } catch (err) { showAlertDialog(this, { title: "Setting hostname failed", - text: err.body.message, + text: + typeof err === "object" ? err.body?.message || "Unkown error" : err, }); } } } + + private async _importFromUSB(): Promise { + try { + await configSyncOS(this.hass); + this.hostInfo = await fetchHassioHostInfo(this.hass); + } catch (err) { + showAlertDialog(this, { + title: "Failed to import from USB", + text: + typeof err === "object" ? err.body?.message || "Unkown error" : err, + }); + } + } + + private async _loadData(): Promise { + this._networkInfo = await fetchNetworkInfo(this.hass); + } } declare global { diff --git a/hassio/src/system/hassio-supervisor-info.ts b/hassio/src/system/hassio-supervisor-info.ts index b76f791c4d..4ad09acde9 100644 --- a/hassio/src/system/hassio-supervisor-info.ts +++ b/hassio/src/system/hassio-supervisor-info.ts @@ -6,27 +6,28 @@ import { html, LitElement, property, - internalProperty, TemplateResult, } from "lit-element"; -import { fireEvent } from "../../../src/common/dom/fire_event"; -import "../../../src/components/buttons/ha-call-api-button"; -import "../../../src/components/ha-card"; + import { HassioHostInfo as HassioHostInfoType } from "../../../src/data/hassio/host"; -import { - HassioSupervisorInfo as HassioSupervisorInfoType, - setSupervisorOption, - SupervisorOptions, -} from "../../../src/data/hassio/supervisor"; -import "../../../src/components/ha-switch"; -import { - showConfirmationDialog, - showAlertDialog, -} from "../../../src/dialogs/generic/show-dialog-box"; -import "../../../src/components/ha-settings-row"; +import { hassioStyle } from "../resources/hassio-style"; import { haStyle } from "../../../src/resources/styles"; import { HomeAssistant } from "../../../src/types"; -import { hassioStyle } from "../resources/hassio-style"; +import { + HassioSupervisorInfo as HassioSupervisorInfoType, + reloadSupervisor, + setSupervisorOption, + SupervisorOptions, + updateSupervisor, +} from "../../../src/data/hassio/supervisor"; +import { + showAlertDialog, + showConfirmationDialog, +} from "../../../src/dialogs/generic/show-dialog-box"; + +import "../../../src/components/ha-card"; +import "../../../src/components/ha-settings-row"; +import "../../../src/components/ha-switch"; @customElement("hassio-supervisor-info") class HassioSupervisorInfo extends LitElement { @@ -36,104 +37,108 @@ class HassioSupervisorInfo extends LitElement { @property() public hostInfo!: HassioHostInfoType; - @internalProperty() private _errors?: string; - public render(): TemplateResult | void { return html` - +
-

Supervisor

- - - - - - - - - - - ${this.supervisorInfo.channel !== "stable" - ? html` - - - - - ` - : ""} - -
Version${this.supervisorInfo.version}
Latest version${this.supervisorInfo.version_latest}
Channel${this.supervisorInfo.channel}
-
- ${this.supervisorInfo?.supported - ? html` - - Share Diagnostics - -
- Share crash reports and diagnostic information. - -
- -
` - : html`
- You are running an unsupported installation. - Learn More + + Version + + + ${this.supervisorInfo.version} + + + + + Newest version + + + ${this.supervisorInfo.version_latest} + + ${this.supervisorInfo.version !== this.supervisorInfo.version_latest + ? html` + -
`} -
- ${this._errors - ? html`
Error: ${this._errors}
` - : ""} + + ` + : ""} + + + + Channel + + + ${this.supervisorInfo.channel} + + ${this.supervisorInfo.channel === "beta" + ? html` + + + ` + : this.supervisorInfo.channel === "stable" + ? html` + + + ` + : ""} + + + ${this.supervisorInfo?.supported + ? html` + + Share diagnostics + +
+ Share crash reports and diagnostic information. + +
+ +
` + : html`
+ You are running an unsupported installation. + + Learn More + +
`}
- Reload - ${this.supervisorInfo.version !== this.supervisorInfo.version_latest - ? html` - Update - ` - : ""} - ${this.supervisorInfo.channel === "beta" - ? html` - Leave beta channel - ` - : ""} - ${this.supervisorInfo.channel === "stable" - ? html` - Join beta channel - ` - : ""} +
`; @@ -146,93 +151,103 @@ class HassioSupervisorInfo extends LitElement { css` ha-card { height: 100%; - width: 100%; + justify-content: space-between; + flex-direction: column; + display: flex; } - .card-content { - color: var(--primary-text-color); - box-sizing: border-box; - height: calc(100% - 47px); - } - .info, - .options { - width: 100%; - } - .info td:nth-child(2) { - text-align: right; - } - ha-settings-row { - padding: 0; + .card-actions { + height: 48px; + border-top: none; + display: flex; + justify-content: space-between; + align-items: center; } button.link { color: var(--primary-color); } - .diagnostics-description { - white-space: normal; + ha-settings-row { padding: 0; + height: 54px; + width: 100%; + } + ha-settings-row[three-line] { + height: 74px; + } + ha-settings-row[three-line] > div { + white-space: normal; color: var(--secondary-text-color); } `, ]; } - protected firstUpdated(): void { - this.addEventListener("hass-api-called", (ev) => this._apiCalled(ev)); - } + private async _toggleBeta(): Promise { + if (this.supervisorInfo.channel === "stable") { + const confirmed = await showConfirmationDialog(this, { + title: "WARNING", + text: html` Beta releases are for testers and early adopters and can + contain unstable code changes. +
+ + Make sure you have backups of your data before you activate this + feature. + +

+ This includes beta releases for: +
  • Home Assistant Core
  • +
  • Home Assistant Supervisor
  • +
  • Home Assistant Operating System
  • +
    + Do you want to join the beta channel?`, + confirmText: "join beta", + dismissText: "no", + }); - private _apiCalled(ev): void { - if (ev.detail.success) { - this._errors = undefined; - return; - } - - const response = ev.detail.response; - - this._errors = - typeof response.body === "object" - ? response.body.message || "Unknown error" - : response.body; - } - - private async _joinBeta() { - const confirmed = await showConfirmationDialog(this, { - title: "WARNING", - text: html` Beta releases are for testers and early adopters and can - contain unstable code changes. -
    - - Make sure you have backups of your data before you activate this - feature. - -

    - This includes beta releases for: -
  • Home Assistant Core
  • -
  • Home Assistant Supervisor
  • -
  • Home Assistant Operating System
  • -
    - Do you want to join the beta channel?`, - confirmText: "join beta", - dismissText: "no", - }); - - if (!confirmed) { - return; + if (!confirmed) { + return; + } } try { - const data: SupervisorOptions = { channel: "beta" }; - await setSupervisorOption(this.hass, data); - const eventdata = { - success: true, - response: undefined, - path: "option", + const data: Partial = { + channel: this.supervisorInfo.channel !== "stable" ? "beta" : "stable", }; - fireEvent(this, "hass-api-called", eventdata); + await setSupervisorOption(this.hass, data); + await reloadSupervisor(this.hass); } catch (err) { - this._errors = `Error joining beta channel, ${err.body?.message || err}`; + showAlertDialog(this, { + title: "Failed to set supervisor option", + text: + typeof err === "object" ? err.body?.message || "Unkown error" : err, + }); } } - private async _diagnosticsInformationDialog() { + private async _supervisorReload(): Promise { + try { + await reloadSupervisor(this.hass); + } catch (err) { + showAlertDialog(this, { + title: "Failed to reload the supervisor", + text: + typeof err === "object" ? err.body?.message || "Unkown error" : err, + }); + } + } + + private async _supervisorUpdate(): Promise { + try { + await updateSupervisor(this.hass); + } catch (err) { + showAlertDialog(this, { + title: "Failed to update the supervisor", + text: + typeof err === "object" ? err.body.message || "Unkown error" : err, + }); + } + } + + private async _diagnosticsInformationDialog(): Promise { await showAlertDialog(this, { title: "Help Improve Home Assistant", text: html`Would you want to automatically share crash reports and @@ -247,22 +262,18 @@ class HassioSupervisorInfo extends LitElement { }); } - private async _toggleDiagnostics() { + private async _toggleDiagnostics(): Promise { try { const data: SupervisorOptions = { diagnostics: !this.supervisorInfo?.diagnostics, }; await setSupervisorOption(this.hass, data); - const eventdata = { - success: true, - response: undefined, - path: "option", - }; - fireEvent(this, "hass-api-called", eventdata); } catch (err) { - this._errors = `Error changing supervisor setting, ${ - err.body?.message || err - }`; + showAlertDialog(this, { + title: "Failed to set supervisor option", + text: + typeof err === "object" ? err.body.message || "Unkown error" : err, + }); } } } diff --git a/hassio/src/system/hassio-supervisor-log.ts b/hassio/src/system/hassio-supervisor-log.ts index ec2dbe7031..de2c08cda5 100644 --- a/hassio/src/system/hassio-supervisor-log.ts +++ b/hassio/src/system/hassio-supervisor-log.ts @@ -7,18 +7,20 @@ import { CSSResult, customElement, html, + internalProperty, LitElement, property, - internalProperty, TemplateResult, } from "lit-element"; -import "../../../src/components/ha-card"; + import { fetchHassioLogs } from "../../../src/data/hassio/supervisor"; -import "../../../src/layouts/hass-loading-screen"; +import { hassioStyle } from "../resources/hassio-style"; import { haStyle } from "../../../src/resources/styles"; import { HomeAssistant } from "../../../src/types"; + +import "../../../src/components/ha-card"; +import "../../../src/layouts/hass-loading-screen"; import "../components/hassio-ansi-to-html"; -import { hassioStyle } from "../resources/hassio-style"; interface LogProvider { key: string; @@ -102,7 +104,7 @@ class HassioSupervisorLog extends LitElement { : html``}
    - Refresh + Refresh
    `; @@ -114,6 +116,7 @@ class HassioSupervisorLog extends LitElement { hassioStyle, css` ha-card { + margin-top: 8px; width: 100%; } pre { @@ -127,9 +130,6 @@ class HassioSupervisorLog extends LitElement { color: var(--error-color); margin-bottom: 16px; } - .card-content { - padding-top: 0px; - } `, ]; } @@ -142,7 +142,6 @@ class HassioSupervisorLog extends LitElement { private async _loadData(): Promise { this._error = undefined; - this._content = undefined; try { this._content = await fetchHassioLogs( @@ -151,14 +150,10 @@ class HassioSupervisorLog extends LitElement { ); } catch (err) { this._error = `Failed to get supervisor logs, ${ - err.body?.message || err + typeof err === "object" ? err.body?.message || "Unkown error" : err }`; } } - - private async _refresh(): Promise { - await this._loadData(); - } } declare global { diff --git a/hassio/src/system/hassio-system.ts b/hassio/src/system/hassio-system.ts index 3a52347a08..ad91a42825 100644 --- a/hassio/src/system/hassio-system.ts +++ b/hassio/src/system/hassio-system.ts @@ -52,7 +52,6 @@ class HassioSystem extends LitElement { > System
    -

    Information

    -

    System log

    diff --git a/src/components/ha-settings-row.ts b/src/components/ha-settings-row.ts index 43c886ab99..0d53c58d29 100644 --- a/src/components/ha-settings-row.ts +++ b/src/components/ha-settings-row.ts @@ -25,7 +25,7 @@ export class HaSettingsRow extends LitElement {
    diff --git a/src/data/hassio/host.ts b/src/data/hassio/host.ts index bdefe66dde..a8db6dbc71 100644 --- a/src/data/hassio/host.ts +++ b/src/data/hassio/host.ts @@ -40,6 +40,10 @@ export const updateOS = async (hass: HomeAssistant) => { return hass.callApi>("POST", "hassio/os/update"); }; +export const configSyncOS = async (hass: HomeAssistant) => { + return hass.callApi>("POST", "hassio/os/config/sync"); +}; + export const changeHostOptions = async (hass: HomeAssistant, options: any) => { return hass.callApi>( "POST", diff --git a/src/data/hassio/network.ts b/src/data/hassio/network.ts new file mode 100644 index 0000000000..5e2e7c20e1 --- /dev/null +++ b/src/data/hassio/network.ts @@ -0,0 +1,43 @@ +import { HomeAssistant } from "../../types"; +import { hassioApiResultExtractor, HassioResponse } from "./common"; + +export interface NetworkInterface { + gateway: string; + id: string; + ip_address: string; + address?: string; + method: "static" | "dhcp"; + nameservers: string[] | string; + dns?: string[]; + primary: boolean; + type: string; +} + +export interface NetworkInterfaces { + [key: string]: NetworkInterface; +} + +export interface NetworkInfo { + interfaces: NetworkInterfaces; +} + +export const fetchNetworkInfo = async (hass: HomeAssistant) => { + return hassioApiResultExtractor( + await hass.callApi>( + "GET", + "hassio/network/info" + ) + ); +}; + +export const updateNetworkInterface = async ( + hass: HomeAssistant, + network_interface: string, + options: Partial +) => { + await hass.callApi>( + "POST", + `hassio/network/interface/${network_interface}/update`, + options + ); +}; diff --git a/src/data/hassio/supervisor.ts b/src/data/hassio/supervisor.ts index aaf854ac81..992425b8bc 100644 --- a/src/data/hassio/supervisor.ts +++ b/src/data/hassio/supervisor.ts @@ -35,6 +35,14 @@ export interface SupervisorOptions { addons_repositories?: string[]; } +export const reloadSupervisor = async (hass: HomeAssistant) => { + await hass.callApi>("POST", `hassio/supervisor/reload`); +}; + +export const updateSupervisor = async (hass: HomeAssistant) => { + await hass.callApi>("POST", `hassio/supervisor/update`); +}; + export const fetchHassioHomeAssistantInfo = async (hass: HomeAssistant) => { return hassioApiResultExtractor( await hass.callApi>(