mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-13 20:36:35 +00:00
Move Logbook and make device page better (#12763)
Co-authored-by: Bram Kragten <mail@bramkragten.nl>
This commit is contained in:
parent
d7971c69ad
commit
e61aa266a6
@ -19,7 +19,6 @@ import { afterNextRender } from "../../../common/util/render-status";
|
|||||||
import "../../../components/ha-card";
|
import "../../../components/ha-card";
|
||||||
import "../../../components/ha-icon-button";
|
import "../../../components/ha-icon-button";
|
||||||
import "../../../components/ha-icon-next";
|
import "../../../components/ha-icon-next";
|
||||||
import "../../logbook/ha-logbook";
|
|
||||||
import {
|
import {
|
||||||
AreaRegistryEntry,
|
AreaRegistryEntry,
|
||||||
deleteAreaRegistryEntry,
|
deleteAreaRegistryEntry,
|
||||||
@ -43,15 +42,16 @@ import { SceneEntity } from "../../../data/scene";
|
|||||||
import { ScriptEntity } from "../../../data/script";
|
import { ScriptEntity } from "../../../data/script";
|
||||||
import { findRelated, RelatedResult } from "../../../data/search";
|
import { findRelated, RelatedResult } from "../../../data/search";
|
||||||
import { showConfirmationDialog } from "../../../dialogs/generic/show-dialog-box";
|
import { showConfirmationDialog } from "../../../dialogs/generic/show-dialog-box";
|
||||||
|
import { SubscribeMixin } from "../../../mixins/subscribe-mixin";
|
||||||
import { haStyle } from "../../../resources/styles";
|
import { haStyle } from "../../../resources/styles";
|
||||||
import { HomeAssistant, Route } from "../../../types";
|
import { HomeAssistant, Route } from "../../../types";
|
||||||
|
import "../../logbook/ha-logbook";
|
||||||
import { showEntityEditorDialog } from "../entities/show-dialog-entity-editor";
|
import { showEntityEditorDialog } from "../entities/show-dialog-entity-editor";
|
||||||
import { configSections } from "../ha-panel-config";
|
import { configSections } from "../ha-panel-config";
|
||||||
import {
|
import {
|
||||||
loadAreaRegistryDetailDialog,
|
loadAreaRegistryDetailDialog,
|
||||||
showAreaRegistryDetailDialog,
|
showAreaRegistryDetailDialog,
|
||||||
} from "./show-dialog-area-registry-detail";
|
} from "./show-dialog-area-registry-detail";
|
||||||
import { SubscribeMixin } from "../../../mixins/subscribe-mixin";
|
|
||||||
|
|
||||||
declare type NameAndEntity<EntityType extends HassEntity> = {
|
declare type NameAndEntity<EntityType extends HassEntity> = {
|
||||||
name: string;
|
name: string;
|
||||||
@ -761,10 +761,10 @@ class HaConfigAreaPage extends SubscribeMixin(LitElement) {
|
|||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
}
|
}
|
||||||
ha-logbook {
|
ha-logbook {
|
||||||
height: 800px;
|
height: 400px;
|
||||||
}
|
}
|
||||||
:host([narrow]) ha-logbook {
|
:host([narrow]) ha-logbook {
|
||||||
height: 400px;
|
height: 235px;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
|
@ -0,0 +1,13 @@
|
|||||||
|
import type { DeviceRegistryEntry } from "../../../../../../data/device_registry";
|
||||||
|
import type { DeviceAction } from "../../../ha-config-device-page";
|
||||||
|
import { showMQTTDeviceDebugInfoDialog } from "./show-dialog-mqtt-device-debug-info";
|
||||||
|
|
||||||
|
export const getMQTTDeviceActions = (
|
||||||
|
el: HTMLElement,
|
||||||
|
device: DeviceRegistryEntry
|
||||||
|
): DeviceAction[] => [
|
||||||
|
{
|
||||||
|
label: "MQTT Info",
|
||||||
|
action: async () => showMQTTDeviceDebugInfoDialog(el, { device }),
|
||||||
|
},
|
||||||
|
];
|
@ -1,36 +0,0 @@
|
|||||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
|
||||||
import { customElement, property } from "lit/decorators";
|
|
||||||
import { DeviceRegistryEntry } from "../../../../../../data/device_registry";
|
|
||||||
import { haStyle } from "../../../../../../resources/styles";
|
|
||||||
import { HomeAssistant } from "../../../../../../types";
|
|
||||||
import { showMQTTDeviceDebugInfoDialog } from "./show-dialog-mqtt-device-debug-info";
|
|
||||||
|
|
||||||
@customElement("ha-device-actions-mqtt")
|
|
||||||
export class HaDeviceActionsMqtt extends LitElement {
|
|
||||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
|
||||||
|
|
||||||
@property() public device!: DeviceRegistryEntry;
|
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
|
||||||
return html`
|
|
||||||
<mwc-button @click=${this._showDebugInfo}> MQTT Info </mwc-button>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
private async _showDebugInfo(): Promise<void> {
|
|
||||||
const device = this.device;
|
|
||||||
await showMQTTDeviceDebugInfoDialog(this, { device });
|
|
||||||
}
|
|
||||||
|
|
||||||
static get styles(): CSSResultGroup {
|
|
||||||
return [
|
|
||||||
haStyle,
|
|
||||||
css`
|
|
||||||
:host {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
}
|
|
||||||
`,
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,108 @@
|
|||||||
|
import { navigate } from "../../../../../../common/navigate";
|
||||||
|
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 { showZHAReconfigureDeviceDialog } from "../../../../integrations/integration-panels/zha/show-dialog-zha-reconfigure-device";
|
||||||
|
import type { DeviceAction } from "../../../ha-config-device-page";
|
||||||
|
|
||||||
|
export const getZHADeviceActions = async (
|
||||||
|
el: HTMLElement,
|
||||||
|
hass: HomeAssistant,
|
||||||
|
device: DeviceRegistryEntry
|
||||||
|
): Promise<DeviceAction[]> => {
|
||||||
|
const zigbeeConnection = device.connections.find(
|
||||||
|
(conn) => conn[0] === "zigbee"
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!zigbeeConnection) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
const zhaDevice = await fetchZHADevice(hass, zigbeeConnection[1]);
|
||||||
|
|
||||||
|
if (!zhaDevice) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
const actions: DeviceAction[] = [];
|
||||||
|
|
||||||
|
if (zhaDevice.device_type !== "Coordinator") {
|
||||||
|
actions.push({
|
||||||
|
label: hass.localize("ui.dialogs.zha_device_info.buttons.reconfigure"),
|
||||||
|
action: () => showZHAReconfigureDeviceDialog(el, { device: zhaDevice }),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
zhaDevice.power_source === "Mains" &&
|
||||||
|
(zhaDevice.device_type === "Router" ||
|
||||||
|
zhaDevice.device_type === "Coordinator")
|
||||||
|
) {
|
||||||
|
actions.push(
|
||||||
|
...[
|
||||||
|
{
|
||||||
|
label: hass.localize("ui.dialogs.zha_device_info.buttons.add"),
|
||||||
|
action: () => navigate(`/config/zha/add/${zhaDevice!.ieee}`),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: hass.localize(
|
||||||
|
"ui.dialogs.zha_device_info.buttons.device_children"
|
||||||
|
),
|
||||||
|
action: () => showZHADeviceChildrenDialog(el, { device: zhaDevice! }),
|
||||||
|
},
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (zhaDevice.device_type !== "Coordinator") {
|
||||||
|
actions.push(
|
||||||
|
...[
|
||||||
|
{
|
||||||
|
label: hass.localize(
|
||||||
|
"ui.dialogs.zha_device_info.buttons.zigbee_information"
|
||||||
|
),
|
||||||
|
action: () =>
|
||||||
|
showZHADeviceZigbeeInfoDialog(el, { device: zhaDevice }),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: hass.localize("ui.dialogs.zha_device_info.buttons.clusters"),
|
||||||
|
action: () => showZHAClusterDialog(el, { device: zhaDevice }),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: hass.localize(
|
||||||
|
"ui.dialogs.zha_device_info.buttons.view_in_visualization"
|
||||||
|
),
|
||||||
|
action: () =>
|
||||||
|
navigate(`/config/zha/visualization/${zhaDevice!.device_reg_id}`),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: hass.localize("ui.dialogs.zha_device_info.buttons.remove"),
|
||||||
|
classes: "warning",
|
||||||
|
action: async () => {
|
||||||
|
const confirmed = await showConfirmationDialog(el, {
|
||||||
|
text: hass.localize(
|
||||||
|
"ui.dialogs.zha_device_info.confirmations.remove"
|
||||||
|
),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!confirmed) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await hass.callService("zha", "remove", {
|
||||||
|
ieee: zhaDevice.ieee,
|
||||||
|
});
|
||||||
|
|
||||||
|
history.back();
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return actions;
|
||||||
|
};
|
@ -1,155 +0,0 @@
|
|||||||
import {
|
|
||||||
css,
|
|
||||||
CSSResultGroup,
|
|
||||||
html,
|
|
||||||
LitElement,
|
|
||||||
PropertyValues,
|
|
||||||
TemplateResult,
|
|
||||||
} from "lit";
|
|
||||||
import { customElement, property, state } from "lit/decorators";
|
|
||||||
import { navigate } from "../../../../../../common/navigate";
|
|
||||||
import { DeviceRegistryEntry } from "../../../../../../data/device_registry";
|
|
||||||
import { fetchZHADevice, ZHADevice } from "../../../../../../data/zha";
|
|
||||||
import { showConfirmationDialog } from "../../../../../../dialogs/generic/show-dialog-box";
|
|
||||||
import { haStyle } from "../../../../../../resources/styles";
|
|
||||||
import { 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 { showZHAReconfigureDeviceDialog } from "../../../../integrations/integration-panels/zha/show-dialog-zha-reconfigure-device";
|
|
||||||
|
|
||||||
@customElement("ha-device-actions-zha")
|
|
||||||
export class HaDeviceActionsZha extends LitElement {
|
|
||||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
|
||||||
|
|
||||||
@property() public device!: DeviceRegistryEntry;
|
|
||||||
|
|
||||||
@state() private _zhaDevice?: ZHADevice;
|
|
||||||
|
|
||||||
protected updated(changedProperties: PropertyValues) {
|
|
||||||
if (changedProperties.has("device")) {
|
|
||||||
const zigbeeConnection = this.device.connections.find(
|
|
||||||
(conn) => conn[0] === "zigbee"
|
|
||||||
);
|
|
||||||
if (!zigbeeConnection) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
fetchZHADevice(this.hass, zigbeeConnection[1]).then((device) => {
|
|
||||||
this._zhaDevice = device;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
|
||||||
if (!this._zhaDevice) {
|
|
||||||
return html``;
|
|
||||||
}
|
|
||||||
return html`
|
|
||||||
${this._zhaDevice.device_type !== "Coordinator"
|
|
||||||
? html`
|
|
||||||
<mwc-button @click=${this._onReconfigureNodeClick}>
|
|
||||||
${this.hass!.localize(
|
|
||||||
"ui.dialogs.zha_device_info.buttons.reconfigure"
|
|
||||||
)}
|
|
||||||
</mwc-button>
|
|
||||||
`
|
|
||||||
: ""}
|
|
||||||
${this._zhaDevice.power_source === "Mains" &&
|
|
||||||
(this._zhaDevice.device_type === "Router" ||
|
|
||||||
this._zhaDevice.device_type === "Coordinator")
|
|
||||||
? html`
|
|
||||||
<mwc-button @click=${this._onAddDevicesClick}>
|
|
||||||
${this.hass!.localize("ui.dialogs.zha_device_info.buttons.add")}
|
|
||||||
</mwc-button>
|
|
||||||
<mwc-button @click=${this._handleDeviceChildrenClicked}>
|
|
||||||
${this.hass!.localize(
|
|
||||||
"ui.dialogs.zha_device_info.buttons.device_children"
|
|
||||||
)}
|
|
||||||
</mwc-button>
|
|
||||||
`
|
|
||||||
: ""}
|
|
||||||
${this._zhaDevice.device_type !== "Coordinator"
|
|
||||||
? html`
|
|
||||||
<mwc-button @click=${this._handleZigbeeInfoClicked}>
|
|
||||||
${this.hass!.localize(
|
|
||||||
"ui.dialogs.zha_device_info.buttons.zigbee_information"
|
|
||||||
)}
|
|
||||||
</mwc-button>
|
|
||||||
<mwc-button @click=${this._showClustersDialog}>
|
|
||||||
${this.hass!.localize(
|
|
||||||
"ui.dialogs.zha_device_info.buttons.clusters"
|
|
||||||
)}
|
|
||||||
</mwc-button>
|
|
||||||
<mwc-button @click=${this._onViewInVisualizationClick}>
|
|
||||||
${this.hass!.localize(
|
|
||||||
"ui.dialogs.zha_device_info.buttons.view_in_visualization"
|
|
||||||
)}
|
|
||||||
</mwc-button>
|
|
||||||
<mwc-button class="warning" @click=${this._removeDevice}>
|
|
||||||
${this.hass!.localize(
|
|
||||||
"ui.dialogs.zha_device_info.buttons.remove"
|
|
||||||
)}
|
|
||||||
</mwc-button>
|
|
||||||
`
|
|
||||||
: ""}
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
private async _showClustersDialog(): Promise<void> {
|
|
||||||
await showZHAClusterDialog(this, { device: this._zhaDevice! });
|
|
||||||
}
|
|
||||||
|
|
||||||
private async _onReconfigureNodeClick(): Promise<void> {
|
|
||||||
if (!this.hass) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
showZHAReconfigureDeviceDialog(this, { device: this._zhaDevice! });
|
|
||||||
}
|
|
||||||
|
|
||||||
private _onAddDevicesClick() {
|
|
||||||
navigate(`/config/zha/add/${this._zhaDevice!.ieee}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
private _onViewInVisualizationClick() {
|
|
||||||
navigate(`/config/zha/visualization/${this._zhaDevice!.device_reg_id}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
private async _handleZigbeeInfoClicked() {
|
|
||||||
showZHADeviceZigbeeInfoDialog(this, { device: this._zhaDevice! });
|
|
||||||
}
|
|
||||||
|
|
||||||
private async _handleDeviceChildrenClicked() {
|
|
||||||
showZHADeviceChildrenDialog(this, { device: this._zhaDevice! });
|
|
||||||
}
|
|
||||||
|
|
||||||
private async _removeDevice() {
|
|
||||||
const confirmed = await showConfirmationDialog(this, {
|
|
||||||
text: this.hass.localize(
|
|
||||||
"ui.dialogs.zha_device_info.confirmations.remove"
|
|
||||||
),
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!confirmed) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
await this.hass.callService("zha", "remove", {
|
|
||||||
ieee: this._zhaDevice!.ieee,
|
|
||||||
});
|
|
||||||
|
|
||||||
history.back();
|
|
||||||
}
|
|
||||||
|
|
||||||
static get styles(): CSSResultGroup {
|
|
||||||
return [
|
|
||||||
haStyle,
|
|
||||||
css`
|
|
||||||
:host {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: flex-start;
|
|
||||||
}
|
|
||||||
`,
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
@ -7,6 +7,7 @@ import {
|
|||||||
TemplateResult,
|
TemplateResult,
|
||||||
} from "lit";
|
} from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
|
import "../../../../../../components/ha-expansion-panel";
|
||||||
import { DeviceRegistryEntry } from "../../../../../../data/device_registry";
|
import { DeviceRegistryEntry } from "../../../../../../data/device_registry";
|
||||||
import { fetchZHADevice, ZHADevice } from "../../../../../../data/zha";
|
import { fetchZHADevice, ZHADevice } from "../../../../../../data/zha";
|
||||||
import { haStyle } from "../../../../../../resources/styles";
|
import { haStyle } from "../../../../../../resources/styles";
|
||||||
@ -40,7 +41,7 @@ export class HaDeviceActionsZha extends LitElement {
|
|||||||
return html``;
|
return html``;
|
||||||
}
|
}
|
||||||
return html`
|
return html`
|
||||||
<h4>Zigbee info</h4>
|
<ha-expansion-panel header="Zigbee info">
|
||||||
<div>IEEE: ${this._zhaDevice.ieee}</div>
|
<div>IEEE: ${this._zhaDevice.ieee}</div>
|
||||||
<div>Nwk: ${formatAsPaddedHex(this._zhaDevice.nwk)}</div>
|
<div>Nwk: ${formatAsPaddedHex(this._zhaDevice.nwk)}</div>
|
||||||
<div>Device Type: ${this._zhaDevice.device_type}</div>
|
<div>Device Type: ${this._zhaDevice.device_type}</div>
|
||||||
@ -72,6 +73,7 @@ export class HaDeviceActionsZha extends LitElement {
|
|||||||
</div>
|
</div>
|
||||||
`
|
`
|
||||||
: ""}
|
: ""}
|
||||||
|
</ha-expansion-panel>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -86,6 +88,11 @@ export class HaDeviceActionsZha extends LitElement {
|
|||||||
word-break: break-all;
|
word-break: break-all;
|
||||||
margin-top: 2px;
|
margin-top: 2px;
|
||||||
}
|
}
|
||||||
|
ha-expansion-panel {
|
||||||
|
--expansion-panel-summary-padding: 0;
|
||||||
|
--expansion-panel-content-padding: 0;
|
||||||
|
padding-top: 4px;
|
||||||
|
}
|
||||||
`,
|
`,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,69 @@
|
|||||||
|
import { getConfigEntries } from "../../../../../../data/config_entries";
|
||||||
|
import { DeviceRegistryEntry } from "../../../../../../data/device_registry";
|
||||||
|
import { fetchZwaveNodeStatus } from "../../../../../../data/zwave_js";
|
||||||
|
import type { HomeAssistant } from "../../../../../../types";
|
||||||
|
import { showZWaveJSHealNodeDialog } from "../../../../integrations/integration-panels/zwave_js/show-dialog-zwave_js-heal-node";
|
||||||
|
import { showZWaveJSReinterviewNodeDialog } from "../../../../integrations/integration-panels/zwave_js/show-dialog-zwave_js-reinterview-node";
|
||||||
|
import { showZWaveJSRemoveFailedNodeDialog } from "../../../../integrations/integration-panels/zwave_js/show-dialog-zwave_js-remove-failed-node";
|
||||||
|
import type { DeviceAction } from "../../../ha-config-device-page";
|
||||||
|
|
||||||
|
export const getZwaveDeviceActions = async (
|
||||||
|
el: HTMLElement,
|
||||||
|
hass: HomeAssistant,
|
||||||
|
device: DeviceRegistryEntry
|
||||||
|
): Promise<DeviceAction[]> => {
|
||||||
|
const configEntries = await getConfigEntries(hass, {
|
||||||
|
domain: "zwave_js",
|
||||||
|
});
|
||||||
|
|
||||||
|
const configEntry = configEntries.find((entry) =>
|
||||||
|
device.config_entries.includes(entry.entry_id)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!configEntry) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
const entryId = configEntry.entry_id;
|
||||||
|
|
||||||
|
const node = await fetchZwaveNodeStatus(hass, device.id);
|
||||||
|
|
||||||
|
if (!node || node.is_controller_node) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
label: hass.localize(
|
||||||
|
"ui.panel.config.zwave_js.device_info.device_config"
|
||||||
|
),
|
||||||
|
href: `/config/zwave_js/node_config/${device.id}?config_entry=${entryId}`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: hass.localize(
|
||||||
|
"ui.panel.config.zwave_js.device_info.reinterview_device"
|
||||||
|
),
|
||||||
|
action: () =>
|
||||||
|
showZWaveJSReinterviewNodeDialog(el, {
|
||||||
|
device_id: device.id,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: hass.localize("ui.panel.config.zwave_js.device_info.heal_node"),
|
||||||
|
action: () =>
|
||||||
|
showZWaveJSHealNodeDialog(el, {
|
||||||
|
entry_id: entryId,
|
||||||
|
device: device,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: hass.localize(
|
||||||
|
"ui.panel.config.zwave_js.device_info.remove_failed"
|
||||||
|
),
|
||||||
|
action: () =>
|
||||||
|
showZWaveJSRemoveFailedNodeDialog(el, {
|
||||||
|
device_id: device.id,
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
};
|
@ -1,138 +0,0 @@
|
|||||||
import "@material/mwc-button/mwc-button";
|
|
||||||
import {
|
|
||||||
css,
|
|
||||||
CSSResultGroup,
|
|
||||||
html,
|
|
||||||
LitElement,
|
|
||||||
PropertyValues,
|
|
||||||
TemplateResult,
|
|
||||||
} from "lit";
|
|
||||||
import { customElement, property, state } from "lit/decorators";
|
|
||||||
import { DeviceRegistryEntry } from "../../../../../../data/device_registry";
|
|
||||||
import {
|
|
||||||
fetchZwaveNodeStatus,
|
|
||||||
ZWaveJSNodeStatus,
|
|
||||||
} from "../../../../../../data/zwave_js";
|
|
||||||
import { haStyle } from "../../../../../../resources/styles";
|
|
||||||
import { HomeAssistant } from "../../../../../../types";
|
|
||||||
import { showZWaveJSReinterviewNodeDialog } from "../../../../integrations/integration-panels/zwave_js/show-dialog-zwave_js-reinterview-node";
|
|
||||||
import { showZWaveJSHealNodeDialog } from "../../../../integrations/integration-panels/zwave_js/show-dialog-zwave_js-heal-node";
|
|
||||||
import { showZWaveJSRemoveFailedNodeDialog } from "../../../../integrations/integration-panels/zwave_js/show-dialog-zwave_js-remove-failed-node";
|
|
||||||
import { getConfigEntries } from "../../../../../../data/config_entries";
|
|
||||||
|
|
||||||
@customElement("ha-device-actions-zwave_js")
|
|
||||||
export class HaDeviceActionsZWaveJS extends LitElement {
|
|
||||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
|
||||||
|
|
||||||
@property() public device!: DeviceRegistryEntry;
|
|
||||||
|
|
||||||
@state() private _entryId?: string;
|
|
||||||
|
|
||||||
@state() private _node?: ZWaveJSNodeStatus;
|
|
||||||
|
|
||||||
public willUpdate(changedProperties: PropertyValues) {
|
|
||||||
super.willUpdate(changedProperties);
|
|
||||||
if (changedProperties.has("device")) {
|
|
||||||
this._fetchNodeDetails();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected async _fetchNodeDetails() {
|
|
||||||
if (!this.device) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this._node = undefined;
|
|
||||||
|
|
||||||
const configEntries = await getConfigEntries(this.hass, {
|
|
||||||
domain: "zwave_js",
|
|
||||||
});
|
|
||||||
|
|
||||||
const configEntry = configEntries.find((entry) =>
|
|
||||||
this.device.config_entries.includes(entry.entry_id)
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!configEntry) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this._entryId = configEntry.entry_id;
|
|
||||||
|
|
||||||
this._node = await fetchZwaveNodeStatus(this.hass, this.device.id);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
|
||||||
if (!this._node) {
|
|
||||||
return html``;
|
|
||||||
}
|
|
||||||
return html`
|
|
||||||
${!this._node.is_controller_node
|
|
||||||
? html`
|
|
||||||
<a
|
|
||||||
.href=${`/config/zwave_js/node_config/${this.device.id}?config_entry=${this._entryId}`}
|
|
||||||
>
|
|
||||||
<mwc-button>
|
|
||||||
${this.hass.localize(
|
|
||||||
"ui.panel.config.zwave_js.device_info.device_config"
|
|
||||||
)}
|
|
||||||
</mwc-button>
|
|
||||||
</a>
|
|
||||||
<mwc-button @click=${this._reinterviewClicked}>
|
|
||||||
${this.hass.localize(
|
|
||||||
"ui.panel.config.zwave_js.device_info.reinterview_device"
|
|
||||||
)}
|
|
||||||
</mwc-button>
|
|
||||||
<mwc-button @click=${this._healNodeClicked}>
|
|
||||||
${this.hass.localize(
|
|
||||||
"ui.panel.config.zwave_js.device_info.heal_node"
|
|
||||||
)}
|
|
||||||
</mwc-button>
|
|
||||||
<mwc-button @click=${this._removeFailedNode}>
|
|
||||||
${this.hass.localize(
|
|
||||||
"ui.panel.config.zwave_js.device_info.remove_failed"
|
|
||||||
)}
|
|
||||||
</mwc-button>
|
|
||||||
`
|
|
||||||
: ""}
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
private async _reinterviewClicked() {
|
|
||||||
if (!this.device) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
showZWaveJSReinterviewNodeDialog(this, {
|
|
||||||
device_id: this.device.id,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private async _healNodeClicked() {
|
|
||||||
if (!this.device) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
showZWaveJSHealNodeDialog(this, {
|
|
||||||
entry_id: this._entryId!,
|
|
||||||
device: this.device,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private async _removeFailedNode() {
|
|
||||||
if (!this.device) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
showZWaveJSRemoveFailedNodeDialog(this, {
|
|
||||||
device_id: this.device.id,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
static get styles(): CSSResultGroup {
|
|
||||||
return [
|
|
||||||
haStyle,
|
|
||||||
css`
|
|
||||||
a {
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
`,
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
@ -7,16 +7,17 @@ import {
|
|||||||
TemplateResult,
|
TemplateResult,
|
||||||
} from "lit";
|
} from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
import { DeviceRegistryEntry } from "../../../../../../data/device_registry";
|
import "../../../../../../components/ha-expansion-panel";
|
||||||
import {
|
import {
|
||||||
ConfigEntry,
|
ConfigEntry,
|
||||||
getConfigEntries,
|
getConfigEntries,
|
||||||
} from "../../../../../../data/config_entries";
|
} from "../../../../../../data/config_entries";
|
||||||
|
import { DeviceRegistryEntry } from "../../../../../../data/device_registry";
|
||||||
import {
|
import {
|
||||||
fetchZwaveNodeStatus,
|
fetchZwaveNodeStatus,
|
||||||
nodeStatus,
|
nodeStatus,
|
||||||
ZWaveJSNodeStatus,
|
|
||||||
SecurityClass,
|
SecurityClass,
|
||||||
|
ZWaveJSNodeStatus,
|
||||||
} from "../../../../../../data/zwave_js";
|
} from "../../../../../../data/zwave_js";
|
||||||
import { haStyle } from "../../../../../../resources/styles";
|
import { haStyle } from "../../../../../../resources/styles";
|
||||||
import { HomeAssistant } from "../../../../../../types";
|
import { HomeAssistant } from "../../../../../../types";
|
||||||
@ -69,9 +70,11 @@ export class HaDeviceInfoZWaveJS extends LitElement {
|
|||||||
return html``;
|
return html``;
|
||||||
}
|
}
|
||||||
return html`
|
return html`
|
||||||
<h4>
|
<ha-expansion-panel
|
||||||
${this.hass.localize("ui.panel.config.zwave_js.device_info.zwave_info")}
|
.header=${this.hass.localize(
|
||||||
</h4>
|
"ui.panel.config.zwave_js.device_info.zwave_info"
|
||||||
|
)}
|
||||||
|
>
|
||||||
${this._multipleConfigEntries
|
${this._multipleConfigEntries
|
||||||
? html`
|
? html`
|
||||||
<div>
|
<div>
|
||||||
@ -136,6 +139,7 @@ export class HaDeviceInfoZWaveJS extends LitElement {
|
|||||||
</div>
|
</div>
|
||||||
`
|
`
|
||||||
: ""}
|
: ""}
|
||||||
|
</ha-expansion-panel>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -150,6 +154,11 @@ export class HaDeviceInfoZWaveJS extends LitElement {
|
|||||||
word-break: break-all;
|
word-break: break-all;
|
||||||
margin-top: 2px;
|
margin-top: 2px;
|
||||||
}
|
}
|
||||||
|
ha-expansion-panel {
|
||||||
|
--expansion-panel-summary-padding: 0;
|
||||||
|
--expansion-panel-content-padding: 0;
|
||||||
|
padding-top: 4px;
|
||||||
|
}
|
||||||
`,
|
`,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,9 @@
|
|||||||
import { mdiOpenInNew, mdiPencil, mdiPlusCircle } from "@mdi/js";
|
import {
|
||||||
|
mdiDotsVertical,
|
||||||
|
mdiOpenInNew,
|
||||||
|
mdiPencil,
|
||||||
|
mdiPlusCircle,
|
||||||
|
} from "@mdi/js";
|
||||||
import "@polymer/paper-tooltip/paper-tooltip";
|
import "@polymer/paper-tooltip/paper-tooltip";
|
||||||
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
@ -13,6 +18,7 @@ import { slugify } from "../../../common/string/slugify";
|
|||||||
import { groupBy } from "../../../common/util/group-by";
|
import { groupBy } from "../../../common/util/group-by";
|
||||||
import "../../../components/entity/ha-battery-icon";
|
import "../../../components/entity/ha-battery-icon";
|
||||||
import "../../../components/ha-alert";
|
import "../../../components/ha-alert";
|
||||||
|
import "../../../components/ha-button-menu";
|
||||||
import "../../../components/ha-icon-button";
|
import "../../../components/ha-icon-button";
|
||||||
import "../../../components/ha-icon-next";
|
import "../../../components/ha-icon-next";
|
||||||
import "../../../components/ha-svg-icon";
|
import "../../../components/ha-svg-icon";
|
||||||
@ -26,14 +32,14 @@ import {
|
|||||||
import {
|
import {
|
||||||
computeDeviceName,
|
computeDeviceName,
|
||||||
DeviceRegistryEntry,
|
DeviceRegistryEntry,
|
||||||
updateDeviceRegistryEntry,
|
|
||||||
removeConfigEntryFromDevice,
|
removeConfigEntryFromDevice,
|
||||||
|
updateDeviceRegistryEntry,
|
||||||
} from "../../../data/device_registry";
|
} from "../../../data/device_registry";
|
||||||
import {
|
import {
|
||||||
fetchDiagnosticHandler,
|
|
||||||
getDeviceDiagnosticsDownloadUrl,
|
|
||||||
getConfigEntryDiagnosticsDownloadUrl,
|
|
||||||
DiagnosticInfo,
|
DiagnosticInfo,
|
||||||
|
fetchDiagnosticHandler,
|
||||||
|
getConfigEntryDiagnosticsDownloadUrl,
|
||||||
|
getDeviceDiagnosticsDownloadUrl,
|
||||||
} from "../../../data/diagnostics";
|
} from "../../../data/diagnostics";
|
||||||
import {
|
import {
|
||||||
EntityRegistryEntry,
|
EntityRegistryEntry,
|
||||||
@ -51,9 +57,10 @@ import {
|
|||||||
import "../../../layouts/hass-error-screen";
|
import "../../../layouts/hass-error-screen";
|
||||||
import "../../../layouts/hass-tabs-subpage";
|
import "../../../layouts/hass-tabs-subpage";
|
||||||
import { haStyle } from "../../../resources/styles";
|
import { haStyle } from "../../../resources/styles";
|
||||||
import { HomeAssistant, Route } from "../../../types";
|
import type { HomeAssistant, Route } from "../../../types";
|
||||||
import { brandsUrl } from "../../../util/brands-url";
|
import { brandsUrl } from "../../../util/brands-url";
|
||||||
import { fileDownload } from "../../../util/file_download";
|
import { fileDownload } from "../../../util/file_download";
|
||||||
|
import "../../logbook/ha-logbook";
|
||||||
import "../ha-config-section";
|
import "../ha-config-section";
|
||||||
import { configSections } from "../ha-panel-config";
|
import { configSections } from "../ha-panel-config";
|
||||||
import "./device-detail/ha-device-entities-card";
|
import "./device-detail/ha-device-entities-card";
|
||||||
@ -63,12 +70,19 @@ import {
|
|||||||
loadDeviceRegistryDetailDialog,
|
loadDeviceRegistryDetailDialog,
|
||||||
showDeviceRegistryDetailDialog,
|
showDeviceRegistryDetailDialog,
|
||||||
} from "./device-registry-detail/show-dialog-device-registry-detail";
|
} from "./device-registry-detail/show-dialog-device-registry-detail";
|
||||||
import "../../logbook/ha-logbook";
|
|
||||||
|
|
||||||
export interface EntityRegistryStateEntry extends EntityRegistryEntry {
|
export interface EntityRegistryStateEntry extends EntityRegistryEntry {
|
||||||
stateName?: string | null;
|
stateName?: string | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface DeviceAction {
|
||||||
|
href?: string;
|
||||||
|
action?: (ev: any) => void;
|
||||||
|
label: string;
|
||||||
|
trailingIcon?: string;
|
||||||
|
classes?: string;
|
||||||
|
}
|
||||||
|
|
||||||
@customElement("ha-config-device-page")
|
@customElement("ha-config-device-page")
|
||||||
export class HaConfigDevicePage extends LitElement {
|
export class HaConfigDevicePage extends LitElement {
|
||||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
@ -94,11 +108,11 @@ export class HaConfigDevicePage extends LitElement {
|
|||||||
@state() private _related?: RelatedResult;
|
@state() private _related?: RelatedResult;
|
||||||
|
|
||||||
// If a number, it's the request ID so we make sure we don't show older info
|
// If a number, it's the request ID so we make sure we don't show older info
|
||||||
@state() private _diagnosticDownloadLinks?:
|
@state() private _diagnosticDownloadLinks?: number | DeviceAction[];
|
||||||
| number
|
|
||||||
| (TemplateResult | string)[];
|
|
||||||
|
|
||||||
@state() private _deleteButtons?: (TemplateResult | string)[];
|
@state() private _deleteButtons?: DeviceAction[];
|
||||||
|
|
||||||
|
@state() private _deviceActions?: DeviceAction[];
|
||||||
|
|
||||||
private _logbookTime = { recent: 86400 };
|
private _logbookTime = { recent: 86400 };
|
||||||
|
|
||||||
@ -196,15 +210,17 @@ export class HaConfigDevicePage extends LitElement {
|
|||||||
if (
|
if (
|
||||||
changedProps.has("deviceId") ||
|
changedProps.has("deviceId") ||
|
||||||
changedProps.has("devices") ||
|
changedProps.has("devices") ||
|
||||||
changedProps.has("deviceId") ||
|
|
||||||
changedProps.has("entries")
|
changedProps.has("entries")
|
||||||
) {
|
) {
|
||||||
this._diagnosticDownloadLinks = undefined;
|
this._diagnosticDownloadLinks = undefined;
|
||||||
this._deleteButtons = undefined;
|
this._deleteButtons = undefined;
|
||||||
|
this._deviceActions = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
(this._diagnosticDownloadLinks && this._deleteButtons) ||
|
(this._diagnosticDownloadLinks &&
|
||||||
|
this._deleteButtons &&
|
||||||
|
this._deviceActions) ||
|
||||||
!this.devices ||
|
!this.devices ||
|
||||||
!this.deviceId ||
|
!this.deviceId ||
|
||||||
!this.entries
|
!this.entries
|
||||||
@ -214,129 +230,10 @@ export class HaConfigDevicePage extends LitElement {
|
|||||||
|
|
||||||
this._diagnosticDownloadLinks = Math.random();
|
this._diagnosticDownloadLinks = Math.random();
|
||||||
this._deleteButtons = []; // To prevent re-rendering if no delete buttons
|
this._deleteButtons = []; // To prevent re-rendering if no delete buttons
|
||||||
this._renderDiagnosticButtons(this._diagnosticDownloadLinks);
|
this._deviceActions = [];
|
||||||
this._renderDeleteButtons();
|
this._getDiagnosticButtons(this._diagnosticDownloadLinks);
|
||||||
}
|
this._getDeleteActions();
|
||||||
|
this._getDeviceActions();
|
||||||
private async _renderDiagnosticButtons(requestId: number): Promise<void> {
|
|
||||||
if (!isComponentLoaded(this.hass, "diagnostics")) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const device = this._device(this.deviceId, this.devices);
|
|
||||||
|
|
||||||
if (!device) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let links = await Promise.all(
|
|
||||||
this._integrations(device, this.entries).map(
|
|
||||||
async (entry): Promise<boolean | { link: string; domain: string }> => {
|
|
||||||
if (entry.state !== "loaded") {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
let info: DiagnosticInfo;
|
|
||||||
try {
|
|
||||||
info = await fetchDiagnosticHandler(this.hass, entry.domain);
|
|
||||||
} catch (err: any) {
|
|
||||||
if (err.code === "not_found") {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
throw err;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!info.handlers.device && !info.handlers.config_entry) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
link: info.handlers.device
|
|
||||||
? getDeviceDiagnosticsDownloadUrl(entry.entry_id, this.deviceId)
|
|
||||||
: getConfigEntryDiagnosticsDownloadUrl(entry.entry_id),
|
|
||||||
domain: entry.domain,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
links = links.filter(Boolean);
|
|
||||||
|
|
||||||
if (this._diagnosticDownloadLinks !== requestId) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (links.length > 0) {
|
|
||||||
this._diagnosticDownloadLinks = (
|
|
||||||
links as { link: string; domain: string }[]
|
|
||||||
).map(
|
|
||||||
(link) => html`
|
|
||||||
<a href=${link.link} @click=${this._signUrl}>
|
|
||||||
<mwc-button>
|
|
||||||
${links.length > 1
|
|
||||||
? this.hass.localize(
|
|
||||||
`ui.panel.config.devices.download_diagnostics_integration`,
|
|
||||||
{
|
|
||||||
integration: domainToName(
|
|
||||||
this.hass.localize,
|
|
||||||
link.domain
|
|
||||||
),
|
|
||||||
}
|
|
||||||
)
|
|
||||||
: this.hass.localize(
|
|
||||||
`ui.panel.config.devices.download_diagnostics`
|
|
||||||
)}
|
|
||||||
</mwc-button>
|
|
||||||
</a>
|
|
||||||
`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private _renderDeleteButtons() {
|
|
||||||
const device = this._device(this.deviceId, this.devices);
|
|
||||||
|
|
||||||
if (!device) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const buttons: TemplateResult[] = [];
|
|
||||||
this._integrations(device, this.entries).forEach((entry) => {
|
|
||||||
if (entry.state !== "loaded" || !entry.supports_remove_device) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
buttons.push(html`
|
|
||||||
<mwc-button
|
|
||||||
class="warning"
|
|
||||||
.entryId=${entry.entry_id}
|
|
||||||
@click=${this._confirmDeleteEntry}
|
|
||||||
>
|
|
||||||
${buttons.length > 1
|
|
||||||
? this.hass.localize(
|
|
||||||
`ui.panel.config.devices.delete_device_integration`,
|
|
||||||
{
|
|
||||||
integration: domainToName(this.hass.localize, entry.domain),
|
|
||||||
}
|
|
||||||
)
|
|
||||||
: this.hass.localize(`ui.panel.config.devices.delete_device`)}
|
|
||||||
</mwc-button>
|
|
||||||
`);
|
|
||||||
});
|
|
||||||
|
|
||||||
if (buttons.length > 0) {
|
|
||||||
this._deleteButtons = buttons;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async _confirmDeleteEntry(e: MouseEvent): Promise<void> {
|
|
||||||
const entryId = (e.currentTarget as any).entryId;
|
|
||||||
|
|
||||||
const confirmed = await showConfirmationDialog(this, {
|
|
||||||
text: this.hass.localize("ui.panel.config.devices.confirm_delete"),
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!confirmed) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
await removeConfigEntryFromDevice(this.hass!, this.deviceId, entryId);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected firstUpdated(changedProps) {
|
protected firstUpdated(changedProps) {
|
||||||
@ -381,16 +278,19 @@ export class HaConfigDevicePage extends LitElement {
|
|||||||
: undefined;
|
: undefined;
|
||||||
const area = this._computeArea(this.areas, device);
|
const area = this._computeArea(this.areas, device);
|
||||||
|
|
||||||
const configurationUrlIsHomeAssistant =
|
|
||||||
device.configuration_url?.startsWith("homeassistant://") || false;
|
|
||||||
|
|
||||||
const configurationUrl = configurationUrlIsHomeAssistant
|
|
||||||
? device.configuration_url!.replace("homeassistant://", "/")
|
|
||||||
: device.configuration_url;
|
|
||||||
|
|
||||||
const deviceInfo: TemplateResult[] = [];
|
const deviceInfo: TemplateResult[] = [];
|
||||||
const deviceAlerts: TemplateResult[] = [];
|
const deviceAlerts: TemplateResult[] = [];
|
||||||
|
|
||||||
|
const actions = [...(this._deviceActions || [])];
|
||||||
|
if (Array.isArray(this._diagnosticDownloadLinks)) {
|
||||||
|
actions.push(...this._diagnosticDownloadLinks);
|
||||||
|
}
|
||||||
|
if (this._deleteButtons) {
|
||||||
|
actions.push(...this._deleteButtons);
|
||||||
|
}
|
||||||
|
|
||||||
|
const firstDeviceAction = actions.shift();
|
||||||
|
|
||||||
if (device.disabled_by) {
|
if (device.disabled_by) {
|
||||||
deviceInfo.push(
|
deviceInfo.push(
|
||||||
html`
|
html`
|
||||||
@ -408,54 +308,19 @@ export class HaConfigDevicePage extends LitElement {
|
|||||||
)}
|
)}
|
||||||
</ha-alert>
|
</ha-alert>
|
||||||
${device.disabled_by === "user"
|
${device.disabled_by === "user"
|
||||||
? html` <div class="card-actions" slot="actions">
|
? html`
|
||||||
|
<div class="card-actions" slot="actions">
|
||||||
<mwc-button unelevated @click=${this._enableDevice}>
|
<mwc-button unelevated @click=${this._enableDevice}>
|
||||||
${this.hass.localize("ui.common.enable")}
|
${this.hass.localize("ui.common.enable")}
|
||||||
</mwc-button>
|
</mwc-button>
|
||||||
</div>`
|
</div>
|
||||||
|
`
|
||||||
: ""}
|
: ""}
|
||||||
`
|
`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const deviceActions: (TemplateResult | string)[] = [];
|
this._renderIntegrationInfo(device, integrations, deviceInfo, deviceAlerts);
|
||||||
|
|
||||||
if (configurationUrl) {
|
|
||||||
deviceActions.push(html`
|
|
||||||
<a
|
|
||||||
href=${configurationUrl}
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
.target=${configurationUrlIsHomeAssistant ? "_self" : "_blank"}
|
|
||||||
>
|
|
||||||
<mwc-button>
|
|
||||||
${this.hass.localize(
|
|
||||||
`ui.panel.config.devices.open_configuration_url_${
|
|
||||||
device.entry_type || "device"
|
|
||||||
}`
|
|
||||||
)}
|
|
||||||
<ha-svg-icon
|
|
||||||
.path=${mdiOpenInNew}
|
|
||||||
slot="trailingIcon"
|
|
||||||
></ha-svg-icon>
|
|
||||||
</mwc-button>
|
|
||||||
</a>
|
|
||||||
`);
|
|
||||||
}
|
|
||||||
|
|
||||||
this._renderIntegrationInfo(
|
|
||||||
device,
|
|
||||||
integrations,
|
|
||||||
deviceInfo,
|
|
||||||
deviceActions,
|
|
||||||
deviceAlerts
|
|
||||||
);
|
|
||||||
|
|
||||||
if (Array.isArray(this._diagnosticDownloadLinks)) {
|
|
||||||
deviceActions.push(...this._diagnosticDownloadLinks);
|
|
||||||
}
|
|
||||||
if (this._deleteButtons) {
|
|
||||||
deviceActions.push(...this._deleteButtons);
|
|
||||||
}
|
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<hass-tabs-subpage
|
<hass-tabs-subpage
|
||||||
@ -479,10 +344,6 @@ export class HaConfigDevicePage extends LitElement {
|
|||||||
`
|
`
|
||||||
: ""
|
: ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="header fullwidth">
|
<div class="header fullwidth">
|
||||||
${
|
${
|
||||||
@ -562,57 +423,70 @@ export class HaConfigDevicePage extends LitElement {
|
|||||||
>
|
>
|
||||||
${deviceInfo}
|
${deviceInfo}
|
||||||
${
|
${
|
||||||
deviceActions.length
|
firstDeviceAction || actions.length
|
||||||
? html`
|
? html`
|
||||||
<div class="card-actions" slot="actions">
|
<div class="card-actions" slot="actions">
|
||||||
${deviceActions}
|
<div>
|
||||||
|
<a href=${ifDefined(firstDeviceAction!.href)}>
|
||||||
|
<mwc-button
|
||||||
|
class=${ifDefined(firstDeviceAction!.classes)}
|
||||||
|
.action=${firstDeviceAction!.action}
|
||||||
|
@click=${this._deviceActionClicked}
|
||||||
|
>
|
||||||
|
${firstDeviceAction!.label}
|
||||||
|
${firstDeviceAction!.trailingIcon
|
||||||
|
? html`
|
||||||
|
<ha-svg-icon
|
||||||
|
.path=${firstDeviceAction!.trailingIcon}
|
||||||
|
slot="trailingIcon"
|
||||||
|
></ha-svg-icon>
|
||||||
|
`
|
||||||
|
: ""}
|
||||||
|
</mwc-button>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
${actions.length
|
||||||
|
? html`
|
||||||
|
<ha-button-menu corner="BOTTOM_START">
|
||||||
|
<ha-icon-button
|
||||||
|
slot="trigger"
|
||||||
|
.label=${this.hass.localize(
|
||||||
|
"ui.common.menu"
|
||||||
|
)}
|
||||||
|
.path=${mdiDotsVertical}
|
||||||
|
></ha-icon-button>
|
||||||
|
${actions.map(
|
||||||
|
(deviceAction) => html`
|
||||||
|
<a href=${ifDefined(deviceAction.href)}>
|
||||||
|
<mwc-list-item
|
||||||
|
class=${ifDefined(
|
||||||
|
deviceAction.classes
|
||||||
|
)}
|
||||||
|
.action=${deviceAction.action}
|
||||||
|
@click=${this._deviceActionClicked}
|
||||||
|
>
|
||||||
|
${deviceAction.label}
|
||||||
|
${deviceAction.trailingIcon
|
||||||
|
? html`
|
||||||
|
<ha-svg-icon
|
||||||
|
.path=${deviceAction.trailingIcon}
|
||||||
|
></ha-svg-icon>
|
||||||
|
`
|
||||||
|
: ""}
|
||||||
|
</mwc-list-item>
|
||||||
|
</a>
|
||||||
|
`
|
||||||
|
)}
|
||||||
|
</ha-button-menu>
|
||||||
|
`
|
||||||
|
: ""}
|
||||||
</div>
|
</div>
|
||||||
`
|
`
|
||||||
: ""
|
: ""
|
||||||
}
|
}
|
||||||
</ha-device-info-card>
|
</ha-device-info-card>
|
||||||
</div>
|
|
||||||
<div class="column">
|
|
||||||
${["control", "sensor", "config", "diagnostic"].map((category) =>
|
|
||||||
// Make sure we render controls if no other cards will be rendered
|
|
||||||
entitiesByCategory[category].length > 0 ||
|
|
||||||
(entities.length === 0 && category === "control")
|
|
||||||
? html`
|
|
||||||
<ha-device-entities-card
|
|
||||||
.hass=${this.hass}
|
|
||||||
.header=${this.hass.localize(
|
|
||||||
`ui.panel.config.devices.entities.${category}`
|
|
||||||
)}
|
|
||||||
.deviceName=${deviceName}
|
|
||||||
.entities=${entitiesByCategory[category]}
|
|
||||||
.showHidden=${device.disabled_by !== null}
|
|
||||||
>
|
|
||||||
</ha-device-entities-card>
|
|
||||||
`
|
|
||||||
: ""
|
|
||||||
)}
|
|
||||||
${
|
|
||||||
isComponentLoaded(this.hass, "logbook")
|
|
||||||
? html`
|
|
||||||
<ha-card outlined>
|
|
||||||
<h1 class="card-header">
|
|
||||||
${this.hass.localize("panel.logbook")}
|
|
||||||
</h1>
|
|
||||||
<ha-logbook
|
|
||||||
.hass=${this.hass}
|
|
||||||
.time=${this._logbookTime}
|
|
||||||
.entityIds=${this._entityIds(entities)}
|
|
||||||
.deviceIds=${this._deviceIdInList(this.deviceId)}
|
|
||||||
virtualize
|
|
||||||
narrow
|
|
||||||
no-icon
|
|
||||||
></ha-logbook>
|
|
||||||
</ha-card>
|
|
||||||
`
|
|
||||||
: ""
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
<div class="column">
|
|
||||||
${
|
${
|
||||||
isComponentLoaded(this.hass, "automation")
|
isComponentLoaded(this.hass, "automation")
|
||||||
? html`
|
? html`
|
||||||
@ -882,11 +756,226 @@ export class HaConfigDevicePage extends LitElement {
|
|||||||
: ""
|
: ""
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
|
<div class="column">
|
||||||
|
${["control", "sensor", "config", "diagnostic"].map((category) =>
|
||||||
|
// Make sure we render controls if no other cards will be rendered
|
||||||
|
entitiesByCategory[category].length > 0 ||
|
||||||
|
(entities.length === 0 && category === "control")
|
||||||
|
? html`
|
||||||
|
<ha-device-entities-card
|
||||||
|
.hass=${this.hass}
|
||||||
|
.header=${this.hass.localize(
|
||||||
|
`ui.panel.config.devices.entities.${category}`
|
||||||
|
)}
|
||||||
|
.deviceName=${deviceName}
|
||||||
|
.entities=${entitiesByCategory[category]}
|
||||||
|
.showHidden=${device.disabled_by !== null}
|
||||||
|
>
|
||||||
|
</ha-device-entities-card>
|
||||||
|
`
|
||||||
|
: ""
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div class="column">
|
||||||
|
${
|
||||||
|
isComponentLoaded(this.hass, "logbook")
|
||||||
|
? html`
|
||||||
|
<ha-card outlined>
|
||||||
|
<h1 class="card-header">
|
||||||
|
${this.hass.localize("panel.logbook")}
|
||||||
|
</h1>
|
||||||
|
<ha-logbook
|
||||||
|
.hass=${this.hass}
|
||||||
|
.time=${this._logbookTime}
|
||||||
|
.entityIds=${this._entityIds(entities)}
|
||||||
|
.deviceIds=${this._deviceIdInList(this.deviceId)}
|
||||||
|
virtualize
|
||||||
|
narrow
|
||||||
|
no-icon
|
||||||
|
></ha-logbook>
|
||||||
|
</ha-card>
|
||||||
|
`
|
||||||
|
: ""
|
||||||
|
}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</ha-config-section>
|
</ha-config-section>
|
||||||
</hass-tabs-subpage> `;
|
</hass-tabs-subpage> `;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async _getDiagnosticButtons(requestId: number): Promise<void> {
|
||||||
|
if (!isComponentLoaded(this.hass, "diagnostics")) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const device = this._device(this.deviceId, this.devices);
|
||||||
|
|
||||||
|
if (!device) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let links = await Promise.all(
|
||||||
|
this._integrations(device, this.entries).map(
|
||||||
|
async (entry): Promise<boolean | { link: string; domain: string }> => {
|
||||||
|
if (entry.state !== "loaded") {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
let info: DiagnosticInfo;
|
||||||
|
try {
|
||||||
|
info = await fetchDiagnosticHandler(this.hass, entry.domain);
|
||||||
|
} catch (err: any) {
|
||||||
|
if (err.code === "not_found") {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!info.handlers.device && !info.handlers.config_entry) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
link: info.handlers.device
|
||||||
|
? getDeviceDiagnosticsDownloadUrl(entry.entry_id, this.deviceId)
|
||||||
|
: getConfigEntryDiagnosticsDownloadUrl(entry.entry_id),
|
||||||
|
domain: entry.domain,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
links = links.filter(Boolean);
|
||||||
|
|
||||||
|
if (this._diagnosticDownloadLinks !== requestId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (links.length > 0) {
|
||||||
|
this._diagnosticDownloadLinks = (
|
||||||
|
links as { link: string; domain: string }[]
|
||||||
|
).map((link) => ({
|
||||||
|
href: link.link,
|
||||||
|
action: (ev) => this._signUrl(ev),
|
||||||
|
label:
|
||||||
|
links.length > 1
|
||||||
|
? this.hass.localize(
|
||||||
|
`ui.panel.config.devices.download_diagnostics_integration`,
|
||||||
|
{
|
||||||
|
integration: domainToName(this.hass.localize, link.domain),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
: this.hass.localize(
|
||||||
|
`ui.panel.config.devices.download_diagnostics`
|
||||||
|
),
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private _getDeleteActions() {
|
||||||
|
const device = this._device(this.deviceId, this.devices);
|
||||||
|
|
||||||
|
if (!device) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const buttons: DeviceAction[] = [];
|
||||||
|
this._integrations(device, this.entries).forEach((entry) => {
|
||||||
|
if (entry.state !== "loaded" || !entry.supports_remove_device) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
buttons.push({
|
||||||
|
action: async () => {
|
||||||
|
const confirmed = await showConfirmationDialog(this, {
|
||||||
|
text: this.hass.localize("ui.panel.config.devices.confirm_delete"),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!confirmed) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await removeConfigEntryFromDevice(
|
||||||
|
this.hass!,
|
||||||
|
this.deviceId,
|
||||||
|
entry.entry_id
|
||||||
|
);
|
||||||
|
},
|
||||||
|
classes: "warning",
|
||||||
|
label:
|
||||||
|
buttons.length > 1
|
||||||
|
? this.hass.localize(
|
||||||
|
`ui.panel.config.devices.delete_device_integration`,
|
||||||
|
{
|
||||||
|
integration: domainToName(this.hass.localize, entry.domain),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
: this.hass.localize(`ui.panel.config.devices.delete_device`),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
if (buttons.length > 0) {
|
||||||
|
this._deleteButtons = buttons;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _getDeviceActions() {
|
||||||
|
const device = this._device(this.deviceId, this.devices);
|
||||||
|
|
||||||
|
if (!device) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const deviceActions: DeviceAction[] = [];
|
||||||
|
|
||||||
|
const configurationUrlIsHomeAssistant =
|
||||||
|
device.configuration_url?.startsWith("homeassistant://") || false;
|
||||||
|
|
||||||
|
const configurationUrl = configurationUrlIsHomeAssistant
|
||||||
|
? device.configuration_url!.replace("homeassistant://", "/")
|
||||||
|
: device.configuration_url;
|
||||||
|
|
||||||
|
if (configurationUrl) {
|
||||||
|
deviceActions.push({
|
||||||
|
href: configurationUrl,
|
||||||
|
label: this.hass.localize(
|
||||||
|
`ui.panel.config.devices.open_configuration_url_${
|
||||||
|
device.entry_type || "device"
|
||||||
|
}`
|
||||||
|
),
|
||||||
|
trailingIcon: mdiOpenInNew,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const domains = this._integrations(device, this.entries).map(
|
||||||
|
(int) => int.domain
|
||||||
|
);
|
||||||
|
|
||||||
|
if (domains.includes("mqtt")) {
|
||||||
|
const mqtt = await import(
|
||||||
|
"./device-detail/integration-elements/mqtt/device-actions"
|
||||||
|
);
|
||||||
|
const actions = mqtt.getMQTTDeviceActions(this, device);
|
||||||
|
deviceActions.push(...actions);
|
||||||
|
}
|
||||||
|
if (domains.includes("zha")) {
|
||||||
|
const zha = await import(
|
||||||
|
"./device-detail/integration-elements/zha/device-actions"
|
||||||
|
);
|
||||||
|
const actions = await zha.getZHADeviceActions(this, this.hass, device);
|
||||||
|
deviceActions.push(...actions);
|
||||||
|
}
|
||||||
|
if (domains.includes("zwave_js")) {
|
||||||
|
const zwave = await import(
|
||||||
|
"./device-detail/integration-elements/zwave_js/device-actions"
|
||||||
|
);
|
||||||
|
const actions = await zwave.getZwaveDeviceActions(
|
||||||
|
this,
|
||||||
|
this.hass,
|
||||||
|
device
|
||||||
|
);
|
||||||
|
deviceActions.push(...actions);
|
||||||
|
}
|
||||||
|
|
||||||
|
this._deviceActions = deviceActions;
|
||||||
|
}
|
||||||
|
|
||||||
private _computeEntityName(entity: EntityRegistryEntry) {
|
private _computeEntityName(entity: EntityRegistryEntry) {
|
||||||
if (entity.name) {
|
if (entity.name) {
|
||||||
return entity.name;
|
return entity.name;
|
||||||
@ -935,23 +1024,10 @@ export class HaConfigDevicePage extends LitElement {
|
|||||||
device: DeviceRegistryEntry,
|
device: DeviceRegistryEntry,
|
||||||
integrations: ConfigEntry[],
|
integrations: ConfigEntry[],
|
||||||
deviceInfo: TemplateResult[],
|
deviceInfo: TemplateResult[],
|
||||||
deviceActions: (string | TemplateResult)[],
|
|
||||||
deviceAlerts: TemplateResult[]
|
deviceAlerts: TemplateResult[]
|
||||||
) {
|
) {
|
||||||
const domains = integrations.map((int) => int.domain);
|
const domains = integrations.map((int) => int.domain);
|
||||||
if (domains.includes("mqtt")) {
|
|
||||||
import(
|
|
||||||
"./device-detail/integration-elements/mqtt/ha-device-actions-mqtt"
|
|
||||||
);
|
|
||||||
deviceActions.push(html`
|
|
||||||
<ha-device-actions-mqtt
|
|
||||||
.hass=${this.hass}
|
|
||||||
.device=${device}
|
|
||||||
></ha-device-actions-mqtt>
|
|
||||||
`);
|
|
||||||
}
|
|
||||||
if (domains.includes("zha")) {
|
if (domains.includes("zha")) {
|
||||||
import("./device-detail/integration-elements/zha/ha-device-actions-zha");
|
|
||||||
import("./device-detail/integration-elements/zha/ha-device-info-zha");
|
import("./device-detail/integration-elements/zha/ha-device-info-zha");
|
||||||
deviceInfo.push(html`
|
deviceInfo.push(html`
|
||||||
<ha-device-info-zha
|
<ha-device-info-zha
|
||||||
@ -959,12 +1035,6 @@ export class HaConfigDevicePage extends LitElement {
|
|||||||
.device=${device}
|
.device=${device}
|
||||||
></ha-device-info-zha>
|
></ha-device-info-zha>
|
||||||
`);
|
`);
|
||||||
deviceActions.push(html`
|
|
||||||
<ha-device-actions-zha
|
|
||||||
.hass=${this.hass}
|
|
||||||
.device=${device}
|
|
||||||
></ha-device-actions-zha>
|
|
||||||
`);
|
|
||||||
}
|
}
|
||||||
if (domains.includes("zwave_js")) {
|
if (domains.includes("zwave_js")) {
|
||||||
import(
|
import(
|
||||||
@ -973,9 +1043,6 @@ export class HaConfigDevicePage extends LitElement {
|
|||||||
import(
|
import(
|
||||||
"./device-detail/integration-elements/zwave_js/ha-device-info-zwave_js"
|
"./device-detail/integration-elements/zwave_js/ha-device-info-zwave_js"
|
||||||
);
|
);
|
||||||
import(
|
|
||||||
"./device-detail/integration-elements/zwave_js/ha-device-actions-zwave_js"
|
|
||||||
);
|
|
||||||
deviceAlerts.push(html`
|
deviceAlerts.push(html`
|
||||||
<ha-device-alerts-zwave_js
|
<ha-device-alerts-zwave_js
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
@ -988,12 +1055,6 @@ export class HaConfigDevicePage extends LitElement {
|
|||||||
.device=${device}
|
.device=${device}
|
||||||
></ha-device-info-zwave_js>
|
></ha-device-info-zwave_js>
|
||||||
`);
|
`);
|
||||||
deviceActions.push(html`
|
|
||||||
<ha-device-actions-zwave_js
|
|
||||||
.hass=${this.hass}
|
|
||||||
.device=${device}
|
|
||||||
></ha-device-actions-zwave_js>
|
|
||||||
`);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1132,8 +1193,7 @@ export class HaConfigDevicePage extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async _signUrl(ev) {
|
private async _signUrl(ev) {
|
||||||
const anchor = ev.target.closest("a");
|
const anchor = ev.currentTarget.closest("a");
|
||||||
ev.preventDefault();
|
|
||||||
const signedUrl = await getSignedPath(
|
const signedUrl = await getSignedPath(
|
||||||
this.hass,
|
this.hass,
|
||||||
anchor.getAttribute("href")
|
anchor.getAttribute("href")
|
||||||
@ -1141,6 +1201,16 @@ export class HaConfigDevicePage extends LitElement {
|
|||||||
fileDownload(signedUrl.path);
|
fileDownload(signedUrl.path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _deviceActionClicked(ev) {
|
||||||
|
if (!ev.currentTarget.action) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ev.preventDefault();
|
||||||
|
|
||||||
|
(ev.currentTarget as any).action(ev);
|
||||||
|
}
|
||||||
|
|
||||||
static get styles(): CSSResultGroup {
|
static get styles(): CSSResultGroup {
|
||||||
return [
|
return [
|
||||||
haStyle,
|
haStyle,
|
||||||
@ -1171,9 +1241,6 @@ export class HaConfigDevicePage extends LitElement {
|
|||||||
padding: 16px;
|
padding: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.show-more {
|
|
||||||
}
|
|
||||||
|
|
||||||
h1 {
|
h1 {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
font-family: var(--paper-font-headline_-_font-family);
|
font-family: var(--paper-font-headline_-_font-family);
|
||||||
@ -1282,7 +1349,19 @@ export class HaConfigDevicePage extends LitElement {
|
|||||||
:host([narrow]) ha-logbook {
|
:host([narrow]) ha-logbook {
|
||||||
height: 235px;
|
height: 235px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.card-actions {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
`,
|
`,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"ha-config-device-page": HaConfigDevicePage;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -556,10 +556,6 @@ class HaLogbookRenderer extends LitElement {
|
|||||||
padding: 0 16px;
|
padding: 0 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.narrow .date {
|
|
||||||
padding: 0 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.rtl .date {
|
.rtl .date {
|
||||||
direction: rtl;
|
direction: rtl;
|
||||||
}
|
}
|
||||||
@ -590,6 +586,12 @@ class HaLogbookRenderer extends LitElement {
|
|||||||
|
|
||||||
a {
|
a {
|
||||||
color: var(--primary-color);
|
color: var(--primary-color);
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
button.link {
|
||||||
|
color: var(--paper-item-icon-color);
|
||||||
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.container {
|
.container {
|
||||||
@ -607,7 +609,6 @@ class HaLogbookRenderer extends LitElement {
|
|||||||
|
|
||||||
.narrow .entry {
|
.narrow .entry {
|
||||||
line-height: 1.5;
|
line-height: 1.5;
|
||||||
padding: 8px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.narrow .icon-message state-badge {
|
.narrow .icon-message state-badge {
|
||||||
|
@ -158,6 +158,7 @@ export const buttonLinkStyle = css`
|
|||||||
text-align: left;
|
text-align: left;
|
||||||
text-decoration: underline;
|
text-decoration: underline;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
outline: none;
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user