mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-28 11:46:42 +00:00
More Info: Add History Tab (#6758)
Co-authored-by: J. Nick Koston <nick@koston.org>
This commit is contained in:
parent
be8812e0af
commit
1431e75f8b
@ -23,7 +23,8 @@ export const getLogbookData = (
|
|||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
startDate: string,
|
startDate: string,
|
||||||
endDate: string,
|
endDate: string,
|
||||||
entityId?: string
|
entityId?: string,
|
||||||
|
entity_matches_only?: boolean
|
||||||
) => {
|
) => {
|
||||||
const ALL_ENTITIES = "*";
|
const ALL_ENTITIES = "*";
|
||||||
|
|
||||||
@ -51,7 +52,8 @@ export const getLogbookData = (
|
|||||||
hass,
|
hass,
|
||||||
startDate,
|
startDate,
|
||||||
endDate,
|
endDate,
|
||||||
entityId !== ALL_ENTITIES ? entityId : undefined
|
entityId !== ALL_ENTITIES ? entityId : undefined,
|
||||||
|
entity_matches_only
|
||||||
).then((entries) => entries.reverse());
|
).then((entries) => entries.reverse());
|
||||||
return DATA_CACHE[cacheKey][entityId];
|
return DATA_CACHE[cacheKey][entityId];
|
||||||
};
|
};
|
||||||
@ -60,11 +62,13 @@ const getLogbookDataFromServer = async (
|
|||||||
hass: HomeAssistant,
|
hass: HomeAssistant,
|
||||||
startDate: string,
|
startDate: string,
|
||||||
endDate: string,
|
endDate: string,
|
||||||
entityId?: string
|
entityId?: string,
|
||||||
|
entity_matches_only?: boolean
|
||||||
) => {
|
) => {
|
||||||
const url = `logbook/${startDate}?end_time=${endDate}${
|
const url = `logbook/${startDate}?end_time=${endDate}${
|
||||||
entityId ? `&entity=${entityId}` : ""
|
entityId ? `&entity=${entityId}` : ""
|
||||||
}`;
|
}${entity_matches_only ? `&entity_matches_only` : ""}`;
|
||||||
|
|
||||||
return hass.callApi<LogbookEntry[]>("GET", url);
|
return hass.callApi<LogbookEntry[]>("GET", url);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,33 +1,34 @@
|
|||||||
import "@material/mwc-button";
|
import "@material/mwc-button";
|
||||||
import "@material/mwc-icon-button";
|
import "@material/mwc-icon-button";
|
||||||
import "../../components/ha-header-bar";
|
import "@material/mwc-tab";
|
||||||
import "../../components/ha-dialog";
|
import "@material/mwc-tab-bar";
|
||||||
import "../../components/ha-svg-icon";
|
import { mdiClose, mdiCog, mdiPencil } from "@mdi/js";
|
||||||
|
import {
|
||||||
|
css,
|
||||||
|
customElement,
|
||||||
|
html,
|
||||||
|
internalProperty,
|
||||||
|
LitElement,
|
||||||
|
property,
|
||||||
|
} from "lit-element";
|
||||||
import { isComponentLoaded } from "../../common/config/is_component_loaded";
|
import { isComponentLoaded } from "../../common/config/is_component_loaded";
|
||||||
import { DOMAINS_MORE_INFO_NO_HISTORY } from "../../common/const";
|
import { DOMAINS_MORE_INFO_NO_HISTORY } from "../../common/const";
|
||||||
|
import { fireEvent } from "../../common/dom/fire_event";
|
||||||
|
import { computeDomain } from "../../common/entity/compute_domain";
|
||||||
import { computeStateName } from "../../common/entity/compute_state_name";
|
import { computeStateName } from "../../common/entity/compute_state_name";
|
||||||
import { navigate } from "../../common/navigate";
|
import { navigate } from "../../common/navigate";
|
||||||
import { fireEvent } from "../../common/dom/fire_event";
|
import "../../components/ha-dialog";
|
||||||
|
import "../../components/ha-header-bar";
|
||||||
|
import "../../components/ha-svg-icon";
|
||||||
import "../../components/state-history-charts";
|
import "../../components/state-history-charts";
|
||||||
import { removeEntityRegistryEntry } from "../../data/entity_registry";
|
import { removeEntityRegistryEntry } from "../../data/entity_registry";
|
||||||
import { showEntityEditorDialog } from "../../panels/config/entities/show-dialog-entity-editor";
|
import { showEntityEditorDialog } from "../../panels/config/entities/show-dialog-entity-editor";
|
||||||
|
import "../../panels/logbook/ha-logbook";
|
||||||
|
import { haStyleDialog } from "../../resources/styles";
|
||||||
import "../../state-summary/state-card-content";
|
import "../../state-summary/state-card-content";
|
||||||
|
import { HomeAssistant } from "../../types";
|
||||||
import { showConfirmationDialog } from "../generic/show-dialog-box";
|
import { showConfirmationDialog } from "../generic/show-dialog-box";
|
||||||
import "./more-info-content";
|
import "./more-info-content";
|
||||||
import {
|
|
||||||
customElement,
|
|
||||||
LitElement,
|
|
||||||
property,
|
|
||||||
internalProperty,
|
|
||||||
css,
|
|
||||||
html,
|
|
||||||
} from "lit-element";
|
|
||||||
import { haStyleDialog } from "../../resources/styles";
|
|
||||||
import { HomeAssistant } from "../../types";
|
|
||||||
import { getRecentWithCache } from "../../data/cached-history";
|
|
||||||
import { computeDomain } from "../../common/entity/compute_domain";
|
|
||||||
import { mdiClose, mdiCog, mdiPencil } from "@mdi/js";
|
|
||||||
import { HistoryResult } from "../../data/history";
|
|
||||||
|
|
||||||
const DOMAINS_NO_INFO = ["camera", "configurator"];
|
const DOMAINS_NO_INFO = ["camera", "configurator"];
|
||||||
const EDITABLE_DOMAINS_WITH_ID = ["scene", "automation"];
|
const EDITABLE_DOMAINS_WITH_ID = ["scene", "automation"];
|
||||||
@ -43,11 +44,9 @@ export class MoreInfoDialog extends LitElement {
|
|||||||
|
|
||||||
@property({ type: Boolean, reflect: true }) public large = false;
|
@property({ type: Boolean, reflect: true }) public large = false;
|
||||||
|
|
||||||
@internalProperty() private _stateHistory?: HistoryResult;
|
|
||||||
|
|
||||||
@internalProperty() private _entityId?: string | null;
|
@internalProperty() private _entityId?: string | null;
|
||||||
|
|
||||||
private _historyRefreshInterval?: number;
|
@internalProperty() private _currTabIndex = 0;
|
||||||
|
|
||||||
public showDialog(params: MoreInfoDialogParams) {
|
public showDialog(params: MoreInfoDialogParams) {
|
||||||
this._entityId = params.entityId;
|
this._entityId = params.entityId;
|
||||||
@ -55,21 +54,11 @@ export class MoreInfoDialog extends LitElement {
|
|||||||
this.closeDialog();
|
this.closeDialog();
|
||||||
}
|
}
|
||||||
this.large = false;
|
this.large = false;
|
||||||
this._stateHistory = undefined;
|
|
||||||
if (this._computeShowHistoryComponent(this._entityId)) {
|
|
||||||
this._getStateHistory();
|
|
||||||
clearInterval(this._historyRefreshInterval);
|
|
||||||
this._historyRefreshInterval = window.setInterval(() => {
|
|
||||||
this._getStateHistory();
|
|
||||||
}, 60 * 1000);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public closeDialog() {
|
public closeDialog() {
|
||||||
this._entityId = undefined;
|
this._entityId = undefined;
|
||||||
this._stateHistory = undefined;
|
this._currTabIndex = 0;
|
||||||
clearInterval(this._historyRefreshInterval);
|
|
||||||
this._historyRefreshInterval = undefined;
|
|
||||||
fireEvent(this, "dialog-closed", { dialog: this.localName });
|
fireEvent(this, "dialog-closed", { dialog: this.localName });
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -93,11 +82,14 @@ export class MoreInfoDialog extends LitElement {
|
|||||||
hideActions
|
hideActions
|
||||||
data-domain=${domain}
|
data-domain=${domain}
|
||||||
>
|
>
|
||||||
<ha-header-bar slot="heading">
|
<div slot="heading" class="heading">
|
||||||
|
<ha-header-bar>
|
||||||
<mwc-icon-button
|
<mwc-icon-button
|
||||||
slot="navigationIcon"
|
slot="navigationIcon"
|
||||||
.label=${this.hass.localize("ui.dialogs.more_info_control.dismiss")}
|
|
||||||
dialogAction="cancel"
|
dialogAction="cancel"
|
||||||
|
.label=${this.hass.localize(
|
||||||
|
"ui.dialogs.more_info_control.dismiss"
|
||||||
|
)}
|
||||||
>
|
>
|
||||||
<ha-svg-icon .path=${mdiClose}></ha-svg-icon>
|
<ha-svg-icon .path=${mdiClose}></ha-svg-icon>
|
||||||
</mwc-icon-button>
|
</mwc-icon-button>
|
||||||
@ -105,7 +97,8 @@ export class MoreInfoDialog extends LitElement {
|
|||||||
${computeStateName(stateObj)}
|
${computeStateName(stateObj)}
|
||||||
</div>
|
</div>
|
||||||
${this.hass.user!.is_admin
|
${this.hass.user!.is_admin
|
||||||
? html`<mwc-icon-button
|
? html`
|
||||||
|
<mwc-icon-button
|
||||||
slot="actionItems"
|
slot="actionItems"
|
||||||
.label=${this.hass.localize(
|
.label=${this.hass.localize(
|
||||||
"ui.dialogs.more_info_control.settings"
|
"ui.dialogs.more_info_control.settings"
|
||||||
@ -113,13 +106,15 @@ export class MoreInfoDialog extends LitElement {
|
|||||||
@click=${this._gotoSettings}
|
@click=${this._gotoSettings}
|
||||||
>
|
>
|
||||||
<ha-svg-icon .path=${mdiCog}></ha-svg-icon>
|
<ha-svg-icon .path=${mdiCog}></ha-svg-icon>
|
||||||
</mwc-icon-button>`
|
</mwc-icon-button>
|
||||||
|
`
|
||||||
: ""}
|
: ""}
|
||||||
${this.hass.user!.is_admin &&
|
${this.hass.user!.is_admin &&
|
||||||
((EDITABLE_DOMAINS_WITH_ID.includes(domain) &&
|
((EDITABLE_DOMAINS_WITH_ID.includes(domain) &&
|
||||||
stateObj.attributes.id) ||
|
stateObj.attributes.id) ||
|
||||||
EDITABLE_DOMAINS.includes(domain))
|
EDITABLE_DOMAINS.includes(domain))
|
||||||
? html` <mwc-icon-button
|
? html`
|
||||||
|
<mwc-icon-button
|
||||||
slot="actionItems"
|
slot="actionItems"
|
||||||
.label=${this.hass.localize(
|
.label=${this.hass.localize(
|
||||||
"ui.dialogs.more_info_control.edit"
|
"ui.dialogs.more_info_control.edit"
|
||||||
@ -127,36 +122,49 @@ export class MoreInfoDialog extends LitElement {
|
|||||||
@click=${this._gotoEdit}
|
@click=${this._gotoEdit}
|
||||||
>
|
>
|
||||||
<ha-svg-icon .path=${mdiPencil}></ha-svg-icon>
|
<ha-svg-icon .path=${mdiPencil}></ha-svg-icon>
|
||||||
</mwc-icon-button>`
|
</mwc-icon-button>
|
||||||
|
`
|
||||||
: ""}
|
: ""}
|
||||||
</ha-header-bar>
|
</ha-header-bar>
|
||||||
|
${this._computeShowHistoryComponent(entityId)
|
||||||
|
? html`
|
||||||
|
<mwc-tab-bar
|
||||||
|
.activeIndex=${this._currTabIndex}
|
||||||
|
@MDCTabBar:activated=${this._handleTabChanged}
|
||||||
|
>
|
||||||
|
<mwc-tab
|
||||||
|
.label=${this.hass.localize(
|
||||||
|
"ui.dialogs.more_info_control.controls"
|
||||||
|
)}
|
||||||
|
></mwc-tab>
|
||||||
|
<mwc-tab
|
||||||
|
.label=${this.hass.localize(
|
||||||
|
"ui.dialogs.more_info_control.history"
|
||||||
|
)}
|
||||||
|
></mwc-tab>
|
||||||
|
</mwc-tab-bar>
|
||||||
|
`
|
||||||
|
: ""}
|
||||||
|
</div>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
|
${this._currTabIndex === 0
|
||||||
|
? html`
|
||||||
${DOMAINS_NO_INFO.includes(domain)
|
${DOMAINS_NO_INFO.includes(domain)
|
||||||
? ""
|
? ""
|
||||||
: html`
|
: html`
|
||||||
<state-card-content
|
<state-card-content
|
||||||
|
in-dialog
|
||||||
.stateObj=${stateObj}
|
.stateObj=${stateObj}
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
in-dialog
|
|
||||||
></state-card-content>
|
></state-card-content>
|
||||||
`}
|
`}
|
||||||
${this._computeShowHistoryComponent(entityId)
|
|
||||||
? html`
|
|
||||||
<state-history-charts
|
|
||||||
.hass=${this.hass}
|
|
||||||
.historyData=${this._stateHistory}
|
|
||||||
up-to-now
|
|
||||||
.isLoadingData=${!this._stateHistory}
|
|
||||||
></state-history-charts>
|
|
||||||
`
|
|
||||||
: ""}
|
|
||||||
<more-info-content
|
<more-info-content
|
||||||
.stateObj=${stateObj}
|
.stateObj=${stateObj}
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
></more-info-content>
|
></more-info-content>
|
||||||
|
|
||||||
${stateObj.attributes.restored
|
${stateObj.attributes.restored
|
||||||
? html`<p>
|
? html`
|
||||||
|
<p>
|
||||||
${this.hass.localize(
|
${this.hass.localize(
|
||||||
"ui.dialogs.more_info_control.restored.not_provided"
|
"ui.dialogs.more_info_control.restored.not_provided"
|
||||||
)}
|
)}
|
||||||
@ -170,32 +178,27 @@ export class MoreInfoDialog extends LitElement {
|
|||||||
${this.hass.localize(
|
${this.hass.localize(
|
||||||
"ui.dialogs.more_info_control.restored.remove_action"
|
"ui.dialogs.more_info_control.restored.remove_action"
|
||||||
)}
|
)}
|
||||||
</mwc-button>`
|
</mwc-button>
|
||||||
|
`
|
||||||
: ""}
|
: ""}
|
||||||
|
`
|
||||||
|
: html`
|
||||||
|
<ha-more-info-tab-history
|
||||||
|
.hass=${this.hass}
|
||||||
|
.entityId=${this._entityId}
|
||||||
|
></ha-more-info-tab-history>
|
||||||
|
`}
|
||||||
</div>
|
</div>
|
||||||
</ha-dialog>
|
</ha-dialog>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _enlarge() {
|
protected firstUpdated(): void {
|
||||||
this.large = !this.large;
|
import("./ha-more-info-tab-history");
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _getStateHistory(): Promise<void> {
|
private _enlarge() {
|
||||||
if (!this._entityId) {
|
this.large = !this.large;
|
||||||
return;
|
|
||||||
}
|
|
||||||
this._stateHistory = await getRecentWithCache(
|
|
||||||
this.hass!,
|
|
||||||
this._entityId,
|
|
||||||
{
|
|
||||||
refresh: 60,
|
|
||||||
cacheKey: `more_info.${this._entityId}`,
|
|
||||||
hoursToShow: 24,
|
|
||||||
},
|
|
||||||
this.hass!.localize,
|
|
||||||
this.hass!.language
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private _computeShowHistoryComponent(entityId) {
|
private _computeShowHistoryComponent(entityId) {
|
||||||
@ -243,6 +246,15 @@ export class MoreInfoDialog extends LitElement {
|
|||||||
this.closeDialog();
|
this.closeDialog();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _handleTabChanged(ev: CustomEvent): void {
|
||||||
|
const newTab = ev.detail.index;
|
||||||
|
if (newTab === this._currTabIndex) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._currTabIndex = ev.detail.index;
|
||||||
|
}
|
||||||
|
|
||||||
static get styles() {
|
static get styles() {
|
||||||
return [
|
return [
|
||||||
haStyleDialog,
|
haStyleDialog,
|
||||||
@ -256,8 +268,6 @@ export class MoreInfoDialog extends LitElement {
|
|||||||
--mdc-theme-on-primary: var(--primary-text-color);
|
--mdc-theme-on-primary: var(--primary-text-color);
|
||||||
--mdc-theme-primary: var(--mdc-theme-surface);
|
--mdc-theme-primary: var(--mdc-theme-surface);
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
border-bottom: 1px solid
|
|
||||||
var(--mdc-dialog-scroll-divider-color, rgba(0, 0, 0, 0.12));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@media all and (max-width: 450px), all and (max-height: 500px) {
|
@media all and (max-width: 450px), all and (max-height: 500px) {
|
||||||
@ -268,6 +278,11 @@ export class MoreInfoDialog extends LitElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.heading {
|
||||||
|
border-bottom: 1px solid
|
||||||
|
var(--mdc-dialog-scroll-divider-color, rgba(0, 0, 0, 0.12));
|
||||||
|
}
|
||||||
|
|
||||||
@media all and (min-width: 451px) and (min-height: 501px) {
|
@media all and (min-width: 451px) and (min-height: 501px) {
|
||||||
ha-dialog {
|
ha-dialog {
|
||||||
--mdc-dialog-max-width: 90vw;
|
--mdc-dialog-max-width: 90vw;
|
||||||
@ -306,8 +321,7 @@ export class MoreInfoDialog extends LitElement {
|
|||||||
--dialog-content-padding: 0;
|
--dialog-content-padding: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
state-card-content,
|
state-card-content {
|
||||||
state-history-charts {
|
|
||||||
display: block;
|
display: block;
|
||||||
margin-bottom: 16px;
|
margin-bottom: 16px;
|
||||||
}
|
}
|
||||||
@ -315,3 +329,9 @@ export class MoreInfoDialog extends LitElement {
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"ha-more-info-dialog": MoreInfoDialog;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
164
src/dialogs/more-info/ha-more-info-tab-history.ts
Normal file
164
src/dialogs/more-info/ha-more-info-tab-history.ts
Normal file
@ -0,0 +1,164 @@
|
|||||||
|
import {
|
||||||
|
css,
|
||||||
|
customElement,
|
||||||
|
html,
|
||||||
|
internalProperty,
|
||||||
|
LitElement,
|
||||||
|
property,
|
||||||
|
PropertyValues,
|
||||||
|
TemplateResult,
|
||||||
|
} from "lit-element";
|
||||||
|
import { classMap } from "lit-html/directives/class-map";
|
||||||
|
import { computeStateDomain } from "../../common/entity/compute_state_domain";
|
||||||
|
import "../../components/ha-circular-progress";
|
||||||
|
import "../../components/state-history-charts";
|
||||||
|
import { getRecentWithCache } from "../../data/cached-history";
|
||||||
|
import { HistoryResult } from "../../data/history";
|
||||||
|
import { getLogbookData, LogbookEntry } from "../../data/logbook";
|
||||||
|
import "../../panels/logbook/ha-logbook";
|
||||||
|
import { haStyleDialog } from "../../resources/styles";
|
||||||
|
import { HomeAssistant } from "../../types";
|
||||||
|
|
||||||
|
@customElement("ha-more-info-tab-history")
|
||||||
|
export class MoreInfoTabHistoryDialog extends LitElement {
|
||||||
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
|
@property() public entityId!: string;
|
||||||
|
|
||||||
|
@internalProperty() private _stateHistory?: HistoryResult;
|
||||||
|
|
||||||
|
@internalProperty() private _entries?: LogbookEntry[];
|
||||||
|
|
||||||
|
@internalProperty() private _persons = {};
|
||||||
|
|
||||||
|
private _historyRefreshInterval?: number;
|
||||||
|
|
||||||
|
protected render(): TemplateResult {
|
||||||
|
if (!this.entityId) {
|
||||||
|
return html``;
|
||||||
|
}
|
||||||
|
const stateObj = this.hass.states[this.entityId];
|
||||||
|
|
||||||
|
if (!stateObj) {
|
||||||
|
return html``;
|
||||||
|
}
|
||||||
|
|
||||||
|
return html`
|
||||||
|
<state-history-charts
|
||||||
|
up-to-now
|
||||||
|
.hass=${this.hass}
|
||||||
|
.historyData=${this._stateHistory}
|
||||||
|
.isLoadingData=${!this._stateHistory}
|
||||||
|
></state-history-charts>
|
||||||
|
${!this._entries
|
||||||
|
? html`
|
||||||
|
<ha-circular-progress
|
||||||
|
active
|
||||||
|
alt=${this.hass.localize("ui.common.loading")}
|
||||||
|
></ha-circular-progress>
|
||||||
|
`
|
||||||
|
: html`
|
||||||
|
<ha-logbook
|
||||||
|
narrow
|
||||||
|
no-icon
|
||||||
|
no-name
|
||||||
|
class=${classMap({
|
||||||
|
"has-entries": Boolean(this._entries?.length),
|
||||||
|
})}
|
||||||
|
.hass=${this.hass}
|
||||||
|
.entries=${this._entries}
|
||||||
|
.userIdToName=${this._persons}
|
||||||
|
></ha-logbook>
|
||||||
|
`}
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected firstUpdated(): void {
|
||||||
|
this._fetchPersonNames();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected updated(changedProps: PropertyValues): void {
|
||||||
|
super.updated(changedProps);
|
||||||
|
if (!this.entityId) {
|
||||||
|
clearInterval(this._historyRefreshInterval);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (changedProps.has("entityId")) {
|
||||||
|
this._stateHistory = undefined;
|
||||||
|
this._entries = undefined;
|
||||||
|
|
||||||
|
this._getStateHistory();
|
||||||
|
this._getLogBookData();
|
||||||
|
|
||||||
|
clearInterval(this._historyRefreshInterval);
|
||||||
|
this._historyRefreshInterval = window.setInterval(() => {
|
||||||
|
this._getStateHistory();
|
||||||
|
}, 60 * 1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _getStateHistory(): Promise<void> {
|
||||||
|
this._stateHistory = await getRecentWithCache(
|
||||||
|
this.hass!,
|
||||||
|
this.entityId,
|
||||||
|
{
|
||||||
|
refresh: 60,
|
||||||
|
cacheKey: `more_info.${this.entityId}`,
|
||||||
|
hoursToShow: 24,
|
||||||
|
},
|
||||||
|
this.hass!.localize,
|
||||||
|
this.hass!.language
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async _getLogBookData() {
|
||||||
|
const yesterday = new Date(new Date().getTime() - 24 * 60 * 60 * 1000);
|
||||||
|
const now = new Date();
|
||||||
|
this._entries = await getLogbookData(
|
||||||
|
this.hass,
|
||||||
|
yesterday.toISOString(),
|
||||||
|
now.toISOString(),
|
||||||
|
this.entityId,
|
||||||
|
true
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private _fetchPersonNames() {
|
||||||
|
Object.values(this.hass.states).forEach((entity) => {
|
||||||
|
if (
|
||||||
|
entity.attributes.user_id &&
|
||||||
|
computeStateDomain(entity) === "person"
|
||||||
|
) {
|
||||||
|
this._persons[entity.attributes.user_id] =
|
||||||
|
entity.attributes.friendly_name;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static get styles() {
|
||||||
|
return [
|
||||||
|
haStyleDialog,
|
||||||
|
css`
|
||||||
|
state-history-charts {
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
ha-logbook.has-entries {
|
||||||
|
height: 360px;
|
||||||
|
}
|
||||||
|
|
||||||
|
ha-circular-progress {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"ha-more-info-tab-history": MoreInfoTabHistoryDialog;
|
||||||
|
}
|
||||||
|
}
|
@ -1,35 +1,48 @@
|
|||||||
import {
|
import {
|
||||||
css,
|
css,
|
||||||
CSSResult,
|
CSSResult,
|
||||||
|
customElement,
|
||||||
|
eventOptions,
|
||||||
html,
|
html,
|
||||||
LitElement,
|
LitElement,
|
||||||
property,
|
property,
|
||||||
PropertyValues,
|
PropertyValues,
|
||||||
TemplateResult,
|
TemplateResult,
|
||||||
eventOptions,
|
|
||||||
} from "lit-element";
|
} from "lit-element";
|
||||||
|
import { classMap } from "lit-html/directives/class-map";
|
||||||
import { scroll } from "lit-virtualizer";
|
import { scroll } from "lit-virtualizer";
|
||||||
import { formatDate } from "../../common/datetime/format_date";
|
import { formatDate } from "../../common/datetime/format_date";
|
||||||
import { formatTimeWithSeconds } from "../../common/datetime/format_time";
|
import { formatTimeWithSeconds } from "../../common/datetime/format_time";
|
||||||
|
import { restoreScroll } from "../../common/decorators/restore-scroll";
|
||||||
import { fireEvent } from "../../common/dom/fire_event";
|
import { fireEvent } from "../../common/dom/fire_event";
|
||||||
import { domainIcon } from "../../common/entity/domain_icon";
|
import { domainIcon } from "../../common/entity/domain_icon";
|
||||||
import { stateIcon } from "../../common/entity/state_icon";
|
import { stateIcon } from "../../common/entity/state_icon";
|
||||||
import { computeRTL, emitRTLDirection } from "../../common/util/compute_rtl";
|
import { computeRTL, emitRTLDirection } from "../../common/util/compute_rtl";
|
||||||
|
import "../../components/ha-circular-progress";
|
||||||
import "../../components/ha-icon";
|
import "../../components/ha-icon";
|
||||||
import { LogbookEntry } from "../../data/logbook";
|
import { LogbookEntry } from "../../data/logbook";
|
||||||
import { HomeAssistant } from "../../types";
|
import { HomeAssistant } from "../../types";
|
||||||
import { restoreScroll } from "../../common/decorators/restore-scroll";
|
|
||||||
|
|
||||||
|
@customElement("ha-logbook")
|
||||||
class HaLogbook extends LitElement {
|
class HaLogbook extends LitElement {
|
||||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
@property() public userIdToName = {};
|
@property({ attribute: false }) public userIdToName = {};
|
||||||
|
|
||||||
@property() public entries: LogbookEntry[] = [];
|
@property({ attribute: false }) public entries: LogbookEntry[] = [];
|
||||||
|
|
||||||
@property({ attribute: "rtl", type: Boolean, reflect: true })
|
@property({ type: Boolean, attribute: "narrow" })
|
||||||
|
public narrow = false;
|
||||||
|
|
||||||
|
@property({ attribute: "rtl", type: Boolean })
|
||||||
private _rtl = false;
|
private _rtl = false;
|
||||||
|
|
||||||
|
@property({ type: Boolean, attribute: "no-icon" })
|
||||||
|
public noIcon = false;
|
||||||
|
|
||||||
|
@property({ type: Boolean, attribute: "no-name" })
|
||||||
|
public noName = false;
|
||||||
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
@restoreScroll(".container") private _savedScrollPos?: number;
|
@restoreScroll(".container") private _savedScrollPos?: number;
|
||||||
|
|
||||||
@ -52,14 +65,22 @@ class HaLogbook extends LitElement {
|
|||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
if (!this.entries?.length) {
|
if (!this.entries?.length) {
|
||||||
return html`
|
return html`
|
||||||
<div class="container" .dir=${emitRTLDirection(this._rtl)}>
|
<div class="container no-entries" .dir=${emitRTLDirection(this._rtl)}>
|
||||||
${this.hass.localize("ui.panel.logbook.entries_not_found")}
|
${this.hass.localize("ui.panel.logbook.entries_not_found")}
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<div class="container" @scroll=${this._saveScrollPos}>
|
<div
|
||||||
|
class="container ${classMap({
|
||||||
|
narrow: this.narrow,
|
||||||
|
rtl: this._rtl,
|
||||||
|
"no-name": this.noName,
|
||||||
|
"no-icon": this.noIcon,
|
||||||
|
})}"
|
||||||
|
@scroll=${this._saveScrollPos}
|
||||||
|
>
|
||||||
${scroll({
|
${scroll({
|
||||||
items: this.entries,
|
items: this.entries,
|
||||||
renderItem: (item: LogbookEntry, index?: number) =>
|
renderItem: (item: LogbookEntry, index?: number) =>
|
||||||
@ -76,6 +97,7 @@ class HaLogbook extends LitElement {
|
|||||||
if (index === undefined) {
|
if (index === undefined) {
|
||||||
return html``;
|
return html``;
|
||||||
}
|
}
|
||||||
|
|
||||||
const previous = this.entries[index - 1];
|
const previous = this.entries[index - 1];
|
||||||
const state = item.entity_id ? this.hass.states[item.entity_id] : undefined;
|
const state = item.entity_id ? this.hass.states[item.entity_id] : undefined;
|
||||||
const item_username =
|
const item_username =
|
||||||
@ -98,11 +120,17 @@ class HaLogbook extends LitElement {
|
|||||||
<div class="time">
|
<div class="time">
|
||||||
${formatTimeWithSeconds(new Date(item.when), this.hass.language)}
|
${formatTimeWithSeconds(new Date(item.when), this.hass.language)}
|
||||||
</div>
|
</div>
|
||||||
|
<div class="icon-message">
|
||||||
|
${!this.noIcon
|
||||||
|
? html`
|
||||||
<ha-icon
|
<ha-icon
|
||||||
.icon=${state ? stateIcon(state) : domainIcon(item.domain)}
|
.icon=${state ? stateIcon(state) : domainIcon(item.domain)}
|
||||||
></ha-icon>
|
></ha-icon>
|
||||||
|
`
|
||||||
|
: ""}
|
||||||
<div class="message">
|
<div class="message">
|
||||||
${!item.entity_id
|
${!this.noName
|
||||||
|
? !item.entity_id
|
||||||
? html`<span class="name">${item.name}</span>`
|
? html`<span class="name">${item.name}</span>`
|
||||||
: html`
|
: html`
|
||||||
<a
|
<a
|
||||||
@ -112,17 +140,16 @@ class HaLogbook extends LitElement {
|
|||||||
class="name"
|
class="name"
|
||||||
>${item.name}</a
|
>${item.name}</a
|
||||||
>
|
>
|
||||||
`}
|
`
|
||||||
<span
|
: ""}
|
||||||
>${item.message}${item_username
|
<span class="item-message">${item.message}</span>
|
||||||
? ` (${item_username})`
|
<span>${item_username ? ` (${item_username})` : ``}</span>
|
||||||
: ``}</span
|
|
||||||
>
|
|
||||||
${!item.context_event_type
|
${!item.context_event_type
|
||||||
? ""
|
? ""
|
||||||
: item.context_event_type === "call_service"
|
: item.context_event_type === "call_service"
|
||||||
? // Service Call
|
? // Service Call
|
||||||
html` by service ${item.context_domain}.${item.context_service}`
|
html` by service
|
||||||
|
${item.context_domain}.${item.context_service}`
|
||||||
: item.context_entity_id === item.entity_id
|
: item.context_entity_id === item.entity_id
|
||||||
? // HomeKit or something that self references
|
? // HomeKit or something that self references
|
||||||
html` by
|
html` by
|
||||||
@ -141,6 +168,7 @@ class HaLogbook extends LitElement {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -163,26 +191,36 @@ class HaLogbook extends LitElement {
|
|||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
:host([rtl]) {
|
.rtl {
|
||||||
direction: ltr;
|
direction: ltr;
|
||||||
}
|
}
|
||||||
|
|
||||||
.entry {
|
.entry {
|
||||||
display: flex;
|
display: flex;
|
||||||
line-height: 2em;
|
line-height: 2em;
|
||||||
|
padding-bottom: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.time {
|
.time {
|
||||||
width: 65px;
|
width: 65px;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
font-size: 0.8em;
|
font-size: 12px;
|
||||||
color: var(--secondary-text-color);
|
color: var(--secondary-text-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
:host([rtl]) .date {
|
.rtl .date {
|
||||||
direction: rtl;
|
direction: rtl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.icon-message {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.no-entries {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
ha-icon {
|
ha-icon {
|
||||||
margin: 0 8px 0 16px;
|
margin: 0 8px 0 16px;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
@ -193,6 +231,10 @@ class HaLogbook extends LitElement {
|
|||||||
color: var(--primary-text-color);
|
color: var(--primary-text-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.no-name .item-message {
|
||||||
|
text-transform: capitalize;
|
||||||
|
}
|
||||||
|
|
||||||
a {
|
a {
|
||||||
color: var(--primary-color);
|
color: var(--primary-color);
|
||||||
}
|
}
|
||||||
@ -212,8 +254,21 @@ class HaLogbook extends LitElement {
|
|||||||
.uni-virtualizer-host > * {
|
.uni-virtualizer-host > * {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.narrow .entry {
|
||||||
|
flex-direction: column-reverse;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.narrow .icon-message ha-icon {
|
||||||
|
margin-left: 0;
|
||||||
|
}
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
customElements.define("ha-logbook", HaLogbook);
|
declare global {
|
||||||
|
interface HTMLElementTagNameMap {
|
||||||
|
"ha-logbook": HaLogbook;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -389,6 +389,8 @@
|
|||||||
"dismiss": "Dismiss dialog",
|
"dismiss": "Dismiss dialog",
|
||||||
"settings": "Entity settings",
|
"settings": "Entity settings",
|
||||||
"edit": "Edit entity",
|
"edit": "Edit entity",
|
||||||
|
"controls": "Controls",
|
||||||
|
"history": "History",
|
||||||
"script": {
|
"script": {
|
||||||
"last_action": "Last Action",
|
"last_action": "Last Action",
|
||||||
"last_triggered": "Last Triggered"
|
"last_triggered": "Last Triggered"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user