diff --git a/.devcontainer/ui-lovelace.yaml b/.devcontainer/ui-lovelace.yaml
index 5e7bea7..65520d9 100644
--- a/.devcontainer/ui-lovelace.yaml
+++ b/.devcontainer/ui-lovelace.yaml
@@ -56,7 +56,10 @@ views:
- type: custom:apexcharts-card
hours_to_show: 1
header:
- show: false
+ title: Test Aggregate
+ show: true
+ show_states: true
+ colorize_states: true
series:
- entity: sensor.random0_100
name: AVG
@@ -96,6 +99,8 @@ views:
- type: custom:apexcharts-card
hours_to_show: 4
+ header:
+ title: Test
series:
- entity: sensor.humidity
type: area
@@ -109,6 +114,9 @@ views:
group_by:
func: avg
duration: 1h
+ - type: sensor
+ entity: sensor.humidity
+ graph: line
- type: custom:apexcharts-card
hours_to_show: 6
diff --git a/README.md b/README.md
index 52ba8a6..525584d 100644
--- a/README.md
+++ b/README.md
@@ -126,8 +126,10 @@ The card stricly validates all the options available (but not for the `apex_conf
| Name | Type | Default | Since | Description |
| ---- | :--: | :-----: | :---: | ----------- |
-| `show` | boolean | `true` | v1.0.0 | Show or hide the header |
+| `show` | boolean | `false` | v1.0.0 | Show or hide the header |
| `floating` | boolean | `false` | v1.0.0 | Makes the header float above the graph. Positionning will be supported later |
+| `show_states` | boolean | `false` | NEXT_VERSION | Show or hide the states in the header |
+| `colorize_states` | boolean | `false` | NEXT_VERSION | Colorize the states based on the color of the serie |
### `group_by` Options
diff --git a/src/apex-layouts.ts b/src/apex-layouts.ts
index 82259b8..a3f3bc8 100644
--- a/src/apex-layouts.ts
+++ b/src/apex-layouts.ts
@@ -27,7 +27,7 @@ export function getLayoutConfig(config: ChartCardConfig, hass: HomeAssistant | u
},
series: config?.series.map((serie, index) => {
return {
- name: computeName(index, config, hass?.states),
+ name: computeName(index, config, undefined, hass?.states[serie.entity]),
type: serie.type,
data: [],
};
@@ -53,6 +53,21 @@ export function getLayoutConfig(config: ChartCardConfig, hass: HomeAssistant | u
return moment(new Date(val)).format('MMM Do, HH:mm:ss');
},
},
+ y: {
+ formatter: function (_, opts, conf = config, hass2 = hass) {
+ let value = opts.w.globals.series[opts.seriesIndex].slice(-1)[0];
+ if (value !== null && typeof value === 'number' && !Number.isInteger(value)) {
+ value = (value as number).toFixed(1);
+ }
+ const uom = computeUom(
+ opts.seriesIndex,
+ conf,
+ undefined,
+ hass2?.states[conf.series[opts.seriesIndex].entity],
+ );
+ return [`${value} ${uom}`];
+ },
+ },
fixed: {
enabled: true,
postion: 'topRight',
@@ -60,21 +75,14 @@ export function getLayoutConfig(config: ChartCardConfig, hass: HomeAssistant | u
},
legend: {
formatter: function (_, opts, conf = config, hass2 = hass) {
- return [
- `${computeName(opts.seriesIndex, conf, undefined, hass2?.states[conf.series[opts.seriesIndex].entity])}:`,
- `${
- opts.w.globals.series[opts.seriesIndex].slice(-1).length !== 0
- ? opts.w.globals.series[opts.seriesIndex].slice(-1)[0].toFixed(1)
- : opts.w.globals.series[opts.seriesIndex].slice(-1)
- }
- ${computeUom(
- opts.seriesIndex,
- conf,
- undefined,
- hass2?.states[conf.series[opts.seriesIndex].entity],
- )}
- `,
- ];
+ 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)) {
+ value = (value as number).toFixed(1);
+ }
+ const uom = computeUom(opts.seriesIndex, conf, undefined, hass2?.states[conf.series[opts.seriesIndex].entity]);
+ return [name, `${value} ${uom}`];
},
},
stroke: {
diff --git a/src/apexcharts-card.ts b/src/apexcharts-card.ts
index 4f7d71a..14ebf88 100644
--- a/src/apexcharts-card.ts
+++ b/src/apexcharts-card.ts
@@ -59,13 +59,13 @@ class ChartsCard extends LitElement {
private _loaded = false;
- @property() private _updating = false;
+ @property({ type: Boolean }) private _updating = false;
private _graphs: (GraphEntry | undefined)[] | undefined;
- @property() private _config?: ChartCardConfig;
+ @property({ attribute: false }) private _config?: ChartCardConfig;
- @property() private _entities: HassEntity[] = [];
+ private _entities: HassEntity[] = [];
private _interval?: number | null;
@@ -73,6 +73,8 @@ class ChartsCard extends LitElement {
private _colors?: string[];
+ @property({ attribute: false }) private _lastState: (number | string | null)[] = [];
+
public connectedCallback() {
super.connectedCallback();
if (this._config && this._hass && !this._loaded) {
@@ -153,7 +155,6 @@ class ChartsCard extends LitElement {
cache: true,
useCompress: false,
show: { loading: true },
- header: { show: true },
},
JSON.parse(JSON.stringify(config)),
);
@@ -252,11 +253,32 @@ class ChartsCard extends LitElement {
};
return html`
+ `;
+ }
+
+ private _renderStates(): TemplateResult {
+ return html`
+
`;
}
@@ -288,6 +310,16 @@ class ChartsCard extends LitElement {
series: this._graphs.map((graph) => {
if (!graph || graph.history.length === 0) return { data: [] };
const index = graph.index;
+ if (graph.history.length > 0) {
+ this._lastState[index] = graph.history[graph.history.length - 1][1];
+ if (
+ this._lastState[index] !== null &&
+ typeof this._lastState[index] === 'number' &&
+ !Number.isInteger(this._lastState[index])
+ ) {
+ this._lastState[index] = (this._lastState[index] as number).toFixed(1);
+ }
+ }
return {
data:
this._config?.series[index].extend_to_end && this._config?.series[index].type !== 'column'
@@ -302,6 +334,7 @@ class ChartsCard extends LitElement {
},
colors: computeColors(this._colors),
};
+ this._lastState = [...this._lastState];
this._apexChart?.updateOptions(graphData, false, false);
} catch (err) {
log(err);
diff --git a/src/styles.ts b/src/styles.ts
index e4ea82e..2195e58 100644
--- a/src/styles.ts
+++ b/src/styles.ts
@@ -12,7 +12,7 @@ export const styles: CSSResult = css`
.wrapper {
display: grid;
- grid-template-areas: "header" "graph";
+ grid-template-areas: 'header' 'graph';
grid-template-columns: 1fr;
grid-template-rows: min-content 1fr;
}
@@ -28,28 +28,58 @@ export const styles: CSSResult = css`
}
#header {
- padding-top: 10px;
- padding-left: 10px;
+ padding: 8px 16px 0px;
grid-area: header;
}
#header.floating {
position: absolute;
top: 0px;
left: 0px;
+ right: 0px;
}
- #header__title > #state {
+ #header__title {
+ color: var(--secondary-text-color);
+ font-size: 16px;
+ font-weight: 500;
+ overflow: hidden;
+ white-space: nowrap;
+ text-overflow: ellipsis;
+ padding-bottom: 5px;
+ }
+
+ #header__states {
+ display: flex;
+ justify-content: space-between;
+ flex-flow: row wrap;
+ margin: -5px;
+ }
+
+ #header__states > * {
+ margin: 5px;
+ }
+
+ #states__state {
+ flex: 0 0 10%;
+ }
+
+ #state__value > #state {
font-size: 1.8em;
font-weight: 500;
}
- #header__title > #uom {
+
+ #state__value > #uom {
font-size: 1em;
font-weight: 400;
opacity: 0.8;
}
- #header__subtitle {
+
+ #state__name {
font-size: 0.8em;
font-weight: 300;
+ overflow: hidden;
+ white-space: nowrap;
+ text-overflow: ellipsis;
}
/* Apex Charts Default CSS */
@@ -318,7 +348,7 @@ export const styles: CSSResult = css`
opacity: 0;
padding: 4px 10px;
pointer-events: none;
- color: var(--primary-text-color)
+ color: var(--primary-text-color);
font-size: 13px;
text-align: center;
border-radius: 2px;
diff --git a/src/types-config-ti.ts b/src/types-config-ti.ts
index fa5c48f..c9e57a9 100644
--- a/src/types-config-ti.ts
+++ b/src/types-config-ti.ts
@@ -41,6 +41,9 @@ export const GroupByFunc = t.union(t.lit('raw'), t.lit('avg'), t.lit('min'), t.l
export const ChartCardHeaderExternalConfig = t.iface([], {
"show": t.opt("boolean"),
"floating": t.opt("boolean"),
+ "title": t.opt("string"),
+ "show_states": t.opt("boolean"),
+ "colorize_states": t.opt("boolean"),
});
const exportedTypeSuite: t.ITypeSuite = {
diff --git a/src/types-config.ts b/src/types-config.ts
index 0310df9..474d133 100644
--- a/src/types-config.ts
+++ b/src/types-config.ts
@@ -36,4 +36,7 @@ export type GroupByFunc = 'raw' | 'avg' | 'min' | 'max' | 'last' | 'first' | 'su
export interface ChartCardHeaderExternalConfig {
show?: boolean;
floating?: boolean;
+ title?: string;
+ show_states?: boolean;
+ colorize_states?: boolean;
}
diff --git a/src/utils.ts b/src/utils.ts
index dd5543f..ff3b835 100644
--- a/src/utils.ts
+++ b/src/utils.ts
@@ -67,8 +67,8 @@ export function computeName(
} else if (entities) {
return (
config.series[index].name ||
- entities[config.series[index].entity]?.attributes?.friendly_name ||
- entities[entities[config.series[index].entity]]?.entity_id ||
+ entities[index]?.attributes?.friendly_name ||
+ entities[entities[index]]?.entity_id ||
''
);
}
@@ -85,7 +85,7 @@ export function computeUom(
if (entity) {
return config.series[index].unit || entity.attributes?.unit_of_measurement || '';
} else if (entities) {
- return config.series[index].unit || entities[config.series[index].entity]?.attributes?.unit_of_measurement || '';
+ return config.series[index].unit || entities[index]?.attributes?.unit_of_measurement || '';
}
return '';
}
@@ -93,14 +93,18 @@ export function computeUom(
export function computeColors(colors: string[] | undefined): string[] {
if (!colors) return [];
return colors.map((color) => {
- if (color[0] === '#') {
- return color;
- } else if (color.substring(0, 3) === 'var') {
- return new TinyColor(
- window.getComputedStyle(document.documentElement).getPropertyValue(color.substring(4).slice(0, -1)).trim(),
- ).toHexString();
- } else {
- return new TinyColor(color).toHexString();
- }
+ return computeColor(color);
});
}
+
+export function computeColor(color: string): string {
+ if (color[0] === '#') {
+ return color;
+ } else if (color.substring(0, 3) === 'var') {
+ return new TinyColor(
+ window.getComputedStyle(document.documentElement).getPropertyValue(color.substring(4).slice(0, -1)).trim(),
+ ).toHexString();
+ } else {
+ return new TinyColor(color).toHexString();
+ }
+}