ApexCharts card - A highly customizable graph card

Does anybody know if apexcharts can display the slope of a line?

Guess not, if you want to do research then note that the HA version is based on the apexcharts.js. That one shows slope as charts only.
But… if you look at the HA version, there are only a limited amount of Charts implemented versus those available in js
RomRider/apexcharts-card: :chart_with_upwards_trend: A Lovelace card to display advanced graphs and charts based on ApexChartsJS for Home Assistant (github.com)

1 Like

SOLUTION: Tooltip with data_generator + standard sensors

I found a “neat” solution that I would like to share.

The issue that a shared tooltip isn’t working seems to be a bug in Apex Charts itself, rather than the implementation in HA.
There was already an issue raised (Shared tooltip not working with data_generator · Issue #593 · RomRider/apexcharts-card · GitHub) and also the solution is mentioned there.

The code works fine and now the result looks as follows also including the values from the data_generator function:

I added some further tweaks (code below):

  • Time stamp was missing in the tooltip’s header
  • Number format (I use the German number formatting with comma as decimal separator and dot as thousands separator)
  • Show “n/a” for data points with no data - the initial code from the links above shows the last known value

This is the improved code:

type: custom:apexcharts-card
graph_span: 1d
span:
  end: day
all_series_config:
  fill_raw: last
  extend_to: false
  show:
    legend_value: false
header:
  show: false
apex_config:
  stroke:
    show: true
    width: 1
    dashArray:
      - 2
      - 0
  dataLabels:
    enabled: true
  chart:
    height: 560px
  legend:
    showForZeroSeries: true
  tooltip:
    enabled: true
    shared: true
    fixed: true
    custom: |
      EVAL:function a({ series, seriesIndex, dataPointIndex, w }) {
          const hoverXaxis = w.globals.seriesX[seriesIndex][dataPointIndex];
          const seriesX = w.globals.seriesX.map((seriesX) => {
              return seriesX.findIndex(x => x >= hoverXaxis);
          });

          // Function to format numbers with comma as decimal separator and dot as thousands separator
          function formatNumber(value) {
              const [integerPart, decimalPart] = value.toFixed(1).split('.');
              return integerPart.replace(/\B(?=(\d{3})+(?!\d))/g, '.') + ',' + decimalPart;
          }

          let hoverList = "";

          w.globals.seriesNames.forEach((seriesName, seriesEachIndex) => {
              const data = series[seriesEachIndex];
              const index = seriesX[seriesEachIndex];

              let value;
              if (index >= 0 && index < data.length && w.globals.seriesX[seriesEachIndex][index] >= hoverXaxis) {
                  value = data[index];
              } else {
                  value = 'n/a';
              }

              hoverList += `
                  <div class="apexcharts-tooltip-series-group apexcharts-active" style="order: 1; display: flex;">
                      <span class="apexcharts-tooltip-marker" style="background-color: ${
                          w.globals.markers.colors[seriesEachIndex]
                      };"></span>
                      <div class="apexcharts-tooltip-text" style="font-family: Helvetica, Arial, sans-serif; font-size: 12px;">
                          <div class="apexcharts-tooltip-y-group">
                              <span class="apexcharts-tooltip-text-y-label">${
                                  seriesName
                              }: </span>
                              <span class="apexcharts-tooltip-text-y-value">${value === 'n/a' ? value : formatNumber(value)}</span>
                          </div>
                      </div>
                  </div>`;
          });

          const date = new Date(hoverXaxis);
          const formattedDate = date.toLocaleDateString('en-GB', {
              day: '2-digit',
              month: 'short',
              year: 'numeric'
          });
          const formattedTime = date.toLocaleTimeString('en-GB', {
              hour: '2-digit',
              minute: '2-digit',
              second: '2-digit'
          });

          return `<div class="apexcharts-tooltip-title" style="font-family: Helvetica, Arial, sans-serif; font-size: 12px;">${
              formattedDate + ", " + formattedTime
          }</div>${hoverList}`;
      }
series:
  - entity: sensor.solcast_pv_forecast_prognose_heute
    name: PV-Forecast
    data_generator: |
      return entity.attributes.detailedForecast.map((entry) => {
        return [new Date(entry.period_start), (entry.pv_estimate * 1000)];
      });
    color: lightgrey
    group_by:
      func: avg
      duration: 1min
  - entity: sensor.inverter_pv_leistung
    type: area
    name: PV-Eingangsleistung
    color: '#37872D'
    opacity: 0.2
    group_by:
      func: avg
      duration: 1min
  - entity: sensor.inverter_eingangsleistung_verlustkorrigiert
    color: '#73BF69'
    name: PV-Eingangsleistung (verlustkorr.)
    group_by:
      func: avg
      duration: 1min
  - entity: sensor.hausverbrauchsleistung
    type: area
    color: '#B877D9'
    opacity: 0.2
    name: Gesamtverbrauch
    group_by:
      func: avg
      duration: 1min
  - entity: sensor.power_meter_netzbezug
    type: area
    color: '#F2495C'
    opacity: 0.2
    name: Netzbezug
    group_by:
      func: avg
      duration: 1min
  - entity: sensor.battery_lade_entladeleistung_verlustkorrigiert
    type: area
    color: '#5794F2'
    opacity: 0.2
    name: Akku Ladung/Entladung
    group_by:
      func: avg
      duration: 1min
  - entity: sensor.power_meter_netzeinspeisung
    type: area
    invert: true
    color: '#FF9830'
    opacity: 0.2
    name: Netzeinspeisung
    group_by:
      func: avg
      duration: 1min

.
QUESTION (further improvement):
Does anyone know, which object the unit of measurement is stored in within an Apex chart card?
I would like to read the respective data series’ unit of measurement and plot it along with the values within the tooltip.

Cheers,
Mat

EDIT:
Updated Code: ApexCharts card - A highly customizable graph card - #3699 by madmat17

1 Like

I would not want to call this neat though… have used it since a while and from my pov this is more a horrible workaround :rofl:

is it possible to create a (calendar) view like this?

Not sure why you think a calendar is a graph … but anyhow: read the docs on the types

Oh - I missed to frame the word neat in quotation marks.
Sarcasm often gets lost in written… :laughing:

1 Like

not sure, perhaps i can convert calendar entries in a diagram

i ve a start and enddate…

was only a question

I did some more adjustments.
It seems that the unit of measurement cannot be pulled into tooltip.custom.

Changes:

  • Unit of measurement is now hardcoded
  • Data series names and corresponding values are aligned in a table (names with left alignment and values with right alignment)

Now it looks as follows:


Here’s some code-p*rn (as this workaround is slightly pervert):

type: custom:apexcharts-card
graph_span: 1d
span:
  end: day
all_series_config:
  fill_raw: last
  extend_to: false
  show:
    legend_value: false
header:
  show: false
apex_config:
  stroke:
    show: true
    width: 1
    dashArray:
      - 2
      - 0
  dataLabels:
    enabled: true
  chart:
    height: 560px
  legend:
    showForZeroSeries: true
  tooltip:
    enabled: true
    custom: |
      EVAL:function({ series, seriesIndex, dataPointIndex, w }) {
        const hoverXaxis = w.globals.seriesX[seriesIndex][dataPointIndex];
        const seriesX = w.globals.seriesX.map(seriesX => seriesX.findIndex(x => x >= hoverXaxis));

        function formatNumber(value) {
          value = Number(value);
          if (isNaN(value)) return 'n/a';
          const [integerPart, decimalPart] = value.toFixed(1).split('.');
          return integerPart.replace(/\B(?=(\d{3})+(?!\d))/g, '.') + ',' + decimalPart;
        }

        const formattedDate = new Date(hoverXaxis).toLocaleDateString('en-GB', {
          day: '2-digit',
          month: 'short',
          year: 'numeric'
        });
        const formattedTime = new Date(hoverXaxis).toLocaleTimeString('en-GB', {
          hour: '2-digit',
          minute: '2-digit'
        });

        const hoverList = `<table style="width: 100%; border-collapse: collapse; font-family: Helvetica, Arial, sans-serif; font-size: 12px;">
          ${w.globals.seriesNames.map((seriesName, i) => {
            const data = series[i];
            const index = seriesX[i];
            const value = (index >= 0 && index < data.length && w.globals.seriesX[i][index] >= hoverXaxis)
              ? formatNumber(data[index]) + ' W'
              : 'n/a';
            return `<tr>
              <td style="padding: 2px 5px; text-align: left; vertical-align: middle; max-width: 50%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;">
                <span class="apexcharts-tooltip-marker" style="background-color: ${w.globals.markers.colors[i]}; width: 8px; height: 8px; display: inline-block; border-radius: 50%; margin-right: 5px;"></span>
                ${seriesName}:
              </td>
              <td style="padding: 2px 5px; text-align: right; vertical-align: middle; max-width: 50%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;">
                <strong>${value}</strong>
              </td>
            </tr>`;
          }).join('')}
        </table>`;

        return `<div class="apexcharts-tooltip-title" style="font-family: Helvetica, Arial, sans-serif; font-size: 12px; padding-bottom: 5px;">
          ${formattedDate}, ${formattedTime}
        </div>${hoverList}`;
      }
series:
  - entity: sensor.solcast_pv_forecast_prognose_heute
    name: PV-Forecast
    data_generator: >
      return entity.attributes.detailedForecast.map(entry => [new
      Date(entry.period_start), entry.pv_estimate * 1000]);
    color: lightgrey
    group_by:
      func: avg
      duration: 1min
  - entity: sensor.inverter_pv_leistung
    type: area
    name: PV-Eingangsleistung
    color: '#37872D'
    opacity: 0.2
    group_by:
      func: avg
      duration: 1min
  - entity: sensor.inverter_eingangsleistung_verlustkorrigiert
    color: '#73BF69'
    name: PV-Eingangsleistung (verlustkorr.)
    group_by:
      func: avg
      duration: 1min
  - entity: sensor.hausverbrauchsleistung
    type: area
    color: '#B877D9'
    opacity: 0.2
    name: Gesamtverbrauch
    group_by:
      func: avg
      duration: 1min
  - entity: sensor.power_meter_netzbezug
    type: area
    color: '#F2495C'
    opacity: 0.2
    name: Netzbezug
    group_by:
      func: avg
      duration: 1min
  - entity: sensor.battery_lade_entladeleistung_verlustkorrigiert
    type: area
    color: '#5794F2'
    opacity: 0.2
    name: Akku Ladung/Entladung
    group_by:
      func: avg
      duration: 1min
  - entity: sensor.power_meter_netzeinspeisung
    type: area
    invert: true
    color: '#FF9830'
    opacity: 0.2
    name: Netzeinspeisung
    group_by:
      func: avg
      duration: 1min
view_layout:
  grid-area: pvchart
card_mod:
  style: |
    ha-card {
      height: 572px !important;
    }
1 Like

Hello everybody! The opportunities enabled by this integration are amazing, and I really like it!

I have mixed mushroom cards with radial bar charts, obtaining the following result (actually… someone on the web I copied :D)

image

Nevertheless, a description appears at the mouse hovering over one of the hollows.

image

Well, I want to inhibit this behaviour. I already tried the option involving the tooltip, as follows:

apex_config:
    tooltip:
        enabled: false

But actually, nothing happens. What am I missing?

2 Likes

I want to show the solar production of each mont over the last year. So 12 bars with the max of each month.

I think the sensor ist correct:

I tried with this basic config, but it shows nothing.

type: custom:apexcharts-card
graph_span: 1y
series:
  - entity: sensor.strom_solar_gesamt_energie_m
    type: column
    statistics:
      type: max
      period: month
    group_by:
      func: sum
      duration: 1month

Suggest to start by simplifying, remove the group by and try sum/mean/etc for the stats…only then add more stuff

Please share your code.

Thanks.

The only type I get values with is “state”

But this do not work, when the helper reset was made incorrect at 23:00 like in Mar.

So I need something with “max of each month”. Tried a lot with statitstics and group, but I can’t get it.

type: custom:apexcharts-card
graph_span: 1y
span:
  start: year
series:
  - entity: sensor.strom_solar_gesamt_energie_m
    type: column
    statistics:
      type: state
      period: month

I am missing that too . It should be there for sure cause here in central europe we use the thousand . as separator and not such ugly endless figures like

1000000
1.000.000 is the general way to show figures here from excel over word to access and it has been part of the OS ses for ages.

Time to see that in apex arrive like a line
thousandseparator: true

From where did you get the thousand dot as separator shown in the graphic y axis.

The apexchart code is not showing something like that, but I can see that on the y axis.

I have no idea what your data looks like, i.e. how you configured your sensor. Alsonot sure what you need…when a sensor has no data it cannot show it logically

Hello,
I have a stacked graph card, and I would like to customize the y axis units. I could just customize units on one graph, or both.
The problem is that as soon as I use the y-axis option, my 2 graphs unstack.
Is there a way to customize y axis while stacked ??
Thanks

Stacked, no Y Axis customization:

With y axis customization, but not stacked anymore:

Thank you :slight_smile:

My guess is that this not possible. The 2nd y-axis cannot have two start-values. Example from your graph is the blue in 3rd stack (about0,5) and the 4th stack (about 12,5)…where should the y-axis take ts values from ?

Thanks for your answer. I may have misunderstood y axis function.
I don’t want to change the start value or the range of values displayed (it won’t be compatible with stacked graphe anyway).
What I try to do is just to change the scale of the graph.

To illustrate lets say that graph1 represent rain water. Scale unit = 1mm.
graph2 is irrigation water: unit = 1 min of irrigation
Let’s say that 60 mins of irrigation correspond to 5mm, I would like to adjust the scale of graph1 (or graph2) to match the other one.
What I’m trying to do is just change the scale of one of the graphes. There may be another option that I missed…