mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-18 23:06:40 +00:00
Allow reset of otbr network, thread panel fixes (#15815)
This commit is contained in:
parent
c9d709152a
commit
24c3ddb96b
@ -9,3 +9,24 @@ export const getOTBRInfo = (hass: HomeAssistant): Promise<OTBRInfo> =>
|
||||
hass.callWS({
|
||||
type: "otbr/info",
|
||||
});
|
||||
|
||||
export const OTBRCreateNetwork = (hass: HomeAssistant): Promise<void> =>
|
||||
hass.callWS({
|
||||
type: "otbr/create_network",
|
||||
});
|
||||
|
||||
export const OTBRSetNetwork = (
|
||||
hass: HomeAssistant,
|
||||
dataset_id: string
|
||||
): Promise<void> =>
|
||||
hass.callWS({
|
||||
type: "otbr/set_network",
|
||||
dataset_id,
|
||||
});
|
||||
|
||||
export const OTBRGetExtendedAddress = (
|
||||
hass: HomeAssistant
|
||||
): Promise<{ extended_address: string }> =>
|
||||
hass.callWS({
|
||||
type: "otbr/get_extended_address",
|
||||
});
|
||||
|
@ -4,6 +4,7 @@ export interface ThreadRouter {
|
||||
brand: "google" | "apple" | "homeassistant";
|
||||
server: string;
|
||||
extended_pan_id: string;
|
||||
extended_address: string;
|
||||
model_name: string | null;
|
||||
network_name: string;
|
||||
vendor_name: string;
|
||||
@ -87,3 +88,12 @@ export const removeThreadDataSet = (
|
||||
type: "thread/delete_dataset",
|
||||
dataset_id,
|
||||
});
|
||||
|
||||
export const setPreferredThreadDataSet = (
|
||||
hass: HomeAssistant,
|
||||
dataset_id: string
|
||||
): Promise<void> =>
|
||||
hass.callWS({
|
||||
type: "thread/set_preferred_dataset",
|
||||
dataset_id,
|
||||
});
|
||||
|
@ -1,4 +1,5 @@
|
||||
import "@material/mwc-button";
|
||||
import { ActionDetail } from "@material/mwc-list";
|
||||
import {
|
||||
mdiDeleteOutline,
|
||||
mdiDevices,
|
||||
@ -14,11 +15,18 @@ import { extractSearchParam } from "../../../../../common/url/search-params";
|
||||
import "../../../../../components/ha-card";
|
||||
import { getSignedPath } from "../../../../../data/auth";
|
||||
import { getConfigEntryDiagnosticsDownloadUrl } from "../../../../../data/diagnostics";
|
||||
import { getOTBRInfo } from "../../../../../data/otbr";
|
||||
import {
|
||||
getOTBRInfo,
|
||||
OTBRCreateNetwork,
|
||||
OTBRGetExtendedAddress,
|
||||
OTBRInfo,
|
||||
OTBRSetNetwork,
|
||||
} from "../../../../../data/otbr";
|
||||
import {
|
||||
addThreadDataSet,
|
||||
listThreadDataSets,
|
||||
removeThreadDataSet,
|
||||
setPreferredThreadDataSet,
|
||||
subscribeDiscoverThreadRouters,
|
||||
ThreadDataSet,
|
||||
ThreadRouter,
|
||||
@ -54,6 +62,8 @@ export class ThreadConfigPanel extends SubscribeMixin(LitElement) {
|
||||
|
||||
@state() private _datasets: ThreadDataSet[] = [];
|
||||
|
||||
@state() private _otbrInfo?: OTBRInfo & { extended_address?: string };
|
||||
|
||||
protected render(): TemplateResult {
|
||||
const networks = this._groupRoutersByNetwork(this._routers, this._datasets);
|
||||
|
||||
@ -82,11 +92,13 @@ export class ThreadConfigPanel extends SubscribeMixin(LitElement) {
|
||||
"ui.panel.config.thread.add_dataset_from_tlv"
|
||||
)}</mwc-list-item
|
||||
>
|
||||
<mwc-list-item @click=${this._addOTBR}
|
||||
>${this.hass.localize(
|
||||
"ui.panel.config.thread.add_open_thread_border_router"
|
||||
)}</mwc-list-item
|
||||
>
|
||||
${!this._otbrInfo
|
||||
? html`<mwc-list-item @click=${this._addOTBR}
|
||||
>${this.hass.localize(
|
||||
"ui.panel.config.thread.add_open_thread_border_router"
|
||||
)}</mwc-list-item
|
||||
>`
|
||||
: ""}
|
||||
</ha-button-menu>
|
||||
<div class="content">
|
||||
<h1>${this.hass.localize("ui.panel.config.thread.my_network")}</h1>
|
||||
@ -150,7 +162,13 @@ export class ThreadConfigPanel extends SubscribeMixin(LitElement) {
|
||||
</div>
|
||||
${network.routers.map(
|
||||
(router) =>
|
||||
html`<ha-list-item noninteractive twoline graphic="avatar">
|
||||
html`<ha-list-item
|
||||
class="router"
|
||||
twoline
|
||||
graphic="avatar"
|
||||
.hasMeta=${router.extended_address ===
|
||||
this._otbrInfo?.extended_address}
|
||||
>
|
||||
<img
|
||||
slot="graphic"
|
||||
.src=${brandsUrl({
|
||||
@ -166,22 +184,69 @@ export class ThreadConfigPanel extends SubscribeMixin(LitElement) {
|
||||
/>
|
||||
${router.model_name || router.server.replace(".local.", "")}
|
||||
<span slot="secondary">${router.server}</span>
|
||||
${router.extended_address === this._otbrInfo?.extended_address
|
||||
? html`<ha-button-menu
|
||||
corner="BOTTOM_START"
|
||||
slot="meta"
|
||||
@action=${this._handleRouterAction}
|
||||
>
|
||||
<ha-icon-button
|
||||
.label=${this.hass.localize(
|
||||
"ui.common.overflow_menu"
|
||||
)}
|
||||
.path=${mdiDotsVertical}
|
||||
slot="trigger"
|
||||
></ha-icon-button
|
||||
><ha-list-item
|
||||
>${this.hass.localize(
|
||||
"ui.panel.config.thread.reset_border_router"
|
||||
)}</ha-list-item
|
||||
>${network.dataset?.preferred
|
||||
? ""
|
||||
: html`<ha-list-item
|
||||
>${this.hass.localize(
|
||||
"ui.panel.config.thread.add_to_my_network"
|
||||
)}</ha-list-item
|
||||
></ha-button-menu
|
||||
>`}</ha-button-menu
|
||||
>`
|
||||
: ""}
|
||||
</ha-list-item>`
|
||||
)}`
|
||||
: html`<div class="card-content no-routers">
|
||||
<ha-svg-icon .path=${mdiDevices}></ha-svg-icon>
|
||||
${this.hass.localize("ui.panel.config.thread.no_border_routers")}
|
||||
</div>`}
|
||||
${network.dataset?.extended_pan_id &&
|
||||
this._otbrInfo?.active_dataset_tlvs?.includes(
|
||||
network.dataset.extended_pan_id
|
||||
)
|
||||
? html`${this.hass.localize(
|
||||
"ui.panel.config.thread.no_routers_otbr_network"
|
||||
)}
|
||||
<mwc-button @click=${this._resetBorderRouter}
|
||||
>${this.hass.localize(
|
||||
"ui.panel.config.thread.reset_border_router"
|
||||
)}</mwc-button
|
||||
>`
|
||||
: this.hass.localize("ui.panel.config.thread.no_border_routers")}
|
||||
</div> `}
|
||||
${network.dataset && !network.dataset.preferred
|
||||
? html`<div class="card-actions">
|
||||
<mwc-button
|
||||
.datasetId=${network.dataset.dataset_id}
|
||||
@click=${this._setPreferred}
|
||||
>Make preferred network</mwc-button
|
||||
>
|
||||
</div>`
|
||||
: ""}
|
||||
</ha-card>`;
|
||||
}
|
||||
|
||||
private async _showDatasetInfo(ev: Event) {
|
||||
const dataset = (ev.currentTarget as any).networkDataset as ThreadDataSet;
|
||||
if (isComponentLoaded(this.hass, "otbr")) {
|
||||
const otbrInfo = await getOTBRInfo(this.hass);
|
||||
if (this._otbrInfo) {
|
||||
if (
|
||||
dataset.extended_pan_id &&
|
||||
otbrInfo.active_dataset_tlvs?.includes(dataset.extended_pan_id)
|
||||
this._otbrInfo.active_dataset_tlvs?.includes(dataset.extended_pan_id)
|
||||
) {
|
||||
showAlertDialog(this, {
|
||||
title: dataset.network_name,
|
||||
@ -189,8 +254,8 @@ export class ThreadConfigPanel extends SubscribeMixin(LitElement) {
|
||||
Dataset id: ${dataset.dataset_id}<br />
|
||||
Pan id: ${dataset.pan_id}<br />
|
||||
Extended Pan id: ${dataset.extended_pan_id}<br />
|
||||
OTBR URL: ${otbrInfo.url}<br />
|
||||
Active dataset TLVs: ${otbrInfo.active_dataset_tlvs}`,
|
||||
OTBR URL: ${this._otbrInfo.url}<br />
|
||||
Active dataset TLVs: ${this._otbrInfo.active_dataset_tlvs}`,
|
||||
});
|
||||
return;
|
||||
}
|
||||
@ -236,18 +301,21 @@ export class ThreadConfigPanel extends SubscribeMixin(LitElement) {
|
||||
let preferred: ThreadNetwork | undefined;
|
||||
const networks: { [key: string]: ThreadNetwork } = {};
|
||||
for (const router of routers) {
|
||||
const network = router.network_name;
|
||||
const network = router.extended_pan_id;
|
||||
if (network in networks) {
|
||||
networks[network].routers!.push(router);
|
||||
} else {
|
||||
networks[network] = { name: network, routers: [router] };
|
||||
networks[network] = { name: router.network_name, routers: [router] };
|
||||
}
|
||||
}
|
||||
for (const dataset of datasets) {
|
||||
const network = dataset.network_name;
|
||||
const network = dataset.extended_pan_id;
|
||||
if (!network) {
|
||||
continue;
|
||||
}
|
||||
if (dataset.preferred) {
|
||||
preferred = {
|
||||
name: network,
|
||||
name: dataset.network_name,
|
||||
dataset: dataset,
|
||||
routers: networks[network]?.routers,
|
||||
};
|
||||
@ -257,7 +325,7 @@ export class ThreadConfigPanel extends SubscribeMixin(LitElement) {
|
||||
if (network in networks) {
|
||||
networks[network].dataset = dataset;
|
||||
} else {
|
||||
networks[network] = { name: network, dataset: dataset };
|
||||
networks[network] = { name: dataset.network_name, dataset: dataset };
|
||||
}
|
||||
}
|
||||
return {
|
||||
@ -269,10 +337,24 @@ export class ThreadConfigPanel extends SubscribeMixin(LitElement) {
|
||||
}
|
||||
);
|
||||
|
||||
private _refresh() {
|
||||
private async _refresh() {
|
||||
listThreadDataSets(this.hass).then((datasets) => {
|
||||
this._datasets = datasets.datasets;
|
||||
});
|
||||
if (!isComponentLoaded(this.hass, "otbr")) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const _otbrAddress = OTBRGetExtendedAddress(this.hass);
|
||||
const _otbrInfo = getOTBRInfo(this.hass);
|
||||
const [otbrAddress, otbrInfo] = await Promise.all([
|
||||
_otbrAddress,
|
||||
_otbrInfo,
|
||||
]);
|
||||
this._otbrInfo = { ...otbrAddress, ...otbrInfo };
|
||||
} catch (err) {
|
||||
this._otbrInfo = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
private async _signUrl(ev) {
|
||||
@ -295,6 +377,74 @@ export class ThreadConfigPanel extends SubscribeMixin(LitElement) {
|
||||
});
|
||||
}
|
||||
|
||||
private _handleRouterAction(ev: CustomEvent<ActionDetail>) {
|
||||
switch (ev.detail.index) {
|
||||
case 0:
|
||||
this._resetBorderRouter();
|
||||
break;
|
||||
case 1:
|
||||
this._setDataset();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private async _resetBorderRouter() {
|
||||
const confirm = await showConfirmationDialog(this, {
|
||||
title: this.hass.localize(
|
||||
"ui.panel.config.thread.confirm_reset_border_router"
|
||||
),
|
||||
text: this.hass.localize(
|
||||
"ui.panel.config.thread.confirm_reset_border_router_text"
|
||||
),
|
||||
});
|
||||
if (!confirm) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
await OTBRCreateNetwork(this.hass);
|
||||
} catch (err: any) {
|
||||
showAlertDialog(this, {
|
||||
title: this.hass.localize("ui.panel.config.thread.otbr_config_failed"),
|
||||
text: err.message,
|
||||
});
|
||||
}
|
||||
this._refresh();
|
||||
}
|
||||
|
||||
private async _setDataset() {
|
||||
const networks = this._groupRoutersByNetwork(this._routers, this._datasets);
|
||||
const preferedDatasetId = networks.preferred?.dataset?.dataset_id;
|
||||
if (!preferedDatasetId) {
|
||||
return;
|
||||
}
|
||||
const confirm = await showConfirmationDialog(this, {
|
||||
title: this.hass.localize(
|
||||
"ui.panel.config.thread.confirm_set_dataset_border_router"
|
||||
),
|
||||
text: this.hass.localize(
|
||||
"ui.panel.config.thread.confirm_set_dataset_border_router_text"
|
||||
),
|
||||
});
|
||||
if (!confirm) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
await OTBRSetNetwork(this.hass, preferedDatasetId);
|
||||
} catch (err: any) {
|
||||
showAlertDialog(this, {
|
||||
title: this.hass.localize("ui.panel.config.thread.otbr_config_failed"),
|
||||
text: err.message,
|
||||
});
|
||||
}
|
||||
this._refresh();
|
||||
}
|
||||
|
||||
private async _setPreferred(ev) {
|
||||
const datasetId = ev.target.datasetId;
|
||||
await setPreferredThreadDataSet(this.hass, datasetId);
|
||||
this._refresh();
|
||||
}
|
||||
|
||||
private async _addTLV() {
|
||||
const tlv = await showPromptDialog(this, {
|
||||
title: this.hass.localize("ui.panel.config.thread.add_dataset"),
|
||||
@ -355,6 +505,12 @@ export class ThreadConfigPanel extends SubscribeMixin(LitElement) {
|
||||
margin: 0 auto;
|
||||
direction: ltr;
|
||||
}
|
||||
ha-list-item.router {
|
||||
--mdc-list-side-padding: 16px;
|
||||
--mdc-list-item-meta-size: 48px;
|
||||
cursor: default;
|
||||
overflow: visible;
|
||||
}
|
||||
ha-button-menu a {
|
||||
text-decoration: none;
|
||||
}
|
||||
@ -365,6 +521,7 @@ export class ThreadConfigPanel extends SubscribeMixin(LitElement) {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
}
|
||||
.no-routers ha-svg-icon {
|
||||
background-color: var(--light-primary-color);
|
||||
|
@ -3292,10 +3292,18 @@
|
||||
"my_network": "My network",
|
||||
"no_preferred_network": "You don't have a preferred network yet.",
|
||||
"add_open_thread_border_router": "Add an OpenThread border router",
|
||||
"reset_border_router": "Reset border router",
|
||||
"add_to_my_network": "Add to my network",
|
||||
"no_routers_otbr_network": "No border routers where found, maybe the border router is not configured correctly. You can try to reset it to the factory settings.",
|
||||
"add_dataset_from_tlv": "Add dataset from TLV",
|
||||
"add_dataset": "Add Thread dataset",
|
||||
"add_dataset_label": "Operational dataset TLV",
|
||||
"add_dataset_button": "Add dataset",
|
||||
"confirm_reset_border_router": "Reset border router?",
|
||||
"confirm_reset_border_router_text": "This will reset the Home Assistant border router to its factory defaults and form a new Thread network. The old network may no longer be available, and any devices that were attached to this network may need to be recomissioned.",
|
||||
"confirm_set_dataset_border_router": "Reconfigure border router?",
|
||||
"confirm_set_dataset_border_router_text": "This will reconfigure the Home Assistant border router to use a different Thread network. The old network may no longer be available, and any devices that were attached to this network may need to be recomissioned.",
|
||||
"otbr_config_failed": "Failed to configure the border router",
|
||||
"confirm_delete_dataset": "Delete {name} dataset?",
|
||||
"confirm_delete_dataset_text": "This network will be removed from Home Assistant.",
|
||||
"no_border_routers": "No border routers found",
|
||||
|
Loading…
x
Reference in New Issue
Block a user