OH3 widget: Grafana chart with time ranges

With great support by @BobMiles, @RGroll and @ysc I managed to create a widget to embed grafana charts with configurable time ranges.

If interested, you can find the whole discussion here.

Grafana chart widget

Grafana widget props

Source URL:
Make sure you insert {period} inside the URL where you would want to have the time range pasted.

Example:

http://192.168.0.13:3000/d-solo/4OdYJNbMk/energie-pv-haus?orgId=1&refresh=1m&from=now-{period}&to=now&panelId=2

Full widget YAML (updated 2021-04-05: segmented “strong” removed):

uid: Grafana chart with timeranges
tags: []
props:
  parameters:
    - description: Title of the chart
      label: Title
      name: title
      required: false
      type: TEXT
    - description: Footer of the chart
      label: Footer
      name: footer
      required: false
      type: TEXT
    - description: URL to show in the frame
      label: Source URL
      name: URL
      required: true
      type: TEXT
    - description: Comma-separated list of options. Use value=label format (e.g. 1d=1 day) to provide a label different than the option. Minimum 1 entry required. The first entry is the default timerange.
      label: Time range options
      name: timerange
      required: true
      type: TEXT
    - description: Height of the Frame (empty = default)
      label: Height
      name: height
      required: false
      type: TEXT
  parameterGroups: []
timestamp: Mar 21, 2021, 5:17:50 PM
component: f7-card-content
config:
  style:
    --f7-card-margin-horizontal: 0px
    --f7-card-margin-vertical: 3px
    --f7-card-content-padding-horizontal: 10px
    --f7-card-content-padding-vertical: 10px
slots:
  default:
    - component: oh-webframe-card
      config:
        title: =props.title
        borders: false
        noBorder: false
        outline: true
        height: =props.height
        src: =props.URL.replace('{period}', vars.selectedPeriod || [props.timerange.split(',')[0].split('=')[0]])
        footer: =props.footer
        class:
          - display-block
    - component: f7-segmented
      config:
        round: false
        outline: false
        style:
          --f7-button-bg-color: rgba(255, 255, 255, 0.05)
      slots:
        default:
          - component: oh-repeater
            config:
              sourceType: range
              for: size
              fragment: true
            slots:
              default:
                - component: oh-repeater
                  config:
                    fragment: true
                    for: period
                    in: =[props.timerange.split(",")[loop.size].split("=")[1]]
                  slots:
                    default:
                      - component: oh-button
                        config:
                          text: =loop.period
                          fill: "=(([props.timerange.split(',')[loop.size].split('=')[0]] == vars.selectedPeriod) || (props.timerange.split(',')[0].split('=')[1] === loop.period) && !vars.selectedPeriod) ? true : false"
                          round: false
                          outline: true
                          style:
                            --f7-button-font-size: 14px
                            --f7-button-font-weight: 300
                            --f7-button-text-transform: none
                            --f7-button-border-width: 1px
                            --f7-button-text-color: white
                            --f7-button-border-color: rgba(255,255,255,.15)
                            --f7-button-padding-vertical: 0px
                            --f7-button-padding-horizontal: 0px
                            --f7-button-fill-hover-bg-color: rgba(var(--f7-theme-color-rgb), 1)
                          action: variable
                          actionVariable: selectedPeriod
                          actionVariableValue: =props.timerange.split(",")[loop.size].split("=")[0]

Hope some of you find it useful :slightly_smiling_face:

12 Likes

Hello Marcus
First of all, thank you for this nite idea and I would love to integrate this feature in my UI. But it seems that I have trouble to access the given URL:

If I click the button then Grafana opens in a new window with the correct time range. Did you have a similar problem?

In the mobile version (iOS) I don’t even get the browser warning and everything keeps empty . Any idea?

Best regards
-Prom

It works for me on Win10/Chrome, iOS for iPad and iOS for iPhone.
It behaves exactly as shown in the first post of this thread.

I’m sorry, but I cannot help you.

I tried Chrome as well but with no luck. May I ask you for your setup?
I’m running both OH3 and Grafana&InfluxDB on the same Raspberry Pi 4 (installed via openHABian). My UI-clients are actually all mobiles (iPhones) and iPads in my family. Using Firefox and Chrome is only for configuring and testing.
I used exactly your YAML from the first posting.

I played around a little and I figured if I replace in your YAML in line 42:

src: =props.URL.replace('{period}', vars.selectedPeriod || [props.timerange.split(',')[0].split('=')[0]])

with:
src: http://leo.org

and it works. Meaning I see the Leo-page in the webframe. But if I try:
src: http://google.com or src: http://google.de

I get the same error message than above. Can someone explain this to me please!

well, try this… if not done already…

Adapt grafana.ini

[security]
# set to true if you want to allow browsers to render Grafana in a <frame>, <iframe>, <embed> or <object>. default is false.
allow_embedding = true

You propobly not talking about the grafana.ini in here?

openhabian@openhabian:/etc/grafana $ ls -l
-rw-r----- 1 root grafana 33282 Feb 10 19:02 grafana.ini

because such an entry is not inside. But I think I’m facing here a different problem because even in a standard oh-webframe-card my URL won’t show up. I will start a new topic but thank you for your widget anyway :slight_smile:

Yes, this is the file I’m talking about - and I’m pretty sure this is the solution to your problem.

I need to stress that I’m not on a openhabian deployment. I did a manual install of openHAB3, Grafana and InfluxDB.

The file grafana.ini has a section “Security” starting in line 175 (this may vary a bit depending on how many lines you added above).

same here

You saved my day!!! Thank you, your are right, this parameter did the trick. Maybe I was blessed with blindness when I check the Ini-file yesterday :see_no_evil:

Hello Marcus,

…wow, that was EXACTLY what I was searching for! Thank you (and the supprt as well) so much! Great, it worked instantly! And the best part of it: I learned a couple of things, which I didn´t understand until now :slight_smile:

Just a minimal addition: I added props for the height of the frame, cause some of my dashboards are … lets say “very rich” and therefore very tall.

So cheers and thank you for sharing!

Glad you find it useful.

Well, I learned a lot myself from the very kind community here.

May I kindly ask you to share your YAML - I’m planning to make a few small updates to the code in the first post anyway, so why not add your enhancements as well :slightly_smiling_face:

Sure! Here you go - just a minimal addition:


-uid: Grafana chart with timeranges
tags: []
props:
  parameters:

    - description: Height of the Frame
      label: Height
      name: height
      required: true
      type: TEXT

...

slots:
  default:
    - component: oh-webframe-card
      config: 
...
        
        height: =props.height

…or the whole thing:


uid: Grafana chart with timeranges
tags: []
props:
  parameters:
    - description: Title of the chart
      label: Title
      name: title
      required: false
      type: TEXT
    - description: Footer of the chart
      label: Footer
      name: footer
      required: false
      type: TEXT
    - description: URL to show in the frame
      label: Source URL
      name: URL
      required: true
      type: TEXT
    - description: Comma-separated list of options. Use value=label format (e.g. 1d=1 day) to provide a label different than the option. Minimum 1 entry required. The first entry is the default timerange.
      label: Time range options
      name: timerange
      required: true
      type: TEXT
    - description: Height of the Frame
      label: Height
      name: height
      required: true
      type: TEXT
  parameterGroups: []
timestamp: Feb 20, 2021, 10:28:57 PM
component: f7-card-content
config:
  style:
    --f7-card-margin-horizontal: 0px
    --f7-card-margin-vertical: 3px
    --f7-card-content-padding-horizontal: 10px
    --f7-card-content-padding-vertical: 10px
slots:
  default:
    - component: oh-webframe-card
      config:
        title: =props.title
        borders: false
        noBorder: false
        outline: true
        src: =props.URL.replace('{period}', vars.selectedPeriod || [props.timerange.split(',')[0].split('=')[0]])
        footer: =props.footer
        height: =props.height
        class:
          - display-block
    - component: f7-segmented
      config:
        round: false
        outline: false
        class:
          - segmented-strong
        style:
          --f7-segmented-strong-padding: 0px
          --f7-segmented-strong-between-buttons: 5px
          --f7-segmented-strong-button-font-weight: 300
          --f7-segmented-strong-bg-color: rgba(255, 255, 255, 0.05)
          --f7-segmented-strong-button-hover-bg-color: rgba(255, 255, 255, 0.07)
      slots:![image|620x500](upload://3OrN1UdDsF5brsM96bzhMkQPj6s.png) 
        default:
          - component: oh-repeater
            config:
              sourceType: range
              for: size
              fragment: true
            slots:
              default:
                - component: oh-repeater
                  config:
                    fragment: true
                    for: period
                    in: =[props.timerange.split(",")[loop.size].split("=")[1]]
                  slots:
                    default:
                      - component: oh-button
                        config:
                          text: =loop.period
                          fill: "=(([props.timerange.split(',')[loop.size].split('=')[0]] == vars.selectedPeriod) || (props.timerange.split(',')[0].split('=')[1] === loop.period) && !vars.selectedPeriod) ? true : false"
                          round: false
                          outline: false
                          style:
                            --f7-button-font-size: 12px
                            --f7-button-font-weight: 300
                            --f7-button-border-width: 1px
                            --f7-button-text-color: rgb(255, 255, 255)
                            --f7-button-border-color: rgba(255,255,255,.15)
                            --f7-button-border-radius: 5px
                          action: variable
                          actionVariable: selectedPeriod
                          actionVariableValue: =props.timerange.split(",")[loop.size].split("=")[0]

Thats it :slight_smile:

Hi Marcus,

Thanks a lot for sharing this widget, works great! I’m a beginner at the CSS stuff but with help from various examples in the forum I was able to put together a user interface for my iPad and I adapted your widget for this as well:


The only problem I’m having now is the orange ‘Active’ color for the segmented buttons doesn’t really fit with the rest of the interface… :smiley:

After using the developer tools in the browser I think the orange is the ‘Template color’ but I can’t figure out how to change it. I want to see if I can use the segmented button in more places in the interface but then I need to be able to change the active color.

I tried using
--f7-segmented-strong-button-active-bg-color: "#A1A1A1"
but that doesn’t seem to work.

Do you have any idea how to change the active color for the segmented buttons?

I’m not a CSS expert either - mostly using the default theme colors.
I will try a few things - let’s see…

@ysc once copied me the following which I found to be very useful…

try:

--f7-button-fill-bg-color: blue

BTW I decided not to use segmented “strong” any longer - I just updated the YAML in the first post.

Thanks for the link to the OH color themes stuff. I obviously need to spend a lot more time on this… I did find the Framework7 button documentation but didn’t connect the dots to use the button-fill color instead of the active color

–f7-button-fill-bg-color: “#A1A1A1
–f7-button-fill-text-color: ‘#353535

did the trick, it’s working now! :partying_face: :smiley: The ‘regular’ segmented looks much nicer, using that too now:

Thanks for the help and again for sharing the widget itself, really like the convenient integration with Grafana.