diff --git a/.devcontainer/ui-lovelace.yaml b/.devcontainer/ui-lovelace.yaml
index fe43aa9..c6e49e9 100644
--- a/.devcontainer/ui-lovelace.yaml
+++ b/.devcontainer/ui-lovelace.yaml
@@ -80,6 +80,15 @@ views:
name: AVG
curve: smooth
type: line
+ as_duration: millisecond
+ group_by:
+ duration: 10min
+ func: avg
+ - entity: sensor.random0_100
+ name: AVG
+ curve: smooth
+ type: line
+ as_duration: minute
group_by:
duration: 10min
func: avg
@@ -87,6 +96,7 @@ views:
curve: smooth
name: MIN
type: line
+ as_duration: hour
group_by:
duration: 10min
func: min
@@ -94,6 +104,7 @@ views:
curve: smooth
name: MAX
type: line
+ as_duration: day
group_by:
duration: 10min
func: max
@@ -101,6 +112,7 @@ views:
curve: smooth
name: LAST
type: line
+ as_duration: month
group_by:
duration: 10min
func: last
@@ -108,6 +120,7 @@ views:
curve: smooth
name: FIRST
type: line
+ as_duration: year
group_by:
duration: 10min
func: first
diff --git a/README.md b/README.md
index 9f898b3..2c9630e 100644
--- a/README.md
+++ b/README.md
@@ -123,6 +123,7 @@ The card stricly validates all the options available (but not for the `apex_conf
| `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) |
| `offset` | string | | NEXT_VERSION | This is different from the main `offset` parameter. This is at the series level. It is only usefull if you want to display data from for eg. yesterday on top of the data from today for the same sensor and compare the data. The time displayed in the tooltip will be wrong as will the x axis information. Valid values are any negative time string, eg: `-1h`, `-12min`, `-1d`, `-1h25`, `-10sec`, ... |
+| `to_duration` | string | | NEXT_VERSION | Will pretty print the states as durations. Doesn't affect the graph, only the tooltip/legend/header display. You provide the source unit of your sensor. Valid values are `millisecond`, `second`, `minute`, `hour`, `day`, `week`, `month`, `year`.
Eg: if the state is `345` and `to_duration` is set to `minute` then it would display `5h45m` |
### `show` Options
diff --git a/package-lock.json b/package-lock.json
index 6b49315..9b78496 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -5,7 +5,7 @@
"requires": true,
"packages": {
"": {
- "version": "1.1.0-dev.1",
+ "version": "1.3.0-dev.2",
"license": "MIT",
"dependencies": {
"@ctrl/tinycolor": "^3.3.3",
@@ -17,6 +17,7 @@
"localforage": "^1.9.0",
"lz-string": "^1.4.4",
"moment": "^2.29.1",
+ "moment-duration-format": "^2.3.2",
"moment-range": "^4.0.2",
"parse-duration": "^0.4.4",
"spark-md5": "^3.0.1",
@@ -36,6 +37,7 @@
"@semantic-release/npm": "^7.0.10",
"@semantic-release/release-notes-generator": "^9.0.1",
"@types/lz-string": "^1.3.34",
+ "@types/moment-duration-format": "^2.2.2",
"@types/spark-md5": "^3.0.2",
"@typescript-eslint/eslint-plugin": "^4.14.0",
"@typescript-eslint/parser": "^4.14.0",
@@ -1021,6 +1023,15 @@
"integrity": "sha512-fZQQafSREFyuZcdWFAExYjBiCL7AUCdgsk80iO0q4yihYYdcIiH28CcuPTGFgLOCC8RlW49GSQxdHwZP+I7CNg==",
"dev": true
},
+ "node_modules/@types/moment-duration-format": {
+ "version": "2.2.2",
+ "resolved": "https://registry.npmjs.org/@types/moment-duration-format/-/moment-duration-format-2.2.2.tgz",
+ "integrity": "sha512-CuYswsMI3y5uR5sD9i/VUqIbZrsYN2eaCs7nH3qpDl2CZlNI48mjMf4ve2RpQ/65irprtnQVetfnea9my+jqcg==",
+ "dev": true,
+ "dependencies": {
+ "moment": ">=2.14.0"
+ }
+ },
"node_modules/@types/node": {
"version": "14.14.22",
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.22.tgz",
@@ -3642,6 +3653,11 @@
"node": "*"
}
},
+ "node_modules/moment-duration-format": {
+ "version": "2.3.2",
+ "resolved": "https://registry.npmjs.org/moment-duration-format/-/moment-duration-format-2.3.2.tgz",
+ "integrity": "sha512-cBMXjSW+fjOb4tyaVHuaVE/A5TqkukDWiOfxxAjY+PEqmmBQlLwn+8OzwPiG3brouXKY5Un4pBjAeB6UToXHaQ=="
+ },
"node_modules/moment-range": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/moment-range/-/moment-range-4.0.2.tgz",
@@ -12122,6 +12138,15 @@
"integrity": "sha512-fZQQafSREFyuZcdWFAExYjBiCL7AUCdgsk80iO0q4yihYYdcIiH28CcuPTGFgLOCC8RlW49GSQxdHwZP+I7CNg==",
"dev": true
},
+ "@types/moment-duration-format": {
+ "version": "2.2.2",
+ "resolved": "https://registry.npmjs.org/@types/moment-duration-format/-/moment-duration-format-2.2.2.tgz",
+ "integrity": "sha512-CuYswsMI3y5uR5sD9i/VUqIbZrsYN2eaCs7nH3qpDl2CZlNI48mjMf4ve2RpQ/65irprtnQVetfnea9my+jqcg==",
+ "dev": true,
+ "requires": {
+ "moment": ">=2.14.0"
+ }
+ },
"@types/node": {
"version": "14.14.22",
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.22.tgz",
@@ -14146,6 +14171,11 @@
"resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz",
"integrity": "sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ=="
},
+ "moment-duration-format": {
+ "version": "2.3.2",
+ "resolved": "https://registry.npmjs.org/moment-duration-format/-/moment-duration-format-2.3.2.tgz",
+ "integrity": "sha512-cBMXjSW+fjOb4tyaVHuaVE/A5TqkukDWiOfxxAjY+PEqmmBQlLwn+8OzwPiG3brouXKY5Un4pBjAeB6UToXHaQ=="
+ },
"moment-range": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/moment-range/-/moment-range-4.0.2.tgz",
diff --git a/package.json b/package.json
index 6e8625b..8138dec 100644
--- a/package.json
+++ b/package.json
@@ -38,6 +38,7 @@
"localforage": "^1.9.0",
"lz-string": "^1.4.4",
"moment": "^2.29.1",
+ "moment-duration-format": "^2.3.2",
"moment-range": "^4.0.2",
"parse-duration": "^0.4.4",
"spark-md5": "^3.0.1",
@@ -57,6 +58,7 @@
"@semantic-release/npm": "^7.0.10",
"@semantic-release/release-notes-generator": "^9.0.1",
"@types/lz-string": "^1.3.34",
+ "@types/moment-duration-format": "^2.2.2",
"@types/spark-md5": "^3.0.2",
"@typescript-eslint/eslint-plugin": "^4.14.0",
"@typescript-eslint/parser": "^4.14.0",
diff --git a/src/apex-layouts.ts b/src/apex-layouts.ts
index c4b152f..714897c 100644
--- a/src/apex-layouts.ts
+++ b/src/apex-layouts.ts
@@ -1,8 +1,8 @@
import { HomeAssistant } from 'custom-card-helpers';
import parse from 'parse-duration';
-import { DEFAULT_FLOAT_PRECISION, HOUR_24, moment } from './const';
+import { DEFAULT_FLOAT_PRECISION, HOUR_24, moment, NO_VALUE } from './const';
import { ChartCardConfig } from './types';
-import { computeName, computeUom, mergeDeep } from './utils';
+import { computeName, computeUom, mergeDeep, prettyPrintTime } from './utils';
export function getLayoutConfig(config: ChartCardConfig, hass: HomeAssistant | undefined = undefined): unknown {
const def = {
@@ -59,7 +59,12 @@ export function getLayoutConfig(config: ChartCardConfig, hass: HomeAssistant | u
},
y: {
formatter: function (value, opts, conf = config, hass2 = hass) {
- if (value !== null && typeof value === 'number' && !Number.isInteger(value)) {
+ if (
+ value !== null &&
+ typeof value === 'number' &&
+ !Number.isInteger(value) &&
+ !conf.series[opts.seriesIndex]?.as_duration
+ ) {
value = (value as number).toFixed(
conf.series[opts.seriesIndex].float_precision === undefined
? DEFAULT_FLOAT_PRECISION
@@ -72,7 +77,10 @@ export function getLayoutConfig(config: ChartCardConfig, hass: HomeAssistant | u
undefined,
hass2?.states[conf.series[opts.seriesIndex].entity],
);
- return [`${value} ${uom}`];
+ return conf.series[opts.seriesIndex]?.as_duration
+ ? // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
+ [`${prettyPrintTime(value, conf.series[opts.seriesIndex].as_duration!)}`]
+ : [`${value} ${uom}`];
},
},
fixed: {
@@ -95,7 +103,12 @@ export function getLayoutConfig(config: ChartCardConfig, hass: HomeAssistant | u
const name =
computeName(opts.seriesIndex, conf, undefined, hass2?.states[conf.series[opts.seriesIndex].entity]) + ':';
let value = opts.w.globals.series[opts.seriesIndex].slice(-1)[0];
- if (value !== null && typeof value === 'number' && !Number.isInteger(value)) {
+ if (
+ value !== null &&
+ typeof value === 'number' &&
+ !Number.isInteger(value) &&
+ !conf.series[opts.seriesIndex]?.as_duration
+ ) {
value = (value as number).toFixed(
conf.series[opts.seriesIndex].float_precision === undefined
? DEFAULT_FLOAT_PRECISION
@@ -103,7 +116,18 @@ export function getLayoutConfig(config: ChartCardConfig, hass: HomeAssistant | u
);
}
const uom = computeUom(opts.seriesIndex, conf, undefined, hass2?.states[conf.series[opts.seriesIndex].entity]);
- return [name, value === undefined ? `N/A ${uom}` : `${value} ${uom}`];
+ let valueString = '';
+ if (value === undefined || value === null) {
+ valueString = `${NO_VALUE} ${uom}`;
+ } else {
+ if (conf.series[opts.seriesIndex]?.as_duration) {
+ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
+ valueString = `${prettyPrintTime(value, conf.series[opts.seriesIndex].as_duration!)}`;
+ } else {
+ valueString = `${value} ${uom}`;
+ }
+ }
+ return [name, valueString];
},
},
stroke: {
diff --git a/src/apexcharts-card.ts b/src/apexcharts-card.ts
index 0eb3b1b..124ab44 100644
--- a/src/apexcharts-card.ts
+++ b/src/apexcharts-card.ts
@@ -12,6 +12,7 @@ import {
log,
mergeDeep,
offsetData,
+ prettyPrintTime,
validateInterval,
validateOffset,
} from './utils';
@@ -23,7 +24,7 @@ import GraphEntry from './graphEntry';
import { createCheckers } from 'ts-interface-checker';
import { ChartCardExternalConfig } from './types-config';
import exportedTypeSuite from './types-config-ti';
-import { DEFAULT_FLOAT_PRECISION, moment } from './const';
+import { DEFAULT_FLOAT_PRECISION, moment, NO_VALUE } from './const';
import {
DEFAULT_COLORS,
DEFAULT_DURATION,
@@ -314,7 +315,7 @@ class ChartsCard extends LitElement {
private _renderStates(): TemplateResult {
return html`