UI Widget: Weather

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.

2 Likes

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

1 Like

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.

4 Likes

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.

1 Like

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.

1 Like

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.

1 Like

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.

1 Like

Done :+1:

1 Like

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.

1 Like

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 :+1:

1 Like