ApexCharts card - A highly customizable graph card

Still not agreeing that HA is not compensating, with me all (!) is fine with your server and TZ. And btw…to offset a graph you can probably use offset instead of a lengthy data_generator
EDIT: correction on offset… this would mean ALWAYS offset and you only want a partial offset…anyhow, :slight_smile:

Yes as I stated in the updated post all is fine with HA, the server and TZ.
It’s apexcharts that with those settings I posted ends up requiring the offset: if I plot the same data on a simple line graph like you tried, all is fine. With columns, if I want the ticks between columns instead of in the middle of the columns, I require an offset.

:rocket: No question here – I’m just super excited to share something cool I built!

I combined a few amazing Home Assistant tools to get a full breakdown of my energy usage and costs – and I love how it turned out! :raised_hands:

:sparkles: First, I used the fantastic PowerCalc integration to calculate Hourly, Daily, Monthly, and Yearly Energy Consumption for all my devices.

:moneybag: Then I hooked it up with the Dynamic Energy Cost Integration to multiply the usage with the actual energy prices. Real-time energy cost? Yes please!

:brain: To make things dynamic and clean, I added lovelace-auto-entities to automatically filter and sort everything…

:bar_chart: …and finally visualized it all beautifully with the ApexCharts Card. The result? A slick, informative dashboard that updates itself and gives me full insight into where my energy goes.

Just had to share – Home Assistant + these integrations = :boom:

type: custom:auto-entities
filter:
  include:
    - entity_id: sensor.*daily_energy_cost
  exclude:
    - state 1: "< 0.25"
    - entity_id: sensor.gasverbrauch*energy_cost
    - entity_id: sensor.esp_lesekopf_stromzahler_aktuelle_wirkleistung_*_energy_cost
show_empty: false
unique: true
sort:
  method: state
  reverse: true
card:
  type: custom:apexcharts-card
  chart_type: donut
  header:
    title: Stromkosten pro Tag
    show: true
    show_states: false
    colorize_states: true
  apex_config:
    legend:
      show: false
    dataLabels:
      formatter: |
        EVAL:function(value) {
          return value.toFixed(0) + " %";
        }
    plotOptions:
      pie:
        donut:
          labels:
            show: true
            total:
              show: true
              label: Total
              formatter: |
                EVAL:function(w) {
                  return w.globals.seriesTotals.reduce((a, b) => {return (a + b)} , 0).toFixed(2) + " €"
                  }

3 Likes

So I have built a graph for my PV system which shows electricity production and consumption:

I have also implemented a ‘timeline’ below the graph to help me identify which load corresponds to consumption (ev charger, well pump, water heater, etc) based by color. Here is an example for an ev charger for anyone interested:

      - entity: sensor.wallbox_pulsar_plus_socket_status_description
        transform: "return x === 'Charging' ? -750 : null;"
        type: line
        opacity: 0.8
        color: rgb(80,110,172)
        stroke_width: 5
        curve: stepline
        extend_to: false
        group_by:
          func: last
          duration: 1m

The code takes a sensor value and replaces it with a negative y value based on some condition to plot below the axis as a thick line.

I like it as it is but wonder if something like this is possible:

I was thinking of displaying the current (last) reading of a sensor value and displaying it as the last datalabel or annotation. If someone has any idea how to do it I would be grateful.

1 Like

Hi, I have indeed asked for help from several AIs who have given me different codes, even with examples for the graphs of my heating but he has never managed to make it work.

Are you referring to AI as a ‘he’ now…?
In general HA view/terms (and also other IT areas)…if you donot properly know what you are doing then AI is more than often not a help. It can put you in the right direction but likely not provide solutions on complex stuff…or very bad solutions. This may improve over time but for HA, I am not at all impressed by any AI so far
hover

Hi all,

can someone point me why my graph is showing wrong colors in a color threshold mode?
It is going from blue to red up to 80C and then again back to blue, instead of blue at 20C and red at 110C.

Hi everyone. Can anyone give me a clue as to why the following card does not do what I would like!? The card is supposed to show the change between 2 values in the series, and then when it ticks over it adds 4096 to make the value positive. All is fine with positive diffs, however, it doesn’t seem to detect any negative value in the transform and always shows the x on the graph, even when it is (say?) -4000.

Many thanks! Dave

series:
  - entity: sensor.archer_vr2100_data_received_mb
    name: Received
    type: line
    stroke_width: 2
    group_by:
      func: diff
      duration: 5min
    transform: "return x >= 0 ? x : x + 4096;"

I am at a loss on how to display the correct units (ppm) in the legend instead of percentile.
I have tried adding unit: “ppm” on very possible line within the YAML file but nothing seems to work. Documentation does meantion but not for legend.

type: custom:apexcharts-card
chart_type: radialBar
experimental:
  color_threshold: true
header:
  show: true
  floating: false
  title: Pollen-Tree
  show_states: true
  colorize_states: true
apex_config:
  stacked: true
  legend:
    show: true
    position: left
  plotOptions:
    radialBar:
      offsetY: -10
      startAngle: 0
      endAngle: 270
      hollow:
        margin: 5
        size: 20%
        background: transparent
        image: false
      dataLabels:
        name:
          show: false
        value:
          show: false
series:
  - entity: sensor.pollen_bomen
    type: column
    name: Tree
    show:
      in_header: true
      extremas: true
    float_precision: 1
    color_threshold:
      - value: 0
        color: green
        opacity: 0.3
      - value: 96
        color: orange
      - value: 208
        color: red
  - entity: sensor.pollen_berk
    type: column
    name: Berk
    show:
      in_header: true
      extremas: true
    float_precision: 1
    color_threshold:
      - value: 0
        color: green
        opacity: 0.3
      - value: 96
        color: orange
      - value: 208
        color: red
  - entity: sensor.pollen_cipres
    type: column
    name: Cipres
    show:
      in_header: true
      extremas: true
    float_precision: 1
    color_threshold:
      - value: 0
        color: green
        opacity: 0.3
      - value: 96
        color: orange
      - value: 208
        color: red
  - entity: sensor.pollen_eik
    type: column
    name: Eik
    show:
      in_header: true
      extremas: true
    float_precision: 1
    color_threshold:
      - value: 0
        color: green
        opacity: 0.3
      - value: 96
        color: orange
      - value: 208
        color: red
  - entity: sensor.pollen_els
    type: column
    name: Els
    show:
      in_header: true
      extremas: true
    float_precision: 1
    color_threshold:
      - value: 0
        color: green
        opacity: 0.3
      - value: 96
        color: orange
      - value: 208
        color: red
  - entity: sensor.pollen_hazelaar
    type: column
    name: Hazelaar
    show:
      in_header: true
      extremas: true
    float_precision: 1
    color_threshold:
      - value: 0
        color: green
        opacity: 0.3
      - value: 96
        color: orange
      - value: 208
        color: red
  - entity: sensor.pollen_iep
    type: column
    name: Iep
    show:
      in_header: true
      extremas: true
    float_precision: 1
    color_threshold:
      - value: 0
        color: green
        opacity: 0.3
      - value: 96
        color: orange
      - value: 208
        color: red
  - entity: sensor.pollen_pijnboom
    type: column
    name: Pijnboom
    show:
      in_header: true
      extremas: true
    float_precision: 1
    color_threshold:
      - value: 0
        color: green
        opacity: 0.3
      - value: 96
        color: orange
      - value: 208
        color: red
  - entity: sensor.pollen_plataan
    type: column
    name: Plataan
    show:
      in_header: true
      extremas: true
    float_precision: 1
    color_threshold:
      - value: 0
        color: green
        opacity: 0.3
      - value: 96
        color: orange
      - value: 208
        color: red
  - entity: sensor.pollen_populier
    type: column
    name: Populier
    show:
      in_header: true
      extremas: true
    float_precision: 1
    color_threshold:
      - value: 0
        color: green
        opacity: 0.3
      - value: 96
        color: orange
      - value: 208
        color: red

Can anyone tell my why my columns become so narrow when I add a line to my chart? This has been driving me crazy and I can’t seem to figure out what is causing it.

Here is the code with the line:

type: custom:apexcharts-card
experimental:
  color_threshold: true
graph_span: 7d
apex_config:
  chart:
    height: 535px
show:
  last_updated: true
header:
  title: HVAC Usage Last 7 Days
  show: true
  show_states: true
  colorize_states: true
yaxis:
  - id: temps
    decimals: 1
    min: 0
  - id: HVAC
    decimals: 1
    min: 0
    max: 24
    opposite: true
series:
  - entity: sensor.hallway_temperature
    yaxis_id: temps
    color_threshold:
      - value: 60
        color: blue
      - value: 65
        color: cyan
      - value: 68
        color: green
      - value: 75
        color: orange
      - value: 80
        color: red
  - entity: sensor.hvac_heat_mode
    yaxis_id: HVAC
    name: Heat Mode
    type: column
    color: goldenrod
    stroke_width: 1
    group_by:
      func: last
      duration: 1d
  - entity: sensor.hvac_cool_mode
    yaxis_id: HVAC
    name: Cool Mode
    type: column
    color: dodgerblue
    stroke_width: 1
    group_by:
      func: last
      duration: 1d
  - entity: sensor.hvac_off
    yaxis_id: HVAC
    name: HVAC Off
    type: column
    color: grey
    stroke_width: 1
    group_by:
      func: last
      duration: 1d

And when i remove the line series:

type: custom:apexcharts-card
experimental:
  color_threshold: true
graph_span: 7d
apex_config:
  chart:
    height: 535px
show:
  last_updated: true
header:
  title: HVAC Usage Last 7 Days
  show: true
  show_states: true
  colorize_states: true
yaxis:
  - id: temps
    decimals: 1
    min: 0
  - id: HVAC
    decimals: 1
    min: 0
    max: 24
    opposite: true
series:
  - entity: sensor.hvac_heat_mode
    yaxis_id: HVAC
    name: Heat Mode
    type: column
    color: goldenrod
    stroke_width: 1
    group_by:
      func: last
      duration: 1d
  - entity: sensor.hvac_cool_mode
    yaxis_id: HVAC
    name: Cool Mode
    type: column
    color: dodgerblue
    stroke_width: 1
    group_by:
      func: last
      duration: 1d
  - entity: sensor.hvac_off
    yaxis_id: HVAC
    name: HVAC Off
    type: column
    color: grey
    stroke_width: 1
    group_by:
      func: last
      duration: 1d

Try to use formatter (see apexcharts.com), search for examples in this long thread

rtm

I did rtm and I saw this, but even with the experimental and color threshold lines removed it looks the same:

type: custom:apexcharts-card
graph_span: 7d
apex_config:
  chart:
    height: 535px
  legend:
    show: false
  yaxis:
    - id: temps
      decimalsInFloat: 1
      forceNiceScale: true
      min: 0
      title:
        text: Temp (°F)
    - id: HVAC
      decimalsInFloat: 1
      min: 0
      max: 24
      opposite: true
      title:
        text: Hours
show:
  last_updated: true
header:
  title: HVAC Usage Last 7 Days
  show: true
  show_states: true
  colorize_states: true
series:
  - entity: sensor.hallway_temperature
    yaxis_id: temps
  - entity: sensor.hvac_heat_mode
    yaxis_id: HVAC
    name: Heat Mode
    type: column
    color: goldenrod
    stroke_width: 1
    group_by:
      func: last
      duration: 1d
  - entity: sensor.hvac_cool_mode
    yaxis_id: HVAC
    name: Cool Mode
    type: column
    color: dodgerblue
    stroke_width: 1
    group_by:
      func: last
      duration: 1d
  - entity: sensor.hvac_off
    yaxis_id: HVAC
    name: HVAC Off
    type: column
    color: grey
    stroke_width: 1
    group_by:
      func: last
      duration: 1d

I cannot reproduce this, likely because of not the same dataset, try to use fill_raw in series and/or fill in group_by (see doc)
And in ALL cases, remove anything that is not needed to represent the crude graphs …only add things when the utmost basis is working…start with one graph then add 2nd

I’ll keep playing with the options, but fill_raw and fill don’t really get me what I’m looking for. I’m trying to track the number of hours that my HVAC is heating, cooling or off every day. 1 column per sensor every day though for testing I’ve included only heat since it has the most data. I just set up the sensors to track these yesterday, so there is not a lot of data for them yet. I’ve simplified the chart about as much as possible and still seeing the same thing.

type: custom:apexcharts-card
graph_span: 3d
span:
  start: day
  offset: -2d
header:
  show: false
series:
  - entity: sensor.hallway_temperature
  - entity: sensor.hvac_heat_mode
    type: column
    group_by:
      func: last
      duration: 1d

And when I remove the line serie:

type: custom:apexcharts-card
graph_span: 3d
span:
  start: day
  offset: -2d
header:
  show: false
series:
  - entity: sensor.hvac_heat_mode
    type: column
    group_by:
      func: last
      duration: 1d

Add the same grouping to the first one, it may be that the ‘last’ of 1 and ‘last’ of the other are not aligned. I understand the target but without the exact same datasets, nothing I can do else is but suggest

Hmmm… Seems that is the case with the grouping. Unfortunately though with the grouping on the temperature line it’s only giving me one value (the last) for temperature for each day, no? Not really the best representation of it that way. I can try to play around with the grouping some more…

type: custom:apexcharts-card
graph_span: 3d
span:
  start: day
  offset: -2d
header:
  show: false
series:
  - entity: sensor.hallway_temperature
    group_by:
      func: last
      duration: 1d
  - entity: sensor.hvac_heat_mode
    type: column
    group_by:
      func: last
      duration: 1d

As I donot know what you want to compare to what, you could create a helper to create a single daily value for the HVAC and then you no longer need to group. EDIT: this helper would then not allow historical values before the moment of creation

Interesting thought. That’s essentially what I did but I guess it’s logging as the values are changing. So instead just log one value at say 11:59:59 every day… I’ll look into that.

extremas do not always show the extremes rendered in a series

Extremas are calculated using the datapoints inside the chart span (just as a statistic sensor would), but the line is drawn taking into account also the first value outside (or before) the chart span. That means that if that first outside value is an extrema the line will go beyond the calculated extremas. This also affects the color thresholds, which follow the drawn line and not the extremas.

My solution to finding the extrema of the actually drawn line is an SQL sensor:

SELECT MIN(state) AS state FROM (SELECT CAST(state AS DECIMAL(10,2)) AS state FROM “states” WHERE metadata_id = xxx AND STATE IS NOT ‘unavailable’ ORDER BY last_updated_ts DESC LIMIT (SELECT count(metadata_id) FROM “states” WHERE metadata_id = xxx AND last_updated_ts >= strftime(‘%s’, ‘now’, ‘-8 hours’) AND STATE IS NOT ‘unavailable’) + 1)

Change MIN to MAX as needed and replace xxx by the correct metadata_id for your entity.