diff --git a/.devcontainer/ui-lovelace.yaml b/.devcontainer/ui-lovelace.yaml index d9fb978..e062dd5 100644 --- a/.devcontainer/ui-lovelace.yaml +++ b/.devcontainer/ui-lovelace.yaml @@ -866,7 +866,19 @@ views: show: in_brush: true in_chart: false - - type: entities - title: test - entities: - - sensor.random0_100 + - type: custom:apexcharts-card + graph_span: 1h + all_series_config: + type: column + series: + - entity: sensor.random0_100 + name: last + group_by: + func: last + duration: 10min + - entity: sensor.random0_100 + name: first + group_by: + func: first + duration: 10min + start_with_last: true diff --git a/README.md b/README.md index a161753..501006b 100644 --- a/README.md +++ b/README.md @@ -223,6 +223,7 @@ The position of the marker will only update when the card updates (state change | `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 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` | +| `start_with_last` | boolean | `false` | NEXT_VERSION | If `true`, each bucket of data will start with the last value from the previous bucket of data. Mostly useful only with `func: diff` | ### `func` Options diff --git a/src/graphEntry.ts b/src/graphEntry.ts index 6b839ca..c003cbd 100644 --- a/src/graphEntry.ts +++ b/src/graphEntry.ts @@ -371,8 +371,7 @@ export default class GraphEntry { } buckets.some((bucket, index) => { - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - if (bucket.timestamp > properEntry![0] && index > 0) { + if (bucket.timestamp > properEntry[0] && index > 0) { buckets[index - 1].data.push(properEntry); return true; } @@ -381,7 +380,7 @@ export default class GraphEntry { }); let lastNonNullBucketValue: number | null = null; const now = new Date().getTime(); - buckets.forEach((bucket) => { + buckets.forEach((bucket, index) => { if (bucket.data.length === 0) { if (this._config.group_by.fill === 'last' && bucket.timestamp <= now) { bucket.data[0] = [bucket.timestamp, lastNonNullBucketValue]; @@ -393,6 +392,22 @@ export default class GraphEntry { } else { lastNonNullBucketValue = bucket.data.slice(-1)[0][1]; } + if (this._config.group_by.start_with_last) { + if (index > 0) { + if (bucket.data[0][0] !== bucket.timestamp) { + const prevBucketData = buckets[index - 1].data; + bucket.data.unshift([bucket.timestamp, prevBucketData[prevBucketData.length - 1][1]]); + } + } else { + const firstIndexAfter = history.data.findIndex((entry) => { + if (entry[0] > bucket.timestamp) return true; + return false; + }); + if (firstIndexAfter > 0) { + bucket.data.unshift([bucket.timestamp, history.data[firstIndexAfter - 1][1]]); + } + } + } }); buckets.pop(); while ( diff --git a/src/types-config-ti.ts b/src/types-config-ti.ts index 369cc2f..a9d5d54 100644 --- a/src/types-config-ti.ts +++ b/src/types-config-ti.ts @@ -88,6 +88,7 @@ export const ChartCardAllSeriesExternalConfig = t.iface([], { "duration": t.opt("string"), "func": t.opt("GroupByFunc"), "fill": t.opt("GroupByFill"), + "start_with_last": t.opt("boolean"), })), "transform": t.opt("string"), "color_threshold": t.opt(t.array("ChartCardColorThreshold")), diff --git a/src/types-config.ts b/src/types-config.ts index a26b8f1..62ad498 100644 --- a/src/types-config.ts +++ b/src/types-config.ts @@ -87,6 +87,7 @@ export interface ChartCardAllSeriesExternalConfig { duration?: string; func?: GroupByFunc; fill?: GroupByFill; + start_with_last?: boolean; }; transform?: string; color_threshold?: ChartCardColorThreshold[]; diff --git a/src/types.ts b/src/types.ts index 0fd5ab0..8dcbe22 100644 --- a/src/types.ts +++ b/src/types.ts @@ -23,6 +23,7 @@ export interface ChartCardSeriesConfig extends ChartCardSeriesExternalConfig { duration: string; func: GroupByFunc; fill: GroupByFill; + start_with_last?: boolean; }; show: { as_duration?: ChartCardPrettyTime;