The log is flodded with these messages:
It seem that I’m unable to retrieve the code from the webpage. Does the jsondb part of that widget help you?
The log is flodded with these messages:
It seem that I’m unable to retrieve the code from the webpage. Does the jsondb part of that widget help you?
Those are standard debug messages for the oh-context
. That just shows that part is working correctly, and there must be an error somewhere else.
Sure, that’s just a format change from the yaml, so that will be helpful.
"semanticHomeMenu_Weather_modified": {
"class": "org.openhab.core.ui.components.RootUIComponent",
"value": {
"uid": "semanticHomeMenu_Weather_modified",
"tags": [
"v0.1"
],
"props": {},
"timestamp": "Mar 24, 2025, 5:07:47 PM",
"component": "f7-card",
"config": {
"style": {
"border-radius": "10px",
"margin-left": "5px",
"margin-right": "5px",
"margin-top": "60px",
"min-width": "360px"
},
"stylesheet": ".forecast-grid {\n display: grid;\n grid-template-columns: 30px 30px 50px 1fr 50px;\n width: 100%;\n column-gap: 20px;\n} .hourly-forecast-grid {\n display: grid;\n grid-template-rows: 20px 20px 20px;\n width: 100%;\n column-gap: 20px;\n grid-auto-flow: column;\n justify-items: center;\n} .today-grid {\n display: grid;\n grid-template:\n \"station icon l_minmax minmax\" 20px\n \"temp icon l_wind wind\" 20px\n \"temp icon l_rain rain\" 20px\n \"temp icon a a\" 20px\n \"temp condition a a\" 20px / 80px minmax(130px, 1fr) 140px 80px;\n align-items: stretch;\n justify-items: stretch;\n padding: 20px 20px 10px 20px;\n column-gap: 16px;\n} .today-grid \u003e .label {\n text-align: right;\n font-size: 14px;\n} .today-grid \u003e .value {\n text-align: left;\n font-size: 14px;\n}\n"
},
"slots": {
"default": [
{
"component": "div",
"config": {
"class": "today-grid"
},
"slots": {
"default": [
{
"component": "Label",
"config": {
"style": {
"font-size": "16px",
"grid-area": "station"
},
"text": "\u003ditems.Localweatherandforecast_StationName.state"
}
},
{
"component": "f7-icon",
"config": {
"f7": "\u003d(items[\u0027OneCallAPIweatherandforecast_Current_Iconid\u0027].state \u003d\u003d\u003d \u002701d\u0027) ? \u0027sun_max\u0027 : (items[\u0027OneCallAPIweatherandforecast_Current_Iconid\u0027].state \u003d\u003d\u003d \u002701n\u0027) ? \u0027moon_stars\u0027 : (items[\u0027OneCallAPIweatherandforecast_Current_Iconid\u0027].state \u003d\u003d\u003d \u002702d\u0027) ? \u0027cloud_sun\u0027 : (items[\u0027OneCallAPIweatherandforecast_Current_Iconid\u0027].state \u003d\u003d\u003d \u002702n\u0027) ? \u0027cloud_moon\u0027 : (items[\u0027OneCallAPIweatherandforecast_Current_Iconid\u0027].state \u003d\u003d\u003d \u002703d\u0027) ? \u0027cloud\u0027 : (items[\u0027OneCallAPIweatherandforecast_Current_Iconid\u0027].state \u003d\u003d\u003d \u002703n\u0027) ? \u0027cloud\u0027 : (items[\u0027OneCallAPIweatherandforecast_Current_Iconid\u0027].state \u003d\u003d\u003d \u002704d\u0027) ? \u0027cloud\u0027 : (items[\u0027OneCallAPIweatherandforecast_Current_Iconid\u0027].state \u003d\u003d\u003d \u002704n\u0027) ? \u0027cloud\u0027 : (items[\u0027OneCallAPIweatherandforecast_Current_Iconid\u0027].state \u003d\u003d\u003d \u002709d\u0027) ? \u0027cloud_heavyrain\u0027 : (items[\u0027OneCallAPIweatherandforecast_Current_Iconid\u0027].state \u003d\u003d\u003d \u002709n\u0027) ? \u0027cloud_heavyrain\u0027 : (items[\u0027OneCallAPIweatherandforecast_Current_Iconid\u0027].state \u003d\u003d\u003d \u002710d\u0027) ? \u0027cloud_sun_rain\u0027 : (items[\u0027OneCallAPIweatherandforecast_Current_Iconid\u0027].state \u003d\u003d\u003d \u002710n\u0027) ? \u0027cloud_moon_rain\u0027 : (items[\u0027OneCallAPIweatherandforecast_Current_Iconid\u0027].state \u003d\u003d\u003d \u002711d\u0027) ? \u0027cloud_sun_bolt\u0027 : (items[\u0027OneCallAPIweatherandforecast_Current_Iconid\u0027].state \u003d\u003d\u003d \u002711n\u0027) ? \u0027cloud_moon_bolt\u0027 : (items[\u0027OneCallAPIweatherandforecast_Current_Iconid\u0027].state \u003d\u003d\u003d \u002713d\u0027) ? \u0027cloud_snow\u0027 : (items[\u0027OneCallAPIweatherandforecast_Current_Iconid\u0027].state \u003d\u003d\u003d \u002713n\u0027) ? \u0027cloud_snow\u0027 : (items[\u0027OneCallAPIweatherandforecast_Current_Iconid\u0027].state \u003d\u003d\u003d \u002750d\u0027) ? \u0027cloud_fog\u0027 : (items[\u0027OneCallAPIweatherandforecast_Current_Iconid\u0027].state \u003d\u003d\u003d \u002750n\u0027) ? \u0027cloud_fog\u0027 : \u0027?\u0027",
"size": 80.0,
"style": {
"grid-area": "icon",
"width": "100%"
}
}
},
{
"component": "Label",
"config": {
"style": {
"font-size": "50px",
"grid-area": "temp"
},
"text": "\u003dNumber.parseInt(items.OneCallAPIweatherandforecast_Current_Temperature.state)+\"°\""
}
},
{
"component": "Label",
"config": {
"class": "label",
"style": {
"grid-area": "l_minmax"
},
"text": "Temperaturen:"
}
},
{
"component": "Label",
"config": {
"class": "value",
"style": {
"grid-area": "minmax"
},
"text": "\u003dNumber.parseInt(items.OneCallAPIweatherandforecast_ForecastDay0_Mintemperature.state) + \" °C / \" + Number.parseInt(items.OneCallAPIweatherandforecast_ForecastDay0_Maxtemperature.state) + \u0027 °C\u0027"
}
},
{
"component": "Label",
"config": {
"style": {
"grid-area": "condition",
"text-align": "center",
"width": "100%"
},
"text": "\u003ditems.OneCallAPIweatherandforecast_Current_Condition.state"
}
},
{
"component": "Label",
"config": {
"class": "label",
"style": {
"grid-area": "l_wind"
},
"text": "Windgeschwindigkeit:"
}
},
{
"component": "Label",
"config": {
"class": "value",
"style": {
"grid-area": "wind"
},
"text": "\u003d@\u0027OneCallAPIweatherandforecast_Current_Wind_Speed\u0027"
}
},
{
"component": "Label",
"config": {
"class": "label",
"style": {
"grid-area": "l_rain"
},
"text": "Niederschlag:"
}
},
{
"component": "Label",
"config": {
"class": "value",
"style": {
"grid-area": "rain"
},
"text": "\u003d@\u0027OneCallAPIweatherandforecast_Current_Rain\u0027"
}
}
]
}
},
{
"component": "f7-block",
"config": {
"class": [
"no-padding"
],
"style": {
"margin-top": "10px",
"overflow": "hidden",
"width": "100%"
}
},
"slots": {
"default": [
{
"component": "f7-card-footer",
"config": {},
"slots": {
"default": [
{
"component": "oh-repeater",
"config": {
"containerClasses": "hourly-forecast-grid",
"for": "witem",
"rangeStart": 1.0,
"rangeStop": 6.0,
"sourceType": "range"
},
"slots": {
"default": [
{
"component": "Label",
"config": {
"style": {
"font-size": "14px"
},
"text": "\u003ddayjs(items[\u0027OneCallAPIweatherandforecast_ForecastHours0\u0027+loop.witem+\u0027_Timestamp\u0027].state).format(\"HH:mm\")"
}
},
{
"component": "f7-icon",
"config": {
"f7": "\u003d(items[\u0027OneCallAPIweatherandforecast_ForecastHours0\u0027+loop.witem+\u0027_Iconid\u0027].state \u003d\u003d\u003d \u002701d\u0027) ? \u0027sun_max\u0027 : (items[\u0027OneCallAPIweatherandforecast_ForecastHours0\u0027+loop.witem+\u0027_Iconid\u0027].state \u003d\u003d\u003d \u002701n\u0027) ? \u0027moon_stars\u0027 : (items[\u0027OneCallAPIweatherandforecast_ForecastHours0\u0027+loop.witem+\u0027_Iconid\u0027].state \u003d\u003d\u003d \u002702d\u0027) ? \u0027cloud_sun\u0027 : (items[\u0027OneCallAPIweatherandforecast_ForecastHours0\u0027+loop.witem+\u0027_Iconid\u0027].state \u003d\u003d\u003d \u002702n\u0027) ? \u0027cloud_moon\u0027 : (items[\u0027OneCallAPIweatherandforecast_ForecastHours0\u0027+loop.witem+\u0027_Iconid\u0027].state \u003d\u003d\u003d \u002703d\u0027) ? \u0027cloud\u0027 : (items[\u0027OneCallAPIweatherandforecast_ForecastHours0\u0027+loop.witem+\u0027_Iconid\u0027].state \u003d\u003d\u003d \u002703n\u0027) ? \u0027cloud\u0027 : (items[\u0027OneCallAPIweatherandforecast_ForecastHours0\u0027+loop.witem+\u0027_Iconid\u0027].state \u003d\u003d\u003d \u002704d\u0027) ? \u0027cloud\u0027 : (items[\u0027OneCallAPIweatherandforecast_ForecastHours0\u0027+loop.witem+\u0027_Iconid\u0027].state \u003d\u003d\u003d \u002704n\u0027) ? \u0027cloud\u0027 : (items[\u0027OneCallAPIweatherandforecast_ForecastHours0\u0027+loop.witem+\u0027_Iconid\u0027].state \u003d\u003d\u003d \u002709d\u0027) ? \u0027cloud_heavyrain\u0027 : (items[\u0027OneCallAPIweatherandforecast_ForecastHours0\u0027+loop.witem+\u0027_Iconid\u0027].state \u003d\u003d\u003d \u002709n\u0027) ? \u0027cloud_heavyrain\u0027 : (items[\u0027OneCallAPIweatherandforecast_ForecastHours0\u0027+loop.witem+\u0027_Iconid\u0027].state \u003d\u003d\u003d \u002710d\u0027) ? \u0027cloud_sun_rain\u0027 : (items[\u0027OneCallAPIweatherandforecast_ForecastHours0\u0027+loop.witem+\u0027_Iconid\u0027].state \u003d\u003d\u003d \u002710n\u0027) ? \u0027cloud_moon_rain\u0027 : (items[\u0027OneCallAPIweatherandforecast_ForecastHours0\u0027+loop.witem+\u0027_Iconid\u0027].state \u003d\u003d\u003d \u002711d\u0027) ? \u0027cloud_sun_bolt\u0027 : (items[\u0027OneCallAPIweatherandforecast_ForecastHours0\u0027+loop.witem+\u0027_Iconid\u0027].state \u003d\u003d\u003d \u002711n\u0027) ? \u0027cloud_moon_bolt\u0027 : (items[\u0027OneCallAPIweatherandforecast_ForecastHours0\u0027+loop.witem+\u0027_Iconid\u0027].state \u003d\u003d\u003d \u002713d\u0027) ? \u0027cloud_snow\u0027 : (items[\u0027OneCallAPIweatherandforecast_ForecastHours0\u0027+loop.witem+\u0027_Iconid\u0027].state \u003d\u003d\u003d \u002713n\u0027) ? \u0027cloud_snow\u0027 : (items[\u0027OneCallAPIweatherandforecast_ForecastHours0\u0027+loop.witem+\u0027_Iconid\u0027].state \u003d\u003d\u003d \u002750d\u0027) ? \u0027cloud_fog\u0027 : (items[\u0027OneCallAPIweatherandforecast_ForecastHours0\u0027+loop.witem+\u0027_Iconid\u0027].state \u003d\u003d\u003d \u002750n\u0027) ? \u0027cloud_fog\u0027 : \u0027?\u0027",
"size": 25.0
}
},
{
"component": "Label",
"config": {
"style": {
"font-size": "14px"
},
"text": "\u003dNumber.parseInt(items[\u0027OneCallAPIweatherandforecast_ForecastHours0\u0027+loop.witem+\u0027_Temperature\u0027].state)+\" °C\""
}
}
]
}
}
]
}
}
]
}
},
{
"component": "f7-block",
"config": {
"class": [
"no-padding"
],
"style": {
"margin-top": "10px",
"overflow": "hidden",
"width": "100%"
}
},
"slots": {
"default": [
{
"component": "f7-card-footer",
"config": {},
"slots": {
"default": [
{
"component": "oh-repeater",
"config": {
"containerClasses": "forecast-grid",
"for": "witem",
"rangeStart": 1.0,
"rangeStop": 5.0,
"sourceType": "range"
},
"slots": {
"default": [
{
"component": "Label",
"config": {
"style": {
"font-size": "14px"
},
"text": "\u003ddayjs().add(loop.witem,\u0027day\u0027).startOf(\u0027day\u0027).format(\u0027dd\u0027)"
}
},
{
"component": "f7-icon",
"config": {
"f7": "\u003d(items[\u0027OneCallAPIweatherandforecast_ForecastDay\u0027+loop.witem+\u0027_Iconid\u0027].state \u003d\u003d\u003d \u002701d\u0027) ? \u0027sun_max\u0027 : (items[\u0027OneCallAPIweatherandforecast_ForecastDay\u0027+loop.witem+\u0027_Iconid\u0027].state \u003d\u003d\u003d \u002701n\u0027) ? \u0027moon_stars\u0027 : (items[\u0027OneCallAPIweatherandforecast_ForecastDay\u0027+loop.witem+\u0027_Iconid\u0027].state \u003d\u003d\u003d \u002702d\u0027) ? \u0027cloud_sun\u0027 : (items[\u0027OneCallAPIweatherandforecast_ForecastDay\u0027+loop.witem+\u0027_Iconid\u0027].state \u003d\u003d\u003d \u002702n\u0027) ? \u0027cloud_moon\u0027 : (items[\u0027OneCallAPIweatherandforecast_ForecastDay\u0027+loop.witem+\u0027_Iconid\u0027].state \u003d\u003d\u003d \u002703d\u0027) ? \u0027cloud\u0027 : (items[\u0027OneCallAPIweatherandforecast_ForecastDay\u0027+loop.witem+\u0027_Iconid\u0027].state \u003d\u003d\u003d \u002703n\u0027) ? \u0027cloud\u0027 : (items[\u0027OneCallAPIweatherandforecast_ForecastDay\u0027+loop.witem+\u0027_Iconid\u0027].state \u003d\u003d\u003d \u002704d\u0027) ? \u0027cloud\u0027 : (items[\u0027OneCallAPIweatherandforecast_ForecastDay\u0027+loop.witem+\u0027_Iconid\u0027].state \u003d\u003d\u003d \u002704n\u0027) ? \u0027cloud\u0027 : (items[\u0027OneCallAPIweatherandforecast_ForecastDay\u0027+loop.witem+\u0027_Iconid\u0027].state \u003d\u003d\u003d \u002709d\u0027) ? \u0027cloud_heavyrain\u0027 : (items[\u0027OneCallAPIweatherandforecast_ForecastDay\u0027+loop.witem+\u0027_Iconid\u0027].state \u003d\u003d\u003d \u002709n\u0027) ? \u0027cloud_heavyrain\u0027 : (items[\u0027OneCallAPIweatherandforecast_ForecastDay\u0027+loop.witem+\u0027_Iconid\u0027].state \u003d\u003d\u003d \u002710d\u0027) ? \u0027cloud_sun_rain\u0027 : (items[\u0027OneCallAPIweatherandforecast_ForecastDay\u0027+loop.witem+\u0027_Iconid\u0027].state \u003d\u003d\u003d \u002710n\u0027) ? \u0027cloud_moon_rain\u0027 : (items[\u0027OneCallAPIweatherandforecast_ForecastDay\u0027+loop.witem+\u0027_Iconid\u0027].state \u003d\u003d\u003d \u002711d\u0027) ? \u0027cloud_sun_bolt\u0027 : (items[\u0027OneCallAPIweatherandforecast_ForecastDay\u0027+loop.witem+\u0027_Iconid\u0027].state \u003d\u003d\u003d \u002711n\u0027) ? \u0027cloud_moon_bolt\u0027 : (items[\u0027OneCallAPIweatherandforecast_ForecastDay\u0027+loop.witem+\u0027_Iconid\u0027].state \u003d\u003d\u003d \u002713d\u0027) ? \u0027cloud_snow\u0027 : (items[\u0027OneCallAPIweatherandforecast_ForecastDay\u0027+loop.witem+\u0027_Iconid\u0027].state \u003d\u003d\u003d \u002713n\u0027) ? \u0027cloud_snow\u0027 : (items[\u0027OneCallAPIweatherandforecast_ForecastDay\u0027+loop.witem+\u0027_Iconid\u0027].state \u003d\u003d\u003d \u002750d\u0027) ? \u0027cloud_fog\u0027 : (items[\u0027OneCallAPIweatherandforecast_ForecastDay\u0027+loop.witem+\u0027_Iconid\u0027].state \u003d\u003d\u003d \u002750n\u0027) ? \u0027cloud_fog\u0027 : \u0027?\u0027",
"size": 25.0
}
},
{
"component": "Label",
"config": {
"style": {
"font-size": "14px",
"margin-left": "10px"
},
"text": "\u003dNumber.parseInt(items[\u0027OneCallAPIweatherandforecast_ForecastDay\u0027+loop.witem+\u0027_Mintemperature\u0027].state)+\" °C\""
}
},
{
"component": "div",
"config": {
"key": "\u003dMath.random() + @\u0027maxTempForecastDays\u0027 + @\u0027minTempForecastDays\u0027",
"style": {
"margin-top": "8px"
}
},
"slots": {
"default": [
{
"component": "oh-context",
"config": {
"functions": {
"barWidth": "\u003d(upper, lower, min, max) \u003d\u003e (upper - lower) / (max - min) * 100 + \u0027%\u0027",
"leftPos": "\u003d(val, min, max) \u003d\u003e ( val - min ) / ( max - min ) * 100 + \u0027%\u0027"
},
"variables": {
"max": "\u003dNumber.parseInt(@\u0027maxTempForecastDays\u0027)",
"min": "\u003dNumber.parseInt(@\u0027minTempForecastDays\u0027)"
}
},
"slots": {
"default": [
{
"component": "div",
"config": {
"class": [
"color-blue"
],
"style": {
"height": "6px",
"position": "relative",
"width": "100%"
}
},
"slots": {
"default": [
{
"component": "div",
"config": {
"style": {
"background": "var(--f7-theme-color-tint)",
"height": "100%",
"left": "\u003dfn.leftPos(Number.parseInt(items[\u0027OneCallAPIweatherandforecast_ForecastDay\u0027+loop.witem+\u0027_Mintemperature\u0027].state), vars.min, vars.max)",
"position": "absolute",
"width": "\u003dfn.barWidth(Number.parseInt(items[\u0027OneCallAPIweatherandforecast_ForecastDay\u0027+loop.witem+\u0027_Maxtemperature\u0027].state),Number.parseInt(items[\u0027OneCallAPIweatherandforecast_ForecastDay\u0027+loop.witem+\u0027_Mintemperature\u0027].state), vars.min, vars.max)"
}
}
}
]
}
}
]
}
}
]
}
},
{
"component": "Label",
"config": {
"style": {
"font-size": "14px"
},
"text": "\u003dNumber.parseInt(items[\u0027OneCallAPIweatherandforecast_ForecastDay\u0027+loop.witem+\u0027_Maxtemperature\u0027].state)+\" °C\""
}
}
]
}
}
]
}
}
]
}
}
]
}
}
},
With this method, you shouldn’t still need the key
property in the div that is the parent of the oh-context
. In fact, that seems to be setting up some sort of infinite loop because you have the same item values in the context variables as in the key
.
Clearly this, shouldn’t happen, but it’s easy enough to get around by just removing the key
property. You can, of course if you’re careful, just edit the jsondb file, but you can also delete the widget from the widget list (which doesn’t require opening it) and then re-add it. If you take the json you posted (remove the leading "semanticHomeMenu_Weather_modified":
and the trailing ,
), you can paste it into a json->yaml converter such as the one at ittools and that should give you the well formatted yaml that you can just paste back into the widget editor (after deleting the line with the key
).
This one seems to be a hard one
You are correct. Without the key line the widget is editable in the editor, but the bars are gone again. I wasn’t sure if I’d seen that correctly before, but now I have confirmation. That’s why I added the Key of Death.
Gah!
Whatever that underlying issue is, I cannot fully replicate it (possibly because I’m on a newer version, but I don’t know).
But, I’ve done some basic testing on the locking up and this infinite loop is clearly the interaction between the key
, the context, and the repeater. It’s a deeper issue than I first figured. I’m going to assume that you have no reason to have this weather widget more than once on a page, so we can try one more test. Instead of
key: =Math.random() + @'maxTempForecastDays' + @'minTempForecastDays'
try
key: =loop.witem + @'maxTempForecastDays' + @'minTempForecastDays'
and see if that gets you both the bars and the ability to view the widget in the editor.
Looks promising. Thanks and please add another coffee or beer or whatever you like to my list of things I owe you.
Hi,
have you been able to solve the issue? I also use a modified version of the same widget. The only difference is that I am loading min/max values from an item property without hardcoding it.
In my case the bards are visible, but min and max are ignored. Only after a refresh min and max are set accordingly…
Yes and no Since the change, the bars have always been displayed. One time the left position was not correct so the bar was drawn above the temperature label. Unfortunately I wasn’t able to debug in that situation. But my guess is, that this will be an easy one.
I still could not figure out what the problem is. In case you want to give it a try @JustinG, here’s my widget code.
This is how the widget looks on first render
This is how the widget looks after I refresh the page (e.g. navigate somewhere else, then back)
Widget code: widget.txt (107.4 KB)
This is, in fact, the exact same issue, and because the underlying problem is outside of the custom widgets themselves, there’s no real “fix” other than some work around similar to what has been discussed above.
It does occur to me, however, that there is one version of the workaround that’s much simpler than what’s above: Don’t use dynamic min and max values. By this I mean that you can leave your min and max values hard coded at 0 and 100 and just perform the scaling calculations of the range values in actual the value property itself.
Something like this:
- component: f7-range
config:
color: blue
disabled: true
dual: true
max: 100
min: 0
style:
--f7-range-bar-bg-color: transparent
--f7-range-bar-size: 4px
--f7-range-knob-size: 0px
value:
- =(items[props.forecastEquipment +"_ForecastTomorrow_Mintemperature"].numericState - items[props.minTempGroup].numericState) / (items[props.maxTempGroup].numericState - items[props.minTempGroup].numericState) * 100
- =(items[props.forecastEquipment +"_ForecastTomorrow_Maxtemperature"].numericState - items[props.minTempGroup].numericState) / (items[props.maxTempGroup].numericState - items[props.minTempGroup].numericState) * 100
This way you don’t have to go through the extra steps of using the key
property to cause a refresh, it’s not the max/min of the that need to change when the item calls catch up, it’s the values and those the f7 library does recalculate on the fly.
Elegant workaround