kalkih / mini-graph-card

Minimalistic graph card for Home Assistant Lovelace UI
MIT License
3.01k stars 233 forks source link

Legend indicator: color may not follow color_thresholds #1116

Open ildar170975 opened 2 months ago

ildar170975 commented 2 months ago

Initial report: https://github.com/kalkih/mini-graph-card/pull/1115#issuecomment-2248682920

With https://github.com/kalkih/mini-graph-card/pull/1115 merged: a legend indicator's color may follow color_thresholds:

type: custom:mini-graph-card
entities:
  - entity: sensor.xiaomi_cg_1_co2
    show_state: true
    state_adaptive_color: true
  - entity: sensor.xiaomi_cg_2_co2
    show_state: true
    state_adaptive_color: true
name: CO2
hours_to_show: 24
points_per_hour: 60
lower_bound: ~400
color_thresholds:
  - value: 450
    color: orange
  - value: 550
    color: green
  - value: 650
    color: magenta
color_thresholds_transition: hard
height: 400
show:
  labels: true

изображение i.e. here a color = PINK for both entities.

With changed color_thresholds:

color_thresholds:
  - value: 450
    color: orange
  - value: 550
    color: green
  - value: 680
    color: magenta

there is different picture - an indicator is BLACK for the 2nd entity: изображение

Same - when graphs are built for attributes.

akloeckner commented 2 months ago

TL;DR

We should try to substitute inState by state here: https://github.com/kalkih/mini-graph-card/blob/49ca1cd8627381e2981f57906014f615f08301c9/src/main.js#L659


I did some digging and I think:

We're sorting the thresholds in descending order here: https://github.com/kalkih/mini-graph-card/blob/49ca1cd8627381e2981f57906014f615f08301c9/src/buildConfig.js#L91

We then look for the first threshold c1, which is less than the state, as well as the next higher threshold c2 here: https://github.com/kalkih/mini-graph-card/blob/49ca1cd8627381e2981f57906014f615f08301c9/src/main.js#L655-L657

If all thresholds are below the state, there is no c2 and we land in the else-branch for degenerate cases. This seems to work well: https://github.com/kalkih/mini-graph-card/blob/49ca1cd8627381e2981f57906014f615f08301c9/src/main.js#L662-L664

However, the "normal" branch seems to be broken. This is the case, where we actually have c1 and c2. My current guess is that the factor cannot be calculated, because inState is a string: https://github.com/kalkih/mini-graph-card/blob/49ca1cd8627381e2981f57906014f615f08301c9/src/main.js#L659-L660

I also guess that this is no issue for the graph itself, because it seems to be coloured by an SVG gradient (which I am not familiar with). https://github.com/kalkih/mini-graph-card/blob/49ca1cd8627381e2981f57906014f615f08301c9/src/main.js#L446

akloeckner commented 2 months ago

We should try to substitute inState by state

Nope, that didn't help...

akloeckner commented 2 months ago

Ok... Next guess: the interpolateColor is weird. 😉 It only works on hexadecimal colours, such as '#00ff00': https://github.com/kalkih/mini-graph-card/blob/49ca1cd8627381e2981f57906014f615f08301c9/src/utils.js#L16-L30

Try converting your colours to that notation. This works well for me. Example:

color_thresholds:
  - value: 450
    color: '#ff0000'
  - value: 550
    color: '#00ff00'
  - value: 650
    color: '#0000ff'

I am not sure how to fix this. We'd need to internally convert named colours to hexadecimal notation...

Or go the lazy way used for the state: do not interpolate and use computeColor instead of intColor: https://github.com/kalkih/mini-graph-card/blob/49ca1cd8627381e2981f57906014f615f08301c9/src/main.js#L297 https://github.com/kalkih/mini-graph-card/blob/49ca1cd8627381e2981f57906014f615f08301c9/src/main.js#L608

ildar170975 commented 2 months ago

convert named colours to hexadecimal notation

I wonder is there any standard function to do it?

akloeckner commented 2 months ago

I also wondered... Since 2023 we can even directly use CSS's color-mix. But that would break compatibility with lots of older devices.

Then, I found d3.interpolate, which seems to do (among many other things) exactly what we need. It would be a new dependency, I think. But maybe worth a try.

akloeckner commented 2 months ago

d3 seems to work well. See #1118 . I'll migrate computeColor, too. Maybe you can then do some checks...

ildar170975 commented 2 months ago

@akloeckner very good! I will test it in a week!

github-actions[bot] commented 2 months ago

:tada: This issue has been resolved in version 0.12.2-dev.2 :tada:

The release is available on GitHub release

Your semantic-release bot :package::rocket:

ildar170975 commented 2 months ago

Fixed in 0.12.2-dev.2 Thank you, @akloeckner

jlsjonas commented 1 month ago

Hi @akloeckner, first of all thanks for your continued help in maintaining mini-graph-card!

I sadly enough only got freed up enough now to notice #1118 getting merged.

The original implementation is actually very efficient, despite being limited to hex. Sadly enough adding dependencies in the graph part of the app also goes directly against one of the (unwritten?) core objectives of the project to avoid libraries for the graph (i.e. core) part of the card, with enough focus on efficiency; leading to graphs that still work well on underpowered devices.

We can certainly extend the feature set; and use a (minimal) lib to parse named colors to hex on the configuration-side (as that's definitely a value-add) if required.

(among many other things)

is sadly enough the problem here :)

/cc @ildar170975

p.s.: thanks that you didn't :)

Or go the lazy way used for the state: do not interpolate and use computeColor instead of intColor:

jlsjonas commented 1 month ago

Have a look at https://stackoverflow.com/a/1573141/620141

The canvas-method might be more future-proof as it's per spec, but is quite slow (https://jsperf.app/sobipi). I'd personally consider the risk of many new css colors being added on the low side & just hardcode it.

akloeckner commented 1 month ago

😄 I also found the canvas method and decided to remember this as a fun creative way of doing things. But never to do this myself. 😝 Sorry for calling the efficient implementation "weird"...

Are you experiencing problems on slow devices? We can always re-introduce the original interpolation and just convert everything to hex internally. Or we first wait if someone complains and then possibly re-visit this.

jlsjonas commented 1 month ago

No worries, I fully understand the perception if you're not used to binary.

The slow device support is mainly an extra advantage (I have noticed issues on my slowest device with other graphs, so it's definitely something to stay aware of)

The main reason for reopening is to keep the core code of the card (graphs) dependency-free as intended by kalkih. This would be the first exception to that, and I believe there to be valid alternatives (converting on config import/sanitize).

I'm sadly still quite short on extra time to code but if you're willing & able to contribute I'd be glad to continue assisting architecturally meanwhile (just @ me)!

In this case, we preferably revert the introduction of d3.interpolate in graph.js & use one of the SO methods when importing the config.

Thanks again!