Agree, with using “0/1” we have to hide labels on a secondary Y-axis.
I usually use background graphs for cases like:
– day/night (here greyish fill says “night”, so no labels are needed);
– availability (here reddish spikes or continuous fill say “unavailable” - again no labels needed);
– power on/off - similar as availability (but inverted),
and in all these cases I do not need labels.
May be in future the “state_map” option becomes per-entity which will solve that “spamming” problem.
Increase points_per_hour (will create more points → stepline graphs)
I assume this applies to the whole card not just the status value
Correct.
points-per-hour is a global option.
THIS IS THE FINAL THIRD EDIT 15/01/2024 AFTER FURTHER TESTING
12hr House Temperature Status
-
Deleted
state_map:
as discussed by @Ildar_Gabdullin -
Added the following to deal with my 0/100 AC Status sensor value, sits on the Y-axis.
aggregate_func: max
on AC Status entity discussed and tested below postupper_bound_secondary: 100
ensures humidity shows correctly below postlower_bound_secondary: 0
-
Show secondary labels, for humidity 0-100%
labels_secondary: true
-
Added 4-Sensor card-mod as discussed in below post
-Shows Temperatures at all three levels of my house including outdoors
-Shows Heating status of heat pump (lightsalmon block)
-Shows Cooling status of heat pump (mediumturquoise block)
type: custom:mini-graph-card
name: House Temperatures
entities:
- entity: sensor.netatmo_devonport_tas_indoor_temperature
name: Upstairs
color: brown
show_state: true
show_indicator: false
smoothing: true
show_fill: false
- entity: sensor.netatmo_devonport_tas_indoor_outdoor_temperature
name: Outside
color: dodgerblue
show_state: true
show_indicator: true
smoothing: true
- entity: sensor.air_conditioning_downstairs_temperature
name: Downstairs
color: orange
show_state: true
show_indicator: true
smoothing: true
show_fill: true
- entity: sensor.air_conditioning_temperature
name: Middle
color: gold
show_state: true
show_indicator: true
smoothing: true
show_fill: true
- entity: sensor.ac_cool_living
name: AC Cool
color: mediumturquoise
show_legend: false
show_line: false
show_points: false
smoothing: false
y_axis: secondary
aggregate_func: max
- entity: sensor.ac_heat_living
name: AC Heat
color: lightsalmon
show_legend: false
show_line: false
show_points: false
smoothing: false
y_axis: secondary
aggregate_func: max
show:
name: false
icon: false
legend: true
extrema: false
fill: fade
labels: hover
labels_secondary: false
state: true
font_size_header: 9
align_state: center
hour24: true
animate: true
group: true
smoothing: true
line_width: 3
hours_to_show: 12
points_per_hour: 15
upper_bound_secondary: 100
lower_bound_secondary: 0
height: 150
card_mod:
style: |
.states {
justify-content: normal !important;
font-size: 11px !important;
}
.state {
flex-grow: 1;
}
.states--secondary {
flex-flow: row !important;
flex-grow: 2;
margin-left: 0px !important;
}
.states--secondary .state.state--small {
font-size: unset;
margin-bottom: unset;
flex-grow: unset;
}
.states--secondary .state.state--small:nth-of-type(2) {
margin-left: auto !important;
}
.states--secondary .state.state--small:nth-of-type(3) {
margin-left: auto !important;
}
.ellipsis {
font-size: 12px ;
}
6hr Living Room Climate Status
-
Deleted
state_map:
as discussed by @Ildar_Gabdullin -
Added the following to deal with my 0/100 AC Status sensor value, sits on the Y-axis.
aggregate_func: max
on AC Status entity discussed and tested below postupper_bound_secondary: 100
ensures humidity shows correctly below postlower_bound_secondary: 0
-
Show secondary labels, for humidity 0-100%
labels_secondary: true
-
Added 3-Sensor card-mod as discussed in below post
-
General code layout tidy up
-Shows Temperature of Middle Floor
-Shows Temperature of Upstairs Mezzanine
-Shows Humidity of Middle Floor
-Shows Dew Point of Middle Floor
-Shows Heating status of heat pump (lightsalmon block)
-Shows Cooling status of heat pump (mediumturquoise block)
type: custom:mini-graph-card
name: Middle Floor Temperatures
entities:
- entity: sensor.air_conditioning_temperature
name: Middle
color: gold
show_state: true
show_indicator: false
show_fill: false
- entity: sensor.netatmo_devonport_tas_indoor_temperature
name: Upstairs
color: brown
show_state: false
show_indicator: false
smoothing: true
show_fill: false
- entity: sensor.dewpoint_living_room
name: Dewpoint
color: dodgerblue
show_state: true
show_indicator: true
show_fill: true
- entity: sensor.air_conditioning_humidity
name: Humidity
color: rgb(135,206,250,0.5)
show_state: true
show_indicator: true
show_graph: true
show_legend: true
show_fill: true
y_axis: secondary
- entity: sensor.ac_cool_living
name: AC Cool
color: mediumturquoise
show_legend: false
show_line: false
show_points: false
smoothing: false
y_axis: secondary
aggregate_func: max
- entity: sensor.ac_heat_living
name: AC Heat
color: lightsalmon
show_legend: false
show_line: false
show_points: false
smoothing: false
y_axis: secondary
aggregate_func: max
show:
name: false
icon: false
legend: true
extrema: false
fill: fade
labels: hover
labels_secondary: hover
state: true
font_size_header: 9
align_state: center
hour24: true
animate: true
group: true
smoothing: true
line_width: 3
hours_to_show: 6
points_per_hour: 15
upper_bound_secondary: 100
lower_bound_secondary: 0
height: 150
card_mod:
style: |
.states {
justify-content: normal !important;
font-size: 11px !important;
}
.state {
flex-grow: 1;
}
.states--secondary {
flex-flow: row !important;
flex-grow: 1.2;
margin-left: 0px !important;
}
.states--secondary .state.state--small {
font-size: unset;
margin-bottom: unset;
flex-grow: unset;
}
.states--secondary .state.state--small:nth-of-type(2) {
margin-left: auto !important;
}
.ellipsis {
font-size: 12px ;
}
Sensors required to be added to configuration.yaml
template:
- sensor:
# Air Conditioning sensors for Mini Graph Card
- name: "ac_heat_living"
unique_id: 4b0aa109-9f6b-430e-9f75-d3bc64645e70
icon: mdi:sun-thermometer-outline
state: >-
{% if state_attr('climate.air_conditioning','hvac_action') == 'heating' %}
100
{% else %}
0
{% endif %}
- name: "ac_cool_living"
unique_id: 463de126-9e29-46bd-b0cd-c2c4f97f8fa2
icon: mdi:snowflake-thermometer
state: >-
{% if state_attr('climate.air_conditioning','hvac_action') == 'cooling' %}
100
{% else %}
0
{% endif %}
12hr CO2 vs Door/Window Status
-
Deleted
state_map:
as discussed by @Ildar_Gabdullin -
Added the following to deal with my 0/100 Door Status sensor value, sits on the Y-axis.
aggregate_func: max
on Door Status entity discussed and tested below postupper_bound_secondary: 100
lower_bound_secondary: 0
-
Hide secondary labels, due to being a number and not a word.
labels_secondary: false
-
Added 3-Sensor card-mod as discussed in below post
-
General code layout tidy up
-Shows CO2 levels in relation to a door and window group sensor
type: custom:mini-graph-card
name: CO2 vs Door
entities:
- entity: sensor.netatmo_devonport_tas_indoor_co2
name: CO2
color: lightsalmon
show_state: true
show_indicator: false
- entity: sensor.middle_floor_sensor_status
name: Door Open
color: gray
show_legend: true
show_line: false
show_points: false
y_axis: secondary
smoothing: false
show_state: false
aggregate_func: max
show:
name: false
icon: false
legend: true
extrema: false
fill: fade
labels: hover
labels_secondary: hover
state: true
font_size_header: 9
align_state: left
hour24: true
animate: true
group: true
smoothing: true
line_width: 3
hours_to_show: 12
points_per_hour: 15
upper_bound_secondary: 100
lower_bound_secondary: 0
height: 150
card_mod:
style: |
.states {
justify-content: normal !important;
font-size: 11px !important;
}
.state {
flex-grow: 1;
}
.states--secondary {
flex-flow: row !important;
flex-grow: 1.2;
margin-left: 0px !important;
}
.states--secondary .state.state--small {
font-size: unset;
margin-bottom: unset;
flex-grow: unset;
}
.states--secondary .state.state--small:nth-of-type(2) {
margin-left: auto !important;
}
.states--secondary .state.state--small:nth-of-type(3) {
margin-left: auto !important;
}
.ellipsis {
font-size: 12px ;
}
Sensor required for door status
template:
- sensor:
- name: "middle_floor_sensor_status"
unique_id: bf540135-35df-439c-a6a7-3bd328a1fb08
icon: mdi:door
state: >-
{% if states('binary_sensor.middle_floor_sensor_group') == 'on' %}
100
{% else %}
0
{% endif %}
FINAL LOOK ON DASHBOARD
In my practice I use values “0/1” instead of “0/100” (if it is on/off → no need to have such a large range), also setting “upper_bound_secondary” not needed.
Also, use “aggregate_func: last” for “0/1”.
As for “smoothing: true” - it is up to you, myself do not use any smoothing, graphs are shown more natural (less interpolation).
LINK TO THE FINAL CARD WITH UPDATED CODE
I have set upper bound to 100 because I have put humidity on the Y-axis with my ON/OFF (0/100) sensor. Humidity is measured on a scale of 0-100%.
This allows me to have temperature on the X-axis which ranges from -5 to 40 degrees where I live. Not that I would see that full range in a day.
As you can see by comparing the two graphs between the posts.
- I can now see the upward or downward trend of temperature with more detail. (Not just a straight bumpy line.)
- At a glance where the humidity line sits on the graph gives me a rough approximation of percentage.
- Plus the humidity line sits lower in the graph allowing the 0/100 sensor to be seen above at all times.
- Humidity will never be 100%.
Before
After
EDIT: Example shows what happens when not setting aggregate_func: max
and ‘upper_bound_secondary: 100’
Different time spans in the above pictures. But was able to add a third temperature from the mezzanine of my living room without the graph looking messy.
Then for 0/1 values you need the 3rd y-axis)).
Seriously, this case is supposed to be solved in a different way.
If the secondary-Y-axis value has a wide range, then playing with upper_bound may not help.
Consider a different approach:
- Place 2 cards in a vert stack. Make them “overlap” by card-mod (find my examples here).
- 1st card - for temperature & humidity.
- 2nd card - for 0/1 background graphs.
Your example link has not shown up. Unfortunately no 3rd Y-axis.
This is working for me, all my on off sensors are now 0/100, simple change in yaml.
I will try your “aggregate_func: last” for 0/100
google for “display: grid” here
Using my method effectively frees up the Y-axis to be used by another reading, effectively giving a second Y-axis, pushing the binary_sensor to the back which only has two possible states.
It hurts my brain to make OFF/ON = 0/100 rather than the traditional 0/1 but at the end of the day we are applying a fudge to make the graph display what we want. Using 0/100 gives some range to the Y-axis to allow it to display something else quantitative.
Also dewpoint and temperature are very close together, this gives me the ability to see the relationship better.
There is a few use cases when comparing a percentage, with a numeric value and an ON/OFF state.
- %CPU Use, CPU Temp, Fan ON/OFF state
- %Humidity, Temperature, Humidifier ON/OFF state
- %Humidity, CO2ppm, Door OPEN/CLOSED state
This method also makes all values on the grid available via mouse over. Plus less code.
Thanks for that link for display: grid, I will use the card mod to move my values where I want them.
LINK TO THE FINAL CARD WITH UPDATED CODE
4 State Mini Graph Card: Using the below card-mod I got the above style (Comments in yaml.)
type: custom:mini-graph-card
name: House Temperatures
group: true /*takes space away from top of card*/
card_mod:
style: |
.states {
justify-content: normal !important;
font-size: 11px !important; /*Adjusts state font size*/
}
.state {
flex-grow: 1;
}
.states--secondary {
flex-flow: row !important;
flex-grow: 2; /*Adjusts spacing of 4 readings*/
margin-left: 0px !important;
}
.states--secondary .state.state--small {
font-size: unset;
margin-bottom: unset;
flex-grow: unset;
}
.states--secondary .state.state--small:nth-of-type(2) {
margin-left: auto !important; /*Adjusts the 3rd state*/
}
.states--secondary .state.state--small:nth-of-type(3) {
margin-left: auto !important; /*Adjusts the 4th state*/
}
.ellipsis {
font-size: 12px ; /*Adjusts legend font size*/
}
3 State Mini Graph Card: Using the below card-mod I got the above style (Comments in yaml.)
type: custom:mini-graph-card
name: Downstairs Temperatures
group: true /*takes space away from top of card*/
card_mod:
style: |
.states {
justify-content: normal !important;
font-size: 11px !important; /*Adjusts state font size*/
}
.state {
flex-grow: 1;
}
.states--secondary {
flex-flow: row !important;
flex-grow: 1.2; /*Adjusts spacing of 3 readings*/
margin-left: 0px !important;
}
.states--secondary .state.state--small {
font-size: unset;
margin-bottom: unset;
flex-grow: unset;
}
.states--secondary .state.state--small:nth-of-type(2) {
margin-left: auto !important; /*Adjusts the 3rd state*/
}
.states--secondary .state.state--small:nth-of-type(3) {
margin-left: auto !important;
}
.ellipsis {
font-size: 12px ; /*Adjusts legend font size*/
}
Below example of group: false
- Enables paddings and box-shadow.
Overall result on dashboard, no more cluttered look within cards
These comments inside a string will cause errors in css.
Use “/* … */” for comments.
Thanks mate was trying to work that out
FYI with a few days testing when using 0/100 aggregate_func: last
has no affect.
upper_bound_secondary: 100
and lower_bound_secondary: 0
is what is required.
Only for your case since you use secondary Y-axis for physical values & binary values.
Needed for 0/1 & may be not for 0/100.
Besides, even for ANY values I see no need to use other values than “last”.
I appreciate the conversation and the many posts you have put up here to help others. Thanks.
Can you explain what the aggregate function is actually acting on?
It has many possible values & algorithm is complex, better to check a code. Just consider this simplest example:
Assume there are 60 readings (records in DB) within an hour; points_per_hour=6. Means - you do not need all 60 values for a graph, it will use only 6 values. Now how to convert each 10 real values into 1? Average, min, max, last? If you need only 6 values - then probably “average” is your choice. But if you have points_per_hour=60? 120? 240? These greater values are needed sometimes to show a “steplined” graph. In these cases an additional calculation to get “average” = not needed. Besides, for 0/1 graphs sometimes it shows 0 where it must be 1 - if you set aggregate_func wrongly.
LINK TO THE FINAL CARD WITH UPDATED CODE
With what you have noted above and me re-reading the docs have explained the behavior I am seeing in the below graphs. With some commentary.
Aggregate functions
Recorded values are grouped in time buckets which are determined by
group_by
,points_per_hour
configuration. These buckets are converted later to single point/bar on the graph. Aggregate function defines the methods of that conversion.
Name Since Description avg
v0.8.0 Average median
v0.11.0 Median min
v0.8.0 Minimum - lowest value max
v0.8.0 Maximum - largest value first
v0.9.0 last
v0.9.0 sum
v0.9.2 delta
v0.9.4 Calculates difference between max and min value diff
v0.11.0 Calculates difference between first and last value
Name Type Default Since Description group boolean false v0.2.0 Disable paddings and box-shadow, useful when nesting the card. #Not to be confused for group_by points_per_hour number 0.5 v0.2.0 Specify amount of data points the graph should display for each hour, (basically the detail/accuracy/smoothing of the graph). aggregate_func string avg v0.8.0 Specify aggregate function used to calculate point/bar in the graph. group_by string interval v0.8.0 Specify type of grouping of data, dynamic interval, date or hour.
With 0/100- Setting no aggregate_function:
for the entity.
- The door open shows the short duration event at half the range.
- (Must be some sort of default average?)
With 0/100- Setting aggregate_function: avg
for the entity.
- The door open shows the short duration event at half the range.
- Showing the average of readings in the time bucket.
With 0/100 - Setting aggregate_function: last
for the entity.
- The door open does not show the short duration event unless it is last in the time bucket.
With 0/100 - Setting aggregate_function:min
for the entity.
- The door open only shows the minimum reading within each time bucket.
- Short duration events won’t register unless there is a high point per hour count or short group_by time.
With 0/100 - Setting aggregate_function: max
for the entity.
- The door open only shows the maximum reading within each time bucket.
- The door open uses the full range of graph, even for short duration events.