My first widget. Thanks for all for their code examples.
This widget can control devices with combination of items: switch, dimmer, color. Also it can display readings from device sensor, signal level and connection status.
Any ideas how to improve it?
Screenshots
Changelog
Version 1.0
- initial release
Resources
uid: universal_switch_v1
tags:
- AK
props:
parameters:
- description: Name
label: Header
name: header1
required: false
type: TEXT
- description: oh:iconName or iconName (openHAB icon), f7:iconName (Framework7 icon), material:iconName (Material icon) or iconify:iconSet:iconName (Iconify icon, requires being online if not in cache)
label: Main icon
name: mainIcon
required: false
type: TEXT
- description: HEX or rgba
label: Main icon color (activ)
name: micaColor
required: false
type: TEXT
- description: oh:iconName or iconName (openHAB icon), f7:iconName (Framework7 icon), material:iconName (Material icon) or iconify:iconSet:iconName (Iconify icon, requires being online if not in cache)
label: Header icon
name: smallIcon
required: false
type: TEXT
- description: HEX or rgba
label: Header icon color
name: siColor
required: false
type: TEXT
- description: HEX or rgba
label: Background color
name: bgcolor
required: false
type: TEXT
- context: item
description: Switch item or dimmer item
label: Switch item
name: switchItem
required: false
type: TEXT
- context: item
description: Dimmer item
label: Dimmer item
name: dimmerItem
required: false
type: TEXT
- context: item
description: Color item
label: Color item
name: colorItem
required: false
type: TEXT
- context: item
description: Power item (number)
label: Power item
name: powerItem
required: false
type: TEXT
- context: item
description: Energy item (number)
label: Energy item
name: energyItem
required: false
type: TEXT
- context: item
description: Voltage item (number)
label: Voltage item
name: voltageItem
required: false
type: TEXT
- context: item
description: Current item (number)
label: Current item
name: currentItem
required: false
type: TEXT
- context: item
description: Signal quality item (number)
label: Link quality item
name: sigItem
required: false
type: TEXT
- context: item
description: Reachable item (switch)
label: Reachable item
name: connItem
required: false
type: TEXT
parameterGroups: []
timestamp: Dec 30, 2021, 10:51:30 PM
component: f7-card
config:
style:
background-image: icon(f7:drop)
noShadow: false
margin: 2px
font-size: 1em
class:
- padding
border-radius: var(--f7-card-expandable-border-radius)
box-shadow: var(--f7-card-expandable-box-shadow)
background-color: "=props.bgcolor ? props.bgcolor : ''"
slots:
content:
- component: f7-row
config:
class:
- justify-content-flex-start
- display-flex
visible: true
style:
margin-top: -0.5em
flex-wrap: nowrap
overflow: hidden
slots:
default:
- component: oh-icon
config:
visible: "=props.smallIcon ? true : false"
color: "=props.siColor ? props.siColor : props.micaColor"
icon: =props.smallIcon
width: 26px
class:
- align-self-center
style:
margin-left: 0px
- component: Label
config:
text: =props.header1
class:
- align-self-center
style:
white-space: normal
line-height: 1rem
margin-left: 15px
letter-spacing: .75px
font-size: min(max(5px, 2.5vw), 18px)
font-weight: 400
- component: f7-block
config:
class:
- display-flex
- flex-direction-column
- align-items-center
style:
width: 100% auto
height: 100% auto
margin-top: 0.1em
slots:
default:
- component: oh-link
config:
action: toggle
actionCommand: ON
actionCommandAlt: OFF
actionItem: "=props.switchItem ? props.switchItem : props.dimmerItem"
slots:
default:
- component: oh-icon
config:
color: '=((items[props.switchItem].state == "ON") || (items[props.switchItem].state > 0)) ? props.micaColor : "gray"'
icon: '=props.mainIcon ? props.mainIcon : "f7:ant"'
width: 10vw
- component: f7-row
config:
visible: "=props.dimmerItem ? true : false"
width: auto
class:
- display-flex
- justify-content-flex-center
- align-items-center
style:
margin-top: 5px
white-space: nowrap
flex-wrap: nowrap
overflow: hidden
height: auto
width: auto
slots:
default:
- component: f7-col
config:
style:
margin-right: 1px
margin-left: 1px
width: auto
height: auto
class:
- display-flex
- justify-content-flex-center
slots:
default:
- component: oh-button
config:
color: '=((items[props.switchItem].state == "ON") || (items[props.switchItem].state > 0)) ? props.micaColor : "gray"'
fill: "=((items[props.dimmerItem].state > 0) && (items[props.dimmerItem].state <= 5)) ? true : false"
outline: true
round: false
small: true
action: command
actionItem: =props.dimmerItem
actionCommand: 5
text: 5%
style:
font-size: min(max(5px, 2.1vw), 20px)
width: auto
height: auto
- component: f7-col
config:
style:
margin-right: 1px
margin-left: 1px
width: auto
height: auto
class:
- display-flex
- justify-content-flex-center
slots:
default:
- component: oh-button
config:
color: '=((items[props.switchItem].state == "ON") || (items[props.switchItem].state > 0)) ? props.micaColor : "gray"'
fill: "=((items[props.dimmerItem].state > 5) && (items[props.dimmerItem].state <= 25)) ? true : false"
outline: true
round: false
small: true
action: command
actionItem: =props.dimmerItem
actionCommand: 25
text: 25%
style:
font-size: min(max(5px, 2.1vw), 20px)
width: auto
height: auto
- component: f7-col
config:
style:
margin-right: 1px
margin-left: 1px
width: auto
height: auto
class:
- display-flex
- justify-content-flex-center
slots:
default:
- component: oh-button
config:
color: '=((items[props.switchItem].state == "ON") || (items[props.switchItem].state > 0)) ? props.micaColor : "gray"'
fill: "=((items[props.dimmerItem].state > 25) && (items[props.dimmerItem].state <= 50)) ? true : false"
outline: true
round: false
small: true
action: command
actionItem: =props.dimmerItem
actionCommand: 50
text: 50%
style:
font-size: min(max(5px, 2.1vw), 20px)
width: auto
height: auto
- component: f7-col
config:
style:
margin-right: 1px
margin-left: 1px
width: auto
class:
- display-flex
- justify-content-flex-center
slots:
default:
- component: oh-button
config:
color: '=((items[props.switchItem].state == "ON") || (items[props.switchItem].state > 0)) ? props.micaColor : "gray"'
fill: "=((items[props.dimmerItem].state > 50) && (items[props.dimmerItem].state <= 75)) ? true : false"
outline: true
round: false
small: true
action: command
actionItem: =props.dimmerItem
actionCommand: 75
text: 75%
style:
font-size: min(max(5px, 2.1vw), 20px)
- component: f7-row
config:
visible: "=props.dimmerItem ? true : false"
class:
- display-flex
- justify-content-flex-center
- align-self-center
style:
margin-top: 5px
white-space: nowrap
flex-wrap: nowrap
overflow: hidden
height: auto
width: auto
slots:
default:
- component: f7-col
config:
style:
margin-right: 1px
margin-left: 1px
width: auto
class:
- justify-content-flex-center
- display-flex
slots:
default:
- component: oh-button
config:
color: '=((items[props.switchItem].state == "ON") || (items[props.switchItem].state > 0)) ? props.micaColor : "gray"'
textColor: red
text: =(Number.parseFloat(items[props.dimmerItem].state.split(" ")[0]) * 100 / 100).toFixed(0)
iconF7: light_max
outline: true
round: false
small: true
visible: true
action: popover
popoverOpen: ='.' + props.dimmerItem.split("_")[0] + '.brightPopover'
style:
font-size: min(max(5px, 2.1vw), 20px)
slots:
default:
- component: f7-popover
config:
class: =props.dimmerItem.split("_")[0] + ' brightPopover'
slots:
default:
- component: oh-slider-card
config:
title: Jasność
item: =props.dimmerItem
visible: true
label: true
scale: true
scaleSteps: 10
scaleSubSteps: 2
min: 0
max: 100
step: 1
unit: "%"
vertical: true
releaseOnly: true
style:
width: auto
border-radius: var(--f7-card-expandable-border-radius)
box-shadow: var(--f7-card-expandable-box-shadow)
--f7-range-bar-size: 18px
--f7-range-bar-border-radius: 10px
--f7-range-knob-size: 22px
--f7-range-bar-active-bg-color: rgba(241, 245, 39, 0.8)
--f7-range-bar-bg-color: linear-gradient(to top, rgba(0,0,0,0.9), rgba(246,158,81,0))
--f7-range-knob-box-shadow: 0 2px 4px rgba(0, 0, 0, 0.3)
--f7-range-label-text-color: white
--f7-range-scale-font-size: min(max(5px, 2.5vw), 18px)
--f7-range-scale-text-color: gray
- component: f7-col
config:
visible: "=props.colorItem ? true : false"
style:
margin-right: 1px
margin-left: 1px
width: auto
class:
- justify-content-flex-center
- display-flex
slots:
default:
- component: oh-button
config:
text: ""
iconF7: paintbrush
iconSize: min(max(5px, 3vw), 20px)
fill: '=((items[props.switchItem].state == "ON") || (items[props.switchItem].state > 0)) ? true : false'
outline: true
round: false
small: true
visible: true
action: popover
popoverOpen: ='.' + props.colorItem.split("_")[0] + '.colorPopover'
style:
font-size: min(max(5px, 2.1vw), 20px)
--f7-button-fill-bg-color: >
= "hsl(" + items[props.colorItem].state.split(",")[0]+ "," + ((0 === 100* (Number.parseInt( items[props.colorItem].state.split(",")[2] )/100 * (1-0.5 * Number.parseInt( items[props.colorItem].state.split(",")[1])/100))) || (100 === 100* (Number.parseInt( items[props.colorItem].state.split(",")[2] )/100 * (1-0.5 * Number.parseInt( items[props.colorItem].state.split(",")[1])/100))) ? 0 : 100* ((Number.parseInt( items[props.colorItem].state.split(",")[2] ) - 100* (Number.parseInt( items[props.colorItem].state.split(",")[2] )/100 * (1-0.5 * Number.parseInt( items[props.colorItem].state.split(",")[1])/100))) / Math.min(100* (Number.parseInt( items[props.colorItem].state.split(",")[2] )/100 * (1-0.5 * Number.parseInt( items[props.colorItem].state.split(",")[1])/100)), 100- 100* (Number.parseInt( items[props.colorItem].state.split(",")[2] )/100 * (1-0.5 * Number.parseInt( items[props.colorItem].state.split(",")[1])/100))))) +"%," + 100* (Number.parseInt( items[props.colorItem].state.split(",")[2] )/100 * (1-0.5 * Number.parseInt( items[props.colorItem].state.split(",")[1])/100)) +"%)"
--f7-button-outline-border-color: var(--f7-button-fill-bg-color)
slots:
default:
- component: f7-popover
config:
class: =props.colorItem.split("_")[0] + ' colorPopover'
slots:
default:
- component: oh-colorpicker-card
config:
visible: true
modules:
- palette
- hue-slider
- initial-current-colors
item: =props.colorItem
title: Kolor
noBorder: false
style:
height: 50%
width: 50%
- component: f7-row
config:
class:
- justify-content-flex-start
- padding-top
- display-flex
style:
height: auto
width: auto
margin-top: 0.3em
margin-bottom: -0.9em
text-overflow: ellipsis
overflow: hidden
slots:
default:
- component: Label
config:
text: =(Number.parseFloat(items[props.voltageItem].state.split(" ")[0]) * 100 / 100).toFixed(0) + "V"
visible: "=props.voltageItem ? true : false"
class:
- align-self-center
style:
margin-left: 0px
margin-right: 4px
color: gray
letter-spacing: .75px
font-size: min(max(5px, 2.5vw), 18px)
- component: Label
config:
text: =(Number.parseFloat(items[props.currentItem].state.split(" ")[0]) * 100 / 100).toFixed(1) + "A"
visible: "=props.currentItem ? true : false"
class:
- align-self-center
style:
margin-left: 4px
margin-right: 4px
color: gray
letter-spacing: .75px
font-size: min(max(5px, 2.5vw), 18px)
- component: Label
config:
text: =(Number.parseFloat(items[props.powerItem].state.split(" ")[0]) * 100 / 100).toFixed(1) + "W"
visible: "=props.powerItem ? true : false"
class:
- align-self-center
style:
margin-left: 4px
margin-right: 4px
color: gray
letter-spacing: .75px
font-size: min(max(5px, 2.5vw), 18px)
- component: Label
config:
text: =(Number.parseFloat(items[props.energyItem].state.split(" ")[0]) * 100 / 100).toFixed(0) + "kWh"
visible: "=props.energyItem ? true : false"
class:
- align-self-center
style:
margin-left: 4px
margin-right: 4px
color: gray
letter-spacing: .75px
font-size: min(max(5px, 2.5vw), 18px)
- component: f7-row
config:
visible: true
class:
- justify-content-flex-start
- display-flex
slots:
default:
- component: f7-icon
config:
visible: "=props.connItem ? true : false"
f7: '=(items[props.connItem].state == "ON" ? "wifi" : "wifi_exclamationmark")'
size: 22
tooltip: '=(items[props.connItem].state == "ON" ? "Połączono z siecią" : "Brak połączenia z siecią")'
class:
- align-self-center
style:
margin-left: 4px
margin-right: 1px
color: '=(items[props.connItem].state == "ON" ? "gray" : "red")'
- component: Label
config:
text: =(Number.parseFloat(items[props.sigItem].state.split(" ")[0]) * 100 / 100).toFixed(0)
visible: "=props.sigItem ? true : false"
class:
- align-self-center
style:
margin-left: 1px
margin-right: 4px
color: gray
letter-spacing: .75px
font-size: min(max(5px, 2.2vw), 14px)