I was very happy when long term statistics were introduced, however I am a little underwhelmed by the possibilities to explore, browse und visualize this data. Neither the statistics_graph card nor the energy-dashboard have a lot of flexibility.
Inspired by several posts here, especially Dynamically select the number of hours or sensors to show on a history graph or mini graph card i tried to to create the “statistics explorer”.
You select an entity, a timespan and a date, and the apex-chart will show data for the selected timespan that contains the selected date, much like the energy-dashboard (Month: 1 - 28/29/30/31; week: monday - sunday; year: 01.01 - 31.12).
You can use the arrows to jump forward or backwards or to return to today.
ToDo:
- Apex seems to have problems to aggregate data on a month-level, so timespan-year is aggregated on a daily level for now.
Clean up the form. Right now I can’t get it to work without vertical-stack,which leaves a nasty border.create some nicer charts
Prerequisites:
ApexCharts
Card-Mod
Config Template Card
INPUTS:
input_select:
statistics_chart_entity:
initial: sensor.bathroom_meter_boiler_energy
options:
- sensor.balkon_light_main_energy
- sensor.bathroom_skylight_energy
- sensor.bathroom_meter_boiler_energy
- sensor.hall_outlet_network_energy
- sensor.hall_skylight_energy
- sensor.home_calc_base_energy
- sensor.home_calc_correction_energy
- sensor.home_calc_light_energy
- sensor.home_calc_misc_energy
- sensor.home_calc_unknown_energy
- sensor.home_meter_grid_energy
- sensor.home_outlet_float_energy
- sensor.home_outlet_vacuum_energy
- sensor.kitchen_meter_stove_energy
- sensor.kitchen_outlet_boiler_energy
- sensor.kitchen_outlet_dishwasher_energy
- sensor.kitchen_outlet_fridge_energy
- sensor.livingroom_light_counter_energy
- sensor.livingroom_outlet_media_energy
- sensor.livingroom_outlet_fan_energy
statistics_chart_range:
initial: month
options:
- day
- week
- month
- year
input_datetime:
statistics_chart_date:
has_date: true
has_time: false
Templates:
template:
- sensor:
- name: statistics_chart_grouping
unique_id: statistics_chart_grouping
state: |
{% set grouping={
'day':'h',
'week':'d',
'month':'d',
'year':'d'}
%}
{% set range = states('input_select.statistics_chart_range') %}
{{ grouping[range] }}
- name: statistics_chart_span
unique_id: statistics_chart_span
state: |
{% macro last_date_in_range(date_str,range) -%}
{% set date=as_timestamp(date_str)|timestamp_local()|as_datetime() -%}
{% if range=='day' -%}
{% set end_date=date -%}
{% elif range=='week' -%}
{% set end_date=date + timedelta(days=(6-date.weekday())) -%}
{% elif range=='month' -%}
{% set month=(date.month % 12) + 1 -%}
{% set year=date.year+iif(date.month==12,1,0) -%}
{% set end_date=strptime(year|string + '-' + month|string + '-01', '%Y-%m-%d')-timedelta(days=1) -%}
{% elif range=='year' -%}
{% set end_date=strptime(date.year|string + '-12-31', '%Y-%m-%d') -%}
{% endif -%}
{{ as_timestamp(end_date)|timestamp_local() -}}
{% endmacro -%}
{% set range = states('input_select.statistics_chart_range') %}
{% set date_str = states('input_datetime.statistics_chart_date') %}
{% set spans={
'day':'24',
'week':'7',
'month':(last_date_in_range (strptime(date_str, '%Y-%m-%d'),range)|as_datetime()).day,
'year':last_date_in_range (strptime(date_str, '%Y-%m-%d'),range)|as_timestamp(0)|timestamp_custom('%j',0)|int +1}
%}
{{ spans[range] }}
- name: statistics_chart_period
unique_id: statistics_chart_period
state: |
{% set periods={
'day':'hour',
'week':'day',
'month':'day',
'year':'day'}
%}
{% set range = states('input_select.statistics_chart_range') %}
{{ periods[range] }}
- sensor:
- name: statistics_chart_offset
unique_id: statistics_chart_offset
state: |
{% macro last_date_in_range(date_str,range) -%}
{% set date=as_timestamp(date_str)|timestamp_local()|as_datetime() -%}
{% if range=='day' -%}
{% set end_date = date + timedelta(days=1) -%}
{% elif range=='week' -%}
{% set end_date=date + timedelta(days=(6-date.weekday())) -%}
{% elif range=='month' -%}
{% set month=(date.month % 12) + 1 -%}
{% set year=date.year+iif(date.month==12,1,0) -%}
{% set end_date=strptime(year|string + '-' + month|string + '-01', '%Y-%m-%d')-timedelta(days=1) -%}
{% elif range=='year' -%}
{% set end_date=strptime(date.year|string + '-12-31', '%Y-%m-%d') -%}
{% endif -%}
{{ as_timestamp(end_date) -}}
{% endmacro -%}
{% set range = states('input_select.statistics_chart_range') %}
{% set date_str = states('input_datetime.statistics_chart_date') %}
{% set offset = last_date_in_range(strptime(date_str, '%Y-%m-%d'),range)|int - now()|as_timestamp(0) -%}
{% if states('sensor.statistics_chart_grouping') == 'h' %}
{% set offset=(offset/60/60)|round(method='ceil') %}
{% else %}
{% set offset=(offset/60/60/24)|round(method='ceil') + 1 %}
{% endif %}
{{ iif(offset >= 0,'+','') }}{{ offset }}{{ states('sensor.statistics_chart_grouping') }}
Scripts:
script:
statistics_chart_date_browse:
icon: mdi:arrow-right-bold-outline
sequence:
- service: input_datetime.set_datetime
entity_id: input_datetime.statistics_chart_date
data:
datetime: |
{% set range = states('input_select.statistics_chart_range') %}
{% set date = states('input_datetime.statistics_chart_date')|as_datetime %}
{% set direction = direction %}
{% if range == 'day' %}
{% if direction > 0 %}
{% set target = date + timedelta(days=1) %}
{% else %}
{% set target = date - timedelta(days=1) %}
{% endif %}
{% elif range == 'week' -%}
{% if direction > 0 %}
{% set target = date + timedelta(days=7) %}
{% else %}
{% set target = date - timedelta(days=7) %}
{% endif %}
{% elif range == 'month' -%}
{% if direction > 0 %}
{% set month=((date.month) % 12) + 1 %}
{% set year=date.year+iif(date.month==12,1,0) %}
{% set target=strptime(year|string + '-' + month|string + '-01', '%Y-%m-%d') -%}
{% else %}
{% set month=((date.month - 2) % 12) + 1 %}
{% set year=date.year+iif(date.month==1,-1,0) %}
{% set target=strptime(year|string + '-' + month|string + '-01', '%Y-%m-%d') -%}
{% endif %}
{% elif range=='year' -%}
{% if direction > 0 %}
{% set target=strptime((date.year|int + 1)|string + '-01-01', '%Y-%m-%d') -%}
{% else %}
{% set target=strptime((date.year|int - 1)|string + '-01-01', '%Y-%m-%d') -%}
{% endif %}
{% endif -%}
{% if direction == 0 %}
{% set target = states('sensor.date') %}
{% endif -%}
{{ target }}
Dashboard-card inputs:
type: custom:mod-card
card_mod:
style:
hui-entities-card:
$: |
:host {
flex: auto !important;
width: 70%;
padding: 0px;
margin: 0px !important;
}
div#states {
margin: 0px;
padding: 7px
}
$hui-input-select-entity-row:
$hui-generic-entity-row:
$: |
state-badge {
display: none;
}
ha-select$: |
span#label {
}
card:
type: entities
entities:
- entity: input_select.statistics_chart_entity
name: entity
type: custom:mod-card
card_mod:
style:
hui-horizontal-stack-card:
.: |
ha-card {
height: 100%;
padding: 0px;
margin: 0px;
}
$: |
hui-entities-card:nth-of-type(1){
width: 60%;
}
hui-entities-card:nth-of-type(2){
width: 40%;
}
$hui-entities-card:
$: |
:host {
flex: auto !important;
width: 70%;
padding: 0px;
margin: 0px !important;
}
div#states {
margin: 0px;
padding: 7px
}
$hui-input-select-entity-row:
$hui-generic-entity-row:
$: |
state-badge {
display: none;
}
ha-select$: |
span#label {
}
$hui-input-datetime-entity-row:
$hui-generic-entity-row:
$: |
state-badge {
display: none;
}
div {
display: none;
}
ha-date-input$: |
ha-textfield, :host {
width: 100%;
border: 0px;
}
$hui-glance-card:
$: |
:host {
flex: auto !important;
--glance-column-width: 100px;
padding: 0px;
margin: 0px !important;
}
div.entities.no-header {
margin: 0px;
padding: 7px
}
div.entity {
background: var(--mdc-select-fill-color, whitesmoke);
margin: 0px;
padding: 0px;
}
$div.entity:
$: |
state-badge {
color: <var(--primary-text-color);
}
card:
type: horizontal-stack
cards:
- type: entities
entities:
- entity: input_datetime.statistics_chart_date
name: date
- type: entities
entities:
- entity: input_select.statistics_chart_range
name: period
- type: glance
show_name: false
show_state: false
entities:
- entity: script.statistics_chart_date_browse
icon: mdi:chevron-left
tap_action:
action: call-service
service: script.statistics_chart_date_browse
data:
direction: -1
- entity: script.statistics_chart_date_browse
icon: mdi:chevron-down
tap_action:
action: call-service
service: script.statistics_chart_date_browse
data:
direction: 0
- entity: script.statistics_chart_date_browse
icon: mdi:chevron-right
tap_action:
action: call-service
service: script.statistics_chart_date_browse
data:
direction: 1
Dashboard-card: graph:
type: custom:config-template-card
variables:
entity: states['input_select.statistics_chart_entity'].state
offset: states['sensor.statistics_chart_offset'].state
span: states['sensor.statistics_chart_span'].state
period: states['sensor.statistics_chart_period'].state
grouping: states['sensor.statistics_chart_grouping'].state
entities:
- input_select.statistics_chart_entity
- sensor.statistics_chart_offset
- sensor.statistics_chart_span
- sensor.statistics_chart_period
- sensor.statistics_chart_grouping
card:
type: custom:apexcharts-card
header:
show: true
title: Statistics
show_states: false
colorize_states: true
apex_config:
chart:
zoom:
enabled: true
toolbar:
show: false
tools:
zoom: true
zoomin: true
zoomout: true
pan: true
reset: true
graph_span: ${span + period}
span:
offset: ${offset}
yaxis:
- min: 0
decimals: 2
stacked: false
all_series_config:
type: column
statistics:
type: sum
period: ${period}
align: end
group_by:
func: diff
duration: ${1 + grouping}
fill: last
start_with_last: true
float_precision: 2
time_delta: +0d
show:
legend_value: false
series:
- entity: ${entity}
The templates and cards might be overcomplicated, but I couldn’t manage another way. I’m open for improvements.
The recent improvements to the database make this very fast, even on my RPi 4.
[EDIT: 2022-06-19] updated the definitions of the cards for the inputs, especially the styling.
[EDIT: 2022-06-22] Added the config Template Card to the prerequisites. I somehow forgot