[OH3] Main UI - New „main_widget“ - development and testing [deprecated]

Hi,
with growing interest I’ve been following the development of @Dimitris design draft for a few days now.

As I am professionally involved with Vue.js I found the implementation in openHAB very promising, however the usage within openHAB is significantly different than how I have been using Vuejs so far.

I also have problems to find out where I can find documentation that explains the concept of the implementation and the interaction within openHABs a bit better and I don’t mean the stuff with Things, Models, Items and co, but rather website structure technically. How do I know which config parameters I can use for a page?

Currently I ask myself the question: How can I query the page:uid as an expression?

Have a nice evening and good luck with the implementation of @Dimitris design, which I like very much.

1 Like

I believe that the variable is overwritten with every action, so you’ll have to be careful. If you want an action to change just one key in an object that has more than that, I suspect that you would still have to lay out all the object keys and just pass the unused one through for every action:

- component: oh-button
  config:
    action: variable
    actionVariable: objVar
    actionVariableValue:
      animalHeight: 16 ft.
      animalName: Giraffe
      keyNotUsedHere: =vars.objVar.keyNotUsedHere
    text: Object button

@JustinG wouldn’t it be easier do store the object in the main component as parameter group property and pass this to the child components?

The OH widget system is a very trimmed down interface to the UI’s vue pages. A lot of the direct Vue capabilities that you are expecting are not available to the user via this system. It’s been intentionally developed to be low code and nearly exclusively yaml to be approachable by users with little to no coding experience. Besides basic yaml structure, there is access to rudimentary js expressions and a few workarounds for some more advanced features. You can see the source for the UI here: GitHub - openhab/openhab-webui: Web UIs of openHAB which should give you a better idea of where the custom widget system fits.

Good morning. Having no answer yet, my view is to proceed with a small patch only for the floors:

I have choose the "All " text instead of Home to avoid optical confusion with Home on upper Menu (double texting). Yes, “All (floors)” = “Home”!
Now I’m working on the Home Section trying to eliminate the tab bar (ONLY for this section). This is a very early draft, don’t take it seriously, but pls investigate if we can eliminate/ hide the tab bar if the user selects the Home.

I’ll be back!

2 Likes

@Nic0205 Yesterday, while reading @hmerk’s simple question re floors i realized that i haven’t done all my exercise before release the drawings…
My apologies: I hate to put even a small brake on the enthusiasm that lives here!

2 Likes

@JustinG @hmerk I’m thinking as a daemon or as a dump? Now i really need help.

Tabs (footer) hosts the output for each scenario.

Note that i don’t have extensive knowledge /background in security systems. I created this with a very simple rule in my mind: Trigger (input) - Action (output).

Input:Item(s) state change
Output: Siren and or Notification(s) or an OH Rule.

ANY feedback is warm welcomed!

Sorry @Dimitris but I really don‘t get your question.
What code are you using ?

@hmerk None. It’s just a drawing.

Ok, but whats your question.
On first sight, What you have in mind seems to be achievable, but would need a bit more description besides the drawings…

Hi @hmerk
Here is a short description re layout / functionalities of the alarm page.

  1. Header
    Nav Bar: Home: Security
    • Optional: Underline color express the state (green: stay, red: armed)

  2. Middle part:
    • Stay or Armed. - User can select only one of them.
    • List of Items participated in the Alarm System (icon-Description-State)

  3. Footer (output)
    • Siren, Notifications, OH Rule (icon, Description, status)
    Light Grey: Not selected as output
    Colored: Selected as Output.


The Alarm system have two states:
Stay or Armed. - User can select only one of them (!)

For (ON-OFF) Items that participates in the system, the user will be able to choose what will happen in case the status one of them changes, either from OFF to ON, or from ON to OFF.

ON STAY MODE
Available Options:
Send a Notification (Siren and OH Rule is disabled for STAY mode).

ON ARMED MODE
Available Options:
Activate Siren AND OR
Send Notification OR
Run an OH Rule.

Exceptions:
When the user arms the system and not all Items report their state as OFF the system will arm activating selected outputs and excluding / ignoring (ON) Items and report them with a caution icon (next to ON-OFF ).
The Armed ICON changes to (!) together with the text (Armed with faults).
Optional: The OH can recheck with a timer (only through an OH Rule), and if the excluded items reports their state as OFF, then (magically) will be part of the alarm system.

So, the question is: Does this approach covers a simple alarm system?

Just a quick one :

No, it should have 3 states : Armed, Armed-Home and Away. This is what we see in several Alarm Systems. We could implement a config option to hide the Amed-Home Button.

Should be easy, as it is just a list of Items belonging to a group. Prerequisit will be to have the groups correctly set.

Siren, really ? What benefit do you see to show the srien state here ? You will hear it anyway…
OH-Rule - What do you expect ?

See above

No, it should be just a simple state option. Configuration of the Alarm System should be done in another place, best behind some kind of administration security.

If you mean changing from armed to disarmed should send a notification. This needs to be done with a rule, warching the state of the Security state item.

Everything that happens on an alarm should be built outside the UI in rules. So if motion is detected while security is armed, a rule should trigger the siren and send messages.

No, this should be user configurable. In my installation, arming the alarm is not possible if the terrace door or some windows are still open. openHAB will inform me about what needs to be closed.

Not from my perspective. The UI should be mostly a display for the alarm system, but configuration can be very individual and should be out of scope here. We do not want to restrict users.

1 Like

Hi @hmerk,

Did you made already any effort on this? Just asking if it is worth for me to dive deeper in or just to wait a little bit until you finish your first attempt.
:slight_smile:

Small effort, but nothing to show yet really. I am struggling with the default widgets for Equipment, as we sometimes need multiple items in one widget…

Made a step further, so should be able to post something until the weekend… Stay tuned :wink:

1 Like

pretty cool - I am so excited :sunglasses:

Hey @JustinG,

Did you miss me :innocent: ?

Is there a chance to order the items in the array? I see that they are orderer in alphabetical way. But I want to order them based on the semantic-model meta-data-tag “Default Widget Order Index”.

Is there a chance to do this?

This is the code part:

                      - component: oh-repeater
                        config:
                          fetchMetadata: semantics,uiSemantics
                          fragment: true
                          for: menuButtonFloor
                          itemTags: Floor
                          sourceType: itemsWithTags
                          map: loop.menuButtonFloor_source[(((vars.buttonIndexFloor || 0) % loop.menuButtonFloor_source.length) + loop.menuButtonFloor_source.length + loop.menuButtonFloor_idx) % loop.menuButtonFloor_source.length]
                        slots:
                          default:
                            - component: oh-button
                              config:
                                action: variable
                                actionVariable: floor
                                actionVariableValue: =loop.menuButtonFloor.label
                                text: =loop.menuButtonFloor.label
                                style:
                                  flex: 0 0 auto
                                  color: '=vars.floor ==loop.menuButtonFloor.label ? "black" : "#8C8C8C"'
                                  text-decoration: underline
                                  text-decoration-color: '=vars.floor ==loop.menuButtonFloor.label ? "#F8BB00" : "transparent"'
                                  text-underline-offset: 4px


1 Like

Hoo boy! This seems like an easy question at first, but it turns out it is actually quite a difficult question you’ve gotten into here.

There is no method built-in to the repeater to sort the results. It can be done in a limited fashion by using the map property. That’s basically what the current system does; it uses the map property to reorder the array so that the one that corresponds to the selected menu index is first with wrapping of the index back around to the beginning of the array where necessary.

Theoretically we could just apply the js array sort method in the map expression before we do our custom reordering, but it turns out it is not much help here. By default it sorts an array alphabetically and if you want it to do anything more complex then you have to supply it with a custom sorting function. The problem is that the js expression system that the widgets use doesn’t currently have the add-on that allows functions installed so we can’t use any advanced array sort.

The other option would be, because these items are all in a flex box, to use the order property to tell the flex box which order to place the child items in, but this would be applied after we’ve reordered the array using the map expression, so it would undo whatever re-ordering was done to make the menu function.

So, the long answer is “no, not really any way to get additional ordering of the item.” But…that’s not a very satisfactory answer. Let me think for a while about whether there is a solution to this problem.

1 Like

Guys, as promised, here is the next step from my side.
Prerequisits as before with some additions:
You need the widgets "Cell_Card_Light_2 and Cell_Shutter_Card_1 from the community (just for demo purpose, can be changed at a later step.
All light or rollershutter items need to be member of an according equipmment.

Limitations at the moment :

  • Only working if Thing from Bottom Menud is selected (visible: config needs to be extended)
  • I did not find a way to have more than one item (Switch/Dimmer e.g.) identified for one equipment to setup both for one widget (@JustinG Do you have an idea ?)
    Therefore this is only working for simple lightswitch and rollershutter atm.
  • Resetting the selections when you go back to the home menu missing.

Feel free to test and comment :wink:

uid: main_widget
tags: []
props:
  parameterGroups: []
timestamp: Sep 7, 2022, 4:12:19 PM
component: f7-block
config:
  style:
    display: flex
    flex-direction: column
    height: calc(100vh - var(--f7-toolbar-height) - var(--f7-safe-area-bottom) - var(--f7-navbar-height) - var(--f7-safe-area-top))
    justify-content: space-between
    margin: 0
    padding: 0
    widht: 100vh
slots:
  default:
    - component: f7-block
      config:
        style:
          flex: 0 0 auto
          overflow: scroll
      slots:
        default:
          - component: f7-segmented
            config:
              style:
                flex: 1 1 auto
            slots:
              default:
                - component: oh-repeater
                  config:
                    for: baseMenu
                    fragment: true
                    in:
                      - name: Home
                      - name: Floors
                      - name: Rooms
                    map: loop.baseMenu.name
                    sourceType: array
                  slots:
                    default:
                      - component: oh-button
                        config:
                          action: variable
                          actionVariable: selectSection
                          actionVariableValue: ="SECTION" + loop.baseMenu_idx
                          large: true
                          style:
                            color: '=vars.selectSection=="SECTION" + loop.baseMenu_idx ? "black" : "#8C8C8C"'
                            font-size: 30px
                            font-weight: 200
                            text-decoration: underline
                            text-decoration-color: '=vars.selectSection=="SECTION" + loop.baseMenu_idx ? "#F8BB00" : "transparent"'
                            text-underline-offset: 4px
                          text: =loop.baseMenu
          - component: f7-row
            config:
              style:
                align-items: center
                display: flex
                flex-direction: row
                flex-wrap: nowrap
                height: 2em
                justify-content: space-between
                width: 100%
              visible: =!!(vars.selectSection == "SECTION0")
            slots:
              default:
                - component: oh-button
                  config:
                    action: variable
                    actionVariable: buttonIndexHome
                    actionVariableValue: =(vars.buttonIndexHome || 0) - 1
                    iconF7: chevron_left
                    style:
                      color: "#F8BB00"
                      flex: 0 0 auto
                - component: f7-row
                  config:
                    style:
                      display: flex
                      justify-content: center
                      overflow: hidden
                  slots:
                    default:
                      - component: oh-repeater
                        config:
                          fetchMetadata: semantics,uiSemantics
                          for: menuButtonHome
                          fragment: true
                          itemTags: Home
                          map: loop.menuButtonHome_source[(((vars.buttonIndexHome || 0) % loop.menuButtonHome_source.length) + loop.menuButtonHome_source.length + loop.menuButtonHome_idx) % loop.menuButtonHome_source.length]
                          sourceType: itemsWithTags
                        slots:
                          default:
                            - component: oh-button
                              config:
                                action: variable
                                actionVariable: floor
                                actionVariableValue: =loop.menuButtonHome.name
                                style:
                                  color: '=vars.floor ==loop.menuButtonHome.name ? "black" : "#8C8C8C"'
                                  flex: 0 0 auto
                                  text-decoration: underline
                                  text-decoration-color: '=vars.floor ==loop.menuButtonHome.name ? "#F8BB00" : "transparent"'
                                  text-underline-offset: 4px
                                text: =loop.menuButtonHome.label
                - component: oh-button
                  config:
                    action: variable
                    actionVariable: buttonIndexHome
                    actionVariableValue: =(vars.buttonIndexHome || 0) + 1
                    iconF7: chevron_right
                    style:
                      color: "#F8BB00"
                      flex: 0 0 auto
          - component: f7-row
            config:
              style:
                align-items: center
                display: flex
                flex-direction: row
                flex-wrap: nowrap
                height: 2em
                justify-content: space-between
                width: 100%
              visible: =!!(vars.selectSection == "SECTION1")
            slots:
              default:
                - component: oh-button
                  config:
                    action: variable
                    actionVariable: buttonIndexFloor
                    actionVariableValue: =(vars.buttonIndexFloor || 0) - 1
                    iconF7: chevron_left
                    style:
                      color: "#F8BB00"
                      flex: 0 0 auto
                - component: f7-row
                  config:
                    style:
                      display: flex
                      justify-content: center
                      overflow: hidden
                  slots:
                    default:
                      - component: oh-repeater
                        config:
                          fetchMetadata: semantics,uiSemantics
                          for: menuButtonFloor
                          fragment: true
                          itemTags: Floor
                          map: loop.menuButtonFloor_source[(((vars.buttonIndexFloor || 0) % loop.menuButtonFloor_source.length) + loop.menuButtonFloor_source.length + loop.menuButtonFloor_idx) % loop.menuButtonFloor_source.length]
                          sourceType: itemsWithTags
                        slots:
                          default:
                            - component: oh-button
                              config:
                                action: variable
                                actionVariable: floor
                                actionVariableValue: =loop.menuButtonFloor.name
                                style:
                                  color: '=vars.floor ==loop.menuButtonFloor.name ? "black" : "#8C8C8C"'
                                  flex: 0 0 auto
                                  text-decoration: underline
                                  text-decoration-color: '=vars.floor ==loop.menuButtonFloor.name ? "#F8BB00" : "transparent"'
                                  text-underline-offset: 4px
                                text: =loop.menuButtonFloor.label
                - component: oh-button
                  config:
                    action: variable
                    actionVariable: buttonIndexFloor
                    actionVariableValue: =(vars.buttonIndexFloor || 0) + 1
                    iconF7: chevron_right
                    style:
                      color: "#F8BB00"
                      flex: 0 0 auto
          - component: f7-row
            config:
              style:
                align-items: center
                display: flex
                flex-direction: row
                flex-wrap: nowrap
                height: 2em
                justify-content: space-between
                width: 100%
              visible: =!!(vars.selectSection == "SECTION2")
            slots:
              default:
                - component: oh-button
                  config:
                    action: variable
                    actionVariable: buttonIndexRoom
                    actionVariableValue: =(vars.buttonIndexRoom || 0) - 1
                    iconF7: chevron_left
                    style:
                      color: "#F8BB00"
                      flex: 0 0 auto
                - component: f7-row
                  config:
                    style:
                      display: flex
                      justify-content: center
                      overflow: hidden
                  slots:
                    default:
                      - component: oh-repeater
                        config:
                          fetchMetadata: semantics,uiSemantics
                          for: menuButtonRoom
                          fragment: true
                          itemTags: Room
                          map: loop.menuButtonRoom_source[(((vars.buttonIndexRoom || 0) % loop.menuButtonRoom_source.length) + loop.menuButtonRoom_source.length + loop.menuButtonRoom_idx) % loop.menuButtonRoom_source.length]
                          sourceType: itemsWithTags
                        slots:
                          default:
                            - component: oh-button
                              config:
                                action: variable
                                actionVariable: floor
                                actionVariableValue: =loop.menuButtonRoom.name
                                style:
                                  color: '=vars.floor ==loop.menuButtonRoom.name ? "black" : "#8C8C8C"'
                                  flex: 0 0 auto
                                  text-decoration: underline
                                  text-decoration-color: '=vars.floor ==loop.menuButtonRoom.name ? "#F8BB00" : "transparent"'
                                  text-underline-offset: 4px
                                text: =loop.menuButtonRoom.label
                - component: oh-button
                  config:
                    action: variable
                    actionVariable: buttonIndexRoom
                    actionVariableValue: =(vars.buttonIndexRoom || 0) + 1
                    iconF7: chevron_right
                    style:
                      color: "#F8BB00"
                      flex: 0 0 auto
    - component: f7-block
      config:
        style:
          flex: 1 1 auto
          overflow: scroll
      slots:
        default:
          - component: oh-repeater
            config:
              fetchMetadata: semantics,metadata,listWidget
              filter: loop.equipmentItem.metadata.semantics.config.hasLocation == vars.floor
              for: equipmentItem
              itemTags: Blinds
              sourceType: itemsWithTags
            slots:
              default:
                - component: oh-repeater
                  config:
                    fetchMetadata: semantics,metadata,listWidget
                    for: shutterItem
                    groupItem: =loop.equipmentItem.name
                    sourceType: itemsInGroup
                    filter: '(loop.shutterItem.type=="Rollershutter") ? true : false'
                  slots:
                    default:
                      - component: widget:Cell_Shutter_Card_1
                        config:
                          visible: '=vars.selectThing=="Rollers" ? true : false'
                          header: =loop.shutterItem.label + " (" + loop.shutterItem.name + ")"
                          item: =loop.shutterItem.name
          - component: oh-repeater
            config:
              fetchMetadata: semantics,metadata,listWidget
              filter: loop.equipmentItem.metadata.semantics.config.hasLocation == vars.floor
              for: equipmentItem
              itemTags: Lightbulb
              sourceType: itemsWithTags
            slots:
              default:
                - component: oh-repeater
                  config:
                    fetchMetadata: semantics,metadata,listWidget
                    for: switchItem
                    groupItem: =loop.equipmentItem.name
                    sourceType: itemsInGroup
                    filter: '(loop.switchItem.type=="Switch") ? true : false'
                  slots:
                    default:
                      - component: widget:Cell_Light_Card_2
                        config:
                          visible: '=vars.selectThing=="Lights" ? true : false'
                          header: =loop.switchItem.label + " (" + loop.switchItem.name + ")"
                          item_schalter: =loop.switchItem.name
          - component: widget:Temp_Control
            config:
              style:
                flex: 1 1 auto
                overflow-y: auto
                position: relative
              visible: =!!(vars.selectSection + vars.selectDivision + vars.selectThing == "SECTION1DIV2Climate")
              widgettrend: HeizungWohnzimmer_Temperature
          - component: widget:Temp_Control
            config:
              style:
                flex: 1 1 auto
                overflow-y: auto
                position: relative
              visible: =!!(vars.selectSection + vars.selectDivision + vars.selectThing == "SECTION2DIV2Climate")
              widgettrend: HeizungWohnzimmer_Temperature
          - component: widget:Temp_Control
            config:
              style:
                flex: 1 1 auto
                overflow-y: auto
                position: relative
              visible: =!!(vars.selectSection + vars.selectDivision + vars.selectThing == "SECTION2DIV2Climate")
              widgettrend: HeizungWohnzimmer_Temperature
          - component: widget:Temp_Control
            config:
              style:
                flex: 1 1 auto
                overflow-y: auto
                position: relative
              visible: =!!(vars.selectSection + vars.selectDivision + vars.selectThing == "SECTION2DIV2Climate")
              widgettrend: HeizungWohnzimmer_Temperature
          - component: widget:Temp_Control
            config:
              style:
                flex: 1 1 auto
                overflow-y: auto
                position: relative
              visible: =!!(vars.selectSection + vars.selectDivision + vars.selectThing == "SECTION2DIV2Climate")
              widgettrend: HeizungWohnzimmer_Temperature
          - component: oh-cell
            config:
              icon: f7:lightbulb
              style:
                flex: 1 1 auto
                overflow-y: auto
                position: relative
              subtitle: only for testing
              title: I am a light card in the Bedroom
              visible: =!!(vars.selectSection + vars.selectDivision + vars.selectThing == "SECTION2DIV2Lights")
          - component: oh-cell
            config:
              icon: f7:lightbulb
              style:
                flex: 1 1 auto
                overflow-y: auto
                position: relative
              subtitle: only for testing
              title: I am a light card in the Home
              visible: =!!(vars.selectSection == "SECTION1")
          - component: Label
            config:
              style:
                flex: 1 1 auto
                overflow-y: auto
                position: relative
              text: =vars.selectSection + vars.floor
    - component: f7-block
      config:
        style:
          flex: 0 0 auto
          overflow: scroll
      slots:
        default:
          - component: f7-segmented
            config:
              class:
                - segmented-strong
              style:
                --f7-segmented-strong-between-buttons: 0px
                --f7-segmented-strong-bg-color: transparent
                --f7-segmented-strong-button-font-weight: 300
                --f7-segmented-strong-button-hover-bg-color: rgba(255, 255, 255, 0.15)
                --f7-segmented-strong-padding: 0px
            slots:
              default:
                - component: oh-button
                  config:
                    action: variable
                    actionVariable: selectThing
                    actionVariableValue: Lights
                    class:
                      - padding-top-half
                      - display-flex
                      - flex-direction-column
                    fill: '=vars.selectThing=="Lights" ? true : false'
                    icon-f7: lightbulb
                    iconColor: black
                    iconSize: 20
                    style:
                      --f7-button-bg-color: '=vars.selectThing=="Lights" ? "#F8BB00" : "transparent"'
                      --f7-button-border-radius: 15px
                      --f7-button-hover-bg-color: '=vars.selectThing=="Lights" ? "F8BB00" : "transparent"'
                      --f7-button-padding-horizontal: 0px
                      --f7-button-padding-vertical: 0px
                      --f7-button-text-color: '=vars.selectThing=="Lights" ? "#6A6A6A" : "#8C8C8C"'
                      font-size: 12px
                      height: auto
                    text: Lights
                - component: oh-button
                  config:
                    action: variable
                    actionVariable: selectThing
                    actionVariableValue: Climate
                    class:
                      - padding-top-half
                      - display-flex
                      - flex-direction-column
                    fill: '=vars.selectThing=="Climate" ? true : false'
                    icon-f7: snow
                    iconColor: black
                    iconSize: 20
                    style:
                      --f7-button-bg-color: '=vars.selectThing=="Climate" ? "#F8BB00" : "transparent"'
                      --f7-button-border-radius: 15px
                      --f7-button-hover-bg-color: '=vars.selectThing=="Climate" ? "F8BB00" : "transparent"'
                      --f7-button-padding-horizontal: 0px
                      --f7-button-padding-vertical: 0px
                      --f7-button-text-color: '=vars.selectThing=="Climate" ? "#6A6A6A" : "#8C8C8C"'
                      font-size: 12px
                      height: auto
                    text: Climate
                - component: oh-button
                  config:
                    action: variable
                    actionVariable: selectThing
                    actionVariableValue: Rollers
                    class:
                      - padding-top-half
                      - display-flex
                      - flex-direction-column
                    fill: '=vars.selectThing=="Rollers" ? true : false'
                    icon-f7: archivebox
                    iconColor: black
                    iconSize: 20
                    style:
                      --f7-button-bg-color: '=vars.selectThing=="Rollers" ? "#F8BB00" : "transparent"'
                      --f7-button-border-radius: 15px
                      --f7-button-hover-bg-color: '=vars.selectThing=="Rollers" ? "F8BB00" : "transparent"'
                      --f7-button-padding-horizontal: 0px
                      --f7-button-padding-vertical: 0px
                      --f7-button-text-color: '=vars.selectThing=="Rollers" ? "#6A6A6A" : "#8C8C8C"'
                      font-size: 12px
                      height: auto
                    text: Rollers
                - component: oh-button
                  config:
                    action: variable
                    actionVariable: selectThing
                    actionVariableValue: Security
                    class:
                      - padding-top-half
                      - display-flex
                      - flex-direction-column
                    fill: '=vars.selectThing=="Security" ? true : false'
                    icon-f7: camera
                    iconColor: black
                    iconSize: 20
                    style:
                      --f7-button-bg-color: '=vars.selectThing=="Security" ? "#F8BB00" : "transparent"'
                      --f7-button-border-radius: 15px
                      --f7-button-hover-bg-color: '=vars.selectThing=="Security" ? "F8BB00" : "transparent"'
                      --f7-button-padding-horizontal: 0px
                      --f7-button-padding-vertical: 0px
                      --f7-button-text-color: '=vars.selectThing=="Security" ? "#6A6A6A" : "#8C8C8C"'
                      font-size: 12px
                      height: auto
                    text: Security

I’ve not found a good way to do this yet. If you take a look at the code for my favorites bar, you’ll see that my solution was somewhat brute force: just create a copy of every widget with the item, but then hide the all the widgets where the item type doesn’t match. It works in that situation because there are usually few enough widgets. I don’t know if it becomes a performance issue in with something like this.