mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-27 11:16:35 +00:00
Clean up ZHA configuration UI (#13610)
This commit is contained in:
parent
0848c096b9
commit
3083d5b04c
@ -309,7 +309,7 @@ export const fetchCommandsForCluster = (
|
||||
cluster_type: clusterType,
|
||||
});
|
||||
|
||||
export const fetchClustersForZhaNode = (
|
||||
export const fetchClustersForZhaDevice = (
|
||||
hass: HomeAssistant,
|
||||
ieeeAddress: string
|
||||
): Promise<Cluster[]> =>
|
||||
|
@ -1,9 +1,7 @@
|
||||
import {
|
||||
mdiCogRefresh,
|
||||
mdiDelete,
|
||||
mdiDrawPen,
|
||||
mdiFamilyTree,
|
||||
mdiFileTree,
|
||||
mdiGroup,
|
||||
mdiPlus,
|
||||
} from "@mdi/js";
|
||||
@ -12,9 +10,7 @@ import type { DeviceRegistryEntry } from "../../../../../../data/device_registry
|
||||
import { fetchZHADevice } from "../../../../../../data/zha";
|
||||
import { showConfirmationDialog } from "../../../../../../dialogs/generic/show-dialog-box";
|
||||
import type { HomeAssistant } from "../../../../../../types";
|
||||
import { showZHAClusterDialog } from "../../../../integrations/integration-panels/zha/show-dialog-zha-cluster";
|
||||
import { showZHADeviceChildrenDialog } from "../../../../integrations/integration-panels/zha/show-dialog-zha-device-children";
|
||||
import { showZHADeviceZigbeeInfoDialog } from "../../../../integrations/integration-panels/zha/show-dialog-zha-device-zigbee-info";
|
||||
import { showZHAManageZigbeeDeviceDialog } from "../../../../integrations/integration-panels/zha/show-dialog-zha-manage-zigbee-device";
|
||||
import { showZHAReconfigureDeviceDialog } from "../../../../integrations/integration-panels/zha/show-dialog-zha-reconfigure-device";
|
||||
import type { DeviceAction } from "../../../ha-config-device-page";
|
||||
|
||||
@ -59,13 +55,6 @@ export const getZHADeviceActions = async (
|
||||
icon: mdiPlus,
|
||||
action: () => navigate(`/config/zha/add/${zhaDevice!.ieee}`),
|
||||
},
|
||||
{
|
||||
label: hass.localize(
|
||||
"ui.dialogs.zha_device_info.buttons.device_children"
|
||||
),
|
||||
icon: mdiFileTree,
|
||||
action: () => showZHADeviceChildrenDialog(el, { device: zhaDevice! }),
|
||||
},
|
||||
]
|
||||
);
|
||||
}
|
||||
@ -73,16 +62,10 @@ export const getZHADeviceActions = async (
|
||||
actions.push(
|
||||
...[
|
||||
{
|
||||
label: hass.localize(
|
||||
"ui.dialogs.zha_device_info.buttons.zigbee_information"
|
||||
),
|
||||
icon: mdiDrawPen,
|
||||
action: () => showZHADeviceZigbeeInfoDialog(el, { device: zhaDevice }),
|
||||
},
|
||||
{
|
||||
label: hass.localize("ui.dialogs.zha_device_info.buttons.clusters"),
|
||||
label: hass.localize("ui.dialogs.zha_device_info.buttons.manage"),
|
||||
icon: mdiGroup,
|
||||
action: () => showZHAClusterDialog(el, { device: zhaDevice }),
|
||||
action: () =>
|
||||
showZHAManageZigbeeDeviceDialog(el, { device: zhaDevice }),
|
||||
},
|
||||
{
|
||||
label: hass.localize("ui.dialogs.zha_device_info.buttons.view_network"),
|
||||
|
@ -23,6 +23,7 @@ export class HaDeviceActionsZha extends LitElement {
|
||||
@state() private _zhaDevice?: ZHADevice;
|
||||
|
||||
protected updated(changedProperties: PropertyValues) {
|
||||
super.updated(changedProperties);
|
||||
if (changedProperties.has("device")) {
|
||||
const zigbeeConnection = this.device.connections.find(
|
||||
(conn) => conn[0] === "zigbee"
|
||||
|
@ -1,142 +0,0 @@
|
||||
import {
|
||||
CSSResultGroup,
|
||||
html,
|
||||
LitElement,
|
||||
PropertyValues,
|
||||
TemplateResult,
|
||||
} from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { HASSDomEvent } from "../../../../../common/dom/fire_event";
|
||||
import "../../../../../components/ha-code-editor";
|
||||
import { createCloseHeading } from "../../../../../components/ha-dialog";
|
||||
import {
|
||||
Cluster,
|
||||
fetchBindableDevices,
|
||||
fetchGroups,
|
||||
ZHADevice,
|
||||
ZHAGroup,
|
||||
} from "../../../../../data/zha";
|
||||
import { haStyleDialog } from "../../../../../resources/styles";
|
||||
import { HomeAssistant } from "../../../../../types";
|
||||
import { sortZHADevices, sortZHAGroups } from "./functions";
|
||||
import { ZHADeviceZigbeeInfoDialogParams } from "./show-dialog-zha-device-zigbee-info";
|
||||
import { ZHAClusterSelectedParams } from "./types";
|
||||
import "./zha-cluster-attributes";
|
||||
import "./zha-cluster-commands";
|
||||
import "./zha-clusters";
|
||||
import "./zha-device-binding";
|
||||
import "./zha-group-binding";
|
||||
|
||||
@customElement("dialog-zha-cluster")
|
||||
class DialogZHACluster extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@state() private _device?: ZHADevice;
|
||||
|
||||
@state() private _selectedCluster?: Cluster;
|
||||
|
||||
@state() private _bindableDevices: ZHADevice[] = [];
|
||||
|
||||
@state() private _groups: ZHAGroup[] = [];
|
||||
|
||||
public async showDialog(
|
||||
params: ZHADeviceZigbeeInfoDialogParams
|
||||
): Promise<void> {
|
||||
this._device = params.device;
|
||||
}
|
||||
|
||||
protected updated(changedProperties: PropertyValues): void {
|
||||
super.update(changedProperties);
|
||||
if (changedProperties.has("_device")) {
|
||||
this._fetchData();
|
||||
}
|
||||
}
|
||||
|
||||
protected render(): TemplateResult {
|
||||
if (!this._device) {
|
||||
return html``;
|
||||
}
|
||||
|
||||
return html`
|
||||
<ha-dialog
|
||||
open
|
||||
hideActions
|
||||
@closed=${this._close}
|
||||
.heading=${createCloseHeading(
|
||||
this.hass,
|
||||
this.hass.localize("ui.panel.config.zha.clusters.header")
|
||||
)}
|
||||
>
|
||||
<zha-clusters
|
||||
.hass=${this.hass}
|
||||
.selectedDevice=${this._device}
|
||||
@zha-cluster-selected=${this._onClusterSelected}
|
||||
></zha-clusters>
|
||||
${this._selectedCluster
|
||||
? html`
|
||||
<zha-cluster-attributes
|
||||
.hass=${this.hass}
|
||||
.selectedNode=${this._device}
|
||||
.selectedCluster=${this._selectedCluster}
|
||||
></zha-cluster-attributes>
|
||||
<zha-cluster-commands
|
||||
.hass=${this.hass}
|
||||
.selectedNode=${this._device}
|
||||
.selectedCluster=${this._selectedCluster}
|
||||
></zha-cluster-commands>
|
||||
`
|
||||
: ""}
|
||||
${this._bindableDevices.length > 0
|
||||
? html`
|
||||
<zha-device-binding-control
|
||||
.hass=${this.hass}
|
||||
.selectedDevice=${this._device}
|
||||
.bindableDevices=${this._bindableDevices}
|
||||
></zha-device-binding-control>
|
||||
`
|
||||
: ""}
|
||||
${this._device && this._groups.length > 0
|
||||
? html`
|
||||
<zha-group-binding-control
|
||||
.hass=${this.hass}
|
||||
.selectedDevice=${this._device}
|
||||
.groups=${this._groups}
|
||||
></zha-group-binding-control>
|
||||
`
|
||||
: ""}
|
||||
</ha-dialog>
|
||||
`;
|
||||
}
|
||||
|
||||
private _onClusterSelected(
|
||||
selectedClusterEvent: HASSDomEvent<ZHAClusterSelectedParams>
|
||||
): void {
|
||||
this._selectedCluster = selectedClusterEvent.detail.cluster;
|
||||
}
|
||||
|
||||
private _close(): void {
|
||||
this._device = undefined;
|
||||
}
|
||||
|
||||
private async _fetchData(): Promise<void> {
|
||||
if (this._device && this.hass) {
|
||||
this._bindableDevices =
|
||||
this._device && this._device.device_type !== "Coordinator"
|
||||
? (await fetchBindableDevices(this.hass, this._device.ieee)).sort(
|
||||
sortZHADevices
|
||||
)
|
||||
: [];
|
||||
this._groups = (await fetchGroups(this.hass!)).sort(sortZHAGroups);
|
||||
}
|
||||
}
|
||||
|
||||
static get styles(): CSSResultGroup {
|
||||
return haStyleDialog;
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"dialog-zha-cluster": DialogZHACluster;
|
||||
}
|
||||
}
|
@ -1,69 +0,0 @@
|
||||
import { CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import "../../../../../components/ha-code-editor";
|
||||
import { createCloseHeading } from "../../../../../components/ha-dialog";
|
||||
import { haStyleDialog } from "../../../../../resources/styles";
|
||||
import { HomeAssistant } from "../../../../../types";
|
||||
import { ZHADeviceZigbeeInfoDialogParams } from "./show-dialog-zha-device-zigbee-info";
|
||||
|
||||
@customElement("dialog-zha-device-zigbee-info")
|
||||
class DialogZHADeviceZigbeeInfo extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@state() private _signature: any;
|
||||
|
||||
public async showDialog(
|
||||
params: ZHADeviceZigbeeInfoDialogParams
|
||||
): Promise<void> {
|
||||
this._signature = JSON.stringify(
|
||||
{
|
||||
...params.device.signature,
|
||||
manufacturer: params.device.manufacturer,
|
||||
model: params.device.model,
|
||||
class: params.device.quirk_class,
|
||||
},
|
||||
null,
|
||||
2
|
||||
);
|
||||
}
|
||||
|
||||
protected render(): TemplateResult {
|
||||
if (!this._signature) {
|
||||
return html``;
|
||||
}
|
||||
|
||||
return html`
|
||||
<ha-dialog
|
||||
open
|
||||
hideActions
|
||||
@closed=${this._close}
|
||||
.heading=${createCloseHeading(
|
||||
this.hass,
|
||||
this.hass.localize(`ui.dialogs.zha_device_info.device_signature`)
|
||||
)}
|
||||
>
|
||||
<ha-code-editor
|
||||
mode="yaml"
|
||||
readOnly
|
||||
.value=${this._signature}
|
||||
dir="ltr"
|
||||
>
|
||||
</ha-code-editor>
|
||||
</ha-dialog>
|
||||
`;
|
||||
}
|
||||
|
||||
private _close(): void {
|
||||
this._signature = undefined;
|
||||
}
|
||||
|
||||
static get styles(): CSSResultGroup {
|
||||
return haStyleDialog;
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"dialog-zha-device-zigbee-info": DialogZHADeviceZigbeeInfo;
|
||||
}
|
||||
}
|
@ -0,0 +1,291 @@
|
||||
import {
|
||||
css,
|
||||
CSSResultGroup,
|
||||
html,
|
||||
LitElement,
|
||||
PropertyValues,
|
||||
TemplateResult,
|
||||
} from "lit";
|
||||
import { mdiClose } from "@mdi/js";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { cache } from "lit/directives/cache";
|
||||
import memoizeOne from "memoize-one";
|
||||
import { fireEvent } from "../../../../../common/dom/fire_event";
|
||||
import "../../../../../components/ha-code-editor";
|
||||
import { createCloseHeading } from "../../../../../components/ha-dialog";
|
||||
import {
|
||||
fetchBindableDevices,
|
||||
fetchGroups,
|
||||
ZHADevice,
|
||||
ZHAGroup,
|
||||
} from "../../../../../data/zha";
|
||||
import { haStyleDialog } from "../../../../../resources/styles";
|
||||
import { HomeAssistant } from "../../../../../types";
|
||||
import { sortZHADevices, sortZHAGroups } from "./functions";
|
||||
import "./zha-cluster-attributes";
|
||||
import "./zha-cluster-commands";
|
||||
import "./zha-manage-clusters";
|
||||
import "./zha-device-binding";
|
||||
import "./zha-group-binding";
|
||||
import "./zha-device-children";
|
||||
import "./zha-device-signature";
|
||||
import {
|
||||
Tab,
|
||||
ZHAManageZigbeeDeviceDialogParams,
|
||||
} from "./show-dialog-zha-manage-zigbee-device";
|
||||
import "../../../../../components/ha-header-bar";
|
||||
import "@material/mwc-tab-bar/mwc-tab-bar";
|
||||
import "@material/mwc-tab/mwc-tab";
|
||||
|
||||
@customElement("dialog-zha-manage-zigbee-device")
|
||||
class DialogZHAManageZigbeeDevice extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@property({ type: Boolean, reflect: true }) public large = false;
|
||||
|
||||
@state() private _currTab: Tab = "clusters";
|
||||
|
||||
@state() private _device?: ZHADevice;
|
||||
|
||||
@state() private _bindableDevices: ZHADevice[] = [];
|
||||
|
||||
@state() private _groups: ZHAGroup[] = [];
|
||||
|
||||
public async showDialog(
|
||||
params: ZHAManageZigbeeDeviceDialogParams
|
||||
): Promise<void> {
|
||||
this._device = params.device;
|
||||
if (!this._device) {
|
||||
this.closeDialog();
|
||||
return;
|
||||
}
|
||||
this._currTab = params.tab || "clusters";
|
||||
this.large = false;
|
||||
}
|
||||
|
||||
public closeDialog() {
|
||||
this._device = undefined;
|
||||
fireEvent(this, "dialog-closed", { dialog: this.localName });
|
||||
}
|
||||
|
||||
protected firstUpdated(changedProps: PropertyValues) {
|
||||
super.firstUpdated(changedProps);
|
||||
this.addEventListener("close-dialog", () => this.closeDialog());
|
||||
}
|
||||
|
||||
protected willUpdate(changedProps: PropertyValues) {
|
||||
super.willUpdate(changedProps);
|
||||
if (!this._device) {
|
||||
return;
|
||||
}
|
||||
if (changedProps.has("_device")) {
|
||||
const tabs = this._getTabs(this._device);
|
||||
if (!tabs.includes(this._currTab)) {
|
||||
this._currTab = tabs[0];
|
||||
}
|
||||
this._fetchData();
|
||||
}
|
||||
}
|
||||
|
||||
protected render(): TemplateResult {
|
||||
if (!this._device) {
|
||||
return html``;
|
||||
}
|
||||
|
||||
const tabs = this._getTabs(this._device);
|
||||
|
||||
return html`
|
||||
<ha-dialog
|
||||
open
|
||||
hideActions
|
||||
@closed=${this.closeDialog}
|
||||
.heading=${createCloseHeading(
|
||||
this.hass,
|
||||
this.hass.localize("ui.dialogs.zha_manage_device.heading")
|
||||
)}
|
||||
>
|
||||
<div slot="heading" class="heading">
|
||||
<ha-header-bar>
|
||||
<ha-icon-button
|
||||
slot="navigationIcon"
|
||||
dialogAction="cancel"
|
||||
.label=${this.hass.localize(
|
||||
"ui.dialogs.more_info_control.dismiss"
|
||||
)}
|
||||
.path=${mdiClose}
|
||||
></ha-icon-button>
|
||||
<div
|
||||
slot="title"
|
||||
class="main-title"
|
||||
.title=${this.hass.localize(
|
||||
"ui.dialogs.zha_manage_device.heading"
|
||||
)}
|
||||
@click=${this._enlarge}
|
||||
>
|
||||
${this.hass.localize("ui.dialogs.zha_manage_device.heading")}
|
||||
</div>
|
||||
</ha-header-bar>
|
||||
<mwc-tab-bar
|
||||
.activeIndex=${tabs.indexOf(this._currTab)}
|
||||
@MDCTabBar:activated=${this._handleTabChanged}
|
||||
>
|
||||
${tabs.map(
|
||||
(tab) => html`
|
||||
<mwc-tab
|
||||
.label=${this.hass.localize(
|
||||
`ui.dialogs.zha_manage_device.tabs.${tab}`
|
||||
)}
|
||||
></mwc-tab>
|
||||
`
|
||||
)}
|
||||
</mwc-tab-bar>
|
||||
</div>
|
||||
|
||||
<div class="content" tabindex="-1" dialogInitialFocus>
|
||||
${cache(
|
||||
this._currTab === "clusters"
|
||||
? html`
|
||||
<zha-manage-clusters
|
||||
.hass=${this.hass}
|
||||
.device=${this._device}
|
||||
></zha-manage-clusters>
|
||||
`
|
||||
: this._currTab === "bindings"
|
||||
? html`
|
||||
${this._bindableDevices.length > 0
|
||||
? html`
|
||||
<zha-device-binding-control
|
||||
.hass=${this.hass}
|
||||
.device=${this._device}
|
||||
.bindableDevices=${this._bindableDevices}
|
||||
></zha-device-binding-control>
|
||||
`
|
||||
: ""}
|
||||
${this._device && this._groups.length > 0
|
||||
? html`
|
||||
<zha-group-binding-control
|
||||
.hass=${this.hass}
|
||||
.device=${this._device}
|
||||
.groups=${this._groups}
|
||||
></zha-group-binding-control>
|
||||
`
|
||||
: ""}
|
||||
`
|
||||
: this._currTab === "signature"
|
||||
? html`
|
||||
<zha-device-zigbee-info
|
||||
.hass=${this.hass}
|
||||
.device=${this._device}
|
||||
></zha-device-zigbee-info>
|
||||
`
|
||||
: html`
|
||||
<zha-device-children
|
||||
.hass=${this.hass}
|
||||
.device=${this._device}
|
||||
></zha-device-children>
|
||||
`
|
||||
)}
|
||||
</div>
|
||||
</ha-dialog>
|
||||
`;
|
||||
}
|
||||
|
||||
private async _fetchData(): Promise<void> {
|
||||
if (this._device && this.hass) {
|
||||
this._bindableDevices =
|
||||
this._device && this._device.device_type !== "Coordinator"
|
||||
? (await fetchBindableDevices(this.hass, this._device.ieee)).sort(
|
||||
sortZHADevices
|
||||
)
|
||||
: [];
|
||||
this._groups = (await fetchGroups(this.hass!)).sort(sortZHAGroups);
|
||||
}
|
||||
}
|
||||
|
||||
private _enlarge() {
|
||||
this.large = !this.large;
|
||||
}
|
||||
|
||||
private _handleTabChanged(ev: CustomEvent): void {
|
||||
const newTab = this._getTabs(this._device)[ev.detail.index];
|
||||
if (newTab === this._currTab) {
|
||||
return;
|
||||
}
|
||||
this._currTab = newTab;
|
||||
}
|
||||
|
||||
private _getTabs = memoizeOne((device: ZHADevice | undefined) => {
|
||||
const tabs: Tab[] = ["clusters", "bindings", "signature"];
|
||||
|
||||
if (
|
||||
device &&
|
||||
(device.device_type === "Router" || device.device_type === "Coordinator")
|
||||
) {
|
||||
tabs.push("children");
|
||||
}
|
||||
|
||||
return tabs;
|
||||
});
|
||||
|
||||
static get styles(): CSSResultGroup {
|
||||
return [
|
||||
haStyleDialog,
|
||||
css`
|
||||
ha-dialog {
|
||||
--dialog-surface-position: static;
|
||||
--dialog-content-position: static;
|
||||
--vertial-align-dialog: flex-start;
|
||||
}
|
||||
|
||||
ha-header-bar {
|
||||
--mdc-theme-on-primary: var(--primary-text-color);
|
||||
--mdc-theme-primary: var(--mdc-theme-surface);
|
||||
flex-shrink: 0;
|
||||
display: block;
|
||||
}
|
||||
.content {
|
||||
outline: none;
|
||||
}
|
||||
@media all and (max-width: 450px), all and (max-height: 500px) {
|
||||
ha-header-bar {
|
||||
--mdc-theme-primary: var(--app-header-background-color);
|
||||
--mdc-theme-on-primary: var(--app-header-text-color, white);
|
||||
border-bottom: none;
|
||||
}
|
||||
}
|
||||
|
||||
.heading {
|
||||
border-bottom: 1px solid
|
||||
var(--mdc-dialog-scroll-divider-color, rgba(0, 0, 0, 0.12));
|
||||
}
|
||||
|
||||
@media all and (min-width: 600px) and (min-height: 501px) {
|
||||
ha-dialog {
|
||||
--mdc-dialog-min-width: 560px;
|
||||
--mdc-dialog-max-width: 560px;
|
||||
--dialog-surface-margin-top: 40px;
|
||||
--mdc-dialog-max-height: calc(100% - 72px);
|
||||
}
|
||||
|
||||
.main-title {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
:host([large]) ha-dialog,
|
||||
ha-dialog[data-domain="camera"] {
|
||||
--mdc-dialog-min-width: 90vw;
|
||||
--mdc-dialog-max-width: 90vw;
|
||||
}
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"dialog-zha-manage-zigbee-device": DialogZHAManageZigbeeDevice;
|
||||
}
|
||||
}
|
@ -12,7 +12,7 @@ import {
|
||||
Cluster,
|
||||
ClusterConfigurationEvent,
|
||||
ClusterConfigurationStatus,
|
||||
fetchClustersForZhaNode,
|
||||
fetchClustersForZhaDevice,
|
||||
reconfigureNode,
|
||||
ZHA_CHANNEL_CFG_DONE,
|
||||
ZHA_CHANNEL_MSG_BIND,
|
||||
@ -321,16 +321,16 @@ class DialogZHAReconfigureDevice extends LitElement {
|
||||
return;
|
||||
}
|
||||
this._clusterConfigurationStatuses = new Map(
|
||||
(await fetchClustersForZhaNode(this.hass, this._params.device.ieee)).map(
|
||||
(cluster: Cluster) => [
|
||||
cluster.id,
|
||||
{
|
||||
cluster: cluster,
|
||||
bindSuccess: undefined,
|
||||
attributes: new Map<number, AttributeConfigurationStatus>(),
|
||||
},
|
||||
]
|
||||
)
|
||||
(
|
||||
await fetchClustersForZhaDevice(this.hass, this._params.device.ieee)
|
||||
).map((cluster: Cluster) => [
|
||||
cluster.id,
|
||||
{
|
||||
cluster: cluster,
|
||||
bindSuccess: undefined,
|
||||
attributes: new Map<number, AttributeConfigurationStatus>(),
|
||||
},
|
||||
])
|
||||
);
|
||||
this._subscribe(this._params);
|
||||
this._status = "started";
|
||||
|
@ -1,19 +0,0 @@
|
||||
import { fireEvent } from "../../../../../common/dom/fire_event";
|
||||
import { ZHADevice } from "../../../../../data/zha";
|
||||
|
||||
export interface ZHAClusterDialogParams {
|
||||
device: ZHADevice;
|
||||
}
|
||||
|
||||
export const loadZHAClusterDialog = () => import("./dialog-zha-cluster");
|
||||
|
||||
export const showZHAClusterDialog = (
|
||||
element: HTMLElement,
|
||||
params: ZHAClusterDialogParams
|
||||
): void => {
|
||||
fireEvent(element, "show-dialog", {
|
||||
dialogTag: "dialog-zha-cluster",
|
||||
dialogImport: loadZHAClusterDialog,
|
||||
dialogParams: params,
|
||||
});
|
||||
};
|
@ -1,20 +0,0 @@
|
||||
import { fireEvent } from "../../../../../common/dom/fire_event";
|
||||
import { ZHADevice } from "../../../../../data/zha";
|
||||
|
||||
export interface ZHADeviceChildrenDialogParams {
|
||||
device: ZHADevice;
|
||||
}
|
||||
|
||||
export const loadZHADeviceChildrenDialog = () =>
|
||||
import("./dialog-zha-device-children");
|
||||
|
||||
export const showZHADeviceChildrenDialog = (
|
||||
element: HTMLElement,
|
||||
zhaDeviceChildrenParams: ZHADeviceChildrenDialogParams
|
||||
): void => {
|
||||
fireEvent(element, "show-dialog", {
|
||||
dialogTag: "dialog-zha-device-children",
|
||||
dialogImport: loadZHADeviceChildrenDialog,
|
||||
dialogParams: zhaDeviceChildrenParams,
|
||||
});
|
||||
};
|
@ -1,20 +0,0 @@
|
||||
import { fireEvent } from "../../../../../common/dom/fire_event";
|
||||
import { ZHADevice } from "../../../../../data/zha";
|
||||
|
||||
export interface ZHADeviceZigbeeInfoDialogParams {
|
||||
device: ZHADevice;
|
||||
}
|
||||
|
||||
export const loadZHADeviceZigbeeInfoDialog = () =>
|
||||
import("./dialog-zha-device-zigbee-info");
|
||||
|
||||
export const showZHADeviceZigbeeInfoDialog = (
|
||||
element: HTMLElement,
|
||||
zhaDeviceZigbeeInfoParams: ZHADeviceZigbeeInfoDialogParams
|
||||
): void => {
|
||||
fireEvent(element, "show-dialog", {
|
||||
dialogTag: "dialog-zha-device-zigbee-info",
|
||||
dialogImport: loadZHADeviceZigbeeInfoDialog,
|
||||
dialogParams: zhaDeviceZigbeeInfoParams,
|
||||
});
|
||||
};
|
@ -0,0 +1,23 @@
|
||||
import { fireEvent } from "../../../../../common/dom/fire_event";
|
||||
import { ZHADevice } from "../../../../../data/zha";
|
||||
|
||||
export type Tab = "clusters" | "bindings" | "signature" | "children";
|
||||
|
||||
export interface ZHAManageZigbeeDeviceDialogParams {
|
||||
device: ZHADevice;
|
||||
tab?: Tab;
|
||||
}
|
||||
|
||||
export const loadZHAManageZigbeeDeviceDialog = () =>
|
||||
import("./dialog-zha-manage-zigbee-device");
|
||||
|
||||
export const showZHAManageZigbeeDeviceDialog = (
|
||||
element: HTMLElement,
|
||||
params: ZHAManageZigbeeDeviceDialogParams
|
||||
): void => {
|
||||
fireEvent(element, "show-dialog", {
|
||||
dialogTag: "dialog-zha-manage-zigbee-device",
|
||||
dialogImport: loadZHAManageZigbeeDeviceDialog,
|
||||
dialogParams: params,
|
||||
});
|
||||
};
|
@ -1,5 +1,5 @@
|
||||
import { HaSelect } from "../../../../../components/ha-select";
|
||||
import { Cluster, ZHADevice } from "../../../../../data/zha";
|
||||
import { ZHADevice } from "../../../../../data/zha";
|
||||
|
||||
export interface ItemSelectedEvent {
|
||||
target?: HaSelect;
|
||||
@ -41,10 +41,6 @@ export interface ZHADeviceSelectedParams {
|
||||
node: ZHADevice;
|
||||
}
|
||||
|
||||
export interface ZHAClusterSelectedParams {
|
||||
cluster: Cluster;
|
||||
}
|
||||
|
||||
export interface NodeServiceData {
|
||||
ieee_address: string;
|
||||
}
|
||||
|
@ -1,6 +1,4 @@
|
||||
import "@material/mwc-button";
|
||||
import "@material/mwc-list/mwc-list-item";
|
||||
import { mdiHelpCircle } from "@mdi/js";
|
||||
import "@polymer/paper-input/paper-input";
|
||||
import {
|
||||
css,
|
||||
@ -10,13 +8,12 @@ import {
|
||||
PropertyValues,
|
||||
TemplateResult,
|
||||
} from "lit";
|
||||
import { property, state } from "lit/decorators";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { stopPropagation } from "../../../../../common/dom/stop_propagation";
|
||||
import "../../../../../components/buttons/ha-call-service-button";
|
||||
import "../../../../../components/ha-card";
|
||||
import "../../../../../components/ha-icon-button";
|
||||
import "../../../../../components/ha-select";
|
||||
import "../../../../../components/ha-service-description";
|
||||
import "../../../../../components/buttons/ha-progress-button";
|
||||
import {
|
||||
Attribute,
|
||||
Cluster,
|
||||
@ -27,7 +24,7 @@ import {
|
||||
} from "../../../../../data/zha";
|
||||
import { haStyle } from "../../../../../resources/styles";
|
||||
import { HomeAssistant } from "../../../../../types";
|
||||
import "../../../ha-config-section";
|
||||
import { forwardHaptic } from "../../../../../data/haptics";
|
||||
import { formatAsPaddedHex } from "./functions";
|
||||
import {
|
||||
ChangeEvent,
|
||||
@ -35,18 +32,15 @@ import {
|
||||
SetAttributeServiceData,
|
||||
} from "./types";
|
||||
|
||||
@customElement("zha-cluster-attributes")
|
||||
export class ZHAClusterAttributes extends LitElement {
|
||||
@property({ attribute: false }) public hass?: HomeAssistant;
|
||||
|
||||
@property() public isWide?: boolean;
|
||||
|
||||
@property() public showHelp = false;
|
||||
|
||||
@property() public selectedNode?: ZHADevice;
|
||||
@property() public device?: ZHADevice;
|
||||
|
||||
@property() public selectedCluster?: Cluster;
|
||||
|
||||
@state() private _attributes: Attribute[] = [];
|
||||
@state() private _attributes: Attribute[] | undefined;
|
||||
|
||||
@state() private _selectedAttributeId?: number;
|
||||
|
||||
@ -54,78 +48,52 @@ export class ZHAClusterAttributes extends LitElement {
|
||||
|
||||
@state() private _manufacturerCodeOverride?: string | number;
|
||||
|
||||
@state() private _readingAttribute = false;
|
||||
|
||||
@state()
|
||||
private _setAttributeServiceData?: SetAttributeServiceData;
|
||||
|
||||
protected updated(changedProperties: PropertyValues): void {
|
||||
if (changedProperties.has("selectedCluster")) {
|
||||
this._attributes = [];
|
||||
this._attributes = undefined;
|
||||
this._selectedAttributeId = undefined;
|
||||
this._attributeValue = "";
|
||||
this._fetchAttributesForCluster();
|
||||
}
|
||||
super.update(changedProperties);
|
||||
super.updated(changedProperties);
|
||||
}
|
||||
|
||||
protected render(): TemplateResult {
|
||||
if (!this.device || !this.selectedCluster || !this._attributes) {
|
||||
return html``;
|
||||
}
|
||||
return html`
|
||||
<ha-config-section .isWide=${this.isWide}>
|
||||
<div class="header" slot="header">
|
||||
<span>
|
||||
${this.hass!.localize(
|
||||
"ui.panel.config.zha.cluster_attributes.header"
|
||||
<ha-card class="content">
|
||||
<div class="attribute-picker">
|
||||
<ha-select
|
||||
.label=${this.hass!.localize(
|
||||
"ui.panel.config.zha.cluster_attributes.attributes_of_cluster"
|
||||
)}
|
||||
</span>
|
||||
<ha-icon-button
|
||||
class="toggle-help-icon"
|
||||
@click=${this._onHelpTap}
|
||||
.path=${mdiHelpCircle}
|
||||
.label=${this.hass!.localize("ui.common.help")}
|
||||
class="menu"
|
||||
.value=${String(this._selectedAttributeId)}
|
||||
@selected=${this._selectedAttributeChanged}
|
||||
@closed=${stopPropagation}
|
||||
fixedMenuPosition
|
||||
naturalMenuWidth
|
||||
>
|
||||
</ha-icon-button>
|
||||
</div>
|
||||
<span slot="introduction">
|
||||
${this.hass!.localize(
|
||||
"ui.panel.config.zha.cluster_attributes.introduction"
|
||||
)}
|
||||
</span>
|
||||
|
||||
<ha-card class="content">
|
||||
<div class="attribute-picker">
|
||||
<ha-select
|
||||
.label=${this.hass!.localize(
|
||||
"ui.panel.config.zha.cluster_attributes.attributes_of_cluster"
|
||||
)}
|
||||
class="menu"
|
||||
.value=${String(this._selectedAttributeId)}
|
||||
@selected=${this._selectedAttributeChanged}
|
||||
@closed=${stopPropagation}
|
||||
fixedMenuPosition
|
||||
naturalMenuWidth
|
||||
>
|
||||
${this._attributes.map(
|
||||
(entry) => html`
|
||||
<mwc-list-item .value=${String(entry.id)}>
|
||||
${entry.name + " (id: " + formatAsPaddedHex(entry.id) + ")"}
|
||||
</mwc-list-item>
|
||||
`
|
||||
)}
|
||||
</ha-select>
|
||||
</div>
|
||||
${this.showHelp
|
||||
? html`
|
||||
<div class="help-text">
|
||||
${this.hass!.localize(
|
||||
"ui.panel.config.zha.cluster_attributes.help_attribute_dropdown"
|
||||
)}
|
||||
</div>
|
||||
${this._attributes.map(
|
||||
(entry) => html`
|
||||
<mwc-list-item .value=${String(entry.id)}>
|
||||
${`${entry.name} (id: ${formatAsPaddedHex(entry.id)})`}
|
||||
</mwc-list-item>
|
||||
`
|
||||
: ""}
|
||||
${this._selectedAttributeId !== undefined
|
||||
? this._renderAttributeInteractions()
|
||||
: ""}
|
||||
</ha-card>
|
||||
</ha-config-section>
|
||||
)}
|
||||
</ha-select>
|
||||
</div>
|
||||
${this._selectedAttributeId !== undefined
|
||||
? this._renderAttributeInteractions()
|
||||
: ""}
|
||||
</ha-card>
|
||||
`;
|
||||
}
|
||||
|
||||
@ -152,20 +120,15 @@ export class ZHAClusterAttributes extends LitElement {
|
||||
></paper-input>
|
||||
</div>
|
||||
<div class="card-actions">
|
||||
<mwc-button @click=${this._onGetZigbeeAttributeClick}>
|
||||
<ha-progress-button
|
||||
@click=${this._onGetZigbeeAttributeClick}
|
||||
.progress=${this._readingAttribute}
|
||||
.disabled=${this._readingAttribute}
|
||||
>
|
||||
${this.hass!.localize(
|
||||
"ui.panel.config.zha.cluster_attributes.get_zigbee_attribute"
|
||||
"ui.panel.config.zha.cluster_attributes.read_zigbee_attribute"
|
||||
)}
|
||||
</mwc-button>
|
||||
${this.showHelp
|
||||
? html`
|
||||
<div class="help-text2">
|
||||
${this.hass!.localize(
|
||||
"ui.panel.config.zha.cluster_attributes.help_get_zigbee_attribute"
|
||||
)}
|
||||
</div>
|
||||
`
|
||||
: ""}
|
||||
</ha-progress-button>
|
||||
<ha-call-service-button
|
||||
.hass=${this.hass}
|
||||
domain="zha"
|
||||
@ -173,44 +136,37 @@ export class ZHAClusterAttributes extends LitElement {
|
||||
.serviceData=${this._setAttributeServiceData}
|
||||
>
|
||||
${this.hass!.localize(
|
||||
"ui.panel.config.zha.cluster_attributes.set_zigbee_attribute"
|
||||
"ui.panel.config.zha.cluster_attributes.write_zigbee_attribute"
|
||||
)}
|
||||
</ha-call-service-button>
|
||||
${this.showHelp
|
||||
? html`
|
||||
<ha-service-description
|
||||
.hass=${this.hass}
|
||||
domain="zha"
|
||||
service="set_zigbee_cluster_attribute"
|
||||
class="help-text2"
|
||||
></ha-service-description>
|
||||
`
|
||||
: ""}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
private async _fetchAttributesForCluster(): Promise<void> {
|
||||
if (this.selectedNode && this.selectedCluster && this.hass) {
|
||||
if (this.device && this.selectedCluster && this.hass) {
|
||||
this._attributes = await fetchAttributesForCluster(
|
||||
this.hass,
|
||||
this.selectedNode!.ieee,
|
||||
this.device!.ieee,
|
||||
this.selectedCluster!.endpoint_id,
|
||||
this.selectedCluster!.id,
|
||||
this.selectedCluster!.type
|
||||
);
|
||||
this._attributes.sort((a, b) => a.name.localeCompare(b.name));
|
||||
if (this._attributes.length > 0) {
|
||||
this._selectedAttributeId = this._attributes[0].id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private _computeReadAttributeServiceData():
|
||||
| ReadAttributeServiceData
|
||||
| undefined {
|
||||
if (!this.selectedCluster || !this.selectedNode) {
|
||||
if (!this.selectedCluster || !this.device) {
|
||||
return undefined;
|
||||
}
|
||||
return {
|
||||
ieee: this.selectedNode!.ieee,
|
||||
ieee: this.device!.ieee,
|
||||
endpoint_id: this.selectedCluster!.endpoint_id,
|
||||
cluster_id: this.selectedCluster!.id,
|
||||
cluster_type: this.selectedCluster!.type,
|
||||
@ -224,11 +180,11 @@ export class ZHAClusterAttributes extends LitElement {
|
||||
private _computeSetAttributeServiceData():
|
||||
| SetAttributeServiceData
|
||||
| undefined {
|
||||
if (!this.selectedCluster || !this.selectedNode) {
|
||||
if (!this.selectedCluster || !this.device) {
|
||||
return undefined;
|
||||
}
|
||||
return {
|
||||
ieee: this.selectedNode!.ieee,
|
||||
ieee: this.device!.ieee,
|
||||
endpoint_id: this.selectedCluster!.endpoint_id,
|
||||
cluster_id: this.selectedCluster!.id,
|
||||
cluster_type: this.selectedCluster!.type,
|
||||
@ -250,17 +206,24 @@ export class ZHAClusterAttributes extends LitElement {
|
||||
this._setAttributeServiceData = this._computeSetAttributeServiceData();
|
||||
}
|
||||
|
||||
private async _onGetZigbeeAttributeClick(): Promise<void> {
|
||||
private async _onGetZigbeeAttributeClick(ev: CustomEvent): Promise<void> {
|
||||
const button = ev.currentTarget as any;
|
||||
const data = this._computeReadAttributeServiceData();
|
||||
if (data && this.hass) {
|
||||
this._attributeValue = await readAttributeValue(this.hass, data);
|
||||
this._readingAttribute = true;
|
||||
try {
|
||||
this._attributeValue = await readAttributeValue(this.hass, data);
|
||||
forwardHaptic("success");
|
||||
button.actionSuccess();
|
||||
} catch (err: any) {
|
||||
forwardHaptic("failure");
|
||||
button.actionError();
|
||||
} finally {
|
||||
this._readingAttribute = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private _onHelpTap(): void {
|
||||
this.showHelp = !this.showHelp;
|
||||
}
|
||||
|
||||
private _selectedAttributeChanged(event: ItemSelectedEvent): void {
|
||||
this._selectedAttributeId = Number(event.target!.value);
|
||||
this._attributeValue = "";
|
||||
@ -278,14 +241,6 @@ export class ZHAClusterAttributes extends LitElement {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.content {
|
||||
margin-top: 24px;
|
||||
}
|
||||
|
||||
ha-card {
|
||||
max-width: 680px;
|
||||
}
|
||||
|
||||
.card-actions.warning ha-call-service-button {
|
||||
color: var(--error-color);
|
||||
}
|
||||
@ -306,33 +261,6 @@ export class ZHAClusterAttributes extends LitElement {
|
||||
.header {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.toggle-help-icon {
|
||||
float: right;
|
||||
top: -6px;
|
||||
right: 0;
|
||||
padding-right: 0px;
|
||||
color: var(--primary-color);
|
||||
}
|
||||
|
||||
ha-service-description {
|
||||
display: block;
|
||||
color: grey;
|
||||
}
|
||||
|
||||
[hidden] {
|
||||
display: none;
|
||||
}
|
||||
.help-text {
|
||||
color: grey;
|
||||
padding-left: 28px;
|
||||
padding-right: 28px;
|
||||
padding-bottom: 16px;
|
||||
}
|
||||
.help-text2 {
|
||||
color: grey;
|
||||
padding: 16px;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
@ -343,5 +271,3 @@ declare global {
|
||||
"zha-cluster-attributes": ZHAClusterAttributes;
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("zha-cluster-attributes", ZHAClusterAttributes);
|
||||
|
@ -1,5 +1,4 @@
|
||||
import "@material/mwc-list/mwc-list-item";
|
||||
import { mdiHelpCircle } from "@mdi/js";
|
||||
import "@polymer/paper-input/paper-input";
|
||||
import {
|
||||
css,
|
||||
@ -13,9 +12,7 @@ import { property, state } from "lit/decorators";
|
||||
import { stopPropagation } from "../../../../../common/dom/stop_propagation";
|
||||
import "../../../../../components/buttons/ha-call-service-button";
|
||||
import "../../../../../components/ha-card";
|
||||
import "../../../../../components/ha-icon-button";
|
||||
import "../../../../../components/ha-select";
|
||||
import "../../../../../components/ha-service-description";
|
||||
import {
|
||||
Cluster,
|
||||
Command,
|
||||
@ -24,7 +21,6 @@ import {
|
||||
} from "../../../../../data/zha";
|
||||
import { haStyle } from "../../../../../resources/styles";
|
||||
import { HomeAssistant } from "../../../../../types";
|
||||
import "../../../ha-config-section";
|
||||
import { formatAsPaddedHex } from "./functions";
|
||||
import { ChangeEvent, IssueCommandServiceData } from "./types";
|
||||
|
||||
@ -33,13 +29,11 @@ export class ZHAClusterCommands extends LitElement {
|
||||
|
||||
@property() public isWide?: boolean;
|
||||
|
||||
@property() public selectedNode?: ZHADevice;
|
||||
@property() public device?: ZHADevice;
|
||||
|
||||
@property() public selectedCluster?: Cluster;
|
||||
|
||||
@state() private _showHelp = false;
|
||||
|
||||
@state() private _commands: Command[] = [];
|
||||
@state() private _commands: Command[] | undefined;
|
||||
|
||||
@state() private _selectedCommandId?: number;
|
||||
|
||||
@ -50,132 +44,97 @@ export class ZHAClusterCommands extends LitElement {
|
||||
|
||||
protected updated(changedProperties: PropertyValues): void {
|
||||
if (changedProperties.has("selectedCluster")) {
|
||||
this._commands = [];
|
||||
this._commands = undefined;
|
||||
this._selectedCommandId = undefined;
|
||||
this._fetchCommandsForCluster();
|
||||
}
|
||||
super.update(changedProperties);
|
||||
super.updated(changedProperties);
|
||||
}
|
||||
|
||||
protected render(): TemplateResult {
|
||||
if (!this.device || !this.selectedCluster || !this._commands) {
|
||||
return html``;
|
||||
}
|
||||
return html`
|
||||
<ha-config-section .isWide=${this.isWide}>
|
||||
<div class="header" slot="header">
|
||||
<span>
|
||||
${this.hass!.localize(
|
||||
"ui.panel.config.zha.cluster_commands.header"
|
||||
<ha-card class="content">
|
||||
<div class="command-picker">
|
||||
<ha-select
|
||||
.label=${this.hass!.localize(
|
||||
"ui.panel.config.zha.cluster_commands.commands_of_cluster"
|
||||
)}
|
||||
</span>
|
||||
<ha-icon-button
|
||||
class="toggle-help-icon"
|
||||
@click=${this._onHelpTap}
|
||||
.path=${mdiHelpCircle}
|
||||
.label=${this.hass!.localize("ui.common.help")}
|
||||
class="menu"
|
||||
.value=${String(this._selectedCommandId)}
|
||||
@selected=${this._selectedCommandChanged}
|
||||
@closed=${stopPropagation}
|
||||
fixedMenuPosition
|
||||
naturalMenuWidth
|
||||
>
|
||||
</ha-icon-button>
|
||||
${this._commands.map(
|
||||
(entry) => html`
|
||||
<mwc-list-item .value=${String(entry.id)}>
|
||||
${entry.name + " (id: " + formatAsPaddedHex(entry.id) + ")"}
|
||||
</mwc-list-item>
|
||||
`
|
||||
)}
|
||||
</ha-select>
|
||||
</div>
|
||||
<span slot="introduction">
|
||||
${this.hass!.localize(
|
||||
"ui.panel.config.zha.cluster_commands.introduction"
|
||||
)}
|
||||
</span>
|
||||
|
||||
<ha-card class="content">
|
||||
<div class="command-picker">
|
||||
<ha-select
|
||||
.label=${this.hass!.localize(
|
||||
"ui.panel.config.zha.cluster_commands.commands_of_cluster"
|
||||
)}
|
||||
class="menu"
|
||||
.value=${String(this._selectedCommandId)}
|
||||
@selected=${this._selectedCommandChanged}
|
||||
@closed=${stopPropagation}
|
||||
fixedMenuPosition
|
||||
naturalMenuWidth
|
||||
>
|
||||
${this._commands.map(
|
||||
(entry) => html`
|
||||
<mwc-list-item .value=${String(entry.id)}>
|
||||
${entry.name + " (id: " + formatAsPaddedHex(entry.id) + ")"}
|
||||
</mwc-list-item>
|
||||
`
|
||||
)}
|
||||
</ha-select>
|
||||
</div>
|
||||
${this._showHelp
|
||||
? html`
|
||||
<div class="help-text">
|
||||
${this.hass!.localize(
|
||||
"ui.panel.config.zha.cluster_commands.help_command_dropdown"
|
||||
${this._selectedCommandId !== undefined
|
||||
? html`
|
||||
<div class="input-text">
|
||||
<paper-input
|
||||
label=${this.hass!.localize(
|
||||
"ui.panel.config.zha.common.manufacturer_code_override"
|
||||
)}
|
||||
</div>
|
||||
`
|
||||
: ""}
|
||||
${this._selectedCommandId !== undefined
|
||||
? html`
|
||||
<div class="input-text">
|
||||
<paper-input
|
||||
label=${this.hass!.localize(
|
||||
"ui.panel.config.zha.common.manufacturer_code_override"
|
||||
)}
|
||||
type="number"
|
||||
.value=${this._manufacturerCodeOverride}
|
||||
@value-changed=${this._onManufacturerCodeOverrideChanged}
|
||||
placeholder=${this.hass!.localize(
|
||||
"ui.panel.config.zha.common.value"
|
||||
)}
|
||||
></paper-input>
|
||||
</div>
|
||||
<div class="card-actions">
|
||||
<ha-call-service-button
|
||||
.hass=${this.hass}
|
||||
domain="zha"
|
||||
service="issue_zigbee_cluster_command"
|
||||
.serviceData=${this._issueClusterCommandServiceData}
|
||||
>
|
||||
${this.hass!.localize(
|
||||
"ui.panel.config.zha.cluster_commands.issue_zigbee_command"
|
||||
)}
|
||||
</ha-call-service-button>
|
||||
${this._showHelp
|
||||
? html`
|
||||
<ha-service-description
|
||||
.hass=${this.hass}
|
||||
domain="zha"
|
||||
service="issue_zigbee_cluster_command"
|
||||
class="help-text2"
|
||||
></ha-service-description>
|
||||
`
|
||||
: ""}
|
||||
</div>
|
||||
`
|
||||
: ""}
|
||||
</ha-card>
|
||||
</ha-config-section>
|
||||
type="number"
|
||||
.value=${this._manufacturerCodeOverride}
|
||||
@value-changed=${this._onManufacturerCodeOverrideChanged}
|
||||
placeholder=${this.hass!.localize(
|
||||
"ui.panel.config.zha.common.value"
|
||||
)}
|
||||
></paper-input>
|
||||
</div>
|
||||
<div class="card-actions">
|
||||
<ha-call-service-button
|
||||
.hass=${this.hass}
|
||||
domain="zha"
|
||||
service="issue_zigbee_cluster_command"
|
||||
.serviceData=${this._issueClusterCommandServiceData}
|
||||
>
|
||||
${this.hass!.localize(
|
||||
"ui.panel.config.zha.cluster_commands.issue_zigbee_command"
|
||||
)}
|
||||
</ha-call-service-button>
|
||||
</div>
|
||||
`
|
||||
: ""}
|
||||
</ha-card>
|
||||
`;
|
||||
}
|
||||
|
||||
private async _fetchCommandsForCluster(): Promise<void> {
|
||||
if (this.selectedNode && this.selectedCluster && this.hass) {
|
||||
if (this.device && this.selectedCluster && this.hass) {
|
||||
this._commands = await fetchCommandsForCluster(
|
||||
this.hass,
|
||||
this.selectedNode!.ieee,
|
||||
this.device!.ieee,
|
||||
this.selectedCluster!.endpoint_id,
|
||||
this.selectedCluster!.id,
|
||||
this.selectedCluster!.type
|
||||
);
|
||||
this._commands.sort((a, b) => a.name.localeCompare(b.name));
|
||||
if (this._commands.length > 0) {
|
||||
this._selectedCommandId = this._commands[0].id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private _computeIssueClusterCommandServiceData():
|
||||
| IssueCommandServiceData
|
||||
| undefined {
|
||||
if (!this.selectedNode || !this.selectedCluster) {
|
||||
if (!this.device || !this.selectedCluster || !this._commands) {
|
||||
return undefined;
|
||||
}
|
||||
return {
|
||||
ieee: this.selectedNode!.ieee,
|
||||
ieee: this.device!.ieee,
|
||||
endpoint_id: this.selectedCluster!.endpoint_id,
|
||||
cluster_id: this.selectedCluster!.id,
|
||||
cluster_type: this.selectedCluster!.type,
|
||||
@ -192,10 +151,6 @@ export class ZHAClusterCommands extends LitElement {
|
||||
this._computeIssueClusterCommandServiceData();
|
||||
}
|
||||
|
||||
private _onHelpTap(): void {
|
||||
this._showHelp = !this._showHelp;
|
||||
}
|
||||
|
||||
private _selectedCommandChanged(event): void {
|
||||
this._selectedCommandId = Number(event.target.value);
|
||||
this._issueClusterCommandServiceData =
|
||||
@ -213,14 +168,6 @@ export class ZHAClusterCommands extends LitElement {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.content {
|
||||
margin-top: 24px;
|
||||
}
|
||||
|
||||
ha-card {
|
||||
max-width: 680px;
|
||||
}
|
||||
|
||||
.card-actions.warning ha-call-service-button {
|
||||
color: var(--error-color);
|
||||
}
|
||||
@ -238,18 +185,6 @@ export class ZHAClusterCommands extends LitElement {
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
.help-text {
|
||||
color: grey;
|
||||
padding-left: 28px;
|
||||
padding-right: 28px;
|
||||
padding-bottom: 16px;
|
||||
}
|
||||
|
||||
.help-text2 {
|
||||
color: grey;
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.header {
|
||||
flex-grow: 1;
|
||||
}
|
||||
@ -261,15 +196,6 @@ export class ZHAClusterCommands extends LitElement {
|
||||
padding-right: 0px;
|
||||
color: var(--primary-color);
|
||||
}
|
||||
|
||||
ha-service-description {
|
||||
display: block;
|
||||
color: grey;
|
||||
}
|
||||
|
||||
[hidden] {
|
||||
display: none;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
|
@ -59,7 +59,7 @@ export class ZHAClustersDataTable extends LitElement {
|
||||
title: "ID",
|
||||
template: (id: number) => html` ${formatAsPaddedHex(id)} `,
|
||||
sortable: true,
|
||||
width: "15%",
|
||||
width: "25%",
|
||||
},
|
||||
endpoint_id: {
|
||||
title: "Endpoint ID",
|
||||
|
@ -1,195 +0,0 @@
|
||||
import "@material/mwc-list/mwc-list-item";
|
||||
import { mdiHelpCircle } from "@mdi/js";
|
||||
import {
|
||||
css,
|
||||
CSSResultGroup,
|
||||
html,
|
||||
LitElement,
|
||||
PropertyValues,
|
||||
TemplateResult,
|
||||
} from "lit";
|
||||
import { property, state } from "lit/decorators";
|
||||
import { fireEvent } from "../../../../../common/dom/fire_event";
|
||||
import { stopPropagation } from "../../../../../common/dom/stop_propagation";
|
||||
import "../../../../../components/buttons/ha-call-service-button";
|
||||
import "../../../../../components/ha-card";
|
||||
import "../../../../../components/ha-icon-button";
|
||||
import "../../../../../components/ha-select";
|
||||
import "../../../../../components/ha-service-description";
|
||||
import {
|
||||
Cluster,
|
||||
fetchClustersForZhaNode,
|
||||
ZHADevice,
|
||||
} from "../../../../../data/zha";
|
||||
import { haStyle } from "../../../../../resources/styles";
|
||||
import { HomeAssistant } from "../../../../../types";
|
||||
import "../../../ha-config-section";
|
||||
import { computeClusterKey } from "./functions";
|
||||
|
||||
declare global {
|
||||
// for fire event
|
||||
interface HASSDomEvents {
|
||||
"zha-cluster-selected": {
|
||||
cluster?: Cluster;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export class ZHAClusters extends LitElement {
|
||||
@property({ attribute: false }) public hass?: HomeAssistant;
|
||||
|
||||
@property() public isWide?: boolean;
|
||||
|
||||
@property() public selectedDevice?: ZHADevice;
|
||||
|
||||
@property() public showHelp = false;
|
||||
|
||||
@state() private _selectedClusterIndex = -1;
|
||||
|
||||
@state() private _clusters: Cluster[] = [];
|
||||
|
||||
protected updated(changedProperties: PropertyValues): void {
|
||||
if (changedProperties.has("selectedDevice")) {
|
||||
this._clusters = [];
|
||||
this._selectedClusterIndex = -1;
|
||||
fireEvent(this, "zha-cluster-selected", {
|
||||
cluster: undefined,
|
||||
});
|
||||
this._fetchClustersForZhaNode();
|
||||
}
|
||||
super.update(changedProperties);
|
||||
}
|
||||
|
||||
protected render(): TemplateResult {
|
||||
return html`
|
||||
<ha-config-section .isWide=${this.isWide}>
|
||||
<div class="header" slot="header">
|
||||
<ha-icon-button
|
||||
class="toggle-help-icon"
|
||||
@click=${this._onHelpTap}
|
||||
.path=${mdiHelpCircle}
|
||||
.label=${this.hass!.localize("ui.common.help")}
|
||||
>
|
||||
</ha-icon-button>
|
||||
</div>
|
||||
<span slot="introduction">
|
||||
${this.hass!.localize("ui.panel.config.zha.clusters.introduction")}
|
||||
</span>
|
||||
|
||||
<ha-card class="content">
|
||||
<div class="node-picker">
|
||||
<ha-select
|
||||
.label=${this.hass!.localize(
|
||||
"ui.panel.config.zha.common.clusters"
|
||||
)}
|
||||
class="menu"
|
||||
.value=${String(this._selectedClusterIndex)}
|
||||
@selected=${this._selectedClusterChanged}
|
||||
@closed=${stopPropagation}
|
||||
fixedMenuPosition
|
||||
naturalMenuWidth
|
||||
>
|
||||
${this._clusters.map(
|
||||
(entry, idx) => html`
|
||||
<mwc-list-item .value=${String(idx)}
|
||||
>${computeClusterKey(entry)}</mwc-list-item
|
||||
>
|
||||
`
|
||||
)}
|
||||
</ha-select>
|
||||
</div>
|
||||
${this.showHelp
|
||||
? html`
|
||||
<div class="help-text">
|
||||
${this.hass!.localize(
|
||||
"ui.panel.config.zha.clusters.help_cluster_dropdown"
|
||||
)}
|
||||
</div>
|
||||
`
|
||||
: ""}
|
||||
</ha-card>
|
||||
</ha-config-section>
|
||||
`;
|
||||
}
|
||||
|
||||
private async _fetchClustersForZhaNode(): Promise<void> {
|
||||
if (this.hass) {
|
||||
this._clusters = await fetchClustersForZhaNode(
|
||||
this.hass,
|
||||
this.selectedDevice!.ieee
|
||||
);
|
||||
this._clusters.sort((a, b) => a.name.localeCompare(b.name));
|
||||
}
|
||||
}
|
||||
|
||||
private _selectedClusterChanged(event): void {
|
||||
this._selectedClusterIndex = Number(event.target!.value);
|
||||
fireEvent(this, "zha-cluster-selected", {
|
||||
cluster: this._clusters[this._selectedClusterIndex],
|
||||
});
|
||||
}
|
||||
|
||||
private _onHelpTap(): void {
|
||||
this.showHelp = !this.showHelp;
|
||||
}
|
||||
|
||||
static get styles(): CSSResultGroup {
|
||||
return [
|
||||
haStyle,
|
||||
css`
|
||||
ha-select {
|
||||
margin-top: 16px;
|
||||
}
|
||||
.menu {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.content {
|
||||
margin-top: 24px;
|
||||
}
|
||||
|
||||
.header {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
ha-card {
|
||||
max-width: 680px;
|
||||
}
|
||||
|
||||
.node-picker {
|
||||
align-items: center;
|
||||
padding-left: 28px;
|
||||
padding-right: 28px;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
.toggle-help-icon {
|
||||
float: right;
|
||||
top: -6px;
|
||||
right: 0;
|
||||
padding-right: 0px;
|
||||
color: var(--primary-color);
|
||||
}
|
||||
|
||||
[hidden] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.help-text {
|
||||
color: grey;
|
||||
padding-left: 28px;
|
||||
padding-right: 28px;
|
||||
padding-bottom: 16px;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"zha-cluster": ZHAClusters;
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("zha-clusters", ZHAClusters);
|
@ -1,6 +1,4 @@
|
||||
import "@material/mwc-button/mwc-button";
|
||||
import "@material/mwc-list/mwc-list-item";
|
||||
import { mdiHelpCircle } from "@mdi/js";
|
||||
import {
|
||||
css,
|
||||
CSSResultGroup,
|
||||
@ -11,26 +9,19 @@ import {
|
||||
} from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { stopPropagation } from "../../../../../common/dom/stop_propagation";
|
||||
import "../../../../../components/buttons/ha-call-service-button";
|
||||
import "../../../../../components/buttons/ha-progress-button";
|
||||
import "../../../../../components/ha-card";
|
||||
import "../../../../../components/ha-icon-button";
|
||||
import "../../../../../components/ha-select";
|
||||
import "../../../../../components/ha-service-description";
|
||||
import { bindDevices, unbindDevices, ZHADevice } from "../../../../../data/zha";
|
||||
import { haStyle } from "../../../../../resources/styles";
|
||||
import { HomeAssistant } from "../../../../../types";
|
||||
import "../../../ha-config-section";
|
||||
import { ItemSelectedEvent } from "./types";
|
||||
|
||||
@customElement("zha-device-binding-control")
|
||||
export class ZHADeviceBindingControl extends LitElement {
|
||||
@property({ attribute: false }) public hass?: HomeAssistant;
|
||||
|
||||
@property() public isWide?: boolean;
|
||||
|
||||
@property() public selectedDevice?: ZHADevice;
|
||||
|
||||
@state() private _showHelp = false;
|
||||
@property() public device?: ZHADevice;
|
||||
|
||||
@state() private _bindTargetIndex = -1;
|
||||
|
||||
@ -38,77 +29,58 @@ export class ZHADeviceBindingControl extends LitElement {
|
||||
|
||||
@state() private _deviceToBind?: ZHADevice;
|
||||
|
||||
@state() private _bindingOperationInProgress = false;
|
||||
|
||||
protected updated(changedProperties: PropertyValues): void {
|
||||
if (changedProperties.has("selectedDevice")) {
|
||||
if (changedProperties.has("device")) {
|
||||
this._bindTargetIndex = -1;
|
||||
}
|
||||
super.update(changedProperties);
|
||||
super.updated(changedProperties);
|
||||
}
|
||||
|
||||
protected render(): TemplateResult {
|
||||
return html`
|
||||
<ha-config-section .isWide=${this.isWide}>
|
||||
<div class="header" slot="header">
|
||||
<span>Device Binding</span>
|
||||
<ha-icon-button
|
||||
class="toggle-help-icon"
|
||||
@click=${this._onHelpTap}
|
||||
.path=${mdiHelpCircle}
|
||||
.label=${this.hass!.localize("ui.common.help")}
|
||||
<ha-card class="content">
|
||||
<div class="command-picker">
|
||||
<ha-select
|
||||
label=${this.hass!.localize(
|
||||
"ui.panel.config.zha.device_binding.picker_label"
|
||||
)}
|
||||
class="menu"
|
||||
.value=${String(this._bindTargetIndex)}
|
||||
@selected=${this._bindTargetIndexChanged}
|
||||
@closed=${stopPropagation}
|
||||
fixedMenuPosition
|
||||
naturalMenuWidth
|
||||
>
|
||||
</ha-icon-button>
|
||||
</div>
|
||||
<span slot="introduction">Bind and unbind devices.</span>
|
||||
|
||||
<ha-card class="content">
|
||||
<div class="command-picker">
|
||||
<ha-select
|
||||
label="Bindable Devices"
|
||||
class="menu"
|
||||
.value=${String(this._bindTargetIndex)}
|
||||
@selected=${this._bindTargetIndexChanged}
|
||||
@closed=${stopPropagation}
|
||||
fixedMenuPosition
|
||||
naturalMenuWidth
|
||||
>
|
||||
${this.bindableDevices.map(
|
||||
(device, idx) => html`
|
||||
<mwc-list-item .value=${String(idx)}>
|
||||
${device.user_given_name
|
||||
? device.user_given_name
|
||||
: device.name}
|
||||
</mwc-list-item>
|
||||
`
|
||||
)}
|
||||
</ha-select>
|
||||
</div>
|
||||
${this._showHelp
|
||||
? html`
|
||||
<div class="helpText">
|
||||
Select a device to issue a bind command.
|
||||
</div>
|
||||
${this.bindableDevices.map(
|
||||
(device, idx) => html`
|
||||
<mwc-list-item .value=${String(idx)}>
|
||||
${device.user_given_name
|
||||
? device.user_given_name
|
||||
: device.name}
|
||||
</mwc-list-item>
|
||||
`
|
||||
: ""}
|
||||
<div class="card-actions">
|
||||
<mwc-button
|
||||
@click=${this._onBindDevicesClick}
|
||||
.disabled=${!(this._deviceToBind && this.selectedDevice)}
|
||||
>Bind</mwc-button
|
||||
>
|
||||
${this._showHelp
|
||||
? html` <div class="helpText">Bind devices.</div> `
|
||||
: ""}
|
||||
<mwc-button
|
||||
@click=${this._onUnbindDevicesClick}
|
||||
.disabled=${!(this._deviceToBind && this.selectedDevice)}
|
||||
>Unbind</mwc-button
|
||||
>
|
||||
${this._showHelp
|
||||
? html` <div class="helpText">Unbind devices.</div> `
|
||||
: ""}
|
||||
</div>
|
||||
</ha-card>
|
||||
</ha-config-section>
|
||||
)}
|
||||
</ha-select>
|
||||
</div>
|
||||
<div class="card-actions">
|
||||
<ha-progress-button
|
||||
@click=${this._onBindDevicesClick}
|
||||
.disabled=${!(this._deviceToBind && this.device) ||
|
||||
this._bindingOperationInProgress}
|
||||
>
|
||||
${this.hass!.localize("ui.panel.config.zha.device_binding.bind")}
|
||||
</ha-progress-button>
|
||||
<ha-progress-button
|
||||
@click=${this._onUnbindDevicesClick}
|
||||
.disabled=${!(this._deviceToBind && this.device) ||
|
||||
this._bindingOperationInProgress}
|
||||
>
|
||||
${this.hass!.localize("ui.panel.config.zha.device_binding.unbind")}
|
||||
</ha-progress-button>
|
||||
</div>
|
||||
</ha-card>
|
||||
`;
|
||||
}
|
||||
|
||||
@ -120,27 +92,41 @@ export class ZHADeviceBindingControl extends LitElement {
|
||||
: this.bindableDevices[this._bindTargetIndex];
|
||||
}
|
||||
|
||||
private _onHelpTap(): void {
|
||||
this._showHelp = !this._showHelp;
|
||||
}
|
||||
|
||||
private async _onBindDevicesClick(): Promise<void> {
|
||||
if (this.hass && this._deviceToBind && this.selectedDevice) {
|
||||
await bindDevices(
|
||||
this.hass,
|
||||
this.selectedDevice.ieee,
|
||||
this._deviceToBind.ieee
|
||||
);
|
||||
private async _onBindDevicesClick(ev: CustomEvent): Promise<void> {
|
||||
const button = ev.currentTarget as any;
|
||||
if (this.hass && this._deviceToBind && this.device) {
|
||||
this._bindingOperationInProgress = true;
|
||||
button.progress = true;
|
||||
try {
|
||||
await bindDevices(this.hass, this.device.ieee, this._deviceToBind.ieee);
|
||||
button.actionSuccess();
|
||||
} catch (err: any) {
|
||||
button.actionError();
|
||||
} finally {
|
||||
this._bindingOperationInProgress = false;
|
||||
button.progress = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async _onUnbindDevicesClick(): Promise<void> {
|
||||
if (this.hass && this._deviceToBind && this.selectedDevice) {
|
||||
await unbindDevices(
|
||||
this.hass,
|
||||
this.selectedDevice.ieee,
|
||||
this._deviceToBind.ieee
|
||||
);
|
||||
private async _onUnbindDevicesClick(ev: CustomEvent): Promise<void> {
|
||||
const button = ev.currentTarget as any;
|
||||
if (this.hass && this._deviceToBind && this.device) {
|
||||
this._bindingOperationInProgress = true;
|
||||
button.progress = true;
|
||||
try {
|
||||
await unbindDevices(
|
||||
this.hass,
|
||||
this.device.ieee,
|
||||
this._deviceToBind.ieee
|
||||
);
|
||||
button.actionSuccess();
|
||||
} catch (err: any) {
|
||||
button.actionError();
|
||||
} finally {
|
||||
this._bindingOperationInProgress = false;
|
||||
button.progress = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -152,18 +138,6 @@ export class ZHADeviceBindingControl extends LitElement {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.content {
|
||||
margin-top: 24px;
|
||||
}
|
||||
|
||||
ha-card {
|
||||
max-width: 680px;
|
||||
}
|
||||
|
||||
.card-actions.warning ha-call-service-button {
|
||||
color: var(--error-color);
|
||||
}
|
||||
|
||||
.command-picker {
|
||||
align-items: center;
|
||||
padding-left: 28px;
|
||||
@ -171,33 +145,9 @@ export class ZHADeviceBindingControl extends LitElement {
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
.helpText {
|
||||
color: grey;
|
||||
padding-left: 28px;
|
||||
padding-right: 28px;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
.header {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.toggle-help-icon {
|
||||
float: right;
|
||||
top: -6px;
|
||||
right: 0;
|
||||
padding-right: 0px;
|
||||
color: var(--primary-color);
|
||||
}
|
||||
|
||||
ha-service-description {
|
||||
display: block;
|
||||
color: grey;
|
||||
}
|
||||
|
||||
[hidden] {
|
||||
display: none;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
|
@ -1,12 +1,9 @@
|
||||
import { CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||
import { html, LitElement, PropertyValues, TemplateResult } from "lit";
|
||||
import memoizeOne from "memoize-one";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { computeRTLDirection } from "../../../../../common/util/compute_rtl";
|
||||
import "../../../../../components/ha-code-editor";
|
||||
import { createCloseHeading } from "../../../../../components/ha-dialog";
|
||||
import { haStyleDialog } from "../../../../../resources/styles";
|
||||
import { HomeAssistant } from "../../../../../types";
|
||||
import { ZHADeviceChildrenDialogParams } from "./show-dialog-zha-device-children";
|
||||
import "../../../../../components/data-table/ha-data-table";
|
||||
import type {
|
||||
DataTableColumnContainer,
|
||||
@ -14,7 +11,6 @@ import type {
|
||||
} from "../../../../../components/data-table/ha-data-table";
|
||||
import "../../../../../components/ha-circular-progress";
|
||||
import { fetchDevices, ZHADevice } from "../../../../../data/zha";
|
||||
import { fireEvent } from "../../../../../common/dom/fire_event";
|
||||
|
||||
export interface DeviceRowData extends DataTableRowData {
|
||||
id: string;
|
||||
@ -22,14 +18,21 @@ export interface DeviceRowData extends DataTableRowData {
|
||||
lqi: number;
|
||||
}
|
||||
|
||||
@customElement("dialog-zha-device-children")
|
||||
class DialogZHADeviceChildren extends LitElement {
|
||||
@customElement("zha-device-children")
|
||||
class ZHADeviceChildren extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@state() private _device: ZHADevice | undefined;
|
||||
@property() public device: ZHADevice | undefined;
|
||||
|
||||
@state() private _devices: Map<string, ZHADevice> | undefined;
|
||||
|
||||
protected updated(changedProperties: PropertyValues) {
|
||||
super.updated(changedProperties);
|
||||
if (this.hass && changedProperties.has("device")) {
|
||||
this._fetchData();
|
||||
}
|
||||
}
|
||||
|
||||
private _deviceChildren = memoizeOne(
|
||||
(
|
||||
device: ZHADevice | undefined,
|
||||
@ -69,70 +72,45 @@ class DialogZHADeviceChildren extends LitElement {
|
||||
},
|
||||
};
|
||||
|
||||
public showDialog(params: ZHADeviceChildrenDialogParams): void {
|
||||
this._device = params.device;
|
||||
this._fetchData();
|
||||
}
|
||||
|
||||
public closeDialog(): void {
|
||||
this._device = undefined;
|
||||
this._devices = undefined;
|
||||
fireEvent(this, "dialog-closed", { dialog: this.localName });
|
||||
}
|
||||
|
||||
protected render(): TemplateResult {
|
||||
if (!this._device) {
|
||||
if (!this.device) {
|
||||
return html``;
|
||||
}
|
||||
return html`
|
||||
<ha-dialog
|
||||
hideActions
|
||||
open
|
||||
@closed=${this.closeDialog}
|
||||
.heading=${createCloseHeading(
|
||||
this.hass,
|
||||
this.hass.localize(`ui.dialogs.zha_device_info.device_children`)
|
||||
)}
|
||||
>
|
||||
${!this._devices
|
||||
? html`<ha-circular-progress
|
||||
alt="Loading"
|
||||
size="large"
|
||||
active
|
||||
></ha-circular-progress>`
|
||||
: html`<ha-data-table
|
||||
.hass=${this.hass}
|
||||
.columns=${this._columns}
|
||||
.data=${this._deviceChildren(this._device, this._devices)}
|
||||
auto-height
|
||||
.dir=${computeRTLDirection(this.hass)}
|
||||
.searchLabel=${this.hass.localize(
|
||||
"ui.components.data-table.search"
|
||||
)}
|
||||
.noDataText=${this.hass.localize(
|
||||
"ui.components.data-table.no-data"
|
||||
)}
|
||||
></ha-data-table>`}
|
||||
</ha-dialog>
|
||||
${!this._devices
|
||||
? html`<ha-circular-progress
|
||||
alt="Loading"
|
||||
size="large"
|
||||
active
|
||||
></ha-circular-progress>`
|
||||
: html`<ha-data-table
|
||||
.hass=${this.hass}
|
||||
.columns=${this._columns}
|
||||
.data=${this._deviceChildren(this.device, this._devices)}
|
||||
auto-height
|
||||
.dir=${computeRTLDirection(this.hass)}
|
||||
.searchLabel=${this.hass.localize(
|
||||
"ui.components.data-table.search"
|
||||
)}
|
||||
.noDataText=${this.hass.localize(
|
||||
"ui.components.data-table.no-data"
|
||||
)}
|
||||
></ha-data-table>`}
|
||||
`;
|
||||
}
|
||||
|
||||
private async _fetchData(): Promise<void> {
|
||||
if (this._device && this.hass) {
|
||||
if (this.device && this.hass) {
|
||||
const devices = await fetchDevices(this.hass!);
|
||||
this._devices = new Map(
|
||||
devices.map((device: ZHADevice) => [device.ieee, device])
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
static get styles(): CSSResultGroup {
|
||||
return haStyleDialog;
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"dialog-zha-device-children": DialogZHADeviceChildren;
|
||||
"zha-device-children": ZHADeviceChildren;
|
||||
}
|
||||
}
|
@ -0,0 +1,47 @@
|
||||
import { html, LitElement, PropertyValues, TemplateResult } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import "../../../../../components/ha-code-editor";
|
||||
import { ZHADevice } from "../../../../../data/zha";
|
||||
import { HomeAssistant } from "../../../../../types";
|
||||
|
||||
@customElement("zha-device-zigbee-info")
|
||||
class ZHADeviceZigbeeInfo extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@property() public device: ZHADevice | undefined;
|
||||
|
||||
@state() private _signature: any;
|
||||
|
||||
protected updated(changedProperties: PropertyValues): void {
|
||||
if (changedProperties.has("device") && this.hass && this.device) {
|
||||
this._signature = JSON.stringify(
|
||||
{
|
||||
...this.device.signature,
|
||||
manufacturer: this.device.manufacturer,
|
||||
model: this.device.model,
|
||||
class: this.device.quirk_class,
|
||||
},
|
||||
null,
|
||||
2
|
||||
);
|
||||
}
|
||||
super.updated(changedProperties);
|
||||
}
|
||||
|
||||
protected render(): TemplateResult {
|
||||
if (!this._signature) {
|
||||
return html``;
|
||||
}
|
||||
|
||||
return html`
|
||||
<ha-code-editor mode="yaml" readOnly .value=${this._signature} dir="ltr">
|
||||
</ha-code-editor>
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"zha-device-zigbee-info": ZHADeviceZigbeeInfo;
|
||||
}
|
||||
}
|
@ -1,5 +1,3 @@
|
||||
import "@material/mwc-button/mwc-button";
|
||||
import { mdiHelpCircle } from "@mdi/js";
|
||||
import {
|
||||
css,
|
||||
CSSResultGroup,
|
||||
@ -11,37 +9,29 @@ import {
|
||||
import { customElement, property, state, query } from "lit/decorators";
|
||||
import type { HASSDomEvent } from "../../../../../common/dom/fire_event";
|
||||
import { stopPropagation } from "../../../../../common/dom/stop_propagation";
|
||||
import "../../../../../components/buttons/ha-call-service-button";
|
||||
import "../../../../../components/buttons/ha-progress-button";
|
||||
import { SelectionChangedEvent } from "../../../../../components/data-table/ha-data-table";
|
||||
import "../../../../../components/ha-card";
|
||||
import "../../../../../components/ha-icon-button";
|
||||
import "../../../../../components/ha-service-description";
|
||||
import {
|
||||
bindDeviceToGroup,
|
||||
Cluster,
|
||||
fetchClustersForZhaNode,
|
||||
fetchClustersForZhaDevice,
|
||||
unbindDeviceFromGroup,
|
||||
ZHADevice,
|
||||
ZHAGroup,
|
||||
} from "../../../../../data/zha";
|
||||
import { haStyle } from "../../../../../resources/styles";
|
||||
import type { HomeAssistant } from "../../../../../types";
|
||||
import "../../../ha-config-section";
|
||||
import { ItemSelectedEvent } from "./types";
|
||||
import "./zha-clusters-data-table";
|
||||
import type { ZHAClustersDataTable } from "./zha-clusters-data-table";
|
||||
import "@material/mwc-list/mwc-list-item";
|
||||
|
||||
@customElement("zha-group-binding-control")
|
||||
export class ZHAGroupBindingControl extends LitElement {
|
||||
@property({ attribute: false }) public hass?: HomeAssistant;
|
||||
|
||||
@property() public isWide?: boolean;
|
||||
|
||||
@property() public narrow?: boolean;
|
||||
|
||||
@property() public selectedDevice?: ZHADevice;
|
||||
|
||||
@state() private _showHelp = false;
|
||||
@property() public device?: ZHADevice;
|
||||
|
||||
@state() private _bindTargetIndex = -1;
|
||||
|
||||
@ -51,6 +41,8 @@ export class ZHAGroupBindingControl extends LitElement {
|
||||
|
||||
@state() private _clusters: Cluster[] = [];
|
||||
|
||||
@state() private _bindingOperationInProgress = false;
|
||||
|
||||
private _groupToBind?: ZHAGroup;
|
||||
|
||||
private _clustersToBind?: Cluster[];
|
||||
@ -59,38 +51,17 @@ export class ZHAGroupBindingControl extends LitElement {
|
||||
private _zhaClustersDataTable!: ZHAClustersDataTable;
|
||||
|
||||
protected updated(changedProperties: PropertyValues): void {
|
||||
if (changedProperties.has("selectedDevice")) {
|
||||
if (changedProperties.has("device")) {
|
||||
this._bindTargetIndex = -1;
|
||||
this._selectedClusters = [];
|
||||
this._clustersToBind = [];
|
||||
this._fetchClustersForZhaNode();
|
||||
}
|
||||
super.update(changedProperties);
|
||||
super.updated(changedProperties);
|
||||
}
|
||||
|
||||
protected render(): TemplateResult {
|
||||
return html`
|
||||
<ha-config-section .isWide=${this.isWide}>
|
||||
<div class="sectionHeader" slot="header">
|
||||
<span
|
||||
>${this.hass!.localize(
|
||||
"ui.panel.config.zha.group_binding.header"
|
||||
)}</span
|
||||
>
|
||||
<ha-icon-button
|
||||
class="toggle-help-icon"
|
||||
@click=${this._onHelpTap}
|
||||
.path=${mdiHelpCircle}
|
||||
.label=${this.hass!.localize("ui.common.help")}
|
||||
>
|
||||
</ha-icon-button>
|
||||
</div>
|
||||
<span slot="introduction"
|
||||
>${this.hass!.localize(
|
||||
"ui.panel.config.zha.group_binding.introduction"
|
||||
)}</span
|
||||
>
|
||||
|
||||
<ha-card class="content">
|
||||
<div class="command-picker">
|
||||
<ha-select
|
||||
@ -112,66 +83,32 @@ export class ZHAGroupBindingControl extends LitElement {
|
||||
)}
|
||||
</ha-select>
|
||||
</div>
|
||||
${this._showHelp
|
||||
? html`
|
||||
<div class="helpText">
|
||||
${this.hass!.localize(
|
||||
"ui.panel.config.zha.group_binding.group_picker_help"
|
||||
)}
|
||||
</div>
|
||||
`
|
||||
: ""}
|
||||
<div class="command-picker">
|
||||
<zha-clusters-data-table
|
||||
.hass=${this.hass}
|
||||
.narrow=${this.narrow}
|
||||
.clusters=${this._clusters}
|
||||
@selection-changed=${this._handleClusterSelectionChanged}
|
||||
class="menu"
|
||||
></zha-clusters-data-table>
|
||||
</div>
|
||||
${this._showHelp
|
||||
? html`
|
||||
<div class="helpText">
|
||||
${this.hass!.localize(
|
||||
"ui.panel.config.zha.group_binding.cluster_selection_help"
|
||||
)}
|
||||
</div>
|
||||
`
|
||||
: ""}
|
||||
<div class="card-actions">
|
||||
<mwc-button
|
||||
@click=${this._onBindGroupClick}
|
||||
.disabled=${!this._canBind}
|
||||
>${this.hass!.localize(
|
||||
"ui.panel.config.zha.group_binding.bind_button_label"
|
||||
)}</mwc-button
|
||||
>
|
||||
${this._showHelp
|
||||
? html`
|
||||
<div class="helpText">
|
||||
${this.hass!.localize(
|
||||
"ui.panel.config.zha.group_binding.bind_button_help"
|
||||
)}
|
||||
</div>
|
||||
`
|
||||
: ""}
|
||||
<mwc-button
|
||||
@click=${this._onUnbindGroupClick}
|
||||
.disabled=${!this._canBind}
|
||||
>${this.hass!.localize(
|
||||
"ui.panel.config.zha.group_binding.unbind_button_label"
|
||||
)}</mwc-button
|
||||
>
|
||||
${this._showHelp
|
||||
? html`
|
||||
<div class="helpText">
|
||||
${this.hass!.localize(
|
||||
"ui.panel.config.zha.group_binding.unbind_button_help"
|
||||
)}
|
||||
</div>
|
||||
`
|
||||
: ""}
|
||||
<ha-progress-button
|
||||
@click=${this._onBindGroupClick}
|
||||
.disabled=${!this._canBind || this._bindingOperationInProgress}
|
||||
>
|
||||
${this.hass!.localize(
|
||||
"ui.panel.config.zha.group_binding.bind_button_label"
|
||||
)}
|
||||
</ha-progress-button>
|
||||
|
||||
<ha-progress-button
|
||||
@click=${this._onUnbindGroupClick}
|
||||
.disabled=${!this._canBind || this._bindingOperationInProgress}
|
||||
>
|
||||
${this.hass!.localize(
|
||||
"ui.panel.config.zha.group_binding.unbind_button_label"
|
||||
)}
|
||||
</ha-progress-button>
|
||||
</div>
|
||||
</ha-card>
|
||||
</ha-config-section>
|
||||
@ -186,31 +123,49 @@ export class ZHAGroupBindingControl extends LitElement {
|
||||
: this.groups[this._bindTargetIndex];
|
||||
}
|
||||
|
||||
private _onHelpTap(): void {
|
||||
this._showHelp = !this._showHelp;
|
||||
}
|
||||
|
||||
private async _onBindGroupClick(): Promise<void> {
|
||||
private async _onBindGroupClick(ev: CustomEvent): Promise<void> {
|
||||
const button = ev.currentTarget as any;
|
||||
if (this.hass && this._canBind) {
|
||||
await bindDeviceToGroup(
|
||||
this.hass,
|
||||
this.selectedDevice!.ieee,
|
||||
this._groupToBind!.group_id,
|
||||
this._clustersToBind!
|
||||
);
|
||||
this._zhaClustersDataTable.clearSelection();
|
||||
this._bindingOperationInProgress = true;
|
||||
button.progress = true;
|
||||
try {
|
||||
await bindDeviceToGroup(
|
||||
this.hass,
|
||||
this.device!.ieee,
|
||||
this._groupToBind!.group_id,
|
||||
this._clustersToBind!
|
||||
);
|
||||
this._zhaClustersDataTable.clearSelection();
|
||||
button.actionSuccess();
|
||||
} catch (err: any) {
|
||||
button.actionError();
|
||||
} finally {
|
||||
this._bindingOperationInProgress = false;
|
||||
button.progress = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async _onUnbindGroupClick(): Promise<void> {
|
||||
private async _onUnbindGroupClick(ev: CustomEvent): Promise<void> {
|
||||
const button = ev.currentTarget as any;
|
||||
if (this.hass && this._canBind) {
|
||||
await unbindDeviceFromGroup(
|
||||
this.hass,
|
||||
this.selectedDevice!.ieee,
|
||||
this._groupToBind!.group_id,
|
||||
this._clustersToBind!
|
||||
);
|
||||
this._zhaClustersDataTable.clearSelection();
|
||||
this._bindingOperationInProgress = true;
|
||||
button.progress = true;
|
||||
try {
|
||||
await unbindDeviceFromGroup(
|
||||
this.hass,
|
||||
this.device!.ieee,
|
||||
this._groupToBind!.group_id,
|
||||
this._clustersToBind!
|
||||
);
|
||||
this._zhaClustersDataTable.clearSelection();
|
||||
button.actionSuccess();
|
||||
} catch (err: any) {
|
||||
button.actionError();
|
||||
} finally {
|
||||
this._bindingOperationInProgress = false;
|
||||
button.progress = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -230,9 +185,9 @@ export class ZHAGroupBindingControl extends LitElement {
|
||||
|
||||
private async _fetchClustersForZhaNode(): Promise<void> {
|
||||
if (this.hass) {
|
||||
this._clusters = await fetchClustersForZhaNode(
|
||||
this._clusters = await fetchClustersForZhaDevice(
|
||||
this.hass,
|
||||
this.selectedDevice!.ieee
|
||||
this.device!.ieee
|
||||
);
|
||||
this._clusters = this._clusters
|
||||
.filter((cluster) => cluster.type.toLowerCase() === "out")
|
||||
@ -245,7 +200,7 @@ export class ZHAGroupBindingControl extends LitElement {
|
||||
this._groupToBind &&
|
||||
this._clustersToBind &&
|
||||
this._clustersToBind?.length > 0 &&
|
||||
this.selectedDevice
|
||||
this.device
|
||||
);
|
||||
}
|
||||
|
||||
@ -257,18 +212,6 @@ export class ZHAGroupBindingControl extends LitElement {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.content {
|
||||
margin-top: 24px;
|
||||
}
|
||||
|
||||
ha-card {
|
||||
max-width: 680px;
|
||||
}
|
||||
|
||||
.card-actions.warning ha-call-service-button {
|
||||
color: var(--error-color);
|
||||
}
|
||||
|
||||
.command-picker {
|
||||
align-items: center;
|
||||
padding-left: 28px;
|
||||
@ -285,30 +228,6 @@ export class ZHAGroupBindingControl extends LitElement {
|
||||
.sectionHeader {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.helpText {
|
||||
color: grey;
|
||||
padding-left: 28px;
|
||||
padding-right: 28px;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
.toggle-help-icon {
|
||||
float: right;
|
||||
top: -6px;
|
||||
right: 0;
|
||||
padding-right: 0px;
|
||||
color: var(--primary-color);
|
||||
}
|
||||
|
||||
ha-service-description {
|
||||
display: block;
|
||||
color: grey;
|
||||
}
|
||||
|
||||
[hidden] {
|
||||
display: none;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
|
@ -0,0 +1,198 @@
|
||||
import "@material/mwc-list/mwc-list-item";
|
||||
import {
|
||||
css,
|
||||
CSSResultGroup,
|
||||
html,
|
||||
LitElement,
|
||||
PropertyValues,
|
||||
TemplateResult,
|
||||
} from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { cache } from "lit/directives/cache";
|
||||
import { stopPropagation } from "../../../../../common/dom/stop_propagation";
|
||||
import "../../../../../components/ha-card";
|
||||
import "../../../../../components/ha-select";
|
||||
import {
|
||||
Cluster,
|
||||
fetchClustersForZhaDevice,
|
||||
ZHADevice,
|
||||
} from "../../../../../data/zha";
|
||||
import { haStyle } from "../../../../../resources/styles";
|
||||
import { HomeAssistant } from "../../../../../types";
|
||||
import { computeClusterKey } from "./functions";
|
||||
import "@material/mwc-tab-bar/mwc-tab-bar";
|
||||
import "@material/mwc-tab/mwc-tab";
|
||||
import "./zha-cluster-attributes";
|
||||
import "./zha-cluster-commands";
|
||||
|
||||
declare global {
|
||||
// for fire event
|
||||
interface HASSDomEvents {
|
||||
"zha-cluster-selected": {
|
||||
cluster?: Cluster;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
const tabs = ["attributes", "commands"] as const;
|
||||
|
||||
@customElement("zha-manage-clusters")
|
||||
export class ZHAManageClusters extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@property() public isWide?: boolean;
|
||||
|
||||
@property() public device?: ZHADevice;
|
||||
|
||||
@state() private _selectedClusterIndex = -1;
|
||||
|
||||
@state() private _clusters: Cluster[] = [];
|
||||
|
||||
@state() private _selectedCluster?: Cluster;
|
||||
|
||||
@state() private _currTab: typeof tabs[number] = "attributes";
|
||||
|
||||
@state() private _clustersLoaded = false;
|
||||
|
||||
protected willUpdate(changedProps: PropertyValues) {
|
||||
super.willUpdate(changedProps);
|
||||
if (!this.device) {
|
||||
return;
|
||||
}
|
||||
if (!tabs.includes(this._currTab)) {
|
||||
this._currTab = tabs[0];
|
||||
}
|
||||
}
|
||||
|
||||
protected updated(changedProperties: PropertyValues): void {
|
||||
if (changedProperties.has("device")) {
|
||||
this._clusters = [];
|
||||
this._selectedClusterIndex = -1;
|
||||
this._clustersLoaded = false;
|
||||
this._fetchClustersForZhaDevice();
|
||||
}
|
||||
super.updated(changedProperties);
|
||||
}
|
||||
|
||||
protected render(): TemplateResult {
|
||||
if (!this.device || !this._clustersLoaded) {
|
||||
return html``;
|
||||
}
|
||||
return html`
|
||||
<ha-card class="content">
|
||||
<div class="node-picker">
|
||||
<ha-select
|
||||
.label=${this.hass!.localize("ui.panel.config.zha.common.clusters")}
|
||||
class="menu"
|
||||
.value=${String(this._selectedClusterIndex)}
|
||||
@selected=${this._selectedClusterChanged}
|
||||
@closed=${stopPropagation}
|
||||
fixedMenuPosition
|
||||
naturalMenuWidth
|
||||
>
|
||||
${this._clusters.map(
|
||||
(entry, idx) => html`
|
||||
<mwc-list-item .value=${String(idx)}
|
||||
>${computeClusterKey(entry)}</mwc-list-item
|
||||
>
|
||||
`
|
||||
)}
|
||||
</ha-select>
|
||||
</div>
|
||||
${this._selectedCluster
|
||||
? html`
|
||||
<mwc-tab-bar
|
||||
.activeIndex=${tabs.indexOf(this._currTab)}
|
||||
@MDCTabBar:activated=${this._handleTabChanged}
|
||||
>
|
||||
${tabs.map(
|
||||
(tab) => html`
|
||||
<mwc-tab
|
||||
.label=${this.hass.localize(
|
||||
`ui.panel.config.zha.clusters.tabs.${tab}`
|
||||
)}
|
||||
></mwc-tab>
|
||||
`
|
||||
)}
|
||||
</mwc-tab-bar>
|
||||
|
||||
<div class="content" tabindex="-1" dialogInitialFocus>
|
||||
${cache(
|
||||
this._currTab === "attributes"
|
||||
? html`
|
||||
<zha-cluster-attributes
|
||||
.hass=${this.hass}
|
||||
.device=${this.device}
|
||||
.selectedCluster=${this._selectedCluster}
|
||||
></zha-cluster-attributes>
|
||||
`
|
||||
: html`
|
||||
<zha-cluster-commands
|
||||
.hass=${this.hass}
|
||||
.device=${this.device}
|
||||
.selectedCluster=${this._selectedCluster}
|
||||
></zha-cluster-commands>
|
||||
`
|
||||
)}
|
||||
</div>
|
||||
`
|
||||
: ""}
|
||||
</ha-card>
|
||||
`;
|
||||
}
|
||||
|
||||
private async _fetchClustersForZhaDevice(): Promise<void> {
|
||||
if (this.hass) {
|
||||
this._clusters = await fetchClustersForZhaDevice(
|
||||
this.hass,
|
||||
this.device!.ieee
|
||||
);
|
||||
this._clusters.sort((a, b) => a.name.localeCompare(b.name));
|
||||
if (this._clusters.length > 0) {
|
||||
this._selectedClusterIndex = 0;
|
||||
this._selectedCluster = this._clusters[0];
|
||||
}
|
||||
this._clustersLoaded = true;
|
||||
}
|
||||
}
|
||||
|
||||
private _handleTabChanged(ev: CustomEvent): void {
|
||||
const newTab = tabs[ev.detail.index];
|
||||
if (newTab === this._currTab) {
|
||||
return;
|
||||
}
|
||||
this._currTab = newTab;
|
||||
}
|
||||
|
||||
private _selectedClusterChanged(event): void {
|
||||
this._selectedClusterIndex = Number(event.target!.value);
|
||||
this._selectedCluster = this._clusters[this._selectedClusterIndex];
|
||||
}
|
||||
|
||||
static get styles(): CSSResultGroup {
|
||||
return [
|
||||
haStyle,
|
||||
css`
|
||||
ha-select {
|
||||
margin-top: 16px;
|
||||
}
|
||||
.menu {
|
||||
width: 100%;
|
||||
}
|
||||
.header {
|
||||
flex-grow: 1;
|
||||
}
|
||||
.node-picker {
|
||||
align-items: center;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"zha-manage-clusters": ZHAManageClusters;
|
||||
}
|
||||
}
|
@ -1002,6 +1002,15 @@
|
||||
"attribute": "Attribute",
|
||||
"min_max_change": "min/max/change"
|
||||
},
|
||||
"zha_manage_device": {
|
||||
"heading": "Manage Zigbee Device",
|
||||
"tabs": {
|
||||
"clusters": "Clusters",
|
||||
"bindings": "Bindings",
|
||||
"signature": "Signature",
|
||||
"children": "Children"
|
||||
}
|
||||
},
|
||||
"zha_device_info": {
|
||||
"manuf": "by {manufacturer}",
|
||||
"no_area": "No Area",
|
||||
@ -1010,10 +1019,8 @@
|
||||
"buttons": {
|
||||
"add": "Add devices via this device",
|
||||
"remove": "Remove",
|
||||
"clusters": "Manage clusters",
|
||||
"manage": "Manage zigbee device",
|
||||
"reconfigure": "Reconfigure",
|
||||
"zigbee_information": "Zigbee signature",
|
||||
"device_children": "View children",
|
||||
"view_network": "View network"
|
||||
},
|
||||
"services": {
|
||||
@ -3078,17 +3085,17 @@
|
||||
"clusters": {
|
||||
"header": "Clusters",
|
||||
"help_cluster_dropdown": "Select a cluster to view attributes and commands.",
|
||||
"introduction": "Clusters are the building blocks for Zigbee functionality. They separate functionality into logical units. There are client and server types and that are comprised of attributes and commands."
|
||||
"tabs": {
|
||||
"attributes": "Attributes",
|
||||
"commands": "Commands"
|
||||
}
|
||||
},
|
||||
"cluster_attributes": {
|
||||
"header": "Cluster Attributes",
|
||||
"introduction": "View and edit cluster attributes.",
|
||||
"attributes_of_cluster": "Attributes of the selected cluster",
|
||||
"get_zigbee_attribute": "Get Zigbee Attribute",
|
||||
"set_zigbee_attribute": "Set Zigbee Attribute",
|
||||
"help_attribute_dropdown": "Select an attribute to view or set its value.",
|
||||
"help_get_zigbee_attribute": "Get the value for the selected attribute.",
|
||||
"help_set_zigbee_attribute": "Set attribute value for the specified cluster on the specified entity."
|
||||
"read_zigbee_attribute": "Read Attribute",
|
||||
"write_zigbee_attribute": "Write Attribute"
|
||||
},
|
||||
"cluster_commands": {
|
||||
"header": "Cluster Commands",
|
||||
@ -3138,6 +3145,11 @@
|
||||
"enable_physics": "Enable Physics",
|
||||
"refresh_topology": "Refresh Topology"
|
||||
},
|
||||
"device_binding": {
|
||||
"bind": "Bind",
|
||||
"unbind": "Unbind",
|
||||
"picker_label": "Bindable Devices"
|
||||
},
|
||||
"group_binding": {
|
||||
"header": "Group Binding",
|
||||
"introduction": "Bind and unbind groups.",
|
||||
|
Loading…
x
Reference in New Issue
Block a user