I haven’t used grafana for over a year but does this still work?
Yes, it works. At least on OH 3.4.4.
On 4.0.1 is doesn’t work, same hex code in the widget library, but when adding it, nothing happens
For others it works on OH 4.01.
Maybe try creating the widget manually by copying the YAML from the first post?
Okay so i just spent 2 hours sorting through this and finally found out what my problem was. I’m posting this here to hopefully save someone else the trouble.
When editing grafana.ini you need to remove the “;” from the beginning of each line that you change. Else the change WILL NOT apply. also dont forget to restart the Grafana service.
That’s why I like using vim editor as both the # and ; in the grafana.ini would have been blue coloured text meaning they are comments. If they are not comments then they are white text.
It makes it easier to look for un-commented statements.
I would like to share the following extension:
In addition to different time ranges, different grafana pages can be selected as well:
Code:
uid: Grafana_charts_with_timeranges_MikeTheTux
tags: []
props:
parameters:
- description: Title of the chart
label: Title
name: title
required: false
type: TEXT
- description: Grafana URL with "{period}" and "{panel}" placeholder. Example: "http://nas:3000/d-solo/VCS_VtN4k/openhab?orgId=1&{period}&panelId={panel}"
label: Grafana source URL
name: URL
required: true
type: TEXT
- default: from=now-6h&to=now,6h;from=now-12h&to=now,12h;from=now-1d&to=now,24h;from=now/d&to=now/d,Day;from=now-3d&to=now,Last 3 Days;from=now-7d&to=now,Last 7 Days;from=now-14d&to=now,Last 14 Days
description: List of time ranges (separated with semicolon). Example: "from=now-6h&to=now,-6h;from=now-1d/d&to=now-1d/d,yesterday" for past "6h" and "yesterday". First entry is default.
label: Grafana time range options
name: timerange
required: true
type: TEXT
- default: 27,PV Status;22,PV Forecast
description: List of panels (separated with semicolon). Example: "27,PV Status;22,PV Forecast" for two panels with ID "27" and "22". First entry is default.
label: Grafana Panel IDs
name: panel
required: true
type: TEXT
- description: Height of the Frame (empty = default)
label: Height
name: height
required: false
type: TEXT
parameterGroups: []
timestamp: Oct 29, 2023, 9:59:50 AM
component: f7-card
config:
title: =props.title
outline: true
style:
--f7-card-margin-horizontal: 10px
--f7-card-margin-vertical: 3px
--f7-card-padding-horizontal: 10px
--f7-card-padding-vertical: 100px
margin-top: 10px
margin-bottom: 10px
noShadow: false
border-radius: var(--f7-card-expandable-border-radius)
box-shadow: 5px 5px 10px 1px rgba(0,0,0,0.3)
slots:
default:
- component: oh-webframe-card
config:
borders: false
noBorder: false
noShadow: true
height: =props.height
src: =props.URL.replace('{period}', vars.selectedPeriod || [props.timerange.split(';')[0].split(',')[0]]).replace('{panel}', vars.selectedPanel || [props.panel.split(';')[0].split(',')[0]])
class:
- display-block
- component: f7-segmented
config:
round: false
outline: false
class:
- padding-bottom-half
style:
margin-left: 10px
margin-right: 10px
--f7-button-font-size: 14px
--f7-button-text-color: "=themeOptions.dark === 'light' ? 'black' : 'white'"
--f7-button-text-transform: none
--f7-button-border-radius: 4px
--f7-button-outline-border-width: 1px
--f7-button-font-weight: 300
--f7-button-padding-vertical: 0px
--f7-button-padding-horizontal: 0px
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-border-color: var(--f7-card-outline-border-color)
action: variable
actionVariable: selectedPeriod
actionVariableValue: =props.timerange.split(";")[loop.size].split(",")[0]
- component: f7-segmented
config:
round: false
outline: false
class:
- padding-bottom-half
style:
margin-left: 10px
margin-right: 10px
--f7-button-font-size: 14px
--f7-button-text-color: "=themeOptions.dark === 'light' ? 'black' : 'white'"
--f7-button-text-transform: none
--f7-button-border-radius: 4px
--f7-button-outline-border-width: 1px
--f7-button-font-weight: 300
--f7-button-padding-vertical: 0px
--f7-button-padding-horizontal: 0px
slots:
default:
- component: oh-repeater
config:
sourceType: range
for: size
fragment: true
slots:
default:
- component: oh-repeater
config:
fragment: true
for: panel
in: =[props.panel.split(";")[loop.size].split(",")[1]]
slots:
default:
- component: oh-button
config:
text: =loop.panel
fill: "=(([props.panel.split(';')[loop.size].split(',')[0]] == vars.selectedPanel) || (props.panel.split(';')[0].split(',')[1] === loop.panel) && !vars.selectedPanel) ? true : false"
round: false
outline: true
style:
--f7-button-border-color: var(--f7-card-outline-border-color)
action: variable
actionVariable: selectedPanel
actionVariableValue: =props.panel.split(";")[loop.size].split(",")[0]
Have fun!
I have also made my own adjustments to this widget by adding another row of buttons that allows going back several days/hours… but not changing the overall duration of the displayed range.
There are some additional tweaks to the styling for better visual integration in block layouts.
uid: vas_grafana_picker
tags: []
props:
parameters:
- description: Title of the chart
label: Title
name: title
required: false
type: TEXT
- description: URL to show in the frame
label: Source URL
name: URL
required: true
type: TEXT
- default: from=now-{fcount}h&to=now-{tcount}h;6h;6,from=now-{fcount}h&to=now-{tcount}h;12h;12,from=now-{fcount}h&to=now-{tcount}h;24h;24,from=now-{tcount}d/d&to=now-{tcount}d/d;Day;1,from=now-{fcount}d&to=now-{tcount}d;Last 3 Days;3,from=now-{fcount}d&to=now-{tcount}d;Last 7 Days;7,from=now-{fcount}d&to=now-{tcount}d;Last 14 Days;14
description: Comma-separated List of options. Example "from=now-6h&to=now;-6h,from=now-1d/d&to=now-1d/d;yesterday" for past "6h" and "yesterday". First entry is default.
label: Time range options
name: timerange
required: true
type: TEXT
- default: Previous,Now,Next
description: Labels for previous, now and next buttons as comma separated list
label: Second line buttons labels
name: prevNowNext
required: false
type: TEXT
- default: "0"
description: Index of default time range
label: Time range default
name: rangeDefault
required: false
- description: Height of the Frame (empty = default)
label: Height
name: height
required: false
type: TEXT
parameterGroups: []
timestamp: Nov 30, 2023, 12:20:38 AM
component: f7-card
config:
title: =props.title
outline: false
style:
--f7-card-margin-horizontal: 10px
--f7-card-margin-vertical: 10px
--f7-card-padding-horizontal: 10px
--f7-card-padding-vertical: 100px
margin-top: 10px
margin-bottom: 10px
padding-top: 1px
box-shadow: var(--f7-card-box-shadow)
slots:
default:
- component: oh-webframe-card
config:
borders: false
noBorder: false
noShadow: true
height: =props.height
src: = props.URL.replace('{period}', (vars.sP?.period || props.timerange.split(',')[props.rangeDefault].split(';')[0]) .replaceAll('{fcount}', (vars.sP?.count || props.timerange.split(',')[props.rangeDefault].split(';')[2])*((vars.pIdx||0)+1)) .replaceAll('{tcount}', (vars.sP?.count || props.timerange.split(',')[props.rangeDefault].split(';')[2])*(vars.pIdx||0)))
class:
- display-block
- component: f7-segmented
config:
round: false
outline: false
class:
- padding-bottom-half
style:
margin-left: 10px
margin-right: 10px
--f7-button-font-size: 14px
--f7-button-text-color: "=themeOptions.dark === 'light' ? 'black' : 'white'"
--f7-button-text-transform: none
--f7-button-border-radius: 4px
--f7-button-outline-border-width: 1px
--f7-button-font-weight: 300
--f7-button-padding-vertical: 0px
--f7-button-padding-horizontal: 0px
slots:
default:
- component: oh-button
config:
text: =props.prevNowNext.split(',')[0]
round: false
outline: true
style:
--f7-button-border-color: var(--f7-card-outline-border-color)
action: variable
actionVariable: pIdx
actionVariableValue: =(vars.pIdx || 0)+1
- component: oh-button
config:
text: =props.prevNowNext.split(',')[1]
fill: = (vars.pIdx == undefined || vars.pIdx == 0 )
round: false
outline: true
style:
--f7-button-border-color: var(--f7-card-outline-border-color)
action: variable
actionVariable: pIdx
actionVariableValue: 0
- component: oh-button
config:
text: =props.prevNowNext.split(',')[2]
round: false
outline: true
disabled: = (vars.pIdx == undefined || vars.pIdx == 0 )
style:
--f7-button-border-color: var(--f7-card-outline-border-color)
action: variable
actionVariable: pIdx
actionVariableValue: =(vars.pIdx || 0)-1
- component: f7-segmented
config:
round: false
outline: false
class:
- padding-bottom-half
style:
margin-left: 10px
margin-right: 10px
--f7-button-font-size: 14px
--f7-button-text-color: "=themeOptions.dark === 'light' ? 'black' : 'white'"
--f7-button-text-transform: none
--f7-button-border-radius: 4px
--f7-button-outline-border-width: 1px
--f7-button-font-weight: 300
--f7-button-padding-vertical: 0px
--f7-button-padding-horizontal: 0px
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: "=((loop.size == vars.sP?.index) || loop.size == (props.rangeDefault || 0) && !vars.sP) ? true : false"
round: false
outline: true
style:
--f7-button-border-color: var(--f7-card-outline-border-color)
action: variable
actionVariable: sP
actionVariableValue: "= {'period': props.timerange.split(',')[loop.size].split(';')[0], 'count': props.timerange.split(',')[loop.size].split(';')[2], index: loop.size }"
Here’s what it looks like in context:
Hi @tarag
I’m working on the same widget with a similar concept, only I’m trying to use arrows to go forwards/backwards in time.
I’m trying to use your code as a startingpoint, but I’m struggling to understand 2 things:
-
What is the purpose of “rangeDefault” and its use?
-
When generating the URL, how is this line read:
vars.sP?.count || props.timerange.split(',')[props.rangeDefault].split(';')[2])
in particular what do the OR in this mean, is it bit-wise, or in case sP?.count is not specified then do the split thing?
Also how is the code on the right side of the OR statement read? I dont understand
[props.rangeDefault].split(';')[2])
when rangeDefault is set to “0” in the props
Thank you
Sorry I did not explain much. The computation of the url for the web card is a 2 step process.
- Computing the
from
andto
query string parameters using{fcount}
and/or{tcount}
tags in the selected timerange prop by multiplying thecount
parameter (3rd of each timerange), by the current position (0 upon display) - Replacing the
{period}
tag in the URL prop by the computed timerange in the previous step.
The rangeDefault
prop, should rather be called defaultRange. It is the 0-starting index of the default selected range among all timerange
prop values. I did not want it to be 0 as in the topic widget version (bu the default value of rangeDefaut in 0…). This is written in the prop description
vars.sP
is the selected period, an object with period
and count
properties, when initialised. But since one can’t initialise vars in widgets, I have to take this case into account.
So the vars.sP?.count || props.timerange.split(',')[props.rangeDefault].split(';')[2])
means:
if vars.sP?.count
is defined (when at least one of the range buttons has been selected by the user), use it, else use the count value from the timerange
property, by taking the field at the third position (index 2) of the range at the rangeDefault
index.
This line is buggy if count is 0, but there is no point in having count at 0 so I left it this way. Not being able to initialise vars in widgets is a real pain…
And it does work!
…with some nice query btw to get historical values one year before current ones
Awesome - thanks for your explanation, that was really helpful!
I’ve finished my widget with the help of your inputs, if I want to share the functioning of my widget, with a nice animation like yours, how can I do this, as it explains much better than trying to describe in words.
On my Mac, I record part of the screen using built-in function via Cmd-Shift-5, and then use Gifski app to convert to animated gif.
Cheers.
All done! Here is my widget.
Lots of thanks to maxmaximax for making the base widget and tarag for getting me to the finish line.
Like tarags this widget is made to handle historic data that is preformatted in duration for days, weeks, months and years, and you can go backwards/forwards in those steps by using the arrows.
It requires that you have a single grafana dashboard with 4 panels, one for each duration, and that the data in each panel is controlled by setting the absolute timerange. It does not matter what the range is set to in the dashboard - the widget takes care of setting the time ranges.
Here is the code:
Remember to change the panel numbers in the rangePanels property to match those of your grafana dashboard!
uid: Grafana chart with timeranges v2
tags: []
props:
parameters:
- description: Title of the chart
label: Title
name: title
required: false
type: TEXT
- default: http://<your.ip.here>/<your.base.url.to.grafana.dashboard.here.ends.with.something.like."orgId=1&refresh=5m".{period}
description: URL to show in the frame
label: Source URL
name: URL
required: true
type: TEXT
- default: from=now%2Fd-{frange}h&to=now%2Fd-{trange}h&viewPanel=10&kiosk;Day;24,from=now%2Fw-{frange}d&to=now%2Fw-{trange}d&viewPanel=9&kiosk;Week;7,from=now%2FM-{trange}M-1d&to=now%2FM-{trange}M&viewPanel=11&kiosk;Month;1,from=now%2Fy-{frange}M&to=now%2Fy-{trange}M&viewPanel=12&kiosk;Year;12
description: Comma-separated List of available panels and timeranges. Example "viewPanel=3&kiosk;Day,viewPanel=6&kiosk;Week," for past "Day" and "Week". First entry is default.
label: Available Range Panels
name: rangePanels
required: true
type: TEXT
- default: "0"
description: Index of default time range
label: Time range default
name: rangeDefault
required: false
- description: Height of the Frame (empty = default)
label: Height
name: height
required: false
type: TEXT
parameterGroups: []
timestamp: Dec 6, 2023, 9:00:26 PM
component: f7-card
config:
style:
--f7-card-margin-horizontal: 0px
--f7-card-margin-vertical: 5px
--f7-card-padding-horizontal: 0px
--f7-card-padding-vertical: 0px
box-shadow: 5px 5px 10px 1px rgba(0,0,0,0.3)
margin-bottom: 5px
margin-top: 5px
noShadow: false
padding-right: 0px
padding-left: 0px
padding-top: 0px
padding-bottom: 0px
title: =props.title
slots:
default:
- component: f7-block
config:
style:
display: flex
justify-content: center
align-items: center
padding-right: 0px
padding-left: 0px
padding-top: 0px
padding-bottom: 0px
slots:
default:
- component: oh-button
config:
action: variable
actionVariable: sP
actionVariableValue: = {'period':vars.sP.period, 'count':vars.sP.count, index:vars.sP.index, pIdx:((vars.sP.pIdx || 0)+1) }
iconMaterial: chevron_left
iconSize: 30
style:
display: flex
justify-content: center
align-self: center
color: black
width: 3%
padding-right: 0px
padding-left: 0px
padding-top: 0px
padding-bottom: 0px
margin-right: 0px
- component: f7-block
config:
borders: false
noBorder: false
noShadow: true
style:
width: 155%
margin-left: 0px
margin-right: 0px
padding-right: 0px
padding-left: 0px
padding-top: 0px
padding-bottom: 0px
slots:
default:
- component: oh-webframe-card
config:
height: =props.height
src: = props.URL.replace('{period}', (vars.sP?.period || props.rangePanels.split(',')[props.rangeDefault].split(';')[0]) .replaceAll('{frange}', ((((vars.sP?.count || 0)*(vars.sP.pIdx || 0))+1) || 1)) .replaceAll('{trange}', (((vars.sP?.count || 0)*(vars.sP.pIdx || 0)) || 0)))
borders: false
noBorder: false
noShadow: true
- component: oh-button
config:
visible: =(vars.sP.pIdx > 0) == true
action: variable
actionVariable: sP
actionVariableValue: = {'period':vars.sP.period, 'count':vars.sP.count, index:vars.sP.index, pIdx:((vars.sP.pIdx || 0)-1) }
iconMaterial: chevron_right
iconSize: 30
style:
display: flex
justify-content: center
align-self: center
color: black
width: 3%
padding-right: 0px
padding-left: 0px
padding-top: 0px
padding-bottom: 0px
margin-left: 0px
margin-right: 0px
- component: f7-segmented
config:
class:
- padding-bottom-half
outline: false
round: false
style:
--f7-button-border-radius: 4px
--f7-button-font-size: 14px
--f7-button-font-weight: 300
--f7-button-outline-border-width: 1px
--f7-button-padding-horizontal: 0px
--f7-button-padding-vertical: 0px
--f7-button-text-color: "=themeOptions.dark === 'light' ? 'black' : 'white'"
--f7-button-text-transform: none
margin-left: 10px
margin-right: 10px
slots:
default:
- component: oh-repeater
config:
for: size
fragment: true
sourceType: range
slots:
default:
- component: oh-repeater
config:
for: period
fragment: true
in: =[props.rangePanels.split(",")[loop.size].split(";")[1]]
slots:
default:
- component: oh-button
config:
action: variable
actionVariable: sP
actionVariableValue: = {'period':props.rangePanels.split(',')[loop.size].split(';')[0], 'count':props.rangePanels.split(',')[loop.size].split(';')[2], index:loop.size, pIdx:0 }
fill: "=((loop.size == vars.sP?.index) || loop.size == (props.rangeDefault || 0) && !vars.sP) ? true : false"
outline: true
round: false
style:
--f7-button-border-color: var(--f7-card-outline-border-color)
text: =loop.period
Hope this inspires someone, as tarags and maxmaximaxs did me
Great to see how you improve the widget further!
Sad I cannot dedicate more time to this subject right now…
maxmaximax
Hello everyone,
I’m using InfluxDB and Grafana on my QNAP NAS (running in containers). This NAS is on the same local network as my Raspberry Pi 5 running openHAB. I’ve managed to get the Grafana widget working locally within my network after following the instructions and making some adjustments to the grafana.ini
file.
However, when I try to access openHAB via the myopenHAB cloud service, the content of the Grafana widget is not displayed. I suspect this is related to accessing the widget from a HTTPS context (myopenHAB) but the Grafana instance being accessible via HTTP on the local network.
Has anyone successfully managed to display a Grafana widget in openHAB when accessing it through the myopenHAB cloud service? If so, could you please share your configuration and any specific steps you took to make it work?
Any help or suggestions would be greatly appreciated!
Thanks in advance.