OH3 Color/White Bulb Widget

Hello Community

NEW in v3:
Among smaller fixes and improvements I added the possibility to add a shutdown timer item for your color lamp. The power button will start pulsating when timer is activated (credits go to @Nico_R, I borrowed this animation from his heating widget :wink: )
light

I was always missing a nicely integrated way to control a lightbulb that supports both RGB/HSL and CCT modes. This is my first take to create such a control:

light

Like with my other widgets the usability on my phone was the priority (which is why it is so small).

This widget consists of two slides:

  1. To control the bulb in CCT mode (Brightness & Temperature)
  2. To control the bulb in HSL mode (Hue, Saturation & Brightness)

Switch the light on or off with the bottom left switch and switch modes with the right switch. My bulb also activates itself when the mode is switched so I can also switch it on with the bottom right switch. If your bulbs behave the same way and you think this is undesirable then you may need to add a proxy item to avoid this.

I guess most likely all of the different binding implementations work a bit differently so this widget might need to be adapted for other manufacturers/bindings or might require additional proxy items if you don’t want to mess with the widget code.

I currently know of two issues with the current version of the widget:

  1. The initial slide is no selected correctly (most of the time?): No matter which mode the bulb is currently in the first slide always seems to be the color wheel. I set the initalSlide property for the swiper in the code but I could not get that to work correctly so far.
  2. When you switch the mode in one window/one device the slides are not flipped on another device/in another window. I don’t think there is a way to fix this but let me know if you have any ideas.

The workaround for both of these issues is to hit the mode switch button (bottom right) twice so that the GUI is in sync with the device and its state again.

Configuration

Header: Just some text to display on top of the controls
Toggle: An item that can be used to toggle the light on and off. Important: Even though usually an item connected to a brightness channel would work here you need to use an item that always has an ON/OFF state (maybe a future version might just use the brightness channel)
Brightness: Item that holds the brightness value (1 - 100) - No typed Numbers
Temperature: Item that holds the color temperature value (1700K - 6500K) - No typed Numbers; For now you need to change the min and max values of the temperature (if your bulbs support another range) directly in the code.
Color Mode Item: An item that can be used to set the color mode: 1 stands for White and 2 for Color (so this should work with all(?) yeelights)
Color Item: The item to set the actual color

As always: Improvements are most welcome, especially regarding the described known issues :slight_smile:

uid: color_light_v3
tags: []
props:
  parameters:
    - description: Header text
      label: Header
      name: header
      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 = White, 2 = Color)
      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
  parameterGroups: []
timestamp: Mar 17, 2021, 12:19:06 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
      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: '=items[props.colorMode].state == "1" ? 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: -10px
                        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: 100%
                        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(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
                      - component: f7-row
                        config:
                          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: 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:
                            width: 100%
                        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(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: '=items[props.colorMode].state != "1" ? true : false'
                    class:
                      - no-margin
                    style:
                      animation: f7-fade-in 300ms
                  slots:
                    default:
                      - component: f7-row
                        config:
                          class:
                            - display-flex
                            - justify-content-center
                        slots:
                          default:
                            - component: f7-block
                              config:
                                style:
                                  width: 180px
                              slots:
                                default:
                                  - component: oh-colorpicker
                                    config:
                                      item: =props.color
                                      modules:
                                        - wheel
          - 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: 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:
              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 == 2 ? "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 == 2 ? "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 == 2 ? "linear-gradient(135deg, red, orange, yellow, green, blue)" : "transparent"'
                      transform: '=items[props.colorMode].state == 2 ? "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: 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:
                      - nextSlide
                - component: oh-link
                  config:
                    iconOnly: true
                    iconF7: paintbrush_fill
                    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:
                      - previousSlide
    - component: f7-card-footer
      slots:
        default:
          - component: Label
            config:
              text: '=items[props.lampToggle].state == "ON" ? "Licht an" : "Licht aus"'
12 Likes

Hey @DrRSatzteil

again a very good looking widget - nice work!

I think I have an idea why this happens - If you make your YAML available, I could have a closer look at it. :slight_smile:

It should be possible to change the slides on multiple widgets with the help of variables.

Thanks!

I always forget to post the code :rofl:

I added it now to the first post!

Could you maybe point me towards a resource where I can read about these variables?

Oh and just to be precise I am talking about a scenario like:

  • I switch the light mode on my phone
  • I take a look at my notebook and the mode has changed but the widget hasn’t changed the slides

Hey @DrRSatzteil

sry for the late response - just found some time to have a closer look at your code.


tl;dr
It’s not possible to achieve what you wanna do, at least not with the f7-swiper (a.k.a. swiperjs) component currently.


My assumptions, what is going on here:

This seems to be a timing problem with getting the current item state in the same moment the ‘initialSlide’ gets loaded. So it might work occassionaly, but most of the time it won’t. Maybe theres a fix for this by delaying the load of the initialSlide (which might have negative impact on other things) - but this would be a github issue, I assume.

It seems that it’s by design, that slides won’t get synced over multiple instances (like devices, browser windows and so on) with the f7-swiper. You could mimic similar things with the visibility of the f7-swiper-slide but this isn’t very usefull in my opinion and it would be hard doing this without messing up the flip-effect.

What would possible is, getting rid of the f7-swiper component and using other f7-components - you could add already available animations from the f7-framework or even try to recreate the flip transformation within the styling of the 2 f7-block-components - I used a simple fade here for simplicity.

YAML with fade-animation
uid: color_light_v1
tags: []
props:
  parameters:
    - description: Header text
      label: Header
      name: header
      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 = White, 2 = Color)
      label: Color Mode Item
      name: colorMode
      required: true
      type: TEXT
    - context: item
      description: Color
      label: Color Item
      name: color
      required: true
      type: TEXT
  parameterGroups: []
timestamp: Mar 4, 2021, 7:02:28 PM
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
      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: '=items[props.colorMode].state == "1" ? 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-betwee
                            - align-items-center
                          style:
                            width: 100%
                        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: 100%
                        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(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%
                                  margin-left: 15px
                                  margin-right: 15px
                                  z-index: 99 !important
                      - component: f7-row
                        config:
                          class:
                            - display-flex
                            - justify-content-space-between
                            - align-items-center
                          style:
                            width: 100%
                        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:
                            width: 100%
                        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(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%
                                  margin-left: 15px
                                  margin-right: 15px
                                  z-index: 99 !important
                - component: f7-block
                  config:
                    visible: '=items[props.colorMode].state != "1" ? true : false'
                    class:
                      - no-margin
                    style:
                      animation: f7-fade-in 300ms
                  slots:
                    default:
                      - component: f7-row
                        config:
                          class:
                            - display-flex
                            - justify-content-center
                        slots:
                          default:
                            - component: f7-block
                              config:
                                style:
                                  width: 180px
                              slots:
                                default:
                                  - component: oh-colorpicker
                                    config:
                                      item: =props.color
                                      modules:
                                        - wheel
          - 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: 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.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: 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: 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 == 2 ? "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 == 2 ? "linear-gradient(135deg, red, orange, yellow, green, blue)" : "transparent"'
                      transform: '=items[props.colorMode].state == 2 ? "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: 2
                    variable: test
                    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:
                      - nextSlide
                - component: oh-link
                  config:
                    iconOnly: true
                    iconF7: paintbrush_fill
                    iconSize: 17
                    action: command
                    actionItem: =props.colorMode
                    actionCommand: 1
                    variable: test
                    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:
                      - previousSlide
    - component: f7-card-footer
      slots:
        default:
          - component: Label
            config:
              text: '=items[props.lampToggle].state == "ON" ? "Licht an" : "Licht aus"'

Hope it help you somehow. Keep up your good work for the community!

1 Like

Hi Rainer, thank you for efforts once again!

This is a very good solution indeed, I don’t necessarily need the flip effect.

1 Like

Could you point towards a resource where these are documented? I could not find anything in the f7 documentation so far?!

I don’t think these are documented tbh (or at least I don’t know where) - I used the dirty way here and beautified the app.css from my setup: http://YOUR-OH-IP:8080/css/app.css

… with this CSS beautifier and checked the available ‘keyframes’
https://www.freeformatter.com/css-beautifier.html

Oh I see it is a bit hacky but as long as they don’t rename the effect I should be fine :wink:

Thank you!

This btw does not work for me. I have to put the whole property in " for this to work :man_shrugging:

This looks and works awesome, thanks for sharing! I did notice that at least on mine with a range of 2700-6500k for temperature, it seems that going higher than 3900 the slider goes off the widget and at the 6500k mark it’s way off. Any ideas how to fix that?

I only noticed this behaviour in the editor when I resized the preview panel. Have you resized your window by any chance? Does a page reload fix it?

I had my browser maximized the the whole time, but shrinking it works and then it seems to work fine at any size except for if I then maximize it again and then the 6500k boundary stays at wherever it was when I shrunk the window so I can deal with that. On my phone (edge chromium browser) it seems that the upper boundary ends at the T in TV in my screenshot. Not sure how or if I can fix that since it seems to be a browser rendering issue

Hmmm, this is really weird. I cannot reproduce this issue here so I don’t really know what else you could try :roll_eyes:

I will paste you my current code asap that has some minor changes compared to Rainer’s suggestion above. Maybe this helps!?

This is the code I am currently using which works fine for me:

uid: color_light_v2
tags: []
props:
  parameters:
    - description: Header text
      label: Header
      name: header
      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 = White, 2 = Color)
      label: Color Mode Item
      name: colorMode
      required: true
      type: TEXT
    - context: item
      description: Color
      label: Color Item
      name: color
      required: true
      type: TEXT
  parameterGroups: []
timestamp: Mar 15, 2021, 8:47:08 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
      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: '=items[props.colorMode].state == "1" ? 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: -10px
                        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: 100%
                        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(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
                      - component: f7-row
                        config:
                          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: 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:
                            width: 100%
                        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(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: '=items[props.colorMode].state != "1" ? true : false'
                    class:
                      - no-margin
                    style:
                      animation: f7-fade-in 300ms
                  slots:
                    default:
                      - component: f7-row
                        config:
                          class:
                            - display-flex
                            - justify-content-center
                        slots:
                          default:
                            - component: f7-block
                              config:
                                style:
                                  width: 180px
                              slots:
                                default:
                                  - component: oh-colorpicker
                                    config:
                                      item: =props.color
                                      modules:
                                        - wheel
          - 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:
                  box-shadow: '=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:
                      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: 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 == 2 ? "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 == 2 ? "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 == 2 ? "linear-gradient(135deg, red, orange, yellow, green, blue)" : "transparent"'
                      transform: '=items[props.colorMode].state == 2 ? "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: 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:
                      - nextSlide
                - component: oh-link
                  config:
                    iconOnly: true
                    iconF7: paintbrush_fill
                    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:
                      - previousSlide
    - component: f7-card-footer
      slots:
        default:
          - component: Label
            config:
              text: '=items[props.lampToggle].state == "ON" ? "Licht an" : "Licht aus"'

The v2 version seems to have fixed all of my issues, thanks!

1 Like

You may also want to checkout v3. I’ll also update the code in the first post.

Changes:

  1. Made brightness and temperature sliders a little bit wider for a better experience on small phone screens
  2. You may now add a Shutdown Timer Item that can be controlled via the widget
  3. Couple of small design fixes
uid: color_light_v3
tags: []
props:
  parameters:
    - description: Header text
      label: Header
      name: header
      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 = White, 2 = Color)
      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
  parameterGroups: []
timestamp: Mar 17, 2021, 12:19:06 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
      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: '=items[props.colorMode].state == "1" ? 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: -10px
                        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: 100%
                        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(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
                      - component: f7-row
                        config:
                          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: 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:
                            width: 100%
                        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(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: '=items[props.colorMode].state != "1" ? true : false'
                    class:
                      - no-margin
                    style:
                      animation: f7-fade-in 300ms
                  slots:
                    default:
                      - component: f7-row
                        config:
                          class:
                            - display-flex
                            - justify-content-center
                        slots:
                          default:
                            - component: f7-block
                              config:
                                style:
                                  width: 180px
                              slots:
                                default:
                                  - component: oh-colorpicker
                                    config:
                                      item: =props.color
                                      modules:
                                        - wheel
          - 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: 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:
              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 == 2 ? "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 == 2 ? "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 == 2 ? "linear-gradient(135deg, red, orange, yellow, green, blue)" : "transparent"'
                      transform: '=items[props.colorMode].state == 2 ? "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: 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:
                      - nextSlide
                - component: oh-link
                  config:
                    iconOnly: true
                    iconF7: paintbrush_fill
                    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:
                      - previousSlide
    - component: f7-card-footer
      slots:
        default:
          - component: Label
            config:
              text: '=items[props.lampToggle].state == "ON" ? "Licht an" : "Licht aus"'
1 Like

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