Building Pages in the OH3 UI: documentation draft (2/3)

Is there a way to set a variable too without an action: variable ?

Idea is to evaluate a certain condition only once and use the result multiple times, e.g. for visibility of some of the widget components…

Hey,

I am trying to organize my widgets in a way that they look properly and there are no gaps between rows.

What I have today:

Any idea how to make that disappear? I tried to add two widgets to the same oh-grid-col but then it is not displaying anything.
The code looks like this:

config:
  label: Living Room
  sidebar: true
  order: "1"
blocks:
  - component: oh-block
    config: {}
    slots:
      default:
        - component: oh-grid-row
          config: {}
          slots:
            default:
              - component: oh-grid-col
                config:
                  width: "100"
                  small: "50"
                  medium: "33"
                slots:
                  default:
                    - component: widget:label-cell
                      config:
                        header: Dining Room
                        item: TemperatureDiningRoom_Temperature
                        secondaryitem: TemperatureDiningRoom_Humidity
              - component: oh-grid-col
                config:
                  width: "100"
                  small: "50"
                  medium: "33"
                slots:
                  default:
                    - component: widget:weatherCard
                      config:
                        widget_action: popup
                        widget_actionModal: widget:weatherPopup
                        bigCard: false
                        sunIndicator: true
                        dateScheme: YYYY-DD-MM
                        dateFormat: true
                        itemPrefix2: LocalWeatherAndForecast_
                        itemPrefix: OneCallAPI_
                        widget_actionModalConfig:
                          itemPrefix: OneCallAPI_
                          itemPrefix2: LocalWeatherAndForecast_
                          forecastDays: 4
                        locationTitle: Weather
              - component: oh-grid-col
                config:
                  width: "100"
                  medium: "33"
                slots:
                  default:
                    - component: widget:label-cell
                      config:
                        header: Living Room
                        item: OutletandTemperatureLivingRoom_Temperature
                        secondaryitem: OutletandTemperatureLivingRoom_Humidity
        - component: oh-grid-row
          config: {}
          slots:
            default:
              - component: oh-grid-col
                config:
                  width: "100"
                  small: "50"
                  medium: "33"
                slots:
                  default:
                    - component: widget:Shelly_RGBW
                      config:
                        title: Dining Room RGBW
                        power: RGBWLedStripDiningRoom_Power
                        color: RGBWLedStripDiningRoom_Color
                        gain: RGBWLedStripDiningRoom_Gain
                        white: RGBWLedStripDiningRoom_White
                        colormode: RGBWLedStripDiningRoom_ColorMode
              - component: oh-grid-col
                config:
                  width: "100"
                  small: "50"
                  medium: "33"
                slots:
                  default: []
              - component: oh-grid-col
                config:
                  width: "100"
                  medium: "33"
                slots:
                  default: []
masonry: []

My other question is whether it is possible to have direct links to the UI without the top bar and the left menu bar. I will have some room controller screens based on rPi and I am looking for some kind of kiosk mode.

Thanks!

Hi, your weather widget has a different hight vs. the room temperature widget, so it is a matter of fact that you have gaps. You can consider to use different blocks for it, e.g. a top block with the weather widget and another block for the temperature widgets. then you wont have gaps like this. I have worked around with this and positioned different widget with different sizes in multiple blocks. If you want all of your widgets in the same block, you can alternatively use custom widgets that all have the same hight.

if you then make sure they have the same max width, they flow nicely with the size of the page.

1 Like

trying to answer your second question. You can create a layout page and link directly to it or open it in a browser. I believe you cannot get completely rid of the navigation bar, but minimize it. Here is a good discussion to follow:

Thanks! I’ll come up with something then, either reorg or change the heights.

could you please post the code again I unfortunately can’t set the Hex Opacity Value :frowning: Thank you!!!

i assume you talk about the buttons.

"white" : "rgba(228,228,228,0.9)

ON is white and OFF is rgba value.

Could you please share that weather widget, please? :slight_smile:

Sure. It is based on the weather widget items definition from @RGroll, so using the prefix and items definition:

Here is my code. Please note that i have a link to wetteronline at the bottom of the code. Please adapt or link to whatever you like.

uid: weather_widget_v1
tags:
  - homekit look
  - black
  - in use
props:
  parameters:
    - description: Location?
      label: Location
      name: propLoc
      required: false
      type: TEXT
    - context: item
      description: Outdoor Temperature Item
      label: Item
      name: itemTempOut
      required: false
      type: TEXT
    - context: item
      description: Daily Minimum Temperature
      label: Item
      name: itemTempMin
      required: false
      type: TEXT
    - context: item
      description: Daily Minimum Temperature
      label: Item
      name: itemTempMax
      required: false
      type: TEXT
    - description: <b>Optional prefix</b> for item names.
      label: Item prefix
      name: itemPrefix
      required: false
      type: TEXT
  parameterGroups: []
timestamp: Feb 13, 2021, 12:44:48 PM
component: f7-card
config:
  style:
    noShadow: false
    class:
      - padding
    border-radius: var(--f7-card-expandable-border-radius)
    box-shadow: var(--f7-card-expandable-box-shadow)
    background: linear-gradient(to top, rgba(0,0,0,1) 35%, rgba(80,80,80,1) 100%)
    height: 8.5em
    min-width: 9.5em
    max-width: 400px
slots:
  content:
    - component: f7-block
      config:
        style:
          margin: -9px
          padding: 0px
          display: flex
          flex-direction: column
          align-items: start
    - component: f7-row
      config:
        class:
          - justify-content-left
      slots:
        default:
          - component: Label
            config:
              text: =props.propLoc
              class: -margin-top
              style:
                color: rgb(255,255,255)
                font-size: 20px
                overflow: hidden
                text-overflow: ellipsis
                text-transform: uppercase
                white-space: nowrap
                margin-bottom: 0px
    - component: f7-row
      slots:
        default:
          - component: f7-icon
            config:
              f7: "=(items[((!props.itemPrefix) ? '' : props.itemPrefix) + 'Current_Iconid'].state === '01d') ? 'sun_max_fill' : (items[((!props.itemPrefix) ? '' : props.itemPrefix) + 'Current_Iconid'].state === '01n') ? 'moon_stars_fill' : (items[((!props.itemPrefix) ? '' : props.itemPrefix) + 'Current_Iconid'].state === '02d') ? 'cloud_sun_fill' : (items[((!props.itemPrefix) ? '' : props.itemPrefix) + 'Current_Iconid'].state === '02n') ? 'cloud_moon_fill' : (items[((!props.itemPrefix) ? '' : props.itemPrefix) + 'Current_Iconid'].state === '03d') ? 'cloud_fill' : (items[((!props.itemPrefix) ? '' : props.itemPrefix) + 'Current_Iconid'].state === '03n') ? 'cloud_fill' : (items[((!props.itemPrefix) ? '' : props.itemPrefix) + 'Current_Iconid'].state === '04d') ? 'cloud_fill' : (items[((!props.itemPrefix) ? '' : props.itemPrefix) + 'Current_Iconid'].state === '04n') ? 'cloud_fill' : (items[((!props.itemPrefix) ? '' : props.itemPrefix) + 'Current_Iconid'].state === '09d') ? 'cloud_heavyrain_fill' : (items[((!props.itemPrefix) ? '' : props.itemPrefix) + 'Current_Iconid'].state === '09n') ? 'cloud_heavyrain_fill' : (items[((!props.itemPrefix) ? '' : props.itemPrefix) + 'Current_Iconid'].state === '10d') ? 'cloud_sun_rain_fill' : (items[((!props.itemPrefix) ? '' : props.itemPrefix) + 'Current_Iconid'].state === '10n') ? 'cloud_moon_rain_fill' : (items[((!props.itemPrefix) ? '' : props.itemPrefix) + 'Current_Iconid'].state === '11d') ? 'cloud_sun_bolt_fill' : (items[((!props.itemPrefix) ? '' : props.itemPrefix) + 'Current_Iconid'].state === '11n') ? 'cloud_moon_bolt_fill' : (items[((!props.itemPrefix) ? '' : props.itemPrefix) + 'Current_Iconid'].state === '13d') ? 'cloud_snow_fill' : (items[((!props.itemPrefix) ? '' : props.itemPrefix) + 'Current_Iconid'].state === '13n') ? 'cloud_snow_fill' : (items[((!props.itemPrefix) ? '' : props.itemPrefix) + 'Current_Iconid'].state === '50d') ? 'cloud_fog_fill' : (items[((!props.itemPrefix) ? '' : props.itemPrefix) + 'Current_Iconid'].state === '50n') ? 'cloud_fog_fill' : ''"
              size: 48
              color: white
          - component: Label
            config:
              text: =(Number.parseFloat(items[props.itemTempOut].state.split(" ")[0]) * 100 / 100).toFixed(0) + '°'
              class: -margin-top
              style:
                font-size: 40px
                margin-left: -2px
                margin-top: -9px
                color: rgb(255,255,255)
                white-space: nowrap
                overflow: hidden
                text-overflow: ellipsis
                font-weight: 300
    - component: f7-row
      config:
        class:
          - justify-content-left
      slots:
        default:
          - component: Label
            config:
              text: "=(!props.itemPrefix) ? items.Current_Condition.state : items[props.itemPrefix + 'Current_Condition'].state"
              style:
                font-size: 10px
                margin-left: 1px
                margin-bottom: 0px
                margin-top: 0px
                color: rgb(255,255,255)
                white-space: nowrap
                overflow: hidden
                text-overflow: ellipsis
                font-weight: 400
    - component: f7-row
      config:
        class:
          - justify-content-left
      slots:
        default:
          - component: Label
            config:
              text: ="Hi:" +" " + (Number.parseFloat(items[props.itemTempMax].state.split(" ")[0]) * 100 / 100).toFixed(0) + '°'
              style:
                font-size: 14px
                margin-left: 0px
                margin-top: 0px
                color: rgb(255,255,255)
                white-space: nowrap
                overflow: hidden
                text-overflow: ellipsis
                font-weight: 500
          - component: Label
            config:
              text: ="| Lo:" +" " + (Number.parseFloat(items[props.itemTempMin].state.split(" ")[0]) * 100 / 100).toFixed(0) + '°'
              style:
                font-size: 14px
                margin-left: 0px
                margin-top: 0px
                color: rgb(255,255,255)
                white-space: nowrap
                overflow: hidden
                text-overflow: ellipsis
                font-weight: 400
    - component: oh-link
      config:
        action: url
        actionUrl: http://m.wetteronline.de/yourtown 
        open-in: .popOver
        style:
          position: absolute
          width: 100%
          height: 100%
          top: 0
          left: 0
2 Likes

I really love your IOS Style Widgets :star_struck: do you have a page oder something where i can finde all your “codes” or don’t you want to publish it, which would be perfectly fine

Thanks for the help! :slight_smile: Made some modifications to show more and now it fits :slight_smile:

2 Likes

no problem to share some of them:

Switch:

image

uid: widget_f7-card_test_v3
tags:
  - homekit-look
  - in use
props:
  parameters:
    - description: A text prop
      label: Prop 1
      name: prop1
      required: false
      type: TEXT
    - context: item
      description: An item to control
      label: Item
      name: item
      required: false
      type: TEXT
  parameterGroups: []
timestamp: Feb 15, 2021, 10:26:44 AM
component: f7-card
config:
  style:
    noShadow: false
    class:
      - padding
    border-radius: var(--f7-card-expandable-border-radius)
    box-shadow: '=(items[props.item].state === "ON") ? "10px 10px 28px 1px rgba(255,234,5,0.3)" : "var(--f7-card-expandable-box-shadow)"'
    background-color: '=(items[props.item].state === "ON") ? "rgb(255,255,255)" : "rgba(228,228,228,0.9)"'
    height: 8.5em
    min-width: 8.5em
slots:
  content:
    - component: f7-block
      config:
        style:
          margin: 0px
          padding: 0px
          display: flex
          flex-direction: column
          align-items: start
      slots:
        default:
          - component: f7-icon
            config:
              f7: '=(items[props.item].state === "ON") ? "lightbulb_fill" : "lightbulb"'
              size: 24
              margin: 0px
              padding: 0px
              style:
                color: '=(items[props.item].state === "ON") ? "rgba(255,234,5,1)" : "gray"'
          - component: Label
            config:
              text: =props.prop1
              style:
                font-size: 15px
                font-weight: 500
                margin-left: 0px
                margin-top: 20px
                padding: 0px
                color: '=(items[props.item].state === "ON") ? "black" : "rgba(0, 0, 0, 0.5)"'
          - component: Label
            config:
              text: '=(items[props.item].state === "ON") ? "AN" : "AUS"'
              style:
                font-size: 15px
                font-weight: 500
                margin-left: 0px
                margin-top: 0px
                padding: 0px
                color: '=(items[props.item].state === "ON") ? "red" : "rgba(0, 0, 0, 0.5)"'
    - component: oh-link
      config:
        action: toggle
        actionItem: =props.item
        actionCommand: ON
        actionCommandAlt: OFF
        style:
          position: absolute
          width: 100%
          height: 100%
          top: 0
          left: 0
          actionPosition: center
        actionFeedback: Done!

image

uid: widget_f7-card_status_Icon_Battery
tags:
  - homekit-like
props:
  parameters:
    - description: The headline
      label: Label-description
      name: prop1
      required: true
      type: TEXT
    - description: The size of the Font
      label: Font-Size
      name: fontsize
      required: true
      type: TEXT
    - description: the oh icon name
      label: f7 icon name
      name: prop2
      required: false
      type: TEXT
    - context: item
      description: Status of the Device
      label: Status
      name: item
      required: false
      type: TEXT
    - context: item
      description: Battery status of the device
      label: Battery
      name: item2
      required: false
      type: TEXT
  parameterGroups: []
timestamp: Feb 9, 2021, 9:05:48 AM
component: f7-card
config:
  style:
    noShadow: false
    class:
      - padding: 0px
    border-radius: var(--f7-card-expandable-border-radius)
    box-shadow: var(--f7-card-expandable-box-shadow)
    background-color: '=(items[props.item].state == "OPEN") ? "white" : "rgba(228,228,228,0.9)"'
    height: 8.5em
slots:
  content:
    - component: f7-block
      config:
        style:
          margin: 1px
          padding: 1px
          display: flex
          flex-direction: column
          align-items: start
      slots:
        default:
          - component: f7-icon
            config:
              f7: '=(items[props.item2].state === "OFF") ? "battery_100" : "battery_25"'
              size: 25
              color: '=(items[props.item2].state == "OFF") ? "green" : "red"'
              style:
                opacity: 0.5
                position: absolute
                top: -11px
                right: -5px
          - component: oh-icon
            config:
              icon: =props.prop2
              height: 38
              style:
                filter: grayscale()
                opacity: 0.7
                position: absolute
                top: -5px
                left: -5px
                margin-top: -5px
          - component: Label
            config:
              text: =props.prop1
              style:
                font-size: =props.fontsize + "px"
                font-weight: 500
                line-height: 1.1
                position: relative
                margin-left: 0px
                margin-top: 44px
                color: '=(items[props.item].state == "OPEN") ? "black" : "rgb(0,0,0,0.5)"'
          - component: Label
            config:
              text: =items[props.item].displayState
              style:
                margin-left: 0px
                margin-top: 2px
                font-size: 14px
                font-weight: 500
                color: '=(items[props.item].state == "OPEN") ? "red" : "rgb(0,0,0,0.5)"'

Thermostate Control with updated orange / green badge for set temperature / color code in case the valve is ON or >15%. Still to be improved in terms of structure of the widget, need more time for proper row-column work, my current workaround is to play with margin and padding… Widget turns white background when some thresholds are reached like temperature or humidity.

image
image

uid: widget_f7-card_Temp_V2_Control
tags:
  - homekit-look
  - Soll Tag
  - in use
props:
  parameters:
    - description: Location?
      label: Prop 1
      name: prop1
      required: false
      type: TEXT
    - description: Thermostate icon f7
      label: f7 icon name
      name: icon1
      required: false
      type: TEXT
    - description: Humidity icon f7
      label: f7 icon name
      name: icon2
      required: false
      type: TEXT
    - context: item
      description: A Temperature item to display
      label: Item
      name: itemTemp
      required: false
      type: TEXT
    - context: item
      description: A Set-Temperature item to display
      label: Item
      name: itemTempSoll
      required: false
      type: TEXT
    - context: item
      description: An Humidity item to display
      label: Item
      name: itemHum
      required: false
      type: TEXT
    - context: item
      description: the valve state
      label: Item
      name: itemValve
      required: false
      type: TEXT
    - context: item
      description: Item for temperature channel
      label: Ambient temperature
      name: itemAmbientTemp
      required: false
      type: TEXT
    - context: item
      description: Item for outdoor temperature channel
      label: Outdoor temperature
      name: itemOutdoorTemp
      required: false
      type: TEXT
    - context: item
      description: Item for target temperature channel
      label: Target temperature
      name: itemTargetTemp
      required: true
      type: TEXT
    - context: item
      description: Item for operation mode channel
      label: Operation mode
      name: itemMode
      required: true
      type: TEXT
  parameterGroups: []
timestamp: Feb 17, 2021, 4:03:46 PM
component: f7-card
config:
  style:
    noShadow: false
    class:
      - padding
    border-radius: var(--f7-card-expandable-border-radius)
    box-shadow: var(--f7-card-expandable-box-shadow)
    background-color: '=(Number.parseFloat(items[props.itemHum].state.split(" ")[0]) > 60) ? "white" : (Number.parseFloat(items[props.itemTemp].state.split(" ")[0]) > 23) ? "white" : "rgba(228,228,228,0.9)"'
    height: 8.5em
    min-width: 9.5em
    max-width: 400px
slots:
  content:
    - component: f7-block
      config:
        style:
          margin-left: -6px
          padding: -1px
          display: flex
          flex-direction: column
          align-items: start
    - component: f7-row
      config:
        class:
          - justify-content-left
      slots:
        default:
          - component: f7-icon
            config:
              f7: =props.icon1
              size: 22
              class:
                - align-self-center
              style:
                margin-left: -6px
                margin-top: 0px
                color: '=(Number.parseFloat(items[props.itemHum].state.split(" ")[0]) > 60) ? "black" : (Number.parseFloat(items[props.itemTemp].state.split(" ")[0]) > 23) ? "black" : "rgb(0,0,0,0.5)"'
          - component: Label
            config:
              text: =props.prop1
              style:
                font-size: 14px
                font-weight: 500
                margin-left: -4px
                margin-top: 0px
                color: '=(Number.parseFloat(items[props.itemHum].state.split(" ")[0]) > 60) ? "black" : (Number.parseFloat(items[props.itemTemp].state.split(" ")[0]) > 23) ? "black" : "rgb(0,0,0,0.5)"'
    - component: f7-row
      config:
        class:
          - justify-content-left
      slots:
        default:
          - component: Label
            config:
              text: =(Number.parseFloat(items[props.itemTemp].state.split(" ")[0]) * 100 / 100).toFixed(1) + '°'
              style:
                font-size: 32px
                line-height: 1.1
                font-weight: 498
                margin-left: 0px
                margin-top: 0px
                color: '=(Number.parseFloat(items[props.itemHum].state.split(" ")[0]) > 60) ? "black" : (Number.parseFloat(items[props.itemTemp].state.split(" ")[0]) > 23) ? "black" : "rgb(0,0,0,0.5)"'
    - component: f7-row
      config:
        class:
          - justify-content-left
      slots:
        default:
          - component: f7-icon
            config:
              f7: =props.icon2
              size: 16
              style:
                top: 5px
                left: 0px
                color: '=(Number.parseFloat(items[props.itemHum].state.split(" ")[0]) > 60) ? "red" : "rgb(0,0,0,0.5)"'
          - component: Label
            config:
              text: =(Number.parseFloat(items[props.itemHum].state.split(" ")[0]) * 100 / 100).toFixed(0) + '%'
              visible: =props.itemHum !== undefined
              style:
                top: 5px
                left: 0px
                font-size: 13px
                font-weight: 500
                color: '=(Number.parseFloat(items[props.itemHum].state.split(" ")[0]) > 60) ? "red" : "rgb(0,0,0,0.5)"'
    - component: f7-row
      config:
        class:
          - float-right
      slots:
        default:
          - component: f7-icon
            config:
              f7: arrowshape_turn_up_right
              size: 16
              style:
                opacity: 0.5
                bottom: 0px
                right: -45px
                color: rgb(0,0,0,0.5)
          - component: f7-chip
            config:
              bgColor: '=(Number.parseFloat(items[props.itemValve].state.split(" ")[0]) > 15) ? "orange" : "green"'
              text: =(Number.parseFloat(items[props.itemTempSoll].state.split(" ")[0]) * 100 / 100).toFixed(0)
              style:
                color: white
                size: 20
                z-index: 2
                bottom: 75px
                border-radius: 25px 25px 25px 25px
                margin-top: 2px
                right: -5px
    - component: oh-trend
      config:
        trendItem: =[props.itemTemp]
        style:
          --f7-theme-color-bg-color: transparent
          background: var(--f7-theme-color-bg-color)
          filter: opacity(30%)
          position: absolute
          width: 100%
          height: 100%
          top: 0
          left: 2
          z-index: 1
    - component: oh-link
      config:
        action: popup
        actionModal: widget:HM_Thermostat_Control_Final
        actionModalConfig:
          itemAmbientTemp: =props.itemAmbientTemp
          itemOutdoorTemp: =props.itemOutdoorTemp
          itemTargetTemp: =props.itemTargetTemp
          itemMode: =props.itemMode
        style:
          position: absolute
          width: 100%
          height: 100%
          top: 0
          left: 0
3 Likes

Is there a way to do not only tap actions but also hold actions as well for the cards? I tried to search but no luck…

I would love to be able to tap for on/off and if I tap for 2-3 seconds then it opens a popup with detailed controls.

1 Like

Thank you so much : ) is there a easy way to change the standard “label cells” to the homekit style?
Basically is use the “oh-label-cell” as a switch and to open a nother page.
I tried to insert the style form your widget in the yaml file from the “oh-label-cel”, but nothing changes.

the problem with the oh-label-cell is that it has limitations. specifically if you want to control the bg color other than the standard yellow, green, red… etc… Also you cannot fully control where to place your content. This is mainly why i have re-created the oh-label-cells with my versions, using a f7-card. So the answer is no, you unfortunately cannot tweak the oh-label-cells with my widgets to make them look alike.

I am still waiting for an answer on this.

I am getting more familiar with the UI development so would be great to design the widgets in a way that I can turn on things with a short-tap and be able to do detailed settings by doing a long-tap.

This is more or less what I’ve been using on Home Assistant (which from I am trying to migrate because of the UI and how easy to develop widgets on OH).

Unless this is possible somehow the only thing I could do is to have an icon (like what @muelli1967 showed a few posts back) on the bottom right and by tapping that open the detailed controls. Long-tap would be better :slight_smile:.

Thanks!

Can I find somewhere the code of a pre-made widget-components like ‘oh-image-card’ so that I can create a custom widget based on this code + further adjustments?

The cells have this functionality. You can configure an action on left click/short tap, but it will expand on right click/long tap/click on the 3-dot icon. You can configure what’s in the expanded card by placing additional controls in the default slot.

The default widgets are made with Vue, not YAML, so you can base yours on them (overriding CSS variables for example) but not modify them.

Thanks @ysc!

Sounds like you’ve just helped me to find something to do in the next couple of days :stuck_out_tongue:

For others:

Meawhile I found this:

This is really helping, this is what exactly I am trying to do. :slight_smile: