dbuezas / lovelace-plotly-graph-card

Highly customisable Lovelace card to plot interactive graphs. Brings scrolling, zooming, and much more!
374 stars 16 forks source link

Feature request: programmable y axis limits #60

Closed Rvh91 closed 1 year ago

Rvh91 commented 2 years ago

It would be great if we could change the autoscale behavior. For example one of the following (or all) would be nice:

  1. Exclude certain entities from the automatically computed axis limits.
  2. Specify either a hard coded lower or upper bound for an axis, but have the other bound automatically computed.
  3. Specify a function for automatical computation of limits, for example:
    range:
    - max(entities)
    - max( min(entities), 20 )
  4. Potentially have 'range selector' buttons like we have for the x-axis, but then for various settings. For example button 1 could represent fixed range of 0 - 20, button 2 could be from 12-30, button 3 could be automatic computation, button 4 could be automatic computation with a certain entity excluded, etc.
dbuezas commented 1 year ago

Yes, I really wish plotly had that option. Here's the relevant request https://github.com/plotly/plotly.js/issues/887

One workaround is for me to update the ranges AFTER the user interaction, which will make the UI very jumpy

FrnchFrgg commented 1 year ago

I suppose that universal functions can handle at least a big part of this, provided one can disable the autoscaling feature.

dbuezas commented 1 year ago

I haven't tried it yet, but i think so, yes. Using vars or getFromConfig. If you try it please let me know how it went!

dbuezas commented 1 year ago

I couldn't resist:

type: custom:plotly-graph-dev
hours_to_show: current_week
entities:
  - entity: sensor.senseair_temperature
  - entity: sensor.durchgang_thermometer_temperature

layout:
  uirevision: $fn () => Math.random() // force rescale on scroll
  yaxis:
    range: |
        $fn ({ getFromConfig }) => {
          const all = getFromConfig("entities").flatMap(({ y }) => y);
          return [
            Math.max(12, Math.min(...all)), // cap to lowest value, but at least 12
            Math.min(18, Math.max(...all)), // cap to highest value, but at most 18
          ]
        }

This only works if you use a single yaxis.

You can also:

I tried this and it works, but the code gets too contrived to serve as an example.

dbuezas commented 1 year ago

Here the limits are 15 and 22:

Kapture 2023-02-01 at 22 13 59

It works but the jumps are very unpleasent

dbuezas commented 1 year ago

I'll study transitions a bit more. This has potential: Kapture 2023-02-01 at 22 34 38

Also for some custom made range selectors (the buttons on top to switch between say day and week=

Rvh91 commented 1 year ago

This looks great! it is exactly what i meant! I'll have to play around with the code you shared a bit to understand the syntax a bit better, but great work regardless! Instead of creating 'all', what would be the syntax to create a variable related to a specified list of entities?

I'm surprised by how fast you axii update btw. is this recorded from a dashboard in homeassistant? For me if i increase the timespan of the plot to more then a day (from lets say my default 2 hours) , for sure i'm waiting much longer (eg. 20-30s) before the graph is 'populated' on the new part of the time range. did you do anything clever with that? or should i just sample less often? (i now sample temperatures every 2mins)

dbuezas commented 1 year ago

Happy to hear! and even more happy that the universal functions cover this so nicely. I didn't have this in mind so good that @FrnchFrgg mentioned it.

Instead of creating 'all', what would be the syntax to create a variable related to a specified list of entities?

flatMap is equivalent to mapping followed by flattening (convert an array of arrays into a flat array). Instead of throwing all in the same bag, you can deal with each entity separately in each axis. The generic solution is too cryptic, and a tailored solution depends on how your yaml looks like :). I hope this example is good enough for you for the time being.

I'm surprised by how fast you axii update [...] For me if i increase the timespan of the plot to more then a day

yes, that's just too much data. (and I have a fast computer) You can try using statistics or the resample filter to help with that. I suggest you look at the auto mode for the statistics period too (it adapts the granularity of the data depending on the magnitude of the visible range, and it is configurable)

dbuezas commented 1 year ago

Transferred to discussion #233

Rvh91 commented 1 year ago

Been playing around with the statistics, speeds up the rendering significantly! thanks for the suggestion. It seems however as if the 'auto' period setting doesn't really use 100 data points (or are they shared between all entities?) When switching to a 7 day period, I get a very coarse trace (see below). Is this the expected behavior?

image

for the following yaml: `type: custom:plotly-graph entities:

dbuezas commented 1 year ago

Oh, you are right, I changed it a while back and it seems I forgot to update the readme. I'll fix the default behavior in the next release. In the meanwhile, you can configure at which zoom level each period is used (search for period: auto in the readme). Please use a code block for the yaml in posts:

```yaml
your_yaml: here
```
dbuezas commented 1 year ago

V3.3.0 has the corrected defaults for the auto period, minimum 100 datapoints at each level :)

dbuezas commented 1 year ago

Also, I see you are using the range selector. You may be interested in the custom one linked in the readme ( in the range selector section) it can also scroll and it is animated

Rvh91 commented 1 year ago

Thanks! I have noticed it working now. I'm a bit confused now by the documentation however. related to this block in the readme:

type: custom:plotly-graph
entities:
  - entity: sensor.temperature
    statistic: mean
    period:
      0m: 5minute
      100h: hour
      100d: day
      100w: week
      100M: month # note uppercase M for month. Lowercase are minutes

compared with this one which has a bit more comments:

type: custom:plotly-graph
entities:
  - entity: sensor.temperature
    statistic: mean
    period:
      0s: 5minute
      24h: hour # when the visible range is ≥ 1 day, use the `hour` period
      7d: day # from 7 days on, use `day`
      6M: week # from 6 months on, use weeks. Note Uppercase M! (lower case m means minutes)
      1y: month # from 1 year on, use `month

Am I right to assume that anywhere in between 100h and 99d, the statistics for 'day' wil be used. So actually in that case we would have 2376 samples rather than the 100 datapoints?

also, I initially though that this integration was computing the statistics, but from this it appears to me that plotly doesn't compute the statistics, but rather takes this from the HA recorder (which maybe only tracks statistics for the given periods (hour, day, week, month etc.)). Is that correct?

dbuezas commented 1 year ago

takes this from the HA recorder (which maybe only tracks statistics for the given periods (hour, day, week, month etc.)).

That's correct 👍

Am I right to assume that anywhere in between 100h and 99d, the statistics for 'day' wil be used. So actually in that case we would have 2376 samples rather than the 100 datapoints?

almost. The statistic for hours will be used (not days), and yes 2376 samples will be on screen. Potentially 23 more if you zoom out to almost but not quite 1000 days.

In that case you can just say: period: auto which sets that exact default

Rvh91 commented 1 year ago

In that case you can just say: period: auto which sets that exact default

Yes that was what I was using, I just wanted to understand what was happening. I now understand why it does not take exactly 100 samples in that default scenario, which is what I was expecting. I think my confusion was caused by the misunderstanding of how the statistics where 'computed'. Thanks for the great support by the way!

CendaL commented 1 year ago

fyi added rescaling to just visible area as comment in #233