lovelace-plotly-graph-card icon indicating copy to clipboard operation
lovelace-plotly-graph-card copied to clipboard

Feature request: programmable y axis limits

Open Rvh91 opened this issue 2 years ago • 1 comments

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 )
  1. 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.

Rvh91 avatar Jul 21 '22 12:07 Rvh91

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

dbuezas avatar Sep 18 '22 06:09 dbuezas

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

FrnchFrgg avatar Jan 31 '23 21:01 FrnchFrgg

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 avatar Jan 31 '23 22:01 dbuezas

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:

  • filter the y values by x[i] being inside getFromConfig('visible_range') (if you this to only be applied to what is currently visible)
  • put this in the default yaxes, and only take the entities whose yaxis index matches the one from "y" + path.match(/layout\.yaxis(.*)\..*/)[1];(if you have multiple yaxes and want to cap them independently)

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

dbuezas avatar Feb 01 '23 21:02 dbuezas

Here the limits are 15 and 22:

Kapture 2023-02-01 at 22 13 59

It works but the jumps are very unpleasent

dbuezas avatar Feb 01 '23 21:02 dbuezas

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=

dbuezas avatar Feb 01 '23 21:02 dbuezas

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)

Rvh91 avatar Feb 01 '23 22:02 Rvh91

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 avatar Feb 01 '23 22:02 dbuezas

Transferred to discussion #233

dbuezas avatar Feb 03 '23 22:02 dbuezas

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:

  • entity: weather.buienradar::temperature name: Buitentemperatuur line: width: 3
  • entity: sensor.bme280_temperature name: Office BME280 line: color: red
  • entity: sensor.scd41_temperature name: Bedroom SCD41 line: color: fuchsia width: 2
  • entity: sensor.shtc3_temperature_1 name: Office 1 SHTC3 line: color: darkred
  • entity: sensor.shtc3_temperature_2 name: Office 2 SHTC3 line: color: orangered
  • entity: sensor.temperature_shtc3_bathroom name: Bathroom SHTC3 line: color: MediumPurple
  • entity: sensor.temperature_shtc3_a name: Kitchen SHTC3 line: color: yellow
  • entity: sensor.temperature_shtc3_b name: Livingroom SHTC3 line: color: Palegoldenrod
  • entity: sensor.temperature_shtc3_c name: Babyroom SHTC3 line: color: springgreen hours_to_show: 5 title: Temperature refresh_interval: 1 defaults: entity: statistic: mean period: auto unit_of_measurement: °C show_value: true line: width: 1 layout: legend: orientation: h 'y': -0.6 height: 450 uirevision: $fn () => Math.random() // force rescale on scroll yaxis: range: | $fn ({ getFromConfig }) => { const all = getFromConfig("entities").flatMap(({ y }) => y); return [ Math.max(18, Math.min(...all)), // cap to lowest value, but at least 18 Math.min(22, Math.max(...all)), // cap to highest value, but at most 22 ] } xaxis: rangeselector: 'y': 1.05 x: -0.1 buttons: - count: 1 step: hour - count: 5 step: hour - count: 24 step: hour - count: 2 step: day - count: 7 step: day `

Rvh91 avatar Feb 04 '23 13:02 Rvh91

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 avatar Feb 04 '23 15:02 dbuezas

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

dbuezas avatar Feb 04 '23 19:02 dbuezas

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

dbuezas avatar Feb 04 '23 19:02 dbuezas

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?

Rvh91 avatar Feb 07 '23 10:02 Rvh91

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

dbuezas avatar Feb 07 '23 13:02 dbuezas

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!

Rvh91 avatar Feb 07 '23 13:02 Rvh91

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

CendaL avatar May 11 '23 18:05 CendaL