Here is my first effort at a widget for OH3. It was designed to support the Fibaro motion sensor that provides 5 items, but could easily be adapted to any application where a thing provides a number of items to display.
Design objectives
- Display a number of items. If possible, be an extensible design to support as many items as needed for other devices.
- Function and look good on laptop and mobile screens. Be touch compliant on small screens.
- Be flexible to allow the display of all or some of the items supported by the device.
- Support easy format customisation (Iām not sure on my overall theme design as yet).
- Conditionally highlight icons that require user attention e.g. when the battery level matches or is below a specified threshold.
- Allow drill down to the default widget / graph associated with each item.
I took inspiration from with work on the UI Widget: Weather, specifically the card widget by @RGroll and @oh11. In particular their use of the Swiper component provided a good solution the screen size/resolution issues. Their solution was a great way to start learning the new UI.
Here is the result:
Large screen showing all items being displayed.
Small screen showing swiper functionality to access icons that donāt fit on screen.
So the final feature set / some useful stuff I learnt:
- Most of the features listed in the objectives above (maybe a better solution for extensibility).
- Support for background / font / icon colour selection based on theme and/or any of the HTML colour standards (RGB/RGBA/HEX/HSL). Note you must specify the full format rgba(0,0,0,0) etc.
- How to add an external link within the parameter description to access relevant reference materials.
- How to drill down to display analyser data for each item.
Things that could be better/improved:
- In portrait mode on small screens swiping arrows overlap the buttons, requiring careful clicking (or the analyzer graphs are displayed for the icon. Not so easy with my fat fingers
Need to experiment with the swiper padding options.
- Avoided a repeater as data types (number / text), warning conditions and icons are different for each item. May be worth looking into now I have learnt more
Resources
Here is the YAML:
uid: fibaro_motion_card
tags: []
props:
parameters:
- context: item
description: Name of the item containing motion sensor data. If undefined will not display the associated icon/text.
label: Item Name (Motion sensor)
name: item1
required: false
type: TEXT
groupName: items
- context: item
description: Name of the item containing temperature sensor data. If undefined will not display the associated icon/text.
label: Item Name (Temperature sensor)
name: item2
required: false
type: TEXT
groupName: items
- context: item
description: Name of the item containing light sensor / luminescence data. If undefined will not display the associated icon/text.
label: Item name (Light sensor)
name: item3
required: false
type: TEXT
groupName: items
- context: item
description: Name of the item containing accelerometer / tamper alarm data. If undefined will not display the associated icon/text.
label: Item name (Accelerometer sensor / Tamper alarm)
name: item4
required: false
type: TEXT
groupName: items
- context: item
description: Name of the item containing battery charge level data. If undefined will not display the associated icon/text.
label: Item name (Battery level)
name: item5
required: false
type: TEXT
groupName: items
- description: Highlight the motion sensor icon/text when the value equals the specified warning value e.g. ON. When undefined, no highlighting is displayed.
label: Warning (Motion sensor)
name: warn1
required: false
type: TEXT
groupName: warn
- description: Highlight the temperature icon/text when the value is LESS than the specified warning value e.g. 10. When undefined, no highlighting is displayed.
label: Warning (Temperature sensor)
name: warn2
required: false
type: TEXT
groupName: warn
- description: Highlight the light sensor icon/text when the value is LESS that the specified warning value e.g. 5. When undefined, no highlighting is displayed.
label: Warning (Light sensor)
name: warn3
required: false
type: TEXT
groupName: warn
- description: Highlight the tamper alarm icon/text when the value equals the specified warning value item e.g. ON. When undefined, no highlighting is displayed.
label: Warning (Accelerometer sensor / Tamper alarm)
name: warn4
required: false
type: TEXT
groupName: warn
- description: Highlight the battery level icon/text when the value is LESS than the specified warning level e.g. 15. When undefined, no highlighting is displayed.
label: Warning (Battery level)
name: warn5
required: false
type: TEXT
groupName: warn
- description: Set card header title. Only displays if populated.
label: Card Title
name: title
required: false
type: TEXT
groupName: general
- description: Background image URL e.g. http://10.1.0.1/static/fibaromotion.png
label: Background image URL
name: backgroundUrl
required: false
type: TEXT
groupName: general
- description: Intensity of the background-blur (0 - 10)
label: Background image blur
name: backgroundBlur
required: false
type: TEXT
groupName: general
- context: color
description: Specify the background color for card. Any valid <a class="link external" href="https://www.w3schools.com/css/css_colors.asp" target="_blank">HTML color format (RGB/RGBA/HEX/HSL)</a> or <a class="link external" href="https://framework7.io/docs/css-variables.html" target="_blank">CSS theme</a> color. e.g. rgb(255,255,255), --f7-color-white. <b>Default = --f7-color-white</b>
label: Background color
name: backgroundColor
required: false
type: TEXT
groupName: general
- description: Specify the foreground color for text and icons. Any valid <a class="link external" href="https://www.w3schools.com/css/css_colors.asp" target="_blank">HTML color format (RGB/RGBA/HEX/HSL)</a> or <a class="link external" href="https://framework7.io/docs/css-variables.html" target="_blank">CSS theme</a> color. e.g. rgb(255,255,255), --f7-color-black. <b>Default = --f7-color-black</b>
label: Foreground color (normal)
name: foregroundColor
required: false
type: TEXT
groupName: general
- description: Specify the foreground color for highlighted text / icons (when warning condition is true). Any valid <a class="link external" href="https://www.w3schools.com/css/css_colors.asp" target="_blank">HTML color format (RGB/RGBA/HEX/HSL)</a> or <a class="link external" href="https://framework7.io/docs/css-variables.html" target="_blank">CSS theme</a> color. e.g. rgb(255,255,255), --f7-color-red. <b>Default = --f7-theme-color</b>
label: Foreground color (warning)
name: foregroundColorWarn
required: false
type: TEXT
groupName: general
parameterGroups:
- name: general
label: Display options
- name: items
label: Items
- name: warn
label: Warning settings
timestamp: Jan 17, 2021, 2:45:45 AM
component: f7-card
config:
title: =props.title
class:
- padding
style:
background-image: ='url(' + props.backgroundUrl + ')'
backdrop-filter: ='blur(' + props.backgroundBlur + 'px)'
background-size: contain
background-repeat: no-repeat
background-position: 100% 100%
background-color: "=(props.backgroundColor === undefined ? 'var(--f7-color-white)' : (props.backgroundColor.substring(0,2) === '--' ? 'var(' + props.backgroundColor + ')' : props.backgroundColor))"
border-radius: 20px
overflow: hidden
-ms-user-select: none
-moz-user-select: none
-webkit-user-select: none
user-select: none
--normal-txt-color: "=(props.foregroundColor === undefined ? 'var(--f7-color-black)' : (props.foregroundColor.substring(0,2) === '--' ? 'var(' + props.foregroundColor + ')' : props.foregroundColor))"
--warn-txt-color: "=(props.foregroundColorWarn === undefined ? 'var(--f7-theme-color)' : (props.foregroundColorWarn.substring(0,2) === '--' ? 'var(' + props.foregroundColorWarn + ')' : props.foregroundColorWarn))"
slots:
default:
- component: f7-block
config:
class:
- no-padding
- no-margin
style:
width: 100%
height: 100%
position: absolute
top: 0
left: 0
border-radius: 20px
- component: f7-swiper
config:
navigation: true
class:
- padding-top
params:
initalSlide: 0
runCallbacksOnInit: true
grabCursor: true
observer: true
observeSlideChildren: true
updateOnWindowResize: true
spaceBetween: 5
mousewheel: true
keyboard: true
watchOverflow: true
breakpoints:
"0":
slidesPerView: 1
"240":
slidesPerView: 2
"320":
slidesPerView: 3
"480":
slidesPerView: 4
"640":
slidesPerView: 5
style:
--swiper-navigation-size: 30px
--swiper-navigation-color: var(--normal-txt-color)
slots:
default:
- component: f7-swiper-slide
config:
id: 1
expandable: true
visible: =!(props.item1 === undefined)
style:
border-radius: 5px
slots:
default:
- component: oh-button
config:
action: analyzer
actionAnalyzerItems: =[props.item1]
class:
- justify-content-center
- align-items-center
- text-align-center
style:
height: 100%
--f7-button-hover-bg-color: var(--f7-color-white-shade)
--f7-button-pressed-bg-color: var(--f7-color-white-shade)
slots:
default:
- component: f7-icon
config:
f7: radiowaves_right
size: 40
style:
padding-right: 5px
color: '=(items[props.item1].state === props.warn1) ? "var(--warn-txt-color)" : "var(--normal-txt-color)"'
- component: Label
config:
text: =items[props.item1].state
style:
font-size: 18px
font-weight: 400
color: '=(items[props.item1].state === props.warn1) ? "var(--warn-txt-color)" : "var(--normal-txt-color)"'
- component: f7-swiper-slide
config:
id: 2
expandable: true
visible: =!(props.item2 === undefined)
style:
border-radius: 5px
slots:
default:
- component: oh-button
config:
action: analyzer
actionAnalyzerItems: =[props.item2]
class:
- justify-content-center
- align-items-center
- text-align-center
style:
height: 100%
--f7-button-hover-bg-color: var(--f7-color-white-shade)
--f7-button-pressed-bg-color: var(--f7-color-white-shade)
slots:
default:
- component: f7-icon
config:
f7: thermometer
size: 40
style:
padding-right: 5px
color: '=(items[props.item2].state < props.warn2 ? "var(--warn-txt-color)" : "var(--normal-txt-color)")'
- component: Label
config:
text: =items[(props.item2)].state
style:
font-size: 18px
font-weight: 400
color: '=(items[props.item2].state < props.warn2 ? "var(--warn-txt-color)" : "var(--normal-txt-color)")'
- component: f7-swiper-slide
config:
id: 3
expandable: true
visible: =!(props.item3 === undefined)
style:
border-radius: 5px
slots:
default:
- component: oh-button
config:
action: analyzer
actionAnalyzerItems: =[props.item3]
class:
- justify-content-center
- align-items-center
- text-align-center
style:
height: 100%
--f7-button-hover-bg-color: var(--f7-color-white-shade)
--f7-button-pressed-bg-color: var(--f7-color-white-shade)
slots:
default:
- component: f7-icon
config:
f7: sun_max_fill
size: 40
style:
padding-right: 5px
color: '=(Number(items[props.item3].state) < Number(props.warn3) ? "var(--warn-txt-color)" : "var(--normal-txt-color)")'
- component: Label
config:
text: =items[(props.item3)].state + " Lux"
style:
font-size: 18px
font-weight: 400
color: '=(Number(items[props.item3].state) < Number(props.warn3) ? "var(--warn-txt-color)" : "var(--normal-txt-color)")'
- component: f7-swiper-slide
config:
id: 4
expandable: true
visible: =!(props.item4 === undefined)
style:
border-radius: 5px
slots:
default:
- component: oh-button
config:
action: analyzer
actionAnalyzerItems: =[props.item4]
class:
- justify-content-center
- align-items-center
- text-align-center
style:
height: 100%
--f7-button-hover-bg-color: var(--f7-color-white-shade)
--f7-button-pressed-bg-color: var(--f7-color-white-shade)
slots:
default:
- component: f7-icon
config:
f7: graph_square_fill
size: 40
style:
padding-right: 5px
color: '=(items[props.item4].state === props.warn4) ? "var(--warn-txt-color)" : "var(--normal-txt-color)"'
- component: Label
config:
text: =items[(props.item4)].state
style:
font-size: 18px
font-weight: 400
color: '=(items[props.item4].state === props.warn4) ? "var(--warn-txt-color)" : "var(--normal-txt-color)"'
- component: f7-swiper-slide
config:
id: 5
expandable: true
visible: =!(props.item5 === undefined)
style:
border-radius: 5px
slots:
default:
- component: oh-button
config:
action: analyzer
actionAnalyzerItems: =[props.item5]
class:
- justify-content-center
- align-items-center
- text-align-center
style:
height: 100%
--f7-button-hover-bg-color: var(--f7-color-white-shade)
--f7-button-pressed-bg-color: var(--f7-color-white-shade)
slots:
default:
- component: f7-icon
config:
material: battery_charging_full
size: 40
style:
padding-right: 5px
color: '=(Number(items[props.item5].state) < Number(props.warn5) ? "var(--warn-txt-color)" : "var(--normal-txt-color)")'
- component: Label
config:
text: =items[(props.item5)].state + " %"
style:
font-size: 18px
font-weight: 400
color: '=(Number(items[props.item5].state) < Number(props.warn5) ? "var(--warn-txt-color)" : "var(--normal-txt-color)")'
Let me know if there are any ways in which this code could be improved. Always happy to learn. Iād also like to thank @ysc for the great work on the new UI.
Andy