ApexCharts card - A highly customizable graph card

Thanks for the reply and explanation. apologies, may be I am not able to explain the scenario right or I am not able to understand what you are explaining.

I am already using as_duration as hour and the value is coming from the sensor without transformation. My ask is if we can remove that seconds and ms values by rounding off or something in apex chart header.

I am not a tech writer and not sure what is missing from my explanation, you need to round the data that comes back from the entity, that is data in the form of ‘x’ and you can round via the transform attribute, read the docs ?
Try to use my example and play around (using any of your sensors as entity) and use either 15.222 being hardcoded or ‘x’ (the value form the sensor) maybe this will help with understanding

Hello, just wanted to post some code Claude.ai just did for me.
and it’s the first time I use it for something that advanced.

I have an history stat sensor, that calculate the daily heating time of my pellet stove. The data is stored as hours only, like 0.09h for 0h:5m.

so it was a bit messy in my card.
I asked claude to find a solution to display it with the correct format.
and this came out.

type: custom:apexcharts-card
graph_span: 14d
section_mode: true
grid_options:
  rows: 6
  columns: 16
show:
  last_updated: false
header:
  show: false
  title: Chauffe journalière
  show_states: true
  colorize_states: true
  standard_format: false
span:
  start: week
  offset: "-7d"
apex_config:
  legend:
    show: false
  yaxis:
    labels:
      formatter: |
        EVAL:function(value) {
          const hours = Math.floor(value);
          const minutes = Math.round((value % 1) * 60);
          return hours + "h" + (minutes < 10 ? "0" : "") + minutes + "m";
        }
  tooltip:
    "y":
      formatter: |
        EVAL:function(value) {
          const hours = Math.floor(value);
          const minutes = Math.round((value % 1) * 60);
          return hours + "h " + (minutes < 10 ? "0" : "") + minutes + "m";
        }
series:
  - entity: sensor.poele_on_daily
    unit: h
    type: column
    group_by:
      func: max
      duration: 1d
    stroke_width: 1
    float_precision: 2
    show:
      extremas: false

works perfectly
so here I am just sharing :slight_smile:

So,
claude is not all knowing after all or i’m bad at prompting ^^

I track my forseen electricity and gaz bill using :

  • a raw sensor that get the live m3 index
- sensor:
      name: ConsoGAZ
      device_class: gas
      state_class: measurement
      unit_of_measurement: 'm³'
      state: >
        {{states.sensor.weshomeval.attributes["java"][36]["value"]|replace('m3','')|float }}
  • a utility meter that collects and reset at the time my gaz provider stop and start the monthly bill
  monthly_bill_gaz:
    source: sensor.consogaz
    name: Monthly Bill Gaz
    cycle: monthly
    offset:
     days: 9
  • finaly, a template sensor, that calculate based on the used m3 and some pricing stuff, the monthly bill.
  - sensor:
      name: CumulGazEUR_Monthly
      unique_id: CumulGazEUR_monthly
      unit_of_measurement: "EUR"
      state_class: total_increasing
      device_class: monetary
      state: >
        {{ ((states('sensor.monthly_bill_gaz')| float * states('sensor.cumulgazkwh') | float)) + states('sensor.te_cout_mensuel')| float }}

I wanted to generate a graph based on CumulEDFEUR_Monthly, showing for each month of the year the bill.
which is basicaly the last state before the reset.
I do have the LTS showing up in history graph (screenshot for july)

but every attempt with apex failed and I gues it’s because i can use any min, max option of ‘statistics’

    statistics:
      type: max
      period: month
    group_by:
      func: max
      duration: 1month

because I obviously don’t have those since i’m using a total_increasing sensor (right ?)

does anyone have an idea ? that doesn’t need to create another sensor, because I won’t be able to display past data.

Thx a lot

AI can be OK but also presents mistakes and if you are not aware of it then it may work but it not always the most practical solution. Example of a much simpler tooltip

  tooltip:
    "x":
      format: 'dd/MM'

  xaxis:
      datetimeFormatter:
        hour: HH:mm

all of this can be found in apexcharts.com, the very basis of this card.
The problem with AI is that people (guilty myself) not always know how to code things and it becomes difficult to see if AI is leading you down a rabbit hole. Asking to resolve AI code is not the way forward, posting what you need is.

1 Like

Check the HA statistics graph which presents you the option per sensor

I did, i only have SUM STATE and CHANGE

I don’t know where to go from here

Neither do I as I have no clue how your entity is constructed, fix that first and there are plenty of posts around LTS topics and sensors) … When yo uthink to have data, check with statistics graph card before going apex

yep, that why I posted :smiley:

That’s why I posted the configuration of my sensors :slight_smile:

Anyway, I don’t think there is a workaround this beside creating a new sensor.
I also tried to find a way to get LTS via API but there is no such endpoint.

And that is what I missed…darn

Check one of my posts with a data_generator and callWS

EDIT: did you check if there is data? Developer Tools > Statistics should show the sensor and in-it …values

I added this as an extra post. Then also here:

Hi all,
I’m trying to implement the apex charts card with sensor of array data.

I found this example:

 type: custom:apexcharts-card
        apex_config:
          chart:
            height: 500px
        graph_span: 14d
        span:
          end: day
        show:
          last_updated: true
        header:
          show: true
          show_states: false
          colorize_states: true
          title: EV9 Daily Driving Stats
        series:
          - entity: sensor.my_ev9_daily_driving_stats
            type: column
            data_generator: |
              return Object.entries(entity.attributes).map(([date, stats]) => {
                return { x: new Date(date), y: stats.total_consumed };
              });
            unit: Wh

Adapted to mine:

type: custom:apexcharts-card
apex_config:
  chart:
    height: 500px
graph_span: 14d
span:
  end: day
show:
  last_updated: true
header:
  show: true
  show_states: false
  colorize_states: true
  title: EV3 Fahrstatistiken
series:
  - entity: sensor.ev3_daily_driving_stats
    type: column
    data_generator: |
      return Object.entries(entity.attributes).map(([date, stats]) => {
        return { x: new Date(date), y: stats.total_consumed };
      });
    unit: Wh

But still not working.
I think the problem is the data_generator. I’ve completly no knowledge in javascript. Is anyone here who can help?

Sensor values:

"2025-09-27":
  total_consumed: 1805
  engine_consumption: 1261
  climate_consumption: 284
  onboard_electronics_consumption: 260
  battery_care_consumption: 0
  regenerated_energy: 835
  distance: 14.899993896484375
"2025-09-26":
  total_consumed: 537
  engine_consumption: 134
  climate_consumption: 193
  onboard_electronics_consumption: 210
  battery_care_consumption: 0
  regenerated_energy: 759
  distance: 9.20001220703125
"2025-09-25":
  total_consumed: 2566
  engine_consumption: 1924
  climate_consumption: 442
  onboard_electronics_consumption: 200
  battery_care_consumption: 0
  regenerated_energy: 1288
  distance: 16.5
"2025-09-24":
  total_consumed: 13211
  engine_consumption: 12425
  climate_consumption: 76
  onboard_electronics_consumption: 710
  battery_care_consumption: 0
  regenerated_energy: 3466
  distance: 143.99998474121094
"2025-09-23":
  total_consumed: 1027
  engine_consumption: 745
  climate_consumption: 172
  onboard_electronics_consumption: 110
  battery_care_consumption: 0
  regenerated_energy: 710
  distance: 7.8000030517578125

Hi all,

I’m trying to build a pie chart in Lovelace using apexcharts-card (v2.2.3).
My use case: I have 6 SQL sensors in Home Assistant that calculate the total hours per room over the last 14 days (Top 5 + “Others”). Example values:

  • sensor.verwarmingsvraag_per_kamer_eerste_uren → 59.4 h
  • sensor.verwarmingsvraag_per_kamer_tweede_uren → 58.4 h
  • sensor.verwarmingsvraag_per_kamer_derde_uren → 16.0 h
  • … etc.

I also have 6 companion sensors that hold the names (room labels):

  • sensor.verwarmingsvraag_per_kamer_eerste_naam → “Bathroom”
  • sensor.verwarmingsvraag_per_kamer_tweede_naam → “Kitchen”
  • … etc.

The basic pie chart works perfectly if I hardcode names in the YAML:

type: custom:apexcharts-card
chart_type: pie
series:
  - entity: sensor.verwarmingsvraag_per_kamer_eerste_uren
    name: "Eerste"
  - entity: sensor.verwarmingsvraag_per_kamer_tweede_uren
    name: "Tweede"
  - entity: sensor.verwarmingsvraag_per_kamer_derde_uren
    name: "Derde"
  - entity: sensor.verwarmingsvraag_per_kamer_vierde_uren
    name: "Vierde"
  - entity: sensor.verwarmingsvraag_per_kamer_vijfde_uren
    name: "Vijfde"
  - entity: sensor.verwarmingsvraag_per_kamer_andere_uren
    name: "Others"

But when I try to make the legend/dataLabels dynamic with a formatter (reading the corresponding *_naam entity), the chart completely disappears (empty card, no errors in Dev Tools).

Example (which causes blank chart):


apex_config:
  legend:
    formatter: |
      function (_, opts) {
        const names = [
          states["sensor.verwarmingsvraag_per_kamer_eerste_naam"]?.state ?? "—",
          states["sensor.verwarmingsvraag_per_kamer_tweede_naam"]?.state ?? "—",
          states["sensor.verwarmingsvraag_per_kamer_derde_naam"]?.state ?? "—",
          states["sensor.verwarmingsvraag_per_kamer_vierde_naam"]?.state ?? "—",
          states["sensor.verwarmingsvraag_per_kamer_vijfde_naam"]?.state ?? "—",
          states["sensor.verwarmingsvraag_per_kamer_andere_naam"]?.state ?? "Others",
        ];
        const h = Number(opts.w.globals.series?.[opts.seriesIndex] ?? 0);
        return names[opts.seriesIndex] + " — " + h.toFixed(2) + " h";
      }

If I remove the formatter section, the chart renders again (but of course with only hardcoded names).


Question:

  • Is this a known limitation of apexcharts-card with pie charts + formatter?
  • Is there another way to use entity states (from companion sensors) as labels for pie slices?
  • Or do I need to create template sensors that combine name + value into the entity’s friendly_name instead?

Thanks a lot for any pointers!

Yes there are data since 2024 at least.
no the probleme is really that I can use MAX, and that I reset my utility meter on the 12th of each month

I tried with data generator but it did not work.

Or I could not make it work, and then I gave up after spending my whole saturday (sunny one …) :smiley:

What did you try? For statistics, assuming you want the ‘max’, this should work (as-in: present data) … you may want to change the period

series:
  - entity: sensor.poele_on_daily
    unit: h
    type: column
    statistics:
      type: max
      period: day
    stroke_width: 1
    float_precision: 2
    show:
      extremas: false

Hi everyone, I’ve setup a graph showing today’s and tomorrow’s (once they are available) electricity prices with separation of taxes, transmission fees and so on.

I haven’t managed to get the maximum to scale nicely though. I’m hoping that it’s just something that I’m missing and a new pair of eyes will spot what needs to be adjusted, as you can see from the picture there is way too much space in the Y-axis wasted:

type: custom:apexcharts-card
graph_span: 48h
stacked: true
apex_config:
  yaxis:
    forceNiceScale: true
    min: |
      EVAL: function(min) { return min }
    max: |
      EVAL: function(max) { return max }
experimental:
  color_threshold: true
show:
  last_updated: true
header:
  title: Electricity price
  show: true
  show_states: true
  colorize_states: true
span:
  start: day
all_series_config:
  type: column
now:
  show: true
  label: Now
series:
  - entity: sensor.sahkon_hinta
    show:
      in_header: true
      header_color_threshold: true
      in_chart: false
    name: Kokonaishinta
    type: column
    color: lightgray
    float_precision: 2
    stroke_width: 0
    transform: return x*100
    unit: c/kWh
    color_threshold:
      - value: 0
        color: 368f39
      - value: 15
        color: a3b34d
      - value: 20
        color: ffd57e
      - value: 25
        color: f18c56
      - value: 30
        color: de425b
      - value: 40
        color: c40423
  - entity: sensor.nordpool_kwh_fi_eur_5_095_0
    show:
      in_header: true
      header_color_threshold: true
      in_chart: false
    name: Veroton sähkö
    type: line
    color: lightgray
    float_precision: 2
    stroke_width: 0
    transform: return x*100
    unit: c/kWh
    color_threshold:
      - value: 0
        color: 368f39
      - value: 4
        color: a3b34d
      - value: 10
        color: ffd57e
      - value: 15
        color: f18c56
      - value: 20
        color: de425b
  - entity: sensor.nordpool_kwh_fi_eur_5_095_0
    show:
      in_header: true
      header_color_threshold: true
      in_chart: false
    name: Sähkö
    type: line
    color: lightgray
    float_precision: 2
    stroke_width: 0
    transform: return x*100*1.255
    unit: c/kWh
    color_threshold:
      - value: 0
        color: 368f39
      - value: 6
        color: a3b34d
      - value: 14
        color: ffd57e
      - value: 24
        color: f18c56
      - value: 34
        color: de425b
  - entity: sensor.shf_electricity_price_now
    name: siirron hinta
    stack_group: hinta
    opacity: 1
    show:
      in_header: false
      extremas: false
      legend_value: false
    type: column
    color: orange
    float_precision: 4
    unit: c/kWh
    data_generator: |
      return entity.attributes.data.map((d, index) => {
        return [d["Timestamp"]*1000, entity.attributes.transmission_price*100];
      });
  - entity: sensor.shf_electricity_price_now
    name: marginaalin hinta
    stack_group: hinta
    show:
      in_header: false
      extremas: false
      legend_value: false
    type: column
    color: eb2fdb
    float_precision: 4
    unit: c/kWh
    data_generator: |
      return entity.attributes.data.map((d, index) => {
        return [d["Timestamp"]*1000, entity.attributes.electricity_margin*100];
      });
  - entity: sensor.shf_electricity_price_now
    name: sähkövero
    show:
      in_header: false
      extremas: false
      legend_value: false
    type: column
    stack_group: hinta
    color: ffe92b
    float_precision: 4
    unit: c/kWh
    data_generator: |
      return entity.attributes.data.map((d, index) => {
        return [d["Timestamp"]*1000, entity.attributes.electricity_tax*100];
      });
  - entity: sensor.shf_electricity_price_now
    name: sähkön hinta
    show:
      in_header: false
      extremas: false
      legend_value: false
    type: column
    stack_group: hinta
    color: lightgray
    float_precision: 4
    unit: c/kWh
    data_generator: |
      return entity.attributes.data.map((d, index) => {
        return [d["Timestamp"]*1000, d["PriceWithTax"]*100];
      });
    color_threshold:
      - value: 0
        color: 368f39
      - value: 6
        color: a3b34d
      - value: 14
        color: ffd57e
      - value: 24
        color: f18c56
      - value: 34
        color: de425b
  - entity: sensor.shf_electricity_price_now
    name: " "
    opacity: 0
    show:
      in_header: false
      extremas: true
      legend_value: false
    type: column
    stack_group: hinta
    color: lightgray
    float_precision: 2
    unit: c/kWh
    data_generator: |
      return entity.attributes.data.map((d, index) => {
        return [d["Timestamp"]*1000, d["TotalPrice"]*100];
      });
  - entity: sensor.shf_electricity_price_now
    name: Average Price
    unit: c/kWh
    show:
      in_header: false
      extremas: false
    type: line
    color: blue
    float_precision: 2
    stroke_width: 2
    data_generator: >
      return [[start, entity.attributes.today_avg*100], [moment(end).subtract(1,
      'days'), entity.attributes.today_avg*100], []];

Thanks in advance.

1 Like

Have you tried this?

max: ‘|+10|’: The max of the data in the series is 32, then the y-axis max will be 42 (= 32 + 10)

Yup, not sure which entity messes it up but it doesn’t seem to interpret the values correctly:

This is only with the change of

max: '|+10|'

That did however give me the following idea, if I adjust the formula:

 max: |
      EVAL: function(max) { return max-(max/2.5) }

This is the result:

Much better :+1:

1 Like

And here’s my take on our new 15 minute scenario

Noting that this only works properly outside of apex_config…i.e. on yaml-root-level

yaxis:
  - max: "|+10|"