Hey there. Thanks for your kind words. Nothing is finished yet- at the end I will upload all the icons, that’s for sure. Hint: if you can “edit” the PDF, you can grab the temporary/ sample icons right now.
Cheers
This is precisely why trying to nest all these toolbars is going to drive you crazy. If I were building something like this from scratch using the OH widget system, I would avoid using the tabbars. If you build your own links instead or use something like the segmented buttons you’ll have 1000% easier time with styling, positioning, and keeping track.
As for what’s going on, I don’t see that there are any missing tab sets. There are 4 tab toolbars specified in the code. One at the top with “Home - Floors - Rooms” and then one for each of those three content pages (nothing shows on the Home tab at the moment because there are no tab links listed in the toolbar).
Here’s an example of using segmented button to achieve something much more elegant than the tabs:
uid: demo:segmented
props:
parameterGroups: []
parameters: []
tags: []
component: f7-page
config:
slots:
default:
- component: f7-page-content
config:
style:
height: 100%
slots:
default:
- component: f7-segmented
config:
slots:
default:
- component: oh-button
config:
action: variable
actionVariableValue: SECTION1
actionVariable: selectSection
text: One
large: true
- component: oh-button
config:
action: variable
actionVariableValue: SECTION2
actionVariable: selectSection
text: Two
large: true
- component: oh-button
config:
action: variable
actionVariableValue: SECTION3
actionVariable: selectSection
text: Three
large: true
- component: f7-segmented
config:
visible: =!!(vars.selectSection == "SECTION2")
slots:
default:
- component: oh-button
config:
action: variable
actionVariableValue: DIV1
actionVariable: selectDivision
text: Division One
- component: oh-button
config:
action: variable
actionVariableValue: DIV2
actionVariable: selectDivision
text: Division Two
- component: oh-button
config:
action: variable
actionVariableValue: DIV3
actionVariable: selectDivision
text: Division Three
- component: f7-segmented
config:
visible: =!!(vars.selectSection == "SECTION3")
slots:
default:
- component: oh-button
config:
action: variable
actionVariableValue: OTHER1
actionVariable: selectOther
text: Other One
- component: oh-button
config:
action: variable
actionVariableValue: OTHER2
actionVariable: selectOther
text: Other Two
- component: oh-button
config:
action: variable
actionVariableValue: OTHER3
actionVariable: selectOther
text: Other Three
- component: oh-toggle-card
- component: f7-segmented
config:
slots:
default:
- component: oh-button
config:
action: variable
actionVariableValue: THING1
actionVariable: selectThing
text: Thing One
- component: oh-button
config:
action: variable
actionVariableValue: THING2
actionVariable: selectThing
text: Thing Two
- component: oh-button
config:
action: variable
actionVariableValue: THING3
actionVariable: selectThing
text: Thing Three
Hello @JustinG @ysc @Nic0205 , Hello Community,
Trying to understand the OH principles re pages, structure, design and tools I’m wondering which is the best way to implement a new UI without spending hours to hardcore the platform while giving to end users the ability to configure and install it in a simple way. Yes, i know nothing about coding .
I understand that we can create only pre-structured OH pages, like a tabbed page. What i missed from that type of page- in order to implement the mentioned UI- is the top menu / selector.
The idea is to investigate (ask OH Community?) if there is a way to create / add a new OH page type. - pls check the attachment.
Maybe what I’m thinking is not a solution, but I’m sure that we have to conform with what OH give us and support natively.
@all experienced members Your direction and advice in this critical phase is extremely welcomed!
Create tabbed page with menu.pdf (206.4 KB)
Cheers
Hi @Dimitris
I hope one of the pros here can help us with extending the OH Standards.
Until that I will try to follow Justins approach.
I made some progress and will post the actual increment later.
In combination with the MVP approach we can together see how it grows and improve the usability (for administration and using) .
Cool! Looks amazing.
One question to this approach. I tried it and in a “normal” layout-page the widget is not shown.
Do i have to use fixed-layout for it?
I just want to address this statement in case there is some confusion on this point. I’m more than a novice when it comes to MainUI widgets so I can’t help with any of the rest.
In OH some common terms have a specific technical meaning: Thing, Channel, Link, Item, Location (has two different meanings depending on context), Equipment, Point, Property, etc. I tend to capitalize these words when I’m using the technical OH meanings just to make it clear that when I use “Thing” I mean an OH Thing, not the generic meaning of the word “thing”.
So here, assuming you mean the openHAB definition for Equipments (a Group with a specific tag which groups one or more Items that are closely related to each other such as being part of the same device) then the statement above is not accurate. A widget controls and monitors Items. Those Items may or may not be a member of an Equipment.
It makes a lot of sense to create a single widget for each Equipment but it is not required. You could present a single widget with lots of different Equipment. You may not even use the semantic model so you might not even have Equipment.
Thank you Sir.
Very clear and very straightforward.
Oh, sorry. It probably is shown it’s just compressed into a small space with a scroll-bar and so not easily visible unless you hunt around for it. That’s just because when I was typing up the quick example I wasn’t intending for it to be put on a page and I used f7-page
and f7-page-content
base elements which don’t always play well when added to other pages. If you just replace both of those with one f7-block
it should show up just fine.
uid: demo:segmented
props:
parameterGroups: []
parameters: []
tags: []
component: f7-block
config:
slots:
default:
- component: f7-segmented
config:
slots:
default:
- component: oh-button
config:
action: variable
actionVariableValue: SECTION1
...
This is always the trade-off in developing any system (computer or otherwise). If the system itself is simple, then you probably don’t need to create something to help users interact with it. If the system is complex then that complexity is always going to have to be addressed at some point in the pipeline to users. If you want the user interface to be simple and easy to use then the interface itself has to address that complexity “under the hood” so that the user doesn’t have to, but that is going to require a lot of work on the part of the developer (and also make the user interface more restrictive as many of the complexities have to be addressed by making assumptions that the user then doesn’t get control over).
In this case you could be talking about several different layers of options for “implement a new UI”.
- You could create a whole new add-on for OH that replaces MainUI or at least works along side it (habPanel would be one such example that still has a strong user base). That would be the most most complex coding but allow you to get your UI to the point were the user interaction could be streamlined and optimized for the new UI.
- You could create some additional pages for the MainUI and submit those additions as a pull request to the MainUI repository. Your pages would still require a heavy amount of coding and probably use the same structure that the current pages use (f7 framework using vue) which would include the styling and make it possible for much of the page to be automatically produced (similar to the overview page and it’s semantic tabs). This but would essentially require the same level of user setup and effort as any other MainUI page.
- You could simply release all the different components of the UI as widgets and example page configurations for users to install and setup as they see fit. This is the least coding for you (but, perhaps requiring the most help-doc generation) and the most labor intensive for the users.
My guess is that you are leaning toward the 3rd option. The work that you want to put in to this is creating pages and widgets with your distinct styling (something that many users don’t want to learn to do). But you’re then going to have to leave it up to the user to assemble and configure those different widgets in the way that fits their system the best.
In practice, I think, what this means is that you’ll want to produce pages that contain the essential parts of the UI (e.g., the tab systems) and leaves places where the users can insert widgets. There’s no mechanism at the moment for sharing complete pages other than just posting the yaml code for that page, but that’s fairly easy. Then you’ll want to add all the special widgets to the widget marketplace so that users can download the ones they need and add them to the page(s) where appropriate.
Hey,
here the promised increment.
By clicking on Rooms / Living Room / Lights a test widget occurs.
Please give it a short try.
@JustinG : I replaced the f7-page and f7-page-content with f7-block.
Looks much better - but for now it does not scale correct for mobile device (iPhone).
Could you please have a look?
uid: test_ui
tags: []
props:
parameters:
- description: The label for the widget
label: Title
name: title
required: false
type: TEXT
- context: item
description: The light switch Item
label: Item
name: item
required: false
type: TEXT
parameterGroups: []
timestamp: Aug 16, 2022, 9:58:16 PM
component: f7-block
config:
defineVars:
selectSection: 0
selectDivision: 0
selectThing: Lights
SecDiv: =vars.selectSection & vars.selectDivision
slots:
default:
- component: f7-block
config:
style:
height: 100%
min-width: 400px
slots:
default:
- component: f7-segmented
slots:
default:
- component: oh-button
config:
action: variable
actionVariableValue: SECTION1
actionVariable: selectSection
text: Home
large: true
style:
font-weight: 200
font-size: 30px
text-decoration: underline
text-decoration-color: '=vars.selectSection=="SECTION1" ? "#F8BB00" : "transparent"'
text-underline-offset: 4px
color: '=vars.selectSection=="SECTION1" ? "black" : "#8C8C8C"'
- component: oh-button
config:
action: variable
actionVariableValue: SECTION2
actionVariable: selectSection
text: Rooms
large: true
style:
font-weight: 200
font-size: 30px
color: '=vars.selectSection=="SECTION2" ? "black" : "#8C8C8C"'
text-decoration: underline
text-decoration-color: '=vars.selectSection=="SECTION2" ? "#F8BB00" : "transparent"'
text-underline-offset: 4px
- component: oh-button
config:
action: variable
actionVariableValue: SECTION3
actionVariable: selectSection
text: Floors
large: true
style:
font-weight: 200
font-size: 30px
color: "#8C8C8C"
- component: f7-segmented
config:
visible: =!!(vars.selectSection == "SECTION2")
slots:
default:
- component: oh-button
config:
action: variable
actionVariableValue: DIV1
actionVariable: selectDivision
text: Living Room
style:
color: '=vars.selectDivision=="DIV1" ? "black" : "#8C8C8C"'
text-decoration: underline
text-underline-offset: 4px
text-decoration-color: '=vars.selectSection + vars.selectDivision == "SECTION2DIV1" ? "#F8BB00" : "transparent"'
- component: oh-button
config:
action: variable
actionVariableValue: DIV2
actionVariable: selectDivision
text: Bedroom
style:
color: '=vars.selectDivision=="DIV2" ? "black" : "#8C8C8C"'
text-decoration: underline
text-underline-offset: 3px
text-decoration-color: '=vars.selectSection + vars.selectDivision == "SECTION2DIV2" ? "#F8BB00" : "transparent"'
- component: oh-button
config:
action: variable
actionVariableValue: DIV3
actionVariable: selectDivision
text: Kitchen
style:
color: '=vars.selectDivision=="DIV3" ? "black" : "#8C8C8C"'
text-decoration: underline
text-underline-offset: 3px
text-decoration-color: '=vars.selectSection + vars.selectDivision == "SECTION2DIV3" ? "#F8BB00" : "transparent"'
- component: f7-segmented
config:
visible: =!!(vars.selectSection == "SECTION3")
slots:
default:
- component: oh-button
config:
action: variable
actionVariableValue: OTHER1
actionVariable: selectOther
text: Other One
- component: oh-button
config:
action: variable
actionVariableValue: OTHER2
actionVariable: selectOther
text: Other Two
- component: oh-button
config:
action: variable
actionVariableValue: OTHER3
actionVariable: selectOther
text: Other Three
- component: oh-cell
config:
visible: =!!(vars.selectSection + vars.selectDivision + vars.selectThing == "SECTION2DIV1Lights")
icon: f7:lightbulb
title: I am a light card in the Living Room
subtitle: only for testing
- component: oh-cell
config:
visible: =!!(vars.selectSection + vars.selectDivision + vars.selectThing == "SECTION2DIV2Lights")
icon: f7:lightbulb
title: I am a light card in the Bedroom
subtitle: only for testing
- component: Label
config:
text: =vars.selectSection + vars.selectDivision
- component: Label
config:
text: =vars.SecDiv
- component: f7-segmented
slots:
default:
- component: oh-link
config:
action: variable
actionVariableValue: Lights
actionVariable: selectThing
text: Lights
icon: f7:lightbulb
- component: oh-button
config:
action: variable
actionVariableValue: THING2
actionVariable: selectThing
text: Climate
- component: oh-button
config:
action: variable
actionVariableValue: THING3
actionVariable: selectThing
text: Rollers
- component: oh-button
config:
action: variable
actionVariableValue: THING3
actionVariable: selectThing
text: Security
I see a couple of possible issues. You have a min-width set for one of the f7-blocks of 400px. This is possibly greater than the width of the mobile device screen that you are using so the widget is overflowing off the side. However, if you take away that minimum width, you’re going to start to loose the text in the segmental buttons. So the main issue is that you’re now going to have to deal with scaling text. In my limited experience, dynamically scaling text without using javascript is very difficult (nigh impossible, as far as I can tell). You’re best hope is to find a fixed text scale that works well on the mobile screens and use the device
object available in the widget expressions to determine what kind of screen is in use.
For example:
font-size: =(device.desktop)?('30px'):('18px')
As this gets more complex, I think you’re also going to want to use css variables throughout to simplfy. So in the top level f7-block
you’ll want a style
section where you define the variables once:
component: f7-block
config:
style:
--ui-widget-section-font-size: =(device.desktop)?('30px'):('18px')
And then in the later style settings you can just reference that variable:
font-size: var(--ui-widget-section-font-size)
Some other quick notes:
The Lights
button you added is an oh-link
not an oh-button
which is why it is not properly lined up with the other pieces of the segmented button.
The defineVars
section in the top block doesn’t do anything. Unfortunately, at the moment, I don’t believe there’s a way to initialize variables within a widget. You can do so for page level variables that are shared between widgets, but not for a single widget as far as I am aware.
@JustinG I have read your reply 4 times so far. No words! Totally agree that option 3 is the only way -at least for now. Thanks for your time, you helped me a lot.
@Nic0205 Now i realize that i have to present a design that works in OH life. For this reason, i have start to enrich the idea with more info and documentation.
I’m sharing the latest version.
Thank you both!
HOMEUIv5.pdf (168.8 KB)
Dear friend, can you please take a look and give some hints about widgets optimization? A pair of widgets, smaller parent and bigger child called from parent:
uid: Cell_Temp_Card_3
tags:
- temperature
- humidity
- co
- motion
props:
parameters:
- description: Title on top of the card
label: Title
name: title
required: false
type: TEXT
- description: Icon on top of the card (only f7 icons (without f7:))
label: Icon
name: icon
required: false
type: TEXT
- description: Background image name
label: Background image
name: bg_image_url
required: false
type: TEXT
- description: in rgba() or HEX or empty
label: Background Color
name: bgcolor
required: false
type: TEXT
- context: item
label: Temperature
name: temp_item
required: false
type: TEXT
- context: item
label: Target Temperature
name: temperatureset_item
required: false
type: TEXT
- context: item
label: Lightlevel
name: lightlevel_item
required: false
type: TEXT
- context: item
label: Humidity
name: humidity_item
required: false
type: TEXT
- context: item
label: Target Humidity
name: humidityset_item
required: false
type: TEXT
- context: item
label: CO
name: CO_item
required: false
type: TEXT
- context: item
label: Motion item
name: motion_item
required: false
type: TEXT
- context: item
label: Lights group
name: light
required: false
type: TEXT
- context: item
label: Door item
name: door_item
required: false
type: TEXT
- context: item
label: Lock item
name: lock_item
required: false
type: TEXT
- context: item
label: Window item
name: window_item
required: false
type: TEXT
- context: item
label: Leakage item
name: leakage_item
required: false
type: TEXT
- context: item
label: Smoke item
name: smoke_item
required: false
type: TEXT
- context: item
label: Media Title
name: mediaTitle
required: false
type: TEXT
- context: item
label: Power Item
name: power
required: false
type: TEXT
- context: item
label: Channel
name: channel
required: false
type: TEXT
- context: item
label: Key Code
name: inputAction
required: false
type: TEXT
- context: item
label: Volume Control Item
name: volumeControlItem
required: false
type: TEXT
- context: item
label: Source Name
name: sourceName
required: false
type: TEXT
parameterGroups: []
timestamp: Aug 17, 2022, 2:57:35 PM
component: f7-card
config:
style:
background: transparent
background-brightness: 60%
background-color: "=props.bgcolor ? props.bgcolor : ''"
background-image: "=props.bg_image_url ? 'url(/static/' + (props.bg_image_url) + ')' : ''"
background-position: center
background-repeat: no-repeat
background-size: cover
border-radius: var(--f7-card-expandable-border-radius)
box-shadow: 5px 5px 10px 1px var(--f7-bars-bg-color)
color: var(--f7-text-color)
font-size: medium
font-weight: 500
height: 150px
margin: 5
noShadow: false
padding: 0
text-shadow: 1px 0px 2px var(--f7-bars-bg-color), -1px 0px 2px var(--f7-bars-bg-color), 0px 0px 2px var(--f7-bars-bg-color), 0px 0px 3px var(--f7-bars-bg-color)
slots:
default:
- component: f7-card-header
config:
style:
justify-content: center
margin: 0
min-height: 30px
padding: 0
slots:
default:
- component: f7-icon
config:
f7: =props.icon
size: 20
visible: "=props.icon ? true : false"
- component: Label
config:
style:
font-size: 18px
font-weight: bold
margin-left: 5px
text: "=props.title ? props.title : ''"
- component: f7-card-content
config:
style:
align-items: flex-start
display: flex
justify-content: space-between
margin: 7px
padding: 0
slots:
default:
- component: f7-col
config: {}
slots:
default:
- component: f7-row
config:
style:
flex-wrap: nowrap
justify-content: flex-start
visible: "=props.temp_item ? true : false"
slots:
default:
- component: oh-icon
config:
icon: '=(Number.parseFloat(items[props.temp_item].state.split(" ")[0]) > 30) ? "my_temp_02" : (Number.parseFloat(items[props.temp_item].state.split(" ")[0]) < 0) ? "my_temp_06" : (Number.parseFloat(items[props.temp_item].state.split(" ")[0]) < 10) ? "my_temp_05" : "my_temp_10"'
width: 23
- component: Label
config:
style:
color: '=(Number.parseFloat(items[props.temp_item].state.split(" ")[0]) > 30) ? "rgb(242,75,36)" : (Number.parseFloat(items[props.temp_item].state.split(" ")[0]) < 10) ? "rgba(32, 185, 256,0.8)" : "var(--f7-text-color)"'
margin-left: 5px
text: =(Number.parseFloat(items[props.temp_item].state.split(".")[0]))+" °C"
- component: f7-row
config:
style:
flex-wrap: nowrap
justify-content: flex-start
z-index: 2
visible: "=props.humidity_item ? true : false"
slots:
default:
- component: oh-icon
config:
icon: '=(Number.parseFloat(items[props.humidity_item].state.split(" ")[0]) < 35) ? "hum-14" : (Number.parseFloat(items[props.humidity_item].state.split(" ")[0]) > 75) ? "hum-70" : "hum-42"'
width: 23
- component: Label
config:
style:
color: '=(Number.parseFloat(items[props.humidity_item].state.split(" ")[0]) < 35) ? "rgb(242,75,36)" : (Number.parseFloat(items[props.humidity_item].state.split(" ")[0]) > 75) ? "green" : "var(--f7-text-color)"'
margin-left: 5px
text: =(Number.parseFloat(items[props.humidity_item].state.split(".")[0])) +" %"
- component: f7-row
config:
style:
flex-wrap: nowrap
justify-content: flex-start
z-index: 2
visible: "=props.lightlevel_item ? true : false"
slots:
default:
- component: oh-icon
config:
icon: sun
width: 23
- component: Label
config:
style:
color: "=(items[props.lightlevel_item].state < 1) ? 'gray' : 'var(--f7-text-color)'"
margin-left: 5px
text: =(Number.parseFloat(items[props.lightlevel_item].state.split(".")[0])) +" lux"
- component: f7-row
config:
style:
flex-wrap: nowrap
z-index: 2
visible: "=props.CO_item ? true : false"
slots:
default:
- component: oh-icon
config:
icon: carbondioxide
width: 23
- component: Label
config:
style:
color: '=(Number.parseFloat(items[props.CO_item].state.split(" ")[0]) > 1500) ? "red" : (Number.parseFloat(items[props.CO_item].state.split(" ")[0]) > 1000) ? "yellow" : "var(--f7-text-color)"'
margin-left: 5px
white-space: nowrap
text: =(Number.parseFloat(items[props.CO_item].state.split(".")[0])) +" ppm"
- component: f7-col
config:
style:
align-items: center
display: flex
flex-direction: column
slots:
default:
- component: f7-col
config:
style:
z-index: 1
visible: "=props.motion_item || props.door_item || props.lock_item || props.window_item ? true : false"
slots:
default:
- component: oh-icon
config:
icon: "=(items[props.motion_item].state === 'ON') ? 'mymotion-on' : 'mymotion-off'"
width: 25
visible: "=props.motion_item ? true : false"
- component: oh-icon
config:
icon: "=(items[props.door_item].state === 'ON') ? 'door-closed' : 'door-open'"
width: 25
visible: "=props.door_item ? true : false"
- component: oh-icon
config:
icon: "=(items[props.lock_item].state === 'ON') ? 'lock' : 'lock-open'"
width: 25
visible: "=props.lock_item ? true : false"
- component: oh-icon
config:
icon: "=(items[props.window_item].state === 'ON') ? 'window' : 'window-open'"
width: 25
visible: "=props.window_item ? true : false"
- component: oh-button
config:
action: group
actionGroupPopupItem: =props.light
style:
padding: 0
z-index: 99
visible: "=props.light ? true : false"
slots:
default:
- component: oh-repeater
config:
for: item
fragment: false
groupItem: =props.light
sourceType: itemsInGroup
slots:
default:
- component: oh-icon
config:
icon: light
width: 30
state: =items[loop.item.name].state
item: =loop.item.name
- component: oh-link
config:
action: popup
actionModal: widget:Cell_Temp_Card_Expanded
actionModalConfig:
title: =props.title
icon: =props.icon
bg_image_url: =props.bg_image_url
bgcolor: =props.bgcolor
temp_item: =props.temp_item
lightlevel_item: =props.lightlevel_item
humidity_item: =props.humidity_item
CO_item: =props.CO_item
motion_item: =props.motion_item
light: =props.light
door_item: =props.door_item
lock_item: =props.lock_item
window_item: =props.window_item
leakage_item: =props.leakage_item
smoke_item: =props.smoke_item
temperatureset_item: =props.temperatureset_item
humidityset_item: =props.humidityset_item
mediaTitle: =props.
power: =props.power
channel: =props.channel
inputAction: =props.inputAction
volumeControlItem: =props.volumeControlItem
sourceName: =props.sourceName
style:
height: 137px
left: 0px
margin: 0px
padding: 0
position: absolute
top: -30px
width: 100%
z-index: 3
cell_temp_card_extended.txt (58.3 KB)
Hey @Dimitris ,
thanks for sharing the stylguide. Could you enrich it with example sizes for example for font-size, height of the Header, Footer, etc…
For the meanwhile:
Next Little step - give it a try;-)
uid: test_ui
tags: []
props:
parameters:
- description: The label for the widget
label: Title
name: title
required: false
type: TEXT
- context: item
description: The light switch Item
label: Item
name: item
required: false
type: TEXT
parameterGroups: []
timestamp: Aug 17, 2022, 6:13:01 PM
component: f7-block
config: {}
slots:
default:
- component: f7-block
config:
style:
height: 100%
slots:
default:
- component: f7-segmented
slots:
default:
- component: oh-button
config:
action: variable
actionVariableValue: SECTION1
actionVariable: selectSection
text: Home
large: true
style:
font-weight: 200
font-size: 30px
text-decoration: underline
text-decoration-color: '=vars.selectSection=="SECTION1" ? "#F8BB00" : "transparent"'
text-underline-offset: 4px
color: '=vars.selectSection=="SECTION1" ? "black" : "#8C8C8C"'
- component: oh-button
config:
action: variable
actionVariableValue: SECTION2
actionVariable: selectSection
text: Rooms
large: true
style:
font-weight: 200
font-size: 30px
color: '=vars.selectSection=="SECTION2" ? "black" : "#8C8C8C"'
text-decoration: underline
text-decoration-color: '=vars.selectSection=="SECTION2" ? "#F8BB00" : "transparent"'
text-underline-offset: 4px
- component: oh-button
config:
action: variable
actionVariableValue: SECTION3
actionVariable: selectSection
text: Floors
large: true
style:
font-weight: 200
font-size: 30px
color: "#8C8C8C"
- component: f7-segmented
config:
visible: =!!(vars.selectSection == "SECTION2")
slots:
default:
- component: oh-button
config:
action: variable
actionVariableValue: DIV1
actionVariable: selectDivision
text: Living Room
style:
color: '=vars.selectDivision=="DIV1" ? "black" : "#8C8C8C"'
text-decoration: underline
text-underline-offset: 4px
text-decoration-color: '=vars.selectSection + vars.selectDivision == "SECTION2DIV1" ? "#F8BB00" : "transparent"'
- component: oh-button
config:
action: variable
actionVariableValue: DIV2
actionVariable: selectDivision
text: Bedroom
style:
color: '=vars.selectDivision=="DIV2" ? "black" : "#8C8C8C"'
text-decoration: underline
text-underline-offset: 3px
text-decoration-color: '=vars.selectSection + vars.selectDivision == "SECTION2DIV2" ? "#F8BB00" : "transparent"'
- component: oh-button
config:
action: variable
actionVariableValue: DIV3
actionVariable: selectDivision
text: Kitchen
style:
color: '=vars.selectDivision=="DIV3" ? "black" : "#8C8C8C"'
text-decoration: underline
text-underline-offset: 3px
text-decoration-color: '=vars.selectSection + vars.selectDivision == "SECTION2DIV3" ? "#F8BB00" : "transparent"'
- component: f7-segmented
config:
visible: =!!(vars.selectSection == "SECTION3")
slots:
default:
- component: oh-button
config:
action: variable
actionVariableValue: OTHER1
actionVariable: selectOther
text: Other One
- component: oh-button
config:
action: variable
actionVariableValue: OTHER2
actionVariable: selectOther
text: Other Two
- component: oh-button
config:
action: variable
actionVariableValue: OTHER3
actionVariable: selectOther
text: Other Three
- component: oh-cell
config:
visible: =!!(vars.selectSection + vars.selectDivision + vars.selectThing == "SECTION2DIV1Lights")
icon: f7:lightbulb
title: I am a light card in the Living Room
subtitle: only for testing
- component: oh-cell
config:
visible: =!!(vars.selectSection + vars.selectDivision + vars.selectThing == "SECTION2DIV2Lights")
icon: f7:lightbulb
title: I am a light card in the Bedroom
subtitle: only for testing
- component: Label
config:
text: =vars.selectSection + vars.selectDivision
- component: Label
config:
text: =vars.SecDiv
- component: f7-segmented
config:
class:
- segmented-strong
style:
--f7-segmented-strong-padding: 0px
--f7-segmented-strong-between-buttons: 0px
--f7-segmented-strong-button-font-weight: 300
--f7-segmented-strong-bg-color: transparent
g--f7-segmented-strong-button-hover-bg-color: rgba(255, 255, 255, 0.15)
slots:
default:
- component: oh-button
config:
class:
- padding-top-half
- display-flex
- flex-direction-column
text: Lights
action: variable
actionVariable: selectThing
actionVariableValue: Lights
icon-f7: lightbulb
iconSize: 20
iconColor: black
fill: '=vars.selectThing=="Lights" ? true : false'
style:
--f7-button-text-color: '=vars.selectThing=="Lights" ? "#6A6A6A" : "#8C8C8C"'
--f7-button-border-radius: 15px
--f7-button-padding-vertical: 0px
--f7-button-padding-horizontal: 0px
--f7-button-bg-color: '=vars.selectThing=="Lights" ? "#F8BB00" : "transparent"'
--f7-button-hover-bg-color: '=vars.selectThing=="Lights" ? "F8BB00" : "transparent"'
height: auto
font-size: 12px
- component: oh-button
config:
class:
- padding-top-half
- display-flex
- flex-direction-column
text: Climate
action: variable
actionVariable: selectThing
actionVariableValue: Climate
icon-f7: snow
iconSize: 20
iconColor: black
fill: '=vars.selectThing=="Climate" ? true : false'
style:
--f7-button-text-color: '=vars.selectThing=="Climate" ? "#6A6A6A" : "#8C8C8C"'
--f7-button-border-radius: 15px
--f7-button-padding-vertical: 0px
--f7-button-padding-horizontal: 0px
--f7-button-bg-color: '=vars.selectThing=="Climate" ? "#F8BB00" : "transparent"'
--f7-button-hover-bg-color: '=vars.selectThing=="Climate" ? "F8BB00" : "transparent"'
height: auto
font-size: 12px
- component: oh-button
config:
class:
- padding-top-half
- display-flex
- flex-direction-column
text: Rollers
action: variable
actionVariable: selectThing
actionVariableValue: Rollers
icon-f7: archivebox
iconSize: 20
iconColor: black
fill: '=vars.selectThing=="Rollers" ? true : false'
style:
--f7-button-text-color: '=vars.selectThing=="Rollers" ? "#6A6A6A" : "#8C8C8C"'
--f7-button-border-radius: 15px
--f7-button-padding-vertical: 0px
--f7-button-padding-horizontal: 0px
--f7-button-bg-color: '=vars.selectThing=="Rollers" ? "#F8BB00" : "transparent"'
--f7-button-hover-bg-color: '=vars.selectThing=="Rollers" ? "F8BB00" : "transparent"'
height: auto
font-size: 12px
- component: oh-button
config:
class:
- padding-top-half
- display-flex
- flex-direction-column
text: Security
action: variable
actionVariable: selectThing
actionVariableValue: Security
icon-f7: camera
iconSize: 20
iconColor: black
fill: '=vars.selectThing=="Security" ? true : false'
style:
--f7-button-text-color: '=vars.selectThing=="Security" ? "#6A6A6A" : "#8C8C8C"'
--f7-button-border-radius: 15px
--f7-button-padding-vertical: 0px
--f7-button-padding-horizontal: 0px
--f7-button-bg-color: '=vars.selectThing=="Security" ? "#F8BB00" : "transparent"'
--f7-button-hover-bg-color: '=vars.selectThing=="Security" ? "F8BB00" : "transparent"'
height: auto
font-size: 12px
Beware of browser’s dark theme =) Black on black isn’t good to read. Try to use something like color: var(–f7-text-color), var(–f7-bars-bg-color)- they will change depending of theme used. The vars and their colors are seen in browser’s page inspector.
The only thing, in my experience, that noticeably impacts widget performance is too many oh-repeater
components that require an API call, such as the itemsWithTags
etc. You only have one of those and it’s not really a very complex one so there’s not much performance to optimize.
There are a few places where, if you are interested, the code can be simplified a little:
You have a few places where something like this happens:
text: =(Number.parseFloat(items[props.temp_item].state.split(".")[0]))+" °C"
There are lots of places where you properly use the Number.parseFloat
method to change a string into a number for numerical comparison. Here, however, it is unnecessary. For the text
property you are just creating a string so you don’t need to change the state from a string to a number first, just leave it as a string:
text: =items[props.temp_item].state.split(".")[0] + " °C"
Alternately, if you are splitting the state at the decimal point because you just want an integer value, you can use the Number
object, but just use parseInt
and then you don’t need the split:
text: =Number.parseInt(items[props.temp_item].state)+" °C"
You have several instance where you test a variable (or one instance where you test 4 or 5 variables) with a ternary expression such as:
visible: "=props.humidity_item ? true : false"
This is redundant. The ternary expression tests the first part for true or false and provides the first listed result in case the of true and the second in case of false. So what this says is “If this is true then true, but if it’s false, then false.” So, for properties such as the visible
property that take a boolean value, you can just use the initial expression:
visible: =props.humidity_item
Any real value (except false
and 0
) is true, and undefined values are false. So if the variable exists and has a real value this will be true and if the variable is undefined, then it will be false.
Lastly, perhaps the biggest increase in clarity can come where you have all the possible icon definitions such as here:
icon: '=(Number.parseFloat(items[props.temp_item].state.split(" ")[0]) > 30) ? "my_temp_02" : (Number.parseFloat(items[props.temp_item].state.split(" ")[0]) < 0) ? "my_temp_06" : (Number.parseFloat(items[props.temp_item].state.split(" ")[0]) < 10) ? "my_temp_05" : "my_temp_10"'
There are instructions in the docs for creating icons that follow the OH system for dynamic icons. If you work out how to change the names of your icons to follow that system, then you will probably just be able to replace that whole expression with the base name of the icon.
This looks fantastic! Is this possible in MainUI If so, would you be so kind as to share? Mine looks terrible
Hey @dastrix80,
we try put the things together - but we are still at a starting point.
A first technical draft - based on @Dimitris Design-draft is here:
https://community.openhab.org/t/oh3-main-ui-examples/117928/315?u=nic0205
At the moment it looks like this:
For now I am working to get the Menu-structure working. After that, we will develop the widgets and put all together
And yes - all this is then a combinations of widgets in the MainUI.
Help is appreciate