Merge branch 'dev' into entity_registry_mass_units
This commit is contained in:
commit
a9a69a8995
|
@ -10,7 +10,7 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: 90 days stale policy
|
||||
uses: actions/stale@v5.2.0
|
||||
uses: actions/stale@v6.0.0
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
days-before-stale: 90
|
||||
|
|
|
@ -68,6 +68,7 @@ class HaDemo extends HomeAssistantAppEl {
|
|||
hidden_by: null,
|
||||
entity_category: null,
|
||||
has_entity_name: false,
|
||||
unique_id: "co2_intensity",
|
||||
},
|
||||
{
|
||||
config_entry_id: "co2signal",
|
||||
|
@ -82,6 +83,7 @@ class HaDemo extends HomeAssistantAppEl {
|
|||
hidden_by: null,
|
||||
entity_category: null,
|
||||
has_entity_name: false,
|
||||
unique_id: "grid_fossil_fuel_percentage",
|
||||
},
|
||||
]);
|
||||
|
||||
|
|
|
@ -11,14 +11,12 @@ export const mockEnergy = (hass: MockHomeAssistant) => {
|
|||
{
|
||||
stat_energy_from: "sensor.energy_consumption_tarif_1",
|
||||
stat_cost: "sensor.energy_consumption_tarif_1_cost",
|
||||
entity_energy_from: "sensor.energy_consumption_tarif_1",
|
||||
entity_energy_price: null,
|
||||
number_energy_price: null,
|
||||
},
|
||||
{
|
||||
stat_energy_from: "sensor.energy_consumption_tarif_2",
|
||||
stat_cost: "sensor.energy_consumption_tarif_2_cost",
|
||||
entity_energy_from: "sensor.energy_consumption_tarif_2",
|
||||
entity_energy_price: null,
|
||||
number_energy_price: null,
|
||||
},
|
||||
|
@ -27,14 +25,12 @@ export const mockEnergy = (hass: MockHomeAssistant) => {
|
|||
{
|
||||
stat_energy_to: "sensor.energy_production_tarif_1",
|
||||
stat_compensation: "sensor.energy_production_tarif_1_compensation",
|
||||
entity_energy_to: "sensor.energy_production_tarif_1",
|
||||
entity_energy_price: null,
|
||||
number_energy_price: null,
|
||||
},
|
||||
{
|
||||
stat_energy_to: "sensor.energy_production_tarif_2",
|
||||
stat_compensation: "sensor.energy_production_tarif_2_compensation",
|
||||
entity_energy_to: "sensor.energy_production_tarif_2",
|
||||
entity_energy_price: null,
|
||||
number_energy_price: null,
|
||||
},
|
||||
|
@ -55,7 +51,6 @@ export const mockEnergy = (hass: MockHomeAssistant) => {
|
|||
type: "gas",
|
||||
stat_energy_from: "sensor.energy_gas",
|
||||
stat_cost: "sensor.energy_gas_cost",
|
||||
entity_energy_from: "sensor.energy_gas",
|
||||
entity_energy_price: null,
|
||||
number_energy_price: null,
|
||||
},
|
||||
|
|
|
@ -196,6 +196,7 @@ const createEntityRegistryEntries = (
|
|||
icon: null,
|
||||
platform: "updater",
|
||||
has_entity_name: false,
|
||||
unique_id: "updater",
|
||||
},
|
||||
];
|
||||
|
||||
|
|
|
@ -46,6 +46,14 @@ frontend:
|
|||
# development_repo: ${WD}" >> "${WD}/config/configuration.yaml"
|
||||
fi
|
||||
|
||||
if [ ! -z "${CODESPACES}" ]; then
|
||||
echo "
|
||||
http:
|
||||
use_x_forwarded_for: true
|
||||
trusted_proxies:
|
||||
- 127.0.0.1
|
||||
" >> "${WD}/config/configuration.yaml"
|
||||
fi
|
||||
fi
|
||||
|
||||
hass -c "${WD}/config"
|
||||
|
|
|
@ -41,7 +41,7 @@ export class HaSelectSelector extends LitElement {
|
|||
);
|
||||
|
||||
if (!this.selector.select.custom_value && this._mode === "list") {
|
||||
if (!this.selector.select.multiple || this.required) {
|
||||
if (!this.selector.select.multiple) {
|
||||
return html`
|
||||
<div>
|
||||
${this.label}
|
||||
|
@ -64,7 +64,8 @@ export class HaSelectSelector extends LitElement {
|
|||
|
||||
return html`
|
||||
<div>
|
||||
${this.label}${options.map(
|
||||
${this.label}
|
||||
${options.map(
|
||||
(item: SelectOption) => html`
|
||||
<ha-formfield .label=${item.label}>
|
||||
<ha-checkbox
|
||||
|
@ -114,7 +115,7 @@ export class HaSelectSelector extends LitElement {
|
|||
.required=${this.required && !value.length}
|
||||
.value=${this._filter}
|
||||
.items=${options.filter(
|
||||
(item) => !item.disabled && !this.value?.includes(item.value)
|
||||
(option) => !option.disabled && !value?.includes(option.value)
|
||||
)}
|
||||
@filter-changed=${this._filterChanged}
|
||||
@value-changed=${this._comboBoxValueChanged}
|
||||
|
@ -290,6 +291,9 @@ export class HaSelectSelector extends LitElement {
|
|||
ha-formfield {
|
||||
display: block;
|
||||
}
|
||||
mwc-list-item[disabled] {
|
||||
--mdc-theme-text-primary-on-background: var(--disabled-text-color);
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
||||
|
|
|
@ -29,7 +29,6 @@ export const emptyFlowFromGridSourceEnergyPreference =
|
|||
(): FlowFromGridSourceEnergyPreference => ({
|
||||
stat_energy_from: "",
|
||||
stat_cost: null,
|
||||
entity_energy_from: null,
|
||||
entity_energy_price: null,
|
||||
number_energy_price: null,
|
||||
});
|
||||
|
@ -38,7 +37,6 @@ export const emptyFlowToGridSourceEnergyPreference =
|
|||
(): FlowToGridSourceEnergyPreference => ({
|
||||
stat_energy_to: "",
|
||||
stat_compensation: null,
|
||||
entity_energy_to: null,
|
||||
entity_energy_price: null,
|
||||
number_energy_price: null,
|
||||
});
|
||||
|
@ -68,7 +66,6 @@ export const emptyGasEnergyPreference = (): GasSourceTypeEnergyPreference => ({
|
|||
type: "gas",
|
||||
stat_energy_from: "",
|
||||
stat_cost: null,
|
||||
entity_energy_from: null,
|
||||
entity_energy_price: null,
|
||||
number_energy_price: null,
|
||||
});
|
||||
|
@ -93,7 +90,6 @@ export interface FlowFromGridSourceEnergyPreference {
|
|||
stat_cost: string | null;
|
||||
|
||||
// Can be used to generate costs if stat_cost omitted
|
||||
entity_energy_from: string | null;
|
||||
entity_energy_price: string | null;
|
||||
number_energy_price: number | null;
|
||||
}
|
||||
|
@ -105,8 +101,7 @@ export interface FlowToGridSourceEnergyPreference {
|
|||
// $ meter
|
||||
stat_compensation: string | null;
|
||||
|
||||
// Can be used to generate costs if stat_cost omitted
|
||||
entity_energy_to: string | null;
|
||||
// Can be used to generate costs if stat_compensation omitted
|
||||
entity_energy_price: string | null;
|
||||
number_energy_price: number | null;
|
||||
}
|
||||
|
@ -142,7 +137,6 @@ export interface GasSourceTypeEnergyPreference {
|
|||
stat_cost: string | null;
|
||||
|
||||
// Can be used to generate costs if stat_cost omitted
|
||||
entity_energy_from: string | null;
|
||||
entity_energy_price: string | null;
|
||||
number_energy_price: number | null;
|
||||
unit_of_measurement?: string | null;
|
||||
|
|
|
@ -20,10 +20,10 @@ export interface EntityRegistryEntry {
|
|||
entity_category: "config" | "diagnostic" | null;
|
||||
has_entity_name: boolean;
|
||||
original_name?: string;
|
||||
unique_id: string;
|
||||
}
|
||||
|
||||
export interface ExtEntityRegistryEntry extends EntityRegistryEntry {
|
||||
unique_id: string;
|
||||
capabilities: Record<string, unknown>;
|
||||
original_icon?: string;
|
||||
device_class?: string;
|
||||
|
@ -61,7 +61,7 @@ export interface EntityRegistryEntryUpdateParams {
|
|||
hidden_by: string | null;
|
||||
new_entity_id?: string;
|
||||
options_domain?: string;
|
||||
options?: SensorEntityOptions | WeatherEntityOptions;
|
||||
options?: SensorEntityOptions | NumberEntityOptions | WeatherEntityOptions;
|
||||
}
|
||||
|
||||
export const findBatteryEntity = (
|
||||
|
|
|
@ -93,6 +93,8 @@ export interface LovelaceViewConfig {
|
|||
panel?: boolean;
|
||||
background?: string;
|
||||
visible?: boolean | ShowViewConfig[];
|
||||
subview?: boolean;
|
||||
back_path?: string;
|
||||
}
|
||||
|
||||
export interface LovelaceViewElement extends HTMLElement {
|
||||
|
|
|
@ -309,7 +309,7 @@ export const fetchCommandsForCluster = (
|
|||
cluster_type: clusterType,
|
||||
});
|
||||
|
||||
export const fetchClustersForZhaNode = (
|
||||
export const fetchClustersForZhaDevice = (
|
||||
hass: HomeAssistant,
|
||||
ieeeAddress: string
|
||||
): Promise<Cluster[]> =>
|
||||
|
|
|
@ -90,7 +90,7 @@ export class MoreInfoDialog extends LitElement {
|
|||
const stateObj = this.hass.states[entityId];
|
||||
|
||||
const domain = computeDomain(entityId);
|
||||
const name = stateObj ? computeStateName(stateObj) : entityId;
|
||||
const name = (stateObj && computeStateName(stateObj)) || entityId;
|
||||
const tabs = this._getTabs(entityId, this.hass.user!.is_admin);
|
||||
|
||||
return html`
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
import "@material/mwc-button";
|
||||
import "@material/mwc-list/mwc-list-item";
|
||||
import { mdiOpenInNew } from "@mdi/js";
|
||||
import { ComboBoxLitRenderer } from "@vaadin/combo-box/lit";
|
||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { ComboBoxLitRenderer } from "@vaadin/combo-box/lit";
|
||||
import { fireEvent } from "../../../common/dom/fire_event";
|
||||
import "../../../components/ha-circular-progress";
|
||||
import "../../../components/ha-combo-box";
|
||||
|
@ -10,14 +11,15 @@ import { createCloseHeading } from "../../../components/ha-dialog";
|
|||
import "../../../components/ha-markdown";
|
||||
import "../../../components/ha-textfield";
|
||||
import {
|
||||
fetchApplicationCredentialsConfig,
|
||||
createApplicationCredential,
|
||||
ApplicationCredentialsConfig,
|
||||
ApplicationCredential,
|
||||
ApplicationCredentialsConfig,
|
||||
createApplicationCredential,
|
||||
fetchApplicationCredentialsConfig,
|
||||
} from "../../../data/application_credential";
|
||||
import { domainToName } from "../../../data/integration";
|
||||
import { haStyleDialog } from "../../../resources/styles";
|
||||
import { HomeAssistant } from "../../../types";
|
||||
import { documentationUrl } from "../../../util/documentation-url";
|
||||
import { AddApplicationCredentialDialogParams } from "./show-dialog-add-application-credential";
|
||||
|
||||
interface Domain {
|
||||
|
@ -98,6 +100,25 @@ export class DialogAddApplicationCredential extends LitElement {
|
|||
>
|
||||
<div>
|
||||
${this._error ? html` <div class="error">${this._error}</div> ` : ""}
|
||||
<p>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.application_credentials.editor.description"
|
||||
)}
|
||||
<br />
|
||||
<a
|
||||
href=${documentationUrl(
|
||||
this.hass!,
|
||||
"/integrations/application_credentials"
|
||||
)}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
${this.hass!.localize(
|
||||
"ui.panel.config.application_credentials.editor.view_documentation"
|
||||
)}
|
||||
<ha-svg-icon .path=${mdiOpenInNew}></ha-svg-icon>
|
||||
</a>
|
||||
</p>
|
||||
<ha-combo-box
|
||||
name="domain"
|
||||
.hass=${this.hass}
|
||||
|
@ -143,6 +164,10 @@ export class DialogAddApplicationCredential extends LitElement {
|
|||
@input=${this._handleValueChanged}
|
||||
error-message=${this.hass.localize("ui.common.error_required")}
|
||||
dialogInitialFocus
|
||||
.helper=${this.hass.localize(
|
||||
"ui.panel.config.application_credentials.editor.client_id_helper"
|
||||
)}
|
||||
helperPersistent
|
||||
></ha-textfield>
|
||||
<ha-textfield
|
||||
.label=${this.hass.localize(
|
||||
|
@ -154,6 +179,10 @@ export class DialogAddApplicationCredential extends LitElement {
|
|||
required
|
||||
@input=${this._handleValueChanged}
|
||||
error-message=${this.hass.localize("ui.common.error_required")}
|
||||
.helper=${this.hass.localize(
|
||||
"ui.panel.config.application_credentials.editor.client_secret_helper"
|
||||
)}
|
||||
helperPersistent
|
||||
></ha-textfield>
|
||||
</div>
|
||||
${this._loading
|
||||
|
@ -163,15 +192,18 @@ export class DialogAddApplicationCredential extends LitElement {
|
|||
</div>
|
||||
`
|
||||
: html`
|
||||
<mwc-button slot="primaryAction" @click=${this._abortDialog}>
|
||||
${this.hass.localize("ui.common.cancel")}
|
||||
</mwc-button>
|
||||
<mwc-button
|
||||
slot="primaryAction"
|
||||
.disabled=${!this._domain ||
|
||||
!this._clientId ||
|
||||
!this._clientSecret}
|
||||
@click=${this._createApplicationCredential}
|
||||
@click=${this._addApplicationCredential}
|
||||
>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.application_credentials.editor.create"
|
||||
"ui.panel.config.application_credentials.editor.add"
|
||||
)}
|
||||
</mwc-button>
|
||||
`}
|
||||
|
@ -213,7 +245,7 @@ export class DialogAddApplicationCredential extends LitElement {
|
|||
this.closeDialog();
|
||||
}
|
||||
|
||||
private async _createApplicationCredential(ev) {
|
||||
private async _addApplicationCredential(ev) {
|
||||
ev.preventDefault();
|
||||
if (!this._domain || !this._clientId || !this._clientSecret) {
|
||||
return;
|
||||
|
@ -260,6 +292,12 @@ export class DialogAddApplicationCredential extends LitElement {
|
|||
display: block;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
a {
|
||||
text-decoration: none;
|
||||
}
|
||||
a ha-svg-icon {
|
||||
--mdc-icon-size: 16px;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
|
|
|
@ -575,10 +575,15 @@ export class HaAutomationEditor extends KeyboardShortcutMixin(LitElement) {
|
|||
|
||||
private async _deleteConfirm() {
|
||||
showConfirmationDialog(this, {
|
||||
title: this.hass.localize(
|
||||
"ui.panel.config.automation.picker.delete_confirm_title"
|
||||
),
|
||||
text: this.hass.localize(
|
||||
"ui.panel.config.automation.picker.delete_confirm"
|
||||
"ui.panel.config.automation.picker.delete_confirm_text",
|
||||
{ name: this._config?.alias }
|
||||
),
|
||||
confirmText: this.hass!.localize("ui.common.delete"),
|
||||
destructive: true,
|
||||
dismissText: this.hass!.localize("ui.common.cancel"),
|
||||
confirm: () => this._delete(),
|
||||
});
|
||||
|
|
|
@ -341,12 +341,17 @@ class HaAutomationPicker extends LitElement {
|
|||
|
||||
private async _deleteConfirm(automation) {
|
||||
showConfirmationDialog(this, {
|
||||
title: this.hass.localize(
|
||||
"ui.panel.config.automation.picker.delete_confirm_title"
|
||||
),
|
||||
text: this.hass.localize(
|
||||
"ui.panel.config.automation.picker.delete_confirm"
|
||||
"ui.panel.config.automation.picker.delete_confirm_text",
|
||||
{ name: automation.name }
|
||||
),
|
||||
confirmText: this.hass!.localize("ui.common.delete"),
|
||||
dismissText: this.hass!.localize("ui.common.cancel"),
|
||||
confirm: () => this._delete(automation),
|
||||
destructive: true,
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import "@material/mwc-button";
|
||||
import { mdiOpenInNew } from "@mdi/js";
|
||||
import { css, html, LitElement, TemplateResult } from "lit";
|
||||
import { customElement, property, query, state } from "lit/decorators";
|
||||
import { fireEvent } from "../../../common/dom/fire_event";
|
||||
|
@ -105,50 +106,61 @@ class DialogImportBlueprint extends LitElement {
|
|||
>
|
||||
<pre>${this._result.raw_data}</pre>
|
||||
</ha-expansion-panel>`
|
||||
: html`${this.hass.localize(
|
||||
"ui.panel.config.blueprint.add.import_introduction_link",
|
||||
"community_link",
|
||||
html`<a
|
||||
href="https://www.home-assistant.io/get-blueprints"
|
||||
target="_blank"
|
||||
rel="noreferrer noopener"
|
||||
>${this.hass.localize(
|
||||
"ui.panel.config.blueprint.add.community_forums"
|
||||
)}</a
|
||||
>`
|
||||
)}<ha-textfield
|
||||
: html`
|
||||
<p>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.blueprint.add.import_introduction"
|
||||
)}
|
||||
</p>
|
||||
<a
|
||||
href="https://www.home-assistant.io/get-blueprints"
|
||||
target="_blank"
|
||||
rel="noreferrer noopener"
|
||||
>
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.blueprint.add.community_forums"
|
||||
)}
|
||||
<ha-svg-icon .path=${mdiOpenInNew}></ha-svg-icon>
|
||||
</a>
|
||||
<ha-textfield
|
||||
id="input"
|
||||
.label=${this.hass.localize(
|
||||
"ui.panel.config.blueprint.add.url"
|
||||
)}
|
||||
.value=${this._url || ""}
|
||||
dialogInitialFocus
|
||||
></ha-textfield>`}
|
||||
></ha-textfield>
|
||||
`}
|
||||
</div>
|
||||
<mwc-button
|
||||
slot="primaryAction"
|
||||
@click=${this.closeDialog}
|
||||
.disabled=${this._saving}
|
||||
>
|
||||
${this.hass.localize("ui.common.cancel")}
|
||||
</mwc-button>
|
||||
${!this._result
|
||||
? html`<mwc-button
|
||||
slot="primaryAction"
|
||||
@click=${this._import}
|
||||
.disabled=${this._importing}
|
||||
>
|
||||
${this._importing
|
||||
? html`<ha-circular-progress
|
||||
active
|
||||
size="small"
|
||||
.title=${this.hass.localize(
|
||||
"ui.panel.config.blueprint.add.importing"
|
||||
)}
|
||||
></ha-circular-progress>`
|
||||
: ""}
|
||||
${this.hass.localize("ui.panel.config.blueprint.add.import_btn")}
|
||||
</mwc-button>`
|
||||
: html`<mwc-button
|
||||
slot="secondaryAction"
|
||||
@click=${this.closeDialog}
|
||||
.disabled=${this._saving}
|
||||
? html`
|
||||
<mwc-button
|
||||
slot="primaryAction"
|
||||
@click=${this._import}
|
||||
.disabled=${this._importing}
|
||||
>
|
||||
${this.hass.localize("ui.common.cancel")}
|
||||
${this._importing
|
||||
? html`<ha-circular-progress
|
||||
active
|
||||
size="small"
|
||||
.title=${this.hass.localize(
|
||||
"ui.panel.config.blueprint.add.importing"
|
||||
)}
|
||||
></ha-circular-progress>`
|
||||
: ""}
|
||||
${this.hass.localize(
|
||||
"ui.panel.config.blueprint.add.import_btn"
|
||||
)}
|
||||
</mwc-button>
|
||||
`
|
||||
: html`
|
||||
<mwc-button
|
||||
slot="primaryAction"
|
||||
@click=${this._save}
|
||||
|
@ -164,7 +176,8 @@ class DialogImportBlueprint extends LitElement {
|
|||
></ha-circular-progress>`
|
||||
: ""}
|
||||
${this.hass.localize("ui.panel.config.blueprint.add.save_btn")}
|
||||
</mwc-button>`}
|
||||
</mwc-button>
|
||||
`}
|
||||
</ha-dialog>
|
||||
`;
|
||||
}
|
||||
|
@ -215,9 +228,19 @@ class DialogImportBlueprint extends LitElement {
|
|||
static styles = [
|
||||
haStyleDialog,
|
||||
css`
|
||||
p {
|
||||
margin-top: 0;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
ha-textfield {
|
||||
display: block;
|
||||
margin-top: 8px;
|
||||
margin-top: 24px;
|
||||
}
|
||||
a {
|
||||
text-decoration: none;
|
||||
}
|
||||
a ha-svg-icon {
|
||||
--mdc-icon-size: 16px;
|
||||
}
|
||||
`,
|
||||
];
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
import {
|
||||
mdiCogRefresh,
|
||||
mdiDelete,
|
||||
mdiDrawPen,
|
||||
mdiFamilyTree,
|
||||
mdiFileTree,
|
||||
mdiGroup,
|
||||
mdiPlus,
|
||||
} from "@mdi/js";
|
||||
|
@ -12,9 +10,7 @@ import type { DeviceRegistryEntry } from "../../../../../../data/device_registry
|
|||
import { fetchZHADevice } from "../../../../../../data/zha";
|
||||
import { showConfirmationDialog } from "../../../../../../dialogs/generic/show-dialog-box";
|
||||
import type { HomeAssistant } from "../../../../../../types";
|
||||
import { showZHAClusterDialog } from "../../../../integrations/integration-panels/zha/show-dialog-zha-cluster";
|
||||
import { showZHADeviceChildrenDialog } from "../../../../integrations/integration-panels/zha/show-dialog-zha-device-children";
|
||||
import { showZHADeviceZigbeeInfoDialog } from "../../../../integrations/integration-panels/zha/show-dialog-zha-device-zigbee-info";
|
||||
import { showZHAManageZigbeeDeviceDialog } from "../../../../integrations/integration-panels/zha/show-dialog-zha-manage-zigbee-device";
|
||||
import { showZHAReconfigureDeviceDialog } from "../../../../integrations/integration-panels/zha/show-dialog-zha-reconfigure-device";
|
||||
import type { DeviceAction } from "../../../ha-config-device-page";
|
||||
|
||||
|
@ -59,13 +55,6 @@ export const getZHADeviceActions = async (
|
|||
icon: mdiPlus,
|
||||
action: () => navigate(`/config/zha/add/${zhaDevice!.ieee}`),
|
||||
},
|
||||
{
|
||||
label: hass.localize(
|
||||
"ui.dialogs.zha_device_info.buttons.device_children"
|
||||
),
|
||||
icon: mdiFileTree,
|
||||
action: () => showZHADeviceChildrenDialog(el, { device: zhaDevice! }),
|
||||
},
|
||||
]
|
||||
);
|
||||
}
|
||||
|
@ -73,16 +62,10 @@ export const getZHADeviceActions = async (
|
|||
actions.push(
|
||||
...[
|
||||
{
|
||||
label: hass.localize(
|
||||
"ui.dialogs.zha_device_info.buttons.zigbee_information"
|
||||
),
|
||||
icon: mdiDrawPen,
|
||||
action: () => showZHADeviceZigbeeInfoDialog(el, { device: zhaDevice }),
|
||||
},
|
||||
{
|
||||
label: hass.localize("ui.dialogs.zha_device_info.buttons.clusters"),
|
||||
label: hass.localize("ui.dialogs.zha_device_info.buttons.manage"),
|
||||
icon: mdiGroup,
|
||||
action: () => showZHAClusterDialog(el, { device: zhaDevice }),
|
||||
action: () =>
|
||||
showZHAManageZigbeeDeviceDialog(el, { device: zhaDevice }),
|
||||
},
|
||||
{
|
||||
label: hass.localize("ui.dialogs.zha_device_info.buttons.view_network"),
|
||||
|
|
|
@ -23,6 +23,7 @@ export class HaDeviceActionsZha extends LitElement {
|
|||
@state() private _zhaDevice?: ZHADevice;
|
||||
|
||||
protected updated(changedProperties: PropertyValues) {
|
||||
super.updated(changedProperties);
|
||||
if (changedProperties.has("device")) {
|
||||
const zigbeeConnection = this.device.connections.find(
|
||||
(conn) => conn[0] === "zigbee"
|
||||
|
|
|
@ -280,7 +280,6 @@ export class DialogEnergyGasSettings
|
|||
this._source = {
|
||||
...this._source!,
|
||||
stat_energy_from: ev.detail.value,
|
||||
entity_energy_from: ev.detail.value,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -269,9 +269,6 @@ export class DialogEnergyGridFlowSettings
|
|||
[this._params!.direction === "from"
|
||||
? "stat_energy_from"
|
||||
: "stat_energy_to"]: ev.detail.value,
|
||||
[this._params!.direction === "from"
|
||||
? "entity_energy_from"
|
||||
: "entity_energy_to"]: ev.detail.value,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -111,9 +111,12 @@ const OVERRIDE_NUMBER_UNITS = {
|
|||
};
|
||||
|
||||
const OVERRIDE_SENSOR_UNITS = {
|
||||
distance: ["cm", "ft", "in", "km", "m", "mi", "mm", "yd"],
|
||||
mass: ["g", "kg", "lb", "mg", "oz", "µg"],
|
||||
pressure: ["hPa", "Pa", "kPa", "bar", "cbar", "mbar", "mmHg", "inHg", "psi"],
|
||||
speed: ["ft/s", "in/d", "in/h", "km/h", "kn", "m/s", "mm/d", "mph"],
|
||||
temperature: ["°C", "°F", "K"],
|
||||
volume: ["fl. oz.", "ft³", "gal", "L", "mL", "m³"],
|
||||
};
|
||||
|
||||
const OVERRIDE_WEATHER_UNITS = {
|
||||
|
|
|
@ -68,10 +68,12 @@ import type { HomeAssistant, Route } from "../../../types";
|
|||
import { configSections } from "../ha-panel-config";
|
||||
import "../integrations/ha-integration-overflow-menu";
|
||||
|
||||
export interface StateEntity extends Omit<EntityRegistryEntry, "id"> {
|
||||
export interface StateEntity
|
||||
extends Omit<EntityRegistryEntry, "id" | "unique_id"> {
|
||||
readonly?: boolean;
|
||||
selectable?: boolean;
|
||||
id?: string;
|
||||
unique_id?: string;
|
||||
}
|
||||
|
||||
export interface EntityRow extends StateEntity {
|
||||
|
|
|
@ -1,142 +0,0 @@
|
|||
import {
|
||||
CSSResultGroup,
|
||||
html,
|
||||
LitElement,
|
||||
PropertyValues,
|
||||
TemplateResult,
|
||||
} from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { HASSDomEvent } from "../../../../../common/dom/fire_event";
|
||||
import "../../../../../components/ha-code-editor";
|
||||
import { createCloseHeading } from "../../../../../components/ha-dialog";
|
||||
import {
|
||||
Cluster,
|
||||
fetchBindableDevices,
|
||||
fetchGroups,
|
||||
ZHADevice,
|
||||
ZHAGroup,
|
||||
} from "../../../../../data/zha";
|
||||
import { haStyleDialog } from "../../../../../resources/styles";
|
||||
import { HomeAssistant } from "../../../../../types";
|
||||
import { sortZHADevices, sortZHAGroups } from "./functions";
|
||||
import { ZHADeviceZigbeeInfoDialogParams } from "./show-dialog-zha-device-zigbee-info";
|
||||
import { ZHAClusterSelectedParams } from "./types";
|
||||
import "./zha-cluster-attributes";
|
||||
import "./zha-cluster-commands";
|
||||
import "./zha-clusters";
|
||||
import "./zha-device-binding";
|
||||
import "./zha-group-binding";
|
||||
|
||||
@customElement("dialog-zha-cluster")
|
||||
class DialogZHACluster extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@state() private _device?: ZHADevice;
|
||||
|
||||
@state() private _selectedCluster?: Cluster;
|
||||
|
||||
@state() private _bindableDevices: ZHADevice[] = [];
|
||||
|
||||
@state() private _groups: ZHAGroup[] = [];
|
||||
|
||||
public async showDialog(
|
||||
params: ZHADeviceZigbeeInfoDialogParams
|
||||
): Promise<void> {
|
||||
this._device = params.device;
|
||||
}
|
||||
|
||||
protected updated(changedProperties: PropertyValues): void {
|
||||
super.update(changedProperties);
|
||||
if (changedProperties.has("_device")) {
|
||||
this._fetchData();
|
||||
}
|
||||
}
|
||||
|
||||
protected render(): TemplateResult {
|
||||
if (!this._device) {
|
||||
return html``;
|
||||
}
|
||||
|
||||
return html`
|
||||
<ha-dialog
|
||||
open
|
||||
hideActions
|
||||
@closed=${this._close}
|
||||
.heading=${createCloseHeading(
|
||||
this.hass,
|
||||
this.hass.localize("ui.panel.config.zha.clusters.header")
|
||||
)}
|
||||
>
|
||||
<zha-clusters
|
||||
.hass=${this.hass}
|
||||
.selectedDevice=${this._device}
|
||||
@zha-cluster-selected=${this._onClusterSelected}
|
||||
></zha-clusters>
|
||||
${this._selectedCluster
|
||||
? html`
|
||||
<zha-cluster-attributes
|
||||
.hass=${this.hass}
|
||||
.selectedNode=${this._device}
|
||||
.selectedCluster=${this._selectedCluster}
|
||||
></zha-cluster-attributes>
|
||||
<zha-cluster-commands
|
||||
.hass=${this.hass}
|
||||
.selectedNode=${this._device}
|
||||
.selectedCluster=${this._selectedCluster}
|
||||
></zha-cluster-commands>
|
||||
`
|
||||
: ""}
|
||||
${this._bindableDevices.length > 0
|
||||
? html`
|
||||
<zha-device-binding-control
|
||||
.hass=${this.hass}
|
||||
.selectedDevice=${this._device}
|
||||
.bindableDevices=${this._bindableDevices}
|
||||
></zha-device-binding-control>
|
||||
`
|
||||
: ""}
|
||||
${this._device && this._groups.length > 0
|
||||
? html`
|
||||
<zha-group-binding-control
|
||||
.hass=${this.hass}
|
||||
.selectedDevice=${this._device}
|
||||
.groups=${this._groups}
|
||||
></zha-group-binding-control>
|
||||
`
|
||||
: ""}
|
||||
</ha-dialog>
|
||||
`;
|
||||
}
|
||||
|
||||
private _onClusterSelected(
|
||||
selectedClusterEvent: HASSDomEvent<ZHAClusterSelectedParams>
|
||||
): void {
|
||||
this._selectedCluster = selectedClusterEvent.detail.cluster;
|
||||
}
|
||||
|
||||
private _close(): void {
|
||||
this._device = undefined;
|
||||
}
|
||||
|
||||
private async _fetchData(): Promise<void> {
|
||||
if (this._device && this.hass) {
|
||||
this._bindableDevices =
|
||||
this._device && this._device.device_type !== "Coordinator"
|
||||
? (await fetchBindableDevices(this.hass, this._device.ieee)).sort(
|
||||
sortZHADevices
|
||||
)
|
||||
: [];
|
||||
this._groups = (await fetchGroups(this.hass!)).sort(sortZHAGroups);
|
||||
}
|
||||
}
|
||||
|
||||
static get styles(): CSSResultGroup {
|
||||
return haStyleDialog;
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"dialog-zha-cluster": DialogZHACluster;
|
||||
}
|
||||
}
|
|
@ -1,69 +0,0 @@
|
|||
import { CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import "../../../../../components/ha-code-editor";
|
||||
import { createCloseHeading } from "../../../../../components/ha-dialog";
|
||||
import { haStyleDialog } from "../../../../../resources/styles";
|
||||
import { HomeAssistant } from "../../../../../types";
|
||||
import { ZHADeviceZigbeeInfoDialogParams } from "./show-dialog-zha-device-zigbee-info";
|
||||
|
||||
@customElement("dialog-zha-device-zigbee-info")
|
||||
class DialogZHADeviceZigbeeInfo extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@state() private _signature: any;
|
||||
|
||||
public async showDialog(
|
||||
params: ZHADeviceZigbeeInfoDialogParams
|
||||
): Promise<void> {
|
||||
this._signature = JSON.stringify(
|
||||
{
|
||||
...params.device.signature,
|
||||
manufacturer: params.device.manufacturer,
|
||||
model: params.device.model,
|
||||
class: params.device.quirk_class,
|
||||
},
|
||||
null,
|
||||
2
|
||||
);
|
||||
}
|
||||
|
||||
protected render(): TemplateResult {
|
||||
if (!this._signature) {
|
||||
return html``;
|
||||
}
|
||||
|
||||
return html`
|
||||
<ha-dialog
|
||||
open
|
||||
hideActions
|
||||
@closed=${this._close}
|
||||
.heading=${createCloseHeading(
|
||||
this.hass,
|
||||
this.hass.localize(`ui.dialogs.zha_device_info.device_signature`)
|
||||
)}
|
||||
>
|
||||
<ha-code-editor
|
||||
mode="yaml"
|
||||
readOnly
|
||||
.value=${this._signature}
|
||||
dir="ltr"
|
||||
>
|
||||
</ha-code-editor>
|
||||
</ha-dialog>
|
||||
`;
|
||||
}
|
||||
|
||||
private _close(): void {
|
||||
this._signature = undefined;
|
||||
}
|
||||
|
||||
static get styles(): CSSResultGroup {
|
||||
return haStyleDialog;
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"dialog-zha-device-zigbee-info": DialogZHADeviceZigbeeInfo;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,291 @@
|
|||
import {
|
||||
css,
|
||||
CSSResultGroup,
|
||||
html,
|
||||
LitElement,
|
||||
PropertyValues,
|
||||
TemplateResult,
|
||||
} from "lit";
|
||||
import { mdiClose } from "@mdi/js";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { cache } from "lit/directives/cache";
|
||||
import memoizeOne from "memoize-one";
|
||||
import { fireEvent } from "../../../../../common/dom/fire_event";
|
||||
import "../../../../../components/ha-code-editor";
|
||||
import { createCloseHeading } from "../../../../../components/ha-dialog";
|
||||
import {
|
||||
fetchBindableDevices,
|
||||
fetchGroups,
|
||||
ZHADevice,
|
||||
ZHAGroup,
|
||||
} from "../../../../../data/zha";
|
||||
import { haStyleDialog } from "../../../../../resources/styles";
|
||||
import { HomeAssistant } from "../../../../../types";
|
||||
import { sortZHADevices, sortZHAGroups } from "./functions";
|
||||
import "./zha-cluster-attributes";
|
||||
import "./zha-cluster-commands";
|
||||
import "./zha-manage-clusters";
|
||||
import "./zha-device-binding";
|
||||
import "./zha-group-binding";
|
||||
import "./zha-device-children";
|
||||
import "./zha-device-signature";
|
||||
import {
|
||||
Tab,
|
||||
ZHAManageZigbeeDeviceDialogParams,
|
||||
} from "./show-dialog-zha-manage-zigbee-device";
|
||||
import "../../../../../components/ha-header-bar";
|
||||
import "@material/mwc-tab-bar/mwc-tab-bar";
|
||||
import "@material/mwc-tab/mwc-tab";
|
||||
|
||||
@customElement("dialog-zha-manage-zigbee-device")
|
||||
class DialogZHAManageZigbeeDevice extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@property({ type: Boolean, reflect: true }) public large = false;
|
||||
|
||||
@state() private _currTab: Tab = "clusters";
|
||||
|
||||
@state() private _device?: ZHADevice;
|
||||
|
||||
@state() private _bindableDevices: ZHADevice[] = [];
|
||||
|
||||
@state() private _groups: ZHAGroup[] = [];
|
||||
|
||||
public async showDialog(
|
||||
params: ZHAManageZigbeeDeviceDialogParams
|
||||
): Promise<void> {
|
||||
this._device = params.device;
|
||||
if (!this._device) {
|
||||
this.closeDialog();
|
||||
return;
|
||||
}
|
||||
this._currTab = params.tab || "clusters";
|
||||
this.large = false;
|
||||
}
|
||||
|
||||
public closeDialog() {
|
||||
this._device = undefined;
|
||||
fireEvent(this, "dialog-closed", { dialog: this.localName });
|
||||
}
|
||||
|
||||
protected firstUpdated(changedProps: PropertyValues) {
|
||||
super.firstUpdated(changedProps);
|
||||
this.addEventListener("close-dialog", () => this.closeDialog());
|
||||
}
|
||||
|
||||
protected willUpdate(changedProps: PropertyValues) {
|
||||
super.willUpdate(changedProps);
|
||||
if (!this._device) {
|
||||
return;
|
||||
}
|
||||
if (changedProps.has("_device")) {
|
||||
const tabs = this._getTabs(this._device);
|
||||
if (!tabs.includes(this._currTab)) {
|
||||
this._currTab = tabs[0];
|
||||
}
|
||||
this._fetchData();
|
||||
}
|
||||
}
|
||||
|
||||
protected render(): TemplateResult {
|
||||
if (!this._device) {
|
||||
return html``;
|
||||
}
|
||||
|
||||
const tabs = this._getTabs(this._device);
|
||||
|
||||
return html`
|
||||
<ha-dialog
|
||||
open
|
||||
hideActions
|
||||
@closed=${this.closeDialog}
|
||||
.heading=${createCloseHeading(
|
||||
this.hass,
|
||||
this.hass.localize("ui.dialogs.zha_manage_device.heading")
|
||||
)}
|
||||
>
|
||||
<div slot="heading" class="heading">
|
||||
<ha-header-bar>
|
||||
<ha-icon-button
|
||||
slot="navigationIcon"
|
||||
dialogAction="cancel"
|
||||
.label=${this.hass.localize(
|
||||
"ui.dialogs.more_info_control.dismiss"
|
||||
)}
|
||||
.path=${mdiClose}
|
||||
></ha-icon-button>
|
||||
<div
|
||||
slot="title"
|
||||
class="main-title"
|
||||
.title=${this.hass.localize(
|
||||
"ui.dialogs.zha_manage_device.heading"
|
||||
)}
|
||||
@click=${this._enlarge}
|
||||
>
|
||||
${this.hass.localize("ui.dialogs.zha_manage_device.heading")}
|
||||
</div>
|
||||
</ha-header-bar>
|
||||
<mwc-tab-bar
|
||||
.activeIndex=${tabs.indexOf(this._currTab)}
|
||||
@MDCTabBar:activated=${this._handleTabChanged}
|
||||
>
|
||||
${tabs.map(
|
||||
(tab) => html`
|
||||
<mwc-tab
|
||||
.label=${this.hass.localize(
|
||||
`ui.dialogs.zha_manage_device.tabs.${tab}`
|
||||
)}
|
||||
></mwc-tab>
|
||||
`
|
||||
)}
|
||||
</mwc-tab-bar>
|
||||
</div>
|
||||
|
||||
<div class="content" tabindex="-1" dialogInitialFocus>
|
||||
${cache(
|
||||
this._currTab === "clusters"
|
||||
? html`
|
||||
<zha-manage-clusters
|
||||
.hass=${this.hass}
|
||||
.device=${this._device}
|
||||
></zha-manage-clusters>
|
||||
`
|
||||
: this._currTab === "bindings"
|
||||
? html`
|
||||
${this._bindableDevices.length > 0
|
||||
? html`
|
||||
<zha-device-binding-control
|
||||
.hass=${this.hass}
|
||||
.device=${this._device}
|
||||
.bindableDevices=${this._bindableDevices}
|
||||
></zha-device-binding-control>
|
||||
`
|
||||
: ""}
|
||||
${this._device && this._groups.length > 0
|
||||
? html`
|
||||
<zha-group-binding-control
|
||||
.hass=${this.hass}
|
||||
.device=${this._device}
|
||||
.groups=${this._groups}
|
||||
></zha-group-binding-control>
|
||||
`
|
||||
: ""}
|
||||
`
|
||||
: this._currTab === "signature"
|
||||
? html`
|
||||
<zha-device-zigbee-info
|
||||
.hass=${this.hass}
|
||||
.device=${this._device}
|
||||
></zha-device-zigbee-info>
|
||||
`
|
||||
: html`
|
||||
<zha-device-children
|
||||
.hass=${this.hass}
|
||||
.device=${this._device}
|
||||
></zha-device-children>
|
||||
`
|
||||
)}
|
||||
</div>
|
||||
</ha-dialog>
|
||||
`;
|
||||
}
|
||||
|
||||
private async _fetchData(): Promise<void> {
|
||||
if (this._device && this.hass) {
|
||||
this._bindableDevices =
|
||||
this._device && this._device.device_type !== "Coordinator"
|
||||
? (await fetchBindableDevices(this.hass, this._device.ieee)).sort(
|
||||
sortZHADevices
|
||||
)
|
||||
: [];
|
||||
this._groups = (await fetchGroups(this.hass!)).sort(sortZHAGroups);
|
||||
}
|
||||
}
|
||||
|
||||
private _enlarge() {
|
||||
this.large = !this.large;
|
||||
}
|
||||
|
||||
private _handleTabChanged(ev: CustomEvent): void {
|
||||
const newTab = this._getTabs(this._device)[ev.detail.index];
|
||||
if (newTab === this._currTab) {
|
||||
return;
|
||||
}
|
||||
this._currTab = newTab;
|
||||
}
|
||||
|
||||
private _getTabs = memoizeOne((device: ZHADevice | undefined) => {
|
||||
const tabs: Tab[] = ["clusters", "bindings", "signature"];
|
||||
|
||||
if (
|
||||
device &&
|
||||
(device.device_type === "Router" || device.device_type === "Coordinator")
|
||||
) {
|
||||
tabs.push("children");
|
||||
}
|
||||
|
||||
return tabs;
|
||||
});
|
||||
|
||||
static get styles(): CSSResultGroup {
|
||||
return [
|
||||
haStyleDialog,
|
||||
css`
|
||||
ha-dialog {
|
||||
--dialog-surface-position: static;
|
||||
--dialog-content-position: static;
|
||||
--vertial-align-dialog: flex-start;
|
||||
}
|
||||
|
||||
ha-header-bar {
|
||||
--mdc-theme-on-primary: var(--primary-text-color);
|
||||
--mdc-theme-primary: var(--mdc-theme-surface);
|
||||
flex-shrink: 0;
|
||||
display: block;
|
||||
}
|
||||
.content {
|
||||
outline: none;
|
||||
}
|
||||
@media all and (max-width: 450px), all and (max-height: 500px) {
|
||||
ha-header-bar {
|
||||
--mdc-theme-primary: var(--app-header-background-color);
|
||||
--mdc-theme-on-primary: var(--app-header-text-color, white);
|
||||
border-bottom: none;
|
||||
}
|
||||
}
|
||||
|
||||
.heading {
|
||||
border-bottom: 1px solid
|
||||
var(--mdc-dialog-scroll-divider-color, rgba(0, 0, 0, 0.12));
|
||||
}
|
||||
|
||||
@media all and (min-width: 600px) and (min-height: 501px) {
|
||||
ha-dialog {
|
||||
--mdc-dialog-min-width: 560px;
|
||||
--mdc-dialog-max-width: 560px;
|
||||
--dialog-surface-margin-top: 40px;
|
||||
--mdc-dialog-max-height: calc(100% - 72px);
|
||||
}
|
||||
|
||||
.main-title {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
:host([large]) ha-dialog,
|
||||
ha-dialog[data-domain="camera"] {
|
||||
--mdc-dialog-min-width: 90vw;
|
||||
--mdc-dialog-max-width: 90vw;
|
||||
}
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"dialog-zha-manage-zigbee-device": DialogZHAManageZigbeeDevice;
|
||||
}
|
||||
}
|
|
@ -12,7 +12,7 @@ import {
|
|||
Cluster,
|
||||
ClusterConfigurationEvent,
|
||||
ClusterConfigurationStatus,
|
||||
fetchClustersForZhaNode,
|
||||
fetchClustersForZhaDevice,
|
||||
reconfigureNode,
|
||||
ZHA_CHANNEL_CFG_DONE,
|
||||
ZHA_CHANNEL_MSG_BIND,
|
||||
|
@ -321,16 +321,16 @@ class DialogZHAReconfigureDevice extends LitElement {
|
|||
return;
|
||||
}
|
||||
this._clusterConfigurationStatuses = new Map(
|
||||
(await fetchClustersForZhaNode(this.hass, this._params.device.ieee)).map(
|
||||
(cluster: Cluster) => [
|
||||
cluster.id,
|
||||
{
|
||||
cluster: cluster,
|
||||
bindSuccess: undefined,
|
||||
attributes: new Map<number, AttributeConfigurationStatus>(),
|
||||
},
|
||||
]
|
||||
)
|
||||
(
|
||||
await fetchClustersForZhaDevice(this.hass, this._params.device.ieee)
|
||||
).map((cluster: Cluster) => [
|
||||
cluster.id,
|
||||
{
|
||||
cluster: cluster,
|
||||
bindSuccess: undefined,
|
||||
attributes: new Map<number, AttributeConfigurationStatus>(),
|
||||
},
|
||||
])
|
||||
);
|
||||
this._subscribe(this._params);
|
||||
this._status = "started";
|
||||
|
|
|
@ -1,19 +0,0 @@
|
|||
import { fireEvent } from "../../../../../common/dom/fire_event";
|
||||
import { ZHADevice } from "../../../../../data/zha";
|
||||
|
||||
export interface ZHAClusterDialogParams {
|
||||
device: ZHADevice;
|
||||
}
|
||||
|
||||
export const loadZHAClusterDialog = () => import("./dialog-zha-cluster");
|
||||
|
||||
export const showZHAClusterDialog = (
|
||||
element: HTMLElement,
|
||||
params: ZHAClusterDialogParams
|
||||
): void => {
|
||||
fireEvent(element, "show-dialog", {
|
||||
dialogTag: "dialog-zha-cluster",
|
||||
dialogImport: loadZHAClusterDialog,
|
||||
dialogParams: params,
|
||||
});
|
||||
};
|
|
@ -1,20 +0,0 @@
|
|||
import { fireEvent } from "../../../../../common/dom/fire_event";
|
||||
import { ZHADevice } from "../../../../../data/zha";
|
||||
|
||||
export interface ZHADeviceChildrenDialogParams {
|
||||
device: ZHADevice;
|
||||
}
|
||||
|
||||
export const loadZHADeviceChildrenDialog = () =>
|
||||
import("./dialog-zha-device-children");
|
||||
|
||||
export const showZHADeviceChildrenDialog = (
|
||||
element: HTMLElement,
|
||||
zhaDeviceChildrenParams: ZHADeviceChildrenDialogParams
|
||||
): void => {
|
||||
fireEvent(element, "show-dialog", {
|
||||
dialogTag: "dialog-zha-device-children",
|
||||
dialogImport: loadZHADeviceChildrenDialog,
|
||||
dialogParams: zhaDeviceChildrenParams,
|
||||
});
|
||||
};
|
|
@ -1,20 +0,0 @@
|
|||
import { fireEvent } from "../../../../../common/dom/fire_event";
|
||||
import { ZHADevice } from "../../../../../data/zha";
|
||||
|
||||
export interface ZHADeviceZigbeeInfoDialogParams {
|
||||
device: ZHADevice;
|
||||
}
|
||||
|
||||
export const loadZHADeviceZigbeeInfoDialog = () =>
|
||||
import("./dialog-zha-device-zigbee-info");
|
||||
|
||||
export const showZHADeviceZigbeeInfoDialog = (
|
||||
element: HTMLElement,
|
||||
zhaDeviceZigbeeInfoParams: ZHADeviceZigbeeInfoDialogParams
|
||||
): void => {
|
||||
fireEvent(element, "show-dialog", {
|
||||
dialogTag: "dialog-zha-device-zigbee-info",
|
||||
dialogImport: loadZHADeviceZigbeeInfoDialog,
|
||||
dialogParams: zhaDeviceZigbeeInfoParams,
|
||||
});
|
||||
};
|
|
@ -0,0 +1,23 @@
|
|||
import { fireEvent } from "../../../../../common/dom/fire_event";
|
||||
import { ZHADevice } from "../../../../../data/zha";
|
||||
|
||||
export type Tab = "clusters" | "bindings" | "signature" | "children";
|
||||
|
||||
export interface ZHAManageZigbeeDeviceDialogParams {
|
||||
device: ZHADevice;
|
||||
tab?: Tab;
|
||||
}
|
||||
|
||||
export const loadZHAManageZigbeeDeviceDialog = () =>
|
||||
import("./dialog-zha-manage-zigbee-device");
|
||||
|
||||
export const showZHAManageZigbeeDeviceDialog = (
|
||||
element: HTMLElement,
|
||||
params: ZHAManageZigbeeDeviceDialogParams
|
||||
): void => {
|
||||
fireEvent(element, "show-dialog", {
|
||||
dialogTag: "dialog-zha-manage-zigbee-device",
|
||||
dialogImport: loadZHAManageZigbeeDeviceDialog,
|
||||
dialogParams: params,
|
||||
});
|
||||
};
|
|
@ -1,5 +1,5 @@
|
|||
import { HaSelect } from "../../../../../components/ha-select";
|
||||
import { Cluster, ZHADevice } from "../../../../../data/zha";
|
||||
import { ZHADevice } from "../../../../../data/zha";
|
||||
|
||||
export interface ItemSelectedEvent {
|
||||
target?: HaSelect;
|
||||
|
@ -41,10 +41,6 @@ export interface ZHADeviceSelectedParams {
|
|||
node: ZHADevice;
|
||||
}
|
||||
|
||||
export interface ZHAClusterSelectedParams {
|
||||
cluster: Cluster;
|
||||
}
|
||||
|
||||
export interface NodeServiceData {
|
||||
ieee_address: string;
|
||||
}
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
import "@material/mwc-button";
|
||||
import "@material/mwc-list/mwc-list-item";
|
||||
import { mdiHelpCircle } from "@mdi/js";
|
||||
import "@polymer/paper-input/paper-input";
|
||||
import {
|
||||
css,
|
||||
|
@ -10,13 +8,12 @@ import {
|
|||
PropertyValues,
|
||||
TemplateResult,
|
||||
} from "lit";
|
||||
import { property, state } from "lit/decorators";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { stopPropagation } from "../../../../../common/dom/stop_propagation";
|
||||
import "../../../../../components/buttons/ha-call-service-button";
|
||||
import "../../../../../components/ha-card";
|
||||
import "../../../../../components/ha-icon-button";
|
||||
import "../../../../../components/ha-select";
|
||||
import "../../../../../components/ha-service-description";
|
||||
import "../../../../../components/buttons/ha-progress-button";
|
||||
import {
|
||||
Attribute,
|
||||
Cluster,
|
||||
|
@ -27,7 +24,7 @@ import {
|
|||
} from "../../../../../data/zha";
|
||||
import { haStyle } from "../../../../../resources/styles";
|
||||
import { HomeAssistant } from "../../../../../types";
|
||||
import "../../../ha-config-section";
|
||||
import { forwardHaptic } from "../../../../../data/haptics";
|
||||
import { formatAsPaddedHex } from "./functions";
|
||||
import {
|
||||
ChangeEvent,
|
||||
|
@ -35,18 +32,15 @@ import {
|
|||
SetAttributeServiceData,
|
||||
} from "./types";
|
||||
|
||||
@customElement("zha-cluster-attributes")
|
||||
export class ZHAClusterAttributes extends LitElement {
|
||||
@property({ attribute: false }) public hass?: HomeAssistant;
|
||||
|
||||
@property() public isWide?: boolean;
|
||||
|
||||
@property() public showHelp = false;
|
||||
|
||||
@property() public selectedNode?: ZHADevice;
|
||||
@property() public device?: ZHADevice;
|
||||
|
||||
@property() public selectedCluster?: Cluster;
|
||||
|
||||
@state() private _attributes: Attribute[] = [];
|
||||
@state() private _attributes: Attribute[] | undefined;
|
||||
|
||||
@state() private _selectedAttributeId?: number;
|
||||
|
||||
|
@ -54,78 +48,52 @@ export class ZHAClusterAttributes extends LitElement {
|
|||
|
||||
@state() private _manufacturerCodeOverride?: string | number;
|
||||
|
||||
@state() private _readingAttribute = false;
|
||||
|
||||
@state()
|
||||
private _setAttributeServiceData?: SetAttributeServiceData;
|
||||
|
||||
protected updated(changedProperties: PropertyValues): void {
|
||||
if (changedProperties.has("selectedCluster")) {
|
||||
this._attributes = [];
|
||||
this._attributes = undefined;
|
||||
this._selectedAttributeId = undefined;
|
||||
this._attributeValue = "";
|
||||
this._fetchAttributesForCluster();
|
||||
}
|
||||
super.update(changedProperties);
|
||||
super.updated(changedProperties);
|
||||
}
|
||||
|
||||
protected render(): TemplateResult {
|
||||
if (!this.device || !this.selectedCluster || !this._attributes) {
|
||||
return html``;
|
||||
}
|
||||
return html`
|
||||
<ha-config-section .isWide=${this.isWide}>
|
||||
<div class="header" slot="header">
|
||||
<span>
|
||||
${this.hass!.localize(
|
||||
"ui.panel.config.zha.cluster_attributes.header"
|
||||
<ha-card class="content">
|
||||
<div class="attribute-picker">
|
||||
<ha-select
|
||||
.label=${this.hass!.localize(
|
||||
"ui.panel.config.zha.cluster_attributes.attributes_of_cluster"
|
||||
)}
|
||||
</span>
|
||||
<ha-icon-button
|
||||
class="toggle-help-icon"
|
||||
@click=${this._onHelpTap}
|
||||
.path=${mdiHelpCircle}
|
||||
.label=${this.hass!.localize("ui.common.help")}
|
||||
class="menu"
|
||||
.value=${String(this._selectedAttributeId)}
|
||||
@selected=${this._selectedAttributeChanged}
|
||||
@closed=${stopPropagation}
|
||||
fixedMenuPosition
|
||||
naturalMenuWidth
|
||||
>
|
||||
</ha-icon-button>
|
||||
</div>
|
||||
<span slot="introduction">
|
||||
${this.hass!.localize(
|
||||
"ui.panel.config.zha.cluster_attributes.introduction"
|
||||
)}
|
||||
</span>
|
||||
|
||||
<ha-card class="content">
|
||||
<div class="attribute-picker">
|
||||
<ha-select
|
||||
.label=${this.hass!.localize(
|
||||
"ui.panel.config.zha.cluster_attributes.attributes_of_cluster"
|
||||
)}
|
||||
class="menu"
|
||||
.value=${String(this._selectedAttributeId)}
|
||||
@selected=${this._selectedAttributeChanged}
|
||||
@closed=${stopPropagation}
|
||||
fixedMenuPosition
|
||||
naturalMenuWidth
|
||||
>
|
||||
${this._attributes.map(
|
||||
(entry) => html`
|
||||
<mwc-list-item .value=${String(entry.id)}>
|
||||
${entry.name + " (id: " + formatAsPaddedHex(entry.id) + ")"}
|
||||
</mwc-list-item>
|
||||
`
|
||||
)}
|
||||
</ha-select>
|
||||
</div>
|
||||
${this.showHelp
|
||||
? html`
|
||||
<div class="help-text">
|
||||
${this.hass!.localize(
|
||||
"ui.panel.config.zha.cluster_attributes.help_attribute_dropdown"
|
||||
)}
|
||||
</div>
|
||||
${this._attributes.map(
|
||||
(entry) => html`
|
||||
<mwc-list-item .value=${String(entry.id)}>
|
||||
${`${entry.name} (id: ${formatAsPaddedHex(entry.id)})`}
|
||||
</mwc-list-item>
|
||||
`
|
||||
: ""}
|
||||
${this._selectedAttributeId !== undefined
|
||||
? this._renderAttributeInteractions()
|
||||
: ""}
|
||||
</ha-card>
|
||||
</ha-config-section>
|
||||
)}
|
||||
</ha-select>
|
||||
</div>
|
||||
${this._selectedAttributeId !== undefined
|
||||
? this._renderAttributeInteractions()
|
||||
: ""}
|
||||
</ha-card>
|
||||
`;
|
||||
}
|
||||
|
||||
|
@ -152,20 +120,15 @@ export class ZHAClusterAttributes extends LitElement {
|
|||
></paper-input>
|
||||
</div>
|
||||
<div class="card-actions">
|
||||
<mwc-button @click=${this._onGetZigbeeAttributeClick}>
|
||||
<ha-progress-button
|
||||
@click=${this._onGetZigbeeAttributeClick}
|
||||
.progress=${this._readingAttribute}
|
||||
.disabled=${this._readingAttribute}
|
||||
>
|
||||
${this.hass!.localize(
|
||||
"ui.panel.config.zha.cluster_attributes.get_zigbee_attribute"
|
||||
"ui.panel.config.zha.cluster_attributes.read_zigbee_attribute"
|
||||
)}
|
||||
</mwc-button>
|
||||
${this.showHelp
|
||||
? html`
|
||||
<div class="help-text2">
|
||||
${this.hass!.localize(
|
||||
"ui.panel.config.zha.cluster_attributes.help_get_zigbee_attribute"
|
||||
)}
|
||||
</div>
|
||||
`
|
||||
: ""}
|
||||
</ha-progress-button>
|
||||
<ha-call-service-button
|
||||
.hass=${this.hass}
|
||||
domain="zha"
|
||||
|
@ -173,44 +136,37 @@ export class ZHAClusterAttributes extends LitElement {
|
|||
.serviceData=${this._setAttributeServiceData}
|
||||
>
|
||||
${this.hass!.localize(
|
||||
"ui.panel.config.zha.cluster_attributes.set_zigbee_attribute"
|
||||
"ui.panel.config.zha.cluster_attributes.write_zigbee_attribute"
|
||||
)}
|
||||
</ha-call-service-button>
|
||||
${this.showHelp
|
||||
? html`
|
||||
<ha-service-description
|
||||
.hass=${this.hass}
|
||||
domain="zha"
|
||||
service="set_zigbee_cluster_attribute"
|
||||
class="help-text2"
|
||||
></ha-service-description>
|
||||
`
|
||||
: ""}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
private async _fetchAttributesForCluster(): Promise<void> {
|
||||
if (this.selectedNode && this.selectedCluster && this.hass) {
|
||||
if (this.device && this.selectedCluster && this.hass) {
|
||||
this._attributes = await fetchAttributesForCluster(
|
||||
this.hass,
|
||||
this.selectedNode!.ieee,
|
||||
this.device!.ieee,
|
||||
this.selectedCluster!.endpoint_id,
|
||||
this.selectedCluster!.id,
|
||||
this.selectedCluster!.type
|
||||
);
|
||||
this._attributes.sort((a, b) => a.name.localeCompare(b.name));
|
||||
if (this._attributes.length > 0) {
|
||||
this._selectedAttributeId = this._attributes[0].id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private _computeReadAttributeServiceData():
|
||||
| ReadAttributeServiceData
|
||||
| undefined {
|
||||
if (!this.selectedCluster || !this.selectedNode) {
|
||||
if (!this.selectedCluster || !this.device) {
|
||||
return undefined;
|
||||
}
|
||||
return {
|
||||
ieee: this.selectedNode!.ieee,
|
||||
ieee: this.device!.ieee,
|
||||
endpoint_id: this.selectedCluster!.endpoint_id,
|
||||
cluster_id: this.selectedCluster!.id,
|
||||
cluster_type: this.selectedCluster!.type,
|
||||
|
@ -224,11 +180,11 @@ export class ZHAClusterAttributes extends LitElement {
|
|||
private _computeSetAttributeServiceData():
|
||||
| SetAttributeServiceData
|
||||
| undefined {
|
||||
if (!this.selectedCluster || !this.selectedNode) {
|
||||
if (!this.selectedCluster || !this.device) {
|
||||
return undefined;
|
||||
}
|
||||
return {
|
||||
ieee: this.selectedNode!.ieee,
|
||||
ieee: this.device!.ieee,
|
||||
endpoint_id: this.selectedCluster!.endpoint_id,
|
||||
cluster_id: this.selectedCluster!.id,
|
||||
cluster_type: this.selectedCluster!.type,
|
||||
|
@ -250,17 +206,24 @@ export class ZHAClusterAttributes extends LitElement {
|
|||
this._setAttributeServiceData = this._computeSetAttributeServiceData();
|
||||
}
|
||||
|
||||
private async _onGetZigbeeAttributeClick(): Promise<void> {
|
||||
private async _onGetZigbeeAttributeClick(ev: CustomEvent): Promise<void> {
|
||||
const button = ev.currentTarget as any;
|
||||
const data = this._computeReadAttributeServiceData();
|
||||
if (data && this.hass) {
|
||||
this._attributeValue = await readAttributeValue(this.hass, data);
|
||||
this._readingAttribute = true;
|
||||
try {
|
||||
this._attributeValue = await readAttributeValue(this.hass, data);
|
||||
forwardHaptic("success");
|
||||
button.actionSuccess();
|
||||
} catch (err: any) {
|
||||
forwardHaptic("failure");
|
||||
button.actionError();
|
||||
} finally {
|
||||
this._readingAttribute = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private _onHelpTap(): void {
|
||||
this.showHelp = !this.showHelp;
|
||||
}
|
||||
|
||||
private _selectedAttributeChanged(event: ItemSelectedEvent): void {
|
||||
this._selectedAttributeId = Number(event.target!.value);
|
||||
this._attributeValue = "";
|
||||
|
@ -278,14 +241,6 @@ export class ZHAClusterAttributes extends LitElement {
|
|||
width: 100%;
|
||||
}
|
||||
|
||||
.content {
|
||||
margin-top: 24px;
|
||||
}
|
||||
|
||||
ha-card {
|
||||
max-width: 680px;
|
||||
}
|
||||
|
||||
.card-actions.warning ha-call-service-button {
|
||||
color: var(--error-color);
|
||||
}
|
||||
|
@ -306,33 +261,6 @@ export class ZHAClusterAttributes extends LitElement {
|
|||
.header {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.toggle-help-icon {
|
||||
float: right;
|
||||
top: -6px;
|
||||
right: 0;
|
||||
padding-right: 0px;
|
||||
color: var(--primary-color);
|
||||
}
|
||||
|
||||
ha-service-description {
|
||||
display: block;
|
||||
color: grey;
|
||||
}
|
||||
|
||||
[hidden] {
|
||||
display: none;
|
||||
}
|
||||
.help-text {
|
||||
color: grey;
|
||||
padding-left: 28px;
|
||||
padding-right: 28px;
|
||||
padding-bottom: 16px;
|
||||
}
|
||||
.help-text2 {
|
||||
color: grey;
|
||||
padding: 16px;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
|
@ -343,5 +271,3 @@ declare global {
|
|||
"zha-cluster-attributes": ZHAClusterAttributes;
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("zha-cluster-attributes", ZHAClusterAttributes);
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import "@material/mwc-list/mwc-list-item";
|
||||
import { mdiHelpCircle } from "@mdi/js";
|
||||
import "@polymer/paper-input/paper-input";
|
||||
import {
|
||||
css,
|
||||
|
@ -13,9 +12,7 @@ import { property, state } from "lit/decorators";
|
|||
import { stopPropagation } from "../../../../../common/dom/stop_propagation";
|
||||
import "../../../../../components/buttons/ha-call-service-button";
|
||||
import "../../../../../components/ha-card";
|
||||
import "../../../../../components/ha-icon-button";
|
||||
import "../../../../../components/ha-select";
|
||||
import "../../../../../components/ha-service-description";
|
||||
import {
|
||||
Cluster,
|
||||
Command,
|
||||
|
@ -24,7 +21,6 @@ import {
|
|||
} from "../../../../../data/zha";
|
||||
import { haStyle } from "../../../../../resources/styles";
|
||||
import { HomeAssistant } from "../../../../../types";
|
||||
import "../../../ha-config-section";
|
||||
import { formatAsPaddedHex } from "./functions";
|
||||
import { ChangeEvent, IssueCommandServiceData } from "./types";
|
||||
|
||||
|
@ -33,13 +29,11 @@ export class ZHAClusterCommands extends LitElement {
|
|||
|
||||
@property() public isWide?: boolean;
|
||||
|
||||
@property() public selectedNode?: ZHADevice;
|
||||
@property() public device?: ZHADevice;
|
||||
|
||||
@property() public selectedCluster?: Cluster;
|
||||
|
||||
@state() private _showHelp = false;
|
||||
|
||||
@state() private _commands: Command[] = [];
|
||||
@state() private _commands: Command[] | undefined;
|
||||
|
||||
@state() private _selectedCommandId?: number;
|
||||
|
||||
|
@ -50,132 +44,97 @@ export class ZHAClusterCommands extends LitElement {
|
|||
|
||||
protected updated(changedProperties: PropertyValues): void {
|
||||
if (changedProperties.has("selectedCluster")) {
|
||||
this._commands = [];
|
||||
this._commands = undefined;
|
||||
this._selectedCommandId = undefined;
|
||||
this._fetchCommandsForCluster();
|
||||
}
|
||||
super.update(changedProperties);
|
||||
super.updated(changedProperties);
|
||||
}
|
||||
|
||||
protected render(): TemplateResult {
|
||||
if (!this.device || !this.selectedCluster || !this._commands) {
|
||||
return html``;
|
||||
}
|
||||
return html`
|
||||
<ha-config-section .isWide=${this.isWide}>
|
||||
<div class="header" slot="header">
|
||||
<span>
|
||||
${this.hass!.localize(
|
||||
"ui.panel.config.zha.cluster_commands.header"
|
||||
<ha-card class="content">
|
||||
<div class="command-picker">
|
||||
<ha-select
|
||||
.label=${this.hass!.localize(
|
||||
"ui.panel.config.zha.cluster_commands.commands_of_cluster"
|
||||
)}
|
||||
</span>
|
||||
<ha-icon-button
|
||||
class="toggle-help-icon"
|
||||
@click=${this._onHelpTap}
|
||||
.path=${mdiHelpCircle}
|
||||
.label=${this.hass!.localize("ui.common.help")}
|
||||
class="menu"
|
||||
.value=${String(this._selectedCommandId)}
|
||||
@selected=${this._selectedCommandChanged}
|
||||
@closed=${stopPropagation}
|
||||
fixedMenuPosition
|
||||
naturalMenuWidth
|
||||
>
|
||||
</ha-icon-button>
|
||||
${this._commands.map(
|
||||
(entry) => html`
|
||||
<mwc-list-item .value=${String(entry.id)}>
|
||||
${entry.name + " (id: " + formatAsPaddedHex(entry.id) + ")"}
|
||||
</mwc-list-item>
|
||||
`
|
||||
)}
|
||||
</ha-select>
|
||||
</div>
|
||||
<span slot="introduction">
|
||||
${this.hass!.localize(
|
||||
"ui.panel.config.zha.cluster_commands.introduction"
|
||||
)}
|
||||
</span>
|
||||
|
||||
<ha-card class="content">
|
||||
<div class="command-picker">
|
||||
<ha-select
|
||||
.label=${this.hass!.localize(
|
||||
"ui.panel.config.zha.cluster_commands.commands_of_cluster"
|
||||
)}
|
||||
class="menu"
|
||||
.value=${String(this._selectedCommandId)}
|
||||
@selected=${this._selectedCommandChanged}
|
||||
@closed=${stopPropagation}
|
||||
fixedMenuPosition
|
||||
naturalMenuWidth
|
||||
>
|
||||
${this._commands.map(
|
||||
(entry) => html`
|
||||
<mwc-list-item .value=${String(entry.id)}>
|
||||
${entry.name + " (id: " + formatAsPaddedHex(entry.id) + ")"}
|
||||
</mwc-list-item>
|
||||
`
|
||||
)}
|
||||
</ha-select>
|
||||
</div>
|
||||
${this._showHelp
|
||||
? html`
|
||||
<div class="help-text">
|
||||
${this.hass!.localize(
|
||||
"ui.panel.config.zha.cluster_commands.help_command_dropdown"
|
||||
${this._selectedCommandId !== undefined
|
||||
? html`
|
||||
<div class="input-text">
|
||||
<paper-input
|
||||
label=${this.hass!.localize(
|
||||
"ui.panel.config.zha.common.manufacturer_code_override"
|
||||
)}
|
||||
</div>
|
||||
`
|
||||
: ""}
|
||||
${this._selectedCommandId !== undefined
|
||||
? html`
|
||||
<div class="input-text">
|
||||
<paper-input
|
||||
label=${this.hass!.localize(
|
||||
"ui.panel.config.zha.common.manufacturer_code_override"
|
||||
)}
|
||||
type="number"
|
||||
.value=${this._manufacturerCodeOverride}
|
||||
@value-changed=${this._onManufacturerCodeOverrideChanged}
|
||||
placeholder=${this.hass!.localize(
|
||||
"ui.panel.config.zha.common.value"
|
||||
)}
|
||||
></paper-input>
|
||||
</div>
|
||||
<div class="card-actions">
|
||||
<ha-call-service-button
|
||||
.hass=${this.hass}
|
||||
domain="zha"
|
||||
service="issue_zigbee_cluster_command"
|
||||
.serviceData=${this._issueClusterCommandServiceData}
|
||||
>
|
||||
${this.hass!.localize(
|
||||
"ui.panel.config.zha.cluster_commands.issue_zigbee_command"
|
||||
)}
|
||||
</ha-call-service-button>
|
||||
${this._showHelp
|
||||
? html`
|
||||
<ha-service-description
|
||||
.hass=${this.hass}
|
||||
domain="zha"
|
||||
service="issue_zigbee_cluster_command"
|
||||
class="help-text2"
|
||||
></ha-service-description>
|
||||
`
|
||||
: ""}
|
||||
</div>
|
||||
`
|
||||
: ""}
|
||||
</ha-card>
|
||||
</ha-config-section>
|
||||
type="number"
|
||||
.value=${this._manufacturerCodeOverride}
|
||||
@value-changed=${this._onManufacturerCodeOverrideChanged}
|
||||
placeholder=${this.hass!.localize(
|
||||
"ui.panel.config.zha.common.value"
|
||||
)}
|
||||
></paper-input>
|
||||
</div>
|
||||
<div class="card-actions">
|
||||
<ha-call-service-button
|
||||
.hass=${this.hass}
|
||||
domain="zha"
|
||||
service="issue_zigbee_cluster_command"
|
||||
.serviceData=${this._issueClusterCommandServiceData}
|
||||
>
|
||||
${this.hass!.localize(
|
||||
"ui.panel.config.zha.cluster_commands.issue_zigbee_command"
|
||||
)}
|
||||
</ha-call-service-button>
|
||||
</div>
|
||||
`
|
||||
: ""}
|
||||
</ha-card>
|
||||
`;
|
||||
}
|
||||
|
||||
private async _fetchCommandsForCluster(): Promise<void> {
|
||||
if (this.selectedNode && this.selectedCluster && this.hass) {
|
||||
if (this.device && this.selectedCluster && this.hass) {
|
||||
this._commands = await fetchCommandsForCluster(
|
||||
this.hass,
|
||||
this.selectedNode!.ieee,
|
||||
this.device!.ieee,
|
||||
this.selectedCluster!.endpoint_id,
|
||||
this.selectedCluster!.id,
|
||||
this.selectedCluster!.type
|
||||
);
|
||||
this._commands.sort((a, b) => a.name.localeCompare(b.name));
|
||||
if (this._commands.length > 0) {
|
||||
this._selectedCommandId = this._commands[0].id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private _computeIssueClusterCommandServiceData():
|
||||
| IssueCommandServiceData
|
||||
| undefined {
|
||||
if (!this.selectedNode || !this.selectedCluster) {
|
||||
if (!this.device || !this.selectedCluster || !this._commands) {
|
||||
return undefined;
|
||||
}
|
||||
return {
|
||||
ieee: this.selectedNode!.ieee,
|
||||
ieee: this.device!.ieee,
|
||||
endpoint_id: this.selectedCluster!.endpoint_id,
|
||||
cluster_id: this.selectedCluster!.id,
|
||||
cluster_type: this.selectedCluster!.type,
|
||||
|
@ -192,10 +151,6 @@ export class ZHAClusterCommands extends LitElement {
|
|||
this._computeIssueClusterCommandServiceData();
|
||||
}
|
||||
|
||||
private _onHelpTap(): void {
|
||||
this._showHelp = !this._showHelp;
|
||||
}
|
||||
|
||||
private _selectedCommandChanged(event): void {
|
||||
this._selectedCommandId = Number(event.target.value);
|
||||
this._issueClusterCommandServiceData =
|
||||
|
@ -213,14 +168,6 @@ export class ZHAClusterCommands extends LitElement {
|
|||
width: 100%;
|
||||
}
|
||||
|
||||
.content {
|
||||
margin-top: 24px;
|
||||
}
|
||||
|
||||
ha-card {
|
||||
max-width: 680px;
|
||||
}
|
||||
|
||||
.card-actions.warning ha-call-service-button {
|
||||
color: var(--error-color);
|
||||
}
|
||||
|
@ -238,18 +185,6 @@ export class ZHAClusterCommands extends LitElement {
|
|||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
.help-text {
|
||||
color: grey;
|
||||
padding-left: 28px;
|
||||
padding-right: 28px;
|
||||
padding-bottom: 16px;
|
||||
}
|
||||
|
||||
.help-text2 {
|
||||
color: grey;
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.header {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
@ -261,15 +196,6 @@ export class ZHAClusterCommands extends LitElement {
|
|||
padding-right: 0px;
|
||||
color: var(--primary-color);
|
||||
}
|
||||
|
||||
ha-service-description {
|
||||
display: block;
|
||||
color: grey;
|
||||
}
|
||||
|
||||
[hidden] {
|
||||
display: none;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
|
|
|
@ -59,7 +59,7 @@ export class ZHAClustersDataTable extends LitElement {
|
|||
title: "ID",
|
||||
template: (id: number) => html` ${formatAsPaddedHex(id)} `,
|
||||
sortable: true,
|
||||
width: "15%",
|
||||
width: "25%",
|
||||
},
|
||||
endpoint_id: {
|
||||
title: "Endpoint ID",
|
||||
|
|
|
@ -1,195 +0,0 @@
|
|||
import "@material/mwc-list/mwc-list-item";
|
||||
import { mdiHelpCircle } from "@mdi/js";
|
||||
import {
|
||||
css,
|
||||
CSSResultGroup,
|
||||
html,
|
||||
LitElement,
|
||||
PropertyValues,
|
||||
TemplateResult,
|
||||
} from "lit";
|
||||
import { property, state } from "lit/decorators";
|
||||
import { fireEvent } from "../../../../../common/dom/fire_event";
|
||||
import { stopPropagation } from "../../../../../common/dom/stop_propagation";
|
||||
import "../../../../../components/buttons/ha-call-service-button";
|
||||
import "../../../../../components/ha-card";
|
||||
import "../../../../../components/ha-icon-button";
|
||||
import "../../../../../components/ha-select";
|
||||
import "../../../../../components/ha-service-description";
|
||||
import {
|
||||
Cluster,
|
||||
fetchClustersForZhaNode,
|
||||
ZHADevice,
|
||||
} from "../../../../../data/zha";
|
||||
import { haStyle } from "../../../../../resources/styles";
|
||||
import { HomeAssistant } from "../../../../../types";
|
||||
import "../../../ha-config-section";
|
||||
import { computeClusterKey } from "./functions";
|
||||
|
||||
declare global {
|
||||
// for fire event
|
||||
interface HASSDomEvents {
|
||||
"zha-cluster-selected": {
|
||||
cluster?: Cluster;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export class ZHAClusters extends LitElement {
|
||||
@property({ attribute: false }) public hass?: HomeAssistant;
|
||||
|
||||
@property() public isWide?: boolean;
|
||||
|
||||
@property() public selectedDevice?: ZHADevice;
|
||||
|
||||
@property() public showHelp = false;
|
||||
|
||||
@state() private _selectedClusterIndex = -1;
|
||||
|
||||
@state() private _clusters: Cluster[] = [];
|
||||
|
||||
protected updated(changedProperties: PropertyValues): void {
|
||||
if (changedProperties.has("selectedDevice")) {
|
||||
this._clusters = [];
|
||||
this._selectedClusterIndex = -1;
|
||||
fireEvent(this, "zha-cluster-selected", {
|
||||
cluster: undefined,
|
||||
});
|
||||
this._fetchClustersForZhaNode();
|
||||
}
|
||||
super.update(changedProperties);
|
||||
}
|
||||
|
||||
protected render(): TemplateResult {
|
||||
return html`
|
||||
<ha-config-section .isWide=${this.isWide}>
|
||||
<div class="header" slot="header">
|
||||
<ha-icon-button
|
||||
class="toggle-help-icon"
|
||||
@click=${this._onHelpTap}
|
||||
.path=${mdiHelpCircle}
|
||||
.label=${this.hass!.localize("ui.common.help")}
|
||||
>
|
||||
</ha-icon-button>
|
||||
</div>
|
||||
<span slot="introduction">
|
||||
${this.hass!.localize("ui.panel.config.zha.clusters.introduction")}
|
||||
</span>
|
||||
|
||||
<ha-card class="content">
|
||||
<div class="node-picker">
|
||||
<ha-select
|
||||
.label=${this.hass!.localize(
|
||||
"ui.panel.config.zha.common.clusters"
|
||||
)}
|
||||
class="menu"
|
||||
.value=${String(this._selectedClusterIndex)}
|
||||
@selected=${this._selectedClusterChanged}
|
||||
@closed=${stopPropagation}
|
||||
fixedMenuPosition
|
||||
naturalMenuWidth
|
||||
>
|
||||
${this._clusters.map(
|
||||
(entry, idx) => html`
|
||||
<mwc-list-item .value=${String(idx)}
|
||||
>${computeClusterKey(entry)}</mwc-list-item
|
||||
>
|
||||
`
|
||||
)}
|
||||
</ha-select>
|
||||
</div>
|
||||
${this.showHelp
|
||||
? html`
|
||||
<div class="help-text">
|
||||
${this.hass!.localize(
|
||||
"ui.panel.config.zha.clusters.help_cluster_dropdown"
|
||||
)}
|
||||
</div>
|
||||
`
|
||||
: ""}
|
||||
</ha-card>
|
||||
</ha-config-section>
|
||||
`;
|
||||
}
|
||||
|
||||
private async _fetchClustersForZhaNode(): Promise<void> {
|
||||
if (this.hass) {
|
||||
this._clusters = await fetchClustersForZhaNode(
|
||||
this.hass,
|
||||
this.selectedDevice!.ieee
|
||||
);
|
||||
this._clusters.sort((a, b) => a.name.localeCompare(b.name));
|
||||
}
|
||||
}
|
||||
|
||||
private _selectedClusterChanged(event): void {
|
||||
this._selectedClusterIndex = Number(event.target!.value);
|
||||
fireEvent(this, "zha-cluster-selected", {
|
||||
cluster: this._clusters[this._selectedClusterIndex],
|
||||
});
|
||||
}
|
||||
|
||||
private _onHelpTap(): void {
|
||||
this.showHelp = !this.showHelp;
|
||||
}
|
||||
|
||||
static get styles(): CSSResultGroup {
|
||||
return [
|
||||
haStyle,
|
||||
css`
|
||||
ha-select {
|
||||
margin-top: 16px;
|
||||
}
|
||||
.menu {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.content {
|
||||
margin-top: 24px;
|
||||
}
|
||||
|
||||
.header {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
ha-card {
|
||||
max-width: 680px;
|
||||
}
|
||||
|
||||
.node-picker {
|
||||
align-items: center;
|
||||
padding-left: 28px;
|
||||
padding-right: 28px;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
.toggle-help-icon {
|
||||
float: right;
|
||||
top: -6px;
|
||||
right: 0;
|
||||
padding-right: 0px;
|
||||
color: var(--primary-color);
|
||||
}
|
||||
|
||||
[hidden] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.help-text {
|
||||
color: grey;
|
||||
padding-left: 28px;
|
||||
padding-right: 28px;
|
||||
padding-bottom: 16px;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"zha-cluster": ZHAClusters;
|
||||
}
|
||||
}
|
||||
|
||||
customElements.define("zha-clusters", ZHAClusters);
|
|
@ -1,6 +1,4 @@
|
|||
import "@material/mwc-button/mwc-button";
|
||||
import "@material/mwc-list/mwc-list-item";
|
||||
import { mdiHelpCircle } from "@mdi/js";
|
||||
import {
|
||||
css,
|
||||
CSSResultGroup,
|
||||
|
@ -11,26 +9,19 @@ import {
|
|||
} from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { stopPropagation } from "../../../../../common/dom/stop_propagation";
|
||||
import "../../../../../components/buttons/ha-call-service-button";
|
||||
import "../../../../../components/buttons/ha-progress-button";
|
||||
import "../../../../../components/ha-card";
|
||||
import "../../../../../components/ha-icon-button";
|
||||
import "../../../../../components/ha-select";
|
||||
import "../../../../../components/ha-service-description";
|
||||
import { bindDevices, unbindDevices, ZHADevice } from "../../../../../data/zha";
|
||||
import { haStyle } from "../../../../../resources/styles";
|
||||
import { HomeAssistant } from "../../../../../types";
|
||||
import "../../../ha-config-section";
|
||||
import { ItemSelectedEvent } from "./types";
|
||||
|
||||
@customElement("zha-device-binding-control")
|
||||
export class ZHADeviceBindingControl extends LitElement {
|
||||
@property({ attribute: false }) public hass?: HomeAssistant;
|
||||
|
||||
@property() public isWide?: boolean;
|
||||
|
||||
@property() public selectedDevice?: ZHADevice;
|
||||
|
||||
@state() private _showHelp = false;
|
||||
@property() public device?: ZHADevice;
|
||||
|
||||
@state() private _bindTargetIndex = -1;
|
||||
|
||||
|
@ -38,77 +29,58 @@ export class ZHADeviceBindingControl extends LitElement {
|
|||
|
||||
@state() private _deviceToBind?: ZHADevice;
|
||||
|
||||
@state() private _bindingOperationInProgress = false;
|
||||
|
||||
protected updated(changedProperties: PropertyValues): void {
|
||||
if (changedProperties.has("selectedDevice")) {
|
||||
if (changedProperties.has("device")) {
|
||||
this._bindTargetIndex = -1;
|
||||
}
|
||||
super.update(changedProperties);
|
||||
super.updated(changedProperties);
|
||||
}
|
||||
|
||||
protected render(): TemplateResult {
|
||||
return html`
|
||||
<ha-config-section .isWide=${this.isWide}>
|
||||
<div class="header" slot="header">
|
||||
<span>Device Binding</span>
|
||||
<ha-icon-button
|
||||
class="toggle-help-icon"
|
||||
@click=${this._onHelpTap}
|
||||
.path=${mdiHelpCircle}
|
||||
.label=${this.hass!.localize("ui.common.help")}
|
||||
<ha-card class="content">
|
||||
<div class="command-picker">
|
||||
<ha-select
|
||||
label=${this.hass!.localize(
|
||||
"ui.panel.config.zha.device_binding.picker_label"
|
||||
)}
|
||||
class="menu"
|
||||
.value=${String(this._bindTargetIndex)}
|
||||
@selected=${this._bindTargetIndexChanged}
|
||||
@closed=${stopPropagation}
|
||||
fixedMenuPosition
|
||||
naturalMenuWidth
|
||||
>
|
||||
</ha-icon-button>
|
||||
</div>
|
||||
<span slot="introduction">Bind and unbind devices.</span>
|
||||
|
||||
<ha-card class="content">
|
||||
<div class="command-picker">
|
||||
<ha-select
|
||||
label="Bindable Devices"
|
||||
class="menu"
|
||||
.value=${String(this._bindTargetIndex)}
|
||||
@selected=${this._bindTargetIndexChanged}
|
||||
@closed=${stopPropagation}
|
||||
fixedMenuPosition
|
||||
naturalMenuWidth
|
||||
>
|
||||
${this.bindableDevices.map(
|
||||
(device, idx) => html`
|
||||
<mwc-list-item .value=${String(idx)}>
|
||||
${device.user_given_name
|
||||
? device.user_given_name
|
||||
: device.name}
|
||||
</mwc-list-item>
|
||||
`
|
||||
)}
|
||||
</ha-select>
|
||||
</div>
|
||||
${this._showHelp
|
||||
? html`
|
||||
<div class="helpText">
|
||||
Select a device to issue a bind command.
|
||||
</div>
|
||||
${this.bindableDevices.map(
|
||||
(device, idx) => html`
|
||||
<mwc-list-item .value=${String(idx)}>
|
||||
${device.user_given_name
|
||||
? device.user_given_name
|
||||
: device.name}
|
||||
</mwc-list-item>
|
||||
`
|
||||
: ""}
|
||||
<div class="card-actions">
|
||||
<mwc-button
|
||||
@click=${this._onBindDevicesClick}
|
||||
.disabled=${!(this._deviceToBind && this.selectedDevice)}
|
||||
>Bind</mwc-button
|
||||
>
|
||||
${this._showHelp
|
||||
? html` <div class="helpText">Bind devices.</div> `
|
||||
: ""}
|
||||
<mwc-button
|
||||
@click=${this._onUnbindDevicesClick}
|
||||
.disabled=${!(this._deviceToBind && this.selectedDevice)}
|
||||
>Unbind</mwc-button
|
||||
>
|
||||
${this._showHelp
|
||||
? html` <div class="helpText">Unbind devices.</div> `
|
||||
: ""}
|
||||
</div>
|
||||
</ha-card>
|
||||
</ha-config-section>
|
||||
)}
|
||||
</ha-select>
|
||||
</div>
|
||||
<div class="card-actions">
|
||||
<ha-progress-button
|
||||
@click=${this._onBindDevicesClick}
|
||||
.disabled=${!(this._deviceToBind && this.device) ||
|
||||
this._bindingOperationInProgress}
|
||||
>
|
||||
${this.hass!.localize("ui.panel.config.zha.device_binding.bind")}
|
||||
</ha-progress-button>
|
||||
<ha-progress-button
|
||||
@click=${this._onUnbindDevicesClick}
|
||||
.disabled=${!(this._deviceToBind && this.device) ||
|
||||
this._bindingOperationInProgress}
|
||||
>
|
||||
${this.hass!.localize("ui.panel.config.zha.device_binding.unbind")}
|
||||
</ha-progress-button>
|
||||
</div>
|
||||
</ha-card>
|
||||
`;
|
||||
}
|
||||
|
||||
|
@ -120,27 +92,41 @@ export class ZHADeviceBindingControl extends LitElement {
|
|||
: this.bindableDevices[this._bindTargetIndex];
|
||||
}
|
||||
|
||||
private _onHelpTap(): void {
|
||||
this._showHelp = !this._showHelp;
|
||||
}
|
||||
|
||||
private async _onBindDevicesClick(): Promise<void> {
|
||||
if (this.hass && this._deviceToBind && this.selectedDevice) {
|
||||
await bindDevices(
|
||||
this.hass,
|
||||
this.selectedDevice.ieee,
|
||||
this._deviceToBind.ieee
|
||||
);
|
||||
private async _onBindDevicesClick(ev: CustomEvent): Promise<void> {
|
||||
const button = ev.currentTarget as any;
|
||||
if (this.hass && this._deviceToBind && this.device) {
|
||||
this._bindingOperationInProgress = true;
|
||||
button.progress = true;
|
||||
try {
|
||||
await bindDevices(this.hass, this.device.ieee, this._deviceToBind.ieee);
|
||||
button.actionSuccess();
|
||||
} catch (err: any) {
|
||||
button.actionError();
|
||||
} finally {
|
||||
this._bindingOperationInProgress = false;
|
||||
button.progress = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async _onUnbindDevicesClick(): Promise<void> {
|
||||
if (this.hass && this._deviceToBind && this.selectedDevice) {
|
||||
await unbindDevices(
|
||||
this.hass,
|
||||
this.selectedDevice.ieee,
|
||||
this._deviceToBind.ieee
|
||||
);
|
||||
private async _onUnbindDevicesClick(ev: CustomEvent): Promise<void> {
|
||||
const button = ev.currentTarget as any;
|
||||
if (this.hass && this._deviceToBind && this.device) {
|
||||
this._bindingOperationInProgress = true;
|
||||
button.progress = true;
|
||||
try {
|
||||
await unbindDevices(
|
||||
this.hass,
|
||||
this.device.ieee,
|
||||
this._deviceToBind.ieee
|
||||
);
|
||||
button.actionSuccess();
|
||||
} catch (err: any) {
|
||||
button.actionError();
|
||||
} finally {
|
||||
this._bindingOperationInProgress = false;
|
||||
button.progress = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -152,18 +138,6 @@ export class ZHADeviceBindingControl extends LitElement {
|
|||
width: 100%;
|
||||
}
|
||||
|
||||
.content {
|
||||
margin-top: 24px;
|
||||
}
|
||||
|
||||
ha-card {
|
||||
max-width: 680px;
|
||||
}
|
||||
|
||||
.card-actions.warning ha-call-service-button {
|
||||
color: var(--error-color);
|
||||
}
|
||||
|
||||
.command-picker {
|
||||
align-items: center;
|
||||
padding-left: 28px;
|
||||
|
@ -171,33 +145,9 @@ export class ZHADeviceBindingControl extends LitElement {
|
|||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
.helpText {
|
||||
color: grey;
|
||||
padding-left: 28px;
|
||||
padding-right: 28px;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
.header {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.toggle-help-icon {
|
||||
float: right;
|
||||
top: -6px;
|
||||
right: 0;
|
||||
padding-right: 0px;
|
||||
color: var(--primary-color);
|
||||
}
|
||||
|
||||
ha-service-description {
|
||||
display: block;
|
||||
color: grey;
|
||||
}
|
||||
|
||||
[hidden] {
|
||||
display: none;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
|
|
|
@ -1,12 +1,9 @@
|
|||
import { CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||
import { html, LitElement, PropertyValues, TemplateResult } from "lit";
|
||||
import memoizeOne from "memoize-one";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { computeRTLDirection } from "../../../../../common/util/compute_rtl";
|
||||
import "../../../../../components/ha-code-editor";
|
||||
import { createCloseHeading } from "../../../../../components/ha-dialog";
|
||||
import { haStyleDialog } from "../../../../../resources/styles";
|
||||
import { HomeAssistant } from "../../../../../types";
|
||||
import { ZHADeviceChildrenDialogParams } from "./show-dialog-zha-device-children";
|
||||
import "../../../../../components/data-table/ha-data-table";
|
||||
import type {
|
||||
DataTableColumnContainer,
|
||||
|
@ -14,7 +11,6 @@ import type {
|
|||
} from "../../../../../components/data-table/ha-data-table";
|
||||
import "../../../../../components/ha-circular-progress";
|
||||
import { fetchDevices, ZHADevice } from "../../../../../data/zha";
|
||||
import { fireEvent } from "../../../../../common/dom/fire_event";
|
||||
|
||||
export interface DeviceRowData extends DataTableRowData {
|
||||
id: string;
|
||||
|
@ -22,14 +18,21 @@ export interface DeviceRowData extends DataTableRowData {
|
|||
lqi: number;
|
||||
}
|
||||
|
||||
@customElement("dialog-zha-device-children")
|
||||
class DialogZHADeviceChildren extends LitElement {
|
||||
@customElement("zha-device-children")
|
||||
class ZHADeviceChildren extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@state() private _device: ZHADevice | undefined;
|
||||
@property() public device: ZHADevice | undefined;
|
||||
|
||||
@state() private _devices: Map<string, ZHADevice> | undefined;
|
||||
|
||||
protected updated(changedProperties: PropertyValues) {
|
||||
super.updated(changedProperties);
|
||||
if (this.hass && changedProperties.has("device")) {
|
||||
this._fetchData();
|
||||
}
|
||||
}
|
||||
|
||||
private _deviceChildren = memoizeOne(
|
||||
(
|
||||
device: ZHADevice | undefined,
|
||||
|
@ -69,70 +72,45 @@ class DialogZHADeviceChildren extends LitElement {
|
|||
},
|
||||
};
|
||||
|
||||
public showDialog(params: ZHADeviceChildrenDialogParams): void {
|
||||
this._device = params.device;
|
||||
this._fetchData();
|
||||
}
|
||||
|
||||
public closeDialog(): void {
|
||||
this._device = undefined;
|
||||
this._devices = undefined;
|
||||
fireEvent(this, "dialog-closed", { dialog: this.localName });
|
||||
}
|
||||
|
||||
protected render(): TemplateResult {
|
||||
if (!this._device) {
|
||||
if (!this.device) {
|
||||
return html``;
|
||||
}
|
||||
return html`
|
||||
<ha-dialog
|
||||
hideActions
|
||||
open
|
||||
@closed=${this.closeDialog}
|
||||
.heading=${createCloseHeading(
|
||||
this.hass,
|
||||
this.hass.localize(`ui.dialogs.zha_device_info.device_children`)
|
||||
)}
|
||||
>
|
||||
${!this._devices
|
||||
? html`<ha-circular-progress
|
||||
alt="Loading"
|
||||
size="large"
|
||||
active
|
||||
></ha-circular-progress>`
|
||||
: html`<ha-data-table
|
||||
.hass=${this.hass}
|
||||
.columns=${this._columns}
|
||||
.data=${this._deviceChildren(this._device, this._devices)}
|
||||
auto-height
|
||||
.dir=${computeRTLDirection(this.hass)}
|
||||
.searchLabel=${this.hass.localize(
|
||||
"ui.components.data-table.search"
|
||||
)}
|
||||
.noDataText=${this.hass.localize(
|
||||
"ui.components.data-table.no-data"
|
||||
)}
|
||||
></ha-data-table>`}
|
||||
</ha-dialog>
|
||||
${!this._devices
|
||||
? html`<ha-circular-progress
|
||||
alt="Loading"
|
||||
size="large"
|
||||
active
|
||||
></ha-circular-progress>`
|
||||
: html`<ha-data-table
|
||||
.hass=${this.hass}
|
||||
.columns=${this._columns}
|
||||
.data=${this._deviceChildren(this.device, this._devices)}
|
||||
auto-height
|
||||
.dir=${computeRTLDirection(this.hass)}
|
||||
.searchLabel=${this.hass.localize(
|
||||
"ui.components.data-table.search"
|
||||
)}
|
||||
.noDataText=${this.hass.localize(
|
||||
"ui.components.data-table.no-data"
|
||||
)}
|
||||
></ha-data-table>`}
|
||||
`;
|
||||
}
|
||||
|
||||
private async _fetchData(): Promise<void> {
|
||||
if (this._device && this.hass) {
|
||||
if (this.device && this.hass) {
|
||||
const devices = await fetchDevices(this.hass!);
|
||||
this._devices = new Map(
|
||||
devices.map((device: ZHADevice) => [device.ieee, device])
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
static get styles(): CSSResultGroup {
|
||||
return haStyleDialog;
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"dialog-zha-device-children": DialogZHADeviceChildren;
|
||||
"zha-device-children": ZHADeviceChildren;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
import { html, LitElement, PropertyValues, TemplateResult } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import "../../../../../components/ha-code-editor";
|
||||
import { ZHADevice } from "../../../../../data/zha";
|
||||
import { HomeAssistant } from "../../../../../types";
|
||||
|
||||
@customElement("zha-device-zigbee-info")
|
||||
class ZHADeviceZigbeeInfo extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@property() public device: ZHADevice | undefined;
|
||||
|
||||
@state() private _signature: any;
|
||||
|
||||
protected updated(changedProperties: PropertyValues): void {
|
||||
if (changedProperties.has("device") && this.hass && this.device) {
|
||||
this._signature = JSON.stringify(
|
||||
{
|
||||
...this.device.signature,
|
||||
manufacturer: this.device.manufacturer,
|
||||
model: this.device.model,
|
||||
class: this.device.quirk_class,
|
||||
},
|
||||
null,
|
||||
2
|
||||
);
|
||||
}
|
||||
super.updated(changedProperties);
|
||||
}
|
||||
|
||||
protected render(): TemplateResult {
|
||||
if (!this._signature) {
|
||||
return html``;
|
||||
}
|
||||
|
||||
return html`
|
||||
<ha-code-editor mode="yaml" readOnly .value=${this._signature} dir="ltr">
|
||||
</ha-code-editor>
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"zha-device-zigbee-info": ZHADeviceZigbeeInfo;
|
||||
}
|
||||
}
|
|
@ -1,5 +1,3 @@
|
|||
import "@material/mwc-button/mwc-button";
|
||||
import { mdiHelpCircle } from "@mdi/js";
|
||||
import {
|
||||
css,
|
||||
CSSResultGroup,
|
||||
|
@ -11,37 +9,29 @@ import {
|
|||
import { customElement, property, state, query } from "lit/decorators";
|
||||
import type { HASSDomEvent } from "../../../../../common/dom/fire_event";
|
||||
import { stopPropagation } from "../../../../../common/dom/stop_propagation";
|
||||
import "../../../../../components/buttons/ha-call-service-button";
|
||||
import "../../../../../components/buttons/ha-progress-button";
|
||||
import { SelectionChangedEvent } from "../../../../../components/data-table/ha-data-table";
|
||||
import "../../../../../components/ha-card";
|
||||
import "../../../../../components/ha-icon-button";
|
||||
import "../../../../../components/ha-service-description";
|
||||
import {
|
||||
bindDeviceToGroup,
|
||||
Cluster,
|
||||
fetchClustersForZhaNode,
|
||||
fetchClustersForZhaDevice,
|
||||
unbindDeviceFromGroup,
|
||||
ZHADevice,
|
||||
ZHAGroup,
|
||||
} from "../../../../../data/zha";
|
||||
import { haStyle } from "../../../../../resources/styles";
|
||||
import type { HomeAssistant } from "../../../../../types";
|
||||
import "../../../ha-config-section";
|
||||
import { ItemSelectedEvent } from "./types";
|
||||
import "./zha-clusters-data-table";
|
||||
import type { ZHAClustersDataTable } from "./zha-clusters-data-table";
|
||||
import "@material/mwc-list/mwc-list-item";
|
||||
|
||||
@customElement("zha-group-binding-control")
|
||||
export class ZHAGroupBindingControl extends LitElement {
|
||||
@property({ attribute: false }) public hass?: HomeAssistant;
|
||||
|
||||
@property() public isWide?: boolean;
|
||||
|
||||
@property() public narrow?: boolean;
|
||||
|
||||
@property() public selectedDevice?: ZHADevice;
|
||||
|
||||
@state() private _showHelp = false;
|
||||
@property() public device?: ZHADevice;
|
||||
|
||||
@state() private _bindTargetIndex = -1;
|
||||
|
||||
|
@ -51,6 +41,8 @@ export class ZHAGroupBindingControl extends LitElement {
|
|||
|
||||
@state() private _clusters: Cluster[] = [];
|
||||
|
||||
@state() private _bindingOperationInProgress = false;
|
||||
|
||||
private _groupToBind?: ZHAGroup;
|
||||
|
||||
private _clustersToBind?: Cluster[];
|
||||
|
@ -59,38 +51,17 @@ export class ZHAGroupBindingControl extends LitElement {
|
|||
private _zhaClustersDataTable!: ZHAClustersDataTable;
|
||||
|
||||
protected updated(changedProperties: PropertyValues): void {
|
||||
if (changedProperties.has("selectedDevice")) {
|
||||
if (changedProperties.has("device")) {
|
||||
this._bindTargetIndex = -1;
|
||||
this._selectedClusters = [];
|
||||
this._clustersToBind = [];
|
||||
this._fetchClustersForZhaNode();
|
||||
}
|
||||
super.update(changedProperties);
|
||||
super.updated(changedProperties);
|
||||
}
|
||||
|
||||
protected render(): TemplateResult {
|
||||
return html`
|
||||
<ha-config-section .isWide=${this.isWide}>
|
||||
<div class="sectionHeader" slot="header">
|
||||
<span
|
||||
>${this.hass!.localize(
|
||||
"ui.panel.config.zha.group_binding.header"
|
||||
)}</span
|
||||
>
|
||||
<ha-icon-button
|
||||
class="toggle-help-icon"
|
||||
@click=${this._onHelpTap}
|
||||
.path=${mdiHelpCircle}
|
||||
.label=${this.hass!.localize("ui.common.help")}
|
||||
>
|
||||
</ha-icon-button>
|
||||
</div>
|
||||
<span slot="introduction"
|
||||
>${this.hass!.localize(
|
||||
"ui.panel.config.zha.group_binding.introduction"
|
||||
)}</span
|
||||
>
|
||||
|
||||
<ha-card class="content">
|
||||
<div class="command-picker">
|
||||
<ha-select
|
||||
|
@ -112,66 +83,32 @@ export class ZHAGroupBindingControl extends LitElement {
|
|||
)}
|
||||
</ha-select>
|
||||
</div>
|
||||
${this._showHelp
|
||||
? html`
|
||||
<div class="helpText">
|
||||
${this.hass!.localize(
|
||||
"ui.panel.config.zha.group_binding.group_picker_help"
|
||||
)}
|
||||
</div>
|
||||
`
|
||||
: ""}
|
||||
<div class="command-picker">
|
||||
<zha-clusters-data-table
|
||||
.hass=${this.hass}
|
||||
.narrow=${this.narrow}
|
||||
.clusters=${this._clusters}
|
||||
@selection-changed=${this._handleClusterSelectionChanged}
|
||||
class="menu"
|
||||
></zha-clusters-data-table>
|
||||
</div>
|
||||
${this._showHelp
|
||||
? html`
|
||||
<div class="helpText">
|
||||
${this.hass!.localize(
|
||||
"ui.panel.config.zha.group_binding.cluster_selection_help"
|
||||
)}
|
||||
</div>
|
||||
`
|
||||
: ""}
|
||||
<div class="card-actions">
|
||||
<mwc-button
|
||||
@click=${this._onBindGroupClick}
|
||||
.disabled=${!this._canBind}
|
||||
>${this.hass!.localize(
|
||||
"ui.panel.config.zha.group_binding.bind_button_label"
|
||||
)}</mwc-button
|
||||
>
|
||||
${this._showHelp
|
||||
? html`
|
||||
<div class="helpText">
|
||||
${this.hass!.localize(
|
||||
"ui.panel.config.zha.group_binding.bind_button_help"
|
||||
)}
|
||||
</div>
|
||||
`
|
||||
: ""}
|
||||
<mwc-button
|
||||
@click=${this._onUnbindGroupClick}
|
||||
.disabled=${!this._canBind}
|
||||
>${this.hass!.localize(
|
||||
"ui.panel.config.zha.group_binding.unbind_button_label"
|
||||
)}</mwc-button
|
||||
>
|
||||
${this._showHelp
|
||||
? html`
|
||||
<div class="helpText">
|
||||
${this.hass!.localize(
|
||||
"ui.panel.config.zha.group_binding.unbind_button_help"
|
||||
)}
|
||||
</div>
|
||||
`
|
||||
: ""}
|
||||
<ha-progress-button
|
||||
@click=${this._onBindGroupClick}
|
||||
.disabled=${!this._canBind || this._bindingOperationInProgress}
|
||||
>
|
||||
${this.hass!.localize(
|
||||
"ui.panel.config.zha.group_binding.bind_button_label"
|
||||
)}
|
||||
</ha-progress-button>
|
||||
|
||||
<ha-progress-button
|
||||
@click=${this._onUnbindGroupClick}
|
||||
.disabled=${!this._canBind || this._bindingOperationInProgress}
|
||||
>
|
||||
${this.hass!.localize(
|
||||
"ui.panel.config.zha.group_binding.unbind_button_label"
|
||||
)}
|
||||
</ha-progress-button>
|
||||
</div>
|
||||
</ha-card>
|
||||
</ha-config-section>
|
||||
|
@ -186,31 +123,49 @@ export class ZHAGroupBindingControl extends LitElement {
|
|||
: this.groups[this._bindTargetIndex];
|
||||
}
|
||||
|
||||
private _onHelpTap(): void {
|
||||
this._showHelp = !this._showHelp;
|
||||
}
|
||||
|
||||
private async _onBindGroupClick(): Promise<void> {
|
||||
private async _onBindGroupClick(ev: CustomEvent): Promise<void> {
|
||||
const button = ev.currentTarget as any;
|
||||
if (this.hass && this._canBind) {
|
||||
await bindDeviceToGroup(
|
||||
this.hass,
|
||||
this.selectedDevice!.ieee,
|
||||
this._groupToBind!.group_id,
|
||||
this._clustersToBind!
|
||||
);
|
||||
this._zhaClustersDataTable.clearSelection();
|
||||
this._bindingOperationInProgress = true;
|
||||
button.progress = true;
|
||||
try {
|
||||
await bindDeviceToGroup(
|
||||
this.hass,
|
||||
this.device!.ieee,
|
||||
this._groupToBind!.group_id,
|
||||
this._clustersToBind!
|
||||
);
|
||||
this._zhaClustersDataTable.clearSelection();
|
||||
button.actionSuccess();
|
||||
} catch (err: any) {
|
||||
button.actionError();
|
||||
} finally {
|
||||
this._bindingOperationInProgress = false;
|
||||
button.progress = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async _onUnbindGroupClick(): Promise<void> {
|
||||
private async _onUnbindGroupClick(ev: CustomEvent): Promise<void> {
|
||||
const button = ev.currentTarget as any;
|
||||
if (this.hass && this._canBind) {
|
||||
await unbindDeviceFromGroup(
|
||||
this.hass,
|
||||
this.selectedDevice!.ieee,
|
||||
this._groupToBind!.group_id,
|
||||
this._clustersToBind!
|
||||
);
|
||||
this._zhaClustersDataTable.clearSelection();
|
||||
this._bindingOperationInProgress = true;
|
||||
button.progress = true;
|
||||
try {
|
||||
await unbindDeviceFromGroup(
|
||||
this.hass,
|
||||
this.device!.ieee,
|
||||
this._groupToBind!.group_id,
|
||||
this._clustersToBind!
|
||||
);
|
||||
this._zhaClustersDataTable.clearSelection();
|
||||
button.actionSuccess();
|
||||
} catch (err: any) {
|
||||
button.actionError();
|
||||
} finally {
|
||||
this._bindingOperationInProgress = false;
|
||||
button.progress = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -230,9 +185,9 @@ export class ZHAGroupBindingControl extends LitElement {
|
|||
|
||||
private async _fetchClustersForZhaNode(): Promise<void> {
|
||||
if (this.hass) {
|
||||
this._clusters = await fetchClustersForZhaNode(
|
||||
this._clusters = await fetchClustersForZhaDevice(
|
||||
this.hass,
|
||||
this.selectedDevice!.ieee
|
||||
this.device!.ieee
|
||||
);
|
||||
this._clusters = this._clusters
|
||||
.filter((cluster) => cluster.type.toLowerCase() === "out")
|
||||
|
@ -245,7 +200,7 @@ export class ZHAGroupBindingControl extends LitElement {
|
|||
this._groupToBind &&
|
||||
this._clustersToBind &&
|
||||
this._clustersToBind?.length > 0 &&
|
||||
this.selectedDevice
|
||||
this.device
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -257,18 +212,6 @@ export class ZHAGroupBindingControl extends LitElement {
|
|||
width: 100%;
|
||||
}
|
||||
|
||||
.content {
|
||||
margin-top: 24px;
|
||||
}
|
||||
|
||||
ha-card {
|
||||
max-width: 680px;
|
||||
}
|
||||
|
||||
.card-actions.warning ha-call-service-button {
|
||||
color: var(--error-color);
|
||||
}
|
||||
|
||||
.command-picker {
|
||||
align-items: center;
|
||||
padding-left: 28px;
|
||||
|
@ -285,30 +228,6 @@ export class ZHAGroupBindingControl extends LitElement {
|
|||
.sectionHeader {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.helpText {
|
||||
color: grey;
|
||||
padding-left: 28px;
|
||||
padding-right: 28px;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
.toggle-help-icon {
|
||||
float: right;
|
||||
top: -6px;
|
||||
right: 0;
|
||||
padding-right: 0px;
|
||||
color: var(--primary-color);
|
||||
}
|
||||
|
||||
ha-service-description {
|
||||
display: block;
|
||||
color: grey;
|
||||
}
|
||||
|
||||
[hidden] {
|
||||
display: none;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
|
|
|
@ -0,0 +1,198 @@
|
|||
import "@material/mwc-list/mwc-list-item";
|
||||
import {
|
||||
css,
|
||||
CSSResultGroup,
|
||||
html,
|
||||
LitElement,
|
||||
PropertyValues,
|
||||
TemplateResult,
|
||||
} from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import { cache } from "lit/directives/cache";
|
||||
import { stopPropagation } from "../../../../../common/dom/stop_propagation";
|
||||
import "../../../../../components/ha-card";
|
||||
import "../../../../../components/ha-select";
|
||||
import {
|
||||
Cluster,
|
||||
fetchClustersForZhaDevice,
|
||||
ZHADevice,
|
||||
} from "../../../../../data/zha";
|
||||
import { haStyle } from "../../../../../resources/styles";
|
||||
import { HomeAssistant } from "../../../../../types";
|
||||
import { computeClusterKey } from "./functions";
|
||||
import "@material/mwc-tab-bar/mwc-tab-bar";
|
||||
import "@material/mwc-tab/mwc-tab";
|
||||
import "./zha-cluster-attributes";
|
||||
import "./zha-cluster-commands";
|
||||
|
||||
declare global {
|
||||
// for fire event
|
||||
interface HASSDomEvents {
|
||||
"zha-cluster-selected": {
|
||||
cluster?: Cluster;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
const tabs = ["attributes", "commands"] as const;
|
||||
|
||||
@customElement("zha-manage-clusters")
|
||||
export class ZHAManageClusters extends LitElement {
|
||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||
|
||||
@property() public isWide?: boolean;
|
||||
|
||||
@property() public device?: ZHADevice;
|
||||
|
||||
@state() private _selectedClusterIndex = -1;
|
||||
|
||||
@state() private _clusters: Cluster[] = [];
|
||||
|
||||
@state() private _selectedCluster?: Cluster;
|
||||
|
||||
@state() private _currTab: typeof tabs[number] = "attributes";
|
||||
|
||||
@state() private _clustersLoaded = false;
|
||||
|
||||
protected willUpdate(changedProps: PropertyValues) {
|
||||
super.willUpdate(changedProps);
|
||||
if (!this.device) {
|
||||
return;
|
||||
}
|
||||
if (!tabs.includes(this._currTab)) {
|
||||
this._currTab = tabs[0];
|
||||
}
|
||||
}
|
||||
|
||||
protected updated(changedProperties: PropertyValues): void {
|
||||
if (changedProperties.has("device")) {
|
||||
this._clusters = [];
|
||||
this._selectedClusterIndex = -1;
|
||||
this._clustersLoaded = false;
|
||||
this._fetchClustersForZhaDevice();
|
||||
}
|
||||
super.updated(changedProperties);
|
||||
}
|
||||
|
||||
protected render(): TemplateResult {
|
||||
if (!this.device || !this._clustersLoaded) {
|
||||
return html``;
|
||||
}
|
||||
return html`
|
||||
<ha-card class="content">
|
||||
<div class="node-picker">
|
||||
<ha-select
|
||||
.label=${this.hass!.localize("ui.panel.config.zha.common.clusters")}
|
||||
class="menu"
|
||||
.value=${String(this._selectedClusterIndex)}
|
||||
@selected=${this._selectedClusterChanged}
|
||||
@closed=${stopPropagation}
|
||||
fixedMenuPosition
|
||||
naturalMenuWidth
|
||||
>
|
||||
${this._clusters.map(
|
||||
(entry, idx) => html`
|
||||
<mwc-list-item .value=${String(idx)}
|
||||
>${computeClusterKey(entry)}</mwc-list-item
|
||||
>
|
||||
`
|
||||
)}
|
||||
</ha-select>
|
||||
</div>
|
||||
${this._selectedCluster
|
||||
? html`
|
||||
<mwc-tab-bar
|
||||
.activeIndex=${tabs.indexOf(this._currTab)}
|
||||
@MDCTabBar:activated=${this._handleTabChanged}
|
||||
>
|
||||
${tabs.map(
|
||||
(tab) => html`
|
||||
<mwc-tab
|
||||
.label=${this.hass.localize(
|
||||
`ui.panel.config.zha.clusters.tabs.${tab}`
|
||||
)}
|
||||
></mwc-tab>
|
||||
`
|
||||
)}
|
||||
</mwc-tab-bar>
|
||||
|
||||
<div class="content" tabindex="-1" dialogInitialFocus>
|
||||
${cache(
|
||||
this._currTab === "attributes"
|
||||
? html`
|
||||
<zha-cluster-attributes
|
||||
.hass=${this.hass}
|
||||
.device=${this.device}
|
||||
.selectedCluster=${this._selectedCluster}
|
||||
></zha-cluster-attributes>
|
||||
`
|
||||
: html`
|
||||
<zha-cluster-commands
|
||||
.hass=${this.hass}
|
||||
.device=${this.device}
|
||||
.selectedCluster=${this._selectedCluster}
|
||||
></zha-cluster-commands>
|
||||
`
|
||||
)}
|
||||
</div>
|
||||
`
|
||||
: ""}
|
||||
</ha-card>
|
||||
`;
|
||||
}
|
||||
|
||||
private async _fetchClustersForZhaDevice(): Promise<void> {
|
||||
if (this.hass) {
|
||||
this._clusters = await fetchClustersForZhaDevice(
|
||||
this.hass,
|
||||
this.device!.ieee
|
||||
);
|
||||
this._clusters.sort((a, b) => a.name.localeCompare(b.name));
|
||||
if (this._clusters.length > 0) {
|
||||
this._selectedClusterIndex = 0;
|
||||
this._selectedCluster = this._clusters[0];
|
||||
}
|
||||
this._clustersLoaded = true;
|
||||
}
|
||||
}
|
||||
|
||||
private _handleTabChanged(ev: CustomEvent): void {
|
||||
const newTab = tabs[ev.detail.index];
|
||||
if (newTab === this._currTab) {
|
||||
return;
|
||||
}
|
||||
this._currTab = newTab;
|
||||
}
|
||||
|
||||
private _selectedClusterChanged(event): void {
|
||||
this._selectedClusterIndex = Number(event.target!.value);
|
||||
this._selectedCluster = this._clusters[this._selectedClusterIndex];
|
||||
}
|
||||
|
||||
static get styles(): CSSResultGroup {
|
||||
return [
|
||||
haStyle,
|
||||
css`
|
||||
ha-select {
|
||||
margin-top: 16px;
|
||||
}
|
||||
.menu {
|
||||
width: 100%;
|
||||
}
|
||||
.header {
|
||||
flex-grow: 1;
|
||||
}
|
||||
.node-picker {
|
||||
align-items: center;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface HTMLElementTagNameMap {
|
||||
"zha-manage-clusters": ZHAManageClusters;
|
||||
}
|
||||
}
|
|
@ -332,7 +332,6 @@ export class HaScriptEditor extends KeyboardShortcutMixin(LitElement) {
|
|||
"yaml-mode": this._mode === "yaml",
|
||||
})}"
|
||||
>
|
||||
${this._errors ? html`<div class="errors">${this._errors}</div>` : ""}
|
||||
${this._mode === "gui"
|
||||
? html`
|
||||
<div
|
||||
|
@ -343,6 +342,13 @@ export class HaScriptEditor extends KeyboardShortcutMixin(LitElement) {
|
|||
${this._config
|
||||
? html`
|
||||
<div class="config-container">
|
||||
${this._errors
|
||||
? html`
|
||||
<ha-alert alert-type="error">
|
||||
${this._errors}
|
||||
</ha-alert>
|
||||
`
|
||||
: ""}
|
||||
<ha-card outlined>
|
||||
<div class="card-content">
|
||||
<ha-form
|
||||
|
@ -382,6 +388,11 @@ export class HaScriptEditor extends KeyboardShortcutMixin(LitElement) {
|
|||
`
|
||||
: this._mode === "yaml"
|
||||
? html`
|
||||
${this._errors
|
||||
? html`
|
||||
<ha-alert alert-type="error">${this._errors}</ha-alert>
|
||||
`
|
||||
: ""}
|
||||
<ha-yaml-editor
|
||||
.hass=${this.hass}
|
||||
.defaultValue=${this._preprocessYaml()}
|
||||
|
@ -546,28 +557,7 @@ export class HaScriptEditor extends KeyboardShortcutMixin(LitElement) {
|
|||
});
|
||||
}
|
||||
|
||||
private _modeChanged(mode) {
|
||||
const curMode = this._config!.mode || MODES[0];
|
||||
|
||||
if (mode === curMode) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._config = { ...this._config!, mode };
|
||||
if (!isMaxMode(mode)) {
|
||||
delete this._config.max;
|
||||
}
|
||||
this._dirty = true;
|
||||
}
|
||||
|
||||
private _aliasChanged(alias: string) {
|
||||
if (
|
||||
this.scriptEntityId ||
|
||||
(this._entityId && this._entityId !== slugify(this._config!.alias))
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
private _computeEntityIdFromAlias(alias: string) {
|
||||
const aliasSlugify = slugify(alias);
|
||||
let id = aliasSlugify;
|
||||
let i = 2;
|
||||
|
@ -575,11 +565,10 @@ export class HaScriptEditor extends KeyboardShortcutMixin(LitElement) {
|
|||
id = `${aliasSlugify}_${i}`;
|
||||
i++;
|
||||
}
|
||||
|
||||
this._entityId = id;
|
||||
return id;
|
||||
}
|
||||
|
||||
private _idChanged(id: string) {
|
||||
private _setEntityId(id?: string) {
|
||||
this._entityId = id;
|
||||
if (this.hass.states[`script.${this._entityId}`]) {
|
||||
this._idError = true;
|
||||
|
@ -588,47 +577,60 @@ export class HaScriptEditor extends KeyboardShortcutMixin(LitElement) {
|
|||
}
|
||||
}
|
||||
|
||||
private updateEntityId(
|
||||
newId: string | undefined,
|
||||
newAlias: string | undefined
|
||||
) {
|
||||
const currentAlias = this._config?.alias ?? "";
|
||||
const currentEntityId = this._entityId ?? "";
|
||||
|
||||
if (newId !== this._entityId) {
|
||||
this._setEntityId(newId || undefined);
|
||||
return;
|
||||
}
|
||||
|
||||
const currentComputedEntity = this._computeEntityIdFromAlias(currentAlias);
|
||||
|
||||
if (currentComputedEntity === currentEntityId || !this._entityId) {
|
||||
const newComputedId = newAlias
|
||||
? this._computeEntityIdFromAlias(newAlias)
|
||||
: undefined;
|
||||
|
||||
this._setEntityId(newComputedId);
|
||||
}
|
||||
}
|
||||
|
||||
private _valueChanged(ev: CustomEvent) {
|
||||
ev.stopPropagation();
|
||||
this._errors = undefined;
|
||||
const values = ev.detail.value as any;
|
||||
const currentId = this._entityId;
|
||||
|
||||
let changed = false;
|
||||
const newValues: Omit<ScriptConfig, "sequence"> = {
|
||||
alias: values.alias ?? "",
|
||||
icon: values.icon,
|
||||
mode: values.mode,
|
||||
max: isMaxMode(values.mode) ? values.max : undefined,
|
||||
};
|
||||
|
||||
for (const key of Object.keys(values)) {
|
||||
if (key === "sequence") {
|
||||
if (!this.scriptEntityId) {
|
||||
this.updateEntityId(values.id, values.alias);
|
||||
}
|
||||
|
||||
for (const key of Object.keys(newValues)) {
|
||||
const value = newValues[key];
|
||||
|
||||
if (value === this._config![key]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const value = values[key];
|
||||
|
||||
if (
|
||||
value === this._config![key] ||
|
||||
(key === "id" && currentId === value)
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
changed = true;
|
||||
|
||||
switch (key) {
|
||||
case "id":
|
||||
this._idChanged(value);
|
||||
break;
|
||||
case "alias":
|
||||
this._aliasChanged(value);
|
||||
break;
|
||||
case "mode":
|
||||
this._modeChanged(value);
|
||||
break;
|
||||
}
|
||||
|
||||
if (values[key] === undefined) {
|
||||
if (value === undefined) {
|
||||
const newConfig = { ...this._config! };
|
||||
delete newConfig![key];
|
||||
this._config = newConfig;
|
||||
} else {
|
||||
this._config = { ...this._config!, [key]: value };
|
||||
}
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (changed) {
|
||||
|
@ -638,6 +640,7 @@ export class HaScriptEditor extends KeyboardShortcutMixin(LitElement) {
|
|||
|
||||
private _configChanged(ev) {
|
||||
this._config = ev.detail.value;
|
||||
this._errors = undefined;
|
||||
this._dirty = true;
|
||||
}
|
||||
|
||||
|
@ -759,7 +762,6 @@ export class HaScriptEditor extends KeyboardShortcutMixin(LitElement) {
|
|||
this.hass!.callApi("POST", "config/script/config/" + id, this._config).then(
|
||||
() => {
|
||||
this._dirty = false;
|
||||
|
||||
if (!this.scriptEntityId) {
|
||||
navigate(`/config/script/edit/${id}`, { replace: true });
|
||||
}
|
||||
|
@ -806,6 +808,10 @@ export class HaScriptEditor extends KeyboardShortcutMixin(LitElement) {
|
|||
max-width: 1040px;
|
||||
padding: 28px 20px 0;
|
||||
}
|
||||
.config-container ha-alert {
|
||||
margin-bottom: 16px;
|
||||
display: block;
|
||||
}
|
||||
ha-yaml-editor {
|
||||
flex-grow: 1;
|
||||
--code-mirror-height: 100%;
|
||||
|
|
|
@ -130,13 +130,18 @@ class MoveDatadiskDialog extends LitElement {
|
|||
@selected=${this._select_device}
|
||||
@closed=${stopPropagation}
|
||||
dialogInitialFocus
|
||||
fixedMenuPosition
|
||||
>
|
||||
${this._devices.map(
|
||||
(device) =>
|
||||
html`<mwc-list-item .value=${device}
|
||||
>${device}</mwc-list-item
|
||||
>`
|
||||
html`<mwc-list-item .value=${device}>
|
||||
${device}
|
||||
</mwc-list-item>`
|
||||
)}
|
||||
<mwc-list-item>Test</mwc-list-item>
|
||||
<mwc-list-item>Test</mwc-list-item>
|
||||
<mwc-list-item>Test</mwc-list-item>
|
||||
<mwc-list-item>Test</mwc-list-item>
|
||||
</ha-select>
|
||||
|
||||
<mwc-button
|
||||
|
@ -175,8 +180,9 @@ class MoveDatadiskDialog extends LitElement {
|
|||
),
|
||||
text: extractApiErrorMessage(err),
|
||||
});
|
||||
this.closeDialog();
|
||||
}
|
||||
} finally {
|
||||
this.closeDialog();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { PropertyValues, ReactiveElement } from "lit";
|
||||
import { property } from "lit/decorators";
|
||||
import { navigate, NavigateOptions } from "../../common/navigate";
|
||||
import { deepEqual } from "../../common/util/deep-equal";
|
||||
import { CustomPanelInfo } from "../../data/panel_custom";
|
||||
import { HomeAssistant, Route } from "../../types";
|
||||
import { createCustomPanelElement } from "../../util/custom-panel/create-custom-panel-element";
|
||||
|
@ -54,12 +55,15 @@ export class HaPanelCustom extends ReactiveElement {
|
|||
protected update(changedProps: PropertyValues) {
|
||||
super.update(changedProps);
|
||||
if (changedProps.has("panel")) {
|
||||
// Clean up old things if we had a panel
|
||||
if (changedProps.get("panel")) {
|
||||
this._cleanupPanel();
|
||||
// Clean up old things if we had a panel and the new one is different.
|
||||
const oldPanel = changedProps.get("panel") as CustomPanelInfo | undefined;
|
||||
if (!deepEqual(oldPanel, this.panel)) {
|
||||
if (oldPanel) {
|
||||
this._cleanupPanel();
|
||||
}
|
||||
this._createPanel(this.panel);
|
||||
return;
|
||||
}
|
||||
this._createPanel(this.panel);
|
||||
return;
|
||||
}
|
||||
if (!this._setProperties) {
|
||||
return;
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
import "../../../../components/ha-form/ha-form";
|
||||
import { html, LitElement, TemplateResult } from "lit";
|
||||
import { customElement, property, state } from "lit/decorators";
|
||||
import memoizeOne from "memoize-one";
|
||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
||||
import { slugify } from "../../../../common/string/slugify";
|
||||
import type { LocalizeFunc } from "../../../../common/translations/localize";
|
||||
import "../../../../components/ha-form/ha-form";
|
||||
import type { SchemaUnion } from "../../../../components/ha-form/types";
|
||||
import type { LovelaceViewConfig } from "../../../../data/lovelace";
|
||||
import type { HomeAssistant } from "../../../../types";
|
||||
|
@ -33,7 +33,7 @@ export class HuiViewEditor extends LitElement {
|
|||
private _suggestedPath = false;
|
||||
|
||||
private _schema = memoizeOne(
|
||||
(localize: LocalizeFunc) =>
|
||||
(localize: LocalizeFunc, subview: boolean, showAdvanced: boolean) =>
|
||||
[
|
||||
{ name: "title", selector: { text: {} } },
|
||||
{
|
||||
|
@ -63,6 +63,20 @@ export class HuiViewEditor extends LitElement {
|
|||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "subview",
|
||||
selector: {
|
||||
boolean: {},
|
||||
},
|
||||
},
|
||||
...(subview && showAdvanced
|
||||
? [
|
||||
{
|
||||
name: "back_path",
|
||||
selector: { navigation: {} },
|
||||
},
|
||||
]
|
||||
: []),
|
||||
] as const
|
||||
);
|
||||
|
||||
|
@ -84,7 +98,12 @@ export class HuiViewEditor extends LitElement {
|
|||
return html``;
|
||||
}
|
||||
|
||||
const schema = this._schema(this.hass.localize);
|
||||
const schema = this._schema(
|
||||
this.hass.localize,
|
||||
this._config.subview ?? false,
|
||||
this.hass.userData?.showAdvanced ?? false
|
||||
);
|
||||
|
||||
const data = {
|
||||
theme: "Backend-selected",
|
||||
...this._config,
|
||||
|
@ -96,18 +115,22 @@ export class HuiViewEditor extends LitElement {
|
|||
.hass=${this.hass}
|
||||
.data=${data}
|
||||
.schema=${schema}
|
||||
.computeLabel=${this._computeLabelCallback}
|
||||
.computeLabel=${this._computeLabel}
|
||||
.computeHelper=${this._computeHelper}
|
||||
@value-changed=${this._valueChanged}
|
||||
></ha-form>
|
||||
`;
|
||||
}
|
||||
|
||||
private _valueChanged(ev: CustomEvent): void {
|
||||
const config = ev.detail.value;
|
||||
const config = ev.detail.value as LovelaceViewConfig;
|
||||
|
||||
if (config.type === "masonry") {
|
||||
delete config.type;
|
||||
}
|
||||
if (!config.subview) {
|
||||
delete config.back_path;
|
||||
}
|
||||
|
||||
if (
|
||||
this.isNew &&
|
||||
|
@ -122,7 +145,7 @@ export class HuiViewEditor extends LitElement {
|
|||
fireEvent(this, "view-config-changed", { config });
|
||||
}
|
||||
|
||||
private _computeLabelCallback = (
|
||||
private _computeLabel = (
|
||||
schema: SchemaUnion<ReturnType<typeof this._schema>>
|
||||
) => {
|
||||
switch (schema.name) {
|
||||
|
@ -130,12 +153,35 @@ export class HuiViewEditor extends LitElement {
|
|||
return this.hass!.localize("ui.panel.lovelace.editor.card.generic.url");
|
||||
case "type":
|
||||
return this.hass.localize("ui.panel.lovelace.editor.edit_view.type");
|
||||
case "subview":
|
||||
return this.hass.localize("ui.panel.lovelace.editor.edit_view.subview");
|
||||
case "back_path":
|
||||
return this.hass.localize(
|
||||
"ui.panel.lovelace.editor.edit_view.back_path"
|
||||
);
|
||||
default:
|
||||
return this.hass!.localize(
|
||||
`ui.panel.lovelace.editor.card.generic.${schema.name}`
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
private _computeHelper = (
|
||||
schema: SchemaUnion<ReturnType<typeof this._schema>>
|
||||
) => {
|
||||
switch (schema.name) {
|
||||
case "subview":
|
||||
return this.hass.localize(
|
||||
"ui.panel.lovelace.editor.edit_view.subview_helper"
|
||||
);
|
||||
case "back_path":
|
||||
return this.hass.localize(
|
||||
"ui.panel.lovelace.editor.edit_view.back_path_helper"
|
||||
);
|
||||
default:
|
||||
return undefined;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
declare global {
|
||||
|
|
|
@ -112,6 +112,11 @@ class HUIRoot extends LitElement {
|
|||
}
|
||||
|
||||
protected render(): TemplateResult {
|
||||
const views = this.lovelace?.config.views ?? [];
|
||||
|
||||
const curViewConfig =
|
||||
typeof this._curView === "number" ? views[this._curView] : undefined;
|
||||
|
||||
return html`
|
||||
<ha-app-layout
|
||||
class=${classMap({
|
||||
|
@ -229,11 +234,21 @@ class HUIRoot extends LitElement {
|
|||
`
|
||||
: html`
|
||||
<app-toolbar>
|
||||
<ha-menu-button
|
||||
.hass=${this.hass}
|
||||
.narrow=${this.narrow}
|
||||
></ha-menu-button>
|
||||
${this.lovelace!.config.views.length > 1
|
||||
${curViewConfig?.subview
|
||||
? html`
|
||||
<ha-icon-button-arrow-prev
|
||||
@click=${this._goBack}
|
||||
></ha-icon-button-arrow-prev>
|
||||
`
|
||||
: html`
|
||||
<ha-menu-button
|
||||
.hass=${this.hass}
|
||||
.narrow=${this.narrow}
|
||||
></ha-menu-button>
|
||||
`}
|
||||
${curViewConfig?.subview
|
||||
? html`<div main-title>${curViewConfig.title}</div>`
|
||||
: views.filter((view) => !view.subview).length > 1
|
||||
? html`
|
||||
<ha-tabs
|
||||
scrollable
|
||||
|
@ -241,18 +256,20 @@ class HUIRoot extends LitElement {
|
|||
@iron-activate=${this._handleViewSelected}
|
||||
dir=${computeRTLDirection(this.hass!)}
|
||||
>
|
||||
${this.lovelace!.config.views.map(
|
||||
${views.map(
|
||||
(view) => html`
|
||||
<paper-tab
|
||||
aria-label=${ifDefined(view.title)}
|
||||
class=${classMap({
|
||||
"hide-tab": Boolean(
|
||||
view.visible !== undefined &&
|
||||
((Array.isArray(view.visible) &&
|
||||
!view.visible.some(
|
||||
(e) => e.user === this.hass!.user!.id
|
||||
)) ||
|
||||
view.visible === false)
|
||||
view.subview ||
|
||||
(view.visible !== undefined &&
|
||||
((Array.isArray(view.visible) &&
|
||||
!view.visible.some(
|
||||
(e) =>
|
||||
e.user === this.hass!.user!.id
|
||||
)) ||
|
||||
view.visible === false))
|
||||
),
|
||||
})}
|
||||
>
|
||||
|
@ -473,7 +490,7 @@ class HUIRoot extends LitElement {
|
|||
@iron-activate=${this._handleViewSelected}
|
||||
dir=${computeRTLDirection(this.hass!)}
|
||||
>
|
||||
${this.lovelace!.config.views.map(
|
||||
${views.map(
|
||||
(view) => html`
|
||||
<paper-tab
|
||||
aria-label=${ifDefined(view.title)}
|
||||
|
@ -505,6 +522,9 @@ class HUIRoot extends LitElement {
|
|||
${view.icon
|
||||
? html`
|
||||
<ha-icon
|
||||
class=${classMap({
|
||||
"child-view-icon": Boolean(view.subview),
|
||||
})}
|
||||
title=${ifDefined(view.title)}
|
||||
.icon=${view.icon}
|
||||
></ha-icon>
|
||||
|
@ -528,7 +548,7 @@ class HUIRoot extends LitElement {
|
|||
class="edit-icon view"
|
||||
@click=${this._moveViewRight}
|
||||
?disabled=${(this._curView! as number) + 1 ===
|
||||
this.lovelace!.config.views.length}
|
||||
views.length}
|
||||
></ha-icon-button-arrow-next>
|
||||
`
|
||||
: ""}
|
||||
|
@ -720,6 +740,20 @@ class HUIRoot extends LitElement {
|
|||
});
|
||||
}
|
||||
|
||||
private _goBack(): void {
|
||||
const views = this.lovelace?.config.views ?? [];
|
||||
const curViewConfig =
|
||||
typeof this._curView === "number" ? views[this._curView] : undefined;
|
||||
|
||||
if (curViewConfig?.back_path) {
|
||||
navigate(curViewConfig.back_path);
|
||||
} else if (history.length > 0) {
|
||||
history.back();
|
||||
} else {
|
||||
navigate(views[0].path!);
|
||||
}
|
||||
}
|
||||
|
||||
private _handleRawEditor(ev: CustomEvent<RequestSelectedDetail>): void {
|
||||
if (!shouldHandleRequestSelectedEvent(ev)) {
|
||||
return;
|
||||
|
@ -1019,6 +1053,9 @@ class HUIRoot extends LitElement {
|
|||
--mdc-button-outline-color: var(--app-header-edit-text-color, #fff);
|
||||
--mdc-typography-button-font-size: 14px;
|
||||
}
|
||||
.child-view-icon {
|
||||
opacity: 0.5;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
|
|
|
@ -1002,6 +1002,15 @@
|
|||
"attribute": "Attribute",
|
||||
"min_max_change": "min/max/change"
|
||||
},
|
||||
"zha_manage_device": {
|
||||
"heading": "Manage Zigbee Device",
|
||||
"tabs": {
|
||||
"clusters": "Clusters",
|
||||
"bindings": "Bindings",
|
||||
"signature": "Signature",
|
||||
"children": "Children"
|
||||
}
|
||||
},
|
||||
"zha_device_info": {
|
||||
"manuf": "by {manufacturer}",
|
||||
"no_area": "No Area",
|
||||
|
@ -1010,10 +1019,8 @@
|
|||
"buttons": {
|
||||
"add": "Add devices via this device",
|
||||
"remove": "Remove",
|
||||
"clusters": "Manage clusters",
|
||||
"manage": "Manage zigbee device",
|
||||
"reconfigure": "Reconfigure",
|
||||
"zigbee_information": "Zigbee signature",
|
||||
"device_children": "View children",
|
||||
"view_network": "View network"
|
||||
},
|
||||
"services": {
|
||||
|
@ -1796,7 +1803,8 @@
|
|||
"dev_automation": "Debug automation",
|
||||
"show_info_automation": "Show info about automation",
|
||||
"delete": "[%key:ui::common::delete%]",
|
||||
"delete_confirm": "Are you sure you want to delete this automation?",
|
||||
"delete_confirm_title": "Delete automation?",
|
||||
"delete_confirm_text": "{name} will be permanently deleted.",
|
||||
"duplicate": "[%key:ui::common::duplicate%]",
|
||||
"disabled": "Disabled",
|
||||
"headers": {
|
||||
|
@ -2257,15 +2265,15 @@
|
|||
"add": {
|
||||
"header": "Import a blueprint",
|
||||
"import_header": "Blueprint ''{name}''",
|
||||
"import_introduction_link": "You can import blueprints of other users from Github and the {community_link}. Enter the URL of the blueprint below.",
|
||||
"community_forums": "community forums",
|
||||
"url": "URL of the blueprint",
|
||||
"import_introduction": "Import blueprints of other users from GitHub and the community forums by pasting the address below.",
|
||||
"community_forums": "View blueprints on the community forums",
|
||||
"url": "Blueprint address",
|
||||
"raw_blueprint": "Blueprint content",
|
||||
"importing": "Loading blueprint…",
|
||||
"import_btn": "Preview blueprint",
|
||||
"import_btn": "Preview",
|
||||
"saving": "Importing blueprint…",
|
||||
"save_btn": "Import blueprint",
|
||||
"error_no_url": "Please enter the URL of the blueprint.",
|
||||
"error_no_url": "Please enter the blueprint address.",
|
||||
"unsupported_blueprint": "This blueprint is not supported",
|
||||
"file_name": "Blueprint Path"
|
||||
}
|
||||
|
@ -3013,12 +3021,16 @@
|
|||
"caption": "Application Credentials",
|
||||
"description": "Manage the OAuth Application Credentials used by Integrations",
|
||||
"editor": {
|
||||
"caption": "Add Application Credential",
|
||||
"create": "Create",
|
||||
"caption": "Add Credential",
|
||||
"description": "OAuth is used to grant Home Assistant access to information on other websites without giving a passwords. This mechanism is used by companies such as Spotify, Google, Withings, Microsoft, and Twitter.",
|
||||
"view_documentation": "View documentation",
|
||||
"add": "Add",
|
||||
"domain": "Integration",
|
||||
"name": "Name",
|
||||
"client_id": "OAuth Client ID",
|
||||
"client_secret": "OAuth Client Secret"
|
||||
"client_id_helper": "Public identifier of the OAuth application",
|
||||
"client_secret": "OAuth Client Secret",
|
||||
"client_secret_helper": "Secret of the OAuth application"
|
||||
},
|
||||
"picker": {
|
||||
"add_application_credential": "Add Application Credential",
|
||||
|
@ -3073,17 +3085,17 @@
|
|||
"clusters": {
|
||||
"header": "Clusters",
|
||||
"help_cluster_dropdown": "Select a cluster to view attributes and commands.",
|
||||
"introduction": "Clusters are the building blocks for Zigbee functionality. They separate functionality into logical units. There are client and server types and that are comprised of attributes and commands."
|
||||
"tabs": {
|
||||
"attributes": "Attributes",
|
||||
"commands": "Commands"
|
||||
}
|
||||
},
|
||||
"cluster_attributes": {
|
||||
"header": "Cluster Attributes",
|
||||
"introduction": "View and edit cluster attributes.",
|
||||
"attributes_of_cluster": "Attributes of the selected cluster",
|
||||
"get_zigbee_attribute": "Get Zigbee Attribute",
|
||||
"set_zigbee_attribute": "Set Zigbee Attribute",
|
||||
"help_attribute_dropdown": "Select an attribute to view or set its value.",
|
||||
"help_get_zigbee_attribute": "Get the value for the selected attribute.",
|
||||
"help_set_zigbee_attribute": "Set attribute value for the specified cluster on the specified entity."
|
||||
"read_zigbee_attribute": "Read Attribute",
|
||||
"write_zigbee_attribute": "Write Attribute"
|
||||
},
|
||||
"cluster_commands": {
|
||||
"header": "Cluster Commands",
|
||||
|
@ -3133,6 +3145,11 @@
|
|||
"enable_physics": "Enable Physics",
|
||||
"refresh_topology": "Refresh Topology"
|
||||
},
|
||||
"device_binding": {
|
||||
"bind": "Bind",
|
||||
"unbind": "Unbind",
|
||||
"picker_label": "Bindable Devices"
|
||||
},
|
||||
"group_binding": {
|
||||
"header": "Group Binding",
|
||||
"introduction": "Bind and unbind groups.",
|
||||
|
@ -3748,7 +3765,11 @@
|
|||
"masonry": "Masonry (default)",
|
||||
"sidebar": "Sidebar",
|
||||
"panel": "Panel (1 card)"
|
||||
}
|
||||
},
|
||||
"subview": "Subview",
|
||||
"subview_helper": "Subviews don't appear in tabs and have a back button.",
|
||||
"back_path": "Back path (optional)",
|
||||
"back_path_helper": "Only for subviews. If empty, clicking on back button will go to the previous page."
|
||||
},
|
||||
"edit_badges": {
|
||||
"view_no_badges": "Badges are not be supported by the current view type."
|
||||
|
|
Loading…
Reference in New Issue