diff --git a/src/data/zha.ts b/src/data/zha.ts index 4ae16c680a..2f60c313e4 100644 --- a/src/data/zha.ts +++ b/src/data/zha.ts @@ -51,6 +51,12 @@ export interface ReadAttributeServiceData { manufacturer?: number; } +export interface ZHAGroup { + name: string; + group_id: number; + members: ZHADevice[]; +} + export const reconfigureNode = ( hass: HomeAssistant, ieeeAddress: string @@ -153,3 +159,8 @@ export const fetchClustersForZhaNode = ( type: "zha/devices/clusters", ieee: ieeeAddress, }); + +export const fetchGroups = (hass: HomeAssistant): Promise => + hass.callWS({ + type: "zha/groups", + }); diff --git a/src/panels/config/zha/functions.ts b/src/panels/config/zha/functions.ts index 72ce4d8bdf..1e7a2858de 100644 --- a/src/panels/config/zha/functions.ts +++ b/src/panels/config/zha/functions.ts @@ -1,4 +1,4 @@ -import { ZHADevice } from "../../../data/zha"; +import { ZHADevice, ZHAGroup } from "../../../data/zha"; export const formatAsPaddedHex = (value: string | number): string => { let hex = value; @@ -13,3 +13,9 @@ export const sortZHADevices = (a: ZHADevice, b: ZHADevice): number => { const nameb = b.user_given_name ? b.user_given_name : b.name; return nameA.localeCompare(nameb); }; + +export const sortZHAGroups = (a: ZHAGroup, b: ZHAGroup): number => { + const nameA = a.name; + const nameb = b.name; + return nameA.localeCompare(nameb); +}; diff --git a/src/panels/config/zha/ha-config-zha.ts b/src/panels/config/zha/ha-config-zha.ts index 47db6ae3df..7ea5d19769 100755 --- a/src/panels/config/zha/ha-config-zha.ts +++ b/src/panels/config/zha/ha-config-zha.ts @@ -5,6 +5,7 @@ import "./zha-cluster-attributes"; import "./zha-cluster-commands"; import "./zha-network"; import "./zha-node"; +import "./zha-groups-tile"; import "@polymer/paper-icon-button/paper-icon-button"; import { @@ -45,6 +46,11 @@ export class HaConfigZha extends LitElement { .hass="${this.hass}" > + + + import( + /* webpackChunkName: "zha-groups-dashboard" */ "./zha-groups-dashboard" + ), + }, }, }; @@ -39,6 +47,7 @@ class ZHAConfigPanel extends HassRouterPage { el.route = this.routeTail; el.hass = this.hass; el.isWide = this.isWide; + el.narrow = this.narrow; } } diff --git a/src/panels/config/zha/zha-groups-dashboard.ts b/src/panels/config/zha/zha-groups-dashboard.ts new file mode 100644 index 0000000000..d9cde8cbd4 --- /dev/null +++ b/src/panels/config/zha/zha-groups-dashboard.ts @@ -0,0 +1,77 @@ +import "../../../layouts/hass-subpage"; +import "./zha-groups-data-table"; + +import { + LitElement, + html, + TemplateResult, + property, + customElement, + CSSResult, + css, +} from "lit-element"; +import { HomeAssistant } from "../../../types"; +import { ZHAGroup, fetchGroups } from "../../../data/zha"; +import { sortZHAGroups } from "./functions"; + +@customElement("zha-groups-dashboard") +export class ZHAGroupsDashboard extends LitElement { + @property() public hass!: HomeAssistant; + @property() public narrow = false; + @property() public _groups!: ZHAGroup[]; + + public connectedCallback(): void { + super.connectedCallback(); + this._fetchGroups(); + } + + protected render(): TemplateResult { + return html` + +
+ +
+
+ `; + } + + private async _fetchGroups() { + this._groups = (await fetchGroups(this.hass!)).sort(sortZHAGroups); + } + + static get styles(): CSSResult[] { + return [ + css` + .content { + padding: 4px; + } + zha-groups-data-table { + width: 100%; + } + .button { + float: right; + } + + .table { + height: 200px; + overflow: auto; + } + `, + ]; + } +} + +declare global { + interface HTMLElementTagNameMap { + "zha-groups-dashboard": ZHAGroupsDashboard; + } +} diff --git a/src/panels/config/zha/zha-groups-data-table.ts b/src/panels/config/zha/zha-groups-data-table.ts new file mode 100644 index 0000000000..0f9e6dc6cd --- /dev/null +++ b/src/panels/config/zha/zha-groups-data-table.ts @@ -0,0 +1,85 @@ +import "../../../components/data-table/ha-data-table"; +import "../../../components/entity/ha-state-icon"; + +import memoizeOne from "memoize-one"; + +import { + LitElement, + html, + TemplateResult, + property, + customElement, +} from "lit-element"; +import { HomeAssistant } from "../../../types"; +// tslint:disable-next-line +import { DataTableColumnContainer } from "../../../components/data-table/ha-data-table"; +// tslint:disable-next-line +import { ZHAGroup, ZHADevice } from "../../../data/zha"; +import { formatAsPaddedHex } from "./functions"; + +export interface GroupRowData extends ZHAGroup { + group?: GroupRowData; + id?: number; +} + +@customElement("zha-groups-data-table") +export class ZHAGroupsDataTable extends LitElement { + @property() public hass!: HomeAssistant; + @property() public narrow = false; + @property() public groups: ZHAGroup[] = []; + + private _columns = memoizeOne( + (narrow: boolean): DataTableColumnContainer => + narrow + ? { + name: { + title: "Group", + sortable: true, + filterable: true, + direction: "asc", + }, + } + : { + name: { + title: this.hass.localize("ui.panel.config.zha.groups.groups"), + sortable: true, + filterable: true, + direction: "asc", + }, + group_id: { + title: this.hass.localize("ui.panel.config.zha.groups.group_id"), + template: (groupId: number) => { + return html` + ${formatAsPaddedHex(groupId)} + `; + }, + sortable: true, + }, + members: { + title: this.hass.localize("ui.panel.config.zha.groups.members"), + template: (members: ZHADevice[]) => { + return html` + ${members.length} + `; + }, + sortable: true, + }, + } + ); + + protected render(): TemplateResult { + return html` + + `; + } +} + +declare global { + interface HTMLElementTagNameMap { + "zha-groups-data-table": ZHAGroupsDataTable; + } +} diff --git a/src/panels/config/zha/zha-groups-tile.ts b/src/panels/config/zha/zha-groups-tile.ts new file mode 100644 index 0000000000..e722981461 --- /dev/null +++ b/src/panels/config/zha/zha-groups-tile.ts @@ -0,0 +1,108 @@ +import "../../../components/ha-card"; +import "../ha-config-section"; +import "@material/mwc-button"; +import "@polymer/paper-icon-button/paper-icon-button"; + +import { + css, + CSSResult, + html, + LitElement, + TemplateResult, + property, + customElement, +} from "lit-element"; + +import { navigate } from "../../../common/navigate"; +import { haStyle } from "../../../resources/styles"; +import { HomeAssistant } from "../../../types"; + +@customElement("zha-groups-tile") +export class ZHAGroupsTile extends LitElement { + @property() public hass?: HomeAssistant; + @property() public isWide?: boolean; + @property() private _showHelp = false; + + protected render(): TemplateResult | void { + return html` + +
+ + ${this.hass!.localize("ui.panel.config.zha.groups.header")} + + +
+ + ${this.hass!.localize("ui.panel.config.zha.groups.introduction")} + + + +
+ + ${this.hass!.localize("ui.panel.config.zha.groups.manage_groups")} + +
+
+
+ `; + } + + private _onHelpTap(): void { + this._showHelp = !this._showHelp; + } + + private _onManageGroupsClick() { + navigate(this, "groups"); + } + + static get styles(): CSSResult[] { + return [ + haStyle, + css` + .content { + margin-top: 24px; + } + + ha-card { + margin: 0 auto; + max-width: 600px; + } + + .card-actions.warning ha-call-service-button { + color: var(--google-red-500); + } + + .toggle-help-icon { + position: absolute; + top: -6px; + right: 0; + color: var(--primary-color); + } + + ha-service-description { + display: block; + color: grey; + } + + [hidden] { + display: none; + } + + .help-text2 { + color: grey; + padding: 16px; + } + `, + ]; + } +} + +declare global { + interface HTMLElementTagNameMap { + "zha-groups-tile": ZHAGroupsTile; + } +} diff --git a/src/translations/en.json b/src/translations/en.json index 0641d1e52f..9afee83ad5 100755 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -1437,6 +1437,15 @@ "commands_of_cluster": "Commands of the selected cluster", "issue_zigbee_command": "Issue Zigbee Command", "help_command_dropdown": "Select a command to interact with." + }, + "groups": { + "zha_zigbee_groups": "ZHA Zigbee Groups", + "manage_groups": "Manage Zigbee Groups", + "groups": "Groups", + "group_id": "Group ID", + "members": "Members", + "header": "Zigbee Group Management", + "introduction": "Create and modify zigbee groups" } }, "zwave": {