OH3 Color/White Bulb Widget

Hey,
I really like your widget.

I modified it a little bit, to work with the dimmer item for the hue bridge color temperature (0 - 100%). I also changed the text of the actual color temperature to the following one, because the item state is often NULL.

[...]
text: '=items[props.temperature].state == "NULL" ? "-" : items[props.temperature].state + "%"'
[...]

I have the problem, that sometimes the slider is out of the card (tested on Mac Safari and iPadOS Safari):
Bildschirmfoto 2021-03-19 um 15.58.03
:thinking:

tardismechanic had the same issue (see above). He said that v2 fixed this for him (code also above). You may want to try to start with that and add your charges one by one to see what breaks the widget.

1 Like

My approach to the color temp as a percent was to create a ColorTempK that’s “write only” to the percent channel item that I linked to the widget and this rule. My two items are LightName_ColorTemp and LightName_ColorTempK. There’s probably a better way to do it, or possibly in the widget itself but I don’t know much about doing stuff like that in widgets (yet) so this works for me.

#Get the base item name
  tempName = event.itemName.split("_")[0] 

#2700 is the lowest temp, so make that the new "0"
  colorTempNormalized = float(str(ir.getItem(event.itemName).state)) - 2700 

# Divide the "normalized" temp by the difference between the lowest and highest values
# In my case that's 2700 and 6500, so 3800 is the result. Make that a percent (*100)
# and invert that percent (100 -) because in my case 2700k should be 0% (on the left end of the slider) and 6500k
# should be 100% (on the right end of the slider)
  pct = 100 - ((colorTempNormalized / 3800) * 100) 

# round off the decimals because I don't care about them and post it to the true
# _ColorTemp to make the actual change
  events.sendCommand(tempName + "_ColorTemp","{:.0f}".format(pct))

This way the widget shows 2700k - 6500k as a slider and doesn’t bounce around when you make a change like with mine (zwave bulbs) when I change the color temp, it posts the change to the binding and the binding immediately sets it to NULL when the change it made

1 Like

FYI @DrRSatzteil You can set your own keyframe animations now (see Add stylesheet configuration to widgets by hubsif · Pull Request #979 · openhab/openhab-webui · GitHub)

That offers a whole new world of possibilities for the creation of custom widgets. :slight_smile:

1 Like

That’s really cool, thanks for pointing that out! I’m already looking forward for the first cool examples and probably something comes to my mind as well :slight_smile:

By the way I stumbled upon the same problem a couple of days ago: the espmilight binding also uses a 0 - 100 range for the temperature. I still wanted the Kelvin value to be displayed and it can actually be done conpletely in the widget code: https://community.openhab.org/t/light-widget-with-temperature-control

You may of course stick with your version, I don’t see any disadvantages besides the additional item.

Thanks for sharing! I like having it all in the widget better, I just didn’t have the time to try and sort it out so I went with what I knew

I really like this widget.
Everything works fine, but when i press the color select button and the color wheel is displayed. It closes the color wheel card before I am able to chose a color…
Any suggestions or clues?

I’m not sure exactly what you mean. Would it be possible to post a gif file here?

Do you happen to use the miio binding? If so please try to set the profile of the color mode channel to “follow” and try if that works for you.

Brilliant!
I now realize my description was really poor.
But you solved it for me anyways. It was indeed MIIO binding. Changed to “follow”. Now it works. Many thanks

Great, so it was a good guess by me :wink:

I happen to control a light via the miio binding with this widget as well. The binding always sets the color mode back to white light after a second or so. I don’t know if this is a bug or not but it was easy enough to solve it via the profile so I did not bother to investigate any further.

Here is my Hue modification

uid: color_hue_light_v3
tags: []
props:
  parameters:
    - description: Header text
      label: Header
      name: header
      required: false
      type: TEXT
    - context: item
      description: Choose Lamp to control
      label: Lamp
      name: lamp
      required: true
      type: TEXT
    - description: Choose if your Bulb has Color
      label: Color
      name: color
      required: false
      type: BOOLEAN
    - context: item
      description: Choose the Temperature Item if your Bulb has Temperature
      label: Temperature
      name: temperature
      required: false
      type: TEXT
    - context: item
      description: Timer
      label: Shutdown Timer Item
      name: shutdowntimer
      required: false
      type: TEXT
  parameterGroups: []
timestamp: Apr 13, 2021, 12:00:22 AM
component: f7-card
slots:
  default:
    - component: f7-card-content
      config:
        class:
          - display-flex
          - flex-direction-column
          - justify-content-flex-start
          - align-items-center
        style:
          height: 175px
          --f7-color-picker-slider-size: 18px
          --f7-color-picker-slider-knob-size: 22px
      slots:
        default:
          - component: f7-row
            config:
              style:
                position: absolute
            slots:
              default:
                - component: f7-block-header
                  slots:
                    default:
                      - component: Label
                        config:
                          text: =props.header
          - component: f7-block
            config:
              class:
                - no-padding
              style:
                width: 100%
                height: 100%
                margin-top: 2px
            slots:
              default:
                - component: f7-block
                  config:
                    visible: "=vars.color != true ? true : false"
                    class:
                      - display-flex
                      - flex-direction-column
                      - justify-content-flex-end
                    style:
                      animation: f7-circle-circle-in 300ms
                      height: 60%
                      margin-top: 10px
                  slots:
                    default:
                      - component: f7-row
                        config:
                          visible: "=(props.temperature) ? true : false"
                          class:
                            - display-flex
                            - justify-content-space-between
                            - align-items-center
                          style:
                            width: calc(100% + 20px)
                            margin-left: -10px
                        slots:
                          default:
                            - component: f7-icon
                              config:
                                f7: thermometer
                                size: 20
                                style:
                                  margin-left: 20px
                                  color: var(--f7-block-header-text-color)
                            - component: Label
                              config:
                                text: '=items[props.temperature].state == "NULL" ? "-" : items[props.temperature].state + " %"'
                                style:
                                  margin-right: 20px
                                  color: var(--f7-block-header-text-color)
                      - component: f7-row
                        config:
                          visible: "=(props.temperature) ? true : false"
                          class:
                            - display-flex
                            - justify-content-center
                            - align-items-center
                          style:
                            width: 100%
                        slots:
                          default:
                            - component: oh-slider
                              config:
                                color: white
                                label: true
                                min: 0
                                max: 100
                                item: =props.temperature
                                style:
                                  margin-top: 7px
                                  --f7-range-bar-size: 18px
                                  --f7-range-bar-border-radius: 10px
                                  --f7-range-knob-size: 20px
                                  --f7-range-bar-active-bg-color: transparent
                                  --f7-range-bar-bg-color: linear-gradient(to right, rgba(215, 226, 255), rgba(255, 255, 255),rgba(255, 147, 44, 0.5),rgba(255, 147, 44, 0))
                                  --f7-range-knob-box-shadow: 0 2px 4px rgba(0, 0, 0, 0.3)
                                  --f7-range-label-text-color: black
                                  width: 100%
                                  z-index: 99 !important
                      - component: f7-row
                        config:
                          class:
                            - display-flex
                            - justify-content-space-between
                            - align-items-center
                          style:
                            width: calc(100% + 20px)
                            margin-left: -10px
                            margin-top: 20px
                        slots:
                          default:
                            - component: f7-icon
                              config:
                                f7: sun_min
                                size: 20
                                style:
                                  margin-left: 20px
                                  color: var(--f7-block-header-text-color)
                            - component: Label
                              config:
                                text: =items[props.lamp].state.split(",")[2] + " %"
                                style:
                                  margin-right: 20px
                                  color: var(--f7-block-header-text-color)
                      - component: f7-row
                        config:
                          class:
                            - display-flex
                            - justify-content-center
                            - align-items-center
                          style:
                            width: 100%
                            margin-top: 5px
                        slots:
                          default:
                            - component: oh-slider
                              config:
                                color: white
                                label: true
                                item: =props.lamp
                                style:
                                  --f7-range-bar-size: 18px
                                  --f7-range-bar-border-radius: 10px
                                  --f7-range-knob-size: 20px
                                  --f7-range-bar-active-bg-color: rgba(246,246,0,0.5)
                                  --f7-range-bar-bg-color: linear-gradient(to right, rgba(169,169,169,0.8), rgba(246,158,81,0))
                                  --f7-range-knob-box-shadow: 0 2px 4px rgba(0, 0, 0, 0.3)
                                  --f7-range-label-text-color: black
                                  width: 100%
                                  z-index: 99 !important
                - component: f7-block
                  config:
                    visible: "=vars.color != true ? false : true"
                    class:
                      - no-margin
                    style:
                      animation: f7-circle-circle-in 300ms
                  slots:
                    default:
                      - component: f7-row
                        config:
                          class:
                            - display-flex
                            - justify-content-center
                        slots:
                          default:
                            - component: f7-block
                              config:
                                style:
                                  width: 100%
                              slots:
                                default:
                                  - component: oh-colorpicker
                                    config:
                                      style:
                                        margin-top: 7px
                                        --f7-range-bar-size: 18px
                                        --f7-range-bar-border-radius: 10px
                                        --f7-range-knob-size: 20px
                                        --f7-range-bar-active-bg-color: transparent
                                        --f7-range-bar-bg-color: linear-gradient(to right, rgba(246,158,81,0.8), rgba(246,158,81,0))
                                        --f7-range-knob-box-shadow: 0 2px 4px rgba(0, 0, 0, 0.3)
                                        --f7-range-label-text-color: black
                                        width: 100%
                                        z-index: 99 !important
                                      item: =props.lamp
                                      modules:
                                        - hsb-sliders
          - component: f7-block
            config:
              class:
                - display-flex
                - justify-content-center
                - align-items-center
                - no-padding
                - no-margin
              style:
                position: absolute
                width: 40px
                height: 40px
                left: 30px
                bottom: 20px
                border-top: none
                background: var(--f7-searchbar-search-icon-color)
                box-shadow: var(--f7-actions-bg-color) 0px 0px 16px inset
                border-radius: 50%
            slots:
              default:
                - component: f7-block
                  config:
                    class:
                      - no-margin
                    style:
                      animation: '=items[props.shutdowntimer].state.split(" ")[0] > 0 ? "skeleton-effect-fade 0.2s linear infinite" : "none"'
                      position: absolute
                      width: 100%
                      height: 100%
                      box-shadow: '=items[props.lamp].state.split(",")[2] > 0 ? "0 0 7px #000, 0px 0px 7px rgba(0,255,0,0.5)" : "none"'
                      border-radius: 50%
                      transition: transform 0.2s
                      background: '=items[props.lamp].state.split(",")[2] > 0 ? "rgba(0, 255, 0, 0.9)" : "transparent"'
                      transform: '=items[props.lamp].state.split(",")[2] > 0 ? "scale(1)" : "scale(0,0)"'
                - component: f7-block
                  config:
                    style:
                      position: absolute
                      width: calc(100% - 10px)
                      height: calc(100% - 10px)
                      border-radius: 50%
                - component: oh-link
                  config:
                    iconOnly: true
                    iconF7: power
                    iconSize: 13
                    action: toggle
                    actionItem: =props.lamp
                    actionCommand: '=items[props.lamp].state.split(",")[2] > "0" ? "0" : "100"'
                    style:
                      border-radius: 50%
                      background-color: var(--f7-actions-bg-color)
                      color: "#a7a7a7"
                      width: calc(100% - 10px)
                      height: calc(100% - 10px)
                      text-align: center
                      backdrop-filter: opacity(88%)
                      z-index: 99 !important
                      box-shadow: 0px 0px 7px 2px var(--f7-actions-bg-color)
          - component: oh-link
            config:
              text: Timer
              visible: "=props.shutdowntimer ? true : false"
              action: popover
              popoverOpen: .timerpopover
              style:
                position: absolute
                bottom: 10px
            slots:
              default:
                - component: f7-popover
                  config:
                    class:
                      - timerpopover
                  slots:
                    default:
                      - component: oh-stepper-card
                        config:
                          item: =props.shutdowntimer
                          title: Ausschalten in Minuten
          - component: f7-block
            config:
              visible: "=(props.color) == true ? true : false"
              class:
                - display-flex
                - justify-content-center
                - align-items-center
                - no-padding
                - no-margin
              style:
                position: absolute
                bottom: 20px
                right: 30px
                width: 40px
                height: 40px
                border-top: none
                background: var(--f7-searchbar-search-icon-color)
                box-shadow: var(--f7-actions-bg-color) 0px 0px 16px inset
                border-radius: 50%
            slots:
              default:
                - component: f7-block
                  config:
                    class:
                      - no-margin
                    style:
                      position: absolute
                      width: 100%
                      height: 100%
                      box-shadow: '=vars.color == true ? "rgb(0 0 0) 0px 0px 4px, rgb(255 0 0 / 50%) -1px -1px 4px, rgb(255 255 0 / 50%) 1px -1px 4px, rgb(0 255 0 / 50%) 1px 1px 4px, rgb(0 0 255 / 50%) -1px 1px 4px" : "none"'
                      border-radius: 50%
                      transition: transform 0.5s
                      background: '=vars.color == true ? "linear-gradient(135deg, red, red, orange, yellow, green, blue, blue)" : "transparent"'
                      transform: '=vars.color == true ? "rotate(360deg) scale(1,1)" : "rotate(0deg) scale(0,0)"'
                - component: f7-block
                  config:
                    style:
                      position: absolute
                      width: calc(100% - 10px)
                      height: calc(100% - 10px)
                      background: var(--f7-actions-bg-color)
                      border-radius: 50%
                - component: oh-link
                  config:
                    iconOnly: true
                    iconF7: paintbrush
                    iconSize: 17
                    action: variable
                    actionVariable: color
                    actionVariableValue: true
                    style:
                      display: '=(vars.color == false || vars.color != true) ? "block" : "none"'
                      border: solid 2pt var(--f7-actions-bg-color)
                      border-radius: 50%
                      background-color: var(--f7-actions-bg-color)
                      background-image: "linear-gradient(135deg, #f7f7f7: none, #e7e7e7)"
                      color: "#a7a7a7"
                      width: calc(100% - 10px)
                      height: calc(100% - 10px)
                      text-align: center
                      box-shadow: 0px 0px 7px 2px var(--f7-actions-bg-color)
                      backdrop-filter: opacity(88%)
                    class:
                      - nextSlide
                - component: oh-link
                  config:
                    iconOnly: true
                    iconF7: paintbrush_fill
                    iconSize: 17
                    action: variable
                    actionVariable: color
                    actionVariableValue: false
                    style:
                      display: '=(vars.color) ? "block" : "none"'
                      border: solid 2pt var(--f7-actions-bg-color)
                      border-radius: 50%
                      background-color: var(--f7-actions-bg-color)
                      color: "#a7a7a7"
                      width: calc(100% - 10px)
                      height: calc(100% - 10px)
                      text-align: center
                      backdrop-filter: opacity(88%)
                    class:
                      - previousSlide
    - component: f7-card-footer
      slots:
        default:
          - component: Label
            config:
              text: '=items[props.lamp].state.split(",")[2] > 0 ? "Licht an" : "Licht aus"'

I also made some other modifications. Should work with all Hue Lights (Color and non-color) and Groups

3 Likes

Awesome, your widget! As a beginner working with widgets and Jython rules, I try to understand how this stuff works. I have two issues to integrate the color light widget with my RGBW color lamp which basically works as an RGB lamp plus a separate tunable white lamp.

Issue #1: I do not have an item to select between color mode and white mode. I understand that I need to create a kind of virtual item without a counterpart outside OpenHAB. Upon toggling the color mode item between color and white, the respective other color channels should be turned off to avoid a parallel activation: If color mode = color then white switch = off. If color mode = white then RGB switch = off. However, I get lost whether I can implement this functionality inside the widget (and where to start) or whether I need to write a rule what I have never done before, at least not in Jython so that the rule does not depend on a particular item for exactly one lamp.

Issue #2: I do not have separate switch item, as I added the switch functionality to the respective color / dimmer type as specified in the things file:

Type color : RGBW "DG Dachraum Farbe" [ hsb="232.600:4/5/85+<4/5/100", switch="1.001:4/5/84+<4/5/99", position="5.001:4/5/89+<4/5/104", increaseDecrease="3.007:4/5/92" ]
Type dimmer : TWdim "DG Dachraum Weiß Helligkeit" [ switch="1.001:4/5/84+<4/5/99", position="5.001:4/5/95+<4/5/107", increaseDecrease="3.007:4/5/98" ]
Type number : TWcol "DG Dachraum Weiß Farbtemperatur" [ ga="7.600:4/5/94+<4/5/106" ]

Is there a way to switch on/off the color light in the widget on the basis of non-explicitly available switch items?

Best regards,
Peter

Hi Peter,

let me share my first thoughts on this:

Regarding issue #1: I think you are on the right track here. You are assuming correctly that you would need a rule to simultaneously switch color on/off while switching white off/on. I never used Jython rules so I can’t really help you with these. However I would recommend to simply create a rule from the UI. Since this is pretty basic stuff there is no real need to write a single line of code.

Regarding issue #2: I think I would go for the same solution as with issue #1 (slightly more complex): Create a virtual on/off switch and use a rule to switch off both channels when the switch is turned off. When it is turned on you would need to check what state your color mode switch from issue #1 has and only switch on the corresponding channel (color or dimmer).

Hi Thomas,

Thank you very much for pointing me to the rules to be created from the UI. In order to do so, I created a model first, assigned equipment (things) and points (items). However, I did not find a way to separate business logic from particular items by doing this way. The reason is that I have a number of lamps and would liketo avoid copy-paste the code with different items.

According to what I saw in the rules UI is that I may select a particular item and apply a script. It would be great if somebody could give me a hint as how to abstract the script so that I can re-use it for a number of different items, but following the same business logic.

Best regards,
Peter

Hi Thomas

I also really like your widget. I have made some minor changes to make it compatible with my Hue Hub (%Temperature for lights).

Can you clarify how the “Timer” works? I have created an Item for the shutdowntimer and when it is >0 I see the Power Icon “pulse”. However not clear how and where the countdown happens as I cannot see it in the widget. Does this has to be set externally somehow?

BTW I also see a delay on the Temperature slider when you power on the lights. The label can stay on UNDEF for quiet some time before changing to the correct %. Assuming this is a delay from the Hue Hub?

Thanks again
Mark

Hi Peter,

so far this has not been a big issue with regard to my own home setup. However this is of course a problem that many had before and Rich wrote a very comprehensive article about this topic here: Design Pattern: Working with Groups in Rules

It’s a bit lengthy but worth the read if you want to avoid duplication even though you might need to recreate some items to be able to use the techniques described there.

Regards,
Thomas

1 Like

Oh yes the timer is probably something that would not work out of the box for a lot of users. I control a milight bulb with this widget that has a shutdown timer built into the device that can be controlled via a channel.

For devices that do not support this out of the box you would need to create your own timer Item and add a rule that gets triggered whenever the Item receives a command. The simplest approach would be in my opinion to create a rule that cancels any running timers, switches the light off when the Item received a 0 (0 minutes) or sets up a one minute timer when the Item is > 0 and sends a command to the same item with the Item value decreased by one. This will in turn trigger the rule again and starts a timer for the next minute and so on until you reached 0 and the rule will turn off the light.

Regarding the delays: The delay from the Hue Hub should really only matter if you set “autoupdate” to “false” in your Item config (this is the reason in my setup). If autoupdate is enabled I would guess that it is more likely a network delay between your device and the OpenHab Rest API.

1 Like

I created a TripleToggle Python script which automatically creates and manages timers when needed. The timers are kept in an array, the key is the item name. This rule could serve as inspiration (see e.g. where the timers array is initialised, how it is automatically created and how the timers are reset when needed):

Thanks for contributing to comminity!
Can you please help me to adopt my version for Yeelight color lamps? Code is:

uid: yeelight_color_light_v3
tags: []
props:
  parameters:
    - description: Header text
      label: Header
      name: header
      required: false
      type: TEXT
    - description: Background image, located in static/ folder
      label: Background image
      name: bg_image_url
      required: false
      type: TEXT
    - context: item
      description: Toggle ON/OFF
      label: Toggle
      name: lampToggle
      required: true
      type: TEXT
    - context: item
      description: Brightness
      label: Brightness
      name: brightness
      required: true
      type: TEXT
    - context: item
      description: Temperature in K
      label: Temperature
      name: temperature
      required: true
      type: TEXT
    - context: item
      description: Color mode (1 = RGB, 2 = Temperature, 4 = Color flow)
      label: Color Mode Item
      name: colorMode
      required: true
      type: TEXT
    - context: item
      description: Color
      label: Color Item
      name: color
      required: true
      type: TEXT
    - context: item
      description: Timer
      label: Shutdown Timer Item
      name: shutdowntimer
      required: false
      type: TEXT
    - context: item
      label: Item_flow
      name: item_flow
      required: false
      type: TEXT
  parameterGroups: []
timestamp: Jul 13, 2021, 2:47:31 AM
component: f7-card
config:
  style:
    border-radius: var(--f7-card-expandable-border-radius)
    box-shadow: 5px 5px 10px 1px rgba(0,0,0,0.1)
    background-image: "=props.bg_image_url ? 'url(/static/' + props.bg_image_url + ')' : ''"
    background-repeat: no-repeat;
    background-size: cover
    background-brightness: 60%
    margin-left: 5px
    margin-right: 5px
    font-size: 15px
slots:
  default:
    - component: f7-card-content
      config:
        class:
          - display-flex
          - flex-direction-column
          - justify-content-flex-start
          - align-items-center
        style:
          height: 175px
      slots:
        default:
          - component: f7-row
            config:
              style:
                position: absolute
            slots:
              default:
                - component: f7-block-header
                  slots:
                    default:
                      - component: Label
                        config:
                          text: =props.header
          - component: f7-block
            config:
              class:
                - no-padding
              style:
                width: 110%
                height: 100%
                margin-top: 20px
            slots:
              default:
                - component: f7-block
                  config:
                    visible: '=items[props.colorMode].state == "2" ? true : false'
                    class:
                      - display-flex
                      - flex-direction-column
                      - justify-content-flex-end
                    style:
                      animation: f7-fade-in 300ms
                      height: 60%
                      margin-top: 10px
                  slots:
                    default:
                      - component: f7-row
                        config:
                          class:
                            - display-flex
                            - justify-content-space-between
                            - align-items-center
                          style:
                            width: calc(100% - 20px)
                            margin-left: 15px
                        slots:
                          default:
                            - component: f7-icon
                              config:
                                f7: thermometer
                                size: 20
                                style:
                                  color: var(--f7-block-header-text-color)
                            - component: Label
                              config:
                                text: =items[props.temperature].state + "K"
                                style:
                                  color: var(--f7-block-header-text-color)
                      - component: f7-row
                        config:
                          class:
                            - display-flex
                            - justify-content-center
                            - align-items-center
                          style:
                            width: 95%
                            margin-left: 10px
                        slots:
                          default:
                            - component: oh-slider
                              config:
                                color: white
                                label: true
                                min: 1700
                                max: 6500
                                item: =props.temperature
                                style:
                                  --f7-range-bar-size: 18px
                                  --f7-range-bar-border-radius: 10px
                                  --f7-range-knob-size: 20px
                                  --f7-range-bar-active-bg-color: transparent
                                  --f7-range-bar-bg-color: linear-gradient(to right, rgba(255,121,0,1.8), rgba(50,249,255,1))
                                  --f7-range-knob-box-shadow: 0 2px 4px rgba(0, 0, 0, 0.3)
                                  --f7-range-label-text-color: black
                                  width: 100%
                                  z-index: 99 !important
                      - component: f7-row
                        config:
                          class:
                            - display-flex
                            - justify-content-space-between
                            - align-items-center
                          style:
                            width: calc(100% - 20px)
                            margin-left: 15px
                        slots:
                          default:
                            - component: f7-icon
                              config:
                                f7: sun_min
                                size: 20
                                style:
                                  color: var(--f7-block-header-text-color)
                            - component: Label
                              config:
                                text: =items[props.brightness].state + "%"
                                style:
                                  color: var(--f7-block-header-text-color)
                      - component: f7-row
                        config:
                          class:
                            - display-flex
                            - justify-content-center
                            - align-items-center
                          style:
                            margin-left: 10px
                            width: 95%
                        slots:
                          default:
                            - component: oh-slider
                              config:
                                color: white
                                label: true
                                item: =props.brightness
                                style:
                                  --f7-range-bar-size: 18px
                                  --f7-range-bar-border-radius: 10px
                                  --f7-range-knob-size: 20px
                                  --f7-range-bar-active-bg-color: rgba(100,100,0,0.5)
                                  --f7-range-bar-bg-color: linear-gradient(to right, rgba(100,100,0,0.1), rgba(255,255,0,1))
                                  --f7-range-knob-box-shadow: 0 2px 4px rgba(0, 0, 0, 0.3)
                                  --f7-range-label-text-color: black
                                  width: 100%
                                  z-index: 99 !important
                - component: f7-block
                  config:
                    visible: '=items[props.colorMode].state == "1" ? true : false'
                    class:
                      - display-flex
                      - justify-content-center
                      - align-items-center
                    style:
                      width: 107%
                      margin-left: -20px
                      animation: f7-fade-in 500ms
                  slots:
                    default:
                      - component: f7-row
                        config:
                          style:
                            width: 100%
                          class:
                            - display-flex
                            - justify-content-center
                        slots:
                          default:
                            - component: f7-block
                              config:
                                style:
                                  width: 100%
                                  heigth: 120%
                              slots:
                                default:
                                  - component: oh-colorpicker
                                    config:
                                      item: =props.color
                                      modules:
                                        - hsb-sliders
          - component: f7-block
            config:
              class:
                - display-flex
                - justify-content-center
                - align-items-center
                - no-padding
                - no-margin
              style:
                position: absolute
                width: 40px
                height: 40px
                left: 10px
                bottom: 10px
                border-top: '=items[props.lampToggle].state == "ON" ? "none" : "2px solid #ddd"'
                box-shadow: "inset 0px 1px 2px #eee"
                background: white
                border-radius: 50%
            slots:
              default:
                - component: f7-block
                  config:
                    class:
                      - no-margin
                    style:
                      animation: '=items[props.shutdowntimer].state.split(" ")[0] > 0 ? "skeleton-effect-fade 2s linear infinite" : "none"'
                      position: absolute
                      width: 100%
                      height: 100%
                      box-shadow: '=items[props.lampToggle].state == "ON" ? "0 0 20px #fff, 0px 0px 30px rgba(0,255,0,0.5)" : "none"'
                      border-radius: 50%
                      transition: transform 0.2s
                      background: '=items[props.lampToggle].state == "ON" ? "rgba(124, 252, 0, 0.5)" : "transparent"'
                      transform: '=items[props.lampToggle].state =="ON" ? "scale(1,1)" : "scale(0,0)"'
                - component: f7-block
                  config:
                    style:
                      position: absolute
                      width: calc(100% - 10px)
                      height: calc(100% - 10px)
                      background: rgba(255,255,255,0.8)
                      border-radius: 50%
                - component: oh-link
                  config:
                    iconOnly: true
                    iconF7: power
                    iconSize: 17
                    action: toggle
                    actionItem: =props.lampToggle
                    actionCommand: '=items[props.lampToggle].state == "ON" ? "OFF" : "ON"'
                    style:
                      border: solid 2pt white
                      border-radius: 50%
                      background-color: "#f7f7f7"
                      background-image: "linear-gradient(135deg, #f7f7f7, #e7e7e7)"
                      color: "#a7a7a7"
                      width: calc(100% - 10px)
                      height: calc(100% - 10px)
                      text-align: center
                      box-shadow: 0px 3px 8px
                      backdrop-filter: opacity(88%)
                      z-index: 99 !important
          - component: oh-link
            config:
              text: =items[props.shutdowntimer].state
              visible: "=props.shutdowntimer ? true : false"
              action: popover
              popoverOpen: .timerpopover
              style:
                position: absolute
                bottom: 10px
            slots:
              default:
                - component: f7-popover
                  config:
                    class:
                      - timerpopover
                  slots:
                    default:
                      - component: oh-stepper-card
                        config:
                          item: =props.shutdowntimer
                          title: Таймер в минутах
          - component: f7-block
            config:
              class:
                - display-flex
                - justify-content-center
                - align-items-center
                - no-padding
                - no-margin
              style:
                position: absolute
                bottom: 10px
                right: 10px
                width: 40px
                height: 40px
                border-top: '=items[props.colorMode].state == 1 ? "none" : "2px solid #ddd"'
                box-shadow: "inset 0px 1px 2px #eee"
                background: white
                border-radius: 50%
            slots:
              default:
                - component: f7-block
                  config:
                    class:
                      - no-margin
                    style:
                      position: absolute
                      width: 100%
                      height: 100%
                      box-shadow: '=items[props.colorMode].state == 1 ? "0 0 15px #fff, -7px -7px 15px rgba(255,0,0,0.5), 7px -7px 15px rgba(255,255,0,0.5), 7px 7px 15px rgba(0,255,0,0.5), -7px 7px 15px rgba(255,255,0,0.5)" : "none"'
                      border-radius: 50%
                      transition: transform 0.2s
                      background: '=items[props.colorMode].state == 1 ? "linear-gradient(135deg, red, orange, yellow, green, blue)" : "transparent"'
                      transform: '=items[props.colorMode].state == 1 ? "rotate(360deg) scale(1,1)" : "rotate(0deg) scale(0,0)"'
                - component: f7-block
                  config:
                    style:
                      position: absolute
                      width: calc(100% - 10px)
                      height: calc(100% - 10px)
                      background: rgba(255,255,255,0.8)
                      border-radius: 50%
                - component: oh-link
                  config:
                    iconOnly: true
                    iconF7: paintbrush
                    iconSize: 17
                    action: command
                    actionItem: =props.colorMode
                    actionCommand: 1
                    style:
                      display: '=items[props.colorMode].state == 2 ? "block" : "none"'
                      border: solid 2pt white
                      border-radius: 50%
                      background-color: "#f7f7f7"
                      background-image: "linear-gradient(135deg, #f7f7f7, #e7e7e7)"
                      color: "#a7a7a7"
                      width: calc(100% - 10px)
                      height: calc(100% - 10px)
                      text-align: center
                      box-shadow: 0px 3px 8px
                      backdrop-filter: opacity(88%)
                    class:
                      - nextSlide
                - component: oh-link
                  config:
                    iconOnly: true
                    iconF7: paintbrush_fill
                    iconSize: 17
                    action: command
                    actionItem: =props.colorMode
                    actionCommand: 2
                    style:
                      display: '=items[props.colorMode].state == 1 ? "block" : "none"'
                      border: solid 2pt white
                      border-radius: 50%
                      background-color: "#f7f7f7"
                      background-image: "linear-gradient(135deg, #f7f7f7, #e7e7e7)"
                      color: "#a7a7a7"
                      width: calc(100% - 10px)
                      height: calc(100% - 10px)
                      text-align: center
                      box-shadow: 0px 3px 8px
                      backdrop-filter: opacity(88%)
                    class:
                      - previousSlide
    - component: f7-card-footer
      slots:
        default:
          - component: Label
            config:
              text: '=items[props.lampToggle].state == "ON" ? "Включено" : "Выключено"'

Issues: changed colorwheel to hsb-sliders, which are better to control Yeelight, and can’t understand how to place them in a small cell on mobile phone - they become too small and hard to control.
Is there any way to design widgets other than Openhab`s web ui?