Hey guys, @fibu-freak , @doctor64 , @Peters_Bastelecke
Iāam in progress to implement the some enhancements.
Thanks for the posts, the good feedback and the patience.
It will take a little bit time.
all the best,
Nico
Hey guys, @fibu-freak , @doctor64 , @Peters_Bastelecke
Iāam in progress to implement the some enhancements.
Thanks for the posts, the good feedback and the patience.
It will take a little bit time.
all the best,
Nico
Yes, of course some local modification sometimes needed. I, for example, use MAX! EQ-3 thermostats and zigbee Xiaomi window sensors.
About heater item - i also donāt use electric heating and my thermostat does not return heated state, but it can report valve open state. So I create additional item make simple rule:
Number GF_Living_Radiator_Valve "Valve position [%d %%]" <flowpipe> (GF_Living_Thermostat, MaxValve) ["OpenLevel"] {channel="max:thermostat:NEQ1208352:thermostatLiving:valve"}
String GF_Living_Radiator_Heated "Heated [%s]" <heating> (GF_Living_Thermostat) ["Status"]
rule "Heating indication"
when
Member of gMaxValve changed
then
val changedValve = triggeringItem
val String[] thermostatNameArr=changedValve.name.split("_")
val String thermostatIndicator=thermostatNameArr.get(0)+"_"+thermostatNameArr.get(1)+"_"+thermostatNameArr.get(2)+"_"+"Heated"
logInfo("HeatingUpdate", "triggered=" + changedValve + " Indicator name:" + thermostatIndicator)
if (changedValve.state > 0 ) thermostatIndicator.sendCommand("ON") else thermostatIndicator.sendCommand("OFF")
end
And it gives me fancy icon on location card and on heating widget.
May be it will be useful, I donāt know is your thermostat able to return valve open status.
Sorry, can not check right now, but looks like you need to change line
from: text: "=props.unit ? items[props.setPointItem].state.split(' ')[0] + '' + props.unit : items[props.setPointItem].state.split(' ')[0]"
to: text: "=props.unit ? items[props.currentPointItem].state.split(' ')[0] + '' + props.unit : items[props.currentPointItem].state.split(' ')[0]"
Itās difficult to say correct line number because i have modified widget source at hand, but it near end of widget code. Hope this helps.
Hi Alex,
thx for this tip, - it works
Hi guys,
Thanks again for the positive feedback. I am pleased that so many people are interested in the widget.
Hi @fibu-freak,
based on your suggestion (Heating widget - #18 by fibu-freak) with a list-card
, i have inserted two optional setting buttons. I think this fits in better with the design and has even more potential. See under the enhancements Heating-Mode- Itemand Example-Mode-Item
I hope you like the solution
The implementation of humidity will follow.
The Logo-URL can be an Web-Adress or an static-path. I define a prop for the Image-URL. More under enhancements.
Hi @doctor64,
The design should be better now for small widget. You can define the size of the widget and now the size of all fonts.
In general: The implementation of a battery indicator will follow
I hope you all like the new version v1.8. Have fun and have a nice weekend.
all the best,
Nico
v1.8
Enhancements:
Toogle item is no longer applicable in the widget. If this is required, it can also be implemented using buttons
The url for the logo can be defined in props and is no longer static in the code.
Tipp: Instead of an Internet address, a logo can also be saved in the folder.
Then use in the prop the follow path (eg):
http://local-IP-Adress:8080/static/folder/img.svg
All font sizes can be defined using parameters. This means that even small widgets can be implemented responsively.
Animation added in the center that can be used to visualize a valve item or an alarm item
Now the item must be of the type Switch, but itās easy to switch to an Contact-Item if you want.
Bug-fixing:
Here the v1.8 YAML. Too many characters for one post
Edit: please use the v1.8.1. The v1.8 YAML is removed
Hello, the widget works great! However I have a problem in my setup with the heating mode items. With one single instance of the widget, it works great but when I add a second (or more) the heating mode status wonāt be passed to the defined item.
The second widget will change last added widget heating mode item instead of their own widget item.
I have tried to figure it out myself but I lack knownlegde about YAML. I am not sure if the problem is in my setup or in the widget. I hope you can help me to get on the right track.
Below I have added information of my setup and problem.
This is my first post and could not figure out yet to add an animationā¦ so here a link to the gif to clarify my problem:
https://u.pcloud.link/publink/show?code=kZp0cjXZfNTPeH2aYSu3IRuuv8ayv4FGHQ7V
String HW_BG_Heating_Mode "Verwarmings Modus [%s]" (g_HeatingMode)
String HW_EV_Heating_Mode "Verwarmings Modus [%s]" (g_HeatingMode)
String BW_Heating_Mode "Verwarmings Modus [%s]" (g_HeatingMode)
String BW_SK_Heating_Mode "Verwarmings Modus [%s]" (g_HeatingMode)
- component: oh-block
config:
title: Hoofdwoning
slots:
default:
- component: oh-grid-row
config: {}
slots:
default:
- component: oh-grid-col
config: {}
slots:
default:
- component: widget:wThermostatGauge_V1.8
config:
location: Beganegrond
size: "300"
minTemp: "15"
maxTemp: "25"
setPointItem: HW_BG_Temperatuur_Setpoint
currentPointItem: gT_HW_BG
unit: Ā°C
imgUrl: http://10.0.0.30:8080/static/_empty.png
heatingModeArray: Automatisch,Handmatig,Uit
valveItem: gHA_HW_BG
fontSizeMarker: .75em
fontSizeCenter: 2.6em
fontSizeButtons: 1,8em
fontSizeFooter: 1em
heatingModeItem: HW_BG_Heating_Mode
- component: oh-grid-col
config: {}
slots:
default:
- component: widget:wThermostatGauge_V1.8
config:
location: Eerste verdieping
size: "300"
minTemp: "15"
maxTemp: "25"
setPointItem: HW_EV_Temperatuur_Setpoint
currentPointItem: gT_HW_EV
unit: Ā°C
imgUrl: http://10.0.0.30:8080/static/_empty.png
heatingModeArray: Automatisch,Handmatig,Uit
heatingModeItem: HW_EV_Heating_Mode
valveItem: gHA_HW_EV
fontSizeMarker: .75em
fontSizeCenter: 2.6em
fontSizeButtons: 1,8em
fontSizeFooter: 1em
- component: oh-grid-row
config: {}
slots:
default:
- component: oh-grid-col
config: {}
slots:
default:
- component: oh-list-card
config: {}
slots:
default:
- component: oh-slider-item
config:
item: HW_BG_Temperatuur_Setpoint
min: 15
max: 25
step: 0.5
scale: true
- component: oh-slider-item
config:
item: gT_HW_BG
min: 15
max: 25
step: 0.5
scale: true
- component: oh-label-item
config:
item: gHA_HW_BG
- component: oh-label-item
config:
item: HW_BG_Heating_Mode
- component: oh-grid-col
config: {}
slots:
default:
- component: oh-list-card
config: {}
slots:
default:
- component: oh-slider-item
config:
item: HW_EV_Temperatuur_Setpoint
min: 15
max: 25
step: 0.5
scale: true
- component: oh-slider-item
config:
item: gT_HW_EV
min: 15
max: 25
step: 0.5
scale: true
- component: oh-label-item
config:
item: gHA_HW_EV
- component: oh-label-item
config:
item: HW_EV_Heating_Mode
Hello, the widget works great. a warning message only ever comes up when the page is called up for the first time. It doesnāt matter whether I have entered items or not.
2021-02-21 10:56:35.824 [WARN ] [e.internal.SseItemStatesEventBuilder] - Attempting to send a state update of an item which doesn't exist: undefined
Hi @RowanS
sorry here is a bug in the popover code. I define a identifier to control this proplem.
Now the location props is required and this prop is the identifier if you use more than one widget.
@Tallman I cant reproduce this error. Pls try again and check all needed and referenced items.
all the best,
Nico
uid: widget_HeatingCSS_1.8.1
tags: []
props:
parameters:
- description: eg. living room
label: location and widget identifier
name: location
required: true
type: TEXT
- 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
- 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 control
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: 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
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
advanced: true
- context: item
description: Heating Mode Item [String-Item]
label: Heating Mode Item
name: heatingModeItem
required: false
type: TEXT
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
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
- 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
parameterGroups:
- name: colors
label: Color-Settings
- name: fonts
label: Font-Settings
timestamp: Feb 22, 2021, 12:22:59 PM
component: f7-card
config:
title: "=(props.location) ? 'Klima ' + props.location : ''"
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:
flex-shrink: 0
--f7-block-margin-vertical: 0px
--f7-block-padding-vertical: 0px
--f7-block-padding-horizontal: 0px
padding-left: 0px
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)'"
border-radius: 50%
box-sizing: content-box
border: 2px solid rgb(64, 60, 77)
slots:
default:
- component: f7-block
config:
class: bar
style:
margin-top: 0px
position: absolute
width: "=props.size ? (Number(props.size)*0.89) +'px' : '89%'"
height: "=props.size ? (Number(props.size)*0.89) +'px' : '89%'"
top: 50%
left: 50%
transform: translate(-50%, -50%)
border-radius: 50%
slots:
default:
- component: f7-block
config:
class: inner_bar
style:
margin-top: 0
position: absolute
top: 50%
left: 50%
transform: translate(-50%, -50%)
width: "=props.size ? (Number(props.size)*0.86) +'px' : '97%'"
height: "=props.size ? (Number(props.size)*0.86) +'px' : '97%'"
border-radius: 100%
background-color: "=props.colorThermostat ? props.colorThermostat : 'var(--f7-toggle-inactive-color)'"
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)'"
content: ""
display: block
position: absolute
width: 100%
height: 100%
bottom: "=props.size ? '-7px' : '-7px'"
left: 50%
transform: translate(-50%)
- 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-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: "=(items[props.heatingModeItem].state === loop.buttonlabel ? true : false)"
actionCommand: =loop.buttonlabel
actionItem: =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: "=(items[props.exampleModeItem].state === loop.buttonlabel ? true : false)"
actionCommand: =loop.buttonlabel
actionItem: =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.5 : ''"
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.5 : ''"
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.size ? (Number(props.size)*0.0375) +'px' : '14px'"
color: "=props.colorTypo ? props.colorTypo : 'rgb(87, 84, 95)'"
font-weight: 300
slots:
default:
- component: Label
config:
text: =props.location
- component: f7-block
config:
class: heat
style:
font-size: "=props.fontSizeCenter ? props.fontSizeCenter : '2em'"
color: "=props.colorTypo ? props.colorTypo : 'rgb(87, 84, 95)'"
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:
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%
width: 40%
Nice job, but Iāve small wish: can You display .displayState (if defined) instead .state, so that proper formatting will be possible, for setPointItem and currentPointItem ? I know I can do this myself, but for each of Your great new versions, again and again ā¦
/Sas
The error message comes when the page containing the widget is opened in the browser and the Heating / Example Mode item is not linked to an item.
Hi @Tallman
thanks for the details.
Iāve found the little bug into the code. The error is by the defintion of the F7-Icon if no heatingModeItm is defined.
sorry for this.
I change the post above with version v1.8.1
I also included the array string for the icon selection.
Please give me a feedback if it works.
Change the line 510
from:
f7: "=(items[props.heatingModeItem].state == 'Manuell' ? 'hand_raised' : items[props.heatingModeItem].state == 'Auto' ? 'arrow_2_squarepath' : items[props.heatingModeItem].state == 'Vacation' ? 'airplane' : 'thermometer')"
to:
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') : ''"
It works great now! Thank you very much! The only thing I noticed was the location prop needs to be one single phrase, with a broken phrase (like a space in between two words) the pop-up wonāt show. For me that is no big deal but just wanted to let you know.
It works, good work keep it up
Hi Nico,
fantastic The additions work all well. Now there are nearly all design-options possible.
The āpulsing Coronaā is also a very nice feature, which I tested with a toggling switch of my office-lamp.
.
The wish of @Sas_Bibic (Translation) could be another interesting feature to make it more comfortable for users of different languages. But I donāt think that this is only done by changing the āstateā to ādisplayStateā.
For the moment Iām lucky with your solution and nosy for what is coming next.
cheers,
Peter
Dear all
Based on this wonderful widget (Thanks Nico)
I try to create widget for personal weather station
Any suggestion are welcome
Hi @Nico_R
Thank you again for your work.Popup menu looks really cool, and heating animation beautiful.
Unfortunately, I observe strange behavior of heating mode icon. On page open it displays default value (thermometer), when I switch to different mode icon switched to assigned to mode (airplane, hand, etc) but then i switch to another page and back to page with widget icon become again basic thermometer.
Widget version is 1.8.1
I am glad. In which German forum did you post it? Just out of interest because Iām from Germany too.
I tried to reproduce the error but didnāt succeed. When I switch between pages the icons stay as defined.
The icons are firmly defined in the code. The states of the string item are defined in the heatingMode array.
The first array key gets the hand_raised icon, the second key gets arrow_2_squarepath icon and the third key gets airplane. If you use more than 3 keys, the key> 3 always gets the thermometer.
Nico
I just thought, as your examples mostly shows āKlima Wohnzimmerā
Hereās the link for German-Forum, or here (may be you have to sign in to see the screen-shots)
@doctor64 I donāt know what kind of Thermostats you have, but my AVM-Fritz-Thermostats are āread-onlyā in the heating-mode. So if you make a change in the widget it will be set back after some seconds.
Changes are only possible inside the Fritz-Box-Web-UI.
Hi @Nico_R!
Sorry for false alarm, it was my fault. I just by old habit use spaces in heatingMode array, enter this:
AUTOMATIC, MANUAL, BOOST, VACATION
But spaces not ignored here, and of course strings āMANUALā and " MANUAL" not matched, so default icon drawn.
Then I change array to:
AUTOMATIC,MANUAL,BOOST,VACATION
it start working as intended.
I think it will be common problem for people like me, with background in C or similar languages. I put space after comma by reflex! So i think it will be good to note somewhere what spaces not ignored.
Again, thank you very much for your excellent work!
Just a minor issue, and I not sure it related to Heating widget - i got lots of messages in openhab log like this:
[WARN ] [se.internal.SseItemStatesEventBuilder] - Attempting to send a state update of an item which doesn't exist: undefined
Not sure of root source, but get it when I switch to test page with just one widget - heating. Try to define exampleModeItem, (normally i not use it) and seems like warning message stop appearing.