RomRider / apexcharts-card

📈 A Lovelace card to display advanced graphs and charts based on ApexChartsJS for Home Assistant
MIT License
1.16k stars 79 forks source link

`color_threshold` colors are incorrect when also configuring the y-axis. #763

Open jftanner opened 1 month ago

jftanner commented 1 month ago

Checklist

Describe the bug The gradient applied by color_threshold appears to be calculated over the range of sensor values, rather than the actual y-axis values. So they are correct if the y-axis is auto-generated, but incorrect if the y-axis scale is configured with a specific minimum, or if another series is also displayed. The screenshots below demonstrate this best.

Version of the card Version: 2.1.2

To Reproduce This is the configuration I used:

type: custom:apexcharts-card
experimental:
  color_threshold: true
header:
  show: true
  title: Humidity
  show_states: true
  colorize_states: true
graph_span: 24h
yaxis:
  - min: 0
    max: ~50
    decimals: 0
    align_to: 10
series:
  - entity: sensor.ams_humidity
    color: lightblue
    type: area
    stroke_width: 1
    color_threshold:
      - value: 0
        color: white
      - value: 15
        color: cyan
      - value: 30
        color: green
      - value: 30
        color: red
      - value: 40
        color: black
        opacity: 1

Screenshots This screenshot shows it working correctly. Note that color_threshold is set with overlapping values at 30, to create a hard division between green and red at 30. The resulting gradient goes from a light green, through the hard transition to red, and up to a little bit of black. image

This screenshot is the exact same data and configuration, except the a y-axis set for 0 to ~50. You can see that it gets the exact same gradient applied. The hard division happens around 14 instead of 30, and the low-end colors don't appear. image

This screenshot adds another series which extends the axis, instead of configuring it manually. Again the same gradient is applied. Notice that the hard division happens around 27.5. image

Expected behavior I expect the color gradient to match the configured values, regardless of y-axis scale.

Desktop:

Smartphone:

Additional context N/A

Triple-S commented 3 weeks ago

I was also facing this bug, so I did a little bit debugging and I want to share my findings in case someone else finds them useful:

Apparently the apexcharts library only supports relative coordinates as the colorStops used for the gradient filling. Since the color_threshold property uses absolute coordinates, they need to be transfered into relative coordinates first which is done by the _computeFillColorStops function. To do that, the colors of the minimum and maximum value of the yaxis are determined as they consitute 0 and 100% in the relative coordinates and then the colors between are interpolated.

The border values are determined in lines 929 and 930 of apexcharts-card.ts and the problem is that it is assumed that the border values of a series match the border values of the yaxis which is obviously not the case when the borders of the yaxis are customized. I don't know how to fix the problem properly as I have quite some trouble understanding the datastructures used, but I was able to find a workaround for at least the cases with only a single yaxis:

Small correction to my initial assumption: Apparently the relative coordinates used by the apexcharts library are defined as the range between the minimum of the y-axis but not lower than 0 and the maximum of the graph, because this is exactly the area filled with colors, so the method should be the following in case of only a single y-axis:

let min = this._graphs?.[serie.index]?.min;
const max = this._graphs?.[serie.index]?.max;
if (min === undefined || max === undefined) return [];
const yaxis = Array.isArray(this._config?.apex_config?.yaxis) ? this._config?.apex_config?.yaxis?.[0] : this._config?.apex_config?.yaxis;
if (yaxis?.min !== undefined) min = typeof yaxis.min === 'number' ? yaxis.min : yaxis.min(min);
min = Math.max(0, min);
fnordson commented 4 days ago

I have a related issue. Weirdly, you can see when I configured the y-axis. This is where the color stops working and marks it red.

grafik

This is how it looks without y-axis:

grafik

As you see, I have to modify y-axis because I have those peaks every time my neighbor fires up his hookah on the balcony... When I uncomment the y-axis, it shows everything as it should be:

grafik