From b76bf16e9f3d4dc8c28767d552139de3aa033fa7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20W?= Date: Mon, 1 Feb 2021 11:37:37 +0100 Subject: [PATCH] feat(series): Retrieve an attribute of your entity instead of the state (#32) * Support for attributes * Doc Fixes #29 --- .devcontainer/ui-lovelace.yaml | 13 +++++++++++++ README.md | 2 ++ src/apexcharts-card.ts | 16 ++++++++++++++-- src/const.ts | 2 ++ src/graphEntry.ts | 31 +++++++++++++++++++++++++++---- src/types-config-ti.ts | 2 ++ src/types-config.ts | 2 ++ src/types.ts | 1 + 8 files changed, 63 insertions(+), 6 deletions(-) diff --git a/.devcontainer/ui-lovelace.yaml b/.devcontainer/ui-lovelace.yaml index cbc9bd7..ade100c 100644 --- a/.devcontainer/ui-lovelace.yaml +++ b/.devcontainer/ui-lovelace.yaml @@ -344,3 +344,16 @@ views: group_by: func: sum duration: 20min + + - type: custom:apexcharts-card + graph_span: 1h + update_delay: 5.5s + # apex_config: + # dataLabels: + # enabled: true + header: + show: true + title: Light Brightness (attribute test) + series: + - entity: light.kitchen_lights + attribute: brightness diff --git a/README.md b/README.md index 12b7d83..ebc9093 100644 --- a/README.md +++ b/README.md @@ -107,6 +107,7 @@ The card stricly validates all the options available (but not for the `apex_conf | :white_check_mark: `series` | array | | v1.0.0 | See [series](#series-options) | | `chart_type` | string | `line` | NEXT_VERSION | See [chart_type](#chart_type-options) | | `update_interval` | string | | v1.1.0 | By default the card updates on every state change. Setting this overrides the behaviour. Valid values are any time string, eg: `1h`, `12min`, `1d`, `1h25`, `10sec`, ... | +| `update_delay` | string | `1500ms` | NEXT_VERSION | If doesn't display the last state but the one before, you'll want to increase this value, don't go over `10s`, it's not necessary. You'll also want to increase this value if you are using `attribute` in the `series`. Valid values are any time strings. This is because of how Home-Assistant works with history, see [here](https://www.home-assistant.io/integrations/recorder/#commit_interval) | | `graph_span` | string | `24h` | v1.1.0 | The span of the graph as a time interval. Valid values are any time string, eg: `1h`, `12min`, `1d`, `1h25`, `10sec`, ... | | `span` | object | | v1.2.0 | See [span](#span-options) | | `show` | object | | v1.0.0 | See [show](#main-show-options) | @@ -124,6 +125,7 @@ The card stricly validates all the options available (but not for the `apex_conf | Name | Type | Default | Since | Description | | ---- | :--: | :-----: | :---: | ----------- | | :white_check_mark: `entity` | string | | v1.0.0 | The `entity_id` of the sensor to display | +| `attribute` | string | | NEXT_VERSION | Instead of retrieving the state, it will retrieve an `attribute` of the entity. Make sure you increase `update_delay` if the chart doesn't reflect the last value of the attribute | | `name` | string | | v1.0.0 | Override the name of the entity | | `color` | string | | v1.1.0 | Color of the serie. Supported formats: `yellow`, `#aabbcc`, `rgb(128, 128, 128)` or `var(--css-color-variable)` | | `type` | string | `line` | v1.0.0 | `line`, `area` or `column` are supported for now | diff --git a/src/apexcharts-card.ts b/src/apexcharts-card.ts index f14844a..aaea3e3 100644 --- a/src/apexcharts-card.ts +++ b/src/apexcharts-card.ts @@ -25,7 +25,14 @@ import GraphEntry from './graphEntry'; import { createCheckers } from 'ts-interface-checker'; import { ChartCardExternalConfig, ChartCardSeriesExternalConfig } from './types-config'; import exportedTypeSuite from './types-config-ti'; -import { DEFAULT_FLOAT_PRECISION, DEFAULT_SHOW_LEGEND_VALUE, moment, NO_VALUE, TIMESERIES_TYPES } from './const'; +import { + DEFAULT_FLOAT_PRECISION, + DEFAULT_SHOW_LEGEND_VALUE, + DEFAULT_UPDATE_DELAY, + moment, + NO_VALUE, + TIMESERIES_TYPES, +} from './const'; import { DEFAULT_COLORS, DEFAULT_DURATION, @@ -100,6 +107,8 @@ class ChartsCard extends LitElement { private _seriesOffset: number[] = []; + private _updateDelay: number = DEFAULT_UPDATE_DELAY; + @property({ type: Boolean }) private _warning = false; public connectedCallback() { @@ -186,7 +195,7 @@ class ChartsCard extends LitElement { // give time to HA's recorder component to write the data in the history setTimeout(() => { this._updateData(); - }, 1500); + }, this._updateDelay); } } } @@ -212,6 +221,9 @@ class ChartsCard extends LitElement { this._seriesOffset[index] = validateOffset(serie.offset, `series[${index}].offset`); } }); + if (config.update_delay) { + this._updateDelay = validateInterval(config.update_delay, `update_delay`); + } this._config = mergeDeep( { diff --git a/src/const.ts b/src/const.ts index 88bed2d..81dd9a5 100644 --- a/src/const.ts +++ b/src/const.ts @@ -37,3 +37,5 @@ export const TIMESERIES_TYPES = ['line', 'scatter', undefined]; export const DEFAULT_MIN = 0; export const DEFAULT_MAX = 100; + +export const DEFAULT_UPDATE_DELAY = 1500; diff --git a/src/graphEntry.ts b/src/graphEntry.ts index 11a7174..7da64fb 100644 --- a/src/graphEntry.ts +++ b/src/graphEntry.ts @@ -171,12 +171,33 @@ export default class GraphEntry { new Date(history.data.slice(-1)[0]![0] + 1) : startHistory, end, - skipInitialState, + this._config.attribute ? false : skipInitialState, + this._config.attribute ? true : false, ); if (newHistory && newHistory[0] && newHistory[0].length > 0) { + /* + hack because HA doesn't return anything if skipInitialState is false + when retrieving for attributes so we retrieve it and we remove it. + */ + if (this._config.attribute && skipInitialState) { + newHistory[0].shift(); + } const newStateHistory: EntityCachePoints = newHistory[0].map((item) => { - const stateParsed = parseFloat(item.state); - return [new Date(item.last_changed).getTime(), !Number.isNaN(stateParsed) ? stateParsed : null]; + let stateParsed: number | null = null; + if (this._config.attribute) { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + if (item.attributes && item.attributes![this._config.attribute]) { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + stateParsed = parseFloat(item.attributes![this._config.attribute]); + } + } else { + stateParsed = parseFloat(item.state); + } + if (this._config.attribute) { + return [new Date(item.last_updated).getTime(), !Number.isNaN(stateParsed) ? stateParsed : null]; + } else { + return [new Date(item.last_changed).getTime(), !Number.isNaN(stateParsed) ? stateParsed : null]; + } }); if (history?.data.length) { history.span = this._graphSpan; @@ -221,13 +242,15 @@ export default class GraphEntry { start: Date | undefined, end: Date | undefined, skipInitialState: boolean, + withAttributes = false, ): Promise { let url = 'history/period'; if (start) url += `/${start.toISOString()}`; url += `?filter_entity_id=${this._entityID}`; if (end) url += `&end_time=${end.toISOString()}`; if (skipInitialState) url += '&skip_initial_state'; - url += '&minimal_response'; + if (!withAttributes) url += '&minimal_response'; + if (withAttributes) url += '&significant_changes_only=0'; return this._hass?.callApi('GET', url); } diff --git a/src/types-config-ti.ts b/src/types-config-ti.ts index 1f265ec..923ff0d 100644 --- a/src/types-config-ti.ts +++ b/src/types-config-ti.ts @@ -8,6 +8,7 @@ export const ChartCardExternalConfig = t.iface([], { "type": t.lit('custom:apexcharts-card'), "chart_type": t.opt(t.union(t.lit('line'), t.lit('scatter'), t.lit('pie'), t.lit('donut'), t.lit('radialBar'))), "update_interval": t.opt("string"), + "update_delay": t.opt("string"), "series": t.array("ChartCardSeriesExternalConfig"), "graph_span": t.opt("string"), "span": t.opt("ChartCardSpanExtConfig"), @@ -31,6 +32,7 @@ export const ChartCardSpanExtConfig = t.iface([], { export const ChartCardSeriesExternalConfig = t.iface([], { "entity": "string", + "attribute": t.opt("string"), "name": t.opt("string"), "type": t.opt(t.union(t.lit('line'), t.lit('column'), t.lit('area'))), "color": t.opt("string"), diff --git a/src/types-config.ts b/src/types-config.ts index a5d1d05..802c9d1 100644 --- a/src/types-config.ts +++ b/src/types-config.ts @@ -2,6 +2,7 @@ export interface ChartCardExternalConfig { type: 'custom:apexcharts-card'; chart_type?: 'line' | 'scatter' | 'pie' | 'donut' | 'radialBar'; update_interval?: string; + update_delay?: string; series: ChartCardSeriesExternalConfig[]; graph_span?: string; span?: ChartCardSpanExtConfig; @@ -27,6 +28,7 @@ export interface ChartCardSpanExtConfig { } export interface ChartCardSeriesExternalConfig { entity: string; + attribute?: string; name?: string; type?: 'line' | 'column' | 'area'; color?: string; diff --git a/src/types.ts b/src/types.ts index 143fc7b..539c557 100644 --- a/src/types.ts +++ b/src/types.ts @@ -43,6 +43,7 @@ export interface HassHistoryEntry { last_updated: string; state: string; last_changed: string; + attributes?: never; } export interface HistoryBucket {