diff --git a/src/data/thread.ts b/src/data/thread.ts index e3016b9416..044e44c9a0 100644 --- a/src/data/thread.ts +++ b/src/data/thread.ts @@ -10,13 +10,13 @@ export interface ThreadRouter { } export interface ThreadDataSet { - created; - dataset_id; - extended_pan_id; - network_name: string; - pan_id; + created: string; + dataset_id: string; preferred: boolean; - source; + source: string; + network_name: string; + extended_pan_id?: string; + pan_id?: string; } export interface ThreadRouterDiscoveryEvent { @@ -61,3 +61,29 @@ export const listThreadDataSets = ( hass.callWS({ type: "thread/list_datasets", }); + +export const getThreadDataSetTLV = ( + hass: HomeAssistant, + dataset_id: string +): Promise<{ tlv: string }> => + hass.callWS({ type: "thread/get_dataset_tlv", dataset_id }); + +export const addThreadDataSet = ( + hass: HomeAssistant, + source: string, + tlv: string +): Promise => + hass.callWS({ + type: "thread/add_dataset_tlv", + source, + tlv, + }); + +export const removeThreadDataSet = ( + hass: HomeAssistant, + dataset_id: string +): Promise => + hass.callWS({ + type: "thread/delete_dataset", + dataset_id, + }); diff --git a/src/panels/config/integrations/integration-panels/thread/thread-config-panel.ts b/src/panels/config/integrations/integration-panels/thread/thread-config-panel.ts index 13c935e4d2..07f2b58c4b 100644 --- a/src/panels/config/integrations/integration-panels/thread/thread-config-panel.ts +++ b/src/panels/config/integrations/integration-panels/thread/thread-config-panel.ts @@ -1,5 +1,10 @@ import "@material/mwc-button"; -import { mdiDevices, mdiDotsVertical, mdiInformationOutline } from "@mdi/js"; +import { + mdiDeleteOutline, + mdiDevices, + mdiDotsVertical, + mdiInformationOutline, +} from "@mdi/js"; import { css, html, LitElement, PropertyValues, TemplateResult } from "lit"; import { customElement, property, state } from "lit/decorators"; import memoizeOne from "memoize-one"; @@ -11,13 +16,19 @@ import { getSignedPath } from "../../../../../data/auth"; import { getConfigEntryDiagnosticsDownloadUrl } from "../../../../../data/diagnostics"; import { getOTBRInfo } from "../../../../../data/otbr"; import { + addThreadDataSet, listThreadDataSets, + removeThreadDataSet, subscribeDiscoverThreadRouters, ThreadDataSet, ThreadRouter, } from "../../../../../data/thread"; import { showConfigFlowDialog } from "../../../../../dialogs/config-flow/show-dialog-config-flow"; -import { showAlertDialog } from "../../../../../dialogs/generic/show-dialog-box"; +import { + showAlertDialog, + showConfirmationDialog, + showPromptDialog, +} from "../../../../../dialogs/generic/show-dialog-box"; import "../../../../../layouts/hass-subpage"; import { SubscribeMixin } from "../../../../../mixins/subscribe-mixin"; import { haStyle } from "../../../../../resources/styles"; @@ -66,6 +77,11 @@ export class ThreadConfigPanel extends SubscribeMixin(LitElement) { )} + ${this.hass.localize( + "ui.panel.config.thread.add_dataset_from_tlv" + )} ${this.hass.localize( "ui.panel.config.thread.add_open_thread_border_router" @@ -108,11 +124,20 @@ export class ThreadConfigPanel extends SubscribeMixin(LitElement) { return html`
${network.name}${network.dataset - ? html`` + ? html`
+ ${!network.dataset.preferred && !network.routers?.length + ? html`` + : ""} +
` : ""}
${network.routers?.length @@ -154,7 +179,10 @@ export class ThreadConfigPanel extends SubscribeMixin(LitElement) { const dataset = (ev.currentTarget as any).networkDataset as ThreadDataSet; if (isComponentLoaded(this.hass, "otbr")) { const otbrInfo = await getOTBRInfo(this.hass); - if (otbrInfo.active_dataset_tlvs.includes(dataset.extended_pan_id)) { + if ( + dataset.extended_pan_id && + otbrInfo.active_dataset_tlvs?.includes(dataset.extended_pan_id) + ) { showAlertDialog(this, { title: dataset.network_name, text: html`Network name: ${dataset.network_name}
@@ -267,6 +295,57 @@ export class ThreadConfigPanel extends SubscribeMixin(LitElement) { }); } + private async _addTLV() { + const tlv = await showPromptDialog(this, { + title: this.hass.localize("ui.panel.config.thread.add_dataset"), + inputLabel: this.hass.localize( + "ui.panel.config.thread.add_dataset_label" + ), + confirmText: this.hass.localize( + "ui.panel.config.thread.add_dataset_button" + ), + }); + if (!tlv) { + return; + } + try { + await addThreadDataSet(this.hass, "manual", tlv); + } catch (err: any) { + showAlertDialog(this, { + title: "Error", + text: err.message || err, + }); + } + this._refresh(); + } + + private async _removeDataset(ev: Event) { + const dataset = (ev.currentTarget as any).networkDataset as ThreadDataSet; + const confirm = await showConfirmationDialog(this, { + title: this.hass.localize( + "ui.panel.config.thread.confirm_delete_dataset", + { name: dataset.network_name } + ), + text: this.hass.localize( + "ui.panel.config.thread.confirm_delete_dataset_text" + ), + destructive: true, + confirmText: this.hass.localize("ui.common.delete"), + }); + if (!confirm) { + return; + } + try { + await removeThreadDataSet(this.hass, dataset.dataset_id); + } catch (err: any) { + showAlertDialog(this, { + title: "Error", + text: err.message || err, + }); + } + this._refresh(); + } + static styles = [ haStyle, css` diff --git a/src/translations/en.json b/src/translations/en.json index 9b76b164e6..020ae10cb3 100755 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -3288,6 +3288,12 @@ "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", + "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_delete_dataset": "Delete {name} dataset?", + "confirm_delete_dataset_text": "This network will be removed from Home Assistant.", "no_border_routers": "No border routers found", "border_routers": "{count} border {count, plural,\n one {router}\n other {routers}\n}", "managed_by_home_assistant": "Managed by Home Assistant",