Move integration config panels to integrations (#6122)

This commit is contained in:
Bram Kragten 2020-06-12 11:51:00 +02:00 committed by GitHub
parent ccc9b73f9b
commit 4eb46bc275
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
52 changed files with 1203 additions and 1911 deletions

View File

@ -6,7 +6,7 @@ export const isValidEntityId = (entityId: string) =>
export const createValidEntityId = (input: string) =>
input
.toLowerCase()
.replace(/\s|'/g, "_") // replace spaces and quotes with underscore
.replace(/\s|'|\./g, "_") // replace spaces, points and quotes with underscore
.replace(/\W/g, "") // remove not allowed chars
.replace(/_{2,}/g, "_") // replace multiple underscores with 1
.replace(/_$/, ""); // remove underscores at the end

View File

@ -619,6 +619,11 @@ export class HaDataTable extends LitElement {
text-transform: inherit;
}
.mdc-data-table__cell a {
color: inherit;
text-decoration: none;
}
.mdc-data-table__cell--numeric {
text-align: right;
}

View File

@ -1,46 +0,0 @@
/*
Wrapper for paper-textarea.
paper-textarea crashes on iOS when created programmatically. This only impacts
our automation and script editors as they are using Preact. Polymer is using
template elements and does not have this issue.
paper-textarea issue: https://github.com/PolymerElements/paper-input/issues/556
WebKit issue: https://bugs.webkit.org/show_bug.cgi?id=174629
*/
import "@polymer/paper-input/paper-textarea";
import { html } from "@polymer/polymer/lib/utils/html-tag";
/* eslint-plugin-disable lit */
import { PolymerElement } from "@polymer/polymer/polymer-element";
class HaTextarea extends PolymerElement {
static get template() {
return html`
<style>
:host {
display: block;
}
</style>
<paper-textarea
label="[[label]]"
placeholder="[[placeholder]]"
value="{{value}}"
></paper-textarea>
`;
}
static get properties() {
return {
name: String,
label: String,
placeholder: String,
value: {
type: String,
notify: true,
},
};
}
}
customElements.define("ha-textarea", HaTextarea);

View File

@ -1,113 +0,0 @@
import "@polymer/paper-dialog-scrollable/paper-dialog-scrollable";
import {
css,
CSSResult,
customElement,
html,
LitElement,
property,
TemplateResult,
} from "lit-element";
import "../../components/dialog/ha-paper-dialog";
import type { HaPaperDialog } from "../../components/dialog/ha-paper-dialog";
import { fetchZHADevice, ZHADevice } from "../../data/zha";
import "../../panels/config/zha/zha-device-card";
import type { PolymerChangedEvent } from "../../polymer-types";
import { haStyleDialog } from "../../resources/styles";
import type { HomeAssistant } from "../../types";
import type { ZHADeviceInfoDialogParams } from "./show-dialog-zha-device-info";
@customElement("dialog-zha-device-info")
class DialogZHADeviceInfo extends LitElement {
@property() public hass!: HomeAssistant;
@property() private _params?: ZHADeviceInfoDialogParams;
@property() private _error?: string;
@property() private _device?: ZHADevice;
public async showDialog(params: ZHADeviceInfoDialogParams): Promise<void> {
this._params = params;
this._device = await fetchZHADevice(this.hass, params.ieee);
await this.updateComplete;
this._dialog.open();
}
protected render(): TemplateResult {
if (!this._params || !this._device) {
return html``;
}
return html`
<ha-paper-dialog
with-backdrop
opened
@opened-changed=${this._openedChanged}
>
${this._error
? html` <div class="error">${this._error}</div> `
: html`
<zha-device-card
class="card"
.hass=${this.hass}
.device=${this._device}
@zha-device-removed=${this._onDeviceRemoved}
.showEntityDetail=${false}
.showActions="${this._device.device_type !== "Coordinator"}"
></zha-device-card>
`}
</ha-paper-dialog>
`;
}
private _openedChanged(ev: PolymerChangedEvent<boolean>): void {
if (!ev.detail.value) {
this._params = undefined;
this._error = undefined;
this._device = undefined;
}
}
private _onDeviceRemoved(): void {
this._closeDialog();
}
private get _dialog(): HaPaperDialog {
return this.shadowRoot!.querySelector("ha-paper-dialog")!;
}
private _closeDialog() {
this._dialog.close();
}
static get styles(): CSSResult[] {
return [
haStyleDialog,
css`
ha-paper-dialog > * {
margin: 0;
display: block;
padding: 0;
}
.card {
box-sizing: border-box;
display: flex;
flex: 1 0 300px;
min-width: 0;
max-width: 600px;
word-wrap: break-word;
}
.error {
color: var(--google-red-500);
}
`,
];
}
}
declare global {
interface HTMLElementTagNameMap {
"dialog-zha-device-info": DialogZHADeviceInfo;
}
}

View File

@ -1,21 +0,0 @@
import { fireEvent } from "../../common/dom/fire_event";
export interface ZHADeviceInfoDialogParams {
ieee: string;
}
export const loadZHADeviceInfoDialog = () =>
import(
/* webpackChunkName: "dialog-zha-device-info" */ "./dialog-zha-device-info"
);
export const showZHADeviceInfoDialog = (
element: HTMLElement,
zhaDeviceInfoParams: ZHADeviceInfoDialogParams
): void => {
fireEvent(element, "show-dialog", {
dialogTag: "dialog-zha-device-info",
dialogImport: loadZHADeviceInfoDialog,
dialogParams: zhaDeviceInfoParams,
});
};

View File

@ -1,4 +1,5 @@
import "@polymer/paper-input/paper-input";
import "@polymer/paper-input/paper-textarea";
import { customElement, LitElement, property } from "lit-element";
import { html } from "lit-html";
import { WaitAction } from "../../../../../data/script";
@ -19,7 +20,7 @@ export class HaWaitAction extends LitElement implements ActionElement {
const { wait_template, timeout } = this.action;
return html`
<ha-textarea
<paper-textarea
.label=${this.hass.localize(
"ui.panel.config.automation.editor.actions.type.wait_template.wait_template"
)}
@ -27,7 +28,7 @@ export class HaWaitAction extends LitElement implements ActionElement {
.value=${wait_template}
@value-changed=${this._valueChanged}
dir="ltr"
></ha-textarea>
></paper-textarea>
<paper-input
.label=${this.hass.localize(
"ui.panel.config.automation.editor.actions.type.wait_template.timeout"

View File

@ -2,7 +2,7 @@ import "@polymer/paper-input/paper-input";
import { customElement, html, LitElement, property } from "lit-element";
import { fireEvent } from "../../../../../common/dom/fire_event";
import "../../../../../components/entity/ha-entity-picker";
import "../../../../../components/ha-textarea";
import "@polymer/paper-input/paper-textarea";
import { NumericStateCondition } from "../../../../../data/automation";
import { HomeAssistant } from "../../../../../types";
import { handleChangeEvent } from "../ha-automation-condition-row";
@ -45,7 +45,7 @@ export default class HaNumericStateCondition extends LitElement {
.value=${below}
@value-changed=${this._valueChanged}
></paper-input>
<ha-textarea
<paper-textarea
.label=${this.hass.localize(
"ui.panel.config.automation.editor.conditions.type.numeric_state.value_template"
)}
@ -53,7 +53,7 @@ export default class HaNumericStateCondition extends LitElement {
.value=${value_template}
@value-changed=${this._valueChanged}
dir="ltr"
></ha-textarea>
></paper-textarea>
`;
}

View File

@ -1,5 +1,5 @@
import { customElement, html, LitElement, property } from "lit-element";
import "../../../../../components/ha-textarea";
import "@polymer/paper-input/paper-textarea";
import { TemplateCondition } from "../../../../../data/automation";
import { HomeAssistant } from "../../../../../types";
import { handleChangeEvent } from "../ha-automation-condition-row";
@ -17,7 +17,7 @@ export class HaTemplateCondition extends LitElement {
protected render() {
const { value_template } = this.condition;
return html`
<ha-textarea
<paper-textarea
.label=${this.hass.localize(
"ui.panel.config.automation.editor.conditions.type.template.value_template"
)}
@ -25,7 +25,7 @@ export class HaTemplateCondition extends LitElement {
.value=${value_template}
@value-changed=${this._valueChanged}
dir="ltr"
></ha-textarea>
></paper-textarea>
`;
}

View File

@ -1,5 +1,6 @@
import "@polymer/app-layout/app-header/app-header";
import "@polymer/app-layout/app-toolbar/app-toolbar";
import "@polymer/paper-input/paper-textarea";
import "../../../components/ha-icon-button";
import {
css,
@ -117,7 +118,7 @@ export class HaAutomationEditor extends LitElement {
@value-changed=${this._valueChanged}
>
</paper-input>
<ha-textarea
<paper-textarea
.label=${this.hass.localize(
"ui.panel.config.automation.editor.description.label"
)}
@ -127,7 +128,7 @@ export class HaAutomationEditor extends LitElement {
name="description"
.value=${this._config.description}
@value-changed=${this._valueChanged}
></ha-textarea>
></paper-textarea>
</div>
${stateObj
? html`

View File

@ -2,7 +2,7 @@ import "@polymer/paper-input/paper-input";
import { customElement, html, LitElement, property } from "lit-element";
import { fireEvent } from "../../../../../common/dom/fire_event";
import "../../../../../components/entity/ha-entity-picker";
import "../../../../../components/ha-textarea";
import "@polymer/paper-input/paper-textarea";
import { ForDict, NumericStateTrigger } from "../../../../../data/automation";
import { HomeAssistant } from "../../../../../types";
import { handleChangeEvent } from "../ha-automation-trigger-row";
@ -61,7 +61,7 @@ export default class HaNumericStateTrigger extends LitElement {
.value=${below}
@value-changed=${this._valueChanged}
></paper-input>
<ha-textarea
<paper-textarea
.label=${this.hass.localize(
"ui.panel.config.automation.editor.triggers.type.numeric_state.value_template"
)}
@ -69,7 +69,7 @@ export default class HaNumericStateTrigger extends LitElement {
.value=${value_template}
@value-changed=${this._valueChanged}
dir="ltr"
></ha-textarea>
></paper-textarea>
<paper-input
.label=${this.hass.localize(
"ui.panel.config.automation.editor.triggers.type.state.for"

View File

@ -1,5 +1,5 @@
import { customElement, html, LitElement, property } from "lit-element";
import "../../../../../components/ha-textarea";
import "@polymer/paper-input/paper-textarea";
import { TemplateTrigger } from "../../../../../data/automation";
import { HomeAssistant } from "../../../../../types";
import { handleChangeEvent } from "../ha-automation-trigger-row";
@ -17,7 +17,7 @@ export class HaTemplateTrigger extends LitElement {
protected render() {
const { value_template } = this.trigger;
return html`
<ha-textarea
<paper-textarea
.label=${this.hass.localize(
"ui.panel.config.automation.editor.triggers.type.template.value_template"
)}
@ -25,7 +25,7 @@ export class HaTemplateTrigger extends LitElement {
.value=${value_template}
@value-changed=${this._valueChanged}
dir="ltr"
></ha-textarea>
></paper-textarea>
`;
}

View File

@ -75,6 +75,7 @@ export class HaDeviceCard extends LitElement {
: ""}
<slot></slot>
</div>
<slot name="actions"></slot>
</ha-card>
`;
}
@ -100,7 +101,6 @@ export class HaDeviceCard extends LitElement {
}
ha-card {
flex: 1 0 100%;
padding-bottom: 10px;
min-width: 0;
}
.device {

View File

@ -5,16 +5,17 @@ import {
LitElement,
property,
TemplateResult,
css,
} from "lit-element";
import { DeviceRegistryEntry } from "../../../../data/device_registry";
import { removeMQTTDeviceEntry } from "../../../../data/mqtt";
import { showConfirmationDialog } from "../../../../dialogs/generic/show-dialog-box";
import { showMQTTDeviceDebugInfoDialog } from "../../../../dialogs/mqtt-device-debug-info-dialog/show-dialog-mqtt-device-debug-info";
import { haStyle } from "../../../../resources/styles";
import { HomeAssistant } from "../../../../types";
import { DeviceRegistryEntry } from "../../../../../data/device_registry";
import { removeMQTTDeviceEntry } from "../../../../../data/mqtt";
import { showConfirmationDialog } from "../../../../../dialogs/generic/show-dialog-box";
import { showMQTTDeviceDebugInfoDialog } from "../../../../../dialogs/mqtt-device-debug-info-dialog/show-dialog-mqtt-device-debug-info";
import { haStyle } from "../../../../../resources/styles";
import { HomeAssistant } from "../../../../../types";
@customElement("ha-device-card-mqtt")
export class HaDeviceCardMqtt extends LitElement {
@customElement("ha-device-actions-mqtt")
export class HaDeviceActionsMqtt extends LitElement {
@property() public hass!: HomeAssistant;
@property() public device!: DeviceRegistryEntry;
@ -47,7 +48,15 @@ export class HaDeviceCardMqtt extends LitElement {
await showMQTTDeviceDebugInfoDialog(this, { device });
}
static get styles(): CSSResult {
return haStyle;
static get styles(): CSSResult[] {
return [
haStyle,
css`
:host {
display: flex;
justify-content: space-between;
}
`,
];
}
}

View File

@ -0,0 +1,128 @@
import {
CSSResult,
customElement,
html,
LitElement,
property,
TemplateResult,
css,
PropertyValues,
} from "lit-element";
import { DeviceRegistryEntry } from "../../../../../data/device_registry";
import { haStyle } from "../../../../../resources/styles";
import { HomeAssistant } from "../../../../../types";
import {
ZHADevice,
fetchZHADevice,
reconfigureNode,
} from "../../../../../data/zha";
import { navigate } from "../../../../../common/navigate";
import { showZHADeviceZigbeeInfoDialog } from "../../../../../dialogs/zha-device-zigbee-signature-dialog/show-dialog-zha-device-zigbee-info";
import { showConfirmationDialog } from "../../../../../dialogs/generic/show-dialog-box";
@customElement("ha-device-actions-zha")
export class HaDeviceActionsZha extends LitElement {
@property() public hass!: HomeAssistant;
@property() public device!: DeviceRegistryEntry;
@property() private _zhaDevice?: ZHADevice;
protected updated(changedProperties: PropertyValues) {
if (changedProperties.has("device")) {
const zigbeeConnection = this.device.connections.find(
(conn) => conn[0] === "zigbee"
);
if (!zigbeeConnection) {
return;
}
fetchZHADevice(this.hass, zigbeeConnection[1]).then((device) => {
this._zhaDevice = device;
});
}
}
protected render(): TemplateResult {
if (!this._zhaDevice) {
return html``;
}
return html`
${this._zhaDevice.device_type !== "Coordinator"
? html`
<mwc-button @click=${this._onReconfigureNodeClick}>
${this.hass!.localize(
"ui.dialogs.zha_device_info.buttons.reconfigure"
)}
</mwc-button>
`
: ""}
${this._zhaDevice.power_source === "Mains" &&
(this._zhaDevice.device_type === "Router" ||
this._zhaDevice.device_type === "Coordinator")
? html`
<mwc-button @click=${this._onAddDevicesClick}>
${this.hass!.localize("ui.dialogs.zha_device_info.buttons.add")}
</mwc-button>
`
: ""}
${this._zhaDevice.device_type !== "Coordinator"
? html`
<mwc-button @click=${this._handleZigbeeInfoClicked}>
${this.hass!.localize(
"ui.dialogs.zha_device_info.buttons.zigbee_information"
)}
</mwc-button>
<mwc-button class="warning" @click=${this._removeDevice}>
${this.hass!.localize(
"ui.dialogs.zha_device_info.buttons.remove"
)}
</mwc-button>
`
: ""}
`;
}
private async _onReconfigureNodeClick(): Promise<void> {
if (!this.hass) {
return;
}
reconfigureNode(this.hass, this._zhaDevice!.ieee);
}
private _onAddDevicesClick() {
navigate(this, "/config/zha/add/" + this._zhaDevice!.ieee);
}
private async _handleZigbeeInfoClicked() {
showZHADeviceZigbeeInfoDialog(this, { device: this._zhaDevice! });
}
private async _removeDevice() {
const confirmed = await showConfirmationDialog(this, {
text: this.hass.localize(
"ui.dialogs.zha_device_info.confirmations.remove"
),
});
if (!confirmed) {
return;
}
this.hass.callService("zha", "remove", {
ieee_address: this._zhaDevice!.ieee,
});
}
static get styles(): CSSResult[] {
return [
haStyle,
css`
:host {
display: flex;
flex-direction: column;
align-items: flex-start;
}
`,
];
}
}

View File

@ -0,0 +1,93 @@
import {
CSSResult,
customElement,
html,
LitElement,
property,
TemplateResult,
css,
PropertyValues,
} from "lit-element";
import { DeviceRegistryEntry } from "../../../../../data/device_registry";
import { haStyle } from "../../../../../resources/styles";
import { HomeAssistant } from "../../../../../types";
import { ZHADevice, fetchZHADevice } from "../../../../../data/zha";
import { formatAsPaddedHex } from "../../../integrations/integration-panels/zha/functions";
@customElement("ha-device-info-zha")
export class HaDeviceActionsZha extends LitElement {
@property() public hass!: HomeAssistant;
@property() public device!: DeviceRegistryEntry;
@property() private _zhaDevice?: ZHADevice;
protected updated(changedProperties: PropertyValues) {
if (changedProperties.has("device")) {
const zigbeeConnection = this.device.connections.find(
(conn) => conn[0] === "zigbee"
);
if (!zigbeeConnection) {
return;
}
fetchZHADevice(this.hass, zigbeeConnection[1]).then((device) => {
this._zhaDevice = device;
});
}
}
protected render(): TemplateResult {
if (!this._zhaDevice) {
return html``;
}
return html`
<h4>Zigbee info</h4>
<div>IEEE: ${this._zhaDevice.ieee}</div>
<div>Nwk: ${formatAsPaddedHex(this._zhaDevice.nwk)}</div>
<div>Device Type: ${this._zhaDevice.device_type}</div>
<div>
LQI:
${this._zhaDevice.lqi ||
this.hass!.localize("ui.dialogs.zha_device_info.unknown")}
</div>
<div>
RSSI:
${this._zhaDevice.rssi ||
this.hass!.localize("ui.dialogs.zha_device_info.unknown")}
</div>
<div>
${this.hass!.localize("ui.dialogs.zha_device_info.last_seen")}:
${this._zhaDevice.last_seen ||
this.hass!.localize("ui.dialogs.zha_device_info.unknown")}
</div>
<div>
${this.hass!.localize("ui.dialogs.zha_device_info.power_source")}:
${this._zhaDevice.power_source ||
this.hass!.localize("ui.dialogs.zha_device_info.unknown")}
</div>
${this._zhaDevice.quirk_applied
? html`
<div>
${this.hass!.localize("ui.dialogs.zha_device_info.quirk")}:
${this._zhaDevice.quirk_class}
</div>
`
: ""}
`;
}
static get styles(): CSSResult[] {
return [
haStyle,
css`
h4 {
margin-bottom: 4px;
}
div {
word-break: break-all;
margin-top: 2px;
}
`,
];
}
}

View File

@ -6,6 +6,7 @@ import {
html,
LitElement,
property,
TemplateResult,
} from "lit-element";
import { ifDefined } from "lit-html/directives/if-defined";
import memoizeOne from "memoize-one";
@ -38,13 +39,12 @@ import "../../../layouts/hass-tabs-subpage";
import { HomeAssistant, Route } from "../../../types";
import "../ha-config-section";
import { configSections } from "../ha-panel-config";
import "./device-detail/ha-device-card-mqtt";
import "./device-detail/ha-device-entities-card";
import "./device-detail/ha-device-info-card";
import { showDeviceAutomationDialog } from "./device-detail/show-dialog-device-automation";
export interface EntityRegistryStateEntry extends EntityRegistryEntry {
stateName?: string;
stateName?: string | null;
}
@customElement("ha-config-device-page")
@ -226,16 +226,7 @@ export class HaConfigDevicePage extends LitElement {
.devices=${this.devices}
.device=${device}
>
${
integrations.includes("mqtt")
? html`
<ha-device-card-mqtt
.hass=${this.hass}
.device=${device}
></ha-device-card-mqtt>
`
: html``
}
${this._renderIntegrationInfo(device, integrations)}
</ha-device-info-card>
${
@ -439,7 +430,7 @@ export class HaConfigDevicePage extends LitElement {
</hass-tabs-subpage> `;
}
private _computeEntityName(entity) {
private _computeEntityName(entity: EntityRegistryEntry) {
if (entity.name) {
return entity.name;
}
@ -480,6 +471,41 @@ export class HaConfigDevicePage extends LitElement {
});
}
private _renderIntegrationInfo(
device,
integrations: string[]
): TemplateResult[] {
const templates: TemplateResult[] = [];
if (integrations.includes("mqtt")) {
import("./device-detail/integration-elements/ha-device-actions-mqtt");
templates.push(html`
<div class="card-actions" slot="actions">
<ha-device-actions-mqtt
.hass=${this.hass}
.device=${device}
></ha-device-actions-mqtt>
</div>
`);
}
if (integrations.includes("zha")) {
import("./device-detail/integration-elements/ha-device-actions-zha");
import("./device-detail/integration-elements/ha-device-info-zha");
templates.push(html`
<ha-device-info-zha
.hass=${this.hass}
.device=${device}
></ha-device-info-zha>
<div class="card-actions" slot="actions">
<ha-device-actions-zha
.hass=${this.hass}
.device=${device}
></ha-device-actions-zha>
</div>
`);
}
return templates;
}
private async _showSettings() {
const device = this._device(this.deviceId, this.devices)!;
showDeviceRegistryDetailDialog(this, {

View File

@ -163,20 +163,6 @@ export const configSections: { [name: string]: PageNavigation[] } = {
advancedOnly: true,
},
],
other: [
{
component: "zha",
path: "/config/zha",
translationKey: "component.zha.title",
icon: "hass:zigbee",
},
{
component: "zwave",
path: "/config/zwave",
translationKey: "component.zwave.title",
icon: "hass:z-wave",
},
],
};
@customElement("ha-panel-config")
@ -327,14 +313,14 @@ class HaPanelConfig extends HassRouterPage {
tag: "zha-config-dashboard-router",
load: () =>
import(
/* webpackChunkName: "panel-config-zha" */ "./zha/zha-config-dashboard-router"
/* webpackChunkName: "panel-config-zha" */ "./integrations/integration-panels/zha/zha-config-dashboard-router"
),
},
zwave: {
tag: "ha-config-zwave",
load: () =>
import(
/* webpackChunkName: "panel-config-zwave" */ "./zwave/ha-config-zwave"
/* webpackChunkName: "panel-config-zwave" */ "./integrations/integration-panels/zwave/ha-config-zwave"
),
},
},

View File

@ -45,6 +45,17 @@ declare global {
}
}
const integrationsWithPanel = {
zha: {
buttonLocalizeKey: "ui.panel.config.zha.button",
path: "/config/zha/dashboard",
},
zwave: {
buttonLocalizeKey: "ui.panel.config.zwave.button",
path: "/config/zwave",
},
};
@customElement("ha-integration-card")
export class HaIntegrationCard extends LitElement {
@property() public hass!: HomeAssistant;
@ -180,13 +191,24 @@ export class HaIntegrationCard extends LitElement {
"ui.panel.config.integrations.config_entry.rename"
)}</mwc-button
>
${item.supports_options
${item.domain in integrationsWithPanel
? html`<a
href=${`${
integrationsWithPanel[item.domain].path
}?config_entry=${item.entry_id}`}
><mwc-button>
${this.hass.localize(
integrationsWithPanel[item.domain].buttonLocalizeKey
)}
</mwc-button></a
>`
: item.supports_options
? html`
<mwc-button @click=${this._showOptions}
>${this.hass.localize(
<mwc-button @click=${this._showOptions}>
${this.hass.localize(
"ui.panel.config.integrations.config_entry.options"
)}</mwc-button
>
)}
</mwc-button>
`
: ""}
</div>

View File

@ -1,4 +1,4 @@
import { Cluster, ZHADevice, ZHAGroup } from "../../../data/zha";
import { Cluster, ZHADevice, ZHAGroup } from "../../../../../data/zha";
export const formatAsPaddedHex = (value: string | number): string => {
let hex = value;
@ -8,6 +8,9 @@ export const formatAsPaddedHex = (value: string | number): string => {
return "0x" + hex.toString(16).padStart(4, "0");
};
export const getIeeeTail = (ieee: string) =>
ieee.split(":").slice(-4).reverse().join("");
export const sortZHADevices = (a: ZHADevice, b: ZHADevice): number => {
const nameA = a.user_given_name ? a.user_given_name : a.name;
const nameb = b.user_given_name ? b.user_given_name : b.name;

View File

@ -1,4 +1,4 @@
import { Cluster, ZHADevice } from "../../../data/zha";
import { Cluster, ZHADevice } from "../../../../../data/zha";
export interface PickerTarget extends EventTarget {
selected: number;

View File

@ -1,5 +1,5 @@
import "@material/mwc-button";
import "../../../components/ha-icon-button";
import "../../../../../components/ha-icon-button";
import "@polymer/paper-spinner/paper-spinner";
import {
css,
@ -9,19 +9,24 @@ import {
LitElement,
property,
TemplateResult,
PropertyValues,
} from "lit-element";
import "../../../components/ha-service-description";
import "../../../components/ha-textarea";
import { ZHADevice } from "../../../data/zha";
import "../../../layouts/hass-subpage";
import { haStyle } from "../../../resources/styles";
import { HomeAssistant, Route } from "../../../types";
import "../../../../../components/ha-service-description";
import "@polymer/paper-input/paper-textarea";
import { ZHADevice } from "../../../../../data/zha";
import "../../../../../layouts/hass-tabs-subpage";
import { haStyle } from "../../../../../resources/styles";
import { HomeAssistant, Route } from "../../../../../types";
import "./zha-device-card";
import { zhaTabs } from "./zha-config-dashboard";
import { IronAutogrowTextareaElement } from "@polymer/iron-autogrow-textarea";
@customElement("zha-add-devices-page")
class ZHAAddDevicesPage extends LitElement {
@property() public hass!: HomeAssistant;
@property() public narrow?: boolean;
@property() public isWide?: boolean;
@property() public route?: Route;
@ -36,6 +41,8 @@ class ZHAAddDevicesPage extends LitElement {
@property() private _showHelp = false;
@property() private _showLogs = false;
private _ieeeAddress?: string;
private _addDevicesTimeoutHandle: any = undefined;
@ -60,58 +67,63 @@ class ZHAAddDevicesPage extends LitElement {
this._formattedEvents = "";
}
protected updated(changedProps: PropertyValues) {
super.updated(changedProps);
if (
changedProps.has("hass") &&
!this._active &&
!changedProps.get("hass")
) {
this._subscribe();
}
}
protected render(): TemplateResult {
return html`
<hass-subpage
header="${this.hass!.localize(
"ui.panel.config.zha.add_device_page.header"
)}"
<hass-tabs-subpage
.hass=${this.hass}
.narrow=${this.narrow}
.route=${this.route}
.tabs=${zhaTabs}
>
${this._active
? html`
<h2>
<paper-spinner
?active="${this._active}"
alt="Searching"
></paper-spinner>
${this.hass!.localize(
"ui.panel.config.zha.add_device_page.spinner"
)}
</h2>
`
: html`
<div class="card-actions">
<mwc-button @click=${this._subscribe} class="search-button">
<mwc-button slot="toolbar-icon" @click=${this._toggleLogs}
>${this._showLogs ? "Hide logs" : "Show logs"}</mwc-button
>
<div class="searching">
${this._active
? html`
<h1>
${this.hass!.localize(
"ui.panel.config.zha.add_device_page.search_again"
"ui.panel.config.zha.add_device_page.spinner"
)}
</mwc-button>
<ha-icon-button
class="toggle-help-icon"
@click="${this._onHelpTap}"
icon="hass:help-circle"
></ha-icon-button>
${this._showHelp
? html`
<ha-service-description
.hass=${this.hass}
domain="zha"
service="permit"
class="help-text"
></ha-service-description>
`
: ""}
</div>
`}
</h1>
<paper-spinner active alt="Searching"></paper-spinner>
`
: html`
<div>
<mwc-button @click=${this._subscribe} class="search-button">
${this.hass!.localize(
"ui.panel.config.zha.add_device_page.search_again"
)}
</mwc-button>
</div>
`}
</div>
${this._error ? html` <div class="error">${this._error}</div> ` : ""}
<div class="content-header"></div>
<div class="content">
${this._discoveredDevices.length < 1
? html`
<div class="discovery-text">
<h4>
${this.hass!.localize(
"ui.panel.config.zha.add_device_page.discovery_text"
"ui.panel.config.zha.add_device_page.pairing_mode"
)}
</h4>
<h4>
${this.hass!.localize(
this._active
? "ui.panel.config.zha.add_device_page.discovered_text"
: "ui.panel.config.zha.add_device_page.no_devices_found"
)}
</h4>
</div>
@ -123,27 +135,38 @@ class ZHAAddDevicesPage extends LitElement {
class="card"
.hass=${this.hass}
.device=${device}
.narrow=${!this.isWide}
.narrow=${this.narrow}
.showHelp=${this._showHelp}
.showActions=${!this._active}
.showEntityDetail=${false}
></zha-device-card>
`
)}
`}
</div>
<ha-textarea class="events" value="${this._formattedEvents}">
</ha-textarea>
</hass-subpage>
${this._showLogs
? html`<paper-textarea
readonly
max-rows="10"
class="log"
value="${this._formattedEvents}"
>
</paper-textarea>`
: ""}
</hass-tabs-subpage>
`;
}
private _toggleLogs() {
this._showLogs = !this._showLogs;
}
private _handleMessage(message: any): void {
if (message.type === "log_output") {
this._formattedEvents += message.log_entry.message + "\n";
if (this.shadowRoot) {
const textArea = this.shadowRoot.querySelector("ha-textarea");
if (textArea) {
const paperTextArea = this.shadowRoot.querySelector("paper-textarea");
if (paperTextArea) {
const textArea = (paperTextArea.inputElement as IronAutogrowTextareaElement)
.textarea;
textArea.scrollTop = textArea.scrollHeight;
}
}
@ -165,69 +188,58 @@ class ZHAAddDevicesPage extends LitElement {
}
private _subscribe(): void {
if (!this.hass) {
return;
}
this._active = true;
const data: any = { type: "zha/devices/permit" };
if (this._ieeeAddress) {
data.ieee = this._ieeeAddress;
}
this._subscribed = this.hass!.connection.subscribeMessage(
this._subscribed = this.hass.connection.subscribeMessage(
(message) => this._handleMessage(message),
data
);
this._active = true;
this._addDevicesTimeoutHandle = setTimeout(
() => this._unsubscribe(),
120000
);
}
private _onHelpTap(): void {
this._showHelp = !this._showHelp;
}
static get styles(): CSSResult[] {
return [
haStyle,
css`
.discovery-text,
.content-header {
margin: 16px;
.discovery-text {
width: 100%;
padding: 16px;
display: flex;
flex-direction: column;
align-items: center;
}
.content {
border-top: 1px solid var(--light-primary-color);
min-height: 500px;
display: flex;
flex-wrap: wrap;
padding: 4px;
justify-content: left;
overflow: scroll;
justify-content: center;
}
.error {
color: var(--google-red-500);
}
paper-spinner {
display: none;
margin-right: 20px;
margin-left: 16px;
padding: 20px;
}
paper-spinner[active] {
display: block;
float: left;
margin-right: 20px;
margin-left: 16px;
.searching {
margin-top: 20px;
display: flex;
flex-direction: column;
align-items: center;
}
.card {
margin-left: 16px;
margin-right: 16px;
margin-bottom: 0px;
margin-top: 10px;
margin: 8px;
}
.events {
margin: 16px;
border-top: 1px solid var(--light-primary-color);
padding-top: 16px;
min-height: 200px;
max-height: 200px;
overflow: scroll;
.log {
padding: 16px;
}
.toggle-help-icon {
position: absolute;

View File

@ -12,20 +12,20 @@ import {
PropertyValues,
query,
} from "lit-element";
import type { HASSDomEvent } from "../../../common/dom/fire_event";
import { navigate } from "../../../common/navigate";
import type { SelectionChangedEvent } from "../../../components/data-table/ha-data-table";
import type { HASSDomEvent } from "../../../../../common/dom/fire_event";
import { navigate } from "../../../../../common/navigate";
import type { SelectionChangedEvent } from "../../../../../components/data-table/ha-data-table";
import {
addGroup,
fetchGroupableDevices,
ZHAGroup,
ZHADeviceEndpoint,
} from "../../../data/zha";
import "../../../layouts/hass-error-screen";
import "../../../layouts/hass-subpage";
import type { PolymerChangedEvent } from "../../../polymer-types";
import type { HomeAssistant } from "../../../types";
import "../ha-config-section";
} from "../../../../../data/zha";
import "../../../../../layouts/hass-error-screen";
import "../../../../../layouts/hass-subpage";
import type { PolymerChangedEvent } from "../../../../../polymer-types";
import type { HomeAssistant } from "../../../../../types";
import "../../../ha-config-section";
import "./zha-device-endpoint-data-table";
import type { ZHADeviceEndpointDataTable } from "./zha-device-endpoint-data-table";

View File

@ -1,6 +1,6 @@
import "@material/mwc-button";
import "@polymer/paper-dropdown-menu/paper-dropdown-menu";
import "../../../components/ha-icon-button";
import "../../../../../components/ha-icon-button";
import "@polymer/paper-input/paper-input";
import "@polymer/paper-item/paper-item";
import "@polymer/paper-listbox/paper-listbox";
@ -13,9 +13,9 @@ import {
PropertyValues,
TemplateResult,
} from "lit-element";
import "../../../components/buttons/ha-call-service-button";
import "../../../components/ha-card";
import "../../../components/ha-service-description";
import "../../../../../components/buttons/ha-call-service-button";
import "../../../../../components/ha-card";
import "../../../../../components/ha-service-description";
import {
Attribute,
Cluster,
@ -23,10 +23,10 @@ import {
ReadAttributeServiceData,
readAttributeValue,
ZHADevice,
} from "../../../data/zha";
import { haStyle } from "../../../resources/styles";
import { HomeAssistant } from "../../../types";
import "../ha-config-section";
} from "../../../../../data/zha";
import { haStyle } from "../../../../../resources/styles";
import { HomeAssistant } from "../../../../../types";
import "../../../ha-config-section";
import { formatAsPaddedHex } from "./functions";
import {
ChangeEvent,

View File

@ -1,5 +1,5 @@
import "@polymer/paper-dropdown-menu/paper-dropdown-menu";
import "../../../components/ha-icon-button";
import "../../../../../components/ha-icon-button";
import "@polymer/paper-input/paper-input";
import "@polymer/paper-item/paper-item";
import "@polymer/paper-listbox/paper-listbox";
@ -12,18 +12,18 @@ import {
PropertyValues,
TemplateResult,
} from "lit-element";
import "../../../components/buttons/ha-call-service-button";
import "../../../components/ha-card";
import "../../../components/ha-service-description";
import "../../../../../components/buttons/ha-call-service-button";
import "../../../../../components/ha-card";
import "../../../../../components/ha-service-description";
import {
Cluster,
Command,
fetchCommandsForCluster,
ZHADevice,
} from "../../../data/zha";
import { haStyle } from "../../../resources/styles";
import { HomeAssistant } from "../../../types";
import "../ha-config-section";
} from "../../../../../data/zha";
import { haStyle } from "../../../../../resources/styles";
import { HomeAssistant } from "../../../../../types";
import "../../../ha-config-section";
import { formatAsPaddedHex } from "./functions";
import {
ChangeEvent,

View File

@ -7,14 +7,14 @@ import {
TemplateResult,
} from "lit-element";
import memoizeOne from "memoize-one";
import "../../../components/data-table/ha-data-table";
import "../../../../../components/data-table/ha-data-table";
import type {
DataTableColumnContainer,
HaDataTable,
} from "../../../components/data-table/ha-data-table";
import "../../../components/entity/ha-state-icon";
import type { Cluster } from "../../../data/zha";
import type { HomeAssistant } from "../../../types";
} from "../../../../../components/data-table/ha-data-table";
import "../../../../../components/entity/ha-state-icon";
import type { Cluster } from "../../../../../data/zha";
import type { HomeAssistant } from "../../../../../types";
import { formatAsPaddedHex } from "./functions";
export interface ClusterRowData extends Cluster {

View File

@ -1,5 +1,5 @@
import "@polymer/paper-dropdown-menu/paper-dropdown-menu";
import "../../../components/ha-icon-button";
import "../../../../../components/ha-icon-button";
import "@polymer/paper-item/paper-item";
import "@polymer/paper-listbox/paper-listbox";
import {
@ -11,14 +11,18 @@ import {
PropertyValues,
TemplateResult,
} from "lit-element";
import { fireEvent } from "../../../common/dom/fire_event";
import "../../../components/buttons/ha-call-service-button";
import "../../../components/ha-card";
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 { fireEvent } from "../../../../../common/dom/fire_event";
import "../../../../../components/buttons/ha-call-service-button";
import "../../../../../components/ha-card";
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";
import { ItemSelectedEvent } from "./types";

View File

@ -2,8 +2,9 @@ import { customElement, property } from "lit-element";
import {
HassRouterPage,
RouterOptions,
} from "../../../layouts/hass-router-page";
import { HomeAssistant } from "../../../types";
} from "../../../../../layouts/hass-router-page";
import { HomeAssistant } from "../../../../../types";
import { navigate } from "../../../../../common/navigate";
@customElement("zha-config-dashboard-router")
class ZHAConfigDashboardRouter extends HassRouterPage {
@ -13,6 +14,10 @@ class ZHAConfigDashboardRouter extends HassRouterPage {
@property() public narrow!: boolean;
private _configEntry = new URLSearchParams(window.location.search).get(
"config_entry"
);
protected routerOptions: RouterOptions = {
defaultPage: "dashboard",
showLoading: true,
@ -24,13 +29,6 @@ class ZHAConfigDashboardRouter extends HassRouterPage {
/* webpackChunkName: "zha-config-dashboard" */ "./zha-config-dashboard"
),
},
device: {
tag: "zha-device-page",
load: () =>
import(
/* webpackChunkName: "zha-devices-page" */ "./zha-device-page"
),
},
add: {
tag: "zha-add-devices-page",
load: () =>
@ -65,11 +63,24 @@ class ZHAConfigDashboardRouter extends HassRouterPage {
el.hass = this.hass;
el.isWide = this.isWide;
el.narrow = this.narrow;
el.configEntryId = this._configEntry;
if (this._currentPage === "group") {
el.groupId = this.routeTail.path.substr(1);
} else if (this._currentPage === "device") {
el.ieee = this.routeTail.path.substr(1);
}
const searchParams = new URLSearchParams(window.location.search);
if (this._configEntry && !searchParams.has("config_entry")) {
searchParams.append("config_entry", this._configEntry);
navigate(
this,
`${this.routeTail.prefix}${
this.routeTail.path
}?${searchParams.toString()}`,
true
);
}
}
}

View File

@ -0,0 +1,132 @@
import "@polymer/paper-item/paper-item";
import "@polymer/paper-item/paper-item-body";
import "@material/mwc-fab";
import {
css,
CSSResultArray,
customElement,
html,
LitElement,
property,
TemplateResult,
} from "lit-element";
import "../../../../../components/ha-card";
import "../../../../../components/ha-icon-next";
import { haStyle } from "../../../../../resources/styles";
import type { HomeAssistant, Route } from "../../../../../types";
import "../../../ha-config-section";
import { mdiNetwork, mdiFolderMultipleOutline, mdiPlus } from "@mdi/js";
import "../../../../../layouts/hass-tabs-subpage";
import type { PageNavigation } from "../../../../../layouts/hass-tabs-subpage";
import { computeRTL } from "../../../../../common/util/compute_rtl";
export const zhaTabs: PageNavigation[] = [
{
translationKey: "ui.panel.config.zha.network.caption",
path: `/config/zha/dashboard`,
iconPath: mdiNetwork,
},
{
translationKey: "ui.panel.config.zha.groups.caption",
path: `/config/zha/groups`,
iconPath: mdiFolderMultipleOutline,
},
];
@customElement("zha-config-dashboard")
class ZHAConfigDashboard extends LitElement {
@property({ type: Object }) public hass!: HomeAssistant;
@property({ type: Object }) public route!: Route;
@property({ type: Boolean }) public narrow!: boolean;
@property({ type: Boolean }) public isWide!: boolean;
@property() public configEntryId?: string;
protected render(): TemplateResult {
return html`
<hass-tabs-subpage
.hass=${this.hass}
.narrow=${this.narrow}
.route=${this.route}
.tabs=${zhaTabs}
back-path="/config/integrations"
>
<ha-card header="Zigbee Network">
<div class="card-content">
Network info/settings for specific config entry
</div>
${this.configEntryId
? html`<div class="card-actions">
<a
href="${`/config/devices/dashboard?historyBack=1&config_entry=${this.configEntryId}`}"
>
<mwc-button>Devices</mwc-button>
</a>
<a
href="${`/config/entities/dashboard?historyBack=1&config_entry=${this.configEntryId}`}"
>
<mwc-button>Entities</mwc-button>
</a>
</div>`
: ""}
</ha-card>
<a href="/config/zha/add">
<mwc-fab
?is-wide=${this.isWide}
?narrow=${this.narrow}
title=${this.hass.localize("ui.panel.config.zha.add_device")}
?rtl=${computeRTL(this.hass)}
>
<ha-svg-icon slot="icon" path=${mdiPlus}></ha-svg-icon>
</mwc-fab>
</a>
</hass-tabs-subpage>
`;
}
static get styles(): CSSResultArray {
return [
haStyle,
css`
ha-card {
margin: auto;
margin-top: 16px;
max-width: 500px;
}
mwc-fab {
position: fixed;
bottom: 16px;
right: 16px;
z-index: 1;
}
mwc-fab[is-wide] {
bottom: 24px;
right: 24px;
}
mwc-fab[narrow] {
bottom: 84px;
}
mwc-fab[rtl] {
right: auto;
left: 16px;
}
mwc-fab[rtl][is-wide] {
bottom: 24px;
right: auto;
left: 24px;
}
`,
];
}
}
declare global {
interface HTMLElementTagNameMap {
"zha-config-dashboard": ZHAConfigDashboard;
}
}

View File

@ -1,6 +1,6 @@
import "@material/mwc-button/mwc-button";
import "@polymer/paper-dropdown-menu/paper-dropdown-menu";
import "../../../components/ha-icon-button";
import "../../../../../components/ha-icon-button";
import "@polymer/paper-item/paper-item";
import "@polymer/paper-listbox/paper-listbox";
import {
@ -13,13 +13,13 @@ import {
PropertyValues,
TemplateResult,
} from "lit-element";
import "../../../components/buttons/ha-call-service-button";
import "../../../components/ha-card";
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 "../../../../../components/buttons/ha-call-service-button";
import "../../../../../components/ha-card";
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")

View File

@ -0,0 +1,243 @@
import "@polymer/paper-input/paper-input";
import "@polymer/paper-listbox/paper-listbox";
import { UnsubscribeFunc } from "home-assistant-js-websocket";
import {
css,
CSSResult,
customElement,
html,
LitElement,
property,
TemplateResult,
} from "lit-element";
import { fireEvent } from "../../../../../common/dom/fire_event";
import { computeStateName } from "../../../../../common/entity/compute_state_name";
import "../../../../../components/buttons/ha-call-service-button";
import "../../../../../components/entity/state-badge";
import "../../../../../components/ha-card";
import "../../../../../components/ha-service-description";
import { updateDeviceRegistryEntry } from "../../../../../data/device_registry";
import { ZHADevice } from "../../../../../data/zha";
import { haStyle } from "../../../../../resources/styles";
import { HomeAssistant } from "../../../../../types";
import "../../../../../components/ha-area-picker";
import { showAlertDialog } from "../../../../../dialogs/generic/show-dialog-box";
import { SubscribeMixin } from "../../../../../mixins/subscribe-mixin";
import {
subscribeEntityRegistry,
EntityRegistryEntry,
updateEntityRegistryEntry,
} from "../../../../../data/entity_registry";
import { createValidEntityId } from "../../../../../common/entity/valid_entity_id";
import memoizeOne from "memoize-one";
import { EntityRegistryStateEntry } from "../../../devices/ha-config-device-page";
import { compare } from "../../../../../common/string/compare";
import { getIeeeTail } from "./functions";
@customElement("zha-device-card")
class ZHADeviceCard extends SubscribeMixin(LitElement) {
@property() public hass!: HomeAssistant;
@property() public device?: ZHADevice;
@property({ type: Boolean }) public narrow?: boolean;
@property() private _entities: EntityRegistryEntry[] = [];
private _deviceEntities = memoizeOne(
(
deviceId: string,
entities: EntityRegistryEntry[]
): EntityRegistryStateEntry[] =>
entities
.filter((entity) => entity.device_id === deviceId)
.map((entity) => {
return { ...entity, stateName: this._computeEntityName(entity) };
})
.sort((ent1, ent2) =>
compare(
ent1.stateName || `zzz${ent1.entity_id}`,
ent2.stateName || `zzz${ent2.entity_id}`
)
)
);
public hassSubscribe(): UnsubscribeFunc[] {
return [
subscribeEntityRegistry(this.hass.connection, (entities) => {
this._entities = entities;
}),
];
}
protected render(): TemplateResult {
if (!this.hass || !this.device) {
return html``;
}
const entities = this._deviceEntities(
this.device.device_reg_id,
this._entities
);
return html`
<ha-card .header=${this.device.user_given_name || this.device.name}>
<div class="card-content">
<div class="info">
<div class="model">${this.device.model}</div>
<div class="manuf">
${this.hass.localize(
"ui.dialogs.zha_device_info.manuf",
"manufacturer",
this.device.manufacturer
)}
</div>
</div>
<div class="device-entities">
${entities.map(
(entity) => html`
<state-badge
@click="${this._openMoreInfo}"
.title=${entity.stateName!}
.stateObj="${this.hass!.states[entity.entity_id]}"
slot="item-icon"
></state-badge>
`
)}
</div>
<paper-input
type="string"
@change=${this._rename}
.value=${this.device.user_given_name || this.device.name}
.label=${this.hass.localize(
"ui.dialogs.zha_device_info.zha_device_card.device_name_placeholder"
)}
></paper-input>
<ha-area-picker
.hass=${this.hass}
.device=${this.device.device_reg_id}
@value-changed=${this._areaPicked}
></ha-area-picker>
</div>
</ha-card>
`;
}
private async _rename(event): Promise<void> {
if (!this.hass || !this.device) {
return;
}
const device = this.device;
const oldDeviceName = device.user_given_name || device.name;
const newDeviceName = event.target.value;
this.device.user_given_name = newDeviceName;
await updateDeviceRegistryEntry(this.hass, device.device_reg_id, {
name_by_user: newDeviceName,
});
if (!oldDeviceName || !newDeviceName || oldDeviceName === newDeviceName) {
return;
}
const entities = this._deviceEntities(device.device_reg_id, this._entities);
const oldDeviceEntityId = createValidEntityId(oldDeviceName);
const newDeviceEntityId = createValidEntityId(newDeviceName);
const ieeeTail = getIeeeTail(device.ieee);
const updateProms = entities.map((entity) => {
const name = entity.name || entity.stateName;
let newEntityId: string | null = null;
let newName: string | null = null;
if (name && name.includes(oldDeviceName)) {
newName = name.replace(` ${ieeeTail}`, "");
newName = newName.replace(oldDeviceName, newDeviceName);
newEntityId = entity.entity_id.replace(`_${ieeeTail}`, "");
newEntityId = newEntityId.replace(oldDeviceEntityId, newDeviceEntityId);
}
if (!newName && !newEntityId) {
return new Promise((resolve) => resolve());
}
return updateEntityRegistryEntry(this.hass!, entity.entity_id, {
name: newName || name,
disabled_by: entity.disabled_by,
new_entity_id: newEntityId || entity.entity_id,
});
});
await Promise.all(updateProms);
}
private _openMoreInfo(ev: MouseEvent): void {
fireEvent(this, "hass-more-info", {
entityId: (ev.currentTarget as any).stateObj.entity_id,
});
}
private _computeEntityName(entity: EntityRegistryEntry): string {
if (this.hass.states[entity.entity_id]) {
return computeStateName(this.hass.states[entity.entity_id]);
}
return entity.name;
}
private async _areaPicked(ev: CustomEvent) {
const picker = ev.currentTarget as any;
const area = ev.detail.value;
try {
await updateDeviceRegistryEntry(this.hass, this.device!.device_reg_id, {
area_id: area,
});
this.device!.area_id = area;
} catch (err) {
showAlertDialog(this, {
text: this.hass.localize(
"ui.panel.config.integrations.config_flow.error_saving_area",
"error",
err.message
),
});
picker.value = null;
}
}
static get styles(): CSSResult[] {
return [
haStyle,
css`
.device-entities {
display: flex;
flex-wrap: wrap;
padding: 4px;
justify-content: left;
min-height: 48px;
}
.device {
width: 30%;
}
.device .name {
font-weight: bold;
}
.device .manuf {
color: var(--secondary-text-color);
margin-bottom: 20px;
}
.extra-info {
margin-top: 8px;
}
state-badge {
cursor: pointer;
}
`,
];
}
}
declare global {
interface HTMLElementTagNameMap {
"zha-device-card": ZHADeviceCard;
}
}

View File

@ -9,16 +9,18 @@ import {
CSSResult,
} from "lit-element";
import memoizeOne from "memoize-one";
import "../../../components/data-table/ha-data-table";
import "../../../../../components/data-table/ha-data-table";
import type {
DataTableColumnContainer,
HaDataTable,
DataTableRowData,
} from "../../../components/data-table/ha-data-table";
import "../../../components/entity/ha-state-icon";
import type { ZHADeviceEndpoint, ZHAEntityReference } from "../../../data/zha";
import { showZHADeviceInfoDialog } from "../../../dialogs/zha-device-info-dialog/show-dialog-zha-device-info";
import type { HomeAssistant } from "../../../types";
} from "../../../../../components/data-table/ha-data-table";
import "../../../../../components/entity/ha-state-icon";
import type {
ZHADeviceEndpoint,
ZHAEntityReference,
} from "../../../../../data/zha";
import type { HomeAssistant } from "../../../../../types";
export interface DeviceEndpointRowData extends DataTableRowData {
id: string;
@ -55,6 +57,7 @@ export class ZHADeviceEndpointDataTable extends LitElement {
ieee: deviceEndpoint.device.ieee,
endpoint_id: deviceEndpoint.endpoint_id,
entities: deviceEndpoint.entities,
dev_id: deviceEndpoint.device.device_reg_id,
});
});
@ -72,14 +75,10 @@ export class ZHADeviceEndpointDataTable extends LitElement {
filterable: true,
direction: "asc",
grows: true,
template: (name) => html`
<div
class="mdc-data-table__cell table-cell-text"
@click=${this._handleClicked}
style="cursor: pointer;"
>
template: (name, device: any) => html`
<a href="${`/config/devices/device/${device.dev_id}`}">
${name}
</div>
</a>
`,
},
endpoint_id: {
@ -95,14 +94,10 @@ export class ZHADeviceEndpointDataTable extends LitElement {
filterable: true,
direction: "asc",
grows: true,
template: (name) => html`
<div
class="mdc-data-table__cell table-cell-text"
@click=${this._handleClicked}
style="cursor: pointer;"
>
template: (name, device: any) => html`
<a href="${`/config/devices/device/${device.dev_id}`}">
${name}
</div>
</a>
`,
},
endpoint_id: {
@ -156,14 +151,6 @@ export class ZHADeviceEndpointDataTable extends LitElement {
`;
}
private async _handleClicked(ev: CustomEvent) {
const rowId = ((ev.target as HTMLElement).closest(
".mdc-data-table__row"
) as any).rowId;
const ieee = rowId.substring(0, rowId.indexOf("_"));
showZHADeviceInfoDialog(this, { ieee });
}
static get styles(): CSSResult[] {
return [
css`

View File

@ -1,6 +1,6 @@
import "@material/mwc-button/mwc-button";
import "@polymer/paper-dropdown-menu/paper-dropdown-menu";
import "../../../components/ha-icon-button";
import "../../../../../components/ha-icon-button";
import "@polymer/paper-item/paper-item";
import "@polymer/paper-listbox/paper-listbox";
import {
@ -14,11 +14,11 @@ import {
query,
TemplateResult,
} from "lit-element";
import type { HASSDomEvent } from "../../../common/dom/fire_event";
import "../../../components/buttons/ha-call-service-button";
import { SelectionChangedEvent } from "../../../components/data-table/ha-data-table";
import "../../../components/ha-card";
import "../../../components/ha-service-description";
import type { HASSDomEvent } from "../../../../../common/dom/fire_event";
import "../../../../../components/buttons/ha-call-service-button";
import { SelectionChangedEvent } from "../../../../../components/data-table/ha-data-table";
import "../../../../../components/ha-card";
import "../../../../../components/ha-service-description";
import {
bindDeviceToGroup,
Cluster,
@ -26,10 +26,10 @@ import {
unbindDeviceFromGroup,
ZHADevice,
ZHAGroup,
} from "../../../data/zha";
import { haStyle } from "../../../resources/styles";
import type { HomeAssistant } from "../../../types";
import "../ha-config-section";
} 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";

View File

@ -1,5 +1,5 @@
import "@material/mwc-button";
import "../../../components/ha-icon-button";
import "../../../../../components/ha-icon-button";
import "@polymer/paper-spinner/paper-spinner";
import {
css,
@ -11,9 +11,9 @@ import {
PropertyValues,
query,
} from "lit-element";
import { HASSDomEvent } from "../../../common/dom/fire_event";
import { navigate } from "../../../common/navigate";
import { SelectionChangedEvent } from "../../../components/data-table/ha-data-table";
import { HASSDomEvent } from "../../../../../common/dom/fire_event";
import { navigate } from "../../../../../common/navigate";
import { SelectionChangedEvent } from "../../../../../components/data-table/ha-data-table";
import {
addMembersToGroup,
fetchGroup,
@ -22,13 +22,12 @@ import {
removeMembersFromGroup,
ZHAGroup,
ZHADeviceEndpoint,
} from "../../../data/zha";
import "../../../layouts/hass-error-screen";
import "../../../layouts/hass-subpage";
import { HomeAssistant } from "../../../types";
import "../ha-config-section";
} from "../../../../../data/zha";
import "../../../../../layouts/hass-error-screen";
import "../../../../../layouts/hass-subpage";
import { HomeAssistant } from "../../../../../types";
import "../../../ha-config-section";
import { formatAsPaddedHex } from "./functions";
import "./zha-device-card";
import "./zha-device-endpoint-data-table";
import type { ZHADeviceEndpointDataTable } from "./zha-device-endpoint-data-table";
@ -122,25 +121,26 @@ export class ZHAGroupPage extends LitElement {
<div class="header">
${this.hass.localize("ui.panel.config.zha.groups.members")}
</div>
${this.group.members.length
? this.group.members.map(
(member) => html`
<zha-device-card
class="card"
.hass=${this.hass}
.device=${member.device}
.narrow=${this.narrow}
.showActions=${false}
.showEditableInfo=${false}
></zha-device-card>
`
)
: html`
<p>
This group has no members
</p>
`}
<ha-card>
${this.group.members.length
? this.group.members.map(
(member) =>
html`<a
href="/config/devices/device/${member.device
.device_reg_id}"
>
<paper-item
>${member.device.user_given_name ||
member.device.name}</paper-item
>
</a>`
)
: html`
<paper-item>
This group has no members
</paper-item>
`}
</ha-card>
${this.group.members.length
? html`
<div class="header">
@ -285,6 +285,9 @@ export class ZHAGroupPage extends LitElement {
static get styles(): CSSResult[] {
return [
css`
hass-subpage {
--app-header-text-color: var(--sidebar-icon-color);
}
.header {
font-family: var(--paper-font-display1_-_font-family);
-webkit-font-smoothing: var(
@ -297,14 +300,15 @@ export class ZHAGroupPage extends LitElement {
opacity: var(--dark-primary-opacity);
}
ha-config-section *:last-child {
padding-bottom: 24px;
}
.button {
float: right;
}
a {
color: var(--primary-color);
text-decoration: none;
}
mwc-button paper-spinner {
width: 14px;
height: 14px;

View File

@ -0,0 +1,193 @@
import "@material/mwc-button";
import "@material/mwc-fab";
import "../../../../../components/ha-icon-button";
import memoizeOne from "memoize-one";
import {
customElement,
html,
LitElement,
property,
PropertyValues,
TemplateResult,
CSSResultArray,
css,
} from "lit-element";
import { HASSDomEvent } from "../../../../../common/dom/fire_event";
import { navigate } from "../../../../../common/navigate";
import {
DataTableColumnContainer,
RowClickedEvent,
} from "../../../../../components/data-table/ha-data-table";
import { fetchGroups, ZHAGroup, ZHADevice } from "../../../../../data/zha";
import "../../../../../layouts/hass-tabs-subpage-data-table";
import { HomeAssistant, Route } from "../../../../../types";
import { sortZHAGroups, formatAsPaddedHex } from "./functions";
import { zhaTabs } from "./zha-config-dashboard";
import { computeRTL } from "../../../../../common/util/compute_rtl";
import { mdiPlus } from "@mdi/js";
import { haStyle } from "../../../../../resources/styles";
export interface GroupRowData extends ZHAGroup {
group?: GroupRowData;
id?: string;
}
@customElement("zha-groups-dashboard")
export class ZHAGroupsDashboard extends LitElement {
@property() public hass!: HomeAssistant;
@property({ type: Object }) public route!: Route;
@property({ type: Boolean }) public narrow!: boolean;
@property({ type: Boolean }) public isWide!: boolean;
@property() public _groups: ZHAGroup[] = [];
private _firstUpdatedCalled = false;
public connectedCallback(): void {
super.connectedCallback();
if (this.hass && this._firstUpdatedCalled) {
this._fetchGroups();
}
}
protected firstUpdated(changedProperties: PropertyValues): void {
super.firstUpdated(changedProperties);
if (this.hass) {
this._fetchGroups();
}
this._firstUpdatedCalled = true;
}
private _formattedGroups = memoizeOne((groups: ZHAGroup[]) => {
let outputGroups: GroupRowData[] = groups;
outputGroups = outputGroups.map((group) => {
return {
...group,
id: String(group.group_id),
};
});
return outputGroups;
});
private _columns = memoizeOne(
(narrow: boolean): DataTableColumnContainer =>
narrow
? {
name: {
title: "Group",
sortable: true,
filterable: true,
direction: "asc",
grows: true,
},
}
: {
name: {
title: this.hass.localize("ui.panel.config.zha.groups.groups"),
sortable: true,
filterable: true,
direction: "asc",
grows: true,
},
group_id: {
title: this.hass.localize("ui.panel.config.zha.groups.group_id"),
type: "numeric",
width: "15%",
template: (groupId: number) => {
return html` ${formatAsPaddedHex(groupId)} `;
},
sortable: true,
},
members: {
title: this.hass.localize("ui.panel.config.zha.groups.members"),
type: "numeric",
width: "15%",
template: (members: ZHADevice[]) => {
return html` ${members.length} `;
},
sortable: true,
},
}
);
protected render(): TemplateResult {
return html`
<hass-tabs-subpage-data-table
.tabs=${zhaTabs}
.hass=${this.hass}
.narrow=${this.narrow}
.route=${this.route}
.columns=${this._columns(this.narrow)}
.data=${this._formattedGroups(this._groups)}
@row-click=${this._handleRowClicked}
>
</hass-tabs-subpage-data-table>
<a href="/config/zha/group-add">
<mwc-fab
?is-wide=${this.isWide}
?narrow=${this.narrow}
title=${this.hass!.localize("ui.panel.config.zha.groups.add_group")}
?rtl=${computeRTL(this.hass)}
>
<ha-svg-icon slot="icon" path=${mdiPlus}></ha-svg-icon>
</mwc-fab>
</a>
`;
}
private async _fetchGroups() {
this._groups = (await fetchGroups(this.hass!)).sort(sortZHAGroups);
}
private _handleRowClicked(ev: HASSDomEvent<RowClickedEvent>) {
const groupId = ev.detail.id;
navigate(this, `/config/zha/group/${groupId}`);
}
static get styles(): CSSResultArray {
return [
haStyle,
css`
mwc-fab {
position: fixed;
bottom: 16px;
right: 16px;
z-index: 1;
}
mwc-fab[is-wide] {
bottom: 24px;
right: 24px;
}
mwc-fab[narrow] {
bottom: 84px;
}
mwc-fab[rtl] {
right: auto;
left: 16px;
}
mwc-fab[rtl][is-wide] {
bottom: 24px;
right: auto;
left: 24px;
}
a {
color: var(--primary-color);
}
`,
];
}
}
declare global {
interface HTMLElementTagNameMap {
"zha-groups-dashboard": ZHAGroupsDashboard;
}
}

View File

@ -1,27 +1,27 @@
import "@polymer/app-layout/app-header/app-header";
import "@polymer/app-layout/app-toolbar/app-toolbar";
import "@polymer/paper-dropdown-menu/paper-dropdown-menu";
import "../../../components/ha-icon-button";
import "../../../../../components/ha-icon-button";
import "@polymer/paper-input/paper-input";
import "@polymer/paper-item/paper-item";
import "@polymer/paper-listbox/paper-listbox";
import { html } from "@polymer/polymer/lib/utils/html-tag";
/* eslint-plugin-disable lit */
import { PolymerElement } from "@polymer/polymer/polymer-element";
import { computeStateDomain } from "../../../common/entity/compute_state_domain";
import { computeStateName } from "../../../common/entity/compute_state_name";
import { sortStatesByName } from "../../../common/entity/states_sort_by_name";
import "../../../components/buttons/ha-call-service-button";
import "../../../components/ha-card";
import "../../../components/ha-menu-button";
import "../../../components/ha-icon-button-arrow-prev";
import "../../../components/ha-service-description";
import "../../../layouts/ha-app-layout";
import { EventsMixin } from "../../../mixins/events-mixin";
import LocalizeMixin from "../../../mixins/localize-mixin";
import "../../../styles/polymer-ha-style";
import "../ha-config-section";
import "../ha-form-style";
import { computeStateDomain } from "../../../../../common/entity/compute_state_domain";
import { computeStateName } from "../../../../../common/entity/compute_state_name";
import { sortStatesByName } from "../../../../../common/entity/states_sort_by_name";
import "../../../../../components/buttons/ha-call-service-button";
import "../../../../../components/ha-card";
import "../../../../../components/ha-menu-button";
import "../../../../../components/ha-icon-button-arrow-prev";
import "../../../../../components/ha-service-description";
import "../../../../../layouts/ha-app-layout";
import { EventsMixin } from "../../../../../mixins/events-mixin";
import LocalizeMixin from "../../../../../mixins/localize-mixin";
import "../../../../../styles/polymer-ha-style";
import "../../../ha-config-section";
import "../../../ha-form-style";
import "./zwave-groups";
import "./zwave-log";
import "./zwave-network";

View File

@ -4,10 +4,10 @@ import "@polymer/paper-listbox/paper-listbox";
import { html } from "@polymer/polymer/lib/utils/html-tag";
/* eslint-plugin-disable lit */
import { PolymerElement } from "@polymer/polymer/polymer-element";
import { computeStateName } from "../../../common/entity/compute_state_name";
import "../../../components/buttons/ha-call-service-button";
import "../../../components/ha-card";
import "../../../styles/polymer-ha-style";
import { computeStateName } from "../../../../../common/entity/compute_state_name";
import "../../../../../components/buttons/ha-call-service-button";
import "../../../../../components/ha-card";
import "../../../../../styles/polymer-ha-style";
class ZwaveGroups extends PolymerElement {
static get template() {

View File

@ -2,9 +2,9 @@ import "@polymer/paper-dialog-scrollable/paper-dialog-scrollable";
import { html } from "@polymer/polymer/lib/utils/html-tag";
/* eslint-plugin-disable lit */
import { PolymerElement } from "@polymer/polymer/polymer-element";
import "../../../components/dialog/ha-paper-dialog";
import { EventsMixin } from "../../../mixins/events-mixin";
import "../../../styles/polymer-ha-style-dialog";
import "../../../../../components/dialog/ha-paper-dialog";
import { EventsMixin } from "../../../../../mixins/events-mixin";
import "../../../../../styles/polymer-ha-style-dialog";
class ZwaveLogDialog extends EventsMixin(PolymerElement) {
static get template() {

View File

@ -4,12 +4,12 @@ import "@polymer/paper-input/paper-input";
import { html } from "@polymer/polymer/lib/utils/html-tag";
/* eslint-plugin-disable lit */
import { PolymerElement } from "@polymer/polymer/polymer-element";
import isPwa from "../../../common/config/is_pwa";
import "../../../components/ha-card";
import { EventsMixin } from "../../../mixins/events-mixin";
import LocalizeMixin from "../../../mixins/localize-mixin";
import "../ha-config-section";
import "../../../styles/polymer-ha-style";
import isPwa from "../../../../../common/config/is_pwa";
import "../../../../../components/ha-card";
import { EventsMixin } from "../../../../../mixins/events-mixin";
import LocalizeMixin from "../../../../../mixins/localize-mixin";
import "../../../ha-config-section";
import "../../../../../styles/polymer-ha-style";
let registeredDialog = false;

View File

@ -1,4 +1,4 @@
import "../../../components/ha-icon-button";
import "../../../../../components/ha-icon-button";
import "@polymer/paper-spinner/paper-spinner";
import { UnsubscribeFunc } from "home-assistant-js-websocket";
import {
@ -10,11 +10,11 @@ import {
property,
TemplateResult,
} from "lit-element";
import "../../../components/buttons/ha-call-api-button";
import "../../../components/buttons/ha-call-service-button";
import "../../../components/ha-card";
import "../../../components/ha-icon";
import "../../../components/ha-service-description";
import "../../../../../components/buttons/ha-call-api-button";
import "../../../../../components/buttons/ha-call-service-button";
import "../../../../../components/ha-card";
import "../../../../../components/ha-icon";
import "../../../../../components/ha-service-description";
import {
fetchNetworkStatus,
ZWaveNetworkStatus,
@ -22,10 +22,10 @@ import {
ZWAVE_NETWORK_STATE_READY,
ZWAVE_NETWORK_STATE_STARTED,
ZWAVE_NETWORK_STATE_STOPPED,
} from "../../../data/zwave";
import { haStyle } from "../../../resources/styles";
import { HomeAssistant } from "../../../types";
import "../ha-config-section";
} from "../../../../../data/zwave";
import { haStyle } from "../../../../../resources/styles";
import { HomeAssistant } from "../../../../../types";
import "../../../ha-config-section";
@customElement("zwave-network")
export class ZwaveNetwork extends LitElement {

View File

@ -12,16 +12,16 @@ import {
PropertyValues,
TemplateResult,
} from "lit-element";
import "../../../components/buttons/ha-call-service-button";
import "../../../components/ha-card";
import "../../../../../components/buttons/ha-call-service-button";
import "../../../../../components/ha-card";
import {
fetchNodeConfig,
ZWaveConfigItem,
ZWaveConfigServiceData,
ZWaveNode,
} from "../../../data/zwave";
import { haStyle } from "../../../resources/styles";
import { HomeAssistant } from "../../../types";
} from "../../../../../data/zwave";
import { haStyle } from "../../../../../resources/styles";
import { HomeAssistant } from "../../../../../types";
@customElement("zwave-node-config")
export class ZwaveNodeConfig extends LitElement {

View File

@ -5,9 +5,9 @@ import "@polymer/paper-listbox/paper-listbox";
import { html } from "@polymer/polymer/lib/utils/html-tag";
/* eslint-plugin-disable lit */
import { PolymerElement } from "@polymer/polymer/polymer-element";
import "../../../components/buttons/ha-call-api-button";
import "../../../components/ha-card";
import "../../../styles/polymer-ha-style";
import "../../../../../components/buttons/ha-call-api-button";
import "../../../../../components/ha-card";
import "../../../../../styles/polymer-ha-style";
class ZwaveNodeProtection extends PolymerElement {
static get template() {

View File

@ -5,9 +5,9 @@ import "@polymer/paper-listbox/paper-listbox";
import { html } from "@polymer/polymer/lib/utils/html-tag";
/* eslint-plugin-disable lit */
import { PolymerElement } from "@polymer/polymer/polymer-element";
import "../../../components/buttons/ha-call-service-button";
import "../../../components/ha-card";
import "../../../styles/polymer-ha-style";
import "../../../../../components/buttons/ha-call-service-button";
import "../../../../../components/ha-card";
import "../../../../../styles/polymer-ha-style";
class ZwaveUsercodes extends PolymerElement {
static get template() {

View File

@ -10,11 +10,11 @@ import {
property,
TemplateResult,
} from "lit-element";
import "../../../components/buttons/ha-call-service-button";
import "../../../components/ha-card";
import { ZWaveValue } from "../../../data/zwave";
import { haStyle } from "../../../resources/styles";
import { HomeAssistant } from "../../../types";
import "../../../../../components/buttons/ha-call-service-button";
import "../../../../../components/ha-card";
import { ZWaveValue } from "../../../../../data/zwave";
import { haStyle } from "../../../../../resources/styles";
import { HomeAssistant } from "../../../../../types";
@customElement("zwave-values")
export class ZwaveValues extends LitElement {

View File

@ -1,188 +0,0 @@
import "@polymer/paper-item/paper-item";
import "@polymer/paper-item/paper-item-body";
import {
css,
CSSResultArray,
customElement,
html,
LitElement,
property,
PropertyValues,
TemplateResult,
} from "lit-element";
import memoizeOne from "memoize-one";
import { navigate } from "../../../common/navigate";
import "../../../components/data-table/ha-data-table";
import type {
DataTableColumnContainer,
RowClickedEvent,
DataTableRowData,
} from "../../../components/data-table/ha-data-table";
import "../../../components/ha-card";
import "../../../components/ha-icon-next";
import { fetchDevices } from "../../../data/zha";
import type { ZHADevice } from "../../../data/zha";
import "../../../layouts/hass-subpage";
import { haStyle } from "../../../resources/styles";
import type { HomeAssistant, Route } from "../../../types";
import "../ha-config-section";
import { formatAsPaddedHex, sortZHADevices } from "./functions";
export interface DeviceRowData extends DataTableRowData {
device?: DeviceRowData;
}
@customElement("zha-config-dashboard")
class ZHAConfigDashboard extends LitElement {
@property({ type: Object }) public hass!: HomeAssistant;
@property({ type: Object }) public route!: Route;
@property({ type: Boolean }) public narrow!: boolean;
@property({ type: Boolean }) public isWide!: boolean;
@property() private _devices: ZHADevice[] = [];
private pages: string[] = ["add", "groups"];
private _firstUpdatedCalled = false;
private _memoizeDevices = memoizeOne((devices: ZHADevice[]) => {
let outputDevices: DeviceRowData[] = devices;
outputDevices = outputDevices.map((device) => {
return {
...device,
name: device.user_given_name ? device.user_given_name : device.name,
nwk: formatAsPaddedHex(device.nwk),
};
});
return outputDevices;
});
private _columns = memoizeOne(
(narrow: boolean): DataTableColumnContainer =>
narrow
? {
name: {
title: "Devices",
sortable: true,
filterable: true,
direction: "asc",
grows: true,
},
}
: {
name: {
title: "Name",
sortable: true,
filterable: true,
direction: "asc",
grows: true,
},
nwk: {
title: "Nwk",
sortable: true,
filterable: true,
width: "15%",
},
ieee: {
title: "IEEE",
sortable: true,
filterable: true,
width: "30%",
},
}
);
public connectedCallback(): void {
super.connectedCallback();
if (this.hass && this._firstUpdatedCalled) {
this._fetchDevices();
}
}
protected firstUpdated(changedProperties: PropertyValues): void {
super.firstUpdated(changedProperties);
if (this.hass) {
this._fetchDevices();
}
this._firstUpdatedCalled = true;
}
protected render(): TemplateResult {
return html`
<hass-subpage .header=${this.hass.localize("component.zha.title")}>
<ha-config-section .narrow=${this.narrow} .isWide=${this.isWide}>
<div slot="header">
${this.hass.localize("ui.panel.config.zha.header")}
</div>
<div slot="introduction">
${this.hass.localize("ui.panel.config.zha.introduction")}
</div>
<ha-card>
${this.pages.map((page) => {
return html`
<a href=${`/config/zha/${page}`}>
<paper-item>
<paper-item-body two-line="">
${this.hass.localize(
`ui.panel.config.zha.${page}.caption`
)}
<div secondary>
${this.hass.localize(
`ui.panel.config.zha.${page}.description`
)}
</div>
</paper-item-body>
<ha-icon-next></ha-icon-next>
</paper-item>
</a>
`;
})}
</ha-card>
<ha-card>
<ha-data-table
.columns=${this._columns(this.narrow)}
.data=${this._memoizeDevices(this._devices)}
@row-click=${this._handleDeviceClicked}
.id=${"ieee"}
auto-height
></ha-data-table>
</ha-card>
</ha-config-section>
</hass-subpage>
`;
}
private async _fetchDevices() {
this._devices = (await fetchDevices(this.hass!)).sort(sortZHADevices);
}
private async _handleDeviceClicked(ev: CustomEvent) {
const deviceId = (ev.detail as RowClickedEvent).id;
navigate(this, `/config/zha/device/${deviceId}`);
}
static get styles(): CSSResultArray {
return [
haStyle,
css`
a {
text-decoration: none;
color: var(--primary-text-color);
}
`,
];
}
}
declare global {
interface HTMLElementTagNameMap {
"zha-config-dashboard": ZHAConfigDashboard;
}
}

View File

@ -1,566 +0,0 @@
import "@material/mwc-button";
import "@polymer/paper-dropdown-menu/paper-dropdown-menu";
import "@polymer/paper-input/paper-input";
import "@polymer/paper-item/paper-icon-item";
import "@polymer/paper-item/paper-item";
import "@polymer/paper-item/paper-item-body";
import "@polymer/paper-listbox/paper-listbox";
import { HassEvent, UnsubscribeFunc } from "home-assistant-js-websocket";
import {
css,
CSSResult,
customElement,
html,
LitElement,
property,
PropertyValues,
TemplateResult,
} from "lit-element";
import { fireEvent } from "../../../common/dom/fire_event";
import { computeStateName } from "../../../common/entity/compute_state_name";
import { navigate } from "../../../common/navigate";
import "../../../components/buttons/ha-call-service-button";
import "../../../components/entity/state-badge";
import "../../../components/ha-card";
import "../../../components/ha-service-description";
import {
AreaRegistryEntry,
subscribeAreaRegistry,
} from "../../../data/area_registry";
import {
DeviceRegistryEntryMutableParams,
updateDeviceRegistryEntry,
} from "../../../data/device_registry";
import {
reconfigureNode,
ZHADevice,
ZHAEntityReference,
} from "../../../data/zha";
import { showZHADeviceZigbeeInfoDialog } from "../../../dialogs/zha-device-zigbee-signature-dialog/show-dialog-zha-device-zigbee-info";
import { haStyle } from "../../../resources/styles";
import { HomeAssistant } from "../../../types";
import { addEntitiesToLovelaceView } from "../../lovelace/editor/add-entities-to-view";
import { formatAsPaddedHex } from "./functions";
import { ItemSelectedEvent, NodeServiceData } from "./types";
declare global {
// for fire event
interface HASSDomEvents {
"zha-device-removed": {
device?: ZHADevice;
};
}
}
@customElement("zha-device-card")
class ZHADeviceCard extends LitElement {
@property() public hass!: HomeAssistant;
@property() public device?: ZHADevice;
@property({ type: Boolean }) public narrow?: boolean;
@property({ type: Boolean }) public showHelp?: boolean = false;
@property({ type: Boolean }) public showActions?: boolean = true;
@property({ type: Boolean }) public showName?: boolean = true;
@property({ type: Boolean }) public showEntityDetail?: boolean = true;
@property({ type: Boolean }) public showModelInfo?: boolean = true;
@property({ type: Boolean }) public showEditableInfo?: boolean = true;
@property() private _serviceData?: NodeServiceData;
@property() private _areas: AreaRegistryEntry[] = [];
@property() private _selectedAreaIndex = -1;
@property() private _userGivenName?: string;
private _unsubAreas?: UnsubscribeFunc;
private _unsubEntities?: UnsubscribeFunc;
public disconnectedCallback() {
super.disconnectedCallback();
if (this._unsubAreas) {
this._unsubAreas();
}
if (this._unsubEntities) {
this._unsubEntities();
}
}
public connectedCallback() {
super.connectedCallback();
this._unsubAreas = subscribeAreaRegistry(this.hass.connection, (areas) => {
this._areas = areas;
if (this.device) {
this._selectedAreaIndex =
this._areas.findIndex(
(area) => area.area_id === this.device!.area_id
) + 1; // account for the no area selected index
}
});
this.hass.connection
.subscribeEvents((event: HassEvent) => {
if (this.device) {
this.device!.entities.forEach((deviceEntity) => {
if (event.data.old_entity_id === deviceEntity.entity_id) {
deviceEntity.entity_id = event.data.entity_id;
}
});
}
}, "entity_registry_updated")
.then((unsub) => {
this._unsubEntities = unsub;
});
}
protected firstUpdated(changedProperties: PropertyValues): void {
super.firstUpdated(changedProperties);
this.addEventListener("hass-service-called", (ev) =>
this.serviceCalled(ev)
);
}
protected updated(changedProperties: PropertyValues): void {
if (changedProperties.has("device")) {
if (!this._areas || !this.device || !this.device.area_id) {
this._selectedAreaIndex = 0;
} else {
this._selectedAreaIndex =
this._areas.findIndex(
(area) => area.area_id === this.device!.area_id
) + 1;
}
this._userGivenName = this.device!.user_given_name;
this._serviceData = {
ieee_address: this.device!.ieee,
};
}
super.update(changedProperties);
}
protected serviceCalled(ev): void {
// Check if this is for us
if (ev.detail.success && ev.detail.service === "remove") {
fireEvent(this, "zha-device-removed", {
device: this.device,
});
}
}
protected render(): TemplateResult {
return html`
<ha-card header="${this.showName ? this.device!.name : ""}">
${
this.showModelInfo
? html`
<div class="info">
<div class="model">${this.device!.model}</div>
<div class="manuf">
${this.hass!.localize(
"ui.dialogs.zha_device_info.manuf",
"manufacturer",
this.device!.manufacturer
)}
</div>
</div>
`
: ""
}
<div class="card-content">
<dl>
<dt>IEEE:</dt>
<dd class="zha-info">${this.device!.ieee}</dd>
<dt>Nwk:</dt>
<dd class="zha-info">${formatAsPaddedHex(this.device!.nwk)}</dd>
<dt>Device Type:</dt>
<dd class="zha-info">${this.device!.device_type}</dd>
<dt>LQI:</dt>
<dd class="zha-info">${
this.device!.lqi ||
this.hass!.localize("ui.dialogs.zha_device_info.unknown")
}</dd>
<dt>RSSI:</dt>
<dd class="zha-info">${
this.device!.rssi ||
this.hass!.localize("ui.dialogs.zha_device_info.unknown")
}</dd>
<dt>${this.hass!.localize(
"ui.dialogs.zha_device_info.last_seen"
)}:</dt>
<dd class="zha-info">${
this.device!.last_seen ||
this.hass!.localize("ui.dialogs.zha_device_info.unknown")
}</dd>
<dt>${this.hass!.localize(
"ui.dialogs.zha_device_info.power_source"
)}:</dt>
<dd class="zha-info">${
this.device!.power_source ||
this.hass!.localize("ui.dialogs.zha_device_info.unknown")
}</dd>
${
this.device!.quirk_applied
? html`
<dt>
${this.hass!.localize(
"ui.dialogs.zha_device_info.quirk"
)}:
</dt>
<dd class="zha-info">${this.device!.quirk_class}</dd>
`
: ""
}
</dl>
</div>
<div class="device-entities">
${this.device!.entities.map(
(entity) => html`
<paper-icon-item
@click="${this._openMoreInfo}"
.entity="${entity}"
>
<state-badge
.stateObj="${this.hass!.states[entity.entity_id]}"
slot="item-icon"
></state-badge>
${this.showEntityDetail
? html`
<paper-item-body>
<div class="name">
${this._computeEntityName(entity)}
</div>
<div class="secondary entity-id">
${entity.entity_id}
</div>
</paper-item-body>
`
: ""}
</paper-icon-item>
`
)}
</div>
${
this.device!.entities && this.device!.entities.length > 0
? html`
<div class="card-actions">
<mwc-button @click=${this._addToLovelaceView}>
${this.hass.localize(
"ui.panel.config.devices.entities.add_entities_lovelace"
)}
</mwc-button>
</div>
`
: ""
}
${
this.showEditableInfo
? html`
<div class="editable">
<paper-input
type="string"
@change="${this._saveCustomName}"
.value="${this._userGivenName || ""}"
.placeholder="${this.hass!.localize(
"ui.dialogs.zha_device_info.zha_device_card.device_name_placeholder"
)}"
></paper-input>
</div>
<div class="node-picker">
<paper-dropdown-menu
.label="${this.hass!.localize(
"ui.dialogs.zha_device_info.zha_device_card.area_picker_label"
)}"
class="menu"
>
<paper-listbox
slot="dropdown-content"
.selected="${this._selectedAreaIndex}"
@iron-select="${this._selectedAreaChanged}"
>
<paper-item>
${this.hass!.localize(
"ui.dialogs.zha_device_info.no_area"
)}
</paper-item>
${this._areas.map(
(entry) => html`
<paper-item>${entry.name}</paper-item>
`
)}
</paper-listbox>
</paper-dropdown-menu>
</div>
`
: ""
}
${
this.showActions
? html`
<div class="card-actions">
${this.device!.device_type !== "Coordinator"
? html`
<mwc-button @click=${this._onReconfigureNodeClick}>
${this.hass!.localize(
"ui.dialogs.zha_device_info.buttons.reconfigure"
)}
</mwc-button>
${this.showHelp
? html`
<div class="help-text">
${this.hass!.localize(
"ui.dialogs.zha_device_info.services.reconfigure"
)}
</div>
`
: ""}
<ha-call-service-button
.hass=${this.hass}
domain="zha"
service="remove"
.confirmation=${this.hass!.localize(
"ui.dialogs.zha_device_info.confirmations.remove"
)}
.serviceData=${this._serviceData}
>
${this.hass!.localize(
"ui.dialogs.zha_device_info.buttons.remove"
)}
</ha-call-service-button>
${this.showHelp
? html`
<div class="help-text">
${this.hass!.localize(
"ui.dialogs.zha_device_info.services.remove"
)}
</div>
`
: ""}
`
: ""}
${this.device!.power_source === "Mains" &&
(this.device!.device_type === "Router" ||
this.device!.device_type === "Coordinator")
? html`
<mwc-button @click=${this._onAddDevicesClick}>
${this.hass!.localize(
"ui.panel.config.zha.common.add_devices"
)}
</mwc-button>
${this.showHelp
? html`
<ha-service-description
.hass=${this.hass}
domain="zha"
service="permit"
class="help-text2"
></ha-service-description>
`
: ""}
`
: ""}
${this.device!.device_type !== "Coordinator"
? html`
<mwc-button @click=${this._handleZigbeeInfoClicked}>
${this.hass!.localize(
"ui.dialogs.zha_device_info.buttons.zigbee_information"
)}
</mwc-button>
${this.showHelp
? html`
<div class="help-text">
${this.hass!.localize(
"ui.dialogs.zha_device_info.services.zigbee_information"
)}
</div>
`
: ""}
`
: ""}
</div>
`
: ""
}
</div>
</ha-card>
`;
}
private async _onReconfigureNodeClick(): Promise<void> {
if (this.hass) {
await reconfigureNode(this.hass, this.device!.ieee);
}
}
private _computeEntityName(entity: ZHAEntityReference): string {
if (this.hass.states[entity.entity_id]) {
return computeStateName(this.hass.states[entity.entity_id]);
}
return entity.name;
}
private async _saveCustomName(event): Promise<void> {
if (this.hass) {
const values: DeviceRegistryEntryMutableParams = {
name_by_user: event.target.value,
area_id: this.device!.area_id ? this.device!.area_id : undefined,
};
await updateDeviceRegistryEntry(
this.hass,
this.device!.device_reg_id,
values
);
this.device!.user_given_name = event.target.value;
}
}
private _openMoreInfo(ev: MouseEvent): void {
fireEvent(this, "hass-more-info", {
entityId: (ev.currentTarget as any).entity.entity_id,
});
}
private async _selectedAreaChanged(event: ItemSelectedEvent) {
if (!this.device || !this._areas) {
return;
}
this._selectedAreaIndex = event!.target!.selected;
const area = this._areas[this._selectedAreaIndex - 1]; // account for No Area
if (
(!area && !this.device.area_id) ||
(area && area.area_id === this.device.area_id)
) {
return;
}
const newAreaId = area ? area.area_id : undefined;
await updateDeviceRegistryEntry(this.hass!, this.device.device_reg_id, {
area_id: newAreaId,
name_by_user: this.device!.user_given_name,
});
this.device!.area_id = newAreaId;
}
private _onAddDevicesClick() {
navigate(this, "/config/zha/add/" + this.device!.ieee);
}
private async _handleZigbeeInfoClicked() {
showZHADeviceZigbeeInfoDialog(this, { device: this.device! });
}
private _addToLovelaceView(): void {
addEntitiesToLovelaceView(
this,
this.hass,
this.device!.entities.map((entity) => entity.entity_id)
);
}
static get styles(): CSSResult[] {
return [
haStyle,
css`
:host(:not([narrow])) .device-entities {
max-height: 225px;
overflow-y: auto;
display: flex;
flex-wrap: wrap;
padding: 4px;
justify-content: left;
}
ha-card {
flex: 1 0 100%;
padding-bottom: 10px;
min-width: 300px;
}
.device {
width: 30%;
}
.device .name {
font-weight: bold;
}
.device .manuf {
color: var(--secondary-text-color);
margin-bottom: 20px;
}
.extra-info {
margin-top: 8px;
}
.manuf,
.zha-info,
.name {
text-overflow: ellipsis;
}
.entity-id {
text-overflow: ellipsis;
color: var(--secondary-text-color);
}
.info {
margin-left: 16px;
}
dl {
display: flex;
flex-wrap: wrap;
width: 100%;
}
dl dt {
display: inline-block;
width: 30%;
padding-left: 12px;
float: left;
text-align: left;
}
dl dd {
width: 60%;
overflow-wrap: break-word;
margin-inline-start: 20px;
}
paper-icon-item {
overflow-x: hidden;
cursor: pointer;
padding-top: 4px;
padding-bottom: 4px;
}
.editable {
padding-left: 28px;
padding-right: 28px;
padding-bottom: 10px;
}
.help-text {
color: grey;
padding: 16px;
}
.menu {
width: 100%;
}
.node-picker {
align-items: center;
padding-left: 28px;
padding-right: 28px;
padding-bottom: 10px;
}
.buttons .icon {
margin-right: 16px;
}
`,
];
}
}
declare global {
interface HTMLElementTagNameMap {
"zha-device-card": ZHADeviceCard;
}
}

View File

@ -1,157 +0,0 @@
import {
css,
CSSResult,
customElement,
html,
LitElement,
property,
PropertyValues,
TemplateResult,
} from "lit-element";
import { HASSDomEvent } from "../../../common/dom/fire_event";
import {
Cluster,
fetchBindableDevices,
fetchGroups,
fetchZHADevice,
ZHADevice,
ZHAGroup,
} from "../../../data/zha";
import "../../../layouts/hass-subpage";
import { haStyle } from "../../../resources/styles";
import { HomeAssistant } from "../../../types";
import { sortZHADevices, sortZHAGroups } from "./functions";
import { ZHAClusterSelectedParams } from "./types";
import "./zha-cluster-attributes";
import "./zha-cluster-commands";
import "./zha-clusters";
import "./zha-device-binding";
import "./zha-group-binding";
import "./zha-node";
@customElement("zha-device-page")
export class ZHADevicePage extends LitElement {
@property() public hass?: HomeAssistant;
@property() public isWide?: boolean;
@property() public ieee?: string;
@property() public device?: ZHADevice;
@property() public narrow?: boolean;
@property() private _selectedCluster?: Cluster;
@property() private _bindableDevices: ZHADevice[] = [];
@property() private _groups: ZHAGroup[] = [];
protected updated(changedProperties: PropertyValues): void {
if (changedProperties.has("ieee")) {
this._fetchData();
}
super.update(changedProperties);
}
protected render(): TemplateResult {
return html`
<hass-subpage
.header=${this.hass!.localize("ui.panel.config.zha.devices.header")}
.back=${!this.isWide}
>
<zha-node
.isWide="${this.isWide}"
.hass=${this.hass}
.device=${this.device}
></zha-node>
${this.device && this.device.device_type !== "Coordinator"
? html`
<zha-clusters
.hass=${this.hass}
.isWide="${this.isWide}"
.selectedDevice="${this.device}"
@zha-cluster-selected="${this._onClusterSelected}"
></zha-clusters>
${this._selectedCluster
? html`
<zha-cluster-attributes
.isWide="${this.isWide}"
.hass=${this.hass}
.selectedNode="${this.device}"
.selectedCluster="${this._selectedCluster}"
></zha-cluster-attributes>
<zha-cluster-commands
.isWide="${this.isWide}"
.hass=${this.hass}
.selectedNode="${this.device}"
.selectedCluster="${this._selectedCluster}"
></zha-cluster-commands>
`
: ""}
${this._bindableDevices.length > 0
? html`
<zha-device-binding-control
.isWide="${this.isWide}"
.hass=${this.hass}
.selectedDevice="${this.device}"
.bindableDevices="${this._bindableDevices}"
></zha-device-binding-control>
`
: ""}
${this.device && this._groups.length > 0
? html`
<zha-group-binding-control
.isWide="${this.isWide}"
.narrow="${this.narrow}"
.hass=${this.hass}
.selectedDevice="${this.device}"
.groups="${this._groups}"
></zha-group-binding-control>
`
: ""}
`
: ""}
<div class="spacer"></div>
</hass-subpage>
`;
}
private _onClusterSelected(
selectedClusterEvent: HASSDomEvent<ZHAClusterSelectedParams>
): void {
this._selectedCluster = selectedClusterEvent.detail.cluster;
}
private async _fetchData(): Promise<void> {
if (this.ieee && this.hass) {
this.device = await fetchZHADevice(this.hass, this.ieee);
this._bindableDevices =
this.device && this.device.device_type !== "Coordinator"
? (await fetchBindableDevices(this.hass, this.ieee)).sort(
sortZHADevices
)
: [];
this._groups = (await fetchGroups(this.hass!)).sort(sortZHAGroups);
}
}
static get styles(): CSSResult[] {
return [
haStyle,
css`
.spacer {
height: 50px;
}
`,
];
}
}
declare global {
interface HTMLElementTagNameMap {
"zha-device-page": ZHADevicePage;
}
}

View File

@ -1,172 +0,0 @@
import "@material/mwc-button";
import "../../../components/ha-icon-button";
import "@polymer/paper-spinner/paper-spinner";
import {
css,
CSSResult,
customElement,
html,
LitElement,
property,
PropertyValues,
TemplateResult,
} from "lit-element";
import { HASSDomEvent } from "../../../common/dom/fire_event";
import { navigate } from "../../../common/navigate";
import { SelectionChangedEvent } from "../../../components/data-table/ha-data-table";
import { fetchGroups, removeGroups, ZHAGroup } from "../../../data/zha";
import "../../../layouts/hass-subpage";
import { HomeAssistant } from "../../../types";
import { sortZHAGroups } from "./functions";
import "./zha-groups-data-table";
@customElement("zha-groups-dashboard")
export class ZHAGroupsDashboard extends LitElement {
@property() public hass!: HomeAssistant;
@property() public narrow = false;
@property() public _groups?: ZHAGroup[];
@property() private _processingRemove = false;
@property() private _selectedGroupsToRemove: number[] = [];
private _firstUpdatedCalled = false;
public connectedCallback(): void {
super.connectedCallback();
if (this.hass && this._firstUpdatedCalled) {
this._fetchGroups();
}
}
protected firstUpdated(changedProperties: PropertyValues): void {
super.firstUpdated(changedProperties);
if (this.hass) {
this._fetchGroups();
}
this._firstUpdatedCalled = true;
}
protected render(): TemplateResult {
return html`
<hass-subpage
.header=${this.hass!.localize(
"ui.panel.config.zha.groups.groups-header"
)}
>
<ha-icon-button
slot="toolbar-icon"
icon="hass:plus"
@click=${this._addGroup}
></ha-icon-button>
<div class="content">
${this._groups
? html`
<zha-groups-data-table
.hass=${this.hass}
.narrow=${this.narrow}
.groups=${this._groups}
.selectable=${true}
@selection-changed=${this._handleRemoveSelectionChanged}
class="table"
></zha-groups-data-table>
`
: html`
<paper-spinner
active
alt=${this.hass!.localize("ui.common.loading")}
></paper-spinner>
`}
</div>
<div class="paper-dialog-buttons">
<mwc-button
?disabled="${!this._selectedGroupsToRemove.length ||
this._processingRemove}"
@click="${this._removeGroup}"
class="button"
>
<paper-spinner
?active="${this._processingRemove}"
alt=${this.hass!.localize(
"ui.panel.config.zha.groups.removing_groups"
)}
></paper-spinner>
${this.hass!.localize(
"ui.panel.config.zha.groups.remove_groups"
)}</mwc-button
>
</div>
</hass-subpage>
`;
}
private async _fetchGroups() {
this._groups = (await fetchGroups(this.hass!)).sort(sortZHAGroups);
}
private _handleRemoveSelectionChanged(
ev: HASSDomEvent<SelectionChangedEvent>
): void {
this._selectedGroupsToRemove = ev.detail.value.map((value) =>
Number(value)
);
}
private async _removeGroup(): Promise<void> {
this._processingRemove = true;
this._groups = await removeGroups(this.hass, this._selectedGroupsToRemove);
this._selectedGroupsToRemove = [];
this._processingRemove = false;
}
private async _addGroup(): Promise<void> {
navigate(this, `/config/zha/group-add`);
}
static get styles(): CSSResult[] {
return [
css`
.content {
padding: 4px;
}
zha-groups-data-table {
width: 100%;
}
.button {
float: right;
}
.table {
height: 200px;
overflow: auto;
}
mwc-button paper-spinner {
width: 14px;
height: 14px;
margin-right: 20px;
}
paper-spinner {
display: none;
}
paper-spinner[active] {
display: block;
}
.paper-dialog-buttons {
align-items: flex-end;
padding: 8px;
}
.paper-dialog-buttons .warning {
--mdc-theme-primary: var(--google-red-500);
}
`,
];
}
}
declare global {
interface HTMLElementTagNameMap {
"zha-groups-dashboard": ZHAGroupsDashboard;
}
}

View File

@ -1,129 +0,0 @@
import {
customElement,
html,
LitElement,
property,
query,
TemplateResult,
} from "lit-element";
import memoizeOne from "memoize-one";
import { navigate } from "../../../common/navigate";
import "../../../components/data-table/ha-data-table";
import type {
DataTableColumnContainer,
HaDataTable,
} from "../../../components/data-table/ha-data-table";
import "../../../components/entity/ha-state-icon";
import type { ZHADevice, ZHAGroup } from "../../../data/zha";
import type { HomeAssistant } from "../../../types";
import { formatAsPaddedHex } from "./functions";
export interface GroupRowData extends ZHAGroup {
group?: GroupRowData;
id?: string;
}
@customElement("zha-groups-data-table")
export class ZHAGroupsDataTable extends LitElement {
@property() public hass!: HomeAssistant;
@property() public narrow = false;
@property() public groups: ZHAGroup[] = [];
@property() public selectable = false;
@query("ha-data-table") private _dataTable!: HaDataTable;
private _groups = memoizeOne((groups: ZHAGroup[]) => {
let outputGroups: GroupRowData[] = groups;
outputGroups = outputGroups.map((group) => {
return {
...group,
id: String(group.group_id),
};
});
return outputGroups;
});
private _columns = memoizeOne(
(narrow: boolean): DataTableColumnContainer =>
narrow
? {
name: {
title: "Group",
sortable: true,
filterable: true,
direction: "asc",
grows: true,
template: (name) => html`
<div @click=${this._handleRowClicked} style="cursor: pointer;">
${name}
</div>
`,
},
}
: {
name: {
title: this.hass.localize("ui.panel.config.zha.groups.groups"),
sortable: true,
filterable: true,
direction: "asc",
grows: true,
template: (name) => html`
<div @click=${this._handleRowClicked} style="cursor: pointer;">
${name}
</div>
`,
},
group_id: {
title: this.hass.localize("ui.panel.config.zha.groups.group_id"),
type: "numeric",
width: "15%",
template: (groupId: number) => {
return html` ${formatAsPaddedHex(groupId)} `;
},
sortable: true,
},
members: {
title: this.hass.localize("ui.panel.config.zha.groups.members"),
type: "numeric",
width: "15%",
template: (members: ZHADevice[]) => {
return html` ${members.length} `;
},
sortable: true,
},
}
);
public clearSelection() {
this._dataTable.clearSelection();
}
protected render(): TemplateResult {
return html`
<ha-data-table
.columns=${this._columns(this.narrow)}
.data=${this._groups(this.groups)}
.selectable=${this.selectable}
auto-height
></ha-data-table>
`;
}
private _handleRowClicked(ev: CustomEvent) {
const groupId = ((ev.target as HTMLElement).closest(
".mdc-data-table__row"
) as any).rowId;
navigate(this, `/config/zha/group/${groupId}`);
}
}
declare global {
interface HTMLElementTagNameMap {
"zha-groups-data-table": ZHAGroupsDataTable;
}
}

View File

@ -1,148 +0,0 @@
import "../../../components/ha-icon-button";
import {
css,
CSSResult,
customElement,
html,
LitElement,
property,
TemplateResult,
} from "lit-element";
import { navigate } from "../../../common/navigate";
import "../../../components/buttons/ha-call-service-button";
import "../../../components/ha-card";
import "../../../components/ha-service-description";
import { ZHADevice } from "../../../data/zha";
import { haStyle } from "../../../resources/styles";
import { HomeAssistant } from "../../../types";
import "../ha-config-section";
import "./zha-device-card";
@customElement("zha-node")
export class ZHANode extends LitElement {
@property() public hass?: HomeAssistant;
@property() public isWide?: boolean;
@property() public device?: ZHADevice;
@property() private _showHelp = false;
protected render(): TemplateResult {
return html`
<ha-config-section .isWide="${this.isWide}">
<div class="header" slot="header">
<span
>${this.hass!.localize(
"ui.panel.config.zha.node_management.header"
)}</span
>
<ha-icon-button
class="toggle-help-icon"
@click="${this._onHelpTap}"
icon="hass:help-circle"
></ha-icon-button>
</div>
<span slot="introduction">
${this.hass!.localize(
"ui.panel.config.zha.node_management.introduction"
)}
<br /><br />
${this.hass!.localize(
"ui.panel.config.zha.node_management.hint_battery_devices"
)}
<br /><br />
${this.hass!.localize(
"ui.panel.config.zha.node_management.hint_wakeup"
)}
</span>
<div class="content">
${this.device
? html`
<zha-device-card
class="card"
.hass=${this.hass}
.device=${this.device}
.narrow=${!this.isWide}
.showHelp=${this._showHelp}
showName
showModelInfo
.showEntityDetail=${false}
showActions
@zha-device-removed=${this._onDeviceRemoved}
></zha-device-card>
`
: html`
<paper-spinner
active
alt=${this.hass!.localize("ui.common.loading")}
></paper-spinner>
`}
</div>
</ha-config-section>
`;
}
private _onHelpTap(): void {
this._showHelp = !this._showHelp;
}
private _onDeviceRemoved(): void {
this.device = undefined;
navigate(this, `/config/zha`, true);
}
static get styles(): CSSResult[] {
return [
haStyle,
css`
.node-info {
margin-left: 16px;
}
.help-text {
color: grey;
padding-left: 28px;
padding-right: 28px;
padding-bottom: 16px;
}
.content {
max-width: 680px;
}
.card {
padding: 28px 20px 0;
margin-top: 24px;
}
ha-service-description {
display: block;
color: grey;
}
[hidden] {
display: none;
}
.header {
flex-grow: 1;
}
.toggle-help-icon {
float: right;
top: 6px;
right: 0;
padding-right: 0px;
color: var(--primary-color);
}
`,
];
}
}
declare global {
interface HTMLElementTagNameMap {
"zha-node": ZHANode;
}
}

View File

@ -12,7 +12,6 @@ import SidebarMixin from "./sidebar-mixin";
import ThemesMixin from "./themes-mixin";
import TranslationsMixin from "./translations-mixin";
import { urlSyncMixin } from "./url-sync-mixin";
import ZHADialogMixin from "./zha-dialog-mixin";
const ext = <T extends Constructor>(baseClass: T, mixins): T =>
mixins.reduceRight((base, mixin) => mixin(base), baseClass);
@ -28,7 +27,6 @@ export class HassElement extends ext(HassBaseEl, [
NotificationMixin,
dialogManagerMixin,
urlSyncMixin,
ZHADialogMixin,
hapticMixin,
panelTitleMixin,
]) {}

View File

@ -1,30 +0,0 @@
import { UpdatingElement } from "lit-element";
import { HASSDomEvent } from "../common/dom/fire_event";
import {
showZHADeviceInfoDialog,
ZHADeviceInfoDialogParams,
} from "../dialogs/zha-device-info-dialog/show-dialog-zha-device-info";
import { Constructor } from "../types";
import { HassBaseEl } from "./hass-base-mixin";
declare global {
// for fire event
interface HASSDomEvents {
"zha-show-device-dialog": {
ieee: string;
};
}
}
export default (superClass: Constructor<UpdatingElement & HassBaseEl>) =>
class extends superClass {
protected firstUpdated(changedProps) {
super.firstUpdated(changedProps);
this.addEventListener("zha-show-device-dialog", (e) =>
showZHADeviceInfoDialog(
e.target as HTMLElement,
(e as HASSDomEvent<ZHADeviceInfoDialogParams>).detail
)
);
}
};

View File

@ -441,10 +441,10 @@
"no_area": "No Area",
"device_signature": "Zigbee device signature",
"buttons": {
"add": "Add Devices",
"add": "Add Devices via this device",
"remove": "Remove Device",
"reconfigure": "Reconfigure Device",
"zigbee_information": "Zigbee Information"
"zigbee_information": "Zigbee device signature"
},
"services": {
"reconfigure": "Reconfigure ZHA device (heal device). Use this if you are having issues with the device. If the device in question is a battery powered device please ensure it is awake and accepting commands when you use this service.",
@ -460,9 +460,7 @@
"power_source": "Power Source",
"unknown": "Unknown",
"zha_device_card": {
"device_name_placeholder": "User given name",
"area_picker_label": "Area",
"update_name_button": "Update Name"
"device_name_placeholder": "Change device name"
}
},
"domain_toggler": {
@ -1504,6 +1502,7 @@
}
},
"zha": {
"button": "Configure",
"header": "Configure Zigbee Home Automation",
"introduction": "Here it is possible to configure the ZHA component. Not everything is possible to configure from the UI yet, but we're working on it.",
"description": "Zigbee Home Automation network management",
@ -1515,9 +1514,10 @@
"value": "Value"
},
"add_device_page": {
"header": "Zigbee Home Automation - Add Devices",
"spinner": "Searching for ZHA Zigbee devices...",
"discovery_text": "Discovered devices will show up here. Follow the instructions for your device(s) and place the device(s) in pairing mode.",
"pairing_mode": "Make sure your devices are in pairing mode. Check the instructions of your device on how to do this.",
"discovered_text": "Devices will show up here once discovered.",
"no_devices_found": "No devices where found, make sure they are in paring mode and keep them awake while discovering is running.",
"search_again": "Search Again"
},
"network_management": {
@ -1560,6 +1560,9 @@
"issue_zigbee_command": "Issue Zigbee Command",
"help_command_dropdown": "Select a command to interact with."
},
"network": {
"caption": "Network"
},
"groups": {
"caption": "Groups",
"description": "Create and modify Zigbee groups",
@ -1600,6 +1603,7 @@
}
},
"zwave": {
"button": "Configure",
"description": "Manage your Z-Wave network",
"learn_more": "Learn more about Z-Wave",
"common": {