Add network configuration (#9210)

Co-authored-by: Bram Kragten <mail@bramkragten.nl>
This commit is contained in:
J. Nick Koston 2021-05-26 09:44:15 -05:00 committed by GitHub
parent a66b966e7d
commit b0e1f0f73a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 348 additions and 0 deletions

View File

@ -0,0 +1,172 @@
import "@polymer/paper-tooltip/paper-tooltip";
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
import { customElement, state, property } from "lit/decorators";
import {
Adapter,
NetworkConfig,
IPv6ConfiguredAddress,
IPv4ConfiguredAddress,
} from "../data/network";
import { fireEvent } from "../common/dom/fire_event";
import { haStyle } from "../resources/styles";
import { HomeAssistant } from "../types";
import "./ha-checkbox";
import type { HaCheckbox } from "./ha-checkbox";
import "./ha-settings-row";
import "./ha-icon";
const format_addresses = (
addresses: IPv6ConfiguredAddress[] | IPv4ConfiguredAddress[]
): TemplateResult[] =>
addresses.map(
(address) => html`<span>${address.address}/${address.network_prefix}</span>`
);
const format_auto_detected_interfaces = (
adapters: Adapter[]
): Array<TemplateResult | string> =>
adapters.map((adapter) =>
adapter.auto
? html`${adapter.name} (${format_addresses(adapter.ipv4)}
${format_addresses(adapter.ipv6)} )`
: ""
);
declare global {
interface HASSDomEvents {
"network-config-changed": { configured_adapters: string[] };
}
}
@customElement("ha-network")
export class HaNetwork extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;
@property({ attribute: false }) public networkConfig?: NetworkConfig;
@state() private _expanded?: boolean;
protected render(): TemplateResult {
if (this.networkConfig === undefined) {
return html``;
}
const configured_adapters = this.networkConfig.configured_adapters || [];
return html`
<ha-settings-row>
<span slot="prefix">
<ha-checkbox
id="auto_configure"
@change=${this._handleAutoConfigureCheckboxClick}
.checked=${!configured_adapters.length}
name="auto_configure"
>
</ha-checkbox>
</span>
<span slot="heading" data-for="auto_configure"> Auto Configure </span>
<span slot="description" data-for="auto_configure">
Detected:
${format_auto_detected_interfaces(this.networkConfig.adapters)}
</span>
</ha-settings-row>
${configured_adapters.length || this._expanded
? this.networkConfig.adapters.map(
(adapter) =>
html`<ha-settings-row>
<span slot="prefix">
<ha-checkbox
id=${adapter.name}
@change=${this._handleAdapterCheckboxClick}
.checked=${configured_adapters.includes(adapter.name)}
.adapter=${adapter.name}
name=${adapter.name}
>
</ha-checkbox>
</span>
<span slot="heading">
Adapter: ${adapter.name}
${adapter.default
? html`<ha-icon .icon="hass:star"></ha-icon> (Default)`
: ""}
</span>
<span slot="description">
${format_addresses(adapter.ipv4)}
${format_addresses(adapter.ipv6)}
</span>
</ha-settings-row>`
)
: ""}
`;
}
private _handleAutoConfigureCheckboxClick(ev: Event) {
const checkbox = ev.currentTarget as HaCheckbox;
if (this.networkConfig === undefined) {
return;
}
let configured_adapters = [...this.networkConfig.configured_adapters];
if (checkbox.checked) {
this._expanded = false;
configured_adapters = [];
} else {
this._expanded = true;
for (const adapter of this.networkConfig.adapters) {
if (adapter.default) {
configured_adapters = [adapter.name];
break;
}
}
}
fireEvent(this, "network-config-changed", {
configured_adapters: configured_adapters,
});
}
private _handleAdapterCheckboxClick(ev: Event) {
const checkbox = ev.currentTarget as HaCheckbox;
const adapter_name = (checkbox as any).name;
if (this.networkConfig === undefined) {
return;
}
const configured_adapters = [...this.networkConfig.configured_adapters];
if (checkbox.checked) {
configured_adapters.push(adapter_name);
} else {
const index = configured_adapters.indexOf(adapter_name, 0);
configured_adapters.splice(index, 1);
}
fireEvent(this, "network-config-changed", {
configured_adapters: configured_adapters,
});
}
static get styles(): CSSResultGroup {
return [
haStyle,
css`
.error {
color: var(--error-color);
}
ha-settings-row {
padding: 0;
}
span[slot="heading"],
span[slot="description"] {
cursor: pointer;
}
`,
];
}
}
declare global {
interface HTMLElementTagNameMap {
"ha-network": HaNetwork;
}
}

43
src/data/network.ts Normal file
View File

@ -0,0 +1,43 @@
import { HomeAssistant } from "../types";
export interface IPv6ConfiguredAddress {
address: string;
flowinfo: number;
scope_id: number;
network_prefix: number;
}
export interface IPv4ConfiguredAddress {
address: string;
network_prefix: number;
}
export interface Adapter {
name: string;
enabled: boolean;
auto: boolean;
default: boolean;
ipv6: IPv6ConfiguredAddress[];
ipv4: IPv4ConfiguredAddress[];
}
export interface NetworkConfig {
adapters: Adapter[];
configured_adapters: string[];
}
export const getNetworkConfig = (hass: HomeAssistant) =>
hass.callWS<NetworkConfig>({
type: "network",
});
export const setNetworkConfig = (
hass: HomeAssistant,
configured_adapters: string[]
) =>
hass.callWS<string[]>({
type: "network/configure",
config: {
configured_adapters: configured_adapters,
},
});

View File

@ -0,0 +1,131 @@
import "@material/mwc-button/mwc-button";
import {
css,
CSSResultGroup,
html,
LitElement,
PropertyValues,
TemplateResult,
} from "lit";
import { customElement, property, state } from "lit/decorators";
import { isComponentLoaded } from "../../../common/config/is_component_loaded";
import "../../../components/ha-network";
import "../../../components/ha-card";
import "../../../components/ha-checkbox";
import "../../../components/ha-settings-row";
import {
NetworkConfig,
getNetworkConfig,
setNetworkConfig,
} from "../../../data/network";
import { haStyle } from "../../../resources/styles";
import type { HomeAssistant } from "../../../types";
@customElement("ha-config-network")
class ConfigNetwork extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;
@state() private _networkConfig?: NetworkConfig;
@state() private _error?: string;
protected render(): TemplateResult {
if (!this.hass.userData?.showAdvanced) {
return html``;
}
const error = this._error
? this._error
: !isComponentLoaded(this.hass, "network")
? "Network integration not loaded"
: undefined;
return html`
<ha-card header="Network">
<div class="card-content">
${error ? html`<div class="error">${error}</div>` : ""}
<p>
Configure which network adapters integrations will use. Currently
this setting only affects multicast traffic. A restart is required
for these settings to apply.
</p>
<ha-network
@network-config-changed=${this._configChanged}
.hass=${this.hass}
.networkConfig=${this._networkConfig}
></ha-network>
</div>
<div class="card-actions">
<mwc-button @click=${this._save}>
${this.hass.localize(
"ui.panel.config.core.section.core.core_config.save_button"
)}
</mwc-button>
</div>
</ha-card>
`;
}
protected firstUpdated(changedProps: PropertyValues) {
super.firstUpdated(changedProps);
if (isComponentLoaded(this.hass, "network")) {
this._load();
}
}
private async _load() {
this._error = undefined;
try {
this._networkConfig = await getNetworkConfig(this.hass);
} catch (err) {
this._error = err.message || err;
}
}
private async _save() {
this._error = undefined;
try {
await setNetworkConfig(
this.hass,
this._networkConfig?.configured_adapters || []
);
} catch (err) {
this._error = err.message || err;
}
}
private _configChanged(event: CustomEvent): void {
this._networkConfig = {
...this._networkConfig!,
configured_adapters: event.detail.configured_adapters,
};
}
static get styles(): CSSResultGroup {
return [
haStyle,
css`
.error {
color: var(--error-color);
}
ha-settings-row {
padding: 0;
}
.card-actions {
display: flex;
flex-direction: row-reverse;
justify-content: space-between;
align-items: center;
}
`, // row-reverse so we tab first to "save"
];
}
}
declare global {
interface HTMLElementTagNameMap {
"ha-config-network": ConfigNetwork;
}
}

View File

@ -11,6 +11,7 @@ import "../ha-config-section";
import "./ha-config-analytics";
import "./ha-config-core-form";
import "./ha-config-name-form";
import "./ha-config-network";
import "./ha-config-url-form";
/*
@ -30,6 +31,7 @@ class HaConfigSectionCore extends LocalizeMixin(PolymerElement) {
<ha-config-name-form hass="[[hass]]"></ha-config-name-form>
<ha-config-core-form hass="[[hass]]"></ha-config-core-form>
<ha-config-url-form hass="[[hass]]"></ha-config-url-form>
<ha-config-network hass="[[hass]]"></ha-config-network>
<ha-config-analytics hass="[[hass]]"></ha-config-analytics>
</ha-config-section>
`;