Look at this post
I have redesign the widget with the “f7-items-group” instead of “f7-items-list”.
My problem was, that ther were no dividers, no space between the listitems.
Here ist the Code for the whole widget.
uid: weatherPopup
tags:
- weather
- popup
- daily forecast
- OpenWeatherMap
props:
parameters:
- description: <b>Optional prefix</b> for item names.
label: Item prefix
name: itemPrefix
required: false
type: TEXT
groupName: general
- description: <b>Additional prefix</b> for item names that belongs to another Things channel (valid for 'StationName' as it might differ)
label: Additional item prefix
name: itemPrefix2
required: false
type: TEXT
groupName: general
advanced: true
- description: The number of days you want to forecast (<u>default:</u> <b>3</b>)
label: Number of days to forecast
name: forecastDays
required: false
type: TEXT
groupName: general
- description: Format of the timestamp (<u>default:</u> <b>HH:mm</b>)<br>Visit <a 'https://day.js.org/docs/en/display/format'>https://day.js.org/docs/en/display/format</a> for more informations.
label: Timestamp format
name: timestampFormat
required: false
type: TEXT
groupName: general
- description: Add suffix to the timestamp
label: Custom timestamp suffix
name: timestampSuffix
required: false
type: TEXT
groupName: general
- description: Overwrite the location header
label: Location title
name: locationTitle
required: false
type: TEXT
groupName: general
- label: Translation 'Feel'
name: wordingFeel
required: false
type: TEXT
groupName: wording
- label: Translation 'Humidity'
name: wordingHumidity
required: false
type: TEXT
groupName: wording
- label: Translation 'Wind'
name: wordingWind
required: false
type: TEXT
groupName: wording
- label: Translation '%-Precipitation'
name: wordingPrecipitation
required: false
type: TEXT
groupName: wording
- label: Translation 'Last Update'
name: wordingLastUpdate
required: false
type: TEXT
groupName: wording
parameterGroups:
- name: general
label: General settings
- name: wording
label: Language settings
description: Set alternative wordings
timestamp: May 2, 2024, 10:41:09 PM
component: f7-page
config:
style:
position: relative
-ms-user-select: none
-moz-user-select: none
-webkit-user-select: none
user-select: none
background: rgba(42,48,78,1)
--f7-theme-color: none
--f7-page-content-extra-padding-top: 0
--f7-bars-bg-color: none
--f7-bars-border-color: none
--f7-navbar-shadow-image: none
--f7-navbar-text-color: white
--f7-bars-text-color: white
--f7-navbar-link-color: white
--f7-list-bg-color: rgba(0,0,0,.15)
--f7-list-border-color: none
--f7-list-margin-vertical: 7px
--f7-page-navbar-offset: none
--f7-page-toolbar-top-offset: none
--f7-page-subnavbar-offset: none
--f7-page-searchbar-offset: none
--f7-bars-translucent-opacity: none
--f7-bars-translucent-blur: none
backdrop-filter: none
--f7-button-border-radius: 0
--weather-base-font-color: 255,255,255
--weather-base-text-shadow-color: 0,0,0
--weather-font-color: rgba(var(--weather-base-font-color),1)
--weather-transparent-font-color: rgba(var(--weather-base-font-color),.75)
--weather-text-shadow-color: rgba(var(--weather-base-text-shadow-color),.25)
--weather-icon-color: rgba(var(--weather-base-font-color),1)
--weather-hero-font-size: 91px
--weather-hero-text-shadow: 2px 2px rgba(0,0,0,.35)
--weather-small-font-size: 16px
--weather-normal-font-size: 18px
slots:
default:
- component: f7-block
config:
style:
background: "=(dayjs().format() >= items[((!props.itemPrefix) ? '' : props.itemPrefix) + 'ForecastToday_Sunrise'].state && dayjs().format() < items[((!props.itemPrefix) ? '' : props.itemPrefix) + 'ForecastToday_Sunset'].state) ? 'linear-gradient(to bottom, #355b8e, #c0d4f0)' : 'linear-gradient(to bottom, #413D8F, #CE9FC8)'"
background-size: cover
background-repeat: no-repeat
background-position: bottom left
padding: 30px 0 60px 0
margin-top: 0
slots:
default:
- component: f7-block
config:
class:
- no-padding
- no-margin
style:
border-top: 80px solid white
border-image-source: url('data:image/svg+xml,%3Csvg width="100%" height="100%" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:1.41421;"%3E%3Cg transform="matrix(1,0,0,0.198289,0,-19.1443)"%3E%3Cpath d="M2000,116C1713.19,117.226 1658.73,122.546 1360.08,153.634C1159.33,174.531 926.563,327.589 572.792,351.218C337.726,366.918 155.294,280.98 0,298.274L0,500L2000,500L2000,116Z" style="fill:rgba(42,48,78,1);" /%3E%3C/g%3E%3C/svg%3E')
-moz-border-image-source: url('data:image/svg+xml,%3Csvg width="100%" height="100%" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:1.41421;"%3E%3Cg transform="matrix(1,0,0,0.198289,0,-19.1443)"%3E%3Cpath d="M2000,116C1713.19,117.226 1658.73,122.546 1360.08,153.634C1159.33,174.531 926.563,327.589 572.792,351.218C337.726,366.918 155.294,280.98 0,298.274L0,500L2000,500L2000,116Z" style="fill:rgba(42,48,78,1""/%3E%3C/g%3E%3C/svg%3E')
-webkit-border-image-source: url('data:image/svg+xml,%3Csvg width="100%" height="100%" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:1.41421;"%3E%3Cg transform="matrix(1,0,0,0.198289,0,-19.1443)"%3E%3Cpath d="M2000,116C1713.19,117.226 1658.73,122.546 1360.08,153.634C1159.33,174.531 926.563,327.589 572.792,351.218C337.726,366.918 155.294,280.98 0,298.274L0,500L2000,500L2000,116Z" style="fill:rgba(42,48,78,1)"/%3E%3C/g%3E%3C/svg%3E')
-o-border-image-source: url('data:image/svg+xml,%3Csvg width="100%" height="100%" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:1.41421;"%3E%3Cg transform="matrix(1,0,0,0.198289,0,-19.1443)"%3E%3Cpath d="M2000,116C1713.19,117.226 1658.73,122.546 1360.08,153.634C1159.33,174.531 926.563,327.589 572.792,351.218C337.726,366.918 155.294,280.98 0,298.274L0,500L2000,500L2000,116Z" style="fill:rgba(42,48,78,1"/%3E%3C/g%3E%3C/svg%3E')
-webkit-appearance: none
border-image-slice: 100% 0 0 0
border-image-width: 1 0 0 0
width: 100%
position: absolute
left: 0
bottom: 0
height: 100%
max-height: 80px
- component: f7-block
config:
style:
text-align: center
class:
- no-margin
- no-padding
slots:
default:
- component: f7-row
config:
class:
- justify-content-center
slots:
default:
- component: Label
config:
text: "=!props.locationTitle ? items[(!props.itemPrefix2 ? (!props.itemPrefix ? '' : props.itemPrefix) : props.itemPrefix2) + 'StationName'].state : props.locationTitle"
style:
margin-top: calc(var(--f7-toolbar-height) / 2 - var(--f7-toolbar-height))
margin-bottom: 20px
font-size: var(--weather-normal-font-size)
color: var(--weather-font-color)
text-shadow: 2px 2px var(--weather-text-shadow-color)
white-space: nowrap
overflow: hidden
text-overflow: ellipsis
line-height: var(--f7-navbar-link-line-height,var(--f7-navbar-height))
text-transform: uppercase
- component: f7-row
config:
class:
- justify-content-center
slots:
default:
- component: Label
config:
text: "=Math.round(items[((!props.itemPrefix) ? '' : props.itemPrefix) + 'Current_Temperature'].state.split(' ')[0]) + '°'"
style:
line-height: var(--weather-hero-font-size)
font-size: var(--weather-hero-font-size)
color: var(--weather-font-color)
text-shadow: 2px 2px var(--weather-text-shadow-color)
white-space: nowrap
overflow: hidden
text-overflow: ellipsis
- component: f7-icon
config:
f7: "=(items[((!props.itemPrefix) ? '' : props.itemPrefix) + 'Current_Iconid'].state === '01d') ? 'sun_max_fill' : (items[((!props.itemPrefix) ? '' : props.itemPrefix) + 'Current_Iconid'].state === '01n') ? 'moon_stars_fill' : (items[((!props.itemPrefix) ? '' : props.itemPrefix) + 'Current_Iconid'].state === '02d') ? 'cloud_sun_fill' : (items[((!props.itemPrefix) ? '' : props.itemPrefix) + 'Current_Iconid'].state === '02n') ? 'cloud_moon_fill' : (items[((!props.itemPrefix) ? '' : props.itemPrefix) + 'Current_Iconid'].state === '03d') ? 'cloud_fill' : (items[((!props.itemPrefix) ? '' : props.itemPrefix) + 'Current_Iconid'].state === '03n') ? 'cloud_fill' : (items[((!props.itemPrefix) ? '' : props.itemPrefix) + 'Current_Iconid'].state === '04d') ? 'cloud_fill' : (items[((!props.itemPrefix) ? '' : props.itemPrefix) + 'Current_Iconid'].state === '04n') ? 'cloud_fill' : (items[((!props.itemPrefix) ? '' : props.itemPrefix) + 'Current_Iconid'].state === '09d') ? 'cloud_heavyrain_fill' : (items[((!props.itemPrefix) ? '' : props.itemPrefix) + 'Current_Iconid'].state === '09n') ? 'cloud_heavyrain_fill' : (items[((!props.itemPrefix) ? '' : props.itemPrefix) + 'Current_Iconid'].state === '10d') ? 'cloud_sun_rain_fill' : (items[((!props.itemPrefix) ? '' : props.itemPrefix) + 'Current_Iconid'].state === '10n') ? 'cloud_moon_rain_fill' : (items[((!props.itemPrefix) ? '' : props.itemPrefix) + 'Current_Iconid'].state === '11d') ? 'cloud_sun_bolt_fill' : (items[((!props.itemPrefix) ? '' : props.itemPrefix) + 'Current_Iconid'].state === '11n') ? 'cloud_moon_bolt_fill' : (items[((!props.itemPrefix) ? '' : props.itemPrefix) + 'Current_Iconid'].state === '13d') ? 'cloud_snow_fill' : (items[((!props.itemPrefix) ? '' : props.itemPrefix) + 'Current_Iconid'].state === '13n') ? 'cloud_snow_fill' : (items[((!props.itemPrefix) ? '' : props.itemPrefix) + 'Current_Iconid'].state === '50d') ? 'cloud_fog_fill' : (items[((!props.itemPrefix) ? '' : props.itemPrefix) + 'Current_Iconid'].state === '50n') ? 'cloud_fog_fill' : ''"
size: 70
class:
- align-self-center
style:
color: var(--weather-icon-color)
text-shadow: 2px 2px var(--weather-text-shadow-color)
- component: Label
config:
text: "=(!props.itemPrefix) ? items.Current_Condition.state : items[props.itemPrefix + 'Current_Condition'].state"
class:
- no-padding-top
style:
font-size: var(--weather-normal-font-size)
text-shadow: .5px .5px var(--weather-text-shadow-color)
color: var(--weather-font-color)
white-space: nowrap
overflow: hidden
text-overflow: ellipsis
- component: f7-row
config:
class:
- justify-content-center
style:
flex-wrap: nowrap
slots:
default:
- component: Label
config:
text: "=!props.wordingFeel ? 'Feel: ' : props.wordingFeel + ': '"
style:
font-size: var(--weather-normal-font-size)
color: var(--weather-transparent-font-color)
text-shadow: .5px .5px var(--weather-text-shadow-color)
white-space: nowrap
overflow: hidden
text-overflow: ellipsis
- component: Label
config:
text: "=(!props.itemPrefix) ? Math.round(items.Current_Apparenttemperature.state.split(' ')[0]) + '°' : Math.round(items[props.itemPrefix + 'Current_Apparenttemperature'].state.split(' ')[0]) + '°'"
style:
font-size: var(--weather-normal-font-size)
text-shadow: .5px .5px var(--weather-text-shadow-color)
font-weight: 600
color: var(--weather-transparent-font-color)
- component: f7-block
config:
style:
margin-top: -20px
class:
- no-padding
slots:
default:
- component: f7-row
config:
class:
- no-gap
slots:
default:
- component: f7-col
config:
style:
text-align: center
border-right: 2px solid var(--weather-font-color)
slots:
default:
- component: f7-icon
config:
f7: drop
size: 30
style:
color: var(--weather-icon-color)
- component: Label
config:
text: "=(!props.itemPrefix) ? items.ForecastToday_Humidity.state : items[props.itemPrefix + 'ForecastToday_Humidity'].state"
style:
white-space: nowrap
overflow: hidden
text-overflow: ellipsis
font-size: var(--weather-normal-font-size)
color: var(--weather-font-color)
- component: f7-col
config:
style:
text-align: center
border-right: 2px solid var(--weather-font-color)
slots:
default:
- component: f7-icon
config:
f7: thermometer
size: 30
style:
width: 100%
color: var(--weather-icon-color)
- component: f7-row
config:
style:
flex-wrap: nowrap
class:
- justify-content-center
slots:
default:
- component: Label
config:
text: "=(!props.itemPrefix) ? Math.round(items.ForecastToday_Maxtemperature.state.split(' ')[0]) + '°' : Math.round(items[props.itemPrefix + 'ForecastToday_Maxtemperature'].state.split(' ')[0]) + '°'"
style:
font-size: var(--weather-normal-font-size)
color: var(--weather-font-color)
padding-right: 7px
white-space: nowrap
overflow: hidden
text-overflow: ellipsis
- component: Label
config:
text: "=(!props.itemPrefix) ? Math.round(items.ForecastToday_Mintemperature.state.split(' ')[0]) + '°' : Math.round(items[props.itemPrefix + 'ForecastToday_Mintemperature'].state.split(' ')[0]) + '°'"
style:
font-size: var(--weather-normal-font-size)
color: var(--weather-transparent-font-color)
white-space: nowrap
overflow: hidden
text-overflow: ellipsis
- component: f7-col
config:
style:
text-align: center
slots:
default:
- component: f7-icon
config:
f7: wind
size: 30
style:
color: var(--weather-icon-color)
- component: Label
config:
text: "=(!props.itemPrefix) ? items.ForecastToday_Windspeed.displayState : items[props.itemPrefix + 'ForecastToday_Windspeed'].displayState"
style:
font-size: var(--weather-normal-font-size)
color: var(--weather-font-color)
white-space: nowrap
overflow: hidden
text-overflow: ellipsis
- component: oh-repeater
config:
sourceType: range
for: days
rangeStart: 1
rangeStop: "=!props.forecastDays ? 3 : Number(props.forecastDays)"
slots:
default:
- component: f7-list
slots:
default:
- component: f7-accordion-item
config:
style:
color: var(--weather-font-color)
slots:
default:
- component: f7-accordion-toggle
slots:
default:
- component: f7-row
config:
style:
flex-wrap: nowrap
class:
- align-items-center
slots:
default:
- component: f7-row
config:
style:
overflow: hidden
flex-wrap: nowrap
margin: 5px 0 5px 16px
class:
- align-items-center
slots:
default:
- component: f7-icon
config:
f7: "=(items[((!props.itemPrefix) ? '' : props.itemPrefix) + ((loop.days === 1) ? 'ForecastTomorrow' : 'ForecastDay' + (loop.days_idx+1)) + '_Iconid'].state === '01d') ? 'sun_max_fill' : (items[((!props.itemPrefix) ? '' : props.itemPrefix) + ((loop.days === 1) ? 'ForecastTomorrow' : 'ForecastDay' + (loop.days_idx+1)) + '_Iconid'].state === '01n') ? 'moon_stars_fill' : (items[((!props.itemPrefix) ? '' : props.itemPrefix) + ((loop.days === 1) ? 'ForecastTomorrow' : 'ForecastDay' + (loop.days_idx+1)) + '_Iconid'].state === '02d') ? 'cloud_sun_fill' : (items[((!props.itemPrefix) ? '' : props.itemPrefix) + ((loop.days === 1) ? 'ForecastTomorrow' : 'ForecastDay' + (loop.days_idx+1)) + '_Iconid'].state === '02n') ? 'cloud_moon_fill' : (items[((!props.itemPrefix) ? '' : props.itemPrefix) + ((loop.days === 1) ? 'ForecastTomorrow' : 'ForecastDay' + (loop.days_idx+1)) + '_Iconid'].state === '03d') ? 'cloud_fill' : (items[((!props.itemPrefix) ? '' : props.itemPrefix) + ((loop.days === 1) ? 'ForecastTomorrow' : 'ForecastDay' + (loop.days_idx+1)) + '_Iconid'].state === '03n') ? 'cloud_fill' : (items[((!props.itemPrefix) ? '' : props.itemPrefix) + ((loop.days === 1) ? 'ForecastTomorrow' : 'ForecastDay' + (loop.days_idx+1)) + '_Iconid'].state === '04d') ? 'cloud_fill' : (items[((!props.itemPrefix) ? '' : props.itemPrefix) + ((loop.days === 1) ? 'ForecastTomorrow' : 'ForecastDay' + (loop.days_idx+1)) + '_Iconid'].state === '04n') ? 'cloud_fill' : (items[((!props.itemPrefix) ? '' : props.itemPrefix) + ((loop.days === 1) ? 'ForecastTomorrow' : 'ForecastDay' + (loop.days_idx+1)) + '_Iconid'].state === '09d') ? 'cloud_heavyrain_fill' : (items[((!props.itemPrefix) ? '' : props.itemPrefix) + ((loop.days === 1) ? 'ForecastTomorrow' : 'ForecastDay' + (loop.days_idx+1)) + '_Iconid'].state === '09n') ? 'cloud_heavyrain_fill' : (items[((!props.itemPrefix) ? '' : props.itemPrefix) + ((loop.days === 1) ? 'ForecastTomorrow' : 'ForecastDay' + (loop.days_idx+1)) + '_Iconid'].state === '10d') ? 'cloud_sun_rain_fill' : (items[((!props.itemPrefix) ? '' : props.itemPrefix) + ((loop.days === 1) ? 'ForecastTomorrow' : 'ForecastDay' + (loop.days_idx+1)) + '_Iconid'].state === '10n') ? 'cloud_moon_rain_fill' : (items[((!props.itemPrefix) ? '' : props.itemPrefix) + ((loop.days === 1) ? 'ForecastTomorrow' : 'ForecastDay' + (loop.days_idx+1)) + '_Iconid'].state === '11d') ? 'cloud_sun_bolt_fill' : (items[((!props.itemPrefix) ? '' : props.itemPrefix) + ((loop.days === 1) ? 'ForecastTomorrow' : 'ForecastDay' + (loop.days_idx+1)) + '_Iconid'].state === '11n') ? 'cloud_moon_bolt_fill' : (items[((!props.itemPrefix) ? '' : props.itemPrefix) + ((loop.days === 1) ? 'ForecastTomorrow' : 'ForecastDay' + (loop.days_idx+1)) + '_Iconid'].state === '13d') ? 'cloud_snow_fill' : (items[((!props.itemPrefix) ? '' : props.itemPrefix) + ((loop.days === 1) ? 'ForecastTomorrow' : 'ForecastDay' + (loop.days_idx+1)) + '_Iconid'].state === '13n') ? 'cloud_snow_fill' : (items[((!props.itemPrefix) ? '' : props.itemPrefix) + ((loop.days === 1) ? 'ForecastTomorrow' : 'ForecastDay' + (loop.days_idx+1)) + '_Iconid'].state === '50d') ? 'cloud_fog_fill' : (items[((!props.itemPrefix) ? '' : props.itemPrefix) + ((loop.days === 1) ? 'ForecastTomorrow' : 'ForecastDay' + (loop.days_idx+1)) + '_Iconid'].state === '50n') ? 'cloud_fog_fill' : ''"
size: 35
style:
padding-right: 7px
color: var(--weather-icon-color)
- component: Label
config:
text: =dayjs().add(loop.days,'days').format('dddd')
style:
font-size: var(--weather-small-font-size)
overflow: hidden
text-overflow: ellipsis
- component: f7-row
config:
style:
flex-wrap: nowrap
margin: 5px 16px 5px 7px
slots:
default:
- component: Label
config:
text: "=Math.round(items[((!props.itemPrefix) ? '' : props.itemPrefix) + ((loop.days === 1) ? 'ForecastTomorrow' : 'ForecastDay' + (loop.days_idx+1)) + '_Maxtemperature'].state.split(' ')[0]) + '°'"
style:
font-size: var(--weather-normal-font-size)
margin-right: 7px
min-width: 27px
text-align: right
- component: Label
config:
text: "=Math.round(items[((!props.itemPrefix) ? '' : props.itemPrefix) + ((loop.days === 1) ? 'ForecastTomorrow' : 'ForecastDay' + (loop.days_idx+1)) + '_Mintemperature'].state.split(' ')[0]) + '°'"
style:
font-size: var(--weather-normal-font-size)
color: rgba(255,255,255,.75)
min-width: 27px
text-align: right
- component: f7-accordion-content
slots:
default:
- component: f7-list-group
slots:
default:
- component: f7-row
config:
style:
flex-wrap: nowrap
border-bottom: solid 1px
border-color: hsla(0,0%,100%,0.15)
class:
- align-items-center
slots:
default:
- component: f7-row
config:
style:
overflow: hidden
flex-wrap: nowrap
padding: 5px 0 5px 16px
class:
- align-items-center
slots:
default:
- component: Label
config:
text: "=!props.wordingHumidity ? 'Humidity' : props.wordingHumidity"
style:
font-size: var(--weather-small-font-size)
- component: f7-row
config:
style:
flex-wrap: nowrap
margin: 5px 16px 5px 7px
slots:
default:
- component: Label
config:
text: "=items[((!props.itemPrefix) ? '' : props.itemPrefix) + ((loop.days === 1) ? 'ForecastTomorrow' : 'ForecastDay' + (loop.days_idx+1)) + '_Humidity'].state"
style:
font-size: var(--weather-small-font-size)
margin-right: 7px
min-width: 27px
text-align: right
- component: f7-list-group
slots:
default:
- component: f7-row
config:
style:
flex-wrap: nowrap
border-bottom: solid 1px
border-color: hsla(0,0%,100%,0.15)
class:
- align-items-center
slots:
default:
- component: f7-row
config:
style:
overflow: hidden
flex-wrap: nowrap
padding: 5px 0 5px 16px
class:
- align-items-center
slots:
default:
- component: Label
config:
text: "=!props.wordingWind ? 'Wind' : props.wordingWind"
style:
font-size: var(--weather-small-font-size)
- component: f7-row
config:
style:
flex-wrap: nowrap
margin: 5px 16px 5px 7px
slots:
default:
- component: Label
config:
text: "=items[((!props.itemPrefix) ? '' : props.itemPrefix) + ((loop.days === 1) ? 'ForecastTomorrow' : 'ForecastDay' + (loop.days_idx+1)) + '_Windspeed'].displayState"
style:
font-size: var(--weather-small-font-size)
margin-right: 7px
min-width: 27px
text-align: right
- component: f7-list-group
slots:
default:
- component: f7-row
config:
style:
flex-wrap: nowrap
class:
- align-items-center
slots:
default:
- component: f7-row
config:
style:
overflow: hidden
flex-wrap: nowrap
padding: 5px 0 5px 16px
class:
- align-items-center
slots:
default:
- component: Label
config:
text: "=!props.wordingPrecipitation ? '%-Precipitation' : props.wordingPrecipitation"
style:
font-size: var(--weather-small-font-size)
- component: f7-row
config:
style:
flex-wrap: nowrap
margin: 5px 16px 5px 7px
slots:
default:
- component: Label
config:
text: "=items[((!props.itemPrefix) ? '' : props.itemPrefix) + ((loop.days === 1) ? 'ForecastTomorrow' : 'ForecastDay' + (loop.days_idx+1)) + '_Precipprobability'].state.split(' ')[0] + '%'"
style:
font-size: var(--weather-small-font-size)
margin-right: 7px
min-width: 27px
text-align: right
- component: f7-block
config:
style:
text-align: center
color: var(--weather-font-color)
slots:
default:
- component: Label
config:
text: "=(!props.wordingLastUpdate ? 'Last Update: ' : props.wordingLastUpdate + ': ') + dayjs(items[((!props.itemPrefix) ? '' : props.itemPrefix) + 'ObservationTime'].state).format((!props.timestampFormat) ? 'HH:mm' : props.timestampFormat) + (!props.timestampSuffix ? '' : ' ' + props.timestampSuffix)"
style:
font-size: var(--weather-normal-font-size)
white-space: nowrap
overflow: hidden
text-overflow: ellipsis
thanks to @kristofejro, who gave me the hint to the other post.
Hello,
I had a few problems with the WeatherCard and would like to briefly share here how I solved them.
Firstly, the icons were not displayed, but only large “?”.
To solve this, you have to click on “show advanced” at the top when creating the items from the channels so that an item is really created for all channels.
Then I had the problem that the humidity is not displayed at the top right and the precipitation tab always shows 100%. I have adjusted the code a little bit. The modified widget code follows here (line 354 and 801):
uid: weatherCard
tags:
- allInOne
- weather
- expandable
- OpenWeatherMap
- daily forecast
- hourly forecast
props:
parameters:
- description: <b>Optional prefix</b> for item names
label: Item prefix
name: itemPrefix
required: false
type: TEXT
groupName: general
- description: <b>Additional prefix</b> for item names that belongs to another Things channel (valid for 'StationName' as it might differ)
label: Additional item prefix
name: itemPrefix2
required: false
type: TEXT
groupName: general
- description: The number of hours you want to forecast (<u>default:</u> <b>12</b>)
label: Number of hours to forecast
name: forecastHours
required: false
type: TEXT
groupName: general
- description: The number of days you want to forecast (<u>default:</u> <b>3</b>)
label: Number of days to forecast
name: forecastDays
required: false
type: TEXT
groupName: general
- description: Show all informations at once (increases height)
label: Big card
name: bigCard
required: false
type: BOOLEAN
groupName: lookandfeel
- description: Use this only on screens with a very small view-width
label: Mobile optimized
name: mobile
required: false
type: BOOLEAN
groupName: lookandfeel
advanced: true
- description: Set a background-image which will be shown during the day (if empty it will fall back to the linear-gradient)
label: Background image-url (day-cycle)
name: backgroundUrlDay
required: false
type: TEXT
groupName: lookandfeel
- description: Set a background-image which will be shown during the night (if empty it will fall back to the linear-gradient)
label: Background image-url (night-cycle)
name: backgroundUrlNight
required: false
type: TEXT
groupName: lookandfeel
- description: Activate day & night Indication on hourly forecast (background color & sunrise / sunset indicator icon)
label: Show sunrise & sunset
name: sunIndicator
required: false
type: BOOLEAN
groupName: lookandfeel
- description: Overwrite the global font-color as rgb (<u>default:</u> <b>255,255,255<b>)
label: Font color (rgb)
name: fontColor
required: false
type: TEXT
groupName: lookandfeel
- description: Overwrite the global text shadow color as rgb (<u>default:</u> <b>0,0,0<b>)
label: Text shadow color(rgb)
name: textShadowColor
required: false
type: TEXT
groupName: lookandfeel
- label: Daytime background indication color
name: sunIndicatorColorDay
required: false
type: TEXT
groupName: lookandfeel
advanced: true
- label: Nighttime background indication color
name: sunIndicatorColorNight
required: false
type: TEXT
groupName: lookandfeel
advanced: true
- description: Overwrite the station location name
label: Location title
name: locationTitle
required: false
type: TEXT
groupName: localization
- description: Format of the timestamp below the location-name (<u>default:</u> <b>DD. MMMM YYYY</b>)<br>Visit <a 'https://day.js.org/docs/en/display/format'>https://day.js.org/docs/en/display/format</a> for more informations.
label: Timestamp format
name: dateScheme
required: false
type: TEXT
groupName: localization
- description: Acitvate 24-hour time-format (<u>default:</u> <b>12-hour clock-format</b>)
label: 24h hour-format
name: dateFormat
required: false
type: BOOLEAN
groupName: localization
- description: Add suffix to the hourly-forecast timestamp
label: Custom hour suffix
name: timestampSuffix
required: false
type: TEXT
groupName: localization
- label: Translation 'Feel'
name: wordingFeel
required: false
type: TEXT
groupName: localization
- label: Translation 'Hourly'
name: wordingForecastHours
required: false
type: TEXT
groupName: localization
- label: Translation 'Daily'
name: wordingForecastDays
required: false
type: TEXT
groupName: localization
- label: Translation '%-Precipitation'
name: wordingForecastPrecib
required: false
type: TEXT
groupName: localization
- label: Translation 'Now'
name: wordingNow
required: false
type: TEXT
groupName: localization
- label: Translation 'Today'
name: wordingToday
required: false
type: TEXT
groupName: localization
- label: Translation 'Sunrise at'
name: wordingSunrise
required: false
type: TEXT
groupName: localization
- label: Translation 'Sunset at'
name: wordingSunset
required: false
type: TEXT
groupName: localization
parameterGroups:
- name: general
label: General settings
- name: widgetAction
context: action
label: Action
description: Action to perform when the element is clicked (<b>Experimental</b>)
- name: lookandfeel
label: Look & Feel
description: Everything that influences the look & feel of the widget
- name: localization
label: Localization settings
description: Set alternative wordings & date pattern
timestamp: Jul 3, 2024, 4:49:38 PM
component: f7-card
config:
class:
- padding
style:
overflow: hidden
height: "=(props.bigCard) ? '' : '160px'"
-ms-user-select: none
-moz-user-select: none
-webkit-user-select: none
user-select: none
background: "=dayjs().format() >= items[((!props.itemPrefix) ? '' : props.itemPrefix) + 'ForecastToday_Sunrise'].state && dayjs().format() < items[((!props.itemPrefix) ? '' : props.itemPrefix) + 'ForecastToday_Sunset'].state ? 'linear-gradient(to bottom, #355b8e, #c0d4f0)' : 'linear-gradient(to bottom, #413D8F, #CE9FC8)'"
border-radius: var(--weather-card-border-radius)
--weather-card-color: "=(!props.fontColor) ? '255,255,255' : props.fontColor"
--weather-card-text-shadow-color: "=(!props.textShadowColor) ? '0,0,0' : props.textShadowColor"
--weather-card-text-color: rgba(var(--weather-card-color),1)
color: var(--weather-card-text-color)
--weather-card-border-radius: 20px
--weather-font-size-xxsmall: 12px
--weather-font-size-xsmall: 14px
--weather-font-size-small: 16px
--weather-font-size-normal: 18px
--weather-font-size-large: 26px
--weather-font-size-xlarge: 60px
--weather-font-size-xxlarge: 70px
--weather-text-shadow-light: 2px 2px rgba(var(--weather-card-text-shadow-color),.15)
--weather-text-shadow-strong: 2px 2px rgba(var(--weather-card-text-shadow-color),.35)
--weather-text-transform-time: uppercase
slots:
default:
- component: oh-link
config:
visible: "=props.widget_action && (vars.moreInfo === false || !vars.moreInfo) ? true : false"
actionPropsParameterGroup: widgetAction
color: white
class:
- no-padding
- no-margin
style:
max-height: 192px
width: 100%
height: 100%
position: absolute
top: 0
left: 0
z-index: 98
- component: f7-block
config:
class:
- no-padding
- no-margin
style:
background: "=dayjs().format() >= items[((!props.itemPrefix) ? '' : props.itemPrefix) + 'ForecastToday_Sunrise'].state && dayjs().format() < items[((!props.itemPrefix) ? '' : props.itemPrefix) + 'ForecastToday_Sunset'].state ? 'no-repeat 100% / cover url(' + props.backgroundUrlDay + ')' : 'no-repeat 100% / cover url(' + props.backgroundUrlNight + ')'"
width: 100%
height: 100%
position: absolute
top: 0
left: 0
- component: f7-row
config:
visible: "=(!vars.moreInfo) ? true : false"
slots:
default:
- component: f7-col
config:
style:
z-index: 95
slots:
default:
- component: Label
config:
text: "=!props.locationTitle ? items[(!props.itemPrefix2 ? (!props.itemPrefix ? '' : props.itemPrefix) : props.itemPrefix2) + 'StationName'].state : props.locationTitle"
style:
white-space: nowrap
overflow: hidden
text-overflow: ellipsis
letter-spacing: .75px
font-size: var(--weather-font-size-large)
text-shadow: var(--weather-text-shadow-strong)
font-weight: 600
- component: Label
config:
text: "=dayjs().format(props.dateScheme ? props.dateScheme : 'DD. MMMM YYYY')"
style:
white-space: nowrap
overflow: hidden
text-overflow: ellipsis
color: rgba(var(--weather-card-color),.7)
letter-spacing: .75px
font-size: var(--weather-font-size-xxsmall)
text-shadow: var(--weather-text-shadow-light)
- component: f7-row
config:
class:
- align-items-center
style:
flex-wrap: nowrap
slots:
default:
- component: f7-icon
config:
f7: "=(items[((!props.itemPrefix) ? '' : props.itemPrefix) + 'Current_Iconid'].state === '01d') ? 'sun_max_fill' : (items[((!props.itemPrefix) ? '' : props.itemPrefix) + 'Current_Iconid'].state === '01n') ? 'moon_stars_fill' : (items[((!props.itemPrefix) ? '' : props.itemPrefix) + 'Current_Iconid'].state === '02d') ? 'cloud_sun_fill' : (items[((!props.itemPrefix) ? '' : props.itemPrefix) + 'Current_Iconid'].state === '02n') ? 'cloud_moon_fill' : (items[((!props.itemPrefix) ? '' : props.itemPrefix) + 'Current_Iconid'].state === '03d') ? 'cloud_fill' : (items[((!props.itemPrefix) ? '' : props.itemPrefix) + 'Current_Iconid'].state === '03n') ? 'cloud_fill' : (items[((!props.itemPrefix) ? '' : props.itemPrefix) + 'Current_Iconid'].state === '04d') ? 'cloud_fill' : (items[((!props.itemPrefix) ? '' : props.itemPrefix) + 'Current_Iconid'].state === '04n') ? 'cloud_fill' : (items[((!props.itemPrefix) ? '' : props.itemPrefix) + 'Current_Iconid'].state === '09d') ? 'cloud_heavyrain_fill' : (items[((!props.itemPrefix) ? '' : props.itemPrefix) + 'Current_Iconid'].state === '09n') ? 'cloud_heavyrain_fill' : (items[((!props.itemPrefix) ? '' : props.itemPrefix) + 'Current_Iconid'].state === '10d') ? 'cloud_sun_rain_fill' : (items[((!props.itemPrefix) ? '' : props.itemPrefix) + 'Current_Iconid'].state === '10n') ? 'cloud_moon_rain_fill' : (items[((!props.itemPrefix) ? '' : props.itemPrefix) + 'Current_Iconid'].state === '11d') ? 'cloud_sun_bolt_fill' : (items[((!props.itemPrefix) ? '' : props.itemPrefix) + 'Current_Iconid'].state === '11n') ? 'cloud_moon_bolt_fill' : (items[((!props.itemPrefix) ? '' : props.itemPrefix) + 'Current_Iconid'].state === '13d') ? 'cloud_snow_fill' : (items[((!props.itemPrefix) ? '' : props.itemPrefix) + 'Current_Iconid'].state === '13n') ? 'cloud_snow_fill' : (items[((!props.itemPrefix) ? '' : props.itemPrefix) + 'Current_Iconid'].state === '50d') ? 'cloud_fog_fill' : (items[((!props.itemPrefix) ? '' : props.itemPrefix) + 'Current_Iconid'].state === '50n') ? 'cloud_fog_fill' : '?'"
style:
text-shadow: var(--weather-text-shadow-strong)
font-size: var(--weather-font-size-xxlarge)
padding-top: 5px
padding-bottom: 5px
- component: f7-col
config:
visible: =props.mobile === true
slots:
default:
- component: Label
config:
text: "=Math.round(items[((!props.itemPrefix) ? '' : props.itemPrefix) + 'Current_Temperature'].state.split(' ')[0] * 10) / 10 + '°'"
class:
- padding-left
style:
white-space: nowrap
overflow: hidden
text-overflow: ellipsis
text-shadow: var(--weather-text-shadow-strong)
font-size: 31px
line-height: 31px
font-weight: 600
- component: Label
config:
text: "=((!props.wordingFeel) ? 'Feel: ' : props.wordingFeel + ': ') + Math.round(items[((!props.itemPrefix) ? '' : props.itemPrefix) + 'Current_Apparenttemperature'].state.split(' ')[0]) + '°'"
class:
- padding-left
style:
white-space: nowrap
overflow: hidden
text-overflow: ellipsis
text-shadow: var(--weather-text-shadow-light)
font-size: var(--weather-font-size-small)
- component: Label
config:
text: "=items[((!props.itemPrefix) ? '' : props.itemPrefix) + 'Current_Condition'].state"
style:
white-space: nowrap
overflow: hidden
text-overflow: ellipsis
font-size: var(--weather-font-size-small)
- component: f7-col
config:
visible: =!props.mobile
class:
- text-align-right
style:
align-self: "=props.bigCard ? 'flex-start' : 'flex-end'"
z-index: 95
slots:
default:
- component: Label
config:
text: "=Math.round(items[((!props.itemPrefix) ? '' : props.itemPrefix) + 'Current_Temperature'].state.split(' ')[0] * 10) / 10 + '°'"
style:
white-space: nowrap
overflow: hidden
text-overflow: ellipsis
text-shadow: var(--weather-text-shadow-strong)
font-size: var(--weather-font-size-xlarge)
line-height: var(--weather-font-size-xlarge)
font-weight: 600
- component: Label
config:
text: "=((!props.wordingFeel) ? 'Feel: ' : props.wordingFeel + ': ') + Math.round(items[((!props.itemPrefix) ? '' : props.itemPrefix) + 'Current_Apparenttemperature'].state.split(' ')[0]) + '°'"
style:
white-space: nowrap
text-overflow: ellipsis
overflow: hidden
font-size: var(--weather-font-size-normal)
text-shadow: var(--weather-text-shadow-light)
- component: f7-row
config:
class:
- justify-content-flex-end
style:
flex-wrap: nowrap
font-size: var(--weather-font-size-xsmall)
text-shadow: var(--weather-text-shadow-light)
slots:
default:
- component: Label
config:
text: "=Math.round(items[((!props.itemPrefix) ? '' : props.itemPrefix) + 'Current_Humidity'].state.split(' ')[0] * 100) + ' %'"
tooltip: Humidity
style:
white-space: nowrap
overflow: hidden
text-overflow: ellipsis
text-shadow: var(--weather-text-shadow-light)
- component: Label
config:
text: =" | "
style:
color: rgba(var(--weather-card-color),.25)
text-shadow: var(--weather-text-shadow-light)
- component: Label
config:
text: "=Math.round(items[((!props.itemPrefix) ? '' : props.itemPrefix) + 'Current_Windspeed'].state.split(' ')[0]) + ' ' + items[((!props.itemPrefix) ? '' : props.itemPrefix) + 'Current_Windspeed'].state.split(' ')[1]"
tooltip: Windspeed
style:
white-space: nowrap
overflow: hidden
text-overflow: ellipsis
text-shadow: var(--weather-text-shadow-light)
- component: oh-button
config:
colorTheme: white
color: white
iconMaterial: more_horiz
iconSize: 20px
action: variable
actionVariable: moreInfo
actionVariableValue: true
visible: "=(props.bigCard) ? false : ((!vars.moreInfo || vars.moreInfo === false) ? true : false)"
class:
- margin-top
- margin-right
style:
position: absolute
top: 0
right: 0
font-weight: 600
z-index: 99
color: var(--weather-card-text-color)
- component: oh-button
config:
colorTheme: white
color: white
iconMaterial: close
iconSize: 20px
action: variable
actionVariable: moreInfo
actionVariableValue: false
visible: "=(props.bigCard) ? false : ((vars.moreInfo || vars.moreInfo === true) ? true : false)"
class:
- margin-top
- margin-right
style:
position: absolute
top: 0
right: 0
font-weight: 600
z-index: 99
color: var(--weather-card-text-color)
- component: f7-block
config:
visible: =vars.moreInfo || props.bigCard === true
class:
- no-padding
- no-margin
- align-items-space-between
slots:
default:
- component: f7-segmented
config:
strong: true
textColor: white
style:
--f7-segmented-strong-button-active-box-shadow: 0px 4px 0 -1px rgba(var(--weather-card-color),1)
--f7-segmented-strong-button-active-text-color: rgba(var(--weather-card-color),1)
--f7-button-text-color: rgba(var(--weather-card-color),.5)
--f7-segmented-strong-text-color: rgba(var(--weather-card-color),.5)
--f7-segmented-strong-padding: 3px
--f7-button-active-text-color: rgba(var(--weather-card-color),1)
--f7-button-raised-box-shadow: none
--f7-segmented-strong-button-active-bg-color: transparent
--f7-button-border-radius: 0
width: "=(props.bigCard) ? '100%' : 'calc(100% - 38px)'"
padding-top: "=(props.bigCard) ? '16px' : '0'"
background: transparent
z-index: 999
align-items: flex-end
slots:
default:
- component: oh-button
config:
text: "=(!props.wordingForecastHours) ? 'Hourly' : props.wordingForecastHours"
color: var(--weather-card-text-color)
active: =vars.tab === 'hourly_forecast'
action: variable
actionVariable: tab
actionVariableValue: hourly_forecast
- component: oh-button
config:
text: "=(!props.wordingForecastDays) ? 'Daily' : props.wordingForecastDays"
color: var(--weather-card-text-color)
active: =vars.tab === 'daily_forecast'
action: variable
actionVariable: tab
actionVariableValue: daily_forecast
- component: oh-button
config:
text: "=(!props.wordingForecastPrecib) ? '%-Precipitation' : props.wordingForecastPrecib"
color: var(--weather-card-text-color)
active: =vars.tab === 'precip_forecast'
action: variable
actionVariable: tab
actionVariableValue: precip_forecast
- component: f7-block
config:
class:
- no-padding
style:
border-bottom: 4px solid rgba(var(--weather-card-color),.5)
margin-left: 4px
margin-right: 4px
- component: f7-swiper
config:
visible: =vars.tab === 'hourly_forecast' || !vars.tab
navigation: true
class:
- padding-top
params:
initalSlide: 0
runCallbacksOnInit: true
grabCursor: true
observer: true
observeSlideChildren: true
updateOnWindowResize: true
spaceBetween: 5
mousewheel: true
keyboard: true
watchOverflow: true
breakpoints:
"0":
slidesPerView: 1
"240":
slidesPerView: 2
"320":
slidesPerView: 3
"480":
slidesPerView: 4
"640":
slidesPerView: 5
style:
--swiper-navigation-size: 30px
--swiper-navigation-color: var(--weather-card-text-color)
slots:
default:
- component: oh-repeater
config:
sourceType: range
for: hour
rangeStart: 0
rangeStop: "=(!props.forecastHours) ? 12 : Number(props.forecastHours)"
fragment: true
slots:
default:
- component: f7-swiper-slide
config:
expandable: true
style:
background: "=(props.sunIndicator) ? ((dayjs().add(loop.hour,'hour').format() >= items[((!props.itemPrefix) ? '' : props.itemPrefix) + 'Forecast' + (dayjs().add(loop.hour,'hour').startOf('day').format() === dayjs().startOf('day').format() ? 'Today' : (dayjs().add(loop.hour,'hour').startOf('day').format() === dayjs().startOf('day').add(1,'day').format() ? 'Tomorrow' : 'Day2')) + '_Sunrise'].state && dayjs().add(loop.hour,'hour').format() <= items[((!props.itemPrefix) ? '' : props.itemPrefix) + 'Forecast' + (dayjs().add(loop.hour,'hour').startOf('day').format() === dayjs().startOf('day').format() ? 'Today' : (dayjs().add(loop.hour,'hour').startOf('day').format() === dayjs().startOf('day').add(1,'day').format() ? 'Tomorrow' : 'Day2')) + '_Sunset'].state) ? (!props.sunIndicatorColorDay ? 'rgba(255,255,255,.2)' : props.sunIndicatorColorDay) : (!props.sunIndicatorColorNight ? 'rgba(41,109,152,.2)' : props.sunIndicatorColorNight)) : 'none'"
border-radius: 5px
slots:
default:
- component: f7-icon
config:
visible: "=(dayjs().add(loop.hour,'hour').startOf('hour').format() === dayjs(items[((!props.itemPrefix) ? '' : props.itemPrefix) + 'Forecast' + (dayjs().add(loop.hour,'hour').startOf('day').format() === dayjs().startOf('day').format() ? 'Today' : (dayjs().add(loop.hour,'hour').startOf('day').format() === dayjs().startOf('day').add(1,'day').format() ? 'Tomorrow' : 'Day2')) + '_Sunrise'].state).startOf('hour').format() && props.sunIndicator === true)"
f7: sun_max_fill
size: 17px
tooltip: "=((!props.wordingSunrise) ? 'Sunrise at ' : props.wordingSunrise + ' ') + ((!props.dateFormat) ? dayjs(items[((!props.itemPrefix) ? '' : props.itemPrefix) + 'Forecast' + (dayjs().add(loop.hour,'hour').startOf('day').format() === dayjs().startOf('day').format() ? 'Today' : (dayjs().add(loop.hour,'hour').startOf('day').format() === dayjs().startOf('day').add(1,'day').format() ? 'Tomorrow' : 'Day2')) + '_Sunrise'].state).format('h:mm A') : dayjs(items[((!props.itemPrefix) ? '' : props.itemPrefix) + 'Forecast' + (dayjs().add(loop.hour,'hour').startOf('day').format() === dayjs().startOf('day').format() ? 'Today' : (dayjs().add(loop.hour,'hour').startOf('day').format() === dayjs().startOf('day').add(1,'day').format() ? 'Tomorrow' : 'Day2')) + '_Sunrise'].state).format('H:mm') + (!props.timestampSuffix ? '' : ' ' + props.timestampSuffix) ) + '<br><b>' + dayjs(items[((!props.itemPrefix) ? '' : props.itemPrefix) + 'Forecast' + (dayjs().add(loop.hour,'hour').startOf('day').format() === dayjs().startOf('day').format() ? 'Today' : (dayjs().add(loop.hour,'hour').startOf('day').format() === dayjs().startOf('day').add(1,'day').format() ? 'Tomorrow' : 'Day2')) + '_Sunrise'].state).fromNow() + '</b>'"
style:
position: absolute
right: 3px
top: 3px
cursor: pointer
z-index: 998
color: var(--weather-card-text-color)
- component: f7-icon
config:
visible: "=(dayjs().add(loop.hour,'hour').startOf('hour').format() === dayjs(items[((!props.itemPrefix) ? '' : props.itemPrefix) + 'Forecast' + (dayjs().add(loop.hour,'hour').startOf('day').format() === dayjs().startOf('day').format() ? 'Today' : (dayjs().add(loop.hour,'hour').startOf('day').format() === dayjs().startOf('day').add(1,'day').format() ? 'Tomorrow' : 'Day2')) + '_Sunset'].state).startOf('hour').format() && props.sunIndicator === true)"
f7: moon_fill
size: 17px
tooltip: "=((!props.wordingSunset) ? 'Sunset at ' : props.wordingSunset + ' ') + ((!props.dateFormat) ? dayjs(items[((!props.itemPrefix) ? '' : props.itemPrefix) + 'Forecast' + (dayjs().add(loop.hour,'hour').startOf('day').format() === dayjs().startOf('day').format() ? 'Today' : (dayjs().add(loop.hour,'hour').startOf('day').format() === dayjs().startOf('day').add(1,'day').format() ? 'Tomorrow' : 'Day2')) + '_Sunset'].state).format('h:mm A') : dayjs(items[((!props.itemPrefix) ? '' : props.itemPrefix) + 'Forecast' + (dayjs().add(loop.hour,'hour').startOf('day').format() === dayjs().startOf('day').format() ? 'Today' : (dayjs().add(loop.hour,'hour').startOf('day').format() === dayjs().startOf('day').add(1,'day').format() ? 'Tomorrow' : 'Day2')) + '_Sunset'].state).format('H:mm') + (!props.timestampSuffix ? '' : ' ' + props.timestampSuffix) ) + '<br><b>' + dayjs(items[((!props.itemPrefix) ? '' : props.itemPrefix) + 'Forecast' + (dayjs().add(loop.hour,'hour').startOf('day').format() === dayjs().startOf('day').format() ? 'Today' : (dayjs().add(loop.hour,'hour').startOf('day').format() === dayjs().startOf('day').add(1,'day').format() ? 'Tomorrow' : 'Day2')) + '_Sunset'].state).fromNow() + '</b>'"
style:
position: absolute
right: 3px
top: 3px
cursor: pointer
z-index: 998
color: var(--weather-card-text-color)
- component: f7-row
config:
class:
- justify-content-center
slots:
default:
- component: Label
config:
text: "=(loop.hour === 0) ? ((!props.wordingNow) ? 'Now' : props.wordingNow) : ((!props.dateFormat) ? dayjs().add(loop.hour,'hour').startOf('hour').format('h A') : dayjs().add(loop.hour,'hour').startOf('hour').format('H')) + (!props.timestampSuffix ? '' : ' ' + props.timestampSuffix)"
style:
color: var(--weather-card-text-color)
text-transform: var(--weather-text-transform-time)
font-size: var(--weather-font-size-xsmall)
text-shadow: var(--weather-text-shadow-light)
font-weight: 700
- component: f7-col
config:
class:
- text-align-center
slots:
default:
- component: f7-icon
config:
f7: "=(items[((!props.itemPrefix) ? '' : props.itemPrefix) + ((loop.hour === 0) ? 'Current' : 'ForecastHours' + ((loop.hour_idx+1 < 10 ? '0'+(loop.hour_idx+1) : loop.hour_idx+1))) + '_Iconid'].state === '01d') ? 'sun_max_fill' : (items[((!props.itemPrefix) ? '' : props.itemPrefix) + ((loop.hour === 0) ? 'Current' : 'ForecastHours' + ((loop.hour_idx+1 < 10 ? '0'+(loop.hour_idx+1) : loop.hour_idx+1))) + '_Iconid'].state === '01n') ? 'moon_stars_fill' : (items[((!props.itemPrefix) ? '' : props.itemPrefix) + ((loop.hour === 0) ? 'Current' : 'ForecastHours' + ((loop.hour_idx+1 < 10 ? '0'+(loop.hour_idx+1) : loop.hour_idx+1))) + '_Iconid'].state === '02d') ? 'cloud_sun_fill' : (items[((!props.itemPrefix) ? '' : props.itemPrefix) + ((loop.hour === 0) ? 'Current' : 'ForecastHours' + ((loop.hour_idx+1 < 10 ? '0'+(loop.hour_idx+1) : loop.hour_idx+1))) + '_Iconid'].state === '02n') ? 'cloud_moon_fill' : (items[((!props.itemPrefix) ? '' : props.itemPrefix) + ((loop.hour === 0) ? 'Current' : 'ForecastHours' + ((loop.hour_idx+1 < 10 ? '0'+(loop.hour_idx+1) : loop.hour_idx+1))) + '_Iconid'].state === '03d') ? 'cloud_fill' : (items[((!props.itemPrefix) ? '' : props.itemPrefix) + ((loop.hour === 0) ? 'Current' : 'ForecastHours' + ((loop.hour_idx+1 < 10 ? '0'+(loop.hour_idx+1) : loop.hour_idx+1))) + '_Iconid'].state === '03n') ? 'cloud_fill' : (items[((!props.itemPrefix) ? '' : props.itemPrefix) + ((loop.hour === 0) ? 'Current' : 'ForecastHours' + ((loop.hour_idx+1 < 10 ? '0'+(loop.hour_idx+1) : loop.hour_idx+1))) + '_Iconid'].state === '04d') ? 'cloud_fill' : (items[((!props.itemPrefix) ? '' : props.itemPrefix) + ((loop.hour === 0) ? 'Current' : 'ForecastHours' + ((loop.hour_idx+1 < 10 ? '0'+(loop.hour_idx+1) : loop.hour_idx+1))) + '_Iconid'].state === '04n') ? 'cloud_fill' : (items[((!props.itemPrefix) ? '' : props.itemPrefix) + ((loop.hour === 0) ? 'Current' : 'ForecastHours' + ((loop.hour_idx+1 < 10 ? '0'+(loop.hour_idx+1) : loop.hour_idx+1))) + '_Iconid'].state === '09d') ? 'cloud_heavyrain_fill' : (items[((!props.itemPrefix) ? '' : props.itemPrefix) + ((loop.hour === 0) ? 'Current' : 'ForecastHours' + ((loop.hour_idx+1 < 10 ? '0'+(loop.hour_idx+1) : loop.hour_idx+1))) + '_Iconid'].state === '09n') ? 'cloud_heavyrain_fill' : (items[((!props.itemPrefix) ? '' : props.itemPrefix) + ((loop.hour === 0) ? 'Current' : 'ForecastHours' + ((loop.hour_idx+1 < 10 ? '0'+(loop.hour_idx+1) : loop.hour_idx+1))) + '_Iconid'].state === '10d') ? 'cloud_sun_rain_fill' : (items[((!props.itemPrefix) ? '' : props.itemPrefix) + ((loop.hour === 0) ? 'Current' : 'ForecastHours' + ((loop.hour_idx+1 < 10 ? '0'+(loop.hour_idx+1) : loop.hour_idx+1))) + '_Iconid'].state === '10n') ? 'cloud_moon_rain_fill' : (items[((!props.itemPrefix) ? '' : props.itemPrefix) + ((loop.hour === 0) ? 'Current' : 'ForecastHours' + ((loop.hour_idx+1 < 10 ? '0'+(loop.hour_idx+1) : loop.hour_idx+1))) + '_Iconid'].state === '11d') ? 'cloud_sun_bolt_fill' : (items[((!props.itemPrefix) ? '' : props.itemPrefix) + ((loop.hour === 0) ? 'Current' : 'ForecastHours' + ((loop.hour_idx+1 < 10 ? '0'+(loop.hour_idx+1) : loop.hour_idx+1))) + '_Iconid'].state === '11n') ? 'cloud_moon_bolt_fill' : (items[((!props.itemPrefix) ? '' : props.itemPrefix) + ((loop.hour === 0) ? 'Current' : 'ForecastHours' + ((loop.hour_idx+1 < 10 ? '0'+(loop.hour_idx+1) : loop.hour_idx+1))) + '_Iconid'].state === '13d') ? 'cloud_snow_fill' : (items[((!props.itemPrefix) ? '' : props.itemPrefix) + ((loop.hour === 0) ? 'Current' : 'ForecastHours' + ((loop.hour_idx+1 < 10 ? '0'+(loop.hour_idx+1) : loop.hour_idx+1))) + '_Iconid'].state === '13n') ? 'cloud_snow_fill' : (items[((!props.itemPrefix) ? '' : props.itemPrefix) + ((loop.hour === 0) ? 'Current' : 'ForecastHours' + ((loop.hour_idx+1 < 10 ? '0'+(loop.hour_idx+1) : loop.hour_idx+1))) + '_Iconid'].state === '50d') ? 'cloud_fog_fill' : (items[((!props.itemPrefix) ? '' : props.itemPrefix) + ((loop.hour === 0) ? 'Current' : 'ForecastHours' + ((loop.hour_idx+1 < 10 ? '0'+(loop.hour_idx+1) : loop.hour_idx+1))) + '_Iconid'].state === '50n') ? 'cloud_fog_fill' : '?'"
size: 48
style:
color: var(--weather-card-text-color)
text-shadow: var(--weather-text-shadow-strong)
padding-top: 5px
padding-bottom: 5px
- component: Label
config:
text: "=Math.round(items[((!props.itemPrefix) ? '' : props.itemPrefix) + ((loop.hour === 0) ? 'Current' : 'ForecastHours' + ((loop.hour_idx+1 < 10 ? '0'+(loop.hour_idx+1) : loop.hour_idx+1))) + '_Temperature'].state.split(' ')[0]) + '°'"
style:
font-size: var(--weather-font-size-normal)
font-weight: 400
color: var(--weather-card-text-color)
text-shadow: var(--weather-text-shadow-light)
- component: f7-swiper
config:
visible: =vars.tab === 'daily_forecast'
navigation: true
class:
- padding-top
params:
initalSlide: 0
grabCursor: true
observer: true
spaceBetween: 5
observeSlideChildren: true
updateOnWindowResize: true
watchOverflow: true
mousewheel: true
keyboard: true
breakpoints:
"0":
slidesPerView: 1
"210":
slidesPerView: "=(!props.forecastDays) ? 2 : ((props.forecastDays < 2) ? Math.round(Number(props.forecastDays) + 1) : 2)"
"480":
slidesPerView: "=(!props.forecastDays) ? 3 : ((props.forecastDays < 3) ? Math.round(Number(props.forecastDays) + 1) : 3)"
"640":
slidesPerView: "=(!props.forecastDays) ? 4 : ((props.forecastDays < 4) ? Math.round(Number(props.forecastDays) + 1) : 4)"
style:
--swiper-navigation-size: 30px
--swiper-navigation-color: var(--weather-card-text-color)
slots:
default:
- component: oh-repeater
config:
sourceType: range
for: day
rangeStart: 0
rangeStop: "=(!props.forecastDays) ? 3 : Number(props.forecastDays)"
fragment: true
slots:
default:
- component: f7-swiper-slide
config:
class: text-align-center
slots:
default:
- component: f7-row
config:
class:
- justify-content-center
- align-items-center
slots:
default:
- component: Label
config:
text: "=(loop.day === 0) ? ((!props.wordingToday) ? 'Today' : props.wordingToday) : dayjs().add(loop.day,'day').startOf('day').format('dddd')"
style:
color: var(--weather-card-text-color)
text-transform: var(--weather-text-transform-time)
font-size: var(--weather-font-size-xsmall)
text-shadow: var(--weather-text-shadow-light)
font-weight: 700
- component: f7-icon
config:
f7: "=(items[((!props.itemPrefix) ? '' : props.itemPrefix) + 'Forecast' + (((loop.day === 0) ? 'Today' : ((loop.day === 1) ? 'Tomorrow' : 'Day' + loop.day_idx))) + '_Iconid'].state === '01d') ? 'sun_max_fill' : (items[((!props.itemPrefix) ? '' : props.itemPrefix) + 'Forecast' + (((loop.day === 0) ? 'Today' : ((loop.day === 1) ? 'Tomorrow' : 'Day' + loop.day_idx))) + '_Iconid'].state === '01n') ? 'moon_stars_fill' : (items[((!props.itemPrefix) ? '' : props.itemPrefix) + 'Forecast' + (((loop.day === 0) ? 'Today' : ((loop.day === 1) ? 'Tomorrow' : 'Day' + loop.day_idx))) + '_Iconid'].state === '02d') ? 'cloud_sun_fill' : (items[((!props.itemPrefix) ? '' : props.itemPrefix) + 'Forecast' + (((loop.day === 0) ? 'Today' : ((loop.day === 1) ? 'Tomorrow' : 'Day' + loop.day_idx))) + '_Iconid'].state === '02n') ? 'cloud_moon_fill' : (items[((!props.itemPrefix) ? '' : props.itemPrefix) + 'Forecast' + (((loop.day === 0) ? 'Today' : ((loop.day === 1) ? 'Tomorrow' : 'Day' + loop.day_idx))) + '_Iconid'].state === '03d') ? 'cloud_fill' : (items[((!props.itemPrefix) ? '' : props.itemPrefix) + 'Forecast' + (((loop.day === 0) ? 'Today' : ((loop.day === 1) ? 'Tomorrow' : 'Day' + loop.day_idx))) + '_Iconid'].state === '03n') ? 'cloud_fill' : (items[((!props.itemPrefix) ? '' : props.itemPrefix) + 'Forecast' + (((loop.day === 0) ? 'Today' : ((loop.day === 1) ? 'Tomorrow' : 'Day' + loop.day_idx))) + '_Iconid'].state === '04d') ? 'cloud_fill' : (items[((!props.itemPrefix) ? '' : props.itemPrefix) + 'Forecast' + (((loop.day === 0) ? 'Today' : ((loop.day === 1) ? 'Tomorrow' : 'Day' + loop.day_idx))) + '_Iconid'].state === '04n') ? 'cloud_fill' : (items[((!props.itemPrefix) ? '' : props.itemPrefix) + 'Forecast' + (((loop.day === 0) ? 'Today' : ((loop.day === 1) ? 'Tomorrow' : 'Day' + loop.day_idx))) + '_Iconid'].state === '09d') ? 'cloud_heavyrain_fill' : (items[((!props.itemPrefix) ? '' : props.itemPrefix) + 'Forecast' + (((loop.day === 0) ? 'Today' : ((loop.day === 1) ? 'Tomorrow' : 'Day' + loop.day_idx))) + '_Iconid'].state === '09n') ? 'cloud_heavyrain_fill' : (items[((!props.itemPrefix) ? '' : props.itemPrefix) + 'Forecast' + (((loop.day === 0) ? 'Today' : ((loop.day === 1) ? 'Tomorrow' : 'Day' + loop.day_idx))) + '_Iconid'].state === '10d') ? 'cloud_sun_rain_fill' : (items[((!props.itemPrefix) ? '' : props.itemPrefix) + 'Forecast' + (((loop.day === 0) ? 'Today' : ((loop.day === 1) ? 'Tomorrow' : 'Day' + loop.day_idx))) + '_Iconid'].state === '10n') ? 'cloud_moon_rain_fill' : (items[((!props.itemPrefix) ? '' : props.itemPrefix) + 'Forecast' + (((loop.day === 0) ? 'Today' : ((loop.day === 1) ? 'Tomorrow' : 'Day' + loop.day_idx))) + '_Iconid'].state === '11d') ? 'cloud_sun_bolt_fill' : (items[((!props.itemPrefix) ? '' : props.itemPrefix) + 'Forecast' + (((loop.day === 0) ? 'Today' : ((loop.day === 1) ? 'Tomorrow' : 'Day' + loop.day_idx))) + '_Iconid'].state === '11n') ? 'cloud_moon_bolt_fill' : (items[((!props.itemPrefix) ? '' : props.itemPrefix) + 'Forecast' + (((loop.day === 0) ? 'Today' : ((loop.day === 1) ? 'Tomorrow' : 'Day' + loop.day_idx))) + '_Iconid'].state === '13d') ? 'cloud_snow_fill' : (items[((!props.itemPrefix) ? '' : props.itemPrefix) + 'Forecast' + (((loop.day === 0) ? 'Today' : ((loop.day === 1) ? 'Tomorrow' : 'Day' + loop.day_idx))) + '_Iconid'].state === '13n') ? 'cloud_snow_fill' : (items[((!props.itemPrefix) ? '' : props.itemPrefix) + 'Forecast' + (((loop.day === 0) ? 'Today' : ((loop.day === 1) ? 'Tomorrow' : 'Day' + loop.day_idx))) + '_Iconid'].state === '50d') ? 'cloud_fog_fill' : (items[((!props.itemPrefix) ? '' : props.itemPrefix) + 'Forecast' + (((loop.day === 0) ? 'Today' : ((loop.day === 1) ? 'Tomorrow' : 'Day' + loop.day_idx))) + '_Iconid'].state === '50n') ? 'cloud_fog_fill' : '?'"
size: 48
style:
color: var(--weather-card-text-color)
text-shadow: var(--weather-text-shadow-strong)
padding-top: 5px
padding-bottom: 5px
- component: f7-row
config:
class:
- justify-content-center
slots:
default:
- component: Label
config:
text: "=Math.round(items[((!props.itemPrefix) ? '' : props.itemPrefix) + 'Forecast' + ((loop.day === 0) ? 'Today' : ((loop.day === 1) ? 'Tomorrow' : 'Day' + (loop.day_idx))) + '_Maxtemperature'].state.split(' ')[0])+'°'"
style:
font-size: var(--weather-font-size-normal)
font-weight: 400
color: var(--weather-card-text-color)
text-shadow: var(--weather-text-shadow-light)
- component: Label
config:
text: =" | "
style:
font-size: var(--weather-font-size-normal)
color: rgba(255,255,255,.2)
- component: Label
config:
text: "=Math.round(items[((!props.itemPrefix) ? '' : props.itemPrefix) + 'Forecast' + ((loop.day === 0) ? 'Today' : ((loop.day === 1) ? 'Tomorrow' : 'Day' + (loop.day_idx))) + '_Mintemperature'].state.split(' ')[0])+'°'"
style:
font-size: var(--weather-font-size-normal)
font-weight: 400
color: var(--weather-card-text-color)
text-shadow: var(--weather-text-shadow-light)
- component: f7-swiper
config:
visible: =vars.tab === 'precip_forecast'
navigation: true
class:
- padding-top
params:
initalSlide: 0
runCallbacksOnInit: true
grabCursor: true
observer: true
observeSlideChildren: true
updateOnWindowResize: true
spaceBetween: 5
mousewheel: true
keyboard: true
watchOverflow: true
breakpoints:
"0":
slidesPerView: 1
"240":
slidesPerView: 2
"320":
slidesPerView: 3
"480":
slidesPerView: 4
"640":
slidesPerView: 5
style:
--swiper-navigation-size: 30px
--swiper-navigation-color: var(--weather-card-text-color)
slots:
default:
- component: oh-repeater
config:
sourceType: range
for: hour
rangeStart: 0
rangeStop: "=(props.forecastHours === undefined) ? 12 : Number(props.forecastHours)"
fragment: true
slots:
default:
- component: f7-swiper-slide
config:
id: =loop.hour_idx
expandable: true
style:
background: "=(props.sunIndicator) ? ((dayjs().add(loop.hour,'hour').format() >= items[((!props.itemPrefix) ? '' : props.itemPrefix) + 'Forecast' + (dayjs().add(loop.hour,'hour').startOf('day').format() === dayjs().startOf('day').format() ? 'Today' : (dayjs().add(loop.hour,'hour').startOf('day').format() === dayjs().startOf('day').add(1,'day').format() ? 'Tomorrow' : 'Day2')) + '_Sunrise'].state && dayjs().add(loop.hour,'hour').format() <= items[((!props.itemPrefix) ? '' : props.itemPrefix) + 'Forecast' + (dayjs().add(loop.hour,'hour').startOf('day').format() === dayjs().startOf('day').format() ? 'Today' : (dayjs().add(loop.hour,'hour').startOf('day').format() === dayjs().startOf('day').add(1,'day').format() ? 'Tomorrow' : 'Day2')) + '_Sunset'].state) ? (!props.sunIndicatorColorDay ? 'rgba(255,255,255,.2)' : props.sunIndicatorColorDay) : (!props.sunIndicatorColorNight ? 'rgba(41,109,152,.2)' : props.sunIndicatorColorNight)) : 'none'"
border-radius: 5px
slots:
default:
- component: f7-icon
config:
visible: "=(dayjs().add(loop.hour,'hour').startOf('hour').format() === dayjs(items[((!props.itemPrefix) ? '' : props.itemPrefix) + 'Forecast' + (dayjs().add(loop.hour,'hour').startOf('day').format() === dayjs().startOf('day').format() ? 'Today' : (dayjs().add(loop.hour,'hour').startOf('day').format() === dayjs().startOf('day').add(1,'day').format() ? 'Tomorrow' : 'Day2')) + '_Sunrise'].state).startOf('hour').format() && props.sunIndicator === true)"
f7: sun_max_fill
size: 17px
tooltip: "=((!props.wordingSunrise) ? 'Sunrise at ' : props.wordingSunrise + ' ') + ((!props.dateFormat) ? dayjs(items[((!props.itemPrefix) ? '' : props.itemPrefix) + 'Forecast' + (dayjs().add(loop.hour,'hour').startOf('day').format() === dayjs().startOf('day').format() ? 'Today' : (dayjs().add(loop.hour,'hour').startOf('day').format() === dayjs().startOf('day').add(1,'day').format() ? 'Tomorrow' : 'Day2')) + '_Sunrise'].state).format('h:mm A') : dayjs(items[((!props.itemPrefix) ? '' : props.itemPrefix) + 'Forecast' + (dayjs().add(loop.hour,'hour').startOf('day').format() === dayjs().startOf('day').format() ? 'Today' : (dayjs().add(loop.hour,'hour').startOf('day').format() === dayjs().startOf('day').add(1,'day').format() ? 'Tomorrow' : 'Day2')) + '_Sunrise'].state).format('H:mm') + (!props.timestampSuffix ? '' : ' ' + props.timestampSuffix) ) + '<br><b>' + dayjs(items[((!props.itemPrefix) ? '' : props.itemPrefix) + 'Forecast' + (dayjs().add(loop.hour,'hour').startOf('day').format() === dayjs().startOf('day').format() ? 'Today' : (dayjs().add(loop.hour,'hour').startOf('day').format() === dayjs().startOf('day').add(1,'day').format() ? 'Tomorrow' : 'Day2')) + '_Sunrise'].state).fromNow() + '</b>'"
style:
position: absolute
right: 3px
top: 3px
cursor: pointer
z-index: 998
color: var(--weather-card-text-color)
- component: f7-icon
config:
visible: "=(dayjs().add(loop.hour,'hour').startOf('hour').format() === dayjs(items[((!props.itemPrefix) ? '' : props.itemPrefix) + 'Forecast' + (dayjs().add(loop.hour,'hour').startOf('day').format() === dayjs().startOf('day').format() ? 'Today' : (dayjs().add(loop.hour,'hour').startOf('day').format() === dayjs().startOf('day').add(1,'day').format() ? 'Tomorrow' : 'Day2')) + '_Sunset'].state).startOf('hour').format() && props.sunIndicator === true)"
f7: moon_fill
size: 17px
tooltip: "=((!props.wordingSunset) ? 'Sunset at ' : props.wordingSunset + ' ') + ((!props.dateFormat) ? dayjs(items[((!props.itemPrefix) ? '' : props.itemPrefix) + 'Forecast' + (dayjs().add(loop.hour,'hour').startOf('day').format() === dayjs().startOf('day').format() ? 'Today' : (dayjs().add(loop.hour,'hour').startOf('day').format() === dayjs().startOf('day').add(1,'day').format() ? 'Tomorrow' : 'Day2')) + '_Sunset'].state).format('h:mm A') : dayjs(items[((!props.itemPrefix) ? '' : props.itemPrefix) + 'Forecast' + (dayjs().add(loop.hour,'hour').startOf('day').format() === dayjs().startOf('day').format() ? 'Today' : (dayjs().add(loop.hour,'hour').startOf('day').format() === dayjs().startOf('day').add(1,'day').format() ? 'Tomorrow' : 'Day2')) + '_Sunset'].state).format('H:mm') + (!props.timestampSuffix ? '' : ' ' + props.timestampSuffix) ) + '<br><b>' + dayjs(items[((!props.itemPrefix) ? '' : props.itemPrefix) + 'Forecast' + (dayjs().add(loop.hour,'hour').startOf('day').format() === dayjs().startOf('day').format() ? 'Today' : (dayjs().add(loop.hour,'hour').startOf('day').format() === dayjs().startOf('day').add(1,'day').format() ? 'Tomorrow' : 'Day2')) + '_Sunset'].state).fromNow() + '</b>'"
style:
position: absolute
right: 3px
top: 3px
cursor: pointer
z-index: 998
color: var(--weather-card-text-color)
- component: f7-row
config:
class:
- justify-content-center
- align-items-center
slots:
default:
- component: Label
config:
text: "=(loop.hour === 0) ? ((!props.wordingNow) ? 'Now' : props.wordingNow) : ((!props.dateFormat) ? dayjs().add(loop.hour,'hour').startOf('hour').format('h A') : dayjs().add(loop.hour,'hour').startOf('hour').format('H')) + (!props.timestampSuffix ? '' : ' ' + props.timestampSuffix)"
style:
color: var(--weather-card-text-color)
text-transform: var(--weather-text-transform-time)
font-size: var(--weather-font-size-xsmall)
font-weight: 700
text-shadow: var(--weather-text-shadow-light)
- component: f7-col
config:
class:
- justify-content-center
- align-items-center
- text-align-center
slots:
default:
- component: f7-icon
config:
f7: umbrella_fill
size: 48
style:
color: var(--weather-card-text-color)
text-shadow: var(--weather-text-shadow-strong)
padding-top: 5px
padding-bottom: 5px
- component: Label
config:
text: "=Math.round(items[((!props.itemPrefix) ? '' : props.itemPrefix) + 'ForecastHours' + ((loop.hour_idx+1 < 10 ? '0'+(loop.hour_idx+1) : loop.hour_idx+1)) + '_Precipprobability'].state.split(' ')[0] * 100) + '%'"
style:
font-size: var(--weather-font-size-normal)
font-weight: 400
color: var(--weather-card-text-color)
text-shadow: var(--weather-text-shadow-light)
Viele Grüße
Andy
Hi all,
I just wanted to share my refactored version of the weatherCard widget.
This new version makes use of the TimeSeries support (since openHAB 4.1.0) that allows to persist forecasts.
You can now finally get rid of the huge amount of hourly and daily forecast channels and Items that were required to use this widget, and I feel like this improves performance of Main UI pages where the widget is added.
The refactored version looks the same, but uses the new oh-context component to reduce code duplication and gets its data from a String Item that holds a JSON (this is created by a JavaScript rule on the server).
You might wonder why the widget does not directly request the forecast data from persistence, there are two reasons: a. I haven’t found a way to implement this in a nice way (yet) and b. I think performance-wise it anyway is better to collect the required data server-side - this way the client has to do less requests and less computations itself.
You can check out my refactored version on GitHub, where the required dependencies are provided as well:
EDIT: I should probably add that this version makes use of the oh-context component, which is broken in openHAB 4.2.0 — I have already created a fix and backported it to 4.2.x so it will be available in the next patch release, but until then you either need to use a recent UI JAR or upgrade to snapshot.
Hello Florian,
I’ve created the rule with the JSON file on my server in /etc/openhab/automation/js/openweathermap.js
I am unable to find the JSON item while configuring the widget in the list. Already restarted the openhab service.
What should be the name of it? Guess it should be openweathermap or something like this. But I cant find anything, just the items.
Regards.
The Item is included in the openweathermap.items and should be named OneCall_JSON.
Thank you. Possible to share your openweathermap.things syntax?
No. I have these Things defined in the UI …
You can basically create the account and OneCall Thing as you like and then replace the channel UIDs in the items file using search and replace.
Hi @florian-h05, I get this error when the openweathermap.js script runs. I added a log line to see what it is looking for just before where I think it fails. Do I need to wait longer to get data into the Item persistence? items.getItem(PREFIX + FORECAST + HOURLY + field).persistence must be returning null, but why?
2024-12-11 20:25:00.089 [INFO ] [mation.script.file.openweathermap.js] - looking for: OneCall_Forecast_Hourly_IconId
2024-12-11 20:25:00.091 [ERROR] [mation.script.file.openweathermap.js] - Failed to execute rule owm-one-call-weather-forecast-json: TypeError: undefined has no such function "getAllStatesBetween": TypeError: undefined has no such function "getAllStatesBetween"
at addHourlyValues (openweathermap.js:62)
at createJSON (openweathermap.js:181)
at <program> (openweathermap.js:196)
at _run (/etc/openhab/automation/js/node_modules/openhab/src/rules/rule-builder.js:53)
at execute (/etc/openhab/automation/js/node_modules/openhab/src/rules/rule-builder.js:99)
at doExecute (/etc/openhab/automation/js/node_modules/openhab/src/rules/rules.js:235)
2024-12-11 20:25:00.092 [ERROR] [e.automation.internal.RuleEngineImpl] - Failed to execute rule 'owm-one-call-weather-forecast-json': Failed to execute action: 1(Error: Failed to execute rule owm-one-call-weather-forecast-json: TypeError: undefined has no such function "getAllStatesBetween": TypeError: undefined has no such function "getAllStatesBetween"
at addHourlyValues (openweathermap.js:62)
at createJSON (openweathermap.js:181)
at <program> (openweathermap.js:196)
at _run (/etc/openhab/automation/js/node_modules/openhab/src/rules/rule-builder.js:53)
at execute (/etc/openhab/automation/js/node_modules/openhab/src/rules/rule-builder.js:99)
at doExecute (/etc/openhab/automation/js/node_modules/openhab/src/rules/rules.js:235))
I can see the “OneCall_Forecast_Hourly_IconId” in the event.log. Does this help debug?
I’m on OH 4.3.M5
2024-12-11 19:34:54.665 [INFO ] [openhab.event.ItemTimeSeriesEvent ] - Item 'OneCall_Forecast_Hourly_IconId' shall process timeseries [Entry[timestamp=2024-12-12T03:00:00Z, state=02n], Entry[timestamp=2024-12-12T04:00:00Z, state=01n], Entry[timestamp=2024-12-12T05:00:00Z, state=02n], Entry[timestamp=2024-12-12T06:00:00Z, state=03n], Entry[timestamp=2024-12-12T07:00:00Z, state=04n], Entry[timestamp=2024-12-12T08:00:00Z, state=10n], Entry[timestamp=2024-12-12T09:00:00Z, state=10n], Entry[timestamp=2024-12-12T10:00:00Z, state=10n], Entry[timestamp=2024-12-12T11:00:00Z, state=10n], Entry[timestamp=2024-12-12T12:00:00Z, state=10n], Entry[timestamp=2024-12-12T13:00:00Z, state=10n], Entry[timestamp=2024-12-12T14:00:00Z, state=10n], Entry[timestamp=2024-12-12T15:00:00Z, state=10n], Entry[timestamp=2024-12-12T16:00:00Z, state=10d], Entry[timestamp=2024-12-12T17:00:00Z, state=10d], Entry[timestamp=2024-12-12T18:00:00Z, state=10d], Entry[timestamp=2024-12-12T19:00:00Z, state=03d], Entry[timestamp=2024-12-12T20:00:00Z, state=03d], Entry[timestamp=2024-12-12T21:00:00Z, state=03d], Entry[timestamp=2024-12-12T22:00:00Z, state=10d], Entry[timestamp=2024-12-12T23:00:00Z, state=10d], Entry[timestamp=2024-12-13T00:00:00Z, state=10d], Entry[timestamp=2024-12-13T01:00:00Z, state=10n], Entry[timestamp=2024-12-13T02:00:00Z, state=10n], Entry[timestamp=2024-12-13T03:00:00Z, state=10n], Entry[timestamp=2024-12-13T04:00:00Z, state=10n], Entry[timestamp=2024-12-13T05:00:00Z, state=10n], Entry[timestamp=2024-12-13T06:00:00Z, state=10n], Entry[timestamp=2024-12-13T07:00:00Z, state=10n], Entry[timestamp=2024-12-13T08:00:00Z, state=10n], Entry[timestamp=2024-12-13T09:00:00Z, state=04n], Entry[timestamp=2024-12-13T10:00:00Z, state=04n], Entry[timestamp=2024-12-13T11:00:00Z, state=04n], Entry[timestamp=2024-12-13T12:00:00Z, state=04n], Entry[timestamp=2024-12-13T13:00:00Z, state=04n], Entry[timestamp=2024-12-13T14:00:00Z, state=10n], Entry[timestamp=2024-12-13T15:00:00Z, state=10n], Entry[timestamp=2024-12-13T16:00:00Z, state=10d], Entry[timestamp=2024-12-13T17:00:00Z, state=04d], Entry[timestamp=2024-12-13T18:00:00Z, state=04d], Entry[timestamp=2024-12-13T19:00:00Z, state=04d], Entry[timestamp=2024-12-13T20:00:00Z, state=04d], Entry[timestamp=2024-12-13T21:00:00Z, state=04d], Entry[timestamp=2024-12-13T22:00:00Z, state=04d], Entry[timestamp=2024-12-13T23:00:00Z, state=04d], Entry[timestamp=2024-12-14T00:00:00Z, state=04d], Entry[timestamp=2024-12-14T01:00:00Z, state=04n], Entry[timestamp=2024-12-14T02:00:00Z, state=04n]]
2024-12-11 19:34:54.667 [INFO ] [hab.event.ItemTimeSeriesUpdatedEvent] - Item 'OneCall_Forecast_Hourly_IconId' updated timeseries [Entry[timestamp=2024-12-12T03:00:00Z, state=02n], Entry[timestamp=2024-12-12T04:00:00Z, state=01n], Entry[timestamp=2024-12-12T05:00:00Z, state=02n], Entry[timestamp=2024-12-12T06:00:00Z, state=03n], Entry[timestamp=2024-12-12T07:00:00Z, state=04n], Entry[timestamp=2024-12-12T08:00:00Z, state=10n], Entry[timestamp=2024-12-12T09:00:00Z, state=10n], Entry[timestamp=2024-12-12T10:00:00Z, state=10n], Entry[timestamp=2024-12-12T11:00:00Z, state=10n], Entry[timestamp=2024-12-12T12:00:00Z, state=10n], Entry[timestamp=2024-12-12T13:00:00Z, state=10n], Entry[timestamp=2024-12-12T14:00:00Z, state=10n], Entry[timestamp=2024-12-12T15:00:00Z, state=10n], Entry[timestamp=2024-12-12T16:00:00Z, state=10d], Entry[timestamp=2024-12-12T17:00:00Z, state=10d], Entry[timestamp=2024-12-12T18:00:00Z, state=10d], Entry[timestamp=2024-12-12T19:00:00Z, state=03d], Entry[timestamp=2024-12-12T20:00:00Z, state=03d], Entry[timestamp=2024-12-12T21:00:00Z, state=03d], Entry[timestamp=2024-12-12T22:00:00Z, state=10d], Entry[timestamp=2024-12-12T23:00:00Z, state=10d], Entry[timestamp=2024-12-13T00:00:00Z, state=10d], Entry[timestamp=2024-12-13T01:00:00Z, state=10n], Entry[timestamp=2024-12-13T02:00:00Z, state=10n], Entry[timestamp=2024-12-13T03:00:00Z, state=10n], Entry[timestamp=2024-12-13T04:00:00Z, state=10n], Entry[timestamp=2024-12-13T05:00:00Z, state=10n], Entry[timestamp=2024-12-13T06:00:00Z, state=10n], Entry[timestamp=2024-12-13T07:00:00Z, state=10n], Entry[timestamp=2024-12-13T08:00:00Z, state=10n], Entry[timestamp=2024-12-13T09:00:00Z, state=04n], Entry[timestamp=2024-12-13T10:00:00Z, state=04n], Entry[timestamp=2024-12-13T11:00:00Z, state=04n], Entry[timestamp=2024-12-13T12:00:00Z, state=04n], Entry[timestamp=2024-12-13T13:00:00Z, state=04n], Entry[timestamp=2024-12-13T14:00:00Z, state=10n], Entry[timestamp=2024-12-13T15:00:00Z, state=10n], Entry[timestamp=2024-12-13T16:00:00Z, state=10d], Entry[timestamp=2024-12-13T17:00:00Z, state=04d], Entry[timestamp=2024-12-13T18:00:00Z, state=04d], Entry[timestamp=2024-12-13T19:00:00Z, state=04d], Entry[timestamp=2024-12-13T20:00:00Z, state=04d], Entry[timestamp=2024-12-13T21:00:00Z, state=04d], Entry[timestamp=2024-12-13T22:00:00Z, state=04d], Entry[timestamp=2024-12-13T23:00:00Z, state=04d], Entry[timestamp=2024-12-14T00:00:00Z, state=04d], Entry[timestamp=2024-12-14T01:00:00Z, state=04n], Entry[timestamp=2024-12-14T02:00:00Z, state=04n]]
The OneCall_Forecast_Hourly_IconId state is NULL - is that expected?
Have you double checked your persistence config?
You can check if persistence works by opening the analyzer e.g. for the temperature forecast and selecting InMemory persistence service and a future period.
Yes, it will only have a value once the first entry of the timeseries becomes valid, e.g. at the next full hour.
Still struggling with this. I have now verified that I can see the daily max temperature data in a chart from the forecasted data. I’m using InfluxDB v1 and debugging inmemory on the side.
I added a couple more log lines, but somehow the ‘persistence’ object isn’t defined. What am I missing?
Here’s the code section with log lines added (focusing on daily max temperature):
console.log("looking for: " + PREFIX + FORECAST + DAILY + field);
console.log("Item label is: " + items.getItem(PREFIX + FORECAST + DAILY + field).label);
var maxTempPersistence = items.getItem(PREFIX + FORECAST + DAILY + field).persistence;
console.log("object type is: " + typeof(maxTempPersistence));
items.getItem(PREFIX + FORECAST + DAILY + field).persistence.getAllStatesBetween(beginOfDay, beginOfDay.plusDays(days), PERSISTENCE).forEach((s, i) => {
And here we see that .getAllStatesBetween fails. Shouldn’t the ‘.persistence’ have a type? I’m sure I have something misconfigured, but I can’t find it. What else can I do to debug?
2024-12-13 14:55:00.821 [INFO ] [mation.script.file.openweathermap.js] - looking for: OneCall_Forecast_Daily_MaxTemperature
2024-12-13 14:55:00.821 [INFO ] [mation.script.file.openweathermap.js] - Item label is: Max. Temperatur
2024-12-13 14:55:00.821 [INFO ] [mation.script.file.openweathermap.js] - object type is: undefined
2024-12-13 14:55:00.823 [ERROR] [mation.script.file.openweathermap.js] - Failed to execute rule owm-one-call-weather-forecast-json: TypeError: undefined has no such function "getAllStatesBetween": TypeError: undefined has no such function "getAllStatesBetween"
at addDailyValues (openweathermap.js:102)
at createJSON (openweathermap.js:184)
at <program> (openweathermap.js:204)
at _run (/etc/openhab/automation/js/node_modules/openhab/src/rules/rule-builder.js:53)
at execute (/etc/openhab/automation/js/node_modules/openhab/src/rules/rule-builder.js:99)
at doExecute (/etc/openhab/automation/js/node_modules/openhab/src/rules/rules.js:235)
What is your openHAB version?
Do you have openhab-js manually installed?
OH 4.3.M5. Yes, manually installed. I tried an ‘npm i openhab’, but nothing changed. So I did the npm remove openhab and npm install openhab and it bumped the version from 4.9.0 to 5.8.1. And it works perfectly! That’s the hint I needed. Thank you.
One suggestion for your github weather widget page is add a line or two on the need to set up persistence with the forecast strategy for the gOWM_TimeSeries* in the Configuration section. The github page has good instructions except it doesn’t mention persistence (unless I missed it). It’s necessary to read through this thread to realize that must also be set.
It’s a very good widget - thanks for developing it.
Done
Hello Florian,
Greeting from snowy Alaska.
I seem to have a type issue with my icon-id and creating the JSON file when the openweathermap.js runs
I’m running OH ver 4.3. I configured all of the items in the item file from the UI, and they are all populating. I created the JSON item and it remaines null.
The items file from gethub shows that the Icons should be a String.
String OneCall_Current_IconId (gOWM) {channel=“openweathermap:onecall:bridge:local:current#icon-id”}
the channels from the One Call item show ICONS to be an Image.
The Persistance appears to be working for the icon as an image,
But the openweathermap.js throws this error when it runs:
looking at the script it does appear to be expecting a string NOT an image.
Stumped on how to resolve. I checked openweathermap, but can’t find any settings for the oneCall item. Any assistance would be appreciated.
Hi Eric,
you linked the wrong channel.
You need to link the icon-id channel to the IconId Item, I guess the icon-id channel might be advanced, in that case enable showing advanced channels.
Thank you SO much. the Icon ID was in fact an advanced Channel Somehow I missed this checkbox. I recreated the Icon ID with the correct channel and it works perfectly.
Thank you so much for creating a wonderful widget and for helping me find my way.
Is it possible to swap the Hourly with the Daily at the bottom of the widget? Is it as straightforward as finding the sections in the widget corresponding to these and swapping them? Or is there more work involved? I’m more interested in daily than hourly. Thanks.
It is as straightforward as finding and swapping the buttons that open them