mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-25 18:26:35 +00:00
Supervisor system (#6699)
Co-authored-by: Bram Kragten <mail@bramkragten.nl>
This commit is contained in:
parent
1b970e5a66
commit
04df6c3e9e
328
hassio/src/dialogs/network/dialog-hassio-network.ts
Normal file
328
hassio/src/dialogs/network/dialog-hassio-network.ts
Normal file
@ -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<void> {
|
||||||
|
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`
|
||||||
|
<ha-dialog open .heading=${true} hideActions @closed=${this.closeDialog}>
|
||||||
|
<div slot="heading">
|
||||||
|
<ha-header-bar>
|
||||||
|
<span slot="title">
|
||||||
|
Network settings
|
||||||
|
</span>
|
||||||
|
<mwc-icon-button slot="actionItems" dialogAction="cancel">
|
||||||
|
<ha-svg-icon .path=${mdiClose}></ha-svg-icon>
|
||||||
|
</mwc-icon-button>
|
||||||
|
</ha-header-bar>
|
||||||
|
${this._network.length > 1
|
||||||
|
? html` <mwc-tab-bar
|
||||||
|
.activeIndex=${this._curTabIndex}
|
||||||
|
@MDCTabBar:activated=${this._handleTabActivated}
|
||||||
|
>${this._network.map(
|
||||||
|
(device) =>
|
||||||
|
html`<mwc-tab
|
||||||
|
.id=${device.interface}
|
||||||
|
.label=${device.interface}
|
||||||
|
>
|
||||||
|
</mwc-tab>`
|
||||||
|
)}
|
||||||
|
</mwc-tab-bar>`
|
||||||
|
: ""}
|
||||||
|
</div>
|
||||||
|
${cache(this._renderTab())}
|
||||||
|
</ha-dialog>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _renderTab() {
|
||||||
|
return html` <div class="form container">
|
||||||
|
<ha-formfield label="DHCP">
|
||||||
|
<ha-radio
|
||||||
|
@change=${this._handleRadioValueChanged}
|
||||||
|
value="dhcp"
|
||||||
|
name="method"
|
||||||
|
?checked=${this._device!.data.method === "dhcp"}
|
||||||
|
>
|
||||||
|
</ha-radio>
|
||||||
|
</ha-formfield>
|
||||||
|
<ha-formfield label="Static">
|
||||||
|
<ha-radio
|
||||||
|
@change=${this._handleRadioValueChanged}
|
||||||
|
value="static"
|
||||||
|
name="method"
|
||||||
|
?checked=${this._device!.data.method === "static"}
|
||||||
|
>
|
||||||
|
</ha-radio>
|
||||||
|
</ha-formfield>
|
||||||
|
${this._device!.data.method !== "dhcp"
|
||||||
|
? html` <paper-input
|
||||||
|
class="flex-auto"
|
||||||
|
id="ip_address"
|
||||||
|
label="IP address/Netmask"
|
||||||
|
.value="${this._device!.data.ip_address}"
|
||||||
|
@value-changed=${this._handleInputValueChanged}
|
||||||
|
></paper-input>
|
||||||
|
<paper-input
|
||||||
|
class="flex-auto"
|
||||||
|
id="gateway"
|
||||||
|
label="Gateway address"
|
||||||
|
.value="${this._device!.data.gateway}"
|
||||||
|
@value-changed=${this._handleInputValueChanged}
|
||||||
|
></paper-input>
|
||||||
|
<paper-input
|
||||||
|
class="flex-auto"
|
||||||
|
id="nameservers"
|
||||||
|
label="DNS servers"
|
||||||
|
.value="${this._device!.data.nameservers as string}"
|
||||||
|
@value-changed=${this._handleInputValueChanged}
|
||||||
|
></paper-input>
|
||||||
|
NB!: If you are changing IP or gateway addresses, you might lose
|
||||||
|
the connection.`
|
||||||
|
: ""}
|
||||||
|
</div>
|
||||||
|
<div class="buttons">
|
||||||
|
<mwc-button label="close" @click=${this.closeDialog}> </mwc-button>
|
||||||
|
<mwc-button @click=${this._updateNetwork} ?disabled=${!this._dirty}>
|
||||||
|
${this._prosessing
|
||||||
|
? html`<ha-circular-progress active></ha-circular-progress>`
|
||||||
|
: "Update"}
|
||||||
|
</mwc-button>
|
||||||
|
</div>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _updateNetwork() {
|
||||||
|
this._prosessing = true;
|
||||||
|
let options: Partial<NetworkInterface> = {
|
||||||
|
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<void> {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
22
hassio/src/dialogs/network/show-dialog-network.ts
Normal file
22
hassio/src/dialogs/network/show-dialog-network.ts
Normal file
@ -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<void>;
|
||||||
|
}
|
||||||
|
|
||||||
|
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,
|
||||||
|
});
|
||||||
|
};
|
@ -1,18 +1,23 @@
|
|||||||
import "@material/mwc-button";
|
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 {
|
import {
|
||||||
css,
|
css,
|
||||||
CSSResult,
|
CSSResult,
|
||||||
customElement,
|
customElement,
|
||||||
html,
|
html,
|
||||||
|
internalProperty,
|
||||||
LitElement,
|
LitElement,
|
||||||
property,
|
property,
|
||||||
internalProperty,
|
|
||||||
TemplateResult,
|
TemplateResult,
|
||||||
} from "lit-element";
|
} from "lit-element";
|
||||||
import "../../../src/components/buttons/ha-call-api-button";
|
|
||||||
import { fetchHassioHardwareInfo } from "../../../src/data/hassio/hardware";
|
|
||||||
import {
|
import {
|
||||||
changeHostOptions,
|
changeHostOptions,
|
||||||
|
configSyncOS,
|
||||||
fetchHassioHostInfo,
|
fetchHassioHostInfo,
|
||||||
HassioHassOSInfo,
|
HassioHassOSInfo,
|
||||||
HassioHostInfo as HassioHostInfoType,
|
HassioHostInfo as HassioHostInfoType,
|
||||||
@ -20,16 +25,26 @@ import {
|
|||||||
shutdownHost,
|
shutdownHost,
|
||||||
updateOS,
|
updateOS,
|
||||||
} from "../../../src/data/hassio/host";
|
} 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 { HassioInfo } from "../../../src/data/hassio/supervisor";
|
||||||
|
import { hassioStyle } from "../resources/hassio-style";
|
||||||
|
import { haStyle } from "../../../src/resources/styles";
|
||||||
|
import { HomeAssistant } from "../../../src/types";
|
||||||
import {
|
import {
|
||||||
showAlertDialog,
|
showAlertDialog,
|
||||||
showConfirmationDialog,
|
showConfirmationDialog,
|
||||||
showPromptDialog,
|
showPromptDialog,
|
||||||
} from "../../../src/dialogs/generic/show-dialog-box";
|
} 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 { 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")
|
@customElement("hassio-host-info")
|
||||||
class HassioHostInfo extends LitElement {
|
class HassioHostInfo extends LitElement {
|
||||||
@ -41,86 +56,125 @@ class HassioHostInfo extends LitElement {
|
|||||||
|
|
||||||
@property({ attribute: false }) public hassOsInfo!: HassioHassOSInfo;
|
@property({ attribute: false }) public hassOsInfo!: HassioHassOSInfo;
|
||||||
|
|
||||||
@internalProperty() private _errors?: string;
|
@internalProperty() public _networkInfo?: NetworkInfo;
|
||||||
|
|
||||||
public render(): TemplateResult | void {
|
public render(): TemplateResult | void {
|
||||||
|
const primaryIpAddress = this._primaryIpAddress(this._networkInfo!);
|
||||||
return html`
|
return html`
|
||||||
<ha-card>
|
<ha-card header="Host System">
|
||||||
<div class="card-content">
|
<div class="card-content">
|
||||||
<h2>Host system</h2>
|
|
||||||
<table class="info">
|
|
||||||
<tbody>
|
|
||||||
<tr>
|
|
||||||
<td>Hostname</td>
|
|
||||||
<td>${this.hostInfo.hostname}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>System</td>
|
|
||||||
<td>${this.hostInfo.operating_system}</td>
|
|
||||||
</tr>
|
|
||||||
${!this.hostInfo.features.includes("hassos")
|
|
||||||
? html`<tr>
|
|
||||||
<td>Docker version</td>
|
|
||||||
<td>${this.hassioInfo.docker}</td>
|
|
||||||
</tr>`
|
|
||||||
: ""}
|
|
||||||
${this.hostInfo.deployment
|
|
||||||
? html`
|
|
||||||
<tr>
|
|
||||||
<td>Deployment</td>
|
|
||||||
<td>${this.hostInfo.deployment}</td>
|
|
||||||
</tr>
|
|
||||||
`
|
|
||||||
: ""}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
<mwc-button raised @click=${this._showHardware} class="info">
|
|
||||||
Hardware
|
|
||||||
</mwc-button>
|
|
||||||
${this.hostInfo.features.includes("hostname")
|
${this.hostInfo.features.includes("hostname")
|
||||||
? html`
|
? html`<ha-settings-row>
|
||||||
|
<span slot="heading">
|
||||||
|
Hostname
|
||||||
|
</span>
|
||||||
|
<span slot="description">
|
||||||
|
${this.hostInfo.hostname}
|
||||||
|
</span>
|
||||||
<mwc-button
|
<mwc-button
|
||||||
raised
|
title="Change the hostname"
|
||||||
|
label="Change"
|
||||||
@click=${this._changeHostnameClicked}
|
@click=${this._changeHostnameClicked}
|
||||||
class="info"
|
|
||||||
>
|
>
|
||||||
Change hostname
|
|
||||||
</mwc-button>
|
</mwc-button>
|
||||||
`
|
</ha-settings-row>`
|
||||||
: ""}
|
: ""}
|
||||||
${this._errors
|
<ha-settings-row>
|
||||||
? html` <div class="errors">Error: ${this._errors}</div> `
|
<span slot="heading">
|
||||||
|
IP address
|
||||||
|
</span>
|
||||||
|
<span slot="description">
|
||||||
|
${primaryIpAddress}
|
||||||
|
</span>
|
||||||
|
<mwc-button
|
||||||
|
title="Change the network"
|
||||||
|
label="Change"
|
||||||
|
@click=${this._changeNetworkClicked}
|
||||||
|
>
|
||||||
|
</mwc-button>
|
||||||
|
</ha-settings-row>
|
||||||
|
<ha-settings-row>
|
||||||
|
<span slot="heading">
|
||||||
|
Operating system
|
||||||
|
</span>
|
||||||
|
<span slot="description">
|
||||||
|
${this.hostInfo.operating_system}
|
||||||
|
</span>
|
||||||
|
${this.hostInfo.version !== this.hostInfo.version_latest &&
|
||||||
|
this.hostInfo.features.includes("hassos")
|
||||||
|
? html`
|
||||||
|
<mwc-button
|
||||||
|
title="Update the host OS"
|
||||||
|
label="Update"
|
||||||
|
@click=${this._osUpdate}
|
||||||
|
>
|
||||||
|
</mwc-button>
|
||||||
|
`
|
||||||
|
: ""}
|
||||||
|
</ha-settings-row>
|
||||||
|
${!this.hostInfo.features.includes("hassos")
|
||||||
|
? html`<ha-settings-row>
|
||||||
|
<span slot="heading">
|
||||||
|
Docker version
|
||||||
|
</span>
|
||||||
|
<span slot="description">
|
||||||
|
${this.hassioInfo.docker}
|
||||||
|
</span>
|
||||||
|
</ha-settings-row>`
|
||||||
|
: ""}
|
||||||
|
${this.hostInfo.deployment
|
||||||
|
? html`<ha-settings-row>
|
||||||
|
<span slot="heading">
|
||||||
|
Deployment
|
||||||
|
</span>
|
||||||
|
<span slot="description">
|
||||||
|
${this.hostInfo.deployment}
|
||||||
|
</span>
|
||||||
|
</ha-settings-row>`
|
||||||
: ""}
|
: ""}
|
||||||
</div>
|
</div>
|
||||||
<div class="card-actions">
|
<div class="card-actions">
|
||||||
${this.hostInfo.features.includes("reboot")
|
${this.hostInfo.features.includes("reboot")
|
||||||
? html`
|
? html`
|
||||||
<mwc-button class="warning" @click=${this._rebootHost}
|
<mwc-button
|
||||||
>Reboot</mwc-button
|
title="Reboot the host OS"
|
||||||
|
label="Reboot"
|
||||||
|
class="warning"
|
||||||
|
@click=${this._hostReboot}
|
||||||
>
|
>
|
||||||
|
</mwc-button>
|
||||||
`
|
`
|
||||||
: ""}
|
: ""}
|
||||||
${this.hostInfo.features.includes("shutdown")
|
${this.hostInfo.features.includes("shutdown")
|
||||||
? html`
|
? html`
|
||||||
<mwc-button class="warning" @click=${this._shutdownHost}
|
<mwc-button
|
||||||
>Shutdown</mwc-button
|
title="Shutdown the host OS"
|
||||||
>
|
label="Shutdown"
|
||||||
`
|
|
||||||
: ""}
|
|
||||||
${this.hostInfo.features.includes("hassos")
|
|
||||||
? html`
|
|
||||||
<ha-call-api-button
|
|
||||||
class="warning"
|
class="warning"
|
||||||
.hass=${this.hass}
|
@click=${this._hostShutdown}
|
||||||
path="hassio/os/config/sync"
|
|
||||||
title="Load HassOS configs or updates from USB"
|
|
||||||
>Import from USB</ha-call-api-button
|
|
||||||
>
|
>
|
||||||
|
</mwc-button>
|
||||||
`
|
`
|
||||||
: ""}
|
: ""}
|
||||||
${this.hostInfo.version !== this.hostInfo.version_latest
|
|
||||||
? html` <mwc-button @click=${this._updateOS}>Update</mwc-button> `
|
<ha-button-menu
|
||||||
: ""}
|
corner="BOTTOM_START"
|
||||||
|
@action=${this._handleMenuAction}
|
||||||
|
>
|
||||||
|
<mwc-icon-button slot="trigger">
|
||||||
|
<ha-svg-icon .path=${mdiDotsVertical}></ha-svg-icon>
|
||||||
|
</mwc-icon-button>
|
||||||
|
<mwc-list-item title="Show a list of hardware">
|
||||||
|
Hardware
|
||||||
|
</mwc-list-item>
|
||||||
|
${this.hostInfo.features.includes("hassos")
|
||||||
|
? html`<mwc-list-item
|
||||||
|
title="Load HassOS configs or updates from USB"
|
||||||
|
>
|
||||||
|
Import from USB
|
||||||
|
</mwc-list-item>`
|
||||||
|
: ""}
|
||||||
|
</ha-button-menu>
|
||||||
</div>
|
</div>
|
||||||
</ha-card>
|
</ha-card>
|
||||||
`;
|
`;
|
||||||
@ -133,72 +187,96 @@ class HassioHostInfo extends LitElement {
|
|||||||
css`
|
css`
|
||||||
ha-card {
|
ha-card {
|
||||||
height: 100%;
|
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%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
.card-content {
|
ha-settings-row[three-line] {
|
||||||
color: var(--primary-text-color);
|
height: 74px;
|
||||||
box-sizing: border-box;
|
|
||||||
height: calc(100% - 47px);
|
|
||||||
}
|
}
|
||||||
.info {
|
ha-settings-row[three-line] > div {
|
||||||
width: 100%;
|
white-space: normal;
|
||||||
}
|
color: var(--secondary-text-color);
|
||||||
.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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.warning {
|
.warning {
|
||||||
--mdc-theme-primary: var(--error-color);
|
--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 {
|
protected firstUpdated(): void {
|
||||||
this.addEventListener("hass-api-called", (ev) => this._apiCalled(ev));
|
this._loadData();
|
||||||
}
|
}
|
||||||
|
|
||||||
private _apiCalled(ev): void {
|
private _primaryIpAddress = memoizeOne((network_info: NetworkInfo) => {
|
||||||
if (ev.detail.success) {
|
if (!network_info) {
|
||||||
this._errors = undefined;
|
return "";
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
return Object.keys(network_info?.interfaces)
|
||||||
|
.map((device) => network_info.interfaces[device])
|
||||||
|
.find((device) => device.primary)?.ip_address;
|
||||||
|
});
|
||||||
|
|
||||||
const response = ev.detail.response;
|
private async _handleMenuAction(ev: CustomEvent<ActionDetail>) {
|
||||||
|
switch (ev.detail.index) {
|
||||||
this._errors =
|
case 0:
|
||||||
typeof response.body === "object"
|
await this._showHardware();
|
||||||
? response.body.message || "Unknown error"
|
break;
|
||||||
: response.body;
|
case 1:
|
||||||
|
await this._importFromUSB();
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _showHardware(): Promise<void> {
|
private async _showHardware(): Promise<void> {
|
||||||
try {
|
try {
|
||||||
const content = this._objectToMarkdown(
|
const content = await fetchHassioHardwareInfo(this.hass);
|
||||||
await fetchHassioHardwareInfo(this.hass)
|
|
||||||
);
|
|
||||||
showHassioMarkdownDialog(this, {
|
showHassioMarkdownDialog(this, {
|
||||||
title: "Hardware",
|
title: "Hardware",
|
||||||
content,
|
content: `<pre>${safeDump(content, { indent: 2 })}</pre>`,
|
||||||
});
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
showHassioMarkdownDialog(this, {
|
showAlertDialog(this, {
|
||||||
title: "Hardware",
|
title: "Failed to get Hardware list",
|
||||||
content: "Error getting hardware info",
|
text:
|
||||||
|
typeof err === "object" ? err.body?.message || "Unkown error" : err,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _rebootHost(): Promise<void> {
|
private async _hostReboot(): Promise<void> {
|
||||||
const confirmed = await showConfirmationDialog(this, {
|
const confirmed = await showConfirmationDialog(this, {
|
||||||
title: "Reboot",
|
title: "Reboot",
|
||||||
text: "Are you sure you want to reboot the host?",
|
text: "Are you sure you want to reboot the host?",
|
||||||
@ -215,12 +293,13 @@ class HassioHostInfo extends LitElement {
|
|||||||
} catch (err) {
|
} catch (err) {
|
||||||
showAlertDialog(this, {
|
showAlertDialog(this, {
|
||||||
title: "Failed to reboot",
|
title: "Failed to reboot",
|
||||||
text: err.body.message,
|
text:
|
||||||
|
typeof err === "object" ? err.body?.message || "Unkown error" : err,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _shutdownHost(): Promise<void> {
|
private async _hostShutdown(): Promise<void> {
|
||||||
const confirmed = await showConfirmationDialog(this, {
|
const confirmed = await showConfirmationDialog(this, {
|
||||||
title: "Shutdown",
|
title: "Shutdown",
|
||||||
text: "Are you sure you want to shutdown the host?",
|
text: "Are you sure you want to shutdown the host?",
|
||||||
@ -237,12 +316,13 @@ class HassioHostInfo extends LitElement {
|
|||||||
} catch (err) {
|
} catch (err) {
|
||||||
showAlertDialog(this, {
|
showAlertDialog(this, {
|
||||||
title: "Failed to shutdown",
|
title: "Failed to shutdown",
|
||||||
text: err.body.message,
|
text:
|
||||||
|
typeof err === "object" ? err.body?.message || "Unkown error" : err,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _updateOS(): Promise<void> {
|
private async _osUpdate(): Promise<void> {
|
||||||
const confirmed = await showConfirmationDialog(this, {
|
const confirmed = await showConfirmationDialog(this, {
|
||||||
title: "Update",
|
title: "Update",
|
||||||
text: "Are you sure you want to update the OS?",
|
text: "Are you sure you want to update the OS?",
|
||||||
@ -259,30 +339,17 @@ class HassioHostInfo extends LitElement {
|
|||||||
} catch (err) {
|
} catch (err) {
|
||||||
showAlertDialog(this, {
|
showAlertDialog(this, {
|
||||||
title: "Failed to update",
|
title: "Failed to update",
|
||||||
text: err.body.message,
|
text:
|
||||||
|
typeof err === "object" ? err.body?.message || "Unkown error" : err,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private _objectToMarkdown(obj, indent = ""): string {
|
private async _changeNetworkClicked(): Promise<void> {
|
||||||
let data = "";
|
showNetworkDialog(this, {
|
||||||
Object.keys(obj).forEach((key) => {
|
network: this._networkInfo!,
|
||||||
if (typeof obj[key] !== "object") {
|
loadData: () => this._loadData(),
|
||||||
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}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return data;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _changeHostnameClicked(): Promise<void> {
|
private async _changeHostnameClicked(): Promise<void> {
|
||||||
@ -301,11 +368,29 @@ class HassioHostInfo extends LitElement {
|
|||||||
} catch (err) {
|
} catch (err) {
|
||||||
showAlertDialog(this, {
|
showAlertDialog(this, {
|
||||||
title: "Setting hostname failed",
|
title: "Setting hostname failed",
|
||||||
text: err.body.message,
|
text:
|
||||||
|
typeof err === "object" ? err.body?.message || "Unkown error" : err,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async _importFromUSB(): Promise<void> {
|
||||||
|
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<void> {
|
||||||
|
this._networkInfo = await fetchNetworkInfo(this.hass);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
|
@ -6,27 +6,28 @@ import {
|
|||||||
html,
|
html,
|
||||||
LitElement,
|
LitElement,
|
||||||
property,
|
property,
|
||||||
internalProperty,
|
|
||||||
TemplateResult,
|
TemplateResult,
|
||||||
} from "lit-element";
|
} 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 { HassioHostInfo as HassioHostInfoType } from "../../../src/data/hassio/host";
|
||||||
import {
|
import { hassioStyle } from "../resources/hassio-style";
|
||||||
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 { haStyle } from "../../../src/resources/styles";
|
import { haStyle } from "../../../src/resources/styles";
|
||||||
import { HomeAssistant } from "../../../src/types";
|
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")
|
@customElement("hassio-supervisor-info")
|
||||||
class HassioSupervisorInfo extends LitElement {
|
class HassioSupervisorInfo extends LitElement {
|
||||||
@ -36,104 +37,108 @@ class HassioSupervisorInfo extends LitElement {
|
|||||||
|
|
||||||
@property() public hostInfo!: HassioHostInfoType;
|
@property() public hostInfo!: HassioHostInfoType;
|
||||||
|
|
||||||
@internalProperty() private _errors?: string;
|
|
||||||
|
|
||||||
public render(): TemplateResult | void {
|
public render(): TemplateResult | void {
|
||||||
return html`
|
return html`
|
||||||
<ha-card>
|
<ha-card header="Supervisor">
|
||||||
<div class="card-content">
|
<div class="card-content">
|
||||||
<h2>Supervisor</h2>
|
<ha-settings-row>
|
||||||
<table class="info">
|
<span slot="heading">
|
||||||
<tbody>
|
Version
|
||||||
<tr>
|
</span>
|
||||||
<td>Version</td>
|
<span slot="description">
|
||||||
<td>${this.supervisorInfo.version}</td>
|
${this.supervisorInfo.version}
|
||||||
</tr>
|
</span>
|
||||||
<tr>
|
</ha-settings-row>
|
||||||
<td>Latest version</td>
|
<ha-settings-row>
|
||||||
<td>${this.supervisorInfo.version_latest}</td>
|
<span slot="heading">
|
||||||
</tr>
|
Newest version
|
||||||
${this.supervisorInfo.channel !== "stable"
|
</span>
|
||||||
? html`
|
<span slot="description">
|
||||||
<tr>
|
${this.supervisorInfo.version_latest}
|
||||||
<td>Channel</td>
|
</span>
|
||||||
<td>${this.supervisorInfo.channel}</td>
|
${this.supervisorInfo.version !== this.supervisorInfo.version_latest
|
||||||
</tr>
|
? html`
|
||||||
`
|
<mwc-button
|
||||||
: ""}
|
title="Update the supervisor"
|
||||||
</tbody>
|
label="Update"
|
||||||
</table>
|
@click=${this._supervisorUpdate}
|
||||||
<div class="options">
|
|
||||||
${this.supervisorInfo?.supported
|
|
||||||
? html` <ha-settings-row>
|
|
||||||
<span slot="heading">
|
|
||||||
Share Diagnostics
|
|
||||||
</span>
|
|
||||||
<div slot="description" class="diagnostics-description">
|
|
||||||
Share crash reports and diagnostic information.
|
|
||||||
<button
|
|
||||||
class="link"
|
|
||||||
@click=${this._diagnosticsInformationDialog}
|
|
||||||
>
|
|
||||||
Learn more
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<ha-switch
|
|
||||||
.checked=${this.supervisorInfo.diagnostics}
|
|
||||||
@change=${this._toggleDiagnostics}
|
|
||||||
></ha-switch>
|
|
||||||
</ha-settings-row>`
|
|
||||||
: html`<div class="error">
|
|
||||||
You are running an unsupported installation.
|
|
||||||
<a
|
|
||||||
href="https://github.com/home-assistant/architecture/blob/master/adr/${this.hostInfo.features.includes(
|
|
||||||
"hassos"
|
|
||||||
)
|
|
||||||
? "0015-home-assistant-os.md"
|
|
||||||
: "0014-home-assistant-supervised.md"}"
|
|
||||||
target="_blank"
|
|
||||||
rel="noreferrer"
|
|
||||||
>Learn More</a
|
|
||||||
>
|
>
|
||||||
</div>`}
|
</mwc-button>
|
||||||
</div>
|
`
|
||||||
${this._errors
|
: ""}
|
||||||
? html` <div class="error">Error: ${this._errors}</div> `
|
</ha-settings-row>
|
||||||
: ""}
|
<ha-settings-row>
|
||||||
|
<span slot="heading">
|
||||||
|
Channel
|
||||||
|
</span>
|
||||||
|
<span slot="description">
|
||||||
|
${this.supervisorInfo.channel}
|
||||||
|
</span>
|
||||||
|
${this.supervisorInfo.channel === "beta"
|
||||||
|
? html`
|
||||||
|
<mwc-button
|
||||||
|
@click=${this._toggleBeta}
|
||||||
|
label="Leave beta channel"
|
||||||
|
title="Get stable updates for Home Assistant, supervisor and host"
|
||||||
|
>
|
||||||
|
</mwc-button>
|
||||||
|
`
|
||||||
|
: this.supervisorInfo.channel === "stable"
|
||||||
|
? html`
|
||||||
|
<mwc-button
|
||||||
|
@click=${this._toggleBeta}
|
||||||
|
label="Join beta channel"
|
||||||
|
title="Get beta updates for Home Assistant (RCs), supervisor and host"
|
||||||
|
>
|
||||||
|
</mwc-button>
|
||||||
|
`
|
||||||
|
: ""}
|
||||||
|
</ha-settings-row>
|
||||||
|
|
||||||
|
${this.supervisorInfo?.supported
|
||||||
|
? html` <ha-settings-row three-line>
|
||||||
|
<span slot="heading">
|
||||||
|
Share diagnostics
|
||||||
|
</span>
|
||||||
|
<div slot="description" class="diagnostics-description">
|
||||||
|
Share crash reports and diagnostic information.
|
||||||
|
<button
|
||||||
|
class="link"
|
||||||
|
title="Show more information about this"
|
||||||
|
@click=${this._diagnosticsInformationDialog}
|
||||||
|
>
|
||||||
|
Learn more
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<ha-switch
|
||||||
|
haptic
|
||||||
|
.checked=${this.supervisorInfo.diagnostics}
|
||||||
|
@change=${this._toggleDiagnostics}
|
||||||
|
></ha-switch>
|
||||||
|
</ha-settings-row>`
|
||||||
|
: html`<div class="error">
|
||||||
|
You are running an unsupported installation.
|
||||||
|
<a
|
||||||
|
href="https://github.com/home-assistant/architecture/blob/master/adr/${this.hostInfo.features.includes(
|
||||||
|
"hassos"
|
||||||
|
)
|
||||||
|
? "0015-home-assistant-os.md"
|
||||||
|
: "0014-home-assistant-supervised.md"}"
|
||||||
|
target="_blank"
|
||||||
|
rel="noreferrer"
|
||||||
|
title="Learn more about how you can make your system compliant"
|
||||||
|
>
|
||||||
|
Learn More
|
||||||
|
</a>
|
||||||
|
</div>`}
|
||||||
</div>
|
</div>
|
||||||
<div class="card-actions">
|
<div class="card-actions">
|
||||||
<ha-call-api-button .hass=${this.hass} path="hassio/supervisor/reload"
|
<mwc-button
|
||||||
>Reload</ha-call-api-button
|
@click=${this._supervisorReload}
|
||||||
|
title="Reload parts of the supervisor."
|
||||||
|
label="Reload"
|
||||||
>
|
>
|
||||||
${this.supervisorInfo.version !== this.supervisorInfo.version_latest
|
</mwc-button>
|
||||||
? html`
|
|
||||||
<ha-call-api-button
|
|
||||||
.hass=${this.hass}
|
|
||||||
path="hassio/supervisor/update"
|
|
||||||
>Update</ha-call-api-button
|
|
||||||
>
|
|
||||||
`
|
|
||||||
: ""}
|
|
||||||
${this.supervisorInfo.channel === "beta"
|
|
||||||
? html`
|
|
||||||
<ha-call-api-button
|
|
||||||
.hass=${this.hass}
|
|
||||||
path="hassio/supervisor/options"
|
|
||||||
.data=${{ channel: "stable" }}
|
|
||||||
>Leave beta channel</ha-call-api-button
|
|
||||||
>
|
|
||||||
`
|
|
||||||
: ""}
|
|
||||||
${this.supervisorInfo.channel === "stable"
|
|
||||||
? html`
|
|
||||||
<mwc-button
|
|
||||||
@click=${this._joinBeta}
|
|
||||||
class="warning"
|
|
||||||
title="Get beta updates for Home Assistant (RCs), supervisor and host"
|
|
||||||
>Join beta channel</mwc-button
|
|
||||||
>
|
|
||||||
`
|
|
||||||
: ""}
|
|
||||||
</div>
|
</div>
|
||||||
</ha-card>
|
</ha-card>
|
||||||
`;
|
`;
|
||||||
@ -146,93 +151,103 @@ class HassioSupervisorInfo extends LitElement {
|
|||||||
css`
|
css`
|
||||||
ha-card {
|
ha-card {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
width: 100%;
|
justify-content: space-between;
|
||||||
|
flex-direction: column;
|
||||||
|
display: flex;
|
||||||
}
|
}
|
||||||
.card-content {
|
.card-actions {
|
||||||
color: var(--primary-text-color);
|
height: 48px;
|
||||||
box-sizing: border-box;
|
border-top: none;
|
||||||
height: calc(100% - 47px);
|
display: flex;
|
||||||
}
|
justify-content: space-between;
|
||||||
.info,
|
align-items: center;
|
||||||
.options {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
.info td:nth-child(2) {
|
|
||||||
text-align: right;
|
|
||||||
}
|
|
||||||
ha-settings-row {
|
|
||||||
padding: 0;
|
|
||||||
}
|
}
|
||||||
button.link {
|
button.link {
|
||||||
color: var(--primary-color);
|
color: var(--primary-color);
|
||||||
}
|
}
|
||||||
.diagnostics-description {
|
ha-settings-row {
|
||||||
white-space: normal;
|
|
||||||
padding: 0;
|
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);
|
color: var(--secondary-text-color);
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
protected firstUpdated(): void {
|
private async _toggleBeta(): Promise<void> {
|
||||||
this.addEventListener("hass-api-called", (ev) => this._apiCalled(ev));
|
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.
|
||||||
|
<br />
|
||||||
|
<b>
|
||||||
|
Make sure you have backups of your data before you activate this
|
||||||
|
feature.
|
||||||
|
</b>
|
||||||
|
<br /><br />
|
||||||
|
This includes beta releases for:
|
||||||
|
<li>Home Assistant Core</li>
|
||||||
|
<li>Home Assistant Supervisor</li>
|
||||||
|
<li>Home Assistant Operating System</li>
|
||||||
|
<br />
|
||||||
|
Do you want to join the beta channel?`,
|
||||||
|
confirmText: "join beta",
|
||||||
|
dismissText: "no",
|
||||||
|
});
|
||||||
|
|
||||||
private _apiCalled(ev): void {
|
if (!confirmed) {
|
||||||
if (ev.detail.success) {
|
return;
|
||||||
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.
|
|
||||||
<br />
|
|
||||||
<b>
|
|
||||||
Make sure you have backups of your data before you activate this
|
|
||||||
feature.
|
|
||||||
</b>
|
|
||||||
<br /><br />
|
|
||||||
This includes beta releases for:
|
|
||||||
<li>Home Assistant Core</li>
|
|
||||||
<li>Home Assistant Supervisor</li>
|
|
||||||
<li>Home Assistant Operating System</li>
|
|
||||||
<br />
|
|
||||||
Do you want to join the beta channel?`,
|
|
||||||
confirmText: "join beta",
|
|
||||||
dismissText: "no",
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!confirmed) {
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const data: SupervisorOptions = { channel: "beta" };
|
const data: Partial<SupervisorOptions> = {
|
||||||
await setSupervisorOption(this.hass, data);
|
channel: this.supervisorInfo.channel !== "stable" ? "beta" : "stable",
|
||||||
const eventdata = {
|
|
||||||
success: true,
|
|
||||||
response: undefined,
|
|
||||||
path: "option",
|
|
||||||
};
|
};
|
||||||
fireEvent(this, "hass-api-called", eventdata);
|
await setSupervisorOption(this.hass, data);
|
||||||
|
await reloadSupervisor(this.hass);
|
||||||
} catch (err) {
|
} 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<void> {
|
||||||
|
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<void> {
|
||||||
|
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<void> {
|
||||||
await showAlertDialog(this, {
|
await showAlertDialog(this, {
|
||||||
title: "Help Improve Home Assistant",
|
title: "Help Improve Home Assistant",
|
||||||
text: html`Would you want to automatically share crash reports and
|
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<void> {
|
||||||
try {
|
try {
|
||||||
const data: SupervisorOptions = {
|
const data: SupervisorOptions = {
|
||||||
diagnostics: !this.supervisorInfo?.diagnostics,
|
diagnostics: !this.supervisorInfo?.diagnostics,
|
||||||
};
|
};
|
||||||
await setSupervisorOption(this.hass, data);
|
await setSupervisorOption(this.hass, data);
|
||||||
const eventdata = {
|
|
||||||
success: true,
|
|
||||||
response: undefined,
|
|
||||||
path: "option",
|
|
||||||
};
|
|
||||||
fireEvent(this, "hass-api-called", eventdata);
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
this._errors = `Error changing supervisor setting, ${
|
showAlertDialog(this, {
|
||||||
err.body?.message || err
|
title: "Failed to set supervisor option",
|
||||||
}`;
|
text:
|
||||||
|
typeof err === "object" ? err.body.message || "Unkown error" : err,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,18 +7,20 @@ import {
|
|||||||
CSSResult,
|
CSSResult,
|
||||||
customElement,
|
customElement,
|
||||||
html,
|
html,
|
||||||
|
internalProperty,
|
||||||
LitElement,
|
LitElement,
|
||||||
property,
|
property,
|
||||||
internalProperty,
|
|
||||||
TemplateResult,
|
TemplateResult,
|
||||||
} from "lit-element";
|
} from "lit-element";
|
||||||
import "../../../src/components/ha-card";
|
|
||||||
import { fetchHassioLogs } from "../../../src/data/hassio/supervisor";
|
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 { haStyle } from "../../../src/resources/styles";
|
||||||
import { HomeAssistant } from "../../../src/types";
|
import { HomeAssistant } from "../../../src/types";
|
||||||
|
|
||||||
|
import "../../../src/components/ha-card";
|
||||||
|
import "../../../src/layouts/hass-loading-screen";
|
||||||
import "../components/hassio-ansi-to-html";
|
import "../components/hassio-ansi-to-html";
|
||||||
import { hassioStyle } from "../resources/hassio-style";
|
|
||||||
|
|
||||||
interface LogProvider {
|
interface LogProvider {
|
||||||
key: string;
|
key: string;
|
||||||
@ -102,7 +104,7 @@ class HassioSupervisorLog extends LitElement {
|
|||||||
: html`<hass-loading-screen no-toolbar></hass-loading-screen>`}
|
: html`<hass-loading-screen no-toolbar></hass-loading-screen>`}
|
||||||
</div>
|
</div>
|
||||||
<div class="card-actions">
|
<div class="card-actions">
|
||||||
<mwc-button @click=${this._refresh}>Refresh</mwc-button>
|
<mwc-button @click=${this._loadData}>Refresh</mwc-button>
|
||||||
</div>
|
</div>
|
||||||
</ha-card>
|
</ha-card>
|
||||||
`;
|
`;
|
||||||
@ -114,6 +116,7 @@ class HassioSupervisorLog extends LitElement {
|
|||||||
hassioStyle,
|
hassioStyle,
|
||||||
css`
|
css`
|
||||||
ha-card {
|
ha-card {
|
||||||
|
margin-top: 8px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
pre {
|
pre {
|
||||||
@ -127,9 +130,6 @@ class HassioSupervisorLog extends LitElement {
|
|||||||
color: var(--error-color);
|
color: var(--error-color);
|
||||||
margin-bottom: 16px;
|
margin-bottom: 16px;
|
||||||
}
|
}
|
||||||
.card-content {
|
|
||||||
padding-top: 0px;
|
|
||||||
}
|
|
||||||
`,
|
`,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
@ -142,7 +142,6 @@ class HassioSupervisorLog extends LitElement {
|
|||||||
|
|
||||||
private async _loadData(): Promise<void> {
|
private async _loadData(): Promise<void> {
|
||||||
this._error = undefined;
|
this._error = undefined;
|
||||||
this._content = undefined;
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
this._content = await fetchHassioLogs(
|
this._content = await fetchHassioLogs(
|
||||||
@ -151,14 +150,10 @@ class HassioSupervisorLog extends LitElement {
|
|||||||
);
|
);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
this._error = `Failed to get supervisor logs, ${
|
this._error = `Failed to get supervisor logs, ${
|
||||||
err.body?.message || err
|
typeof err === "object" ? err.body?.message || "Unkown error" : err
|
||||||
}`;
|
}`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _refresh(): Promise<void> {
|
|
||||||
await this._loadData();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
|
@ -52,7 +52,6 @@ class HassioSystem extends LitElement {
|
|||||||
>
|
>
|
||||||
<span slot="header">System</span>
|
<span slot="header">System</span>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<h1>Information</h1>
|
|
||||||
<div class="card-group">
|
<div class="card-group">
|
||||||
<hassio-supervisor-info
|
<hassio-supervisor-info
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
@ -66,7 +65,6 @@ class HassioSystem extends LitElement {
|
|||||||
.hassOsInfo=${this.hassOsInfo}
|
.hassOsInfo=${this.hassOsInfo}
|
||||||
></hassio-host-info>
|
></hassio-host-info>
|
||||||
</div>
|
</div>
|
||||||
<h1>System log</h1>
|
|
||||||
<hassio-supervisor-log .hass=${this.hass}></hassio-supervisor-log>
|
<hassio-supervisor-log .hass=${this.hass}></hassio-supervisor-log>
|
||||||
</div>
|
</div>
|
||||||
</hass-tabs-subpage>
|
</hass-tabs-subpage>
|
||||||
|
@ -25,7 +25,7 @@ export class HaSettingsRow extends LitElement {
|
|||||||
</style>
|
</style>
|
||||||
<paper-item-body
|
<paper-item-body
|
||||||
?two-line=${!this.threeLine}
|
?two-line=${!this.threeLine}
|
||||||
?three-line=${!this.threeLine}
|
?three-line=${this.threeLine}
|
||||||
>
|
>
|
||||||
<slot name="heading"></slot>
|
<slot name="heading"></slot>
|
||||||
<div secondary><slot name="description"></slot></div>
|
<div secondary><slot name="description"></slot></div>
|
||||||
|
@ -40,6 +40,10 @@ export const updateOS = async (hass: HomeAssistant) => {
|
|||||||
return hass.callApi<HassioResponse<void>>("POST", "hassio/os/update");
|
return hass.callApi<HassioResponse<void>>("POST", "hassio/os/update");
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const configSyncOS = async (hass: HomeAssistant) => {
|
||||||
|
return hass.callApi<HassioResponse<void>>("POST", "hassio/os/config/sync");
|
||||||
|
};
|
||||||
|
|
||||||
export const changeHostOptions = async (hass: HomeAssistant, options: any) => {
|
export const changeHostOptions = async (hass: HomeAssistant, options: any) => {
|
||||||
return hass.callApi<HassioResponse<void>>(
|
return hass.callApi<HassioResponse<void>>(
|
||||||
"POST",
|
"POST",
|
||||||
|
43
src/data/hassio/network.ts
Normal file
43
src/data/hassio/network.ts
Normal file
@ -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<HassioResponse<NetworkInfo>>(
|
||||||
|
"GET",
|
||||||
|
"hassio/network/info"
|
||||||
|
)
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const updateNetworkInterface = async (
|
||||||
|
hass: HomeAssistant,
|
||||||
|
network_interface: string,
|
||||||
|
options: Partial<NetworkInterface>
|
||||||
|
) => {
|
||||||
|
await hass.callApi<HassioResponse<NetworkInfo>>(
|
||||||
|
"POST",
|
||||||
|
`hassio/network/interface/${network_interface}/update`,
|
||||||
|
options
|
||||||
|
);
|
||||||
|
};
|
@ -35,6 +35,14 @@ export interface SupervisorOptions {
|
|||||||
addons_repositories?: string[];
|
addons_repositories?: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const reloadSupervisor = async (hass: HomeAssistant) => {
|
||||||
|
await hass.callApi<HassioResponse<void>>("POST", `hassio/supervisor/reload`);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const updateSupervisor = async (hass: HomeAssistant) => {
|
||||||
|
await hass.callApi<HassioResponse<void>>("POST", `hassio/supervisor/update`);
|
||||||
|
};
|
||||||
|
|
||||||
export const fetchHassioHomeAssistantInfo = async (hass: HomeAssistant) => {
|
export const fetchHassioHomeAssistantInfo = async (hass: HomeAssistant) => {
|
||||||
return hassioApiResultExtractor(
|
return hassioApiResultExtractor(
|
||||||
await hass.callApi<HassioResponse<HassioHomeAssistantInfo>>(
|
await hass.callApi<HassioResponse<HassioHomeAssistantInfo>>(
|
||||||
|
Loading…
x
Reference in New Issue
Block a user