diff --git a/src/data/otbr.ts b/src/data/otbr.ts index 8af577efd34d..ec831bf67184 100644 --- a/src/data/otbr.ts +++ b/src/data/otbr.ts @@ -5,33 +5,44 @@ export interface OTBRInfo { border_agent_id: string; channel: number; extended_address: string; + extended_pan_id: string; url: string; } -export const getOTBRInfo = (hass: HomeAssistant): Promise => +export type OTBRInfoDict = Record; + +export const getOTBRInfo = (hass: HomeAssistant): Promise => hass.callWS({ type: "otbr/info", }); -export const OTBRCreateNetwork = (hass: HomeAssistant): Promise => +export const OTBRCreateNetwork = ( + hass: HomeAssistant, + extended_address: string +): Promise => hass.callWS({ type: "otbr/create_network", + extended_address, }); export const OTBRSetNetwork = ( hass: HomeAssistant, + extended_address: string, dataset_id: string ): Promise => hass.callWS({ type: "otbr/set_network", + extended_address, dataset_id, }); export const OTBRSetChannel = ( hass: HomeAssistant, + extended_address: string, channel: number ): Promise<{ delay: number }> => hass.callWS({ type: "otbr/set_channel", + extended_address, channel, }); 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 3d99a4f9e369..aa4a98a867f0 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 @@ -28,6 +28,7 @@ import { getConfigEntryDiagnosticsDownloadUrl } from "../../../../../data/diagno import { OTBRCreateNetwork, OTBRInfo, + OTBRInfoDict, OTBRSetChannel, OTBRSetNetwork, getOTBRInfo, @@ -75,7 +76,7 @@ export class ThreadConfigPanel extends SubscribeMixin(LitElement) { @state() private _datasets: ThreadDataSet[] = []; - @state() private _otbrInfo?: OTBRInfo; + @state() private _otbrInfo?: OTBRInfoDict; protected render(): TemplateResult { const networks = this._groupRoutersByNetwork(this._routers, this._datasets); @@ -160,25 +161,36 @@ export class ThreadConfigPanel extends SubscribeMixin(LitElement) { } private _renderNetwork(network: ThreadNetwork) { + const otbrForNetwork = + this._otbrInfo && + network.dataset && + ((network.dataset.preferred_extended_address && + this._otbrInfo[network.dataset.preferred_extended_address]) || + Object.values(this._otbrInfo).find( + (otbr) => otbr.extended_pan_id === network.dataset!.extended_pan_id + )); const canImportKeychain = this.hass.auth.external?.config.canTransferThreadCredentialsToKeychain && - network.dataset?.extended_pan_id && - this._otbrInfo && - this._otbrInfo?.active_dataset_tlvs?.includes( - network.dataset.extended_pan_id - ); + otbrForNetwork; return html`
${network.name}${network.dataset ? html`
${!network.dataset.preferred && !network.routers?.length ? html`
${network.routers.map((router) => { + const otbr = + this._otbrInfo && this._otbrInfo[router.extended_address]; const showOverflow = - ("dataset" in network && router.border_agent_id) || - router.extended_address === this._otbrInfo?.extended_address; + ("dataset" in network && router.border_agent_id) || otbr; return html` ` : ""} - ${router.extended_address === - this._otbrInfo?.extended_address + ${otbr ? html` ${this.hass.localize( "ui.panel.config.thread.reset_border_router" @@ -288,14 +301,13 @@ export class ThreadConfigPanel extends SubscribeMixin(LitElement) { })}` : html`
- ${network.dataset?.extended_pan_id && - this._otbrInfo?.active_dataset_tlvs?.includes( - network.dataset.extended_pan_id - ) + ${otbrForNetwork ? html`${this.hass.localize( "ui.panel.config.thread.no_routers_otbr_network" )} - ${this.hass.localize( "ui.panel.config.thread.reset_border_router" )} - Send credentials to phone
` @@ -321,23 +333,25 @@ export class ThreadConfigPanel extends SubscribeMixin(LitElement) { `; } - private _sendCredentials() { - if (!this._otbrInfo) { + private _sendCredentials(ev) { + const otbr = (ev.currentTarget as any).otbr as OTBRInfo; + if (!otbr) { return; } this.hass.auth.external!.fireMessage({ type: "thread/store_in_platform_keychain", payload: { - mac_extended_address: this._otbrInfo.extended_address, - border_agent_id: this._otbrInfo.border_agent_id ?? "", - active_operational_dataset: this._otbrInfo.active_dataset_tlvs ?? "", + mac_extended_address: otbr.extended_address, + border_agent_id: otbr.border_agent_id ?? "", + active_operational_dataset: otbr.active_dataset_tlvs ?? "", }, }); } private async _showDatasetInfo(ev: Event) { const network = (ev.currentTarget as any).network as ThreadNetwork; - showThreadDatasetDialog(this, { network, otbrInfo: this._otbrInfo }); + const otbr = (ev.currentTarget as any).otbr as OTBRInfo; + showThreadDatasetDialog(this, { network, otbrInfo: otbr }); } private _importExternalThreadCredentials() { @@ -454,6 +468,7 @@ export class ThreadConfigPanel extends SubscribeMixin(LitElement) { private _handleRouterAction(ev: CustomEvent) { const network = (ev.currentTarget as any).network as ThreadNetwork; const router = (ev.currentTarget as any).router as ThreadRouter; + const otbr = (ev.currentTarget as any).otbr as OTBRInfo; const index = network.dataset && router.border_agent_id ? Number(ev.detail.index) @@ -463,18 +478,23 @@ export class ThreadConfigPanel extends SubscribeMixin(LitElement) { this._setPreferredBorderAgent(network.dataset!, router); break; case 1: - this._resetBorderRouter(); + this._resetBorderRouter(otbr); break; case 2: - this._changeChannel(); + this._changeChannel(otbr); break; case 3: - this._setDataset(); + this._setDataset(otbr); break; } } - private async _resetBorderRouter() { + private _resetBorderRouterEvent(ev) { + const otbr = (ev.currentTarget as any).otbr as OTBRInfo; + this._resetBorderRouter(otbr); + } + + private async _resetBorderRouter(otbr: OTBRInfo) { const confirm = await showConfirmationDialog(this, { title: this.hass.localize( "ui.panel.config.thread.confirm_reset_border_router" @@ -487,7 +507,7 @@ export class ThreadConfigPanel extends SubscribeMixin(LitElement) { return; } try { - await OTBRCreateNetwork(this.hass); + await OTBRCreateNetwork(this.hass, otbr.extended_address); } catch (err: any) { showAlertDialog(this, { title: this.hass.localize("ui.panel.config.thread.otbr_config_failed"), @@ -497,7 +517,7 @@ export class ThreadConfigPanel extends SubscribeMixin(LitElement) { this._refresh(); } - private async _setDataset() { + private async _setDataset(otbr: OTBRInfo) { const networks = this._groupRoutersByNetwork(this._routers, this._datasets); const preferedDatasetId = networks.preferred?.dataset?.dataset_id; if (!preferedDatasetId) { @@ -515,7 +535,7 @@ export class ThreadConfigPanel extends SubscribeMixin(LitElement) { return; } try { - await OTBRSetNetwork(this.hass, preferedDatasetId); + await OTBRSetNetwork(this.hass, otbr.extended_address, preferedDatasetId); } catch (err: any) { showAlertDialog(this, { title: this.hass.localize("ui.panel.config.thread.otbr_config_failed"), @@ -595,8 +615,8 @@ export class ThreadConfigPanel extends SubscribeMixin(LitElement) { this._refresh(); } - private async _changeChannel() { - const currentChannel = this._otbrInfo?.channel; + private async _changeChannel(otbr: OTBRInfo) { + const currentChannel = otbr.channel; const channelStr = await showPromptDialog(this, { title: this.hass.localize("ui.panel.config.thread.change_channel"), text: this.hass.localize("ui.panel.config.thread.change_channel_text"), @@ -623,7 +643,11 @@ export class ThreadConfigPanel extends SubscribeMixin(LitElement) { return; } try { - const result = await OTBRSetChannel(this.hass, channel); + const result = await OTBRSetChannel( + this.hass, + otbr.extended_address, + channel + ); showAlertDialog(this, { title: this.hass.localize( "ui.panel.config.thread.change_channel_initiated_title" diff --git a/src/translations/en.json b/src/translations/en.json index 822152b2214e..13fa24f4870d 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -4550,7 +4550,9 @@ "change_channel_multiprotocol_enabled_title": "The Thread radio has multiprotocol enabled", "change_channel_multiprotocol_enabled_text": "To change channel when the Thread radio has multiprotocol enabled, please use the hardware settings menu.", "change_channel_range": "Channel must be in the range 11 to 26", - "change_channel_text": "Initiating a channel change for your Home Assistant Thread network should be performed with caution. Some Thread devices may not migrate to the new channel automatically and, if the new channel is congested, your Thread devices may become intermittently unavailable. Some devices may need to be manually re-joined to your Thread network before they show in Home Assistant again. This action cannot be reversed (without performing another channel change)." + "change_channel_text": "Initiating a channel change for your Home Assistant Thread network should be performed with caution. Some Thread devices may not migrate to the new channel automatically and, if the new channel is congested, your Thread devices may become intermittently unavailable. Some devices may need to be manually re-joined to your Thread network before they show in Home Assistant again. This action cannot be reversed (without performing another channel change).", + "thread_network_info": "Thread network information", + "thread_network_delete_credentials": "Delete Thread network credentials" }, "zha": { "common": {