From e2433b679aad6efdb38137970d6acbfa2d3bc75f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Wiedemann?= Date: Wed, 3 Feb 2021 01:26:18 +0000 Subject: [PATCH] feat(series): New `fill_raw` option to fill missing data in raw history --- .devcontainer/ui-lovelace.yaml | 11 +++++++++++ README.md | 3 ++- src/apexcharts-card.ts | 2 ++ src/const.ts | 1 + src/graphEntry.ts | 17 ++++++++++++++++- src/types-config-ti.ts | 1 + src/types-config.ts | 1 + 7 files changed, 34 insertions(+), 2 deletions(-) diff --git a/.devcontainer/ui-lovelace.yaml b/.devcontainer/ui-lovelace.yaml index 6af9f62..10206b0 100644 --- a/.devcontainer/ui-lovelace.yaml +++ b/.devcontainer/ui-lovelace.yaml @@ -383,3 +383,14 @@ views: group_by: duration: 1h func: last + + - type: custom:apexcharts-card + update_interval: 2s + graph_span: 10s + header: + show: true + title: should show a line + series: + - entity: sensor.outside_temperature + curve: stepline + extend_to_end: false diff --git a/README.md b/README.md index 8503c8f..4876f76 100644 --- a/README.md +++ b/README.md @@ -135,6 +135,7 @@ The card stricly validates all the options available (but not for the `apex_conf | `extend_to_end` | boolean | `true` | v1.0.0 | If the last data is older than the end time displayed on the graph, setting to true will extend the value until the end of the timeline. Only works for `line` and `area` types. | | `unit` | string | | v1.0.0 | Override the unit of the sensor | | `float_precision` | number | `1` | v1.2.0 | The precision used to display data in the legend and the tooltip. It doesn't impact how the data is displayed on the graph | +| `fill_raw` | string | `'null'` | NEXT_VERSION | If there is any missing value in the history, `last` will replace them with the last non-empty state, `zero` will fill missing values with `0`, `'null'` will fill missing values with `null`. This is applied before `group_by` options | | `group_by` | object | | v1.0.0 | See [group_by](#group_by-options) | | `invert` | boolean | `false` | v1.2.0 | Negates the data (`1` -> `-1`). Usefull to display opposites values like network in (standard)/out (inverted) | | `data_generator` | string | | v1.2.0 | See [data_generator](#data_generator-option) | @@ -174,7 +175,7 @@ The card stricly validates all the options available (but not for the `apex_conf | ---- | :--: | :-----: | :---: | ----------- | | `func` | string | `raw` | v1.0.0 | See [func](#func-options) | | `duration` | string | `1h` | v1.0.0 | If `func` is **not** `raw` only. It builds buckets of states over `duration` period of time. Doesn't work for months. Eg of valid values: `2h`, `1d`, `10s`, `25min`, `1h30`, ... | -| `fill` | string | `last` | v1.0.0 | If `func` is **not** `raw` only. If there is any missing value in the states history, `last` will replace it the last non-empty state, `zero` will fill missing values with `0`, `'null'` will fill missing values with `null` +| `fill` | string | `last` | v1.0.0 | If `func` is **not** `raw` only. If there is any missing value in the buckets of history data (grouped by duration), `last` will replace them with the last non-empty state, `zero` will fill missing values with `0`, `'null'` will fill missing values with `null` | ### `func` Options diff --git a/src/apexcharts-card.ts b/src/apexcharts-card.ts index 02fee97..3ccf1e3 100644 --- a/src/apexcharts-card.ts +++ b/src/apexcharts-card.ts @@ -27,6 +27,7 @@ import { createCheckers } from 'ts-interface-checker'; import { ChartCardExternalConfig, ChartCardSeriesExternalConfig } from './types-config'; import exportedTypeSuite from './types-config-ti'; import { + DEFAULT_FILL_RAW, DEFAULT_FLOAT_PRECISION, DEFAULT_SHOW_IN_CHART, DEFAULT_SHOW_IN_HEADER, @@ -270,6 +271,7 @@ class ChartsCard extends LitElement { if (serie.color) { this._headerColors[index] = serie.color; } + serie.fill_raw = serie.fill_raw || DEFAULT_FILL_RAW; serie.extend_to_end = serie.extend_to_end !== undefined ? serie.extend_to_end : true; serie.type = this._config?.chart_type ? undefined : serie.type || DEFAULT_SERIE_TYPE; serie.unit = this._config?.chart_type === 'radialBar' ? '%' : serie.unit; diff --git a/src/const.ts b/src/const.ts index 30fe08f..50af99e 100644 --- a/src/const.ts +++ b/src/const.ts @@ -12,6 +12,7 @@ export const DEFAULT_SERIE_TYPE = 'line'; export const DEFAULT_DURATION = '1h'; export const DEFAULT_FUNC = 'raw'; export const DEFAULT_GROUP_BY_FILL = 'last'; +export const DEFAULT_FILL_RAW = 'null'; export const DEFAULT_SHOW_LEGEND_VALUE = true; export const DEFAULT_SHOW_IN_HEADER = true; export const DEFAULT_SHOW_IN_CHART = true; diff --git a/src/graphEntry.ts b/src/graphEntry.ts index d6789f4..2ad12d1 100644 --- a/src/graphEntry.ts +++ b/src/graphEntry.ts @@ -1,7 +1,7 @@ import { HomeAssistant } from 'custom-card-helpers'; import { ChartCardSeriesConfig, EntityCachePoints, EntityEntryCache, HassHistory, HistoryBuckets } from './types'; import { compress, decompress, log } from './utils'; -import localForage from 'localforage'; +import localForage, { config } from 'localforage'; import { HassEntity } from 'home-assistant-js-websocket'; import { DateRange } from 'moment-range'; import { HOUR_24, moment } from './const'; @@ -182,6 +182,10 @@ export default class GraphEntry { if (this._config.attribute && skipInitialState) { newHistory[0].shift(); } + let lastNonNull: number | null = null; + if (history && history.data && history.data.length > 0) { + lastNonNull = history.data[history.data.length - 1][1]; + } const newStateHistory: EntityCachePoints = newHistory[0].map((item) => { let stateParsed: number | null = null; if (this._config.attribute) { @@ -193,6 +197,17 @@ export default class GraphEntry { } else { stateParsed = parseFloat(item.state); } + stateParsed = !Number.isNaN(stateParsed) ? stateParsed : null; + if (stateParsed === null) { + if (this._config.fill_raw === 'zero') { + stateParsed = 0; + } else if (this._config.fill_raw === 'last') { + stateParsed = lastNonNull; + } + } else { + lastNonNull = stateParsed; + } + if (this._config.attribute) { return [new Date(item.last_updated).getTime(), !Number.isNaN(stateParsed) ? stateParsed : null]; } else { diff --git a/src/types-config-ti.ts b/src/types-config-ti.ts index 7e14289..fe49b3d 100644 --- a/src/types-config-ti.ts +++ b/src/types-config-ti.ts @@ -45,6 +45,7 @@ export const ChartCardSeriesExternalConfig = t.iface([], { "min": t.opt("number"), "max": t.opt("number"), "offset": t.opt("string"), + "fill_raw": t.opt("GroupByFill"), "show": t.opt(t.iface([], { "as_duration": t.opt("ChartCardPrettyTime"), "legend_value": t.opt("boolean"), diff --git a/src/types-config.ts b/src/types-config.ts index 19a2bb5..66b62c6 100644 --- a/src/types-config.ts +++ b/src/types-config.ts @@ -41,6 +41,7 @@ export interface ChartCardSeriesExternalConfig { min?: number; max?: number; offset?: string; + fill_raw?: GroupByFill; show?: { as_duration?: ChartCardPrettyTime; legend_value?: boolean;