Compare commits

..
5 changed files with 105 additions and 6 deletions
@@ -30,6 +30,7 @@ import {
} from "../../../data/config_flow";
import type { IntegrationManifest } from "../../../data/integration";
import { showConfigFlowDialog } from "../../../dialogs/config-flow/show-dialog-config-flow";
import { showMatterAddDeviceDialog } from "./integration-panels/matter/show-dialog-add-matter-device";
import {
showAlertDialog,
showConfirmationDialog,
@@ -182,6 +183,16 @@ export class HaConfigFlowCard extends LitElement {
});
return;
}
// A discovered Matter device is commissioned through the Matter dialog;
// other steps of a Bluetooth sourced flow set up the integration itself.
if (
this.flow.handler === "matter" &&
this.flow.context.source === "bluetooth" &&
this.flow.step_id === "bluetooth_confirm"
) {
showMatterAddDeviceDialog(this, { discoveryFlowId: this.flow.flow_id });
return;
}
showConfigFlowDialog(this, {
continueFlowId: this.flow.flow_id,
navigateToResult: true,
@@ -12,10 +12,13 @@ import "../../../../../components/ha-icon-button-arrow-prev";
import "../../../../../components/ha-button";
import "../../../../../components/ha-dialog";
import type { MatterCommissionFinish } from "../../../../../external_app/external_messaging";
import { handleConfigFlowStep } from "../../../../../data/config_flow";
import {
canCommissionMatterExternal,
commissionMatterDevice,
watchForNewMatterDevice,
} from "../../../../../data/matter";
import type { MatterAddDeviceDialogParams } from "./show-dialog-add-matter-device";
import { haStyleDialog } from "../../../../../resources/styles";
import type { HomeAssistant } from "../../../../../types";
import type { DeviceRegistryEntry } from "../../../../../data/device/device_registry";
@@ -37,6 +40,7 @@ import "./matter-add-device/matter-add-device-main";
import "./matter-add-device/matter-add-device-new";
import "./matter-add-device/matter-add-device-commissioning";
import "./matter-add-device/matter-add-device-device-added";
import "./matter-add-device/matter-add-device-discovered";
import { showToast } from "../../../../../util/toast";
export type MatterAddDeviceStep =
@@ -48,7 +52,8 @@ export type MatterAddDeviceStep =
| "apple_home"
| "generic"
| "commissioning"
| "device_added";
| "device_added"
| "discovered";
declare global {
interface HASSDomEvents {
@@ -68,6 +73,7 @@ const BACK_STEP: Record<MatterAddDeviceStep, MatterAddDeviceStep | undefined> =
generic: "existing",
commissioning: undefined,
device_added: undefined,
discovered: undefined,
};
@customElement("dialog-matter-add-device")
@@ -104,8 +110,15 @@ class DialogMatterAddDevice extends LitElement {
private _unsub?: UnsubscribeFunc;
public showDialog(): void {
private _discoveryFlowId?: string;
public showDialog(params: MatterAddDeviceDialogParams): void {
this._discoveryFlowId = params.discoveryFlowId;
this._open = true;
// A discovered device only needs its code, unless the app can scan it.
if (this._discoveryFlowId && !canCommissionMatterExternal(this.hass)) {
this._step = "discovered";
}
this._unsub = watchForNewMatterDevice(this.hass, (device) => {
// make sure a refresh of the page will navigate to the device page, old iOS apps will refresh the webview when commissioning is done
setRefreshUrl(`/config/devices/device/${device.id}`);
@@ -211,6 +224,7 @@ class DialogMatterAddDevice extends LitElement {
this._open = false;
this._step = "main";
this._pairingCode = "";
this._discoveryFlowId = undefined;
this._newDevice = undefined;
this._mainEntity = undefined;
this._mainEntityFetched = false;
@@ -283,7 +297,7 @@ class DialogMatterAddDevice extends LitElement {
const savedStep = this._step;
try {
this._step = "commissioning";
await commissionMatterDevice(this.hass, code, true);
await this._commission(code);
} catch (_err) {
showToast(this, {
message: this.hass.localize(
@@ -296,6 +310,20 @@ class DialogMatterAddDevice extends LitElement {
// On success, keep showing commissioning spinner until watchForNewMatterDevice fires
}
private async _commission(code: string): Promise<void> {
if (!this._discoveryFlowId) {
await commissionMatterDevice(this.hass, code, true);
return;
}
// The discovery flow commissions over Bluetooth and ends itself on success.
const step = await handleConfigFlowStep(this.hass, this._discoveryFlowId, {
code,
});
if (step.type !== "abort" || step.reason !== "commission_successful") {
throw new Error("Commissioning failed");
}
}
private async _finishDeviceAdded(): Promise<void> {
const device = this._newDevice!;
const { name, area, deviceClass, hasPendingUpdates } =
@@ -377,7 +405,8 @@ class DialogMatterAddDevice extends LitElement {
if (
this._step === "apple_home" ||
this._step === "google_home_fallback" ||
this._step === "generic"
this._step === "generic" ||
this._step === "discovered"
) {
return html`
<ha-button
@@ -0,0 +1,46 @@
import { LitElement, html } from "lit";
import { customElement, property, state } from "lit/decorators";
import { fireEvent } from "../../../../../../common/dom/fire_event";
import "../../../../../../components/input/ha-input";
import type { HomeAssistant } from "../../../../../../types";
import { sharedStyles } from "./matter-add-device-shared-styles";
@customElement("matter-add-device-discovered")
class MatterAddDeviceDiscovered extends LitElement {
@property({ attribute: false }) public hass!: HomeAssistant;
@state() private _code = "";
render() {
return html`
<div class="content">
<p>
${this.hass.localize(
"ui.dialogs.matter-add-device.discovered.code_instructions"
)}
</p>
<ha-input
label=${this.hass.localize(
"ui.dialogs.matter-add-device.discovered.setup_code"
)}
.value=${this._code}
@input=${this._onCodeChanged}
></ha-input>
</div>
`;
}
private _onCodeChanged(ev: InputEvent) {
const value = (ev.target as HTMLInputElement).value;
this._code = value;
fireEvent(this, "pairing-code-changed", { code: value });
}
static styles = [sharedStyles];
}
declare global {
interface HTMLElementTagNameMap {
"matter-add-device-discovered": MatterAddDeviceDiscovered;
}
}
@@ -1,11 +1,19 @@
import { fireEvent } from "../../../../../common/dom/fire_event";
export interface MatterAddDeviceDialogParams {
/** Bluetooth discovery flow to commission through, if any. */
discoveryFlowId?: string;
}
export const loadAddDeviceDialog = () => import("./dialog-matter-add-device");
export const showMatterAddDeviceDialog = (element: HTMLElement): void => {
export const showMatterAddDeviceDialog = (
element: HTMLElement,
dialogParams: MatterAddDeviceDialogParams = {}
): void => {
fireEvent(element, "show-dialog", {
dialogTag: "dialog-matter-add-device",
dialogImport: loadAddDeviceDialog,
dialogParams: {},
dialogParams,
});
};
+5
View File
@@ -2514,6 +2514,11 @@
"code_instructions": "Search for the sharing mode in the app of your controller, and activate it. You will get a setup code, enter that below.",
"setup_code": "Setup code"
},
"discovered": {
"header": "Add discovered device",
"code_instructions": "This device was found nearby over Bluetooth. Enter the setup code printed on the device or its packaging, or the text of its QR code.",
"setup_code": "[%key:ui::dialogs::matter-add-device::generic::setup_code%]"
},
"device_added": {
"header": "Device added",
"finish": "Finish",