[OH3] Heating widget based on CSS

Tags: #<Tag:0x00007fc8e97793b8> #<Tag:0x00007fc8e97792f0> #<Tag:0x00007fc8e9779228>

Hey guys,
I’m not a programmer, I tried to change it for half the day, but failed.
Would it be possible to separate the large displayed temperature in the input?
(a selection which is displayed)
I would prefer to see the current temperature than the set target temperature.
bad

I mean in this example it would now be 18,5 instead of 6 in the middle
Thx !!!

sy - Google translated

Hey guys, @fibu-freak , @doctor64 , @Peters_Bastelecke

I’am in progress to implement the some enhancements.
Thanks for the posts, the good feedback and the patience.

It will take a little bit time.

all the best,

Nico

2 Likes

Yes, of course some local modification sometimes needed. I, for example, use MAX! EQ-3 thermostats and zigbee Xiaomi window sensors.
About heater item - i also don’t use electric heating and my thermostat does not return heated state, but it can report valve open state. So I create additional item make simple rule:

Number GF_Living_Radiator_Valve  "Valve position [%d %%]" <flowpipe> (GF_Living_Thermostat, MaxValve)  ["OpenLevel"]  {channel="max:thermostat:NEQ1208352:thermostatLiving:valve"}
String  GF_Living_Radiator_Heated "Heated [%s]" <heating> (GF_Living_Thermostat) ["Status"]
rule "Heating indication"
when
    Member of gMaxValve changed
then 
    val changedValve = triggeringItem
    val String[] thermostatNameArr=changedValve.name.split("_")
    val String thermostatIndicator=thermostatNameArr.get(0)+"_"+thermostatNameArr.get(1)+"_"+thermostatNameArr.get(2)+"_"+"Heated"
    logInfo("HeatingUpdate", "triggered=" + changedValve + " Indicator name:" + thermostatIndicator)
    if (changedValve.state > 0 ) thermostatIndicator.sendCommand("ON") else thermostatIndicator.sendCommand("OFF")
end

And it gives me fancy icon on location card and on heating widget.
May be it will be useful, I don’t know is your thermostat able to return valve open status.

1 Like

Sorry, can not check right now, but looks like you need to change line

from:       text: "=props.unit ? items[props.setPointItem].state.split(' ')[0] + '' + props.unit : items[props.setPointItem].state.split(' ')[0]"
to:         text: "=props.unit ? items[props.currentPointItem].state.split(' ')[0] + '' + props.unit : items[props.currentPointItem].state.split(' ')[0]"

It’s difficult to say correct line number because i have modified widget source at hand, but it near end of widget code. Hope this helps.

1 Like

Hi Alex,
thx for this tip, - it works

Hi guys,

Thanks again for the positive feedback. I am pleased that so many people are interested in the widget.

Hi @fibu-freak,
based on your suggestion ([OH3] Heating widget based on CSS - #18 by fibu-freak) with a list-card, i have inserted two optional setting buttons. I think this fits in better with the design and has even more potential. See under the enhancements Heating-Mode- Itemand Example-Mode-Item
I hope you like the solution :slight_smile:

The implementation of humidity will follow.

The Logo-URL can be an Web-Adress or an static-path. I define a prop for the Image-URL. More under enhancements.

Hi @doctor64,
The design should be better now for small widget. You can define the size of the widget and now the size of all fonts.

In general: The implementation of a battery indicator will follow

I hope you all like the new version v1.8. Have fun and have a nice weekend.

all the best,

Nico
v1.8

Enhancements:

  • Implementation of the heating mode and an example mode placeholder.
    The item must be a string item. (props: heatingModeItem and exampleModeItem)
    The strings to be sent are defined in an array e.g. ,OFF, ON, VACATION, AUTO’’ (props: heatingModeArray and exampleModeArray
    The buttons are embedded dynamically after entering the props.
    If only one item is used, only one icon is displayed. If you use two items, both icons rotate into a corresponding position.

grafik grafik

  • Toogle item is no longer applicable in the widget. If this is required, it can also be implemented using buttons

  • The url for the logo can be defined in props and is no longer static in the code.
    Tipp: Instead of an Internet address, a logo can also be saved in the folder.
    Then use in the prop the follow path (eg):
    http://local-IP-Adress:8080/static/folder/img.svg

  • All font sizes can be defined using parameters. This means that even small widgets can be implemented responsively.

  • Animation added in the center that can be used to visualize a valve item or an alarm item
    Now the item must be of the type Switch, but it’s easy to switch to an Contact-Item if you want.

Bug-fixing:

  • Add box-sizing: content-box to the parent container. Now the circle is really a circle too :wink:.
1 Like

Here the v1.8 YAML. Too many characters for one post :wink:

Edit: please use the v1.8.1. The v1.8 YAML is removed

6 Likes

Hello, the widget works great! However I have a problem in my setup with the heating mode items. With one single instance of the widget, it works great but when I add a second (or more) the heating mode status won’t be passed to the defined item.

The second widget will change last added widget heating mode item instead of their own widget item.
I have tried to figure it out myself but I lack knownlegde about YAML. I am not sure if the problem is in my setup or in the widget. I hope you can help me to get on the right track.

Below I have added information of my setup and problem.

This is my first post and could not figure out yet to add an animation… so here a link to the gif to clarify my problem:

https://u.pcloud.link/publink/show?code=kZp0cjXZfNTPeH2aYSu3IRuuv8ayv4FGHQ7V
HeaterWidget_Problem

Items
    String HW_BG_Heating_Mode       "Verwarmings Modus [%s]"    (g_HeatingMode)
    String HW_EV_Heating_Mode       "Verwarmings Modus [%s]"    (g_HeatingMode)
    String BW_Heating_Mode          "Verwarmings Modus [%s]"    (g_HeatingMode)
    String BW_SK_Heating_Mode       "Verwarmings Modus [%s]"    (g_HeatingMode)
Widget configuration
  - component: oh-block
    config:
      title: Hoofdwoning
    slots:
      default:
        - component: oh-grid-row
          config: {}
          slots:
            default:
              - component: oh-grid-col
                config: {}
                slots:
                  default:
                    - component: widget:wThermostatGauge_V1.8
                      config:
                        location: Beganegrond
                        size: "300"
                        minTemp: "15"
                        maxTemp: "25"
                        setPointItem: HW_BG_Temperatuur_Setpoint
                        currentPointItem: gT_HW_BG
                        unit: °C
                        imgUrl: http://10.0.0.30:8080/static/_empty.png
                        heatingModeArray: Automatisch,Handmatig,Uit
                        valveItem: gHA_HW_BG
                        fontSizeMarker: .75em
                        fontSizeCenter: 2.6em
                        fontSizeButtons: 1,8em
                        fontSizeFooter: 1em
                        heatingModeItem: HW_BG_Heating_Mode
              - component: oh-grid-col
                config: {}
                slots:
                  default:
                    - component: widget:wThermostatGauge_V1.8
                      config:
                        location: Eerste verdieping
                        size: "300"
                        minTemp: "15"
                        maxTemp: "25"
                        setPointItem: HW_EV_Temperatuur_Setpoint
                        currentPointItem: gT_HW_EV
                        unit: °C
                        imgUrl: http://10.0.0.30:8080/static/_empty.png
                        heatingModeArray: Automatisch,Handmatig,Uit
                        heatingModeItem: HW_EV_Heating_Mode
                        valveItem: gHA_HW_EV
                        fontSizeMarker: .75em
                        fontSizeCenter: 2.6em
                        fontSizeButtons: 1,8em
                        fontSizeFooter: 1em
        - component: oh-grid-row
          config: {}
          slots:
            default:
              - component: oh-grid-col
                config: {}
                slots:
                  default:
                    - component: oh-list-card
                      config: {}
                      slots:
                        default:
                          - component: oh-slider-item
                            config:
                              item: HW_BG_Temperatuur_Setpoint
                              min: 15
                              max: 25
                              step: 0.5
                              scale: true
                          - component: oh-slider-item
                            config:
                              item: gT_HW_BG
                              min: 15
                              max: 25
                              step: 0.5
                              scale: true
                          - component: oh-label-item
                            config:
                              item: gHA_HW_BG
                          - component: oh-label-item
                            config:
                              item: HW_BG_Heating_Mode
              - component: oh-grid-col
                config: {}
                slots:
                  default:
                    - component: oh-list-card
                      config: {}
                      slots:
                        default:
                          - component: oh-slider-item
                            config:
                              item: HW_EV_Temperatuur_Setpoint
                              min: 15
                              max: 25
                              step: 0.5
                              scale: true
                          - component: oh-slider-item
                            config:
                              item: gT_HW_EV
                              min: 15
                              max: 25
                              step: 0.5
                              scale: true
                          - component: oh-label-item
                            config:
                              item: gHA_HW_EV
                          - component: oh-label-item
                            config:
                              item: HW_EV_Heating_Mode

Hello, the widget works great. a warning message only ever comes up when the page is called up for the first time. It doesn’t matter whether I have entered items or not.

2021-02-21 10:56:35.824 [WARN ] [e.internal.SseItemStatesEventBuilder] - Attempting to send a state update of an item which doesn't exist: undefined

Hi @RowanS

sorry here is a bug in the popover code. I define a identifier to control this proplem.
Now the location props is required and this prop is the identifier if you use more than one widget.

@Tallman I cant reproduce this error. Pls try again and check all needed and referenced items.

all the best,
Nico

V1.8.1
uid: widget_HeatingCSS_1.8.1
tags: []
props:
  parameters:
    - description: eg. living room
      label: location and widget identifier
      name: location
      required: true
      type: TEXT
    - description: Visual size of the control in px (default 400px), without a size the design is responsive
      label: size [px]
      name: size
      required: false
      type: TEXT
    - description: Minimum value
      label: minTemp
      name: minTemp
      required: true
      type: TEXT
    - description: Maximum value
      label: maxTemp
      name: maxTemp
      required: true
    - context: item
      description: Item to control
      label: Set point Item
      name: setPointItem
      required: true
      type: TEXT
    - context: item
      label: Item for current temperature
      name: currentPointItem
      required: true
      type: TEXT
    - description: Control item unit eg °C
      label: unit
      name: unit
      required: false
      type: TEXT
    - description: URL or path to a Image (if you use a local img eg. -> http://local-IP-Adress:8080/static/folder/img.svg )
      label: URL or path to image
      name: imgUrl
      required: false
      type: TEXT
      advanced: true
    - description: Heating mode strings as array eg. AUTO,MANU,OFF. The string will be send to the Heating-Mode-Item [String-Item]
      label: Heating Mode Array
      name: heatingModeArray
      required: false
      type: TEXT
      advanced: true
    - description: Example Mode Strings as Array eg. OFF,ON,BOOST. The string will be send to the Example-Mode-Item [String-Item]
      label: Example Mode Array
      name: exampleModeArray
      required: false
      type: TEXT
      advanced: true
    - context: item
      description: Heating Mode Item [String-Item]
      label: Heating Mode Item
      name: heatingModeItem
      required: false
      type: TEXT
      advanced: true
    - context: item
      description: Example Mode Item [String-Item] (eg. State of heating valve)
      label: Example Mode Item
      name: exampleModeItem
      required: false
      type: TEXT
      advanced: true
    - context: item
      description: Valve Item [Switch-Item] (eg. State of heating valve) for Animation
      label: Valve Item
      name: valveItem
      required: false
      type: TEXT
      advanced: true
    - label: Custom font-size current&set-point marker (eg. 1em)
      name: fontSizeMarker
      required: false
      type: TEXT
      groupName: fonts
      advanced: true
    - label: Custom font-Size center (eg. 2em)
      name: fontSizeCenter
      required: false
      type: TEXT
      groupName: fonts
      advanced: true
    - label: Custom font-Size buttons (eg. 1.8em)
      name: fontSizeButtons
      required: false
      type: TEXT
      groupName: fonts
      advanced: true
    - label: Custom font-Size footer (eg. 1em)
      name: fontSizeFooter
      required: false
      type: TEXT
      groupName: fonts
      advanced: true
    - label: Main-Color Thermostat
      name: colorThermostat
      required: false
      type: TEXT
      groupName: colors
      advanced: true
    - label: Color control ring
      name: colorControlRing
      required: false
      type: TEXT
      groupName: colors
      advanced: true
    - label: Color buttons
      name: colorButton
      required: false
      type: TEXT
      groupName: colors
      advanced: true
    - label: Color center
      name: colorCenter
      required: false
      type: TEXT
      groupName: colors
      advanced: true
    - label: Color Typo
      name: colorTypo
      required: false
      type: TEXT
      groupName: colors
      advanced: true
    - label: Color setPoint Marker
      name: colorSetMarker
      required: false
      type: TEXT
      groupName: colors
      advanced: true
    - label: Color currentPoint Marker
      name: colorCurrentMarker
      required: false
      type: TEXT
      groupName: colors
      advanced: true
    - label: Color bar linear gradient startPoint
      name: colorBarStartPoint
      required: false
      type: TEXT
      groupName: colors
      advanced: true
    - label: Color bar linear gradient endPoint
      name: colorBarEndPoint
      required: false
      type: TEXT
      groupName: colors
      advanced: true
  parameterGroups:
    - name: colors
      label: Color-Settings
    - name: fonts
      label: Font-Settings
timestamp: Feb 22, 2021, 12:22:59 PM
component: f7-card
config:
  title: "=(props.location) ? 'Klima ' + props.location : ''"
slots:
  default:
    - component: f7-card-content
      slots:
        default:
          - component: f7-row
            config:
              resizableFixed: true
              resizable-absolute: true
              class:
                - justify-content-center
            slots:
              default:
                - component: f7-block
                  config:
                    class: thermostat
                    style:
                      flex-shrink: 0
                      --f7-block-margin-vertical: 0px
                      --f7-block-padding-vertical: 0px
                      --f7-block-padding-horizontal: 0px
                      padding-left: 0px
                      padding-top: "=props.size ? props.size + 'px': '100%'"
                      width: "=props.size ? Number(props.size)+'px' : '100%'"
                      background: "=props.colorThermostat ? props.colorThermostat : 'var(--f7-toggle-inactive-color)'"
                      border-radius: 50%
                      box-sizing: content-box
                      border: 2px solid rgb(64, 60, 77)
                  slots:
                    default:
                      - component: f7-block
                        config:
                          class: bar
                          style:
                            margin-top: 0px
                            position: absolute
                            width: "=props.size ? (Number(props.size)*0.89) +'px' : '89%'"
                            height: "=props.size ? (Number(props.size)*0.89) +'px' : '89%'"
                            top: 50%
                            left: 50%
                            transform: translate(-50%, -50%)
                            border-radius: 50%
                        slots:
                          default:
                            - component: f7-block
                              config:
                                class: inner_bar
                                style:
                                  margin-top: 0
                                  position: absolute
                                  top: 50%
                                  left: 50%
                                  transform: translate(-50%, -50%)
                                  width: "=props.size ? (Number(props.size)*0.86) +'px' : '97%'"
                                  height: "=props.size ? (Number(props.size)*0.86) +'px' : '97%'"
                                  border-radius: 100%
                                  background-color: "=props.colorThermostat ? props.colorThermostat : 'var(--f7-toggle-inactive-color)'"
                                  z-index: 4 !important
                              slots:
                                default:
                                  - component: f7-block
                                    config:
                                      style:
                                        background: "='conic-gradient(transparent 0deg 160deg, ' + (props.colorThermostat ? props.colorThermostat : 'var(--f7-toggle-inactive-color)') + ' 160deg 200deg, transparent 200deg 360deg)'"
                                        content: ""
                                        display: block
                                        position: absolute
                                        width: 100%
                                        height: 100%
                                        bottom: "=props.size ? '-7px' : '-7px'"
                                        left: 50%
                                        transform: translate(-50%)
                            - component: f7-block
                              config:
                                class: hold left
                                style:
                                  margin-top: 0px
                                  position: absolute
                                  width: 100%
                                  height: 100%
                                  clip-path: "=props.size ? 'inset(0px 0px 0px ' + (Number(props.size)*0.89/2) + 'px)' : 'inset(0% 0% 0% 50%)'"
                                  border-radius: 100%
                                  background-color: rgb(58, 55, 73)
                              slots:
                                default:
                                  - component: f7-block
                                    config:
                                      class: fill fill1
                                      style:
                                        margin-top: 0px
                                        position: absolute
                                        width: 100%
                                        height: 100%
                                        border-radius: 100%
                                        clip-path: "=props.size ? 'inset(0px ' + (Number(props.size)*0.89/2) + 'px 0px 0px)' : 'inset(0% 50% 0% 0%)'"
                                        background: "=props.colorBarStartPoint && props.colorBarEndPoint ? '-webkit-linear-gradient(top, ' + props.colorBarEndPoint + ' 20%,' + props.colorBarEndPoint + ' 100%)' : '-webkit-linear-gradient(top, rgb(255, 73, 0) 20%,rgb(255, 73, 0) 100%)'"
                                        z-index: 1 !important
                                        transition: transform 0.6s
                                        transform: "=(items[props.setPointItem].state.split(' ')[0] >= (((Number(props.maxTemp) - Number(props.minTemp)) / 2) + Number(props.minTemp)) && items[props.setPointItem].state.split(' ')[0] <= Number(props.maxTemp) ? 'rotate('+(320/(Number(props.maxTemp)-Number(props.minTemp))*(items[props.setPointItem].state.split(' ')[0]-Number(props.minTemp))-160)+'deg)' : (items[props.setPointItem].state.split(' ')[0] > Number(props.maxTemp)) ? 'rotate(180deg)' : '')"
                            - component: f7-block
                              config:
                                class: hold right
                                style:
                                  margin-top: 0px
                                  position: absolute
                                  width: 100%
                                  height: 100%
                                  clip-path: "=props.size ? 'inset(0px 0px 0px ' + (Number(props.size)*0.89/2) + 'px)' : 'inset(0% 0% 0% 50%)'"
                                  border-radius: 100%
                                  background-color: rgb(58, 55, 73)
                                  z-index: 3 !important
                                  transform: rotate(180deg)
                              slots:
                                default:
                                  - component: f7-block
                                    config:
                                      class: fill
                                      style:
                                        margin-top: 0px
                                        position: absolute
                                        width: 100%
                                        height: 100%
                                        border-radius: 100%
                                        z-index: 3 !important
                                        animation: right 1s linear both
                                        transition: transform 0.6s
                                  - component: f7-block
                                    config:
                                      class: fill fill2
                                      style:
                                        position: absolute
                                        margin-top: 0px
                                        width: 100%
                                        height: 100%
                                        border-radius: 50%
                                        z-index: 3 !important
                                        clip-path: "=props.size ? 'inset(0px '+ (Number(props.size)*0.89/2) + 'px 0px 0px)' : 'inset(0% 50% 0% 0%)'"
                                        background: "=props.colorBarStartPoint && props.colorBarEndPoint ? '-webkit-linear-gradient(top, ' + props.colorBarEndPoint + ' 40%,' + props.colorBarStartPoint + ' 100%)' : '-webkit-linear-gradient(top, rgb(255, 73, 0) 40%,rgb(255, 158, 35) 100%)'"
                                        transform: "=(items[props.setPointItem].state.split(' ')[0] <= (((Number(props.maxTemp) - Number(props.minTemp)) / 2) + Number(props.minTemp)) && items[props.setPointItem].state.split(' ')[0] >= Number(props.minTemp) ? 'rotate('+(320/(Number(props.maxTemp)-Number(props.minTemp))*(items[props.setPointItem].state.split(' ')[0]-Number(props.minTemp))+20)+'deg)' : (items[props.setPointItem].state.split(' ')[0] > (((Number(props.maxTemp) - Number(props.minTemp)) / 2) + Number(props.minTemp))) ? 'rotate(180deg)' :  '')"
                            - component: f7-block
                              config:
                                class: span
                                style:
                                  margin-top: 0px
                                  width: "=props.size ? (Number(props.size)*0.89) +'px' : '100%'"
                                  font-weight: "=props.size ? (Number(props.size)*2) +'px' : 'calc(var(--f7-list-item-title-font-weight)*2)'"
                                  position: absolute
                                  bottom: 0px
                                  text-align: center
                                  text-transform: uppercase
                                  font-size: "=props.fontSizeFooter ? props.fontSizeFooter : '1em'"
                                  color: "=props.colorTypo ? props.colorTypo : 'rgb(87, 84, 95)'"
                                  z-index: 99 !important
                              slots:
                                default:
                                  - component: Label
                                    config:
                                      text: Heating
                      - component: f7-block
                        config:
                          class: shadow
                          style:
                            margin-top: 0px
                            position: absolute
                            top: 50%
                            left: 50%
                            transform: "=(items[props.setPointItem].state.split(' ')[0] >= Number(props.minTemp) && items[props.setPointItem].state.split(' ')[0] <= Number(props.maxTemp) ? 'translate(-50%, -50%) rotate('+(320/(Number(props.maxTemp)-Number(props.minTemp))*(items[props.setPointItem].state.split(' ')[0]-Number(props.minTemp))-160)+'deg)' : 'translate(-50%, -50%) rotate(0deg)')"
                            width: "=props.size ? (Number(props.size)*0.0625) +'px' : '6.25%'"
                            height: 86%
                            text-align: center
                            transition: 0.7s ease
                            animation: shadow 1.4s ease-out both
                        slots:
                          default:
                            - component: f7-block
                              config:
                                class: shadow-cube
                                style:
                                  margin-top: 0px
                                  position: absolute
                                  top: 0
                                  width: "=props.size ? (Number(props.size)*0.0625) +'px' : '100%'"
                                  height: 0px
                                  box-shadow: "=props.size ? '0 0 ' + (Number(props.size)*0.1125) +'px ' + (Number(props.size)*0.0325) + 'px ' + (props.colorSetMarker ? props.colorSetMarker : 'rgba(255, 158, 35, 0.5)'): '0 0 45px 13px rgba(255, 158, 35, 0.5)'"
                      - component: f7-block
                        config:
                          class: markerContainer
                          style:
                            pointer-events: none
                            margin-top: 0px
                            position: absolute
                            top: 50%
                            left: 50%
                            transform: "=(items[props.currentPointItem].state.split(' ')[0] >= Number(props.minTemp) && items[props.currentPointItem].state.split(' ')[0] <= Number(props.maxTemp) ? 'translate(-50%, -50%) rotate('+(320/(Number(props.maxTemp)-Number(props.minTemp))*(items[props.currentPointItem].state.split(' ')[0]-Number(props.minTemp))-160)+'deg)' : 'translate(-50%, -50%) rotate(0deg)')"
                            width: "=props.size ? (Number(props.size)*0.1) +'px' : '10%'"
                            height: 100%
                            text-align: center
                            transition: 0.7s ease
                            opacity: 1
                            z-index: 99 !important
                        slots:
                          default:
                            - component: f7-block
                              config:
                                class: markerCurrent
                                style:
                                  margin-top: 0px
                                  width: "=props.size ? (Number(props.size)*0.1) +'px' : '100%'"
                                  height: "=props.size ? (Number(props.size)*0.1) +'px' : ''"
                                  padding-top: "=props.size ? '' : '100%'"
                                  background: "=props.colorCurrentMarker ? props.colorCurrentMarker : 'rgb(33, 150, 243)'"
                                  position: absolute
                                  transform: translate(-50%,-50%) rotate(45deg)
                                  left: 50%
                                  top: "=props.size ? (Number(props.size)*0.14) +'px' : '15%'"
                                  border-radius: 0% 50% 50% 50%
                                  box-shadow: 0 0 5px 1px rgb(48, 46, 56)
                              slots:
                                default:
                                  - component: f7-block
                                    config:
                                      class: number
                                      style:
                                        margin-top: 0px
                                        position: absolute
                                        top: 50%
                                        left: 50%
                                        transform: translate(-50%, -50%) rotate(-45deg)
                                        text-align: center
                                    slots:
                                      default:
                                        - component: Label
                                          config:
                                            text: =items[props.currentPointItem].state.split(' ')[0]
                                            style:
                                              font-size: "=props.fontSizeMarker ? props.fontSizeMarker : '1em'"
                                              color: white
                                              font-weight: bold
                      - component: f7-block
                        config:
                          class: markerContainer
                          style:
                            pointer-events: none
                            margin-top: 0px
                            position: absolute
                            top: 50%
                            left: 50%
                            transform: "=(items[props.setPointItem].state.split(' ')[0] >= Number(props.minTemp) && items[props.setPointItem].state.split(' ')[0] <= Number(props.maxTemp) ? 'translate(-50%, -50%) rotate('+(320/(Number(props.maxTemp)-Number(props.minTemp))*(items[props.setPointItem].state.split(' ')[0]-Number(props.minTemp))-160)+'deg)' : 'translate(-50%, -50%) rotate(0deg)')"
                            width: "=props.size ? (Number(props.size)*0.1) +'px' : '10%'"
                            height: 100%
                            text-align: center
                            transition: 0.7s ease
                            opacity: 1
                            z-index: 99 !important
                        slots:
                          default:
                            - component: f7-block
                              config:
                                class: markerSet
                                style:
                                  margin-top: 0px
                                  width: "=props.size ? (Number(props.size)*0.1) +'px' : '100%'"
                                  height: "=props.size ? (Number(props.size)*0.1) +'px' : ''"
                                  padding-top: "=props.size ? '' : '100%'"
                                  background: "=props.colorSetMarker ? props.colorSetMarker : 'rgb(230, 74, 25)'"
                                  position: absolute
                                  transform: translate(-50%,-50%) rotate(-45deg)
                                  left: 50%
                                  top: "=props.size ?  (Number(props.size)*Number(-0.0125)) +'px': '-2%'"
                                  border-radius: 50% 50% 50% 0
                                  box-shadow: 0 0 5px 1px rgb(48, 46, 56)
                                  z-index: 100 !important
                              slots:
                                default:
                                  - component: f7-block
                                    config:
                                      class: number
                                      style:
                                        margin-top: 0px
                                        position: absolute
                                        top: 50%
                                        left: 50%
                                        transform: translate(-50%, -50%) rotate(45deg)
                                        text-align: center
                                    slots:
                                      default:
                                        - component: Label
                                          config:
                                            text: =items[props.setPointItem].state.split(' ')[0]
                                            style:
                                              font-size: "=props.fontSizeMarker ? props.fontSizeMarker : '1em'"
                                              color: white
                                              font-weight: bold
                      - component: f7-block
                        config:
                          class: center
                          style:
                            margin-top: 0px
                            position: absolute
                            width: "=props.size ? (Number(props.size)*0.65) +'px' : '65%'"
                            height: "=props.size ? (Number(props.size)*0.65) +'px' : '65%'"
                            background: "=props.colorControlRing ? props.colorControlRing : 'rgb(227, 228, 237)'"
                            top: 50%
                            left: 50%
                            transform: translate(-50%, -50%)
                            border-radius: 50%
                            box-shadow: 0px 15px 35px 11px rgba(46, 44, 58,0.60)
                        slots:
                          default:
                            
                              
                            - component: f7-block
                              config:
                                class: buttonContainer
                                style:
                                  display: "=props.heatingModeItem ? '' : 'none'"
                                  margin-top: 0px
                                  position: absolute
                                  top: 50%
                                  left: 50%
                                  transform: "=props.exampleModeItem ? 'translate(-50%, -50%) rotate(30deg)' : 'translate(-50%, -50%)'"
                                  width: "=props.size ? (Number(props.size)*0.15) +'px' : '15%'"
                                  height: 100%
                                  text-align: center
                                  transition: 0.7s ease
                                  opacity: 1
                                  z-index: 99 !important
                              slots:
                                default:
                                  - component: oh-button
                                    config:
                                      popoverOpen: ='.' + props.location + '.popoverHeatingMode'
                                      style:
                                        width: 100%
                                        height: 20%
                                        position: absolute
                                        bottom: 0%
                                        left: 50%
                                        transform: translate(-50%)
                                        color: "=props.colorButton ? props.colorButton + ' !important': ''"
                                    slots:
                                      default:
                                        - component: f7-icon
                                          config:
                                            f7: "=props.heatingModeItem ? (items[props.heatingModeItem].state ==  props.heatingModeArray.split(',')[0] ? 'hand_raised' : items[props.heatingModeItem].state == props.heatingModeArray.split(',')[1] ? 'arrow_2_squarepath' : items[props.heatingModeItem].state == props.heatingModeArray.split(',')[2] ? 'airplane' : 'thermometer') : ''"
                                            style:
                                              font-size: "=props.fontSizeButtons ? props.fontSizeButtons : '2em'"
                                              position: absolute
                                              transform: translate(-50%, -50%)
                                              top: 50%
                                              margin-top: auto
                                        - component: f7-popover
                                          config:
                                            class: =props.location + ' popoverHeatingMode'
                                          slots:
                                            default:
                                              - component: f7-card
                                                config:
                                                  noShadow: true
                                                  class:
                                                    - popover-close
                                                  action: variable
                                                  actionVariable: myVar
                                                  clearVariable: true
                                                  actionVariableValue: success
                                                slots:
                                                  default:
                                                    - component: f7-row
                                                      config: {}
                                                      slots:
                                                        default:
                                                          - component: f7-col
                                                            slots:
                                                              default:
                                                                - component: oh-repeater
                                                                  config:
                                                                    for: buttonlabel
                                                                    in: =props.heatingModeArray.split(",")
                                                                    containerStyle:
                                                                      width: 100%
                                                                  slots:
                                                                    default:
                                                                      - component: oh-button
                                                                        config:
                                                                          color: "=props.colorButton ? props.colorButton : ''"
                                                                          class: margin
                                                                          text: =loop.buttonlabel
                                                                          outline: true
                                                                          action: command
                                                                          active: "=(items[props.heatingModeItem].state === loop.buttonlabel ? true : false)"
                                                                          actionCommand: =loop.buttonlabel
                                                                          actionItem: =props.heatingModeItem
                            - component: f7-block
                              config:
                                class: buttonContainer
                                style:
                                  display: "=props.exampleModeItem ? '' : 'none'"
                                  margin-top: 0px
                                  position: absolute
                                  top: 50%
                                  left: 50%
                                  transform: "=props.heatingModeItem ? 'translate(-50%, -50%) rotate(-30deg)' : 'translate(-50%, -50%)'"
                                  width: "=props.size ? (Number(props.size)*0.15) +'px' : '15%'"
                                  height: 100%
                                  text-align: center
                                  transition: 0.7s ease
                                  opacity: 1
                                  z-index: 99 !important
                              slots:
                                default:
                                  - component: oh-button
                                    config:
                                      popoverOpen: ='.' + props.location + '.popoverExampleMode'
                                      style:
                                        width: 100%
                                        height: 20%
                                        position: absolute
                                        bottom: 0%
                                        left: 50%
                                        transform: translate(-50%)
                                        color: "=props.colorButton ? props.colorButton + ' !important': ''"
                                    slots:
                                      default:
                                        - component: f7-icon
                                          config:
                                            f7: wrench
                                            style:
                                              font-size: "=props.fontSizeButtons ? props.fontSizeButtons : '2em'"
                                              position: absolute
                                              transform: translate(-50%, -50%)
                                              top: 50%
                                              margin-top: auto
                                        - component: f7-popover
                                          config:
                                            class: =props.location + ' popoverExampleMode'
                                          slots:
                                            default:
                                              - component: f7-card
                                                config:
                                                  noShadow: true
                                                  class:
                                                    - popover-close
                                                  action: variable
                                                  actionVariable: myVar
                                                  clearVariable: true
                                                  actionVariableValue: success
                                                slots:
                                                  default:
                                                    - component: f7-row
                                                      config: {}
                                                      slots:
                                                        default:
                                                          - component: f7-col
                                                            slots:
                                                              default:
                                                                - component: oh-repeater
                                                                  config:
                                                                    for: buttonlabel
                                                                    in: =props.exampleModeArray.split(",")
                                                                    containerStyle:
                                                                      width: 100%
                                                                  slots:
                                                                    default:
                                                                      - component: oh-button
                                                                        config:
                                                                          color: "=props.colorButton ? props.colorButton : ''"
                                                                          class: margin
                                                                          text: =loop.buttonlabel
                                                                          outline: true
                                                                          action: command
                                                                          active: "=(items[props.exampleModeItem].state === loop.buttonlabel ? true : false)"
                                                                          actionCommand: =loop.buttonlabel
                                                                          actionItem: =props.exampleModeItem
                            - component: oh-button
                              config:
                                style:
                                  width: 30%
                                  height: 50%
                                  position: absolute
                                  margin-top: 0px
                                  top: 50%
                                  left: 10%
                                  transform: translate(-50%, -50%)
                                  color: "=props.colorButton ? props.colorButton : ''"
                                action: command
                                actionItem: =props.setPointItem
                                actionCommand: "=Number(items[props.setPointItem].state.split(' ')[0]) > Number(props.minTemp) ? Number(items[props.setPointItem].state.split(' ')[0]) - 0.5 : ''"
                              slots:
                                default:
                                  - component: f7-icon
                                    config:
                                      style:
                                        font-size: "=props.fontSizeButtons ? props.fontSizeButtons : '2em'"
                                        position: absolute
                                        transform: translate(-50%, -50%)
                                        top: 50%
                                        margin-top: auto
                                      f7: arrow_turn_left_down
                            - component: oh-button
                              config:
                                style:
                                  width: 30%
                                  height: 50%
                                  position: relative
                                  margin-top: 0px
                                  top: 50%
                                  left: 90%
                                  transform: translate(-50%, -50%)
                                  color: "=props.colorButton ? props.colorButton : ''"
                                action: command
                                actionItem: =props.setPointItem
                                actionCommand: "=Number(items[props.setPointItem].state.split(' ')[0]) < Number(props.maxTemp) ? Number(items[props.setPointItem].state.split(' ')[0]) + 0.5 : ''"
                              slots:
                                default:
                                  - component: f7-icon
                                    config:
                                      style:
                                        font-size: "=props.fontSizeButtons ? props.fontSizeButtons : '2em'"
                                        position: absolute
                                        transform: translate(-50%, -50%)
                                        top: 50%
                                        margin-top: auto
                                      f7: arrow_turn_right_up
                            - component: f7-block
                              config:
                                class: valveAnimation
                                style:
                                  z-index: -100 !important
                                  pointer-events: none
                                  display: "=(props.valveItem && items[props.valveItem].state == 'ON' )? '' : 'none'"
                                  background: "=props.colorSetMarker ? 'radial-gradient(' + props.colorSetMarker +' 30%, transparent 50%)' : 'radial-gradient(var(--f7-theme-color) 30%, transparent 50%)'"
                                  margin-top: 0px
                                  position: absolute
                                  transform: translate(-50%, -50%)
                                  width: 100%
                                  height: 100%
                                  top: 50%
                                  left: 50%
                                  border-radius: 50%
                                  animation: skeleton-effect-fade 2s linear infinite
                            - component: f7-block
                              config:
                                class: small
                                style:
                                  z-index: 100 !important
                                  margin-top: 0px
                                  position: absolute
                                  width: "=props.size ? (Number(props.size)*0.375) +'px' : '57.6%'"
                                  height: "=props.size ? (Number(props.size)*0.375) +'px' : '57.6%'"
                                  background: "=props.colorCenter ? props.colorCenter : 'rgb(248, 249, 250)'"
                                  text-align: center
                                  animation: bound-in-small 0.6s ease forwards
                                  top: 50%
                                  left: 50%
                                  transform: translate(-50%, -50%)
                                  border-radius: 50%
                                  box-shadow: 0px 5px 10px 5px rgba(96, 93, 111,0.19)
                              slots:
                                default:
                                  - component: f7-block
                                    config:
                                      class: heat
                                      style:
                                        font-size: "=props.size ? (Number(props.size)*0.0375) +'px' : '14px'"
                                        color: "=props.colorTypo ? props.colorTypo : 'rgb(87, 84, 95)'"
                                        font-weight: 300
                                    slots:
                                      default:
                                        - component: Label
                                          config:
                                            text: =props.location
                                  - component: f7-block
                                    config:
                                      class: heat
                                      style:
                                        font-size: "=props.fontSizeCenter ? props.fontSizeCenter : '2em'"
                                        color: "=props.colorTypo ? props.colorTypo : 'rgb(87, 84, 95)'"
                                        font-weight: 300
                                    slots:
                                      default:
                                        - component: Label
                                          config:
                                            text: "=props.unit ? items[props.setPointItem].state.split(' ')[0] + '' + props.unit : items[props.setPointItem].state.split(' ')[0]"
                                  - component: oh-image
                                    config:
                                      url: "=props.imgUrl ? props.imgUrl : 'https://community-openhab-org.s3-eu-central-1.amazonaws.com/original/2X/7/7d388a86c95471f89b1bb911d96d7609a3e3a059.svg'"
                                      style:
                                        position: absolute
                                        transform: translate(-50%)
                                        left: 50%
                                        width: 40%
2 Likes

Nice job, but I’ve small wish: can You display .displayState (if defined) instead .state, so that proper formatting will be possible, for setPointItem and currentPointItem ? I know I can do this myself, but for each of Your great new versions, again and again … :wink: :innocent:

/Sas

The error message comes when the page containing the widget is opened in the browser and the Heating / Example Mode item is not linked to an item.

Hi @Tallman

thanks for the details.
I’ve found the little bug into the code. The error is by the defintion of the F7-Icon if no heatingModeItm is defined.
sorry for this.
I change the post above with version v1.8.1

I also included the array string for the icon selection.

Please give me a feedback if it works. :wink:

Change the line 510

from:

                                        f7: "=(items[props.heatingModeItem].state == 'Manuell' ? 'hand_raised' : items[props.heatingModeItem].state == 'Auto' ? 'arrow_2_squarepath' : items[props.heatingModeItem].state == 'Vacation' ? 'airplane' : 'thermometer')"

to:

                                         f7: "=props.heatingModeItem ? (items[props.heatingModeItem].state ==  props.heatingModeArray.split(',')[0] ? 'hand_raised' : items[props.heatingModeItem].state == props.heatingModeArray.split(',')[1] ? 'arrow_2_squarepath' : items[props.heatingModeItem].state == props.heatingModeArray.split(',')[2] ? 'airplane' : 'thermometer') : ''"

It works great now! Thank you very much! The only thing I noticed was the location prop needs to be one single phrase, with a broken phrase (like a space in between two words) the pop-up won’t show. For me that is no big deal but just wanted to let you know.

It works, good work keep it up :clap:

Hi Nico,
fantastic :+1: The additions work all well. Now there are nearly all design-options possible.

The “pulsing Corona” is also a very nice feature, which I tested with a toggling switch of my office-lamp.
grafik .

The wish of @Sas_Bibic (Translation) could be another interesting feature to make it more comfortable for users of different languages. But I don’t think that this is only done by changing the “state” to “displayState”.

For the moment I’m lucky with your solution and nosy for what is coming next.

cheers,
Peter

1 Like

Dear all

Based on this wonderful widget (Thanks Nico)
I try to create widget for personal weather station

Here

Any suggestion are welcome

1 Like

Hi @Nico_R

Thank you again for your work.Popup menu looks really cool, and heating animation beautiful.
Unfortunately, I observe strange behavior of heating mode icon. On page open it displays default value (thermometer), when I switch to different mode icon switched to assigned to mode (airplane, hand, etc) but then i switch to another page and back to page with widget icon become again basic thermometer.
Widget version is 1.8.1

I am glad. In which German forum did you post it? Just out of interest because I’m from Germany too. :wink:

I tried to reproduce the error but didn’t succeed. When I switch between pages the icons stay as defined.
The icons are firmly defined in the code. The states of the string item are defined in the heatingMode array.
The first array key gets the hand_raised icon, the second key gets arrow_2_squarepath icon and the third key gets airplane. If you use more than 3 keys, the key> 3 always gets the thermometer.

Nico

1 Like

I just thought, as your examples mostly shows “Klima Wohnzimmer” :wink:

Here’s the link for German-Forum, or here (may be you have to sign in to see the screen-shots)

@doctor64 I don’t know what kind of Thermostats you have, but my AVM-Fritz-Thermostats are “read-only” in the heating-mode. So if you make a change in the widget it will be set back after some seconds.

Changes are only possible inside the Fritz-Box-Web-UI.

2 Likes