[OH3] Main UI Examples

Hi,
works perfectly!

Screenshot_20221222_192648

Thanks a lot for your help!

With v3.4 there is an @ operator available which shows displayState if available and state if not available.

@(items[props.status])

It helped tidying up my widgets😀

1 Like

mmm i’ve tried different ways but it doesn’t work for me :frowning:
i’m running OH snapshot 4.0.0

can you tell me why?

thanks a lot!

                - component: Label
                  config:
                    style:
                      color: black
                      margin-left: 2px
                      z-index: 98
                    text: @(items[props.opstateItem])

Does it not need to be

text: =@(items[props.opstateItem])

I’m currently on stable 3.3.0

i’ve tried but the state is reported as “-”.
that item should have both state and displayState…

Yes, that is correct.
And you need to have minimum version 3.4 M6.

actually i’m testing on 4.0.0 snapshot and still doesn’t work for me :frowning:

You should not include the items object. The @ operator (and the @@ operator which forces just the state instead of the displayState) acts directly on the item name string. So, in this case:

text: =@(props.opstateItem)
2 Likes

As always,great answer and also great explanation.

Thank you so much Justin!

Technically those are the 2 equivalences:

@<name>    <=>    items[<name>].displayState !== undefined ? items[<name>].displayState : items[<name>].state

@@<name>   <=>    items[<name>].state

@ et @@ are unary operators so due to their precedences parentheses may be useful, but not always.
For instance @props.itemName alone is fine, but beware of the differences between:

@@itemsPrefix + '_Suffix`      <=>    items[itemsPrefix].state + '_Suffix'

and

@@(itemsPrefix + '_Suffix`)    <=>    items[itemsPrefix + '_Suffix'].state
5 Likes

Hi Andras,

many thanks for you Examples, especially the room Card. Quite useful as I was searching for a card that could open a group action…

bkumio

Finally I feel comfortable enough to post my example so far. Have had some great inspiration from @Integer and @BG56 but I’m also inspired by the great design of @Dimitris in the new main widget topic. Thank you all.

I really like the simpleness of the Apple Home app and I try to copy that part, too. When Items are off they are semi-transparent and when one they become white. In some cases I will enable them to become a special color, for example the AlarmPartitionStatus may become red when there is an alarm.

The LightControl widget can be collapsed and expanded by clicking on the title and depending on the set properties will display a slider to dim, two sliders for dim and white tone or will display the color control with three sliders and a set of pre-defined colors.

My HarmonyControl will show the current activity when on and it then also features an off button. It can be expanded when off, but it automatically expanded when on.

I’m currently working on a new widget for displaying status from the thermostate and a player widget for my future Chromecast.

1 Like

Would you mind sharing the code of your widgets ?

1 Like

Sure!

LightControl:

uid: LightControl
tags: []
props:
  parameters:
    - description: Header icon
      label: Icon
      name: iconHeader
      required: false
      type: TEXT
      groupName: header
    - description: Header text
      label: Header
      name: header
      required: false
      type: TEXT
      groupName: header
    - context: item
      description: Item to control on/off
      label: Item ON OFF
      name: itemSwitch
      required: true
      type: TEXT
      groupName: setup
    - context: item
      description: Item to control brightness
      label: Item brightness
      name: itemBrightness
      required: false
      type: TEXT
      groupName: setup
    - context: item
      description: Item to control color temperature
      label: Item light temperature
      name: itemLightTemperature
      required: false
      type: TEXT
      groupName: setup
    - context: item
      description: Item to control color
      label: Item light color
      name: itemColor
      required: false
      type: TEXT
      groupName: setup
  parameterGroups:
    - name: header
      description: Widget general settings
    - name: setup
      description: Widget specific settings
timestamp: Dec 29, 2022, 10:55:04 PM
component: f7-card
config:
  style:
    background-color: "=items[props.itemSwitch].state === 'ON' ? 'rgba(255, 255, 255, 1)' : 'rgba(34, 34, 34, 0.3)'"
    border-radius: 15px
    box-shadow: 0px 0px
    margin: 5px 5px
slots:
  content:
    - component: f7-row
      config:
        style:
          justify-content: left
      slots:
        default:
          - component: oh-image
            config:
              url: ='/static/icons/' + props.iconHeader + '.svg'
              style:
                height: 25px
                margin-right: 10px
                filter: '=items[props.itemSwitch].state === "ON" ? "brightness(1)" : "brightness(0) invert(1)"'
              visible: "=props.iconHeader ? true : false"
          - component: Label
            config:
              text: "=props.header ? props.header : 'Set header title'"
              style:
                font-size: var(--f7-card-header-font-size)
                font-weight: var(--f7-card-header-font-weight)
                color: '=items[props.itemSwitch].state === "ON" ? "black" : "white"'
                --f7-list-item-after-text-color: '=items[props.itemSwitch].state === "ON" ? "black" : "white"'
          - component: oh-toggle
            config:
              item: =props.itemSwitch
              style:
                position: absolute
                top: 20px
                right: 20px
                z-index: 100
                --f7-toggle-active-color: "#F8BB00"
                --f7-toggle-inactive-color: "#ccc"
    - component: oh-link
      config:
        action: variable
        actionVariable: visiblePart
        actionVariableValue: "=vars.visiblePart === true ? false : true"
        style:
          position: absolute
          top: 0
          left: 0
          height: 55px
          width: 100%
          actionPosition: center
    - component: f7-row
      config:
        style:
          justify-content: center
          margin-top: 25px
        visible: =vars.visiblePart === true && props.itemBrightness !== Null
      slots:
        default:
          - component: oh-slider
            config:
              item: =props.itemBrightness
              min: 0
              max: 100
              style:
                width: calc(100% - 20px)
                --f7-range-bar-size: 10px
                --f7-range-bar-border-radius: 8px
                --f7-range-knob-size: 20px
                --f7-range-knob-box-shadow: 0 2px 4px rgba(0, 0, 0, 0.3)
    - component: f7-row
      config:
        style:
          justify-content: center
          margin-top: 10px
        visible: =vars.visiblePart === true && props.itemLightTemperature !== Null
      slots:
        default:
          - component: oh-slider
            config:
              item: =props.itemLightTemperature
              min: 0
              max: 100
              style:
                width: calc(100% - 20px)
                --f7-range-bar-size: 10px
                --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(224, 238, 238),rgba(255, 215, 44, 0.5),rgba(255, 215, 44, 0.8))
                --f7-range-knob-box-shadow: 0 2px 4px rgba(0, 0, 0, 0.3)
                --f7-range-label-text-color: black
    - component: f7-row
      config:
        style:
          justify-content: center
          margin-top: 10px
          --f7-range-bar-size: 10px
          --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
          --f7-color-picker-slider-size: 10px
          --f7-color-picker-slider-knob-size: 20px
          width: 100%
        visible: =vars.visiblePart === true && props.itemColor !== Null
      slots:
        default:
          - component: oh-colorpicker
            config:
              color: red
              label: true
              item: =props.itemColor
              modules:
                - hsb-sliders
    - component: f7-row
      config:
        style:
          justify-content: center
          margin-top: 10px
        visible: =vars.visiblePart === true && props.itemColor !== Null
      slots:
        default:
          - component: oh-button
            config:
              iconColor: yellow
              iconF7: app_fill
              iconSize: 25
              action: command
              actionItem: =props.itemColor
              actionCommand: 60,100,100
              style:
                padding: 0px 5px
                height: 35px
                background: transparent
          - component: oh-button
            config:
              iconColor: orange
              iconF7: app_fill
              iconSize: 25
              action: command
              actionItem: =props.itemColor
              actionCommand: 30,100,100
              style:
                padding: 0px 5px
                height: 35px
                background: transparent
          - component: oh-button
            config:
              iconColor: deeporange
              iconF7: app_fill
              iconSize: 25
              action: command
              actionItem: =props.itemColor
              actionCommand: 15,100,100
              style:
                padding: 0px 5px
                height: 35px
                background: transparent
          - component: oh-button
            config:
              iconColor: red
              iconF7: app_fill
              iconSize: 25
              action: command
              actionItem: =props.itemColor
              actionCommand: 0,100,100
              style:
                padding: 0px 5px
                height: 35px
                background: transparent
          - component: oh-button
            config:
              iconColor: purple
              iconF7: app_fill
              iconSize: 25
              action: command
              actionItem: =props.itemColor
              actionCommand: 300,100,100
              style:
                padding: 0px 5px
                height: 35px
                background: transparent
          - component: oh-button
            config:
              iconColor: blue
              iconF7: app_fill
              iconSize: 25
              action: command
              actionItem: =props.itemColor
              actionCommand: 240,100,100
              style:
                padding: 0px 5px
                height: 35px
                background: transparent
          - component: oh-button
            config:
              iconColor: lightblue
              iconF7: app_fill
              iconSize: 25
              action: command
              actionItem: =props.itemColor
              actionCommand: 180,100,100
              style:
                padding: 0px 5px
                height: 35px
                background: transparent
          - component: oh-button
            config:
              iconColor: green
              iconF7: app_fill
              iconSize: 25
              action: command
              actionItem: =props.itemColor
              actionCommand: 120,100,100
              style:
                padding: 0px 5px
                height: 35px
                background: transparent

SimpleSwitch:

uid: SimpleSwitch
tags: []
props:
  parameters:
    - description: Header icon
      label: Icon
      name: iconHeader
      required: false
      type: TEXT
      groupName: header
    - description: Header text
      label: Header
      name: header
      required: false
      type: TEXT
      groupName: header
    - context: item
      description: Item to control on/off
      label: Item
      name: itemSwitch
      required: false
      type: TEXT
      groupName: setup
  parameterGroups:
    - name: header
      description: Widget general settings
    - name: setup
      description: Widget specific settings
timestamp: Dec 29, 2022, 10:55:19 PM
component: f7-card
config:
  style:
    background-color: "=items[props.itemSwitch].state === 'ON' ? 'rgba(255, 255, 255, 1)' : 'rgba(34, 34, 34, 0.3)'"
    border-radius: 15px
    box-shadow: 0px 0px
    margin: 5px 5px
slots:
  content:
    - component: f7-row
      config:
        style:
          justify-content: left
      slots:
        default:
          - component: oh-image
            config:
              url: ='/static/icons/' + props.iconHeader + '.svg'
              style:
                height: 25px
                margin-right: 10px
                filter: '=items[props.itemSwitch].state === "ON" ? "brightness(1)" : "brightness(0) invert(1)"'
              visible: "=props.iconHeader ? true : false"
          - component: Label
            config:
              text: "=props.header ? props.header : 'Set header title'"
              style:
                font-size: var(--f7-card-header-font-size)
                font-weight: var(--f7-card-header-font-weight)
                color: '=items[props.itemSwitch].state === "ON" ? "black" : "white"'
                --f7-list-item-after-text-color: '=items[props.itemSwitch].state === "ON" ? "black" : "white"'
    - component: f7-row
      config:
        style:
          justify-content: center
          margin-top: 25px
      slots:
        default:
          - component: oh-toggle
            config:
              item: =props.itemSwitch
              style:
                --f7-toggle-active-color: "#F8BB00"
                --f7-toggle-inactive-color: "#ccc"

Let me double check my HarmonyControl first before I post it here. Want to make sure that it’s fully functioning :slight_smile:

1 Like

Very nice! Looks like the one i’ve made too :slight_smile:

I will take from your widget, the preset line colors,i forgot to add that could be useful.

Thanks!

Here is the code for the Harmony widget:

uid: HarmonyControl
tags: []
props:
  parameters:
    - description: Header icon
      label: Icon
      name: iconHeader
      required: false
      type: TEXT
      groupName: header
    - description: Header text
      label: Header
      name: header
      required: false
      type: TEXT
      groupName: header
    - context: item
      description: Item to control Harmony
      label: Item current activity
      name: itemCurrentActivity
      required: true
      type: TEXT
      groupName: setup
    - description: 'Use JSON format [{"label": "Label", "icon": "Icon", "command": "Command to send"}, ...}]'
      label: Activities
      name: activities
      required: true
      type: TEXT
      groupName: setup
  parameterGroups:
    - name: header
      description: Widget general settings
    - name: setup
      description: Widget specific settings
timestamp: Jan 8, 2023, 10:53:50 PM
component: f7-card
config:
  style:
    background-color: "=items[props.itemCurrentActivity].state !== 'PowerOff' ? 'rgba(255, 255, 255, 1)' : 'rgba(34, 34, 34, 0.3)'"
    border-radius: 15px
    box-shadow: 0px 0px
    margin: 5px 5px
slots:
  content:
    - component: f7-row
      config:
        style:
          justify-content: left
      slots:
        default:
          - component: oh-image
            config:
              url: ='/static/icons/' + props.iconHeader + '.svg'
              style:
                height: 25px
                margin-right: 10px
                filter: '=items[props.itemCurrentActivity].state !== "PowerOff" ? "brightness(1)" : "brightness(0) invert(1)"'
              visible: "=props.iconHeader ? true : false"
          - component: Label
            config:
              text: "=props.header ? props.header : 'Set header title'"
              style:
                font-size: var(--f7-card-header-font-size)
                font-weight: var(--f7-card-header-font-weight)
                color: '=items[props.itemCurrentActivity].state !== "PowerOff" ? "black" : "white"'
                --f7-list-item-after-text-color: '=items[props.itemCurrentActivity].state !== "PowerOff" ? "black" : "white"'
          - component: oh-link
            config:
              action: command
              actionItem: =props.itemCurrentActivity
              actionCommand: PowerOff
              actionFeedback: Uitgeschakeld
              style:
                position: absolute
                top: 20px
                right: 20px
                z-index: 100
              visible: "=items[props.itemCurrentActivity].state !== 'PowerOff' ? true : false"
            slots:
              default:
                - component: oh-image
                  config:
                    url: ='/static/icons/power-off.svg'
                    style:
                      height: 18px
    - component: oh-link
      config:
        action: variable
        actionVariable: visiblePart
        actionVariableValue: "=vars.visiblePart === true ? false : true"
        style:
          position: absolute
          top: 0
          left: 0
          height: 55px
          width: 100%
          actionPosition: center
    - component: f7-row
      config:
        style:
          justify-content: left
          margin-top: 10px
          margin-bottom: 10px
      slots:
        default:
          - component: f7-chip
            config:
              text: "=items[props.itemCurrentActivity].state === 'PowerOff' ? 'Uitgeschakeld' : items[props.itemCurrentActivity].state"
              style:
                background-color: "=items[props.itemCurrentActivity].state !== 'PowerOff' ? '#F8BB00' : '#8c8c8c'"
                color: "#fff"
                margin-left: 35px
                font-size: 10px
                height: 18px
    - component: f7-row
      config:
        visible: "=items[props.itemCurrentActivity].state !== 'PowerOff' || vars.visiblePart === true ? true : false"
        style:
          margin-top: 20px
        class:
          - text-align-center
      slots:
        default:
          - component: oh-repeater
            config:
              for: activity
              sourceType: array
              fragment: true
              in: '=JSON.parse(props.activities)'
            slots:
              default:
                - component: f7-col
                  config:
                  slots:
                    default:
                      - component: oh-link
                        config:
                          action: command
                          actionItem: =props.itemCurrentActivity
                          actionCommand: =loop.activity.command
                          actionFeedback: =loop.activity.label + " gestart"
                        slots:
                          default:
                            - component: oh-image
                              config:
                                url: ='/static/icons/' + loop.activity.icon + '.svg'
                                style:
                                  height: 18px
    - component: f7-row
      config:
        visible: "=items[props.itemCurrentActivity].state !== 'PowerOff' || vars.visiblePart === true ? true : false"
        style:
          margin-top: 10px
        class:
          - text-align-center
      slots:
        default:
          - component: oh-repeater
            config:
              for: activity
              sourceType: array
              fragment: true
              in: '=JSON.parse(props.activities)'
            slots:
              default:
                - component: f7-col
                  config:
                  slots:
                    default:
                      - component: oh-link
                        config:
                          action: command
                          actionItem: =props.itemCurrentActivity
                          actionCommand: =loop.activity.command
                          actionFeedback: =loop.activity.label + " gestart"
                        slots:
                          default:
                            - component: Label
                              config:
                                text: =loop.activity.label
                                style:
                                  font-size: 11px
                                  color: '=items[props.itemCurrentActivity].state !== "PowerOff" ? "black" : "white"'

And I’ve got an ‘appliance’ widget as well. I use it in conjunction with the HomeConnect binding to display dishwasher and washing machine status.

uid: ApplianceStatus
tags: []
props:
  parameters:
    - description: Header icon
      label: Icon
      name: iconHeader
      required: false
      type: TEXT
      groupName: header
    - description: Header text
      label: Header
      name: header
      required: false
      type: TEXT
      groupName: header
    - context: item
      description: select item for operation state
      label: Item
      name: operationState
      required: false
      type: TEXT
      groupName: setup
    - context: item
      description: select item for active program
      label: Item
      name: activeProgram
      required: false
      type: TEXT
      groupName: setup
    - context: item
      description: select item for remaining time
      label: Item
      name: remainingTime
      required: false
      type: TEXT
      groupName: setup
    - context: item
      description: select item for progress
      label: Item
      name: progress
      required: false
      type: TEXT
      groupName: setup
    - context: item
      description: select item for temperature
      label: Item
      name: temperature
      required: false
      type: TEXT
      groupName: setup
    - context: item
      description: select item for spinspeed
      label: Item
      name: spinSpeed
      required: false
      type: TEXT
      groupName: setup
  parameterGroups:
    - name: header
      description: Widget general settings
    - name: setup
      description: Widget specific settings
timestamp: Jan 8, 2023, 10:12:11 PM
component: f7-card
config:
  style:
    background-color: "=items[props.operationState].state === 'Run' ? 'rgba(255, 255, 255, 1)' : 'rgba(34, 34, 34, 0.3)'"
    border-radius: 15px
    box-shadow: 0px 0px
    margin: 5px 5px
slots:
  content:
    - component: f7-row
      config:
        style:
          justify-content: left
      slots:
        default:
          - component: oh-image
            config:
              url: ='/static/icons/' + props.iconHeader + '.svg'
              style:
                height: 25px
                margin-right: 10px
                filter: '=items[props.operationState].state === "Run" ? "brightness(1)" : "brightness(0) invert(1)"'
              visible: "=props.iconHeader ? true : false"
          - component: Label
            config:
              text: "=props.header ? props.header : 'Set header title'"
              style:
                font-size: var(--f7-card-header-font-size)
                font-weight: var(--f7-card-header-font-weight)
                color: '=items[props.operationState].state === "Run" ? "black" : "white"'
    - component: f7-row
      config:
        style:
          justify-content: left
          margin-top: 10px
          margin-bottom: 10px
      slots:
        default:
          - component: f7-chip
            config:
              text: =items[props.operationState].displayState
              style:
                background-color: "=items[props.operationState].state === 'Run' ? '#F8BB00' : '#8c8c8c'"
                color: "#fff"
                margin-left: 35px
                font-size: 10px
                height: 18px
    - component: f7-row
      config:
        visible: "=items[props.operationState].state === 'Run' ? true : false"
        style:
          justify-content: left
      slots:
        default:
          - component: f7-chip
            config:
              style:
                --f7-chip-bg-color: rgba(255, 255, 255, 0)
              text: =items[props.activeProgram].state
              iconColor: black
              iconF7: info_circle
              iconSize: 17
          - component: f7-chip
            config:
              visible: =items[props.temperature]
              style:
                --f7-chip-bg-color: rgba(255, 255, 255, 0)
              text: =items[props.temperature].displayState
          - component: f7-chip
            config:
              visible: =items[props.spinSpeed]
              style:
                --f7-chip-bg-color: rgba(255, 255, 255, 0)
              text: =items[props.spinSpeed].displayState
    - component: f7-row
      config:
        visible: "=items[props.operationState].state === 'Run' ? true : false"
        style:
          justify-content: left
      slots:
        default:
          - component: f7-chip
            config:
              style:
                --f7-chip-bg-color: rgba(255, 255, 255, 0)
              text: "='Resterende tijd: ' + items[props.remainingTime].state"
              iconColor: black
              iconF7: clock
              iconSize: 17
          - component: f7-chip
            config:
              style:
                --f7-chip-bg-color: rgba(255, 255, 255, 0)
              text: "='Voortgang: ' + (items[props.progress].displayState === 'undefined') ? items[props.progress].displayState : items[props.progress].displayState"
              iconColor: black
              iconF7: percent
              iconSize: 17

Feel free to use or modify them as you wish. This weeks project: thermostat ‘status’ widget to display what my heating actor is doing (per room).

Anything to show already ?

Not yet actually… I’m still thinking about what exactly to display (related to what my KNX actor has available) and how to visualize. Also I don’t have airco or cooling so it’s only for heating.

1 Like

I’ll also share my Main UI. It all started with the ‘room’ widgets which were from the beginning of OH3 I think. Afterwards I added additional widgets for turning on lights, dimmers…and tried to create or modify other ones:
Main screen




Additional window when pressing the wheel on the Media widget
image|385

Example when clicking on a room:


image|385

Example of the charts pages which opens when clicking on one of the numbers in the Energy widget:


Some random widget for checking devices etc (they are all filled by items which gets their values via rules):



Pressing the eye icon in the widgets above will show all items, without my specific filter (eg I only show batteries % below 40%, I only show GitHub releases of the last 15 days…) The list icons shows the complete content of the group without any formatting


image|385

6 Likes