diff --git a/src/data/hassio/network.ts b/src/data/hassio/network.ts index a0b501d42982..a4d8f7a8541a 100644 --- a/src/data/hassio/network.ts +++ b/src/data/hassio/network.ts @@ -17,7 +17,7 @@ export interface NetworkInterface { ipv4?: Partial; ipv6?: Partial; type: "ethernet" | "wireless" | "vlan"; - wifi?: Partial; + wifi?: Partial | null; } interface DockerNetwork { @@ -27,7 +27,7 @@ interface DockerNetwork { interface: string; } -interface AccessPoint { +export interface AccessPoint { mode: "infrastructure" | "mesh" | "adhoc" | "ap"; ssid: string; mac: string; diff --git a/src/panels/config/network/supervisor-network.ts b/src/panels/config/network/supervisor-network.ts index 2824d7eadf66..0d2fcfaf2134 100644 --- a/src/panels/config/network/supervisor-network.ts +++ b/src/panels/config/network/supervisor-network.ts @@ -1,6 +1,6 @@ import "@material/mwc-tab"; 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 { customElement, property, state } from "lit/decorators"; import { cache } from "lit/directives/cache"; @@ -20,7 +20,7 @@ import "../../../components/ha-textfield"; import type { HaTextField } from "../../../components/ha-textfield"; import { extractApiErrorMessage } from "../../../data/hassio/common"; import { - AccessPoints, + AccessPoint, accesspointScan, fetchNetworkInfo, formatAddress, @@ -58,7 +58,7 @@ const PREDEFINED_DNS = { export class HassioNetwork extends LitElement { @property({ attribute: false }) public hass!: HomeAssistant; - @state() private _accessPoints?: AccessPoints; + @state() private _accessPoints: AccessPoint[] = []; @state() private _curTabIndex = 0; @@ -113,7 +113,7 @@ export class HassioNetwork extends LitElement { ` )} ` - : ""} + : nothing} ${cache(this._renderTab())} `; @@ -121,9 +121,6 @@ export class HassioNetwork extends LitElement { private _renderTab() { return html`
- ${IP_VERSIONS.map((version) => - this._interface![version] ? this._renderIPConfiguration(version) : "" - )} ${this._interface?.type === "wireless" ? html` ${this._interface?.wifi?.ssid ? html`

+ ${this.hass.localize( "ui.panel.config.network.supervisor.connected_to", { ssid: this._interface?.wifi?.ssid } )}

` - : ""} + : nothing} - ${this._accessPoints && - this._accessPoints.accesspoints && - this._accessPoints.accesspoints.length !== 0 + ${this._accessPoints.length ? html` - ${this._accessPoints.accesspoints - .filter((ap) => ap.ssid) - .map( - (ap) => html` - - ${ap.ssid} - - ${ap.mac} - - ${this.hass.localize( - "ui.panel.config.network.supervisor.signal_strength" - )}: - ${ap.signal} - - - ` - )} + ${this._accessPoints.map( + (ap) => html` + + ${ap.ssid} + + ${ap.mac} - + ${this.hass.localize( + "ui.panel.config.network.supervisor.signal_strength" + )}: + ${ap.signal} + + + ` + )} ` - : ""} + : nothing} ${this._wifiConfiguration ? html`
@@ -244,19 +240,24 @@ export class HassioNetwork extends LitElement { > ` - : ""} + : nothing} ` - : ""} + : nothing} ` - : ""} + : nothing} + ${IP_VERSIONS.map((version) => + this._interface![version] + ? this._renderIPConfiguration(version) + : nothing + )} ${this._dirty ? html` ${this.hass.localize( "ui.panel.config.network.supervisor.warning" )} ` - : ""} + : nothing}
@@ -265,11 +266,19 @@ export class HassioNetwork extends LitElement { ` : this.hass.localize("ui.common.save")} + + ${this.hass.localize("ui.panel.config.network.supervisor.reset")} +
`; } private _selectAP(event) { this._wifiConfiguration = event.currentTarget.ap; + IP_VERSIONS.forEach((version) => { + if (this._interface![version]!.method === "disabled") { + this._interface![version]!.method = "auto"; + } + }); this._dirty = true; } @@ -279,10 +288,22 @@ export class HassioNetwork extends LitElement { } this._scanning = true; try { - this._accessPoints = await accesspointScan( - this.hass, - this._interface.interface - ); + const aps = await accesspointScan(this.hass, this._interface.interface); + this._accessPoints = []; + 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) { showAlertDialog(this, { title: "Failed to scan for accesspoints", @@ -294,6 +315,13 @@ export class HassioNetwork extends LitElement { } 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 || []; if (nameservers.length === 0) { nameservers.push(""); // always show input @@ -484,7 +512,7 @@ export class HassioNetwork extends LitElement { ` - : ""} + : nothing}
`; } @@ -529,9 +557,13 @@ export class HassioNetwork extends LitElement { } interfaceOptions.enabled = - this._wifiConfiguration !== undefined || - interfaceOptions.ipv4?.method !== "disabled" || - interfaceOptions.ipv6?.method !== "disabled"; + // at least one ip version is enabled + (interfaceOptions.ipv4?.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 { await updateNetworkInterface( @@ -540,6 +572,7 @@ export class HassioNetwork extends LitElement { interfaceOptions ); this._dirty = false; + await this._fetchNetworkInfo(); } catch (err: any) { showAlertDialog(this, { 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 { if (this._dirty) { const confirm = await showConfirmationDialog(this, { diff --git a/src/translations/en.json b/src/translations/en.json index 540d229bc021..f78d984667f5 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -5225,7 +5225,8 @@ "supervisor": { "title": "Configure network interfaces", "connected_to": "Connected to {ssid}", - "scan_ap": "Scan for access points", + "scan_ap": "Search networks", + "reset": "Reset configuration", "signal_strength": "Signal strength", "open": "Open", "wep": "WEP",