From aa34f22c10cd40d8bd8a2ab6ea53b5e7ada0c26a Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Mon, 6 May 2024 17:24:13 +0200 Subject: [PATCH 1/3] Allow storing thread credentials in phone keychain --- src/external_app/external_messaging.ts | 13 ++- .../thread/dialog-thread-dataset.ts | 87 +++++++++++++++++++ .../thread/show-dialog-thread-dataset.ts | 19 ++++ .../thread/thread-config-panel.ts | 34 ++------ 4 files changed, 123 insertions(+), 30 deletions(-) create mode 100644 src/panels/config/integrations/integration-panels/thread/dialog-thread-dataset.ts create mode 100644 src/panels/config/integrations/integration-panels/thread/show-dialog-thread-dataset.ts diff --git a/src/external_app/external_messaging.ts b/src/external_app/external_messaging.ts index e533a453cb8d..c4d56f8aacf1 100644 --- a/src/external_app/external_messaging.ts +++ b/src/external_app/external_messaging.ts @@ -129,6 +129,15 @@ interface EMOutgoingMessageAssistShow extends EMMessage { }; } +interface EMOutgoingMessageThreadStoreInPlatformKeychain extends EMMessage { + type: "thread/store_in_platform_keychain"; + payload: { + mac_extended_address: string; + border_agent_id: string | null; + active_operational_dataset: string; + }; +} + type EMOutgoingMessageWithoutAnswer = | EMMessageResultError | EMMessageResultSuccess @@ -146,7 +155,8 @@ type EMOutgoingMessageWithoutAnswer = | EMOutgoingMessageMatterCommission | EMOutgoingMessageSidebarShow | EMOutgoingMessageTagWrite - | EMOutgoingMessageThemeUpdate; + | EMOutgoingMessageThemeUpdate + | EMOutgoingMessageThreadStoreInPlatformKeychain; interface EMIncomingMessageRestart { id: number; @@ -239,6 +249,7 @@ export interface ExternalConfig { hasExoPlayer: boolean; canCommissionMatter: boolean; canImportThreadCredentials: boolean; + canTransferThreadCredentialsToKeychain: boolean; hasAssist: boolean; hasBarCodeScanner: number; } diff --git a/src/panels/config/integrations/integration-panels/thread/dialog-thread-dataset.ts b/src/panels/config/integrations/integration-panels/thread/dialog-thread-dataset.ts new file mode 100644 index 000000000000..c92bf8376184 --- /dev/null +++ b/src/panels/config/integrations/integration-panels/thread/dialog-thread-dataset.ts @@ -0,0 +1,87 @@ +import { LitElement, html, nothing } from "lit"; +import { customElement, property, state } from "lit/decorators"; +import { fireEvent } from "../../../../../common/dom/fire_event"; +import { HassDialog } from "../../../../../dialogs/make-dialog-manager"; +import { HomeAssistant } from "../../../../../types"; +import { DialogThreadDatasetParams } from "./show-dialog-thread-dataset"; +import { createCloseHeading } from "../../../../../components/ha-dialog"; + +@customElement("ha-dialog-thread-dataset") +class DialogThreadDataset extends LitElement implements HassDialog { + @property({ attribute: false }) public hass!: HomeAssistant; + + @state() private _params?: DialogThreadDatasetParams; + + public async showDialog( + params: DialogThreadDatasetParams + ): Promise> { + this._params = params; + } + + public closeDialog(): void { + this._params = undefined; + fireEvent(this, "dialog-closed", { dialog: this.localName }); + } + + protected render() { + if (!this._params) { + return nothing; + } + const network = this._params.network; + const dataset = network.dataset!; + const otbrInfo = this._params.otbrInfo; + + const hasOTBR = + otbrInfo && + dataset.extended_pan_id && + otbrInfo.active_dataset_tlvs?.includes(dataset.extended_pan_id); + + const canImportKeychain = + hasOTBR && + !this.hass.auth.external?.config.canTransferThreadCredentialsToKeychain && + network.routers?.length; + + return html` +
+ Network name: ${dataset.network_name}
+ Channel: ${dataset.channel}
+ Dataset id: ${dataset.dataset_id}
+ Pan id: ${dataset.pan_id}
+ Extended Pan id: ${dataset.extended_pan_id}
+ + ${hasOTBR + ? html`OTBR URL: ${otbrInfo.url}
+ Active dataset TLVs: ${otbrInfo.active_dataset_tlvs}` + : nothing} +
+ ${canImportKeychain + ? html`Send credentials to phone` + : nothing} +
`; + } + + private _sendCredentials() { + this.hass.auth.external!.fireMessage({ + type: "thread/store_in_platform_keychain", + payload: { + mac_extended_address: + this._params!.network.routers![0]!.extended_pan_id, + border_agent_id: this._params!.network.routers![0]!.border_agent_id, + active_operational_dataset: this._params!.otbrInfo!.active_dataset_tlvs, + }, + }); + } +} + +declare global { + interface HTMLElementTagNameMap { + "ha-dialog-thread-dataset": DialogThreadDataset; + } +} diff --git a/src/panels/config/integrations/integration-panels/thread/show-dialog-thread-dataset.ts b/src/panels/config/integrations/integration-panels/thread/show-dialog-thread-dataset.ts new file mode 100644 index 000000000000..c2be30410710 --- /dev/null +++ b/src/panels/config/integrations/integration-panels/thread/show-dialog-thread-dataset.ts @@ -0,0 +1,19 @@ +import { fireEvent } from "../../../../../common/dom/fire_event"; +import { OTBRInfo } from "../../../../../data/otbr"; +import { ThreadNetwork } from "./thread-config-panel"; + +export interface DialogThreadDatasetParams { + network: ThreadNetwork; + otbrInfo?: OTBRInfo; +} + +export const showThreadDatasetDialog = ( + element: HTMLElement, + dialogParams: DialogThreadDatasetParams +): void => { + fireEvent(element, "show-dialog", { + dialogTag: "ha-dialog-thread-dataset", + dialogImport: () => import("./dialog-thread-dataset"), + dialogParams, + }); +}; 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 7a5375d8b207..daf166180384 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 @@ -55,8 +55,9 @@ import { HomeAssistant } from "../../../../../types"; import { brandsUrl } from "../../../../../util/brands-url"; import { fileDownload } from "../../../../../util/file_download"; import { documentationUrl } from "../../../../../util/documentation-url"; +import { showThreadDatasetDialog } from "./show-dialog-thread-dataset"; -interface ThreadNetwork { +export interface ThreadNetwork { name: string; dataset?: ThreadDataSet; routers?: ThreadRouter[]; @@ -164,7 +165,7 @@ export class ThreadConfigPanel extends SubscribeMixin(LitElement) { ${network.name}${network.dataset ? html`
- Channel: ${dataset.channel}
- Dataset id: ${dataset.dataset_id}
- Pan id: ${dataset.pan_id}
- Extended Pan id: ${dataset.extended_pan_id}
- OTBR URL: ${this._otbrInfo.url}
- Active dataset TLVs: ${this._otbrInfo.active_dataset_tlvs}`, - }); - return; - } - } - showAlertDialog(this, { - title: dataset.network_name, - text: html`Network name: ${dataset.network_name}
- Channel: ${dataset.channel}
- Dataset id: ${dataset.dataset_id}
- Pan id: ${dataset.pan_id}
- Extended Pan id: ${dataset.extended_pan_id}`, - }); + const network = (ev.currentTarget as any).network as ThreadNetwork; + showThreadDatasetDialog(this, { network, otbrInfo: this._otbrInfo }); } private _importExternalThreadCredentials() { From b23dea1b48f0ff5ce31b4c5cbeb195eeeaecb6e3 Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Tue, 7 May 2024 15:24:20 +0200 Subject: [PATCH 2/3] Update dialog-thread-dataset.ts --- .../integration-panels/thread/dialog-thread-dataset.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/panels/config/integrations/integration-panels/thread/dialog-thread-dataset.ts b/src/panels/config/integrations/integration-panels/thread/dialog-thread-dataset.ts index c92bf8376184..ee305d6825ab 100644 --- a/src/panels/config/integrations/integration-panels/thread/dialog-thread-dataset.ts +++ b/src/panels/config/integrations/integration-panels/thread/dialog-thread-dataset.ts @@ -72,7 +72,7 @@ class DialogThreadDataset extends LitElement implements HassDialog { type: "thread/store_in_platform_keychain", payload: { mac_extended_address: - this._params!.network.routers![0]!.extended_pan_id, + this._params!.network.routers![0]!.extended_address, border_agent_id: this._params!.network.routers![0]!.border_agent_id, active_operational_dataset: this._params!.otbrInfo!.active_dataset_tlvs, }, From 9aee3db30328477151fff809478a774e27e506f8 Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Tue, 7 May 2024 15:25:44 +0200 Subject: [PATCH 3/3] use preferred of dataset if available --- .../integration-panels/thread/dialog-thread-dataset.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/panels/config/integrations/integration-panels/thread/dialog-thread-dataset.ts b/src/panels/config/integrations/integration-panels/thread/dialog-thread-dataset.ts index ee305d6825ab..191049ecf663 100644 --- a/src/panels/config/integrations/integration-panels/thread/dialog-thread-dataset.ts +++ b/src/panels/config/integrations/integration-panels/thread/dialog-thread-dataset.ts @@ -72,8 +72,11 @@ class DialogThreadDataset extends LitElement implements HassDialog { type: "thread/store_in_platform_keychain", payload: { mac_extended_address: + this._params?.network.dataset?.preferred_extended_address || this._params!.network.routers![0]!.extended_address, - border_agent_id: this._params!.network.routers![0]!.border_agent_id, + border_agent_id: + this._params?.network.dataset?.preferred_border_agent_id || + this._params!.network.routers![0]!.border_agent_id, active_operational_dataset: this._params!.otbrInfo!.active_dataset_tlvs, }, });