Add question based matter commissioning flow (#20188)
* Create add device dialog * Add translations and shared design * Disable add buttons when no code * Use right endpoint * Add loading state * Update logos * Add native flow * Add store links to download app * Always display qr code and link * Update translations * Share assets and translations with app dialog
This commit is contained in:
parent
eb4ae926b7
commit
5547bc7356
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 7.1 KiB |
Binary file not shown.
After Width: | Height: | Size: 19 KiB |
Binary file not shown.
After Width: | Height: | Size: 8.6 KiB |
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 6.2 KiB |
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 52 KiB |
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 69 KiB |
File diff suppressed because one or more lines are too long
|
@ -1,17 +1,56 @@
|
|||
import "@material/mwc-button/mwc-button";
|
||||
import { mdiClose } from "@mdi/js";
|
||||
import { UnsubscribeFunc } from "home-assistant-js-websocket";
|
||||
import { css, html, LitElement, nothing } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { dynamicElement } from "../../../../../common/dom/dynamic-element-directive";
|
||||
import { fireEvent } from "../../../../../common/dom/fire_event";
|
||||
import "../../../../../components/ha-circular-progress";
|
||||
import { createCloseHeading } from "../../../../../components/ha-dialog";
|
||||
import "../../../../../components/ha-dialog-header";
|
||||
import "../../../../../components/ha-icon-button";
|
||||
import "../../../../../components/ha-icon-button-arrow-prev";
|
||||
import {
|
||||
addMatterDevice,
|
||||
canCommissionMatterExternal,
|
||||
commissionMatterDevice,
|
||||
redirectOnNewMatterDevice,
|
||||
} from "../../../../../data/matter";
|
||||
import { haStyleDialog } from "../../../../../resources/styles";
|
||||
import { HomeAssistant } from "../../../../../types";
|
||||
import "./matter-add-device/matter-add-device-apple-home";
|
||||
import "./matter-add-device/matter-add-device-existing";
|
||||
import "./matter-add-device/matter-add-device-generic";
|
||||
import "./matter-add-device/matter-add-device-google-home";
|
||||
import "./matter-add-device/matter-add-device-google-home-fallback";
|
||||
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 { showToast } from "../../../../../util/toast";
|
||||
|
||||
export type MatterAddDeviceStep =
|
||||
| "main"
|
||||
| "new"
|
||||
| "existing"
|
||||
| "google_home"
|
||||
| "google_home_fallback"
|
||||
| "apple_home"
|
||||
| "generic"
|
||||
| "commissioning";
|
||||
|
||||
declare global {
|
||||
interface HASSDomEvents {
|
||||
"step-selected": { step: MatterAddDeviceStep };
|
||||
"pairing-code-changed": { code: string };
|
||||
}
|
||||
}
|
||||
|
||||
const BACK_STEP: Record<MatterAddDeviceStep, MatterAddDeviceStep | undefined> =
|
||||
{
|
||||
main: undefined,
|
||||
new: "main",
|
||||
existing: "main",
|
||||
google_home: "existing",
|
||||
google_home_fallback: "google_home",
|
||||
apple_home: "existing",
|
||||
generic: "existing",
|
||||
commissioning: undefined,
|
||||
};
|
||||
|
||||
@customElement("dialog-matter-add-device")
|
||||
class DialogMatterAddDevice extends LitElement {
|
||||
|
@ -19,50 +58,145 @@ class DialogMatterAddDevice extends LitElement {
|
|||
|
||||
@state() private _open = false;
|
||||
|
||||
@state() _pairingCode = "";
|
||||
|
||||
@state() _step: MatterAddDeviceStep = "main";
|
||||
|
||||
private _unsub?: UnsubscribeFunc;
|
||||
|
||||
public showDialog(): void {
|
||||
this._open = true;
|
||||
if (!canCommissionMatterExternal(this.hass)) {
|
||||
return;
|
||||
}
|
||||
this._unsub = redirectOnNewMatterDevice(this.hass, () =>
|
||||
this.closeDialog()
|
||||
);
|
||||
addMatterDevice(this.hass);
|
||||
}
|
||||
|
||||
public closeDialog(): void {
|
||||
this._open = false;
|
||||
this._step = "main";
|
||||
this._pairingCode = "";
|
||||
this._unsub?.();
|
||||
this._unsub = undefined;
|
||||
fireEvent(this, "dialog-closed", { dialog: this.localName });
|
||||
}
|
||||
|
||||
private _handleStepSelected(ev: CustomEvent) {
|
||||
this._step = ev.detail.step;
|
||||
this._pairingCode = "";
|
||||
}
|
||||
|
||||
private _handlePairingCodeChanged(ev: CustomEvent) {
|
||||
this._pairingCode = ev.detail.code;
|
||||
}
|
||||
|
||||
private _back() {
|
||||
const backStep = BACK_STEP[this._step];
|
||||
if (!backStep) return;
|
||||
this._step = backStep;
|
||||
}
|
||||
|
||||
private _renderStep() {
|
||||
return html`
|
||||
<div
|
||||
@pairing-code-changed=${this._handlePairingCodeChanged}
|
||||
@step-selected=${this._handleStepSelected}
|
||||
.hass=${this.hass}
|
||||
>
|
||||
${dynamicElement(
|
||||
`matter-add-device-${this._step.replaceAll("_", "-")}`,
|
||||
{
|
||||
hass: this.hass,
|
||||
}
|
||||
)}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
private async _addDevice() {
|
||||
const code = this._pairingCode;
|
||||
const savedStep = this._step;
|
||||
try {
|
||||
this._step = "commissioning";
|
||||
await commissionMatterDevice(this.hass, code);
|
||||
} catch (err) {
|
||||
showToast(this, {
|
||||
message: this.hass.localize(
|
||||
"ui.dialogs.matter-add-device.add_device_failed"
|
||||
),
|
||||
duration: 2000,
|
||||
});
|
||||
}
|
||||
this._step = savedStep;
|
||||
}
|
||||
|
||||
private _renderActions() {
|
||||
if (
|
||||
this._step === "apple_home" ||
|
||||
this._step === "google_home_fallback" ||
|
||||
this._step === "generic"
|
||||
) {
|
||||
return html`
|
||||
<ha-button
|
||||
slot="primaryAction"
|
||||
@click=${this._addDevice}
|
||||
.disabled=${!this._pairingCode}
|
||||
>
|
||||
${this.hass.localize("ui.dialogs.matter-add-device.add_device")}
|
||||
</ha-button>
|
||||
`;
|
||||
}
|
||||
if (this._step === "new") {
|
||||
return html`
|
||||
<ha-button slot="primaryAction" @click=${this.closeDialog}>
|
||||
${this.hass.localize("ui.common.ok")}
|
||||
</ha-button>
|
||||
`;
|
||||
}
|
||||
return nothing;
|
||||
}
|
||||
|
||||
protected render() {
|
||||
if (!this._open) {
|
||||
return nothing;
|
||||
}
|
||||
|
||||
const title = this.hass.localize(
|
||||
`ui.dialogs.matter-add-device.${this._step}.header`
|
||||
);
|
||||
|
||||
const hasBackStep = BACK_STEP[this._step];
|
||||
|
||||
const actions = this._renderActions();
|
||||
|
||||
return html`
|
||||
<ha-dialog
|
||||
open
|
||||
@closed=${this.closeDialog}
|
||||
.heading=${createCloseHeading(this.hass, "Add Matter device")}
|
||||
.heading=${title}
|
||||
?hideActions=${actions === nothing}
|
||||
scrimClickAction
|
||||
escapeKeyAction
|
||||
>
|
||||
<div>
|
||||
${!canCommissionMatterExternal(this.hass)
|
||||
? this.hass.localize(
|
||||
"ui.panel.config.integrations.config_flow.matter_mobile_app"
|
||||
)
|
||||
: html`<ha-circular-progress
|
||||
size="large"
|
||||
indeterminate
|
||||
></ha-circular-progress>`}
|
||||
</div>
|
||||
<mwc-button slot="primaryAction" @click=${this.closeDialog}>
|
||||
${this.hass.localize("ui.common.cancel")}
|
||||
</mwc-button>
|
||||
<ha-dialog-header slot="heading">
|
||||
${hasBackStep
|
||||
? html`
|
||||
<ha-icon-button-arrow-prev
|
||||
slot="navigationIcon"
|
||||
.hass=${this.hass}
|
||||
@click=${this._back}
|
||||
></ha-icon-button-arrow-prev>
|
||||
`
|
||||
: html`
|
||||
<ha-icon-button
|
||||
slot="navigationIcon"
|
||||
dialogAction="cancel"
|
||||
.label=${this.hass.localize("ui.common.close")}
|
||||
.path=${mdiClose}
|
||||
></ha-icon-button>
|
||||
`}
|
||||
<span slot="title">${title}</span>
|
||||
</ha-dialog-header>
|
||||
${this._renderStep()} ${actions}
|
||||
</ha-dialog>
|
||||
`;
|
||||
}
|
||||
|
@ -70,11 +204,34 @@ class DialogMatterAddDevice extends LitElement {
|
|||
static styles = [
|
||||
haStyleDialog,
|
||||
css`
|
||||
div {
|
||||
display: grid;
|
||||
:host {
|
||||
--horizontal-padding: 24px;
|
||||
}
|
||||
ha-circular-progress {
|
||||
justify-self: center;
|
||||
ha-dialog {
|
||||
--dialog-content-padding: 0;
|
||||
}
|
||||
ha-dialog {
|
||||
--mdc-dialog-min-width: 450px;
|
||||
--mdc-dialog-max-width: 450px;
|
||||
}
|
||||
@media all and (max-width: 450px), all and (max-height: 500px) {
|
||||
:host {
|
||||
--horizontal-padding: 16px;
|
||||
}
|
||||
ha-dialog {
|
||||
--mdc-dialog-min-width: calc(
|
||||
100vw - env(safe-area-inset-right) - env(safe-area-inset-left)
|
||||
);
|
||||
--mdc-dialog-max-width: calc(
|
||||
100vw - env(safe-area-inset-right) - env(safe-area-inset-left)
|
||||
);
|
||||
}
|
||||
}
|
||||
.loading {
|
||||
padding: 24px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
`,
|
||||
];
|
||||
|
|
|
@ -0,0 +1,80 @@
|
|||
import { LitElement, html } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { fireEvent } from "../../../../../../common/dom/fire_event";
|
||||
import "../../../../../../components/ha-icon-next";
|
||||
import "../../../../../../components/ha-list-item-new";
|
||||
import "../../../../../../components/ha-list-new";
|
||||
import { HomeAssistant } from "../../../../../../types";
|
||||
import { sharedStyles } from "./matter-add-device-shared-styles";
|
||||
|
||||
@customElement("matter-add-device-apple-home")
|
||||
class MatterAddDeviceAppleHome extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@state() private _code: string = "";
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<div class="content">
|
||||
<ol>
|
||||
<li>
|
||||
${this.hass.localize(
|
||||
"ui.dialogs.matter-add-device.apple_home.step_1",
|
||||
{
|
||||
accessory_details: html`<b
|
||||
>${this.hass.localize(
|
||||
"ui.dialogs.matter-add-device.apple_home.accessory_details"
|
||||
)}</b
|
||||
>`,
|
||||
}
|
||||
)}
|
||||
</li>
|
||||
<li>
|
||||
${this.hass.localize(
|
||||
"ui.dialogs.matter-add-device.apple_home.step_2",
|
||||
{
|
||||
turn_on_pairing_mode: html`<b
|
||||
>${this.hass.localize(
|
||||
"ui.dialogs.matter-add-device.apple_home.turn_on_pairing_mode"
|
||||
)}</b
|
||||
>`,
|
||||
}
|
||||
)}
|
||||
</li>
|
||||
<li>
|
||||
${this.hass.localize(
|
||||
"ui.dialogs.matter-add-device.apple_home.step_3"
|
||||
)}
|
||||
</li>
|
||||
</ol>
|
||||
<br />
|
||||
<p>
|
||||
${this.hass.localize(
|
||||
"ui.dialogs.matter-add-device.apple_home.code_instructions"
|
||||
)}
|
||||
</p>
|
||||
<ha-textfield
|
||||
label=${this.hass.localize(
|
||||
"ui.dialogs.matter-add-device.apple_home.setup_code"
|
||||
)}
|
||||
.value=${this._code}
|
||||
@input=${this._onCodeChanged}
|
||||
></ha-textfield>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
private _onCodeChanged(ev: any) {
|
||||
const value = ev.currentTarget.value;
|
||||
this._code = value;
|
||||
fireEvent(this, "pairing-code-changed", { code: value });
|
||||
}
|
||||
|
||||
static styles = [sharedStyles];
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"matter-add-device-apple-home": MatterAddDeviceAppleHome;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
import { LitElement, css, html } from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import { HomeAssistant } from "../../../../../../types";
|
||||
import { sharedStyles } from "./matter-add-device-shared-styles";
|
||||
import "../../../../../../components/ha-circular-progress";
|
||||
|
||||
@customElement("matter-add-device-commissioning")
|
||||
class MatterAddDeviceCommissioning extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<div class="content">
|
||||
<ha-circular-progress
|
||||
size="medium"
|
||||
indeterminate
|
||||
></ha-circular-progress>
|
||||
<p>
|
||||
${this.hass.localize(
|
||||
"ui.dialogs.matter-add-device.commissioning.note"
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
static styles = [
|
||||
sharedStyles,
|
||||
css`
|
||||
.content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
text-align: center;
|
||||
}
|
||||
ha-circular-progress {
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"matter-add-device-commissioning": MatterAddDeviceCommissioning;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,123 @@
|
|||
import { mdiHomeAutomation } from "@mdi/js";
|
||||
import { LitElement, css, html } from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import { fireEvent } from "../../../../../../common/dom/fire_event";
|
||||
import "../../../../../../components/ha-icon-next";
|
||||
import "../../../../../../components/ha-list-item-new";
|
||||
import "../../../../../../components/ha-list-new";
|
||||
import { HomeAssistant } from "../../../../../../types";
|
||||
import { MatterAddDeviceStep } from "../dialog-matter-add-device";
|
||||
import { sharedStyles } from "./matter-add-device-shared-styles";
|
||||
|
||||
@customElement("matter-add-device-existing")
|
||||
class MatterAddDeviceExisting extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<div class="content">
|
||||
<p>
|
||||
${this.hass.localize(
|
||||
`ui.dialogs.matter-add-device.existing.question`
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<ha-list-new>
|
||||
<ha-list-item-new
|
||||
interactive
|
||||
type="button"
|
||||
.step=${"google_home"}
|
||||
@click=${this._onItemClick}
|
||||
@keydown=${this._onItemClick}
|
||||
>
|
||||
<img
|
||||
src="/static/images/logo_google_home.png"
|
||||
alt=""
|
||||
class="logo"
|
||||
slot="start"
|
||||
/>
|
||||
<span slot="headline">
|
||||
${this.hass.localize(
|
||||
`ui.dialogs.matter-add-device.existing.answer_google_home`
|
||||
)}
|
||||
</span>
|
||||
<ha-icon-next slot="end"></ha-icon-next>
|
||||
</ha-list-item-new>
|
||||
<ha-list-item-new
|
||||
interactive
|
||||
type="button"
|
||||
.step=${"apple_home"}
|
||||
@click=${this._onItemClick}
|
||||
@keydown=${this._onItemClick}
|
||||
>
|
||||
<img
|
||||
src="/static/images/logo_apple_home.png"
|
||||
alt=""
|
||||
class="logo"
|
||||
slot="start"
|
||||
/>
|
||||
<span slot="headline">
|
||||
${this.hass.localize(
|
||||
`ui.dialogs.matter-add-device.existing.answer_apple_home`
|
||||
)}
|
||||
</span>
|
||||
<ha-icon-next slot="end"></ha-icon-next>
|
||||
</ha-list-item-new>
|
||||
<ha-list-item-new
|
||||
interactive
|
||||
type="button"
|
||||
.step=${"generic"}
|
||||
@click=${this._onItemClick}
|
||||
@keydown=${this._onItemClick}
|
||||
>
|
||||
<div class="logo" slot="start">
|
||||
<ha-svg-icon path=${mdiHomeAutomation}></ha-svg-icon>
|
||||
</div>
|
||||
<span slot="headline">
|
||||
${this.hass.localize(
|
||||
`ui.dialogs.matter-add-device.existing.answer_generic`
|
||||
)}
|
||||
</span>
|
||||
<ha-icon-next slot="end"></ha-icon-next>
|
||||
</ha-list-item-new>
|
||||
</ha-list-new>
|
||||
`;
|
||||
}
|
||||
|
||||
private _onItemClick(ev) {
|
||||
if (ev.type === "keydown" && ev.key !== "Enter" && ev.key !== " ") {
|
||||
return;
|
||||
}
|
||||
const item = ev.currentTarget as any;
|
||||
const step = item.step as MatterAddDeviceStep;
|
||||
fireEvent(this, "step-selected", { step });
|
||||
}
|
||||
|
||||
static styles = [
|
||||
sharedStyles,
|
||||
css`
|
||||
.logo {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
border-radius: 12px;
|
||||
border: 1px solid var(--divider-color);
|
||||
padding: 10px;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
object-fit: contain;
|
||||
}
|
||||
.logo ha-svg-icon {
|
||||
--mdc-icon-size: 36px;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"matter-add-device-existing": MatterAddDeviceExisting;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
import { LitElement, html } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { fireEvent } from "../../../../../../common/dom/fire_event";
|
||||
import "../../../../../../components/ha-icon-next";
|
||||
import "../../../../../../components/ha-list-item-new";
|
||||
import "../../../../../../components/ha-list-new";
|
||||
import { HomeAssistant } from "../../../../../../types";
|
||||
import { sharedStyles } from "./matter-add-device-shared-styles";
|
||||
|
||||
@customElement("matter-add-device-generic")
|
||||
class MatterAddDeviceGeneric extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@state() private _code: string = "";
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<div class="content">
|
||||
<p>
|
||||
${this.hass.localize(
|
||||
"ui.dialogs.matter-add-device.generic.code_instructions"
|
||||
)}
|
||||
</p>
|
||||
<ha-textfield
|
||||
label=${this.hass.localize(
|
||||
"ui.dialogs.matter-add-device.generic.setup_code"
|
||||
)}
|
||||
.value=${this._code}
|
||||
@input=${this._onCodeChanged}
|
||||
></ha-textfield>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
private _onCodeChanged(ev: any) {
|
||||
const value = ev.currentTarget.value;
|
||||
this._code = value;
|
||||
fireEvent(this, "pairing-code-changed", { code: value });
|
||||
}
|
||||
|
||||
static styles = [sharedStyles];
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"matter-add-device-generic": MatterAddDeviceGeneric;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,85 @@
|
|||
import { LitElement, html } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { fireEvent } from "../../../../../../common/dom/fire_event";
|
||||
import "../../../../../../components/ha-icon-next";
|
||||
import "../../../../../../components/ha-list-item-new";
|
||||
import "../../../../../../components/ha-list-new";
|
||||
import { HomeAssistant } from "../../../../../../types";
|
||||
import { sharedStyles } from "./matter-add-device-shared-styles";
|
||||
|
||||
@customElement("matter-add-device-google-home-fallback")
|
||||
class MatterAddDeviceGoogleHomeFallback extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@state() private _code: string = "";
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<div class="content">
|
||||
<ol>
|
||||
<li>
|
||||
${this.hass.localize(
|
||||
`ui.dialogs.matter-add-device.google_home_fallback.step_1`
|
||||
)}
|
||||
</li>
|
||||
<li>
|
||||
${this.hass.localize(
|
||||
`ui.dialogs.matter-add-device.google_home_fallback.step_2`,
|
||||
{
|
||||
linked_matter_apps_services: html`<b
|
||||
>${this.hass.localize(
|
||||
`ui.dialogs.matter-add-device.google_home_fallback.linked_matter_apps_services`
|
||||
)}</b
|
||||
>`,
|
||||
}
|
||||
)}
|
||||
</li>
|
||||
<li>
|
||||
${this.hass.localize(
|
||||
`ui.dialogs.matter-add-device.google_home_fallback.step_3`,
|
||||
{
|
||||
link_apps_services: html`<b
|
||||
>${this.hass.localize(
|
||||
`ui.dialogs.matter-add-device.google_home_fallback.link_apps_services`
|
||||
)}</b
|
||||
>`,
|
||||
use_pairing_code: html`<b
|
||||
>${this.hass.localize(
|
||||
`ui.dialogs.matter-add-device.google_home_fallback.use_pairing_code`
|
||||
)}</b
|
||||
>`,
|
||||
}
|
||||
)}
|
||||
</li>
|
||||
</ol>
|
||||
<br />
|
||||
<p>
|
||||
${this.hass.localize(
|
||||
`ui.dialogs.matter-add-device.google_home_fallback.code_instructions`
|
||||
)}
|
||||
</p>
|
||||
<ha-textfield
|
||||
label=${this.hass.localize(
|
||||
`ui.dialogs.matter-add-device.google_home_fallback.pairing_code`
|
||||
)}
|
||||
.value=${this._code}
|
||||
@input=${this._onCodeChanged}
|
||||
></ha-textfield>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
private _onCodeChanged(ev: any) {
|
||||
const value = ev.currentTarget.value;
|
||||
this._code = value;
|
||||
fireEvent(this, "pairing-code-changed", { code: value });
|
||||
}
|
||||
|
||||
static styles = [sharedStyles];
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"matter-add-device-google-home-fallback": MatterAddDeviceGoogleHomeFallback;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,82 @@
|
|||
import { LitElement, html } from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import { fireEvent } from "../../../../../../common/dom/fire_event";
|
||||
import "../../../../../../components/ha-icon-next";
|
||||
import "../../../../../../components/ha-list-item-new";
|
||||
import "../../../../../../components/ha-list-new";
|
||||
import { HomeAssistant } from "../../../../../../types";
|
||||
import { sharedStyles } from "./matter-add-device-shared-styles";
|
||||
|
||||
@customElement("matter-add-device-google-home")
|
||||
class MatterAddDeviceGoogleHome extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<div class="content">
|
||||
<ol>
|
||||
<li>
|
||||
${this.hass.localize(
|
||||
`ui.dialogs.matter-add-device.google_home.step_1`
|
||||
)}
|
||||
</li>
|
||||
<li>
|
||||
${this.hass.localize(
|
||||
`ui.dialogs.matter-add-device.google_home.step_2`,
|
||||
{
|
||||
linked_matter_apps_services: html`<b
|
||||
>${this.hass.localize(
|
||||
`ui.dialogs.matter-add-device.google_home.linked_matter_apps_services`
|
||||
)}</b
|
||||
>`,
|
||||
}
|
||||
)}
|
||||
</li>
|
||||
<li>
|
||||
${this.hass.localize(
|
||||
`ui.dialogs.matter-add-device.google_home.step_3`,
|
||||
{
|
||||
link_apps_services: html`<b
|
||||
>${this.hass.localize(
|
||||
`ui.dialogs.matter-add-device.google_home.link_apps_services`
|
||||
)}</b
|
||||
>`,
|
||||
home_assistant: html`<b>Home Assistant</b>`,
|
||||
}
|
||||
)}
|
||||
<br />
|
||||
<span
|
||||
class="link"
|
||||
type="button"
|
||||
tabindex="0"
|
||||
@keydown=${this._nextStep}
|
||||
@click=${this._nextStep}
|
||||
>
|
||||
${this.hass.localize(
|
||||
`ui.dialogs.matter-add-device.google_home.no_home_assistant`
|
||||
)}
|
||||
</span>
|
||||
</li>
|
||||
</ol>
|
||||
<br />
|
||||
<p>
|
||||
${this.hass.localize(
|
||||
`ui.dialogs.matter-add-device.google_home.redirect`
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
private _nextStep() {
|
||||
fireEvent(this, "step-selected", { step: "google_home_fallback" });
|
||||
}
|
||||
|
||||
static styles = [sharedStyles];
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"matter-add-device-google-home": MatterAddDeviceGoogleHome;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,80 @@
|
|||
import { LitElement, html } from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import { fireEvent } from "../../../../../../common/dom/fire_event";
|
||||
import "../../../../../../components/ha-icon-next";
|
||||
import "../../../../../../components/ha-list-item-new";
|
||||
import "../../../../../../components/ha-list-new";
|
||||
import { HomeAssistant } from "../../../../../../types";
|
||||
import { sharedStyles } from "./matter-add-device-shared-styles";
|
||||
|
||||
@customElement("matter-add-device-main")
|
||||
class MatterAddDeviceMain extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
render() {
|
||||
return html`
|
||||
<div class="content">
|
||||
<p class="text">
|
||||
${this.hass.localize(`ui.dialogs.matter-add-device.main.question`)}
|
||||
</p>
|
||||
</div>
|
||||
<ha-list-new>
|
||||
<ha-list-item-new
|
||||
interactive
|
||||
type="button"
|
||||
.step=${"new"}
|
||||
@click=${this._onItemClick}
|
||||
@keydown=${this._onItemClick}
|
||||
>
|
||||
<span slot="headline">
|
||||
${this.hass.localize(
|
||||
`ui.dialogs.matter-add-device.main.answer_new`
|
||||
)}
|
||||
</span>
|
||||
<span slot="supporting-text">
|
||||
${this.hass.localize(
|
||||
`ui.dialogs.matter-add-device.main.answer_new_description`
|
||||
)}
|
||||
</span>
|
||||
<ha-icon-next slot="end"></ha-icon-next>
|
||||
</ha-list-item-new>
|
||||
<ha-list-item-new
|
||||
interactive
|
||||
type="button"
|
||||
.step=${"existing"}
|
||||
@click=${this._onItemClick}
|
||||
@keydown=${this._onItemClick}
|
||||
>
|
||||
<span slot="headline">
|
||||
${this.hass.localize(
|
||||
`ui.dialogs.matter-add-device.main.answer_existing`
|
||||
)}
|
||||
</span>
|
||||
<span slot="supporting-text">
|
||||
${this.hass.localize(
|
||||
`ui.dialogs.matter-add-device.main.answer_existing_description`
|
||||
)}
|
||||
</span>
|
||||
<ha-icon-next slot="end"></ha-icon-next>
|
||||
</ha-list-item-new>
|
||||
</ha-list-new>
|
||||
`;
|
||||
}
|
||||
|
||||
private _onItemClick(ev) {
|
||||
if (ev.type === "keydown" && ev.key !== "Enter" && ev.key !== " ") {
|
||||
return;
|
||||
}
|
||||
const item = ev.currentTarget as any;
|
||||
const step = item.step as "new" | "existing";
|
||||
fireEvent(this, "step-selected", { step });
|
||||
}
|
||||
|
||||
static styles = [sharedStyles];
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"matter-add-device-main": MatterAddDeviceMain;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,113 @@
|
|||
import { LitElement, css, html } from "lit";
|
||||
import { customElement, property } from "lit/decorators";
|
||||
import "../../../../../../components/ha-circular-progress";
|
||||
import {
|
||||
canCommissionMatterExternal,
|
||||
startExternalCommissioning,
|
||||
} from "../../../../../../data/matter";
|
||||
import { HomeAssistant } from "../../../../../../types";
|
||||
import { sharedStyles } from "./matter-add-device-shared-styles";
|
||||
|
||||
@customElement("matter-add-device-new")
|
||||
class MatterAddDeviceNew extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
protected firstUpdated(): void {
|
||||
if (!canCommissionMatterExternal(this.hass)) {
|
||||
return;
|
||||
}
|
||||
startExternalCommissioning(this.hass);
|
||||
}
|
||||
|
||||
render() {
|
||||
if (canCommissionMatterExternal(this.hass)) {
|
||||
return html`
|
||||
<div class="content">
|
||||
<ha-circular-progress
|
||||
size="medium"
|
||||
indeterminate
|
||||
></ha-circular-progress>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
return html`
|
||||
<div class="content">
|
||||
<p>${this.hass.localize("ui.dialogs.matter-add-device.new.note")}</p>
|
||||
<p>
|
||||
${this.hass.localize("ui.dialogs.matter-add-device.new.download_app")}
|
||||
</p>
|
||||
<div class="app-qr">
|
||||
<a
|
||||
target="_blank"
|
||||
rel="noreferrer noopener"
|
||||
href="https://apps.apple.com/app/home-assistant/id1099568401?mt=8"
|
||||
>
|
||||
<img
|
||||
loading="lazy"
|
||||
src="/static/images/appstore.svg"
|
||||
alt=${this.hass.localize(
|
||||
"ui.dialogs.matter-add-device.new.appstore"
|
||||
)}
|
||||
class="icon"
|
||||
/>
|
||||
<img
|
||||
loading="lazy"
|
||||
src="/static/images/qr-appstore.svg"
|
||||
alt=${this.hass.localize(
|
||||
"ui.dialogs.matter-add-device.new.appstore"
|
||||
)}
|
||||
/>
|
||||
</a>
|
||||
<a
|
||||
target="_blank"
|
||||
rel="noreferrer noopener"
|
||||
href="https://play.google.com/store/apps/details?id=io.homeassistant.companion.android"
|
||||
>
|
||||
<img
|
||||
loading="lazy"
|
||||
src="/static/images/playstore.svg"
|
||||
alt=${this.hass.localize(
|
||||
"ui.dialogs.matter-add-device.new.playstore"
|
||||
)}
|
||||
class="icon"
|
||||
/>
|
||||
<img
|
||||
loading="lazy"
|
||||
src="/static/images/qr-playstore.svg"
|
||||
alt=${this.hass.localize(
|
||||
"ui.dialogs.matter-add-device.new.playstore"
|
||||
)}
|
||||
/>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
static styles = [
|
||||
sharedStyles,
|
||||
css`
|
||||
.app-qr {
|
||||
margin: 24px auto 0 auto;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding: 0 24px;
|
||||
box-sizing: border-box;
|
||||
gap: 16px;
|
||||
width: 100%;
|
||||
max-width: 400px;
|
||||
}
|
||||
.app-qr a,
|
||||
.app-qr img {
|
||||
flex: 1;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"matter-add-device-new": MatterAddDeviceNew;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
import { css } from "lit";
|
||||
|
||||
export const sharedStyles = css`
|
||||
.content {
|
||||
padding: 16px var(--horizontal-padding, 16px);
|
||||
}
|
||||
p {
|
||||
margin: 0;
|
||||
}
|
||||
p:not(:last-child) {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
ol {
|
||||
padding-inline-start: 20px;
|
||||
margin-block-start: 0;
|
||||
margin-block-end: 8px;
|
||||
}
|
||||
li {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.link {
|
||||
color: var(--primary-color);
|
||||
cursor: pointer;
|
||||
text-decoration: underline;
|
||||
}
|
||||
ha-list-new {
|
||||
padding: 0;
|
||||
--md-list-item-leading-space: var(--horizontal-padding, 16px);
|
||||
--md-list-item-trailing-space: var(--horizontal-padding, 16px);
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
ha-textfield {
|
||||
width: 100%;
|
||||
}
|
||||
`;
|
|
@ -1657,6 +1657,72 @@
|
|||
"title": "Create backup?",
|
||||
"text": "This will create a backup before installing.",
|
||||
"create": "Create"
|
||||
},
|
||||
"matter-add-device": {
|
||||
"add_device": "Add device",
|
||||
"add_device_failed": "Failed to add the device",
|
||||
"commissioning": {
|
||||
"header": "Add Matter device",
|
||||
"note": "Adding the device, this may take a minute or two."
|
||||
},
|
||||
"main": {
|
||||
"header": "Add Matter device",
|
||||
"question": "Is your device already added to another Matter controller?",
|
||||
"answer_new": "No. It’s new.",
|
||||
"answer_new_description": "My device is brand new or factory reset.",
|
||||
"answer_existing": "Yes. It’s already in use.",
|
||||
"answer_existing_description": "My device is connected to another controller."
|
||||
},
|
||||
"new": {
|
||||
"header": "[%key:ui::dialogs::matter-add-device::main::header%]",
|
||||
"note": "You need to use the Home Assistant Companion app on your mobile phone to add Matter devices.",
|
||||
"download_app": "Install it from the Google Play Store or the App Store if you don't have it.",
|
||||
"playstore": "[%key:ui::panel::page-onboarding::welcome::playstore%]",
|
||||
"appstore": "[%key:ui::panel::page-onboarding::welcome::appstore%]"
|
||||
},
|
||||
"existing": {
|
||||
"header": "[%key:ui::dialogs::matter-add-device::main::header%]",
|
||||
"question": "Which controller is it connected to?",
|
||||
"answer_google_home": "Google Home",
|
||||
"answer_apple_home": "Apple Home",
|
||||
"answer_generic": "Other controllers"
|
||||
},
|
||||
"google_home": {
|
||||
"header": "Link Matter app",
|
||||
"step_1": "Find your device in Google Home. Tap the gear icon to open the device settings.",
|
||||
"step_2": "Tap {linked_matter_apps_services}.",
|
||||
"step_3": "Tap {link_apps_services} and choose {home_assistant} from the list.",
|
||||
"linked_matter_apps_services": "Linked Matter apps & services",
|
||||
"link_apps_services": "Link apps & services",
|
||||
"no_home_assistant": "I can’t find Home Assistant on the list",
|
||||
"redirect": "You are redirected to the Home Assistant app. Please follow the instructions."
|
||||
},
|
||||
"google_home_fallback": {
|
||||
"header": "Copy pairing code",
|
||||
"step_1": "[%key:ui::dialogs::matter-add-device::google_home::step_1%]",
|
||||
"step_2": "[%key:ui::dialogs::matter-add-device::google_home::step_2%]",
|
||||
"step_3": "Tap {link_apps_services} and choose {use_pairing_code} form the list",
|
||||
"linked_matter_apps_services": "[%key:ui::dialogs::matter-add-device::google_home::linked_matter_apps_services%]",
|
||||
"link_apps_services": "[%key:ui::dialogs::matter-add-device::google_home::link_apps_services%]",
|
||||
"use_pairing_code": "Use Pairing Code",
|
||||
"pairing_code": "Pairing code",
|
||||
"code_instructions": "Paste the code you just received from the other controller."
|
||||
},
|
||||
"apple_home": {
|
||||
"header": "Copy setup code",
|
||||
"step_1": "Find your device in Apple Home and open {accessory_details}.",
|
||||
"step_2": "Scroll all the way down and tap {turn_on_pairing_mode}.",
|
||||
"step_3": "You now see the setup code.",
|
||||
"accessory_details": "Accessory Details",
|
||||
"turn_on_pairing_mode": "Turn On Pairing Mode",
|
||||
"setup_code": "Setup code",
|
||||
"code_instructions": "Paste the code you just received from the other controller."
|
||||
},
|
||||
"generic": {
|
||||
"header": "Copy setup code",
|
||||
"code_instructions": "Search for the sharing mode in the app of your controller, and activate it. You will get a sharing code, enter that below.",
|
||||
"setup_code": "Setup code"
|
||||
}
|
||||
}
|
||||
},
|
||||
"weekdays": {
|
||||
|
@ -4182,7 +4248,6 @@
|
|||
"missing_zwave_zigbee_title": "{integration} is not setup",
|
||||
"missing_zwave_zigbee": "To add a {brand} device, you first need {supported_hardware_link} and the {integration} integration set up. If you already have the hardware then you can proceed with the setup of {integration}.",
|
||||
"missing_matter": "To add a {brand} device, you first need the {integration} integration and {supported_hardware_link}. Do you want to proceed with the setup of {integration}?",
|
||||
"matter_mobile_app": "You need to use the Home Assistant Companion app on your mobile phone to commission Matter devices.",
|
||||
"supported_hardware": "supported hardware",
|
||||
"proceed": "Proceed",
|
||||
"single_config_entry_title": "This integration allows only one configuration",
|
||||
|
@ -6623,7 +6688,9 @@
|
|||
"forums": "Home Assistant forums",
|
||||
"open_home_newsletter": "Building the Open Home newsletter",
|
||||
"discord": "Discord chat",
|
||||
"twitter": "Twitter"
|
||||
"twitter": "Twitter",
|
||||
"playstore": "Get it on Google Play",
|
||||
"appstore": "Download on the App Store"
|
||||
},
|
||||
"user": {
|
||||
"header": "Create user",
|
||||
|
|
Loading…
Reference in New Issue