Improve Wifi configuration UI (#22471)

* Improve Wifi configuration UI

* some UI tweaks based on comments

* change label and remove DNS on reset

* remove mock code
This commit is contained in:
Petar Petrov 2024-10-24 11:33:56 +03:00 committed by GitHub
parent 5843877cc8
commit fc9a0958d4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 97 additions and 49 deletions

View File

@ -17,7 +17,7 @@ export interface NetworkInterface {
ipv4?: Partial<IpConfiguration>; ipv4?: Partial<IpConfiguration>;
ipv6?: Partial<IpConfiguration>; ipv6?: Partial<IpConfiguration>;
type: "ethernet" | "wireless" | "vlan"; type: "ethernet" | "wireless" | "vlan";
wifi?: Partial<WifiConfiguration>; wifi?: Partial<WifiConfiguration> | null;
} }
interface DockerNetwork { interface DockerNetwork {
@ -27,7 +27,7 @@ interface DockerNetwork {
interface: string; interface: string;
} }
interface AccessPoint { export interface AccessPoint {
mode: "infrastructure" | "mesh" | "adhoc" | "ap"; mode: "infrastructure" | "mesh" | "adhoc" | "ap";
ssid: string; ssid: string;
mac: string; mac: string;

View File

@ -1,6 +1,6 @@
import "@material/mwc-tab"; import "@material/mwc-tab";
import "@material/mwc-tab-bar"; import "@material/mwc-tab-bar";
import { mdiDeleteOutline, mdiPlus, mdiMenuDown } from "@mdi/js"; import { mdiDeleteOutline, mdiPlus, mdiMenuDown, mdiWifi } from "@mdi/js";
import { css, CSSResultGroup, html, LitElement, nothing } from "lit"; import { css, CSSResultGroup, html, LitElement, nothing } from "lit";
import { customElement, property, state } from "lit/decorators"; import { customElement, property, state } from "lit/decorators";
import { cache } from "lit/directives/cache"; import { cache } from "lit/directives/cache";
@ -20,7 +20,7 @@ import "../../../components/ha-textfield";
import type { HaTextField } from "../../../components/ha-textfield"; import type { HaTextField } from "../../../components/ha-textfield";
import { extractApiErrorMessage } from "../../../data/hassio/common"; import { extractApiErrorMessage } from "../../../data/hassio/common";
import { import {
AccessPoints, AccessPoint,
accesspointScan, accesspointScan,
fetchNetworkInfo, fetchNetworkInfo,
formatAddress, formatAddress,
@ -58,7 +58,7 @@ const PREDEFINED_DNS = {
export class HassioNetwork extends LitElement { export class HassioNetwork extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant; @property({ attribute: false }) public hass!: HomeAssistant;
@state() private _accessPoints?: AccessPoints; @state() private _accessPoints: AccessPoint[] = [];
@state() private _curTabIndex = 0; @state() private _curTabIndex = 0;
@ -113,7 +113,7 @@ export class HassioNetwork extends LitElement {
</mwc-tab>` </mwc-tab>`
)} )}
</mwc-tab-bar>` </mwc-tab-bar>`
: ""} : nothing}
${cache(this._renderTab())} ${cache(this._renderTab())}
</ha-card> </ha-card>
`; `;
@ -121,9 +121,6 @@ export class HassioNetwork extends LitElement {
private _renderTab() { private _renderTab() {
return html`<div class="card-content"> return html`<div class="card-content">
${IP_VERSIONS.map((version) =>
this._interface![version] ? this._renderIPConfiguration(version) : ""
)}
${this._interface?.type === "wireless" ${this._interface?.type === "wireless"
? html` ? html`
<ha-expansion-panel <ha-expansion-panel
@ -131,15 +128,17 @@ export class HassioNetwork extends LitElement {
"ui.panel.config.network.supervisor.wifi" "ui.panel.config.network.supervisor.wifi"
)} )}
outlined outlined
.expanded=${!this._interface?.wifi?.ssid}
> >
${this._interface?.wifi?.ssid ${this._interface?.wifi?.ssid
? html`<p> ? html`<p>
<ha-svg-icon slot="icon" .path=${mdiWifi}></ha-svg-icon>
${this.hass.localize( ${this.hass.localize(
"ui.panel.config.network.supervisor.connected_to", "ui.panel.config.network.supervisor.connected_to",
{ ssid: this._interface?.wifi?.ssid } { ssid: this._interface?.wifi?.ssid }
)} )}
</p>` </p>`
: ""} : nothing}
<ha-button <ha-button
class="scan" class="scan"
@click=${this._scanForAP} @click=${this._scanForAP}
@ -151,37 +150,34 @@ export class HassioNetwork extends LitElement {
: this.hass.localize( : this.hass.localize(
"ui.panel.config.network.supervisor.scan_ap" "ui.panel.config.network.supervisor.scan_ap"
)} )}
<ha-svg-icon slot="icon" .path=${mdiWifi}></ha-svg-icon>
</ha-button> </ha-button>
${this._accessPoints && ${this._accessPoints.length
this._accessPoints.accesspoints &&
this._accessPoints.accesspoints.length !== 0
? html` ? html`
<mwc-list> <mwc-list>
${this._accessPoints.accesspoints ${this._accessPoints.map(
.filter((ap) => ap.ssid) (ap) => html`
.map( <ha-list-item
(ap) => html` twoline
<ha-list-item @click=${this._selectAP}
twoline .activated=${ap.ssid ===
@click=${this._selectAP} this._wifiConfiguration?.ssid}
.activated=${ap.ssid === .ap=${ap}
this._wifiConfiguration?.ssid} >
.ap=${ap} <span>${ap.ssid}</span>
> <span slot="secondary">
<span>${ap.ssid}</span> ${ap.mac} -
<span slot="secondary"> ${this.hass.localize(
${ap.mac} - "ui.panel.config.network.supervisor.signal_strength"
${this.hass.localize( )}:
"ui.panel.config.network.supervisor.signal_strength" ${ap.signal}
)}: </span>
${ap.signal} </ha-list-item>
</span> `
</ha-list-item> )}
`
)}
</mwc-list> </mwc-list>
` `
: ""} : nothing}
${this._wifiConfiguration ${this._wifiConfiguration
? html` ? html`
<div class="radio-row"> <div class="radio-row">
@ -244,19 +240,24 @@ export class HassioNetwork extends LitElement {
> >
</ha-password-field> </ha-password-field>
` `
: ""} : nothing}
` `
: ""} : nothing}
</ha-expansion-panel> </ha-expansion-panel>
` `
: ""} : nothing}
${IP_VERSIONS.map((version) =>
this._interface![version]
? this._renderIPConfiguration(version)
: nothing
)}
${this._dirty ${this._dirty
? html`<ha-alert alert-type="warning"> ? html`<ha-alert alert-type="warning">
${this.hass.localize( ${this.hass.localize(
"ui.panel.config.network.supervisor.warning" "ui.panel.config.network.supervisor.warning"
)} )}
</ha-alert>` </ha-alert>`
: ""} : nothing}
</div> </div>
<div class="card-actions"> <div class="card-actions">
<ha-button @click=${this._updateNetwork} .disabled=${!this._dirty}> <ha-button @click=${this._updateNetwork} .disabled=${!this._dirty}>
@ -265,11 +266,19 @@ export class HassioNetwork extends LitElement {
</ha-circular-progress>` </ha-circular-progress>`
: this.hass.localize("ui.common.save")} : this.hass.localize("ui.common.save")}
</ha-button> </ha-button>
<ha-button @click=${this._clear}>
${this.hass.localize("ui.panel.config.network.supervisor.reset")}
</ha-button>
</div>`; </div>`;
} }
private _selectAP(event) { private _selectAP(event) {
this._wifiConfiguration = event.currentTarget.ap; this._wifiConfiguration = event.currentTarget.ap;
IP_VERSIONS.forEach((version) => {
if (this._interface![version]!.method === "disabled") {
this._interface![version]!.method = "auto";
}
});
this._dirty = true; this._dirty = true;
} }
@ -279,10 +288,22 @@ export class HassioNetwork extends LitElement {
} }
this._scanning = true; this._scanning = true;
try { try {
this._accessPoints = await accesspointScan( const aps = await accesspointScan(this.hass, this._interface.interface);
this.hass, this._accessPoints = [];
this._interface.interface aps.accesspoints?.forEach((ap) => {
); if (ap.ssid) {
// filter out duplicates
const existing = this._accessPoints.find((a) => a.ssid === ap.ssid);
if (!existing) {
this._accessPoints.push(ap);
} else if (ap.signal > existing.signal) {
this._accessPoints = this._accessPoints.filter(
(a) => a.ssid !== ap.ssid
);
this._accessPoints.push(ap);
}
}
});
} catch (err: any) { } catch (err: any) {
showAlertDialog(this, { showAlertDialog(this, {
title: "Failed to scan for accesspoints", title: "Failed to scan for accesspoints",
@ -294,6 +315,13 @@ export class HassioNetwork extends LitElement {
} }
private _renderIPConfiguration(version: string) { private _renderIPConfiguration(version: string) {
const watingForSSID =
this._interface?.type === "wireless" &&
!this._wifiConfiguration?.ssid &&
!this._interface.wifi?.ssid;
if (watingForSSID) {
return nothing;
}
const nameservers = this._interface![version]?.nameservers || []; const nameservers = this._interface![version]?.nameservers || [];
if (nameservers.length === 0) { if (nameservers.length === 0) {
nameservers.push(""); // always show input nameservers.push(""); // always show input
@ -484,7 +512,7 @@ export class HassioNetwork extends LitElement {
</ha-list-item> </ha-list-item>
</ha-button-menu> </ha-button-menu>
` `
: ""} : nothing}
</ha-expansion-panel> </ha-expansion-panel>
`; `;
} }
@ -529,9 +557,13 @@ export class HassioNetwork extends LitElement {
} }
interfaceOptions.enabled = interfaceOptions.enabled =
this._wifiConfiguration !== undefined || // at least one ip version is enabled
interfaceOptions.ipv4?.method !== "disabled" || (interfaceOptions.ipv4?.method !== "disabled" ||
interfaceOptions.ipv6?.method !== "disabled"; interfaceOptions.ipv6?.method !== "disabled") &&
// require connection if this is a wireless interface
(this._interface!.type !== "wireless" ||
this._wifiConfiguration !== undefined ||
!!this._interface!.wifi);
try { try {
await updateNetworkInterface( await updateNetworkInterface(
@ -540,6 +572,7 @@ export class HassioNetwork extends LitElement {
interfaceOptions interfaceOptions
); );
this._dirty = false; this._dirty = false;
await this._fetchNetworkInfo();
} catch (err: any) { } catch (err: any) {
showAlertDialog(this, { showAlertDialog(this, {
title: this.hass.localize( title: this.hass.localize(
@ -552,6 +585,20 @@ export class HassioNetwork extends LitElement {
} }
} }
private async _clear() {
await this._fetchNetworkInfo();
this._interface!.ipv4!.method = "auto";
this._interface!.ipv4!.nameservers = [];
this._interface!.ipv6!.method = "auto";
this._interface!.ipv6!.nameservers = [];
// removing the connection will disable the interface
// this is the only way to forget the wifi network right now
this._interface!.wifi = null;
this._wifiConfiguration = undefined;
this._dirty = true;
this.requestUpdate("_interface");
}
private async _handleTabActivated(ev: CustomEvent): Promise<void> { private async _handleTabActivated(ev: CustomEvent): Promise<void> {
if (this._dirty) { if (this._dirty) {
const confirm = await showConfirmationDialog(this, { const confirm = await showConfirmationDialog(this, {

View File

@ -5225,7 +5225,8 @@
"supervisor": { "supervisor": {
"title": "Configure network interfaces", "title": "Configure network interfaces",
"connected_to": "Connected to {ssid}", "connected_to": "Connected to {ssid}",
"scan_ap": "Scan for access points", "scan_ap": "Search networks",
"reset": "Reset configuration",
"signal_strength": "Signal strength", "signal_strength": "Signal strength",
"open": "Open", "open": "Open",
"wep": "WEP", "wep": "WEP",