From ebe16a38a176878e39f8035d8144ed61142b6717 Mon Sep 17 00:00:00 2001 From: Zack Arnett Date: Mon, 31 Aug 2020 19:17:26 -0500 Subject: [PATCH 1/8] Add History Tab to More info --- src/dialogs/more-info/ha-more-info-dialog.ts | 336 +++++++++++++------ src/translations/en.json | 2 + 2 files changed, 244 insertions(+), 94 deletions(-) diff --git a/src/dialogs/more-info/ha-more-info-dialog.ts b/src/dialogs/more-info/ha-more-info-dialog.ts index b1953f678d74..4736437b3685 100644 --- a/src/dialogs/more-info/ha-more-info-dialog.ts +++ b/src/dialogs/more-info/ha-more-info-dialog.ts @@ -1,33 +1,40 @@ import "@material/mwc-button"; import "@material/mwc-icon-button"; -import "../../components/ha-header-bar"; -import "../../components/ha-dialog"; -import "../../components/ha-svg-icon"; +import "@material/mwc-tab"; +import "@material/mwc-tab-bar"; +import { mdiClose, mdiCog, mdiPencil } from "@mdi/js"; +import { + css, + customElement, + html, + internalProperty, + LitElement, + property, +} from "lit-element"; +import { styleMap } from "lit-html/directives/style-map"; import { isComponentLoaded } from "../../common/config/is_component_loaded"; 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 { 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 { getRecentWithCache } from "../../data/cached-history"; import { removeEntityRegistryEntry } from "../../data/entity_registry"; +import { HistoryResult } from "../../data/history"; +import { getLogbookData, LogbookEntry } from "../../data/logbook"; +import { fetchPersons } from "../../data/person"; +import { fetchUsers } from "../../data/user"; 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 { HomeAssistant } from "../../types"; import { showConfirmationDialog } from "../generic/show-dialog-box"; 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 EDITABLE_DOMAINS_WITH_ID = ["scene", "automation"]; @@ -47,6 +54,16 @@ export class MoreInfoDialog extends LitElement { @internalProperty() private _entityId?: string | null; + @internalProperty() private _currTabIndex = 0; + + @internalProperty() private _entries: LogbookEntry[] = []; + + @internalProperty() private _userIdToName = {}; + + @internalProperty() private _isLoading = false; + + private _fetchUserDone?: Promise; + private _historyRefreshInterval?: number; public showDialog(params: MoreInfoDialogParams) { @@ -57,7 +74,9 @@ export class MoreInfoDialog extends LitElement { this.large = false; this._stateHistory = undefined; if (this._computeShowHistoryComponent(this._entityId)) { + this._fetchUserDone = this._fetchUserNames(); this._getStateHistory(); + this._getLogBookData(); clearInterval(this._historyRefreshInterval); this._historyRefreshInterval = window.setInterval(() => { this._getStateHistory(); @@ -68,6 +87,9 @@ export class MoreInfoDialog extends LitElement { public closeDialog() { this._entityId = undefined; this._stateHistory = undefined; + this._currTabIndex = 0; + this._entries = []; + this._userIdToName = {}; clearInterval(this._historyRefreshInterval); this._historyRefreshInterval = undefined; fireEvent(this, "dialog-closed", { dialog: this.localName }); @@ -93,85 +115,133 @@ export class MoreInfoDialog extends LitElement { hideActions data-domain=${domain} > - - + + + + +
+ ${computeStateName(stateObj)} +
+ ${this.hass.user!.is_admin + ? html` + + + + ` + : ""} + ${this.hass.user!.is_admin && + ((EDITABLE_DOMAINS_WITH_ID.includes(domain) && + stateObj.attributes.id) || + EDITABLE_DOMAINS.includes(domain)) + ? html` + + + + ` + : ""} +
+ - -
-
- ${computeStateName(stateObj)} -
- ${this.hass.user!.is_admin - ? html` - - ` - : ""} - ${this.hass.user!.is_admin && - ((EDITABLE_DOMAINS_WITH_ID.includes(domain) && - stateObj.attributes.id) || - EDITABLE_DOMAINS.includes(domain)) - ? html` - - ` - : ""} -
+ + + +
- ${DOMAINS_NO_INFO.includes(domain) - ? "" - : html` - - `} - ${this._computeShowHistoryComponent(entityId) + ${this._currTabIndex === 0 ? html` - + `} + + > + ${stateObj.attributes.restored + ? html` +

+ ${this.hass.localize( + "ui.dialogs.more_info_control.restored.not_provided" + )} +

+

+ ${this.hass.localize( + "ui.dialogs.more_info_control.restored.remove_intro" + )} +

+ + ${this.hass.localize( + "ui.dialogs.more_info_control.restored.remove_action" + )} + + ` + : ""} ` - : ""} - - - ${stateObj.attributes.restored - ? html`

- ${this.hass.localize( - "ui.dialogs.more_info_control.restored.not_provided" - )} -

-

- ${this.hass.localize( - "ui.dialogs.more_info_control.restored.remove_intro" - )} -

- - ${this.hass.localize( - "ui.dialogs.more_info_control.restored.remove_action" - )} - ` - : ""} + : html` + ${this._computeShowHistoryComponent(entityId) + ? html` + + ${this._isLoading + ? html` +
+ +
+ ` + : html` + + `} + ` + : ""} + `}
`; @@ -198,6 +268,62 @@ export class MoreInfoDialog extends LitElement { ); } + private async _getLogBookData() { + if (!this._entityId) { + return; + } + this._isLoading = true; + + const yesterday = new Date(new Date().getTime() - 24 * 60 * 60 * 1000); + const now = new Date(); + const [entries] = await Promise.all([ + getLogbookData( + this.hass, + yesterday.toISOString(), + now.toISOString(), + this._entityId + ), + this._fetchUserDone, + ]); + + this._entries = entries.slice(0, 5); + this._isLoading = false; + } + + private async _fetchUserNames() { + const userIdToName = {}; + + // Start loading all the data + const personProm = fetchPersons(this.hass); + const userProm = this.hass.user!.is_admin && fetchUsers(this.hass); + + // Process persons + const persons = await personProm; + + for (const person of persons.storage) { + if (person.user_id) { + userIdToName[person.user_id] = person.name; + } + } + for (const person of persons.config) { + if (person.user_id) { + userIdToName[person.user_id] = person.name; + } + } + + // Process users + if (userProm) { + const users = await userProm; + for (const user of users) { + if (!(user.id in userIdToName)) { + userIdToName[user.id] = user.name; + } + } + } + + this._userIdToName = userIdToName; + } + private _computeShowHistoryComponent(entityId) { return ( isComponentLoaded(this.hass, "history") && @@ -243,6 +369,15 @@ export class MoreInfoDialog extends LitElement { 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() { return [ haStyleDialog, @@ -256,8 +391,6 @@ export class MoreInfoDialog extends LitElement { --mdc-theme-on-primary: var(--primary-text-color); --mdc-theme-primary: var(--mdc-theme-surface); 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) { @@ -268,6 +401,15 @@ export class MoreInfoDialog extends LitElement { } } + mwc-tab-bar { + border-bottom: 1px solid + var(--mdc-dialog-scroll-divider-color, rgba(0, 0, 0, 0.12)); + } + + .progress-wrapper { + height: 50px; + } + @media all and (min-width: 451px) and (min-height: 501px) { ha-dialog { --mdc-dialog-max-width: 90vw; @@ -315,3 +457,9 @@ export class MoreInfoDialog extends LitElement { ]; } } + +declare global { + interface HTMLElementTagNameMap { + "ha-more-info-dialog": MoreInfoDialog; + } +} diff --git a/src/translations/en.json b/src/translations/en.json index d922b5f91940..938734ea5b02 100755 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -389,6 +389,8 @@ "dismiss": "Dismiss dialog", "settings": "Entity settings", "edit": "Edit entity", + "controls": "Controls", + "history": "History", "script": { "last_action": "Last Action", "last_triggered": "Last Triggered" From 6271f0eadc0f3354a77c09357c7a906892ad82c8 Mon Sep 17 00:00:00 2001 From: Zack Arnett Date: Mon, 31 Aug 2020 19:46:04 -0500 Subject: [PATCH 2/8] ignore ts error as in panel logbook --- src/dialogs/more-info/ha-more-info-dialog.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/dialogs/more-info/ha-more-info-dialog.ts b/src/dialogs/more-info/ha-more-info-dialog.ts index 4736437b3685..275b1fe4727c 100644 --- a/src/dialogs/more-info/ha-more-info-dialog.ts +++ b/src/dialogs/more-info/ha-more-info-dialog.ts @@ -286,6 +286,7 @@ export class MoreInfoDialog extends LitElement { this._fetchUserDone, ]); + // @ts-ignore this._entries = entries.slice(0, 5); this._isLoading = false; } From af157a2d9568dea95a8815e5d22d7af61ce7b732 Mon Sep 17 00:00:00 2001 From: Zack Arnett Date: Tue, 1 Sep 2020 15:06:13 -0500 Subject: [PATCH 3/8] comments --- src/data/logbook.ts | 12 +- src/dialogs/more-info/ha-more-info-dialog.ts | 181 +++-------------- .../more-info/ha-more-info-tab-history.ts | 187 ++++++++++++++++++ src/panels/logbook/ha-logbook.ts | 113 ++++++----- 4 files changed, 293 insertions(+), 200 deletions(-) create mode 100644 src/dialogs/more-info/ha-more-info-tab-history.ts diff --git a/src/data/logbook.ts b/src/data/logbook.ts index c1ff44b6fe4e..59a6c8fce2ea 100644 --- a/src/data/logbook.ts +++ b/src/data/logbook.ts @@ -23,7 +23,8 @@ export const getLogbookData = ( hass: HomeAssistant, startDate: string, endDate: string, - entityId?: string + entityId?: string, + entity_matches_only?: boolean ) => { const ALL_ENTITIES = "*"; @@ -51,7 +52,8 @@ export const getLogbookData = ( hass, startDate, endDate, - entityId !== ALL_ENTITIES ? entityId : undefined + entityId !== ALL_ENTITIES ? entityId : undefined, + entity_matches_only ).then((entries) => entries.reverse()); return DATA_CACHE[cacheKey][entityId]; }; @@ -60,11 +62,13 @@ const getLogbookDataFromServer = async ( hass: HomeAssistant, startDate: string, endDate: string, - entityId?: string + entityId?: string, + entity_matches_only?: boolean ) => { const url = `logbook/${startDate}?end_time=${endDate}${ entityId ? `&entity=${entityId}` : "" - }`; + }${entity_matches_only ? `&entity_matches_only=true` : ""}`; + return hass.callApi("GET", url); }; diff --git a/src/dialogs/more-info/ha-more-info-dialog.ts b/src/dialogs/more-info/ha-more-info-dialog.ts index 275b1fe4727c..3a3e07936fe4 100644 --- a/src/dialogs/more-info/ha-more-info-dialog.ts +++ b/src/dialogs/more-info/ha-more-info-dialog.ts @@ -11,7 +11,6 @@ import { LitElement, property, } from "lit-element"; -import { styleMap } from "lit-html/directives/style-map"; import { isComponentLoaded } from "../../common/config/is_component_loaded"; import { DOMAINS_MORE_INFO_NO_HISTORY } from "../../common/const"; import { fireEvent } from "../../common/dom/fire_event"; @@ -22,12 +21,7 @@ import "../../components/ha-dialog"; import "../../components/ha-header-bar"; import "../../components/ha-svg-icon"; import "../../components/state-history-charts"; -import { getRecentWithCache } from "../../data/cached-history"; import { removeEntityRegistryEntry } from "../../data/entity_registry"; -import { HistoryResult } from "../../data/history"; -import { getLogbookData, LogbookEntry } from "../../data/logbook"; -import { fetchPersons } from "../../data/person"; -import { fetchUsers } from "../../data/user"; import { showEntityEditorDialog } from "../../panels/config/entities/show-dialog-entity-editor"; import "../../panels/logbook/ha-logbook"; import { haStyleDialog } from "../../resources/styles"; @@ -50,21 +44,11 @@ export class MoreInfoDialog extends LitElement { @property({ type: Boolean, reflect: true }) public large = false; - @internalProperty() private _stateHistory?: HistoryResult; - @internalProperty() private _entityId?: string | null; @internalProperty() private _currTabIndex = 0; - @internalProperty() private _entries: LogbookEntry[] = []; - - @internalProperty() private _userIdToName = {}; - - @internalProperty() private _isLoading = false; - - private _fetchUserDone?: Promise; - - private _historyRefreshInterval?: number; + private _historyImported = false; public showDialog(params: MoreInfoDialogParams) { this._entityId = params.entityId; @@ -72,26 +56,11 @@ export class MoreInfoDialog extends LitElement { this.closeDialog(); } this.large = false; - this._stateHistory = undefined; - if (this._computeShowHistoryComponent(this._entityId)) { - this._fetchUserDone = this._fetchUserNames(); - this._getStateHistory(); - this._getLogBookData(); - clearInterval(this._historyRefreshInterval); - this._historyRefreshInterval = window.setInterval(() => { - this._getStateHistory(); - }, 60 * 1000); - } } public closeDialog() { this._entityId = undefined; - this._stateHistory = undefined; this._currTabIndex = 0; - this._entries = []; - this._userIdToName = {}; - clearInterval(this._historyRefreshInterval); - this._historyRefreshInterval = undefined; fireEvent(this, "dialog-closed", { dialog: this.localName }); } @@ -159,21 +128,25 @@ export class MoreInfoDialog extends LitElement { ` : ""} - - - - + ${this._computeShowHistoryComponent(entityId) + ? html` + + + + + ` + : ""}
${this._currTabIndex === 0 @@ -212,35 +185,10 @@ export class MoreInfoDialog extends LitElement { : ""} ` : html` - ${this._computeShowHistoryComponent(entityId) - ? html` - - ${this._isLoading - ? html` -
- -
- ` - : html` - - `} - ` - : ""} + `}
@@ -251,80 +199,6 @@ export class MoreInfoDialog extends LitElement { this.large = !this.large; } - private async _getStateHistory(): Promise { - if (!this._entityId) { - 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 async _getLogBookData() { - if (!this._entityId) { - return; - } - this._isLoading = true; - - const yesterday = new Date(new Date().getTime() - 24 * 60 * 60 * 1000); - const now = new Date(); - const [entries] = await Promise.all([ - getLogbookData( - this.hass, - yesterday.toISOString(), - now.toISOString(), - this._entityId - ), - this._fetchUserDone, - ]); - - // @ts-ignore - this._entries = entries.slice(0, 5); - this._isLoading = false; - } - - private async _fetchUserNames() { - const userIdToName = {}; - - // Start loading all the data - const personProm = fetchPersons(this.hass); - const userProm = this.hass.user!.is_admin && fetchUsers(this.hass); - - // Process persons - const persons = await personProm; - - for (const person of persons.storage) { - if (person.user_id) { - userIdToName[person.user_id] = person.name; - } - } - for (const person of persons.config) { - if (person.user_id) { - userIdToName[person.user_id] = person.name; - } - } - - // Process users - if (userProm) { - const users = await userProm; - for (const user of users) { - if (!(user.id in userIdToName)) { - userIdToName[user.id] = user.name; - } - } - } - - this._userIdToName = userIdToName; - } - private _computeShowHistoryComponent(entityId) { return ( isComponentLoaded(this.hass, "history") && @@ -377,6 +251,10 @@ export class MoreInfoDialog extends LitElement { } this._currTabIndex = ev.detail.index; + if (!this._historyImported) { + import("./ha-more-info-tab-history"); + this._historyImported = true; + } } static get styles() { @@ -449,8 +327,7 @@ export class MoreInfoDialog extends LitElement { --dialog-content-padding: 0; } - state-card-content, - state-history-charts { + state-card-content { display: block; margin-bottom: 16px; } diff --git a/src/dialogs/more-info/ha-more-info-tab-history.ts b/src/dialogs/more-info/ha-more-info-tab-history.ts new file mode 100644 index 000000000000..267c4fcaa2b1 --- /dev/null +++ b/src/dialogs/more-info/ha-more-info-tab-history.ts @@ -0,0 +1,187 @@ +import { + css, + customElement, + html, + internalProperty, + LitElement, + property, + PropertyValues, + TemplateResult, +} from "lit-element"; +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 { fetchPersons } from "../../data/person"; +import { fetchUsers } from "../../data/user"; +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 _isLoading = false; + + private _entries: LogbookEntry[] = []; + + private _userIdToName = {}; + + private _historyRefreshInterval?: number; + + protected render(): TemplateResult { + if (!this.entityId) { + return html``; + } + const entityId = this.entityId; + const stateObj = this.hass.states[entityId]; + + if (!stateObj) { + return html``; + } + + return html` + ${this._isLoading + ? html` + + ` + : html` + + + `} + `; + } + + protected updated(changedProps: PropertyValues): void { + if (!this.entityId) { + clearInterval(this._historyRefreshInterval); + } + + if (changedProps.has("entityId")) { + this._refreshData(); + } + } + + private async _refreshData(): Promise { + this._isLoading = true; + const fetchUserDone = this._fetchUserNames(); + const fetchHistoryDone = this._getStateHistory(); + const fetchLogBookDone = this._getLogBookData(); + + await Promise.all([fetchHistoryDone, fetchLogBookDone, fetchUserDone]); + + this._isLoading = false; + + clearInterval(this._historyRefreshInterval); + this._historyRefreshInterval = window.setInterval(() => { + this._getStateHistory(); + }, 60 * 1000); + } + + private async _getStateHistory(): Promise { + 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(); + const entries = await getLogbookData( + this.hass, + yesterday.toISOString(), + now.toISOString(), + this.entityId, + true + ); + + // @ts-ignore + this._entries = entries; + } + + private async _fetchUserNames() { + const userIdToName = {}; + + // Start loading all the data + const personProm = fetchPersons(this.hass); + const userProm = this.hass.user!.is_admin && fetchUsers(this.hass); + + // Process persons + const persons = await personProm; + + for (const person of persons.storage) { + if (person.user_id) { + userIdToName[person.user_id] = person.name; + } + } + for (const person of persons.config) { + if (person.user_id) { + userIdToName[person.user_id] = person.name; + } + } + + // Process users + if (userProm) { + const users = await userProm; + for (const user of users) { + if (!(user.id in userIdToName)) { + userIdToName[user.id] = user.name; + } + } + } + + this._userIdToName = userIdToName; + } + + static get styles() { + return [ + haStyleDialog, + css` + ha-circular-progress { + display: flex; + justify-content: center; + } + state-history-charts { + display: block; + margin-bottom: 16px; + } + + ha-logbook { + height: 360px; + } + `, + ]; + } +} + +declare global { + interface HTMLElementTagNameMap { + "ha-more-info-tab-history": MoreInfoTabHistoryDialog; + } +} diff --git a/src/panels/logbook/ha-logbook.ts b/src/panels/logbook/ha-logbook.ts index af886126d329..f97f6600cd53 100644 --- a/src/panels/logbook/ha-logbook.ts +++ b/src/panels/logbook/ha-logbook.ts @@ -1,16 +1,17 @@ import { css, CSSResult, + eventOptions, html, LitElement, property, PropertyValues, TemplateResult, - eventOptions, } from "lit-element"; import { scroll } from "lit-virtualizer"; import { formatDate } from "../../common/datetime/format_date"; import { formatTimeWithSeconds } from "../../common/datetime/format_time"; +import { restoreScroll } from "../../common/decorators/restore-scroll"; import { fireEvent } from "../../common/dom/fire_event"; import { domainIcon } from "../../common/entity/domain_icon"; import { stateIcon } from "../../common/entity/state_icon"; @@ -18,14 +19,16 @@ import { computeRTL, emitRTLDirection } from "../../common/util/compute_rtl"; import "../../components/ha-icon"; import { LogbookEntry } from "../../data/logbook"; import { HomeAssistant } from "../../types"; -import { restoreScroll } from "../../common/decorators/restore-scroll"; class HaLogbook extends LitElement { @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({ type: Boolean, attribute: "narrow", reflect: true }) + public narrow = false; @property({ attribute: "rtl", type: Boolean, reflect: true }) private _rtl = false; @@ -98,46 +101,49 @@ class HaLogbook extends LitElement {
${formatTimeWithSeconds(new Date(item.when), this.hass.language)}
- -
- ${!item.entity_id - ? html` ${item.name} ` - : html` - ${item.name} - `} - ${item.message}${item_username - ? ` (${item_username})` - : ``} - ${!item.context_event_type - ? "" - : item.context_event_type === "call_service" - ? // Service Call - html` by service ${item.context_domain}.${item.context_service}` - : item.context_entity_id === item.entity_id - ? // HomeKit or something that self references - html` by - ${item.context_name - ? item.context_name - : item.context_event_type}` - : // Another entity such as an automation or script - html` by - ${item.context_entity_id_name}`} +
+ +
+ ${!item.entity_id + ? html` ${item.name} ` + : html` + ${item.name} + `} + ${item.message}${item_username + ? ` (${item_username})` + : ``} + ${!item.context_event_type + ? "" + : item.context_event_type === "call_service" + ? // Service Call + html` by service + ${item.context_domain}.${item.context_service}` + : item.context_entity_id === item.entity_id + ? // HomeKit or something that self references + html` by + ${item.context_name + ? item.context_name + : item.context_event_type}` + : // Another entity such as an automation or script + html` by + ${item.context_entity_id_name}`} +
@@ -212,8 +218,27 @@ class HaLogbook extends LitElement { .uni-virtualizer-host > * { box-sizing: border-box; } + + :host([narrow]) .entry { + flex-direction: column; + } + + :host([narrow]) .icon-message { + display: flex; + align-items: center; + } + + :host([narrow]) .icon-message ha-icon { + margin-left: 0; + } `; } } customElements.define("ha-logbook", HaLogbook); + +declare global { + interface HTMLElementTagNameMap { + "ha-logbook": HaLogbook; + } +} From 2f86f33b231cf8492e1526d0e2f9a5006538ab3c Mon Sep 17 00:00:00 2001 From: Zack Arnett Date: Tue, 1 Sep 2020 15:13:24 -0500 Subject: [PATCH 4/8] Update src/data/logbook.ts Co-authored-by: J. Nick Koston --- src/data/logbook.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/data/logbook.ts b/src/data/logbook.ts index 59a6c8fce2ea..00fd8e937dfb 100644 --- a/src/data/logbook.ts +++ b/src/data/logbook.ts @@ -67,7 +67,7 @@ const getLogbookDataFromServer = async ( ) => { const url = `logbook/${startDate}?end_time=${endDate}${ entityId ? `&entity=${entityId}` : "" - }${entity_matches_only ? `&entity_matches_only=true` : ""}`; + }${entity_matches_only ? `&entity_matches_only` : ""}`; return hass.callApi("GET", url); }; From 9fee9e498afc0b803a8e811b580a79b490e4c82b Mon Sep 17 00:00:00 2001 From: Zack Arnett Date: Tue, 1 Sep 2020 19:28:49 -0500 Subject: [PATCH 5/8] comments --- src/dialogs/more-info/ha-more-info-dialog.ts | 10 ++--- .../more-info/ha-more-info-tab-history.ts | 7 +++- src/panels/logbook/ha-logbook.ts | 41 +++++++++++++++---- 3 files changed, 42 insertions(+), 16 deletions(-) diff --git a/src/dialogs/more-info/ha-more-info-dialog.ts b/src/dialogs/more-info/ha-more-info-dialog.ts index 3a3e07936fe4..854684427569 100644 --- a/src/dialogs/more-info/ha-more-info-dialog.ts +++ b/src/dialogs/more-info/ha-more-info-dialog.ts @@ -48,8 +48,6 @@ export class MoreInfoDialog extends LitElement { @internalProperty() private _currTabIndex = 0; - private _historyImported = false; - public showDialog(params: MoreInfoDialogParams) { this._entityId = params.entityId; if (!this._entityId) { @@ -195,6 +193,10 @@ export class MoreInfoDialog extends LitElement { `; } + protected firstUpdated(): void { + import("./ha-more-info-tab-history"); + } + private _enlarge() { this.large = !this.large; } @@ -251,10 +253,6 @@ export class MoreInfoDialog extends LitElement { } this._currTabIndex = ev.detail.index; - if (!this._historyImported) { - import("./ha-more-info-tab-history"); - this._historyImported = true; - } } static get styles() { diff --git a/src/dialogs/more-info/ha-more-info-tab-history.ts b/src/dialogs/more-info/ha-more-info-tab-history.ts index 267c4fcaa2b1..bc45755efef1 100644 --- a/src/dialogs/more-info/ha-more-info-tab-history.ts +++ b/src/dialogs/more-info/ha-more-info-tab-history.ts @@ -8,6 +8,7 @@ import { PropertyValues, TemplateResult, } from "lit-element"; +import { classMap } from "lit-html/directives/class-map"; import "../../components/ha-circular-progress"; import "../../components/state-history-charts"; import { getRecentWithCache } from "../../data/cached-history"; @@ -62,6 +63,9 @@ export class MoreInfoTabHistoryDialog extends LitElement { > +
${this.hass.localize("ui.panel.logbook.entries_not_found")}
`; @@ -102,9 +108,13 @@ class HaLogbook extends LitElement { ${formatTimeWithSeconds(new Date(item.when), this.hass.language)}
- + ${!this.noIcon + ? html` + + ` + : ""}
${!item.entity_id ? html` ${item.name} ` @@ -156,6 +166,9 @@ class HaLogbook extends LitElement { } private _entityClicked(ev: Event) { + if (this.noClick) { + return; + } ev.preventDefault(); fireEvent(this, "hass-more-info", { entityId: (ev.target as any).entityId, @@ -189,6 +202,15 @@ class HaLogbook extends LitElement { direction: rtl; } + .icon-message { + display: flex; + align-items: center; + } + + .no-entries { + text-align: center; + } + ha-icon { margin: 0 8px 0 16px; flex-shrink: 0; @@ -203,6 +225,12 @@ class HaLogbook extends LitElement { color: var(--primary-color); } + :host([no-click]) a { + color: inherit; + text-decoration: none; + cursor: auto; + } + .container { padding: 0 16px; } @@ -223,11 +251,6 @@ class HaLogbook extends LitElement { flex-direction: column; } - :host([narrow]) .icon-message { - display: flex; - align-items: center; - } - :host([narrow]) .icon-message ha-icon { margin-left: 0; } From be996227c19a3fa95cf9be34576c5c2056b49104 Mon Sep 17 00:00:00 2001 From: Zack Arnett Date: Wed, 2 Sep 2020 11:20:44 -0500 Subject: [PATCH 6/8] Don't get users. Get persons from Hass --- .../more-info/ha-more-info-tab-history.ts | 48 +++++-------------- 1 file changed, 13 insertions(+), 35 deletions(-) diff --git a/src/dialogs/more-info/ha-more-info-tab-history.ts b/src/dialogs/more-info/ha-more-info-tab-history.ts index bc45755efef1..846565f00863 100644 --- a/src/dialogs/more-info/ha-more-info-tab-history.ts +++ b/src/dialogs/more-info/ha-more-info-tab-history.ts @@ -14,8 +14,6 @@ import "../../components/state-history-charts"; import { getRecentWithCache } from "../../data/cached-history"; import { HistoryResult } from "../../data/history"; import { getLogbookData, LogbookEntry } from "../../data/logbook"; -import { fetchPersons } from "../../data/person"; -import { fetchUsers } from "../../data/user"; import "../../panels/logbook/ha-logbook"; import { haStyleDialog } from "../../resources/styles"; import { HomeAssistant } from "../../types"; @@ -32,7 +30,7 @@ export class MoreInfoTabHistoryDialog extends LitElement { private _entries: LogbookEntry[] = []; - private _userIdToName = {}; + private _persons = {}; private _historyRefreshInterval?: number; @@ -68,7 +66,7 @@ export class MoreInfoTabHistoryDialog extends LitElement { class=${classMap({ "no-entries": !this._entries.length })} .hass=${this.hass} .entries=${this._entries} - .userIdToName=${this._userIdToName} + .userIdToName=${this._persons} > `} `; @@ -87,11 +85,11 @@ export class MoreInfoTabHistoryDialog extends LitElement { private async _refreshData(): Promise { this._isLoading = true; - const fetchUserDone = this._fetchUserNames(); + this._fetchPersonNames(); const fetchHistoryDone = this._getStateHistory(); const fetchLogBookDone = this._getLogBookData(); - await Promise.all([fetchHistoryDone, fetchLogBookDone, fetchUserDone]); + await Promise.all([fetchHistoryDone, fetchLogBookDone]); this._isLoading = false; @@ -130,38 +128,18 @@ export class MoreInfoTabHistoryDialog extends LitElement { this._entries = entries; } - private async _fetchUserNames() { - const userIdToName = {}; - - // Start loading all the data - const personProm = fetchPersons(this.hass); - const userProm = this.hass.user!.is_admin && fetchUsers(this.hass); - - // Process persons - const persons = await personProm; - - for (const person of persons.storage) { - if (person.user_id) { - userIdToName[person.user_id] = person.name; - } - } - for (const person of persons.config) { - if (person.user_id) { - userIdToName[person.user_id] = person.name; - } - } + private _fetchPersonNames() { + const personEntities = Object.keys(this.hass.states).filter((entityId) => + entityId.startsWith("person") + ); - // Process users - if (userProm) { - const users = await userProm; - for (const user of users) { - if (!(user.id in userIdToName)) { - userIdToName[user.id] = user.name; - } + for (const personEntityId of personEntities) { + const person = this.hass.states[personEntityId]; + if (person.attributes.user_id) { + this._persons[person.attributes.user_id] = + person.attributes.friendly_name; } } - - this._userIdToName = userIdToName; } static get styles() { From 6f8c772533d7d6d4d9d647bbf76abd610db6e3f9 Mon Sep 17 00:00:00 2001 From: Zack Arnett Date: Wed, 2 Sep 2020 13:53:10 -0500 Subject: [PATCH 7/8] comments --- .../more-info/ha-more-info-tab-history.ts | 20 +++++----- src/panels/logbook/ha-logbook.ts | 38 +++++++++++-------- 2 files changed, 32 insertions(+), 26 deletions(-) diff --git a/src/dialogs/more-info/ha-more-info-tab-history.ts b/src/dialogs/more-info/ha-more-info-tab-history.ts index 846565f00863..f002ac32735e 100644 --- a/src/dialogs/more-info/ha-more-info-tab-history.ts +++ b/src/dialogs/more-info/ha-more-info-tab-history.ts @@ -9,6 +9,7 @@ import { 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"; @@ -63,6 +64,7 @@ export class MoreInfoTabHistoryDialog extends LitElement { narrow no-click no-icon + no-name class=${classMap({ "no-entries": !this._entries.length })} .hass=${this.hass} .entries=${this._entries} @@ -129,17 +131,15 @@ export class MoreInfoTabHistoryDialog extends LitElement { } private _fetchPersonNames() { - const personEntities = Object.keys(this.hass.states).filter((entityId) => - entityId.startsWith("person") - ); - - for (const personEntityId of personEntities) { - const person = this.hass.states[personEntityId]; - if (person.attributes.user_id) { - this._persons[person.attributes.user_id] = - person.attributes.friendly_name; + 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() { diff --git a/src/panels/logbook/ha-logbook.ts b/src/panels/logbook/ha-logbook.ts index d16a9380d926..14d7450d0929 100644 --- a/src/panels/logbook/ha-logbook.ts +++ b/src/panels/logbook/ha-logbook.ts @@ -39,6 +39,9 @@ class HaLogbook extends LitElement { @property({ type: Boolean, attribute: "no-icon", reflect: true }) public noIcon = false; + @property({ type: Boolean, attribute: "no-name", reflect: true }) + public noName = false; + // @ts-ignore @restoreScroll(".container") private _savedScrollPos?: number; @@ -116,22 +119,21 @@ class HaLogbook extends LitElement { ` : ""}
- ${!item.entity_id - ? html` ${item.name} ` - : html` - ${item.name} - `} - ${item.message}${item_username - ? ` (${item_username})` - : ``} + ${!this.noName + ? !item.entity_id + ? html`${item.name}` + : html` + ${item.name} + ` + : ""} + ${item.message} + ${item_username ? ` (${item_username})` : ``} ${!item.context_event_type ? "" : item.context_event_type === "call_service" @@ -221,6 +223,10 @@ class HaLogbook extends LitElement { color: var(--primary-text-color); } + :host([no-name]) .item-message { + text-transform: capitalize; + } + a { color: var(--primary-color); } From 524685cb9033422946bd0f6dd3d9c0c7d5492e90 Mon Sep 17 00:00:00 2001 From: Zack Arnett Date: Thu, 3 Sep 2020 17:26:47 -0500 Subject: [PATCH 8/8] comments --- src/dialogs/more-info/ha-more-info-dialog.ts | 8 +-- .../more-info/ha-more-info-tab-history.ts | 72 +++++++++---------- src/panels/logbook/ha-logbook.ts | 53 +++++++------- 3 files changed, 62 insertions(+), 71 deletions(-) diff --git a/src/dialogs/more-info/ha-more-info-dialog.ts b/src/dialogs/more-info/ha-more-info-dialog.ts index 854684427569..56d429836d01 100644 --- a/src/dialogs/more-info/ha-more-info-dialog.ts +++ b/src/dialogs/more-info/ha-more-info-dialog.ts @@ -82,7 +82,7 @@ export class MoreInfoDialog extends LitElement { hideActions data-domain=${domain} > -
+
+ ${!this._entries ? html` ` : html` - { - this._isLoading = true; - this._fetchPersonNames(); - const fetchHistoryDone = this._getStateHistory(); - const fetchLogBookDone = this._getLogBookData(); + this._stateHistory = undefined; + this._entries = undefined; - await Promise.all([fetchHistoryDone, fetchLogBookDone]); - - this._isLoading = false; - - clearInterval(this._historyRefreshInterval); - this._historyRefreshInterval = window.setInterval(() => { this._getStateHistory(); - }, 60 * 1000); + this._getLogBookData(); + + clearInterval(this._historyRefreshInterval); + this._historyRefreshInterval = window.setInterval(() => { + this._getStateHistory(); + }, 60 * 1000); + } } private async _getStateHistory(): Promise { @@ -118,16 +114,13 @@ export class MoreInfoTabHistoryDialog extends LitElement { private async _getLogBookData() { const yesterday = new Date(new Date().getTime() - 24 * 60 * 60 * 1000); const now = new Date(); - const entries = await getLogbookData( + this._entries = await getLogbookData( this.hass, yesterday.toISOString(), now.toISOString(), this.entityId, true ); - - // @ts-ignore - this._entries = entries; } private _fetchPersonNames() { @@ -146,18 +139,19 @@ export class MoreInfoTabHistoryDialog extends LitElement { return [ haStyleDialog, css` - ha-circular-progress { - display: flex; - justify-content: center; - } state-history-charts { display: block; margin-bottom: 16px; } - ha-logbook:not(.no-entries) { + ha-logbook.has-entries { height: 360px; } + + ha-circular-progress { + display: flex; + justify-content: center; + } `, ]; } diff --git a/src/panels/logbook/ha-logbook.ts b/src/panels/logbook/ha-logbook.ts index 14d7450d0929..173dc592317b 100644 --- a/src/panels/logbook/ha-logbook.ts +++ b/src/panels/logbook/ha-logbook.ts @@ -1,6 +1,7 @@ import { css, CSSResult, + customElement, eventOptions, html, LitElement, @@ -8,6 +9,7 @@ import { PropertyValues, TemplateResult, } from "lit-element"; +import { classMap } from "lit-html/directives/class-map"; import { scroll } from "lit-virtualizer"; import { formatDate } from "../../common/datetime/format_date"; import { formatTimeWithSeconds } from "../../common/datetime/format_time"; @@ -16,10 +18,12 @@ import { fireEvent } from "../../common/dom/fire_event"; import { domainIcon } from "../../common/entity/domain_icon"; import { stateIcon } from "../../common/entity/state_icon"; import { computeRTL, emitRTLDirection } from "../../common/util/compute_rtl"; +import "../../components/ha-circular-progress"; import "../../components/ha-icon"; import { LogbookEntry } from "../../data/logbook"; import { HomeAssistant } from "../../types"; +@customElement("ha-logbook") class HaLogbook extends LitElement { @property({ attribute: false }) public hass!: HomeAssistant; @@ -27,19 +31,16 @@ class HaLogbook extends LitElement { @property({ attribute: false }) public entries: LogbookEntry[] = []; - @property({ type: Boolean, attribute: "narrow", reflect: true }) + @property({ type: Boolean, attribute: "narrow" }) public narrow = false; - @property({ attribute: "rtl", type: Boolean, reflect: true }) + @property({ attribute: "rtl", type: Boolean }) private _rtl = false; - @property({ type: Boolean, attribute: "no-click", reflect: true }) - public noClick = false; - - @property({ type: Boolean, attribute: "no-icon", reflect: true }) + @property({ type: Boolean, attribute: "no-icon" }) public noIcon = false; - @property({ type: Boolean, attribute: "no-name", reflect: true }) + @property({ type: Boolean, attribute: "no-name" }) public noName = false; // @ts-ignore @@ -71,7 +72,15 @@ class HaLogbook extends LitElement { } return html` -
+
${scroll({ items: this.entries, renderItem: (item: LogbookEntry, index?: number) => @@ -88,6 +97,7 @@ class HaLogbook extends LitElement { if (index === undefined) { return html``; } + const previous = this.entries[index - 1]; const state = item.entity_id ? this.hass.states[item.entity_id] : undefined; const item_username = @@ -168,9 +178,6 @@ class HaLogbook extends LitElement { } private _entityClicked(ev: Event) { - if (this.noClick) { - return; - } ev.preventDefault(); fireEvent(this, "hass-more-info", { entityId: (ev.target as any).entityId, @@ -184,23 +191,24 @@ class HaLogbook extends LitElement { height: 100%; } - :host([rtl]) { + .rtl { direction: ltr; } .entry { display: flex; line-height: 2em; + padding-bottom: 8px; } .time { width: 65px; flex-shrink: 0; - font-size: 0.8em; + font-size: 12px; color: var(--secondary-text-color); } - :host([rtl]) .date { + .rtl .date { direction: rtl; } @@ -223,7 +231,7 @@ class HaLogbook extends LitElement { color: var(--primary-text-color); } - :host([no-name]) .item-message { + .no-name .item-message { text-transform: capitalize; } @@ -231,12 +239,6 @@ class HaLogbook extends LitElement { color: var(--primary-color); } - :host([no-click]) a { - color: inherit; - text-decoration: none; - cursor: auto; - } - .container { padding: 0 16px; } @@ -253,19 +255,18 @@ class HaLogbook extends LitElement { box-sizing: border-box; } - :host([narrow]) .entry { - flex-direction: column; + .narrow .entry { + flex-direction: column-reverse; + line-height: 1.5; } - :host([narrow]) .icon-message ha-icon { + .narrow .icon-message ha-icon { margin-left: 0; } `; } } -customElements.define("ha-logbook", HaLogbook); - declare global { interface HTMLElementTagNameMap { "ha-logbook": HaLogbook;