Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use Thread BR extended address in WS API calls #21172

Merged
merged 7 commits into from
Jul 17, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
Use extended address in WS API calls
This follows the HA Core change and passes the extended address to the
OTBR WS API calls where necessary. It also follows the new OTBR info
format which potentially includes multiple OTBRs.

This allows to support multiple OTBR managed by a single system.

Note: There is one corner case when none of the OTBR is found via
discovery. In this case we offer to reset the OTBR. Currently we simply
offer this for the primary or first one found.
  • Loading branch information
agners committed Jun 26, 2024
commit 9a6303021ed74839b4f24d69bcc4ce1c205ffe59
17 changes: 15 additions & 2 deletions src/data/otbr.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,33 +5,46 @@ export interface OTBRInfo {
border_agent_id: string;
channel: number;
extended_address: string;
extended_pan_id: string;
url: string;
}

export const getOTBRInfo = (hass: HomeAssistant): Promise<OTBRInfo> =>
export type OTBRInfoDict = {
[key: string]: OTBRInfo;
steverep marked this conversation as resolved.
Show resolved Hide resolved
};

export const getOTBRInfo = (hass: HomeAssistant): Promise<OTBRInfoDict> =>
hass.callWS({
type: "otbr/info",
});

export const OTBRCreateNetwork = (hass: HomeAssistant): Promise<void> =>
export const OTBRCreateNetwork = (
hass: HomeAssistant,
extended_address: string
): Promise<void> =>
hass.callWS({
type: "otbr/create_network",
extended_address,
});

export const OTBRSetNetwork = (
hass: HomeAssistant,
extended_address: string,
dataset_id: string
): Promise<void> =>
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,
});
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import {
OTBRCreateNetwork,
OTBRInfo,
OTBRInfoDict,
agners marked this conversation as resolved.
Show resolved Hide resolved
OTBRSetChannel,
OTBRSetNetwork,
getOTBRInfo,
Expand Down Expand Up @@ -75,7 +76,7 @@

@state() private _datasets: ThreadDataSet[] = [];

@state() private _otbrInfo?: OTBRInfo;
@state() private _otbrInfo?: OTBRInfoDict;

protected render(): TemplateResult {
const networks = this._groupRoutersByNetwork(this._routers, this._datasets);
Expand Down Expand Up @@ -160,19 +161,24 @@
}

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

Check failure on line 170 in src/panels/config/integrations/integration-panels/thread/thread-config-panel.ts

View workflow job for this annotation

GitHub Actions / Lint and check format

'network.dataset' is possibly 'undefined'.
));
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`<ha-card>
<div class="card-header">
${network.name}${network.dataset
? html`<div>
<ha-icon-button
.otbr=${otbrForNetwork}
.network=${network}
.path=${mdiInformationOutline}
@click=${this._showDatasetInfo}
steverep marked this conversation as resolved.
Show resolved Hide resolved
Expand All @@ -196,9 +202,10 @@
</h4>
</div>
${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`<ha-list-item
class="router"
twoline
Expand Down Expand Up @@ -238,6 +245,7 @@
slot="meta"
.network=${network}
.router=${router}
.otbr=${otbr}
@action=${this._handleRouterAction}
>
<ha-icon-button
Expand All @@ -262,8 +270,7 @@
)}
</ha-list-item>`
: ""}
${router.extended_address ===
this._otbrInfo?.extended_address
${otbr
? html`<ha-list-item>
${this.hass.localize(
"ui.panel.config.thread.reset_border_router"
Expand All @@ -288,14 +295,13 @@
})}`
: html`<div class="card-content no-routers">
<ha-svg-icon .path=${mdiDevices}></ha-svg-icon>
${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"
)}
<mwc-button @click=${this._resetBorderRouter}
<mwc-button
.otbr=${otbrForNetwork}
@click=${this._resetBorderRouterEvent}
>${this.hass.localize(
"ui.panel.config.thread.reset_border_router"
)}</mwc-button
Expand All @@ -313,31 +319,33 @@
: ""}
${canImportKeychain
? html`<div class="card-actions">
<mwc-button @click=${this._sendCredentials}
<mwc-button .otbr=${otbrForNetwork} @click=${this._sendCredentials}
>Send credentials to phone</mwc-button
>
</div>`
: ""}
</ha-card>`;
}

private _sendCredentials() {
private _sendCredentials(ev) {
const otbr = (ev.currentTarget as any).otbr as OTBRInfo;
if (!this._otbrInfo) {
agners marked this conversation as resolved.
Show resolved Hide resolved
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 ?? "",
agners marked this conversation as resolved.
Show resolved Hide resolved
},
});
}

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 });
agners marked this conversation as resolved.
Show resolved Hide resolved
}

private _importExternalThreadCredentials() {
Expand Down Expand Up @@ -454,6 +462,7 @@
private _handleRouterAction(ev: CustomEvent<ActionDetail>) {
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;
agners marked this conversation as resolved.
Show resolved Hide resolved
const index =
network.dataset && router.border_agent_id
? Number(ev.detail.index)
Expand All @@ -463,18 +472,23 @@
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;
agners marked this conversation as resolved.
Show resolved Hide resolved
this._resetBorderRouter(otbr);
}

private async _resetBorderRouter(otbr: OTBRInfo) {
agners marked this conversation as resolved.
Show resolved Hide resolved
const confirm = await showConfirmationDialog(this, {
title: this.hass.localize(
"ui.panel.config.thread.confirm_reset_border_router"
Expand All @@ -487,7 +501,7 @@
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"),
Expand All @@ -497,7 +511,7 @@
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) {
Expand All @@ -515,7 +529,7 @@
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"),
Expand Down Expand Up @@ -595,8 +609,8 @@
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"),
Expand All @@ -623,7 +637,11 @@
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"
Expand Down
Loading