Another Light widget - Suitable for Switch, dimmer, and CCT lighting. Motion detector and timer functionality

Expandable Lighting Widget

I’ve been hacking this together for a while now. I started with an expandable widget I found on the forums (if I can find it again I will link it here) and tweaked it into something more suibable for my needs.

I have a few different types of lights: Switch on/off, dimmer/CCT bulbs, and some with motion detectors.
This widget will adjust itself to be suitable for all of these light types. Just leave some of the props empty, and the widget will know what type of light you have.

I have also designed it to fit nicely on my iPhone, tablet, and desktop nicely without over lapping text and graphics.

There are icons to show; Motion detection, Timer status, Illuminance, and Illuminace Threshold.
When expanded, there are more detailed controls - Scene Brightness, timer settings and Motion detector light threshold.

Here are some screenshots-

Here is the widget code:

uid: widget_Lighting
props:
  parameters:
    - description: Location
      label: Location
      name: location
      required: false
      type: TEXT
    - context: item
      label: Brightness
      name: bright
      required: false
      type: TEXT
    - context: item
      label: Motion Detection
      name: motion
      required: false
      type: TEXT
    - context: item
      label: Illuminance threshold
      name: illumt
      required: false
      type: TEXT
    - context: item
      label: Illuminance
      name: illum
      required: false
      type: TEXT
    - context: item
      label: Scene brightness
      name: sbright
      required: false
      type: TEXT
    - context: item
      label: Motion Timer
      name: mtimer
      required: false
      type: TEXT
    - context: item
      label: "'On Time' Timer"
      name: oTtimer
      required: false
      type: TEXT
    - context: item
      label: Motion Detection On/Off
      name: motionOnOff
      required: false
      type: TEXT
timestamp: Jun 6, 2021, 7:34:31 PM
component: f7-card
config:
  expandable: true
  swipeToClose: true
  backdrop: true
  class:
    - card-expandable-animate-width
  style:
    height: 170px
    margin: 5px
    margin-top: 10px
slots:
  default:
    - component: oh-button
      config:
        iconF7: ellipsis
        iconSize: 30px
        color: gray
        style:
          position: absolute
          top: 0
          right: 0
          padding-top: 10px
          padding-right: 15px
          padding-bottom: 40px
          z-index: 999
        class:
          - cell-open-button
          - card-opened-fade-out
    - component: f7-card-content
      config:
        style:
          width: 100%
          padding-top: 30px
      slots:
        default:
          - component: f7-icon
            config:
              material: lightbulb
              size: '=(items[props.bright].state > 0 || items[props.bright].state == "ON") ? "120px": "90px"'
              color: '=(items[props.bright].state > 0 || items[props.bright].state == "ON") ? "yellow": "gray"'
              style:
                opacity: '=(items[props.bright].state > 0 || items[props.bright].state == "ON") ? "90%": "10%"'
                position: absolute
                top: '=(items[props.bright].state > 0 || items[props.bright].state == "ON") ? "45px": "60px"'
                right: '=(items[props.bright].state > 0 || items[props.bright].state == "ON") ? "-15px": "0px"'
          - component: oh-button
            config:
              iconF7: xmark_circle_fill
              iconSize: 30px
              color: black
              style:
                position: absolute
                top: 0
                right: 0
                padding-top: 10px
                padding-bottom: 35px
                z-index: 999
              class:
                - card-opened-fade-in
                - cell-close-button
                - card-close
          - component: oh-link
            config:
              action: toggle
              actionItem: =props.bright
              actionCommand: '=(props.sbright) ? items[props.sbright].state: "ON"'
              actionCommandAlt: OFF
              class:
                - card-prevent-open
              style:
                width: 100%
                height: 100%
                position: absolute
                top: 0
                left: 0
                z-index: 0
          - component: f7-block
            config:
              class:
                - no-padding
              style:
                margin: 0px
                height: 200px
            slots:
              default:
                - component: f7-row
                  config:
                    style:
                      height: 65px
                      width: auto
                      white-space: nowrap
                      flex-wrap: nowrap
                  slots:
                    default:
                      - component: f7-col
                        slots:
                          default:
                            - component: Label
                              config:
                                text: =props.location
                                style:
                                  font-weight: 600
                                  text-overflow: ellipsis
                                  overflow: hidden
                                  white-space: nowrap
                            - component: f7-chip
                              config:
                                text: '=(items[props.bright].state > 0) ? items[props.bright].state + " %": (items[props.bright].state == "ON") ? "ON": "OFF"'
                                color: '=(items[props.bright].state > 0) ? "yellow": (items[props.bright].state == "ON") ? "yellow": "gray"'
                - component: f7-row
                  config:
                    style:
                      white-space: nowrap
                      flex-wrap: nowrap
                      height: auto
                    class:
                      - justify-content-flex-start
                  slots:
                    default:
                      - component: f7-icon
                        config:
                          visible: '=(props.motionOnOff) ? "true": "false"'
                          f7: dot_radiowaves_right
                          size: 20px
                          color: '=(items[props.motion].state == "OFF" && items[props.motionOnOff].state == "ON") ? "blue" : (items[props.motion].state == "ON") ? "red" : (items[props.motionOnOff].state == "OFF") ? "gray" : "gray"'
                          style:
                            margin-right: 5px
                      - component: f7-icon
                        config:
                          f7: timer
                          size: 20px
                          color: '=(items[props.oTtimer].state != "0 min" && (items[props.bright].state == 0 || items[props.bright].state == "OFF")) ? "blue": 
                            (items[props.oTtimer].state != "0 min" && (items[props.bright].state > 0 || items[props.bright].state != "OFF")) ? "red": "gray"'
                          visible: '=(props.oTtimer) ? "true": "false"'
                          style:
                            margin-right: 5px
                - component: f7-row
                  config:
                    visible: '=(props.illum) ? "true": "false"'
                    style:
                      height: auto
                      width: 100%
                      overflow: hidden
                  slots:
                    default:
                      - component: f7-col
                        config:
                          style:
                            flex-wrap: nowrap
                            align-self: flex-end
                        slots:
                          default:
                            - component: f7-row
                              config:
                                class:
                                  - justify-content-flex-start
                                style:
                                  margin-top: 5px
                                  flex-wrap: nowrap
                              slots:
                                default:
                                  - component: f7-icon
                                    config:
                                      f7: light_max
                                      color: yellow
                                      size: 18px
                                      style:
                                        margin-top: 0px
                                        margin-left: 0px
                                  - component: Label
                                    config:
                                      text: =items[props.illum].displayState
                                      style:
                                        margin-left: 8px
                                        font-size: 12px
                                        color: gray
                                        text-overflow: ellipsis
                                        overflow: hidden
                                        white-space: nowrap
                - component: f7-row
                  config:
                    visible: '=(props.illumt) ? "true": "false"'
                    style:
                      height: 20px
                      width: 100%
                      overflow: hidden
                  slots:
                    default:
                      - component: f7-col
                        config:
                          style:
                            flex-wrap: nowrap
                            align-self: flex-end
                        slots:
                          default:
                            - component: f7-row
                              config:
                                class:
                                  - justify-content-flex-start
                                style:
                                  margin-top: 5px
                                  flex-wrap: nowrap
                              slots:
                                default:
                                  - component: f7-icon
                                    config:
                                      f7: increase_indent
                                      color: '=(items[props.illumt].state < (items[props.illum].state | ".0")) ? "gray": (items[props.motionOnOff].state == "OFF") ? "gray": "green"'
                                      size: 18px
                                      style:
                                        margin-top: 0px
                                        margin-left: 0px
                                  - component: Label
                                    config:
                                      text: =items[props.illumt].displayState
                                      style:
                                        margin-left: 8px
                                        font-size: 12px
                                        color: gray
                                        text-overflow: ellipsis
                                        overflow: hidden
                                        white-space: nowrap
          - component: f7-block
            config:
              class:
                - card-prevent-open
                - card-content-padding
            slots:
              default:
                - component: oh-list
                  config:
                    class:
                      - padding
                  slots:
                    default:
                      - component: oh-toggle-item
                        config:
                          title: On/Off
                          item: =props.bright
                          icon: f7:power
                          color: yellow
                          actionCommand: '=(props.sbright) ? items[props.sbright].state: "ON"'
                          actionCommandAlt: OFF
                      - component: oh-slider-item
                        config:
                          title: Brightness
                          item: =props.bright
                          icon: oh:slider
                          iconUseState: true
                          color: '=(items[props.bright].state > 0) ? "yellow" : "gray"'
                          visible: '=(props.sbright) ? "true": "false"'
                          min: 0
                          max: 100
                      - component: oh-slider-item
                        config:
                          title: Scene Brightness
                          item: =props.sbright
                          icon: oh:slider
                          iconUseState: true
                          color: blue
                          min: 0
                          max: 100
                          visible: '=(props.sbright) ? "true": "false"'
                      - component: oh-list-item
                        config:
                          title: Motion detection
                          icon: f7:dot_radiowaves_right
                          badge: '=(items[props.motionOnOff].state == "ON" && items[props.illumt].state > (items[props.illum].state | ".0")) ? "AUTO": (items[props.motionOnOff].state == "OFF") ? "OFF": (items[props.motionOnOff].state == "ON") ? "AUTO": (items[props.motionOnOff].state == "ON" && items[props.illumt].state < (items[props.illum].state | ".0")) ? "TOO BRIGHT": items[props.motionOnOff].displayState'
                          badge-color: '=(items[props.motionOnOff].state == "ON" && items[props.illumt].state > (items[props.illum].state | ".0") && items[props.motion].state == "OFF") ? "blue": (items[props.motionOnOff].state == "ON") ? "blue" : (items[props.motionOnOff].state == "OFF") ? "black": (items[props.motionOnOff].state == "ON" && items[props.illumt].state < (items[props.illum].state | ".0") ) ? "yellow": (items[props.motion].state == "ON" && items[props.motionOnOff].state == "ON" &&  items[props.illumt].state > (items[props.illum].state | ".0") ) ? "green": "red"'
                          action: toggle
                          actionItem: =props.motionOnOff
                          actionCommand: ON
                          actionCommandAlt: OFF
                          visible: '=(props.motionOnOff) ? "true": "false"'
                      - component: oh-stepper-item
                        config:
                          title: Motion Timer
                          item: =props.mtimer
                          footer: in Minutes
                          color: '=(items[props.motionOnOff].state == "ON") ? "blue": "gray"'
                          fill: true
                          round: true
                          autorepeat: true
                          autorepeatDynamic: true
                          small: true
                          min: 1
                          max: 600
                          icon: f7:timer
                          visible: "=(props.mtimer) ? true: false"
                      - component: oh-stepper-item
                        config:
                          title: lux Threshold
                          item: =props.illumt
                          color: '=(items[props.illumt].state < (items[props.illum].state | ".0")) ? "gray": (items[props.motionOnOff].state == "OFF") ? "gray": "green"'
                          fill: true
                          round: true
                          autorepeat: true
                          autorepeatDynamic: true
                          small: true
                          max: 600
                          icon: f7:light_max
                          footer: ="Currently " + items[props.illum].displayState
                          visible: "=(props.illumt) ? true: false"
                      - component: oh-stepper-item
                        config:
                          title: Auto Off
                          item: =props.oTtimer
                          footer: in Minutes
                          color: '=(items[props.oTtimer].state == "0 min") ? "gray": "blue"'
                          fill: true
                          round: true
                          autorepeat: true
                          autorepeatDynamic: true
                          small: true
                          min: 0
                          max: 600
                          icon: f7:timer
                          visible: "=(props.oTtimer) ? true: false"


The motion detection and Auto Off time should never be operating at the same time, so I have a rule to turn off the appropriate timer when the other one is interacted with -

rule "Lights, Hall - 'On Time' turns OFF 'Motion Detection'"
when
    Item Hall_Ceiling_Light_OnTime changed from "0 min"
then
    if (newState != "0 min" && Hall_MotionLightSensor_OnOff.state == ON) {
        Hall_MotionLightSensor_OnOff.sendCommand(OFF)
        logInfo("Lights - Hall", "'On Time' turned ON, so Motion Detection is turning OFF")
    }
end

rule "Lights, Hall - 'Motion Detection' turns OFF 'On Time'"
when
    Item Hall_MotionLightSensor_OnOff changed to ON
then
    if (Hall_Ceiling_Light_OnTime.state != "0 min") {
        Hall_Ceiling_Light_OnTime.sendCommand("0 min")
        logInfo("Lights - Hall", "'Motion Detection' turned ON, so 'On Time' set to 0 min")
    }
end

PROBLEMS
It’s working quite well but there is a few things I can’t get right:

1-
The two slider items hit 100% only about 1/4 of the total length of the slider

I’m sure it has something to do with the column width of something above, but I can’t figure it out.

2-
When the window expands on my iPhone 12, because of the way the fancy screen goes up into the very top of the phone, the closing X button is obscured by my iPhone time and other icons that live in this space.