how can i make the widget to move the setpoint by 1C and not 0.5C that it is now?
Any ideas on that?i searched the widgetâs code but couldnt understand what need to be changedâŠ
Hello Constantinos,
i quickly scanned the code of Version 2.0.0 and found the following line:
actionCommand: â=Number(items[props.setPointItem].state.split(â â)[0]) > Number(props.minTemp) ? Number(items[props.setPointItem].state.split(â ')[0]) - 0.5 : âââ
See the- 0.5
? Same is true for the button configured in a similar line below with +0.5
It simply decreases/increases the current setpoint value by 0.5 degree if it has not already reached the minimum/maximum.
This should answer your question.
Regards
Thomas
i have a Nest thermostat,the temperature item is a Number:Temperature and in the center of the widget it shows NaN.
If i change the temperature item to just a Number then the widget is showing the Number allrightâŠHow can i fix that?
Many thanks for this great widget. Iâm also having this same issue with rounding the setpoint value. Iâm using %.1f °C pattern in the stateDescription of this item. I tried to use .displayState
instead of .state
but I managed to screw up the widget so I wonder where to make this change.
Another question, where can I change the size of the text for the humidity (38.80%)?
My code for the widget is:
uid: widget_HeatingCSS_2.0.0
tags: []
props:
parameters:
- context: item
description: Item to control
label: Setpoint Item
name: setPointItem
required: true
type: TEXT
groupName: mainItems
- context: item
label: Item for current temperature
name: currentPointItem
required: true
type: TEXT
groupName: mainItems
- context: item
description: Humidity Item [Number-Item]
label: Humidity Item
name: humItem
required: false
type: TEXT
groupName: mainItems
- description: eg. living room
label: location and widget identifier
name: location
required: true
type: TEXT
groupName: generalSettings
- description: Minimum value
label: minTemp
name: minTemp
required: true
type: TEXT
groupName: generalSettings
- description: Maximum value
label: maxTemp
name: maxTemp
required: true
groupName: generalSettings
- description: Visual size of the control in px (default 400px), without a size the design is responsive
label: size [px]
name: size
required: false
type: TEXT
groupName: generalSettings
- description: Control item unit eg °C
label: unit
name: unit
required: false
type: TEXT
groupName: generalSettings
advanced: true
- context: item
description: Heating Mode Item [String-Item]
label: Heating Mode Item
name: heatingModeItem
required: false
type: TEXT
groupName: extensionItems
advanced: true
- description: Heating mode strings as array eg. AUTO,MANU,OFF. The string will be send to the Heating-Mode-Item [String-Item]
label: Heating Mode Array
name: heatingModeArray
required: false
type: TEXT
groupName: extensionItems
advanced: true
- context: item
description: Example Mode Item [String-Item] (eg. State of heating valve)
label: Example Mode Item
name: exampleModeItem
required: false
type: TEXT
groupName: extensionItems
advanced: true
- description: Example Mode Strings as Array eg. OFF,ON,BOOST. The string will be send to the Example-Mode-Item [String-Item]
label: Example Mode Array
name: exampleModeArray
required: false
type: TEXT
groupName: extensionItems
advanced: true
- context: item
description: Valve/Alarm Item [Switch-Item] (eg. State of heating valve) for Animation in the center of the widget
label: Valve Item
name: valveItem
required: false
type: TEXT
groupName: extensionItems
advanced: true
- context: item
description: Extension Item 1 eg. Battery-State [Number-Item]
label: Extension Item 1
name: extensionItem1
required: false
type: TEXT
groupName: extensionItems
advanced: true
- description: Choose the F7-Icons for the Extension Item 1 as Array. -> <battery_100,battery_25,battery_0>. 1st Icon = Value > 75, 2nd Icon = Value > 25, 3th Icon = Value < 25
label: Extension Item 1 F7-Icon
name: extensionItem1Icon
required: false
type: TEXT
groupName: extensionItems
advanced: true
- context: item
description: Extension Item 2 eg. Battery-State [Number-Item]
label: Extension Item 2
name: extensionItem2
required: false
type: TEXT
groupName: extensionItems
advanced: true
- description: Choose the F7-Icons for the Extension Item 2 F7-Icon as Array. -> <battery_100,battery_25,battery_0>. 1st Icon = Value > 75, 2nd Icon = Value > 25, 3th Icon = Value < 25
label: F7-Icon for the Extension Item 2
name: extensionItem2Icon
required: false
type: TEXT
groupName: extensionItems
advanced: true
- description: URL or path to a Image (if you use a local img eg. -> http://local-IP-Adress:8080/static/folder/img.svg )
label: URL or path to image
name: imgUrl
required: false
type: TEXT
groupName: moreDesign
advanced: true
- description: eg. default 1em
label: Custom font-size current&set-point marker
name: fontSizeMarker
required: false
type: TEXT
groupName: fonts
advanced: true
- description: eg. default 2em
label: Custom font-Size center
name: fontSizeCenter
required: false
type: TEXT
groupName: fonts
advanced: true
- description: default 1em for eg. Living Room
label: Custom font-size location label
name: fontSizeLocation
required: false
type: TEXT
groupName: fonts
advanced: true
- description: default eg. 1.8em
label: Custom font-Size buttons
name: fontSizeButtons
required: false
type: TEXT
groupName: fonts
advanced: true
- description: default 1em for Heating-Label in bottom of the widget
label: Custom font-Size footer
name: fontSizeFooter
required: false
type: TEXT
groupName: fonts
advanced: true
- label: Main-Color Thermostat
name: colorThermostat
required: false
type: TEXT
groupName: colors
advanced: true
- label: Color control ring
name: colorControlRing
required: false
type: TEXT
groupName: colors
advanced: true
- label: Color buttons
name: colorButton
required: false
type: TEXT
groupName: colors
advanced: true
- label: Color center
name: colorCenter
required: false
type: TEXT
groupName: colors
advanced: true
- label: Color Typo
name: colorTypo
required: false
type: TEXT
groupName: colors
advanced: true
- label: Color setPoint Marker
name: colorSetMarker
required: false
type: TEXT
groupName: colors
advanced: true
- label: Color currentPoint Marker
name: colorCurrentMarker
required: false
type: TEXT
groupName: colors
advanced: true
- label: Color bar linear gradient startPoint
name: colorBarStartPoint
required: false
type: TEXT
groupName: colors
advanced: true
- label: Color bar linear gradient endPoint
name: colorBarEndPoint
required: false
type: TEXT
groupName: colors
advanced: true
- description: Control if the Chart is visible
label: Chart Swtich
name: chartSwitch
required: false
type: BOOLEAN
groupName: chartSettings
- description: currently not possible -> The minimun value of axis. (eg. 10.5)
label: yAxis min
name: yAxisMin
required: false
type: TEXT
groupName: chartSettings
advanced: true
- description: currently not possible -> The maximum value of axis. (eg. 30.5)
label: yAxis max
name: yAxisMax
required: false
type: TEXT
groupName: chartSettings
advanced: true
parameterGroups:
- name: mainItems
label: Main Items (setpoint-temperature, current temperature, humidity)
- name: advancedItems
label: Advanced Items (Battery-State, ...)
- name: generalSettings
label: General Settings
- name: extensionItems
label: Extension Items
- name: moreDesign
label: More Design Options
- name: colors
label: Color Settings
- name: fonts
label: Font Settings
- name: chartSettings
label: Chart Settings
timestamp: Apr 14, 2021, 9:31:45 PM
component: f7-card
config:
title: "=(props.location) ? 'Klima ' + props.location : ''"
stylesheet: >
.mylink {
#background-color: blue !important;
animation-name: example;
animation-duration: 4s;
} .mylink#####:hover {
text-decoration: underline !important;
transform: scale(1.5);
transition: 0.3s all;
}
@keyframes example {
0% {background-color: red;}
25% {background-color: yellow;}
50% {background-color: blue;}
100% {background-color: green;}
}
.thermostat {
flex-shrink: 0;
--f7-block-margin-vertical: 0px;
--f7-block-padding-vertical: 0px;
--f7-block-padding-horizontal: 0px;
padding-left: 0px;
border-radius: 50%;
box-sizing: content-box;
border: 2px solid rgb(64, 60, 77);
}
.thermostat .bar {
margin-top: 0px;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
border-radius: 50%;
} .thermostat .bar .inner_bar {
margin-top: 0;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
border-radius: 100%;
z-index: 4 !important;
} .thermostat .bar .inner_bar .block {
content: "";
display: block;
position: absolute;
width: 100%;
height: 100%;
left: 50%;
transform: translate(-50%);
}
slots:
default:
- component: f7-card-content
slots:
default:
- component: f7-row
config:
resizableFixed: true
resizable-absolute: true
class:
- justify-content-center
slots:
default:
- component: f7-block
config:
class: thermostat
style:
padding-top: "=props.size ? props.size + 'px': '100%'"
width: "=props.size ? Number(props.size)+'px' : '100%'"
background: "=props.colorThermostat ? props.colorThermostat : 'var(--f7-toggle-inactive-color)'"
slots:
default:
- component: f7-block
config:
class: bar
style:
width: "=props.size ? (Number(props.size)*0.89) +'px' : '89%'"
height: "=props.size ? (Number(props.size)*0.89) +'px' : '89%'"
slots:
default:
- component: f7-block
config:
class: inner_bar
style:
width: "=props.size ? (Number(props.size)*0.86) +'px' : '97%'"
height: "=props.size ? (Number(props.size)*0.86) +'px' : '97%'"
background-color: "=props.colorThermostat ? props.colorThermostat : 'var(--f7-toggle-inactive-color)'"
slots:
default:
- component: f7-block
config:
style:
background: "='conic-gradient(transparent 0deg 160deg, ' + (props.colorThermostat ? props.colorThermostat : 'var(--f7-toggle-inactive-color)') + ' 160deg 200deg, transparent 200deg 360deg)'"
bottom: "=props.size ? '-7px' : '-7px'"
- component: f7-block
config:
class: hold left
style:
margin-top: 0px
position: absolute
width: 100%
height: 100%
clip-path: "=props.size ? 'inset(0px 0px 0px ' + (Number(props.size)*0.89/2) + 'px)' : 'inset(0% 0% 0% 50%)'"
border-radius: 100%
background-color: rgb(58, 55, 73)
slots:
default:
- component: f7-block
config:
class: fill fill1
style:
margin-top: 0px
position: absolute
width: 100%
height: 100%
border-radius: 100%
clip-path: "=props.size ? 'inset(0px ' + (Number(props.size)*0.89/2) + 'px 0px 0px)' : 'inset(0% 50% 0% 0%)'"
background: "=props.colorBarStartPoint && props.colorBarEndPoint ? '-webkit-linear-gradient(top, ' + props.colorBarEndPoint + ' 20%,' + props.colorBarEndPoint + ' 100%)' : '-webkit-linear-gradient(top, rgb(255, 73, 0) 20%,rgb(255, 73, 0) 100%)'"
z-index: 1 !important
transition: transform 0.6s
transform: "=(items[props.setPointItem].state.split(' ')[0] >= (((Number(props.maxTemp) - Number(props.minTemp)) / 2) + Number(props.minTemp)) && items[props.setPointItem].state.split(' ')[0] <= Number(props.maxTemp) ? 'rotate('+(320/(Number(props.maxTemp)-Number(props.minTemp))*(items[props.setPointItem].state.split(' ')[0]-Number(props.minTemp))-160)+'deg)' : (items[props.setPointItem].state.split(' ')[0] > Number(props.maxTemp)) ? 'rotate(180deg)' : '')"
- component: f7-block
config:
class: hold right
style:
margin-top: 0px
position: absolute
width: 100%
height: 100%
clip-path: "=props.size ? 'inset(0px 0px 0px ' + (Number(props.size)*0.89/2) + 'px)' : 'inset(0% 0% 0% 50%)'"
border-radius: 100%
background-color: rgb(58, 55, 73)
z-index: 3 !important
transform: rotate(180deg)
slots:
default:
- component: f7-block
config:
class: fill
style:
margin-top: 0px
position: absolute
width: 100%
height: 100%
border-radius: 100%
z-index: 3 !important
animation: right 1s linear both
transition: transform 0.6s
- component: f7-block
config:
class: fill fill2
style:
position: absolute
margin-top: 0px
width: 100%
height: 100%
border-radius: 50%
z-index: 3 !important
clip-path: "=props.size ? 'inset(0px '+ (Number(props.size)*0.89/2) + 'px 0px 0px)' : 'inset(0% 50% 0% 0%)'"
background: "=props.colorBarStartPoint && props.colorBarEndPoint ? '-webkit-linear-gradient(top, ' + props.colorBarEndPoint + ' 40%,' + props.colorBarStartPoint + ' 100%)' : '-webkit-linear-gradient(top, rgb(255, 73, 0) 40%,rgb(255, 158, 35) 100%)'"
transform: "=(items[props.setPointItem].state.split(' ')[0] <= (((Number(props.maxTemp) - Number(props.minTemp)) / 2) + Number(props.minTemp)) && items[props.setPointItem].state.split(' ')[0] >= Number(props.minTemp) ? 'rotate('+(320/(Number(props.maxTemp)-Number(props.minTemp))*(items[props.setPointItem].state.split(' ')[0]-Number(props.minTemp))+20)+'deg)' : (items[props.setPointItem].state.split(' ')[0] > (((Number(props.maxTemp) - Number(props.minTemp)) / 2) + Number(props.minTemp))) ? 'rotate(180deg)' : '')"
- component: f7-block
config:
class: span
style:
margin-top: 0px
width: "=props.size ? (Number(props.size)*0.89) +'px' : '100%'"
font-weight: "=props.size ? (Number(props.size)*2) +'px' : 'calc(var(--f7-list-item-title-font-weight)*2)'"
position: absolute
bottom: 0px
text-align: center
text-transform: uppercase
font-size: "=props.fontSizeFooter ? props.fontSizeFooter : '1em'"
color: "=props.colorTypo ? props.colorTypo : 'rgb(87, 84, 95)'"
z-index: 99 !important
slots:
default:
- component: Label
config:
text: Heating
- component: f7-block
config:
class: shadow
style:
margin-top: 0px
position: absolute
top: 50%
left: 50%
transform: "=(items[props.setPointItem].state.split(' ')[0] >= Number(props.minTemp) && items[props.setPointItem].state.split(' ')[0] <= Number(props.maxTemp) ? 'translate(-50%, -50%) rotate('+(320/(Number(props.maxTemp)-Number(props.minTemp))*(items[props.setPointItem].state.split(' ')[0]-Number(props.minTemp))-160)+'deg)' : 'translate(-50%, -50%) rotate(0deg)')"
width: "=props.size ? (Number(props.size)*0.0625) +'px' : '6.25%'"
height: 86%
text-align: center
transition: 0.7s ease
animation: shadow 1.4s ease-out both
slots:
default:
- component: f7-block
config:
class: shadow-cube
style:
margin-top: 0px
position: absolute
top: 0
width: "=props.size ? (Number(props.size)*0.0625) +'px' : '100%'"
height: 0px
box-shadow: "=props.size ? '0 0 ' + (Number(props.size)*0.1125) +'px ' + (Number(props.size)*0.0325) + 'px ' + (props.colorSetMarker ? props.colorSetMarker : 'rgba(255, 158, 35, 0.5)'): '0 0 45px 13px rgba(255, 158, 35, 0.5)'"
- component: f7-block
config:
class: markerContainer
style:
pointer-events: none
margin-top: 0px
position: absolute
top: 50%
left: 50%
transform: "=(items[props.currentPointItem].state.split(' ')[0] >= Number(props.minTemp) && items[props.currentPointItem].state.split(' ')[0] <= Number(props.maxTemp) ? 'translate(-50%, -50%) rotate('+(320/(Number(props.maxTemp)-Number(props.minTemp))*(items[props.currentPointItem].state.split(' ')[0]-Number(props.minTemp))-160)+'deg)' : 'translate(-50%, -50%) rotate(0deg)')"
width: "=props.size ? (Number(props.size)*0.1) +'px' : '10%'"
height: 100%
text-align: center
transition: 0.7s ease
opacity: 1
z-index: 99 !important
slots:
default:
- component: f7-block
config:
class: markerCurrent
style:
margin-top: 0px
width: "=props.size ? (Number(props.size)*0.1) +'px' : '100%'"
height: "=props.size ? (Number(props.size)*0.1) +'px' : ''"
padding-top: "=props.size ? '' : '100%'"
background: "=props.colorCurrentMarker ? props.colorCurrentMarker : 'rgb(33, 150, 243)'"
position: absolute
transform: translate(-50%,-50%) rotate(45deg)
left: 50%
top: "=props.size ? (Number(props.size)*0.14) +'px' : '15%'"
border-radius: 0% 50% 50% 50%
box-shadow: 0 0 5px 1px rgb(48, 46, 56)
slots:
default:
- component: f7-block
config:
class: number
style:
margin-top: 0px
position: absolute
top: 50%
left: 50%
transform: translate(-50%, -50%) rotate(-45deg)
text-align: center
slots:
default:
- component: Label
config:
text: =items[props.currentPointItem].state.split(' ')[0]
style:
font-size: "=props.fontSizeMarker ? props.fontSizeMarker : '1em'"
color: white
font-weight: bold
- component: f7-block
config:
class: markerContainer
style:
pointer-events: none
margin-top: 0px
position: absolute
top: 50%
left: 50%
transform: "=(items[props.setPointItem].state.split(' ')[0] >= Number(props.minTemp) && items[props.setPointItem].state.split(' ')[0] <= Number(props.maxTemp) ? 'translate(-50%, -50%) rotate('+(320/(Number(props.maxTemp)-Number(props.minTemp))*(items[props.setPointItem].state.split(' ')[0]-Number(props.minTemp))-160)+'deg)' : 'translate(-50%, -50%) rotate(0deg)')"
width: "=props.size ? (Number(props.size)*0.1) +'px' : '10%'"
height: 100%
text-align: center
transition: 0.7s ease
opacity: 1
z-index: 99 !important
slots:
default:
- component: f7-block
config:
class: markerSet
style:
margin-top: 0px
width: "=props.size ? (Number(props.size)*0.1) +'px' : '100%'"
height: "=props.size ? (Number(props.size)*0.1) +'px' : ''"
padding-top: "=props.size ? '' : '100%'"
background: "=props.colorSetMarker ? props.colorSetMarker : 'rgb(230, 74, 25)'"
position: absolute
transform: translate(-50%,-50%) rotate(-45deg)
left: 50%
top: "=props.size ? (Number(props.size)*Number(-0.0125)) +'px': '-2%'"
border-radius: 50% 50% 50% 0
box-shadow: 0 0 5px 1px rgb(48, 46, 56)
z-index: 100 !important
slots:
default:
- component: f7-block
config:
class: number
style:
margin-top: 0px
position: absolute
top: 50%
left: 50%
transform: translate(-50%, -50%) rotate(45deg)
text-align: center
slots:
default:
- component: Label
config:
text: =items[props.setPointItem].state.split(' ')[0]
style:
font-size: "=props.fontSizeMarker ? props.fontSizeMarker : '1em'"
color: white
font-weight: bold
- component: f7-block
config:
class: center
style:
margin-top: 0px
position: absolute
width: "=props.size ? (Number(props.size)*0.65) +'px' : '65%'"
height: "=props.size ? (Number(props.size)*0.65) +'px' : '65%'"
background: "=props.colorControlRing ? props.colorControlRing : 'rgb(227, 228, 237)'"
top: 50%
left: 50%
transform: translate(-50%, -50%)
border-radius: 50%
box-shadow: 0px 15px 35px 11px rgba(46, 44, 58,0.60)
slots:
default:
- component: f7-swiper
config:
pagination: true
scrollbar: false
params:
slidesPerView: 1
direction: horizontal
mousewheel: true
style:
width: 100%
height: 100%
position: absolute
top: 50%
left: 50%
transform: translate(-50%, -50%)
border-radius: 50%
slots:
default:
- component: f7-swiper-slide
config:
style:
width: 100%
height: 100%
slots:
default:
- component: f7-block
config:
class: page
style:
background: transparent
margin-top: 0px
position: absolute
width: 100%
height: 100%
top: 50%
left: 50%
transform: translate(-50%, -50%)
border-radius: 50%
slots:
default:
- component: f7-block
config:
class: buttonContainer
style:
display: "=props.heatingModeItem ? '' : 'none'"
margin-top: 0px
position: absolute
top: 50%
left: 50%
transform: "=props.exampleModeItem ? 'translate(-50%, -50%) rotate(30deg)' : 'translate(-50%, -50%)'"
width: "=props.size ? (Number(props.size)*0.15) +'px' : '15%'"
height: 100%
text-align: center
transition: 0.7s ease
opacity: 1
z-index: 99 !important
slots:
default:
- component: oh-button
config:
popoverOpen: ='.' + props.location + '.popoverHeatingMode'
style:
width: 100%
height: 20%
position: absolute
bottom: 0%
left: 50%
transform: translate(-50%)
color: "=props.colorButton ? props.colorButton + ' !important': ''"
slots:
default:
- component: f7-icon
config:
f7: "=props.heatingModeItem ? (items[props.heatingModeItem].state == props.heatingModeArray.split(',')[0] ? 'hand_raised' : items[props.heatingModeItem].state == props.heatingModeArray.split(',')[1] ? 'arrow_2_squarepath' : items[props.heatingModeItem].state == props.heatingModeArray.split(',')[2] ? 'airplane' : 'thermometer') : ''"
style:
font-size: "=props.fontSizeButtons ? props.fontSizeButtons : '2em'"
position: absolute
transform: translate(-50%, -50%)
top: 50%
margin-top: auto
- component: f7-popover
config:
class: =props.location + ' popoverHeatingMode'
slots:
default:
- component: f7-card
config:
noShadow: true
class:
- popover-close
action: variable
actionVariable: myVar
clearVariable: true
actionVariableValue: success
slots:
default:
- component: f7-row
config: {}
slots:
default:
- component: f7-col
slots:
default:
- component: oh-repeater
config:
for: buttonlabel
in: =props.heatingModeArray.split(",")
containerStyle:
width: 100%
slots:
default:
- component: oh-button
config:
color: "=props.colorButton ? props.colorButton : ''"
class: margin
text: =loop.buttonlabel
outline: true
action: command
active: "=props.heatingModeItem ? (items[props.heatingModeItem].state === loop.buttonlabel ? true : false) : ''"
actionCommand: =loop.buttonlabel
actionItem: "=props.heatingModeItem ? props.heatingModeItem : ''"
- component: f7-block
config:
class: buttonContainer
style:
display: "=props.exampleModeItem ? '' : 'none'"
margin-top: 0px
position: absolute
top: 50%
left: 50%
transform: "=props.heatingModeItem ? 'translate(-50%, -50%) rotate(-30deg)' : 'translate(-50%, -50%)'"
width: "=props.size ? (Number(props.size)*0.15) +'px' : '15%'"
height: 100%
text-align: center
transition: 0.7s ease
opacity: 1
z-index: 99 !important
slots:
default:
- component: oh-button
config:
popoverOpen: ='.' + props.location + '.popoverExampleMode'
style:
width: 100%
height: 20%
position: absolute
bottom: 0%
left: 50%
transform: translate(-50%)
color: "=props.colorButton ? props.colorButton + ' !important': ''"
slots:
default:
- component: f7-icon
config:
f7: wrench
style:
font-size: "=props.fontSizeButtons ? props.fontSizeButtons : '2em'"
position: absolute
transform: translate(-50%, -50%)
top: 50%
margin-top: auto
- component: f7-popover
config:
class: =props.location + ' popoverExampleMode'
slots:
default:
- component: f7-card
config:
noShadow: true
class:
- popover-close
action: variable
actionVariable: myVar
clearVariable: true
actionVariableValue: success
slots:
default:
- component: f7-row
config: {}
slots:
default:
- component: f7-col
slots:
default:
- component: oh-repeater
config:
for: buttonlabel
in: =props.exampleModeArray.split(",")
containerStyle:
width: 100%
slots:
default:
- component: oh-button
config:
color: "=props.colorButton ? props.colorButton : ''"
class: margin
text: =loop.buttonlabel
outline: true
action: command
active: "=props.exampleModeItem ? (items[props.exampleModeItem].state === loop.buttonlabel ? true : false) : ''"
actionCommand: =loop.buttonlabel
actionItem: "=props.exampleModeItem ? props.exampleModeItem : ''"
- component: oh-button
config:
style:
width: 30%
height: 50%
position: absolute
margin-top: 0px
top: 50%
left: 10%
transform: translate(-50%, -50%)
color: "=props.colorButton ? props.colorButton : ''"
action: command
actionItem: =props.setPointItem
actionCommand: "=Number(items[props.setPointItem].state.split(' ')[0]) > Number(props.minTemp) ? Number(items[props.setPointItem].state.split(' ')[0]) - 0.1 : ''"
slots:
default:
- component: f7-icon
config:
style:
font-size: "=props.fontSizeButtons ? props.fontSizeButtons : '2em'"
position: absolute
transform: translate(-50%, -50%)
top: 50%
margin-top: auto
f7: arrow_turn_left_down
- component: oh-button
config:
style:
width: 30%
height: 50%
position: relative
margin-top: 0px
top: 50%
left: 90%
transform: translate(-50%, -50%)
color: "=props.colorButton ? props.colorButton : ''"
action: command
actionItem: =props.setPointItem
actionCommand: "=Number(items[props.setPointItem].state.split(' ')[0]) < Number(props.maxTemp) ? Number(items[props.setPointItem].state.split(' ')[0]) + 0.1 : ''"
slots:
default:
- component: f7-icon
config:
style:
font-size: "=props.fontSizeButtons ? props.fontSizeButtons : '2em'"
position: absolute
transform: translate(-50%, -50%)
top: 50%
margin-top: auto
f7: arrow_turn_right_up
- component: f7-block
config:
class: valveAnimation
style:
z-index: -100 !important
pointer-events: none
display: "=(props.valveItem && items[props.valveItem].state == 'ON' )? '' : 'none'"
background: "=props.colorSetMarker ? 'radial-gradient(' + props.colorSetMarker +' 30%, transparent 50%)' : 'radial-gradient(var(--f7-theme-color) 30%, transparent 50%)'"
margin-top: 0px
position: absolute
transform: translate(-50%, -50%)
width: 100%
height: 100%
top: 50%
left: 50%
border-radius: 50%
animation: skeleton-effect-fade 2s linear infinite
- component: f7-block
config:
class: small
style:
z-index: 100 !important
margin-top: 0px
position: absolute
width: "=props.size ? (Number(props.size)*0.375) +'px' : '57.6%'"
height: "=props.size ? (Number(props.size)*0.375) +'px' : '57.6%'"
background: "=props.colorCenter ? props.colorCenter : 'rgb(248, 249, 250)'"
text-align: center
animation: bound-in-small 0.6s ease forwards
top: 50%
left: 50%
transform: translate(-50%, -50%)
border-radius: 50%
box-shadow: 0px 5px 10px 5px rgba(96, 93, 111,0.19)
slots:
default:
- component: f7-block
config:
class: heat
style:
font-size: "=props.fontSizeLocation ? props.fontSizeLocation : '1em'"
color: "=props.colorTypo ? props.colorTypo : 'rgb(87, 84, 95)'"
font-weight: 300
transform: translate(-50%)
left: 50%
margin-top: 10%
slots:
default:
- component: Label
config:
text: =props.location
- component: f7-swiper
config:
scrollbar: false
params:
autoplay:
delay: 1500
disableOnInteraction: false
slidesPerView: 1
direction: vertical
mousewheel: true
style:
width: 100%
height: 100%
position: absolute
top: 50%
left: 50%
transform: translate(-50%, -50%)
border-radius: 50%
slots:
default:
- component: f7-swiper-slide
config:
style:
display: "=props.humItem ? '' : 'none'"
slots:
default:
- component: f7-block
config:
class: heat
style:
margin-top: 0
font-size: "=props.fontSizeCenter ? props.fontSizeCenter : '2em'"
color: "=props.colorTypo ? props.colorTypo : 'rgb(87, 84, 95)'"
font-weight: 300
position: absolute
transform: translate(-50%,-50%)
top: 50%
left: 50%
display: flex
slots:
default:
- component: Label
config:
text: =items[props.humItem].state
- component: Label
config:
text: =items[props.humItem].displayState.split(' ')[1]
style:
font-size: "=props.fontSizeCenter ? 'calc(' + props.fontSizeCenter + ' * 0.2 )' : '0.6em'"
vertical-align: top
- component: f7-swiper-slide
config:
style:
display: "=props.currentPointItem ? '' : 'none'"
slots:
default:
- component: f7-block
config:
class: heat
style:
margin-top: 0
font-size: "=props.fontSizeCenter ? props.fontSizeCenter : '2em'"
color: "=props.colorTypo ? props.colorTypo : 'rgb(87, 84, 95)'"
font-weight: 300
position: absolute
transform: translate(-50%,-50%)
top: 50%
left: 50%
display: flex
slots:
default:
- component: Label
config:
text: =Number(items[props.currentPointItem].state).toFixed(1)
- component: Label
config:
text: "=props.unit ? props.unit : items[props.currentPointItem].displayState.split(' ')[1]"
style:
font-size: "=props.fontSizeCenter ? 'calc(' + props.fontSizeCenter + ' * 0.2 )' : '0.6em'"
vertical-align: top
- component: oh-image
config:
url: "=props.imgUrl ? props.imgUrl : 'https://community-openhab-org.s3-eu-central-1.amazonaws.com/original/2X/7/7d388a86c95471f89b1bb911d96d7609a3e3a059.svg'"
style:
position: absolute
transform: translate(-50%)
left: 50%
bottom: 5%
width: 40%
- component: f7-swiper-slide
config:
style:
width: 100%
height: 100%
display: "=props.chartSwitch ? '' : 'none'"
slots:
default:
- component: f7-block
config:
class: page
style:
background: transparent
margin-top: 0px
position: absolute
width: 100%
height: 100%
top: 50%
left: 50%
transform: translate(-50%, -50%)
border-radius: 50%
slots:
default:
- component: oh-chart
config:
height: 100%
periodVisible: false
period: 12h
slots:
title:
- component: oh-chart-title
config:
text: 12h
left: center
bottom: 10%
textStyle:
fontSize: 1em
fontWeight: normal
grid:
- component: oh-chart-grid
config:
show: true
borderWidth: 0
top: 20%
left: 15%
right: 15%
height: 60%
xAxis:
- component: oh-time-axis
config:
show: true
gridIndex: 0
name: Uhrzeit
boundaryGap: false
axisLine:
onZero: true
axisTick:
show: false
axisLabel:
show: false
axisPointer:
label:
show: false
yAxis:
- component: oh-value-axis
config:
show: false
gridIndex: 0
min: auto
max: auto
name: Temperatur
nameLocation: center
nameGap: 45
axisLabel:
formatter: "{value} °C"
series:
- component: oh-time-series
config:
data:
name: istTemp
name: Ist Temp. Indoor
gridIndex: 0
xAxisIndex: 0
yAxisIndex: 0
type: line
item: "=props.currentPointItem ? props.currentPointItem : ''"
markPoint:
itemStyle:
color: "=props.colorCurrentMarker ? props.colorCurrentMarker : 'rgb(33, 150, 243)'"
data:
- type: max
name: Höchsttemperatur
- type: min
name: Tiefsttemperatur
smooth: true
lineStyle:
color: "=props.colorCurrentMarker ? props.colorCurrentMarker : 'rgb(33, 150, 243)'"
- component: oh-time-series
config:
name: Soll Temp. Indoor
gridIndex: 0
xAxisIndex: 0
yAxisIndex: 0
type: line
item: "=props.setPointItem ? props.setPointItem : ''"
step: end
lineStyle:
color: "=props.colorSetMarker ? props.colorSetMarker : 'rgb(230, 74, 25)'"
- component: f7-swiper-slide
config:
style:
width: 100%
height: 100%
display: "=(props.extensionItem1 || props.extensionItem2)? '' : 'none'"
slots:
default:
- component: f7-block
config:
class: page
style:
font-size: "=props.fontSizeLocation ? props.fontSizeLocation : '1em'"
background: transparent
margin-top: 0px
position: absolute
width: 100%
height: 100%
top: 50%
left: 50%
transform: translate(-50%, -50%)
border-radius: 50%
slots:
default:
- component: Label
config:
text: Extension Items
style:
margin-top: 5%
margin-bottom: 5%
text-align: center
- component: oh-repeater
config:
for: i
sourceType: range
rangeStart: "=props.extensionItem1 ? Number(1) : (!props.extensionItem1 && props.extensionItem2) ? Number(2) : ''"
rangeStop: "=(props.extensionItem1 && props.extensionItem2) ? Number(2) : (props.extensionItem1 && !props.extensionItem2) ? Number(1) : Number(2)"
slots:
default:
- component: f7-block
config:
style:
display: flex
flex-flow: row wrap
padding: 0 5% 0 5%
justify-content: space-evenly
align-items: center
slots:
default:
- component: f7-icon
config:
style:
min-width: 40px
line-height: 2
size: 2em
f7: '=props["extensionItem" + loop.i + "Icon"] ? ((Number.parseInt(items[props["extensionItem" + loop.i]].state) > 75) ? props["extensionItem" + loop.i + "Icon"].split(",")[0] : (Number.parseInt(items[props["extensionItem" + loop.i]].state) > 25) ? props["extensionItem" + loop.i + "Icon"].split(",")[1] : props["extensionItem" + loop.i + "Icon"].split(",")[2]) : "clear"'
color: '=(Number.parseInt(items[props["extensionItem" + loop.i]].state) > 75) ? "green" : (Number.parseInt(items[props["extensionItem" + loop.i]].state) > 25) ? "yellow" : "red"'
- component: f7-badge
config:
style:
line-height: 3
font-size: 1em
min-width: 30px
margin: 0 5px 0 5px
color: '=(Number.parseInt(items[props["extensionItem" + loop.i]].state) > 75) ? "green" : (Number.parseInt(items[props["extensionItem" + loop.i]].state) > 25) ? "yellow" : "red"'
slots:
default:
- component: Label
config:
text: =items[props["extensionItem" + loop.i]].displayState
I managed to find the line where I added Number
and.toFixed(1)
. I also found the line where I could decrease the font size.
Really cool widget!
Ah cool you got it!
I also have the same rounding issue, thanks for sharing that super useful! Maybe Iâll add some comments in the code to help future meâŠ
If you still have a problem with decimals and Item-Type. I have a version based on Rel. V.1.8.1 from @Nico_R which works with my AVM-Thermostats (Number:Temperature) and has some other fancy features.
tags:
- Thermostat-Widget
- 1 Window-Sensor (Contact OPEN/CLOSED no AJAR)
- 2 Battery-Items (Switch ON/OFF) for Window and Thermostat
- 4 visible Corner Items - for more Infos
- Background - Multicolor - Fantasy-Colors
- Original from Nico Version V.1.8.1
- flexible Corners
- flexible size
- https://community.openhab.org/t/heating-widget/115107
looks like:
If you like it, hereâs the Code:
uid: Heating-Control_1.8.1_v5
tags:
- 1 Window-Sensor (Contact OPEN/CLOSED no AJAR)
- 2 Battery-Items (Switch ON/OFF) for Window and Thermostat
- 4 visible Corner Items - more Infos
- Background - Multicolor - Fantasy-Colors
- Original from Nico Version V.1.8.1
- Pimps from Peter
- Thermostat-Widget
- flexible Corners
- ON/OFF Switch
- https://community.openhab.org/t/heating-widget/115107
props:
parameters:
- description: eg. living room
label: location and widget identifier
name: location
required: true
type: TEXT
- default: "350"
description: Visual size of the control in px (default 350px), without a size the design is responsive
label: size [px]
name: size
required: false
type: TEXT
- description: Minimum value
label: minTemp
name: minTemp
required: true
type: TEXT
- description: Maximum value
label: maxTemp
name: maxTemp
required: true
- context: item
description: Item to set/control target temperatur
label: Set point Item
name: setPointItem
required: true
type: TEXT
- context: item
label: Item for current temperature
name: currentPointItem
required: true
type: TEXT
- description: Control item unit eg °C
label: unit
name: unit
required: false
type: TEXT
- description: URL or path to a Image (if you use a local img eg. -> http://local-IP-Adress:8080/static/folder/img.svg )
label: URL or path to image
name: imgUrl
required: false
type: TEXT
advanced: true
- description: Device modes as array eg. MANUAL,AUTOMATIC,VACATION.
label: Device Mode Array
name: deviceModeArray
required: false
type: TEXT
advanced: true
- description: Heating modes of the thermostate as array eg. ECO,COMFORT,BOOST,ON,OFF,WINDOW_OPEN.
label: Heating Mode Array
name: heatingModeArray
required: false
type: TEXT
advanced: true
- context: item
description: Device Mode Item [String-Item] - readonly
label: Device Mode Item
name: deviceModeItem
required: false
type: TEXT
advanced: true
- context: item
description: Heating Mode Item [String-Item] (eg. value of heating Item - corresponds also with setpoint )
label: Heating Mode Item
name: heatingModeItem
required: false
type: TEXT
advanced: true
- context: item
description: Valve Item [Switch-Item] (eg. State of heating valve) for Animation
label: Valve Item
name: valveItem
required: false
type: TEXT
advanced: true
- context: item
label: Item to display window state
name: windowItem
required: false
type: TEXT
- context: item
description: Item to display low-state of the battery of the thermostat (Switch ON/OFF)
label: Low-state thermostat-battery (Switch ON/OFF)
name: batteryItem
required: false
type: TEXT
- context: item
description: Item to display low-state of the battery of the window-sensor(s) (Switch ON/OFF Group Item is possible)
label: Low-state window-sensor(s)-battery (Switch ON/OFF)
name: batteryItem1
required: false
type: TEXT
- label: Custom font-size current&set-point marker (eg. 1em)
name: fontSizeMarker
required: false
type: TEXT
groupName: fonts
advanced: true
- label: Custom font-Size center (eg. 2em)
name: fontSizeCenter
required: false
type: TEXT
groupName: fonts
advanced: true
- label: Custom font-Size buttons (eg. 1.8em)
name: fontSizeButtons
required: false
type: TEXT
groupName: fonts
advanced: true
- label: Custom font-Size footer (eg. 1em)
name: fontSizeFooter
required: false
type: TEXT
groupName: fonts
advanced: true
- label: Main-Color Thermostat
name: colorThermostat
required: false
type: TEXT
groupName: colors
advanced: true
- label: Color control ring
name: colorControlRing
required: false
type: TEXT
groupName: colors
advanced: true
- label: Color buttons
name: colorButton
required: false
type: TEXT
groupName: colors
advanced: true
- label: Color center
name: colorCenter
required: false
type: TEXT
groupName: colors
advanced: true
- label: Color Typo
name: colorTypo
required: false
type: TEXT
groupName: colors
advanced: true
- label: Color setPoint Marker
name: colorSetMarker
required: false
type: TEXT
groupName: colors
advanced: true
- label: Color currentPoint Marker
name: colorCurrentMarker
required: false
type: TEXT
groupName: colors
advanced: true
- label: Color bar linear gradient startPoint
name: colorBarStartPoint
required: false
type: TEXT
groupName: colors
advanced: true
- label: Color bar linear gradient endPoint
name: colorBarEndPoint
required: false
type: TEXT
groupName: colors
advanced: true
- context: item
description: Top Left Item to show Information
label: Top Left Item
name: tl_item
required: false
type: TEXT
groupName: outeritems
advanced: true
- description: Top Left Icon to show Information
label: Top Left Icon
name: tl_icon
required: false
type: TEXT
groupName: outeritems
advanced: true
- context: item
description: Top Right Item to show Information
label: Top Right Item
name: tr_item
required: false
type: TEXT
groupName: outeritems
advanced: true
- description: Top Right Icon to show Information
label: Top Right Icon
name: tr_icon
required: false
type: TEXT
groupName: outeritems
advanced: true
- context: item
description: Bottom Left Item to show Information
label: Bottom Left Item
name: bl_item
required: false
type: TEXT
groupName: outeritems
advanced: true
- description: Bottom Left Icon to show Information
label: Bottom Left Icon
name: bl_icon
required: false
type: TEXT
groupName: outeritems
advanced: true
- context: item
description: Bottom Right Item to show Information
label: Bottom Right Item
name: br_item
required: false
type: TEXT
groupName: outeritems
advanced: true
- description: Bottom Right Icon to show Information
label: Bottom Right Icon
name: br_icon
required: false
type: TEXT
groupName: outeritems
advanced: true
- description: Widgetbackground e.g. "transparent" or "blue" or "linear-gradient(to bottom right,#B0E0E6 20%,#1E90FF 30%,#FFC0CB 60%)"
label: Background
name: background1
required: false
type: TEXT
groupName: layout
advanced: true
- description: Corners/Edges from top-left to bottom left e.g. 15% 5% 8% 3%
label: Corners
name: corners
required: false
type: TEXT
groupName: layout
advanced: true
parameterGroups:
- name: colors
label: Color-Settings
- name: fonts
label: Font-Settings
- name: outeritems
label: Outer-Items
- name: layout
label: Widget-Layout
timestamp: Nov 1, 2022, 7:39:43 PM
component: f7-card
config:
border: no
style:
--f7-card-box-shadow: none
--relVer: Rel_1.8.1_v5
background: '=(props.background1) ? props.background1 : "linear-gradient(122deg, rgba(34,193,195,1) 0%, rgba(253,187,45,1) 100%)"'
background-position: down
background-repeat: no-repeat
background-size: cover
border-radius: '=(props.corners) ? props.corners : "0%"'
font-size: medium
height: auto
margin: 5px
noShadow: true
padding: 0px
width: '=(props.size) ? (props.size) * 1.23 + "px": "400px"'
slots:
default:
- component: f7-card-content
slots:
default:
- component: f7-row
config:
class:
- justify-content-center
resizable-absolute: true
resizableFixed: true
slots:
default:
- component: f7-col
config:
style:
align-items: flex-start
display: '=(props.item) ? "flex" : "flex"'
flex-direction: column-reverse
justify-content: flex-start
left: 10px
margin-left: 02px
margin-top: 5px
padding: 10px
position: absolute
top: 5px
z-index: 2
slots:
default:
- component: oh-icon
config:
icon: =props.tl_icon
style:
bottom: 0%
height: "=props.size ? (Number(props.size)/10) +'px' : '65%'"
left: 0%
transform: translate(-20%,-0%)
- component: Label
config:
style:
font-size: "=props.size ? (Number(props.size)/17) +'px' : '15px'"
text: =(items[props.tl_item].displayState)
- component: f7-col
config:
style:
align-items: flex-end
display: flex
flex-direction: column-reverse
justify-content: flex-end
margin-right: 5px
padding: 10px
position: absolute
right: 10px
top: 10px
z-index: 2
slots:
default:
- component: oh-icon
config:
icon: =props.tr_icon
style:
bottom: 0%
height: "=props.size ? (Number(props.size)/10) +'px' : '65%'"
left: 0%
transform: translate(-0%,-0%)
- component: Label
config:
style:
font-size: "=props.size ? (Number(props.size)/17) +'px' : '15px'"
text: =(items[props.tr_item].displayState)
- component: f7-row
config:
class:
- justify-content-center
resizable-absolute: true
resizableFixed: true
slots:
default:
- component: f7-block
config:
class: thermostat
style:
--f7-block-margin-vertical: 0px
--f7-block-padding-horizontal: 0px
--f7-block-padding-vertical: 0px
background: "=props.colorThermostat ? props.colorThermostat : 'var(--f7-toggle-inactive-color)'"
border: 2px solid rgb(64, 60, 77)
border-radius: 50%
box-sizing: content-box
flex-shrink: 0
padding-left: 0px
padding-top: "=props.size ? props.size + 'px': '100%'"
width: "=props.size ? Number(props.size)+'px' : '100%'"
slots:
default:
- component: f7-block
config:
class: bar
style:
border-radius: 50%
height: "=props.size ? (Number(props.size)*0.89) +'px' : '89%'"
left: 50%
margin-top: 0px
position: absolute
top: 50%
transform: translate(-50%, -50%)
width: "=props.size ? (Number(props.size)*0.89) +'px' : '89%'"
slots:
default:
- component: f7-block
config:
class: inner_bar
style:
background-color: "=props.colorThermostat ? props.colorThermostat : 'var(--f7-toggle-inactive-color)'"
border-radius: 100%
height: "=props.size ? (Number(props.size)*0.86) +'px' : '97%'"
left: 50%
margin-top: 0
position: absolute
top: 50%
transform: translate(-50%, -50%)
width: "=props.size ? (Number(props.size)*0.86) +'px' : '97%'"
z-index: 4 !important
slots:
default:
- component: f7-block
config:
style:
background: "='conic-gradient(transparent 0deg 160deg, ' + (props.colorThermostat ? props.colorThermostat : 'var(--f7-toggle-inactive-color)') + ' 160deg 200deg, transparent 200deg 360deg)'"
bottom: "=props.size ? '-7px' : '-7px'"
content: ""
display: block
height: 100%
left: 50%
position: absolute
transform: translate(-50%)
width: 100%
- component: f7-block
config:
class: hold left
style:
background-color: rgb(58, 55, 73)
border-radius: 100%
clip-path: "=props.size ? 'inset(0px 0px 0px ' + (Number(props.size)*0.89/2) + 'px)' : 'inset(0% 0% 0% 50%)'"
height: 100%
margin-top: 0px
position: absolute
width: 100%
slots:
default:
- component: f7-block
config:
class: fill fill1
style:
background: "=props.colorBarStartPoint && props.colorBarEndPoint ? '-webkit-linear-gradient(top, ' + props.colorBarEndPoint + ' 20%,' + props.colorBarEndPoint + ' 100%)' : '-webkit-linear-gradient(top, rgb(255, 73, 0) 20%,rgb(255, 73, 0) 100%)'"
border-radius: 100%
clip-path: "=props.size ? 'inset(0px ' + (Number(props.size)*0.89/2) + 'px 0px 0px)' : 'inset(0% 50% 0% 0%)'"
height: 100%
margin-top: 0px
position: absolute
transform: "=(items[props.setPointItem].state.split(' ')[0] >= (((Number(props.maxTemp) - Number(props.minTemp)) / 2) + Number(props.minTemp)) && items[props.setPointItem].state.split(' ')[0] <= Number(props.maxTemp) ? 'rotate('+(320/(Number(props.maxTemp)-Number(props.minTemp))*(items[props.setPointItem].state.split(' ')[0]-Number(props.minTemp))-160)+'deg)' : (items[props.setPointItem].state.split(' ')[0] > Number(props.maxTemp)) ? 'rotate(180deg)' : '')"
transition: transform 0.6s
width: 100%
z-index: 1 !important
- component: f7-block
config:
class: hold right
style:
background-color: rgb(58, 55, 73)
border-radius: 100%
clip-path: "=props.size ? 'inset(0px 0px 0px ' + (Number(props.size)*0.89/2) + 'px)' : 'inset(0% 0% 0% 50%)'"
height: 100%
margin-top: 0px
position: absolute
transform: rotate(180deg)
width: 100%
z-index: 3 !important
slots:
default:
- component: f7-block
config:
class: fill
style:
animation: right 1s linear both
border-radius: 100%
height: 100%
margin-top: 0px
position: absolute
transition: transform 0.6s
width: 100%
z-index: 3 !important
- component: f7-block
config:
class: fill fill2
style:
background: "=props.colorBarStartPoint && props.colorBarEndPoint ? '-webkit-linear-gradient(top, ' + props.colorBarEndPoint + ' 40%,' + props.colorBarStartPoint + ' 100%)' : '-webkit-linear-gradient(top, rgb(255, 73, 0) 40%,rgb(255, 158, 35) 100%)'"
border-radius: 50%
clip-path: "=props.size ? 'inset(0px '+ (Number(props.size)*0.89/2) + 'px 0px 0px)' : 'inset(0% 50% 0% 0%)'"
height: 100%
margin-top: 0px
position: absolute
transform: "=(items[props.setPointItem].state.split(' ')[0] <= (((Number(props.maxTemp) - Number(props.minTemp)) / 2) + Number(props.minTemp)) && items[props.setPointItem].state.split(' ')[0] >= Number(props.minTemp) ? 'rotate('+(320/(Number(props.maxTemp)-Number(props.minTemp))*(items[props.setPointItem].state.split(' ')[0]-Number(props.minTemp))+20)+'deg)' : (items[props.setPointItem].state.split(' ')[0] > (((Number(props.maxTemp) - Number(props.minTemp)) / 2) + Number(props.minTemp))) ? 'rotate(180deg)' : '')"
width: 100%
z-index: 3 !important
- component: f7-block
config:
class: span
style:
bottom: 0px
color: "=props.colorTypo ? props.colorTypo : 'rgb(87, 84, 95)'"
font-size: "=props.fontSizeFooter ? props.fontSizeFooter : '1em'"
font-weight: "=props.size ? (Number(props.size)*2) +'px' : 'calc(var(--f7-list-item-title-font-weight)*2)'"
margin-top: 0px
position: absolute
text-align: center
text-transform: capitalize
width: "=props.size ? (Number(props.size)*0.89) +'px' : '100%'"
z-index: 99 !important
slots:
default:
- component: Label
config:
style:
font-size: "=props.size ? (Number(props.size)/25) +'px' : '12px'"
text: "Rel.: 1.8.1_v5"
- component: f7-block
config:
class: shadow
style:
animation: shadow 1.4s ease-out both
height: 86%
left: 50%
margin-top: 0px
position: absolute
text-align: center
top: 50%
transform: "=(items[props.setPointItem].state.split(' ')[0] >= Number(props.minTemp) && items[props.setPointItem].state.split(' ')[0] <= Number(props.maxTemp) ? 'translate(-50%, -50%) rotate('+(320/(Number(props.maxTemp)-Number(props.minTemp))*(items[props.setPointItem].state.split(' ')[0]-Number(props.minTemp))-160)+'deg)' : 'translate(-50%, -50%) rotate(0deg)')"
transition: 0.7s ease
width: "=props.size ? (Number(props.size)*0.0625) +'px' : '6.25%'"
slots:
default:
- component: f7-block
config:
class: shadow-cube
style:
box-shadow: "=props.size ? '0 0 ' + (Number(props.size)*0.1125) +'px ' + (Number(props.size)*0.0325) + 'px ' + (props.colorSetMarker ? props.colorSetMarker : 'rgba(255, 158, 35, 0.5)'): '0 0 45px 13px rgba(255, 158, 35, 0.5)'"
height: 0px
margin-top: 0px
position: absolute
top: 0
width: "=props.size ? (Number(props.size)*0.0625) +'px' : '100%'"
- component: f7-block
config:
class: markerContainer
style:
height: 100%
left: 50%
margin-top: 0px
opacity: 1
pointer-events: none
position: absolute
text-align: center
top: 50%
transform: "=(items[props.currentPointItem].state.split(' ')[0] >= Number(props.minTemp) && items[props.currentPointItem].state.split(' ')[0] <= Number(props.maxTemp) ? 'translate(-50%, -50%) rotate('+(320/(Number(props.maxTemp)-Number(props.minTemp))*(items[props.currentPointItem].state.split(' ')[0]-Number(props.minTemp))-160)+'deg)' : 'translate(-50%, -50%) rotate(0deg)')"
transition: 0.7s ease
width: "=props.size ? (Number(props.size)*0.1) +'px' : '10%'"
z-index: 99 !important
slots:
default:
- component: f7-block
config:
class: markerCurrent
style:
background: "=props.colorCurrentMarker ? props.colorCurrentMarker : 'rgb(33, 150, 243)'"
border-radius: 0% 50% 50% 50%
box-shadow: 0 0 5px 1px rgb(48, 46, 56)
height: "=props.size ? (Number(props.size)*0.1) +'px' : ''"
left: 50%
margin-top: 0px
padding-top: "=props.size ? '' : '100%'"
position: absolute
top: "=props.size ? (Number(props.size)*0.14) +'px' : '15%'"
transform: translate(-50%,-50%) rotate(45deg)
width: "=props.size ? (Number(props.size)*0.1) +'px' : '100%'"
slots:
default:
- component: f7-block
config:
class: number
style:
left: 50%
margin-top: 0px
position: absolute
text-align: center
top: 50%
transform: translate(-50%, -50%) rotate(-45deg)
slots:
default:
- component: Label
config:
style:
color: white
font-size: "=props.fontSizeMarker ? props.fontSizeMarker : '1em'"
font-weight: bold
text: =items[props.currentPointItem].state.split(' ')[0]
- component: f7-block
config:
class: markerContainer
style:
height: 100%
left: 50%
margin-top: 0px
opacity: 1
pointer-events: none
position: absolute
text-align: center
top: 50%
transform: "=(items[props.setPointItem].state.split(' ')[0] >= Number(props.minTemp) && items[props.setPointItem].state.split(' ')[0] <= Number(props.maxTemp) ? 'translate(-50%, -50%) rotate('+(320/(Number(props.maxTemp)-Number(props.minTemp))*(items[props.setPointItem].state.split(' ')[0]-Number(props.minTemp))-160)+'deg)' : 'translate(-50%, -50%) rotate(0deg)')"
transition: 0.7s ease
width: "=props.size ? (Number(props.size)*0.1) +'px' : '10%'"
z-index: 99 !important
slots:
default:
- component: f7-block
config:
class: markerSet
style:
background: "=props.colorSetMarker ? props.colorSetMarker : 'rgb(230, 74, 25)'"
border-radius: 50% 50% 50% 0
box-shadow: 0 0 5px 1px rgb(48, 46, 56)
height: "=props.size ? (Number(props.size)*0.1) +'px' : ''"
left: 50%
margin-top: 0px
padding-top: "=props.size ? '' : '100%'"
position: absolute
top: "=props.size ? (Number(props.size)*Number(-0.0125)) +'px': '-2%'"
transform: translate(-50%,-50%) rotate(-45deg)
width: "=props.size ? (Number(props.size)*0.1) +'px' : '100%'"
z-index: 100 !important
slots:
default:
- component: f7-block
config:
class: number
style:
left: 50%
margin-top: 0px
position: absolute
text-align: center
top: 50%
transform: translate(-50%, -50%) rotate(45deg)
slots:
default:
- component: Label
config:
style:
color: white
font-size: "=props.fontSizeMarker ? props.fontSizeMarker : '1em'"
font-weight: bold
text: =items[props.setPointItem].state.split(' ')[0]
- component: f7-block
config:
class: center
style:
background: "=props.colorControlRing ? props.colorControlRing : 'rgb(227, 228, 237)'"
border-radius: 50%
box-shadow: 0px 15px 35px 11px rgba(46, 44, 58,0.60)
height: "=props.size ? (Number(props.size)*0.65) +'px' : '65%'"
left: 50%
margin-top: 0px
position: absolute
top: 50%
transform: translate(-50%, -50%)
width: "=props.size ? (Number(props.size)*0.65) +'px' : '65%'"
slots:
default:
- component: oh-icon
config:
icon: "=(items[props.batteryItem].state === 'OFF') ? 'lowbattery1-off' : 'lowbattery1-on'"
item: =props.batteryItem
style:
bottom: 32%
height: 48%
left: 76%
position: absolute
transform: translate(-55%,-48%) rotate(-140deg)
width: 15%
visible: "=props.batteryItem ? true : false"
- component: oh-icon
config:
icon: "=(items[props.batteryItem1].state === 'OFF') ? 'lowbattery1-off' : 'lowbattery1-on'"
item: =props.batteryItem1
style:
bottom: 32%
height: 48%
left: 25%
position: absolute
transform: translate(-55%,-48%) rotate(-39deg)
width: 15%
visible: "=props.batteryItem1 ? true : false"
- component: oh-icon
config:
icon: "=(items[props.windowItem].state === 'OPEN') ? 'window-open' : 'window-closed'"
style:
bottom: 40%
height: 50%
left: 50%
position: absolute
transform: translate(-50%,-50%)
width: 20%
visible: "=props.windowItem ? true : false"
- component: f7-block
config:
class: buttonContainer
style:
display: "=props.deviceModeItem ? '' : 'none'"
height: 100%
left: 50%
margin-top: 0px
opacity: 1
position: absolute
text-align: center
top: 50%
transform: "=props.heatingModeItem ? 'translate(-50%, -50%) rotate(40deg)' : 'translate(-50%, -50%)'"
transition: 0.7s ease
width: "=props.size ? (Number(props.size)*0.15) +'px' : '15%'"
z-index: 99 !important
slots:
default:
- component: oh-button
config:
popoverOpen: ='.' + props.location + '.popoverHeatingMode'
style:
bottom: 0%
color: "=props.colorButton ? props.colorButton + ' !important': ''"
height: 20%
left: 50%
position: absolute
transform: translate(-50%)
width: 100%
slots:
default:
- component: f7-icon
config:
f7: "=props.deviceModeItem ? (items[props.deviceModeItem].state == props.deviceModeArray.split(',')[0] ? 'hand_raised' : items[props.deviceModeItem].state == props.deviceModeArray.split(',')[1] ? 'arrow_2_squarepath' : items[props.deviceModeItem].state == props.deviceModeArray.split(',')[2] ? 'airplane' : 'thermometer') : ''"
style:
font-size: "=props.fontSizeButtons ? props.fontSizeButtons : '2em'"
margin-top: auto
position: absolute
top: 50%
transform: translate(-50%, -50%)
- component: f7-popover
config:
class: =props.location + ' popoverHeatingMode'
slots:
default:
- component: f7-card
config:
action: variable
actionVariable: myVar
actionVariableValue: success
class:
- popover-close
clearVariable: true
noShadow: true
slots:
default:
- component: f7-row
config: {}
slots:
default:
- component: f7-col
slots:
default:
- component: oh-repeater
config:
containerStyle:
width: 100%
for: buttonlabel
in: =props.deviceModeArray.split(",")
slots:
default:
- component: oh-button
config:
action: command
actionCommand: =loop.buttonlabel
actionItem: "=props.deviceModeItem ? props.deviceModeItem : ''"
active: "=props.deviceModeItem ? (items[props.deviceModeItem].state === loop.buttonlabel ? true : false) : ''"
class: margin
color: "=props.colorButton ? props.colorButton : ''"
outline: true
text: =loop.buttonlabel
- component: f7-block
config:
class: buttonContainer
style:
display: "=props.heatingModeItem ? '' : 'none'"
height: 100%
left: 50%
margin-top: 0px
opacity: 1
position: absolute
text-align: center
top: 50%
transform: "=props.deviceModeItem ? 'translate(-50%, -50%) rotate(-40deg)' : 'translate(-50%, -50%)'"
transition: 0.7s ease
width: "=props.size ? (Number(props.size)*0.15) +'px' : '15%'"
z-index: 99 !important
slots:
default:
- component: oh-button
config:
popoverOpen: ='.' + props.location + '.popoverExampleMode'
style:
bottom: 0%
color: "=props.colorButton ? props.colorButton + ' !important': ''"
height: 20%
left: 50%
position: absolute
transform: translate(-50%)
width: 100%
slots:
default:
- component: f7-icon
config:
f7: "=props.heatingModeItem ? (items[props.heatingModeItem].state == props.heatingModeArray.split(',')[0] ? 'thermometer' : items[props.heatingModeItem].state == props.heatingModeArray.split(',')[1] ? 'thermometer_sun' : items[props.heatingModeItem].state == props.heatingModeArray.split(',')[2] ? 'rocket' : items[props.heatingModeItem].state == props.heatingModeArray.split(',')[3] ? 'gauge' : items[props.heatingModeItem].state == props.heatingModeArray.split(',')[4] ? 'gauge_badge_minus' : 'wrench') : ''"
style:
font-size: "=props.fontSizeButtons ? props.fontSizeButtons : '2em'"
margin-top: auto
position: absolute
top: 50%
transform: translate(-50%, -50%)
- component: f7-popover
config:
class: =props.location + ' popoverExampleMode'
slots:
default:
- component: f7-card
config:
action: variable
actionVariable: myVar
actionVariableValue: success
class:
- popover-close
clearVariable: true
noShadow: true
slots:
default:
- component: f7-row
config: {}
slots:
default:
- component: f7-col
slots:
default:
- component: oh-repeater
config:
containerStyle:
width: 100%
for: buttonlabel
in: =props.heatingModeArray.split(",")
slots:
default:
- component: oh-button
config:
action: command
actionCommand: =loop.buttonlabel
actionItem: "=props.heatingModeItem ? props.heatingModeItem : ''"
active: "=props.heatingModeItem ? (items[props.heatingModeItem].state === loop.buttonlabel ? true : false) : ''"
class: margin
color: "=props.colorButton ? props.colorButton : ''"
outline: true
text: =loop.buttonlabel
- component: oh-button
config:
action: command
actionCommand: "=Number(items[props.setPointItem].state.split(' ')[0]) > Number(props.minTemp) ? Number(items[props.setPointItem].state.split(' ')[0]) - 0.5 : ''"
actionItem: =props.setPointItem
style:
color: "=props.colorButton ? props.colorButton : ''"
height: 50%
left: 10%
margin-top: 0px
position: absolute
top: 50%
transform: translate(-50%, -50%)
width: 30%
slots:
default:
- component: f7-icon
config:
f7: arrow_turn_left_down
style:
font-size: "=props.fontSizeButtons ? props.fontSizeButtons : '2em'"
margin-top: auto
position: absolute
top: 50%
transform: translate(-50%, -50%)
- component: oh-button
config:
action: command
actionCommand: "=Number(items[props.setPointItem].state.split(' ')[0]) < Number(props.maxTemp) ? Number(items[props.setPointItem].state.split(' ')[0]) + 0.5 : ''"
actionItem: =props.setPointItem
style:
color: "=props.colorButton ? props.colorButton : ''"
height: 50%
left: 90%
margin-top: 0px
position: relative
top: 50%
transform: translate(-50%, -50%)
width: 30%
slots:
default:
- component: f7-icon
config:
f7: arrow_turn_right_up
style:
font-size: "=props.fontSizeButtons ? props.fontSizeButtons : '2em'"
margin-top: auto
position: absolute
top: 50%
transform: translate(-50%, -50%)
- component: f7-block
config:
class: valveAnimation
style:
animation: skeleton-effect-fade 2s linear infinite
background: "=props.colorSetMarker ? 'radial-gradient(' + props.colorSetMarker +' 30%, transparent 50%)' : 'radial-gradient(var(--f7-theme-color) 30%, transparent 50%)'"
border-radius: 50%
display: "=(props.valveItem && items[props.valveItem].state == 'ON' )? '' : 'none'"
height: 100%
left: 50%
margin-top: 0px
pointer-events: none
position: absolute
top: 50%
transform: translate(-50%, -50%)
width: 100%
z-index: -100 !important
- component: f7-block
config:
class: small
style:
animation: bound-in-small 0.6s ease forwards
background: "=props.colorCenter ? props.colorCenter : 'rgb(248, 249, 250)'"
border-radius: 50%
box-shadow: 0px 5px 10px 5px rgba(96, 93, 111,0.19)
height: "=props.size ? (Number(props.size)*0.375) +'px' : '57.6%'"
left: 50%
margin-top: 0px
position: absolute
text-align: center
top: 50%
transform: translate(-50%, -50%)
width: "=props.size ? (Number(props.size)*0.375) +'px' : '57.6%'"
z-index: 100 !important
slots:
default:
- component: f7-block
config:
class: heat
style:
color: "=props.colorTypo ? props.colorTypo : 'rgb(87, 84, 95)'"
font-size: "=props.size ? (Number(props.size)*0.0375) +'px' : '14px'"
font-weight: 300
slots:
default:
- component: Label
config:
text: =props.location
- component: f7-block
config:
class: heat
style:
color: "=props.colorTypo ? props.colorTypo : 'rgb(87, 84, 95)'"
font-size: "=props.fontSizeCenter ? props.fontSizeCenter : '2em'"
font-weight: 300
slots:
default:
- component: Label
config:
text: "=props.unit ? items[props.setPointItem].state.split(' ')[0] + '' + props.unit : items[props.setPointItem].state.split(' ')[0]"
- component: oh-image
config:
style:
left: 50%
position: absolute
transform: translate(-50%)
width: 40%
url: "=props.imgUrl ? props.imgUrl : 'https://community-openhab-org.s3-eu-central-1.amazonaws.com/original/2X/7/7d388a86c95471f89b1bb911d96d7609a3e3a059.svg'"
- component: f7-icon
config:
f7: power
style:
bottom: -30%
color: '=(items[props.valveItem].state == "OFF") ? props.colorButton : "red"'
font-size: "=props.fontSizeButtons ? props.fontSizeButtons : '2em'"
left: 50%
position: absolute
transform: translate(-50%)
width: 40%
visible: "=props.valveItem ? true : false"
- component: oh-button
config:
action: command
actionCommand: '=(props.valveItem && items[props.valveItem].state == "OFF") ? "ON" : "OFF"'
actionItem: =props.valveItem
style:
--f7-button-fill-hover-bg-color: transparent
--f7-button-hover-bg-color: transparent
bottom: -30%
left: 50%
position: absolute
transform: translate(-50%)
width: 40%
visible: "=props.valveItem ? true : false"
- component: f7-row
config:
style:
color: '=(props.textcolor1) ? props.textcolor1 : "darkgreen"'
display: true
font-size: 10px
height: 5px
justify-content: flex-end
slots:
default:
- component: f7-col
config:
style:
align-items: flex-start
bottom: 5px
display: flex
flex-direction: column
justify-content: flex-start
left: 5px
margin-left: 10px
padding: 10px
position: absolute
z-index: 2
slots:
default:
- component: oh-icon
config:
icon: =props.bl_icon
style:
bottom: 0%
height: "=props.size ? (Number(props.size)/10) +'px' : '65%'"
left: 0%
transform: translate(-20%,-0%)
- component: Label
config:
style:
font-size: "=props.size ? (Number(props.size)/17) +'px' : '15px'"
text: =(items[props.bl_item].displayState)
- component: f7-col
config:
style:
align-items: flex-end
bottom: 5px
display: flex
flex-direction: column
justify-content: flex-end
margin-right: 10px
padding: 10px
position: absolute
right: 5px
z-index: 2
slots:
default:
- component: oh-icon
config:
icon: =props.br_icon
style:
bottom: 0%
height: "=props.size ? (Number(props.size)/10) +'px' : '65%'"
left: 0%
transform: translate(-0%,-0%)
- component: Label
config:
style:
font-size: "=props.size ? (Number(props.size)/17) +'px' : '15px'"
text: =(items[props.br_item].displayState)
and an example-code for Widget-settings:
component: widget:Heating-Control_1.8.1_v5
config:
background1: conic-gradient(from 90deg,pink,lightblue,#1E90FF)
batteryItem: radiator_valve_02_Battery
batteryItem1: HmIP_SWDO_689A_0LOWBAT
bl_icon: screen
bl_item: Gosund_socket_05_Leistung
br_icon: screen
br_item: Gosund_socket_05
colorBarEndPoint: darkorange
colorBarStartPoint: yellow
colorButton: teal
colorCenter: lightcyan
colorControlRing: powderblue
colorCurrentMarker: lightseegreen
colorSetMarker: salmon
colorThermostat: lightblue
colorTypo: green
corners: 15% 5% 15% 0%
currentPointItem: radiator_valve_02_Temp
deviceModeArray: MANUAL,AUTOMATIC,VACATION
deviceModeItem: radiator_valve_02_ModeX
fontSizeButtons: 1.5em
fontSizeCenter: 1.5em
fontSizeFooter: 0.6em
fontSizeMarker: 0.9em
heatingModeArray: ECO,COMFORT,BOOST,ON,OFF,WINDOW_OPEN
heatingModeItem: radiator_valve_02_Mode
imgUrl: /static/picture/OpenHAB_logo_2.svg
location: York
maxTemp: "30"
minTemp: "6"
setPointItem: radiator_valve_02_Set
size: "270"
tl_icon: '=items.Sonoff_Basic_04.state == "OFF" ? "tablelamp1-off " : "tablelamp1-on"'
tl_item: Sonoff_Basic_04
tr_icon: '=items.Sonoff_T1_01.state == "OFF" ? "lamp_globe_light-off" : "lamp_globe_light-on"'
tr_item: Sonoff_T1_01
unit: °C
valveItem: Sonoff_Basic_04
windowItem: HmIP_SWDO_689A_1STATECONTACT
On temperatures formatting, let me suggest you should be using OH core features rather than to adjust output in the widget:
Use displayState rather than state. Thatâll format the temperature according to the userâs locale and whatever format is applied to the Number:Temperature
item.
That works through either Add metadata - State description - pattern or the â[âŠ]â in the .items file.
If you add say %.1f °C
thatâll be run through locale settings in turn so with an e.g. German locale, you will be not be getting the literal .
from the pattern but a ,
(comma) instead of the point used in the default locale.
As that might be unset for some, you can use it only if set.
The main label around l972 would need to be
=(items[props.currentPointItem].displayState||items[props.currentPointItem].state).split(' ')[0]
Likewise for the ring displays lines 520, 574.
There is an existing simplification in the UI @ysc introduced.
You now can use
@props.currentPointItem
instead of
items[props.currentPointItem].displayState
Which will try to get the displayState first and if not set, will show the state of the item.
Youâre never done learning, thanks.
EDIT: didnât work for me, though. Is this new in 3.4.1 or 4 ?
Mind to put brackets on the part before .split
Hi there.
I had a few issues with this widget (v 2.0.0):
- Temperature showed NaN
- Both temp and humidity showed the following error message: âType Error: Undefined has to propertiesâ
- Graphically the inner circle with the temp and humid readings became a very narrow ellipse rather than circle, making it impossible to see the text.
Here are all the issues in their glory:
I tried some of the suggestions here, and I didnât get anyone to work, so I made my own.
My solutions:
- Simplify row 966 as seen in the diff screen shot
- Remove unit text all together as seen in the diff screen shot (since my item already includes the unit)
- Make sure to configure the size to 400 px. Otherwise it scales dynamically with your canvas, which seems to screw it up.
I hope this helps someone. Maybe the NaN issue people have reported is the same as mine?
Oh oh⊠I also have the same issue. Gonna try this thank you!
I have copied your code and finished your adaptions to OH4 (item units).
oh4-widgetHeating.txt (71.2 KB)
I like your heating widget
Hi all. First of all thanks a lot for this superb widget!
I have two problems I would like to get your opinion on. First, occasionally, I get widgets that look like this:
The error message says: Type error: cannot read properties of undefined (reading 'split')
Whatâs going on there? On the ring, both the current temperature and the setpoint are shown correctly.
=====
Second, I get this:
My items are defined with this state description pattern:
stateDescription=""[pattern="%.1f"]
But apparently, that does not help. How can I round this to a single digit after the period?
Any help would be greatly appreciated!
Same problem here (NaN as a temp reading), latest version didnât solve the priblem.
Running 4.3.0M2
I didnât check the code you need to scan it yourself but this happens when you display item.state
instead of item.displayState
.
Thanks, I just tried this but that does not seem to do the trick, unfortunately.
Like Eric, also did not do the trick here.
How is your item definded? Please show the complete item config.