hello i have had a bunch of help from you guys and here im starting new thread instead of continuing my others.
trying to find the simple way to open a valve or close a valve depending on weather or not something needs cooling.
i came across this in flows however it actually doesnt show the flow. just the ui that was created.
there is a picture of a sample flow but no code to back it up.
please help.
its called Ghost Thermostat
https://flows.nodered.org/flow/9ca3a19e0e2ff606bd64f1e73a2191eb
If you scroll-down to the end of the link you posted, there is a flow you can copy and paste into Node-RED.
yes like i said, if you copy and import its just the custom ui node only not the rest of the flow.
so i have everything going into it ok.
not sure how his switch node and the changed nodes are configured. is my issue at the moment.
I even figured out how to increase the ranges so it can reflect "F"
Oh - I didn't realise that.
It looks like the overall flow is fairly simple and the Javascript for the function node is published, so it should be simple to re-create it. It looks like a very useful gauge for the dashboard.
Edit: If I have some spare time today, I'll have a more in-depth look at the flow.
here is my debug node
coming out of the gage.
im not sure what he is switching. and what he is changing after he switched.
here is my flow so far
[{"id":"a1935b0f08af8408","type":"ui_switch","z":"f6858fe416b029b9","name":"Wferm","label":"Wferm","tooltip":"","group":"7dc3530e.8f07a4","order":4,"width":"3","height":"1","passthru":false,"decouple":"true","topic":"cmnd/Wferm/POWER","style":"","onvalue":"ON","onvalueType":"str","onicon":"","oncolor":"","offvalue":"OFF","offvalueType":"str","officon":"","offcolor":"","x":389.6666831970215,"y":180.25003814697266,"wires":[["4349ab2a681252f2","dcb2c21aa32eec71"]]},{"id":"4349ab2a681252f2","type":"mqtt out","z":"f6858fe416b029b9","name":"","topic":"","qos":"","retain":"","respTopic":"","contentType":"","userProps":"","correl":"","expiry":"","broker":"31a99116.50a74e","x":534.2380714416504,"y":215.99999809265137,"wires":[]},{"id":"ce5c66f008f4cbf3","type":"mqtt in","z":"f6858fe416b029b9","name":"","topic":"stat/Wferm/POWER","qos":"1","datatype":"auto","broker":"31a99116.50a74e","nl":false,"rap":false,"inputs":0,"x":196.23805236816406,"y":140.00006866455078,"wires":[["a1935b0f08af8408","5a422122ed052f7d"]]},{"id":"dcb2c21aa32eec71","type":"debug","z":"f6858fe416b029b9","name":"out","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":528.2380905151367,"y":180.0000343322754,"wires":[]},{"id":"5a422122ed052f7d","type":"debug","z":"f6858fe416b029b9","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":393.8022689819336,"y":139.92453384399414,"wires":[]},{"id":"987d16ece3fa2e58","type":"ui_digital_display","z":"f6858fe416b029b9","name":"","group":"7dc3530e.8f07a4","order":2,"width":0,"height":0,"digits":5,"decimals":1,"x":1219.5643310546875,"y":83.93490505218506,"wires":[]},{"id":"c978949d90d42b1e","type":"mqtt in","z":"f6858fe416b029b9","name":"","topic":"tele/Wferm/SENSOR","qos":"2","datatype":"auto","broker":"31a99116.50a74e","nl":false,"rap":true,"rh":0,"inputs":0,"x":194.56424713134766,"y":88.83074188232422,"wires":[["4c4294bd3b2d8cf3","a1bfb15585d7eb59"]]},{"id":"93a3046bdaeff2c8","type":"debug","z":"f6858fe416b029b9","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":1080.5643310546875,"y":48.921030044555664,"wires":[]},{"id":"65f9fd5be1a51434","type":"change","z":"f6858fe416b029b9","name":"set payload to Temperature value","rules":[{"t":"move","p":"payload.Temperature","pt":"msg","to":"payload","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":887.0089340209961,"y":86.00180053710938,"wires":[["987d16ece3fa2e58","93a3046bdaeff2c8","8f8949fff0292467"]]},{"id":"4c4294bd3b2d8cf3","type":"debug","z":"f6858fe416b029b9","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":348.56078338623047,"y":37.92621994018555,"wires":[]},{"id":"a1bfb15585d7eb59","type":"json","z":"f6858fe416b029b9","name":"","property":"payload","action":"","pretty":false,"x":381.00870513916016,"y":89.33507537841797,"wires":[["27d0a195ec58a6a1"]]},{"id":"bb44589f1917700d","type":"switch","z":"f6858fe416b029b9","name":"If =442FBOIL","property":"payload.Id","propertyType":"msg","rules":[{"t":"cont","v":"3C01F096FCA3","vt":"str"},{"t":"else"}],"checkall":"true","repair":false,"outputs":2,"x":648.1198348999023,"y":90.00185489654541,"wires":[["65f9fd5be1a51434"],[]]},{"id":"27d0a195ec58a6a1","type":"split","z":"f6858fe416b029b9","name":"","splt":"\\n","spltType":"str","arraySplt":1,"arraySpltType":"len","stream":false,"addname":"","x":501.11634826660156,"y":89.97662925720215,"wires":[["bb44589f1917700d"]]},{"id":"ab8fb1c28921f1dd","type":"ui_template","z":"f6858fe416b029b9","group":"7dc3530e.8f07a4","name":"GhostThermostat","order":2,"width":"6","height":"6","format":"<style>\n @import url('https://fonts.googleapis.com/css2?family=Roboto:ital,wght@0,100;0,300;0,400;0,500;0,700;0,900;1,100;1,300;1,400;1,500;1,700;1,900&display=swap');\n \n svg {\n transition: all .6s cubic-bezier(0.175, 0.885, 0.32, 1.2);\n }\n\n stop {\n transition: all .5s;\n }\n \n\t.led {\n \t-webkit-transition: all 0.5s;\n \ttransition: all 0.5s;\n \tfill: url(#ledColor);\n }\n \n .fa-text {\n font-family: FontAwesome !important; \n }\n .dial {\n -webkit-user-select: none;\n -moz-user-select: none;\n -ms-user-select: none;\n user-select: none;\n }\n .qGradient {\n fill : url(#qGradient);\n }\n .qGradientT {\n fill : url(#qGradientT);\n }\n .eGradient {\n fill : url(#eGradient);\n }\n .lbl {\n font-family: 'Roboto', sans-serif;\n text-anchor: middle;\n fill : #ffffff;\n clip-path: url(#qClip);\n }\n .lblDial {\n fill: #dddddd;\n }\n \n .lblAmbient {\n font-weight: 400;\n clip-path: url(#qClip);\n }\n \n .lblAmbient tspan {\n font-weight: 400;\n }\n \n .lblTarget {\n font-weight: 400;\n fill: orange;\n }\n \n .lblTarget tspan {\n font-weight: 400;\n fill: orange;\n clip-path: url(#qClip);\n } \n \n .nodisplay {\n display: none !important;\n }\n \n .icon {\n font-family: FontAwesome !important;\n }\n \n .animate {\n transition: all 0.5s;\n }\n\n</style>\n<div id=\"GhostThermostat\"></div> \n<script>\nvar mousedownID = -1;\nvar ghostThermostatDial = (function() {\n\tconsole.log(\"START\");\n\n\tfunction createSVGElement(tag, attributes, appendTo) {\n\t\tvar element = document.createElementNS('http://www.w3.org/2000/svg', tag);\n\t\tattr(element, attributes);\n\t\tif (appendTo) {\n\t\t\tappendTo.appendChild(element);\n\t\t}\n\t\treturn element;\n\t}\n\n\tfunction attr(element, attrs) {\n\t\tfor (var i in attrs) {\n\t\t\telement.setAttribute(i, attrs[i]);\n\t\t}\n\t}\n\n\tfunction setClass(el, className, state) {\n\t\tel.classList[state ? 'add' : 'remove'](className);\n\t}\n\n\treturn function(targetElement, options) {\n\t\tconsole.log(\"RET FUN\");\n\t\tvar self = this;\n\n\t\t/*\n\t\t * Options\n\t\t */\n\t\toptions = options || {};\n\t\toptions = {\n\t\t\tdiameter: options.diameter || 400,\n\t\t\tmintemp: options.mintemp || 0, // Minimum value for target temperature\n\t\t\tmaxtemp: options.maxtemp || 200, // Maximum value for target temperature\n\t\t\tledColors: {\n\t\t\t\t'off': 'rgb(143,141,141)',\n\t\t\t\t'heating': 'rgb(255,128,0)',\n\t\t\t\t'cooling': 'rgb(81,170,214)'\n\t\t\t}, //Led Ring Colors\n\t\t\tlabels: {\n\t\t\t\tambient: \"AMBIENT\",\n\t\t\t\tset: \"SET\",\n\t\t\t\tmode: \"MODE\",\n\t\t\t\tminus: \"-\",\n\t\t\t\tplus: \"+\",\n\t\t\t\tleft: \"<\",\n\t\t\t\tright: \">\"\n\t\t\t},\n\t\t\tonChangeState: options.onChangeState || function() {} // Function called when switch state change\n\t\t};\n\n\t\t/*\n\t\t * Properties\n\t\t */\n\t\tvar properties = {\n\t\t\tradius: options.diameter / 2,\n\t\t\tmodes: [{\n\t\t\t\t\tlabel: \"heating\",\n\t\t\t\t\ticon: \"\\uf06d\",\n\t\t\t\t\tcolor: \"orange\"\n\t\t\t\t}, {\n\t\t\t\t\tlabel: 'cooling',\n\t\t\t\t\ticon: \"\\uf2dc\",\n\t\t\t\t\tcolor: \"rgb(81,170,214)\"\n\t\t\t\t}, {\n\t\t\t\t\tlabel: \"off\",\n\t\t\t\t\ticon: \"\\uf011\",\n\t\t\t\t\tcolor: \"rgb(230,0,0)\"\n\t\t\t\t}\n\t\t\t\t/*, {\n\t\t\t\tlabel: 'away',\n\t\t\t\ticon: \"\\uf1ce\",\n\t\t\t\tcolor: \"gray\"\n\t\t\t} */\n\t\t\t],\n\t\t\tmodeNames: [\"heating\", \"cooling\", \"off\"],\n\t\t\tswtitchStates: [\"heating\", \"cooling\", \"off\"]\n\t\t};\n\n\t\t/*\n\t\t * Object state\n\t\t */\n\t\tvar state = {\n\t\t\ttarget_temperature: options.mintemp,\n\t\t\tambient_temperature: options.maxtemp,\n\t\t\tmode: properties.modes.indexOf(properties.modes[0]),\n\t\t\tswitch_state: 'off',\n\t\t\taway: false\n\t\t};\n\n\t\t/*\n\t\t * Property getter / setters\n\t\t */\n\t\tObject.defineProperty(this, 'target_temperature', {\n\t\t\tget: function() {\n\t\t\t\treturn state.target_temperature;\n\t\t\t},\n\t\t\tset: function(val) {\n\t\t\t\tstate.target_temperature = rangedTemperature(+val);\n\t\t\t\t//render()\n\t\t\t}\n\t\t});\n\n\t\tObject.defineProperty(this, 'ambient_temperature', {\n\t\t\tget: function() {\n\t\t\t\treturn state.ambient_temperature;\n\t\t\t},\n\t\t\tset: function(val) {\n\t\t\t\tstate.ambient_temperature = +val;\n\t\t\t\trender();\n\t\t\t}\n\t\t});\n\n\t\tObject.defineProperty(this, 'mode_name', {\n\t\t\tget: function() {\n\t\t\t\treturn properties.modeNames[state.mode];\n\t\t\t},\n\t\t\tset: function(val) {\n\t\t\t\tif (properties.modeNames.indexOf(val) >= 0) {\n\t\t\t\t\tstate.mode = properties.modeNames.indexOf(val);\n\t\t\t\t\t//render();\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\n\t\tObject.defineProperty(this, 'switch_state', {\n\t\t\tget: function() {\n\t\t\t\treturn state.switch_state;\n\t\t\t},\n\t\t\tset: function(val) {\n\t\t\t\tif (properties.swtitchStates.indexOf(val) >= 0) {\n\t\t\t\t\tstate.switch_state = val;\n\t\t\t\t\t//render();\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\n\n\t\tfunction str2bool(strvalue) {\n\t\t\treturn (strvalue && typeof strvalue == 'string') ? (strvalue.toLowerCase() == 'true') : (strvalue == true);\n\t\t}\n\n\t\tObject.defineProperty(this, 'away', {\n\t\t\tget: function() {\n\t\t\t\treturn state.away;\n\t\t\t},\n\t\t\tset: function(val) {\n\t\t\t\tstate.away = !!str2bool(val);\n\t\t\t\t//render();\n\t\t\t}\n\t\t});\n\n\n\t\t/*\n\t\t * SVG\n\t\t */\n\t\tvar svg = createSVGElement('svg', {\n\t\t\twidth: '100%', //options.diameter+'px',\n\t\t\theight: '100%', //options.diameter+'px',\n\t\t\tviewBox: '0 0 ' + options.diameter + ' ' + options.diameter,\n\t\t\tclass: 'dial'\n\t\t}, targetElement);\n\n\t\t// DEFS \n\t\tvar defs = createSVGElement('defs', null, svg);\n\n\t\tvar qgradient = createSVGElement('linearGradient', {\n\t\t\t'id': 'qGradient',\n\t\t\tgradientTransform: 'rotate(65)'\n\t\t}, defs);\n\t\tvar stop = createSVGElement('stop', {\n\t\t\t'offset': '50%',\n\t\t\t'stop-color': 'rgb(86,89,94)'\n\t\t}, qgradient);\n\t\tvar stop = createSVGElement('stop', {\n\t\t\t'offset': '65%',\n\t\t\t'stop-color': 'rgb(30,30,30)'\n\t\t}, qgradient);\n\n\t\tvar qGradientT = createSVGElement('linearGradient', {\n\t\t\t'id': 'qGradientT',\n\t\t\tgradientTransform: 'rotate(65)'\n\t\t}, defs);\n\t\tvar stop = createSVGElement('stop', {\n\t\t\t'offset': '55%',\n\t\t\t'stop-color': '#3b3e43',\n\t\t\t'stop-opacity': '1'\n\t\t}, qGradientT);\n\t\tvar stop = createSVGElement('stop', {\n\t\t\t'offset': '90%',\n\t\t\t'stop-color': 'rgb(0,0,0)',\n\t\t\t'stop-opacity': '1'\n\t\t}, qGradientT);\n\n\t\tvar clipPath = createSVGElement('clipPath', {\n\t\t\t'id': 'qClip',\n\t\t}, defs);\n\t\tvar circle = createSVGElement('circle', {\n\t\t\tcx: properties.radius,\n\t\t\tcy: properties.radius,\n\t\t\tr: properties.radius - 25\n\t\t}, clipPath);\n\n\n\t\tvar ledRingGradient = createSVGElement('radialGradient', {\n\t\t\t'id': 'ledColor',\n\t\t\t'cx': \"50%\",\n\t\t\t'cy': \"50%\",\n\t\t\t'r': \"95%\",\n\t\t\t'fx': \"50%\",\n\t\t\t'fy': \"50%\"\n\t\t}, defs);\n\t\tvar ledRingGradientColorIn = createSVGElement('stop', {\n\t\t\t'offset': '45%',\n\t\t\t'stop-color': 'rgb(255,0,130)',\n\t\t\t'stop-opacity': '1'\n\t\t}, ledRingGradient);\n\t\tvar ledRingGradientColorOut = createSVGElement('stop', {\n\t\t\t'offset': '65%',\n\t\t\t'stop-color': 'rgb(0,0,0)',\n\t\t\t'stop-opacity': '1'\n\t\t}, ledRingGradient);\n\n\t\tvar egradient = createSVGElement('linearGradient', {\n\t\t\t'id': 'eGradient',\n\t\t\tgradientTransform: 'rotate(55)'\n\t\t}, defs);\n\t\tvar stop = createSVGElement('stop', {\n\t\t\t'offset': '55%',\n\t\t\t'stop-color': '#888888',\n\t\t\t'stop-opacity': '1'\n\t\t}, egradient);\n\t\tvar stop = createSVGElement('stop', {\n\t\t\t'offset': '95%',\n\t\t\t'stop-color': '#333333',\n\t\t\t'stop-opacity': '1'\n\t\t}, egradient);\n\n\t\t// DIAL\n\t\tvar circle = createSVGElement('circle', {\n\t\t\tcx: properties.radius,\n\t\t\tcy: properties.radius,\n\t\t\tr: properties.radius,\n\t\t\tclass: 'eGradient'\n\t\t}, svg);\n\t\tvar ledRing = createSVGElement('circle', {\n\t\t\tcx: properties.radius,\n\t\t\tcy: properties.radius,\n\t\t\tr: properties.radius - 3,\n\t\t\t'stroke': 'black',\n\t\t\t'stroke-width': '1',\n\t\t\tclass: 'led'\n\t\t}, svg);\n\t\tvar circle = createSVGElement('circle', {\n\t\t\tcx: properties.radius,\n\t\t\tcy: properties.radius,\n\t\t\tr: properties.radius - 20,\n\t\t\tclass: 'qGradient'\n\t\t}, svg);\n\t\tvar circle = createSVGElement('circle', {\n\t\t\tcx: properties.radius,\n\t\t\tcy: properties.radius,\n\t\t\tr: properties.radius - 25,\n\t\t\tclass: 'qGradient'\n\t\t}, svg);\n\t\tvar lblMain = createSVGElement('text', {\n\t\t\tx: properties.radius,\n\t\t\ty: 70,\n\t\t\tclass: 'lbl lblDial'\n\t\t}, svg);\n\t\tvar lblMainText = document.createTextNode(options.labels.ambient);\n\t\tlblMain.appendChild(lblMainText);\n\n\t\tvar lblAmbient = createSVGElement('text', {\n\t\t\tx: properties.radius,\n\t\t\ty: 210,\n\t\t\t'font-size': '160',\n\t\t\tclass: 'lbl lblAmbient'\n\t\t}, svg);\n\t\tvar lblAmbientText = document.createTextNode('21');\n\t\tlblAmbient.appendChild(lblAmbientText);\n\t\tvar lblAmbientDec = createSVGElement('tspan', {\n\t\t\t'font-size': '60',\n\t\t}, lblAmbient);\n\t\tvar lblAmbientDecText = document.createTextNode('.5');\n\t\tlblAmbientDec.appendChild(lblAmbientDecText);\n\n\t\tvar line = createSVGElement('line', {\n\t\t\tx1: 55,\n\t\t\ty1: properties.radius + 35,\n\t\t\tx2: options.diameter - 55,\n\t\t\ty2: properties.radius + 35,\n\t\t\t'stroke': '#DDDDDD',\n\t\t\t'stroke-width': '1',\n\t\t\t'opacity': '0.8'\n\t\t}, svg);\n\n\t\tvar lblLeft = createSVGElement('text', {\n\t\t\tx: 125,\n\t\t\ty: properties.radius + 75,\n\t\t\tclass: 'lbl lblDial'\n\t\t}, svg);\n\t\tvar lblLeftText = document.createTextNode(options.labels.set);\n\t\tlblLeft.appendChild(lblLeftText);\n\n\t\tvar lblTarget = createSVGElement('text', {\n\t\t\tx: 125,\n\t\t\ty: properties.radius + 115,\n\t\t\t'font-size': '35',\n\t\t\tclass: 'lbl lblTarget'\n\t\t}, svg);\n\t\tvar lblTargetText = document.createTextNode('20');\n\t\tlblTarget.appendChild(lblTargetText);\n\n\t\tvar lblTargetDec = createSVGElement('tspan', {\n\t\t\t'font-size': '20',\n\t\t}, lblTarget);\n\n\t\tvar lblTargetDecText = document.createTextNode('.5');\n\t\tlblTargetDec.appendChild(lblTargetDecText);\n\n\t\tvar lblRight = createSVGElement('text', {\n\t\t\tx: options.diameter - 125,\n\t\t\ty: properties.radius + 75,\n\t\t\tclass: 'lbl lblDial'\n\t\t}, svg);\n\t\tvar lblRightText = document.createTextNode(options.labels.mode);\n\t\tlblRight.appendChild(lblRightText);\n\n\t\tvar lblMode = createSVGElement('text', {\n\t\t\tx: options.diameter - 125,\n\t\t\ty: properties.radius + 115,\n\t\t\t'font-size': '35',\n\t\t\tclass: 'lbl lblTarget icon'\n\t\t}, svg);\n\t\tvar lblModeText = document.createTextNode(properties.modes[0].icon);\n\t\tlblMode.appendChild(lblModeText);\n\n\t\tvar btnSet = createSVGElement('g', {\n\t\t\ttransform: 'translate(200,200)'\n\t\t}, svg);\n\t\tvar path = createSVGElement('path', {\n\t\t\td: 'M0,40 L0,175 A175,175 0 0,1 -175,40 z',\n\t\t\tfill: 'blue',\n\t\t\topacity: '0',\n\t\t\t'id': 'btnLeft'\n\t\t}, btnSet);\n\t\tvar path = createSVGElement('path', {\n\t\t\td: 'M0,40 L175,40 A175,175 0 0,1 0,175 z',\n\t\t\tfill: 'red',\n\t\t\topacity: '0',\n\t\t\t'id': 'btnRight'\n\t\t}, btnSet);\n\n\n\n\t\tdocument.getElementById(\"btnLeft\").onclick = function() {\n\t\t\tsetTargetClick();\n\t\t};\n\n\t\tdocument.getElementById(\"btnRight\").onclick = function() {\n\t\t\tsetModeClick();\n\t\t};\n\n\t\tvar targetPanel = false;\n\t\tvar modePanel = false;\n\n\t\tvar lblAmbientAttributes = {\n\t\t\tx: lblAmbient.getAttribute('x'),\n\t\t\ty: lblAmbient.getAttribute('y'),\n\t\t\tsize: lblAmbient.getAttribute('font-size')\n\t\t};\n\n\t\tvar lblAmbientDecAttributes = {\n\t\t\tx: lblAmbientDec.getAttribute('x'),\n\t\t\ty: lblAmbientDec.getAttribute('y'),\n\t\t\tsize: lblAmbientDec.getAttribute('font-size')\n\t\t};\n\n\t\tvar lblTargetAttributes = {\n\t\t\tx: lblTarget.getAttribute('x'),\n\t\t\ty: lblTarget.getAttribute('y'),\n\t\t\tsize: lblTarget.getAttribute('font-size')\n\t\t};\n\n\t\tvar lblTargetDecAttributes = {\n\t\t\tx: lblTargetDec.getAttribute('x'),\n\t\t\ty: lblTargetDec.getAttribute('y'),\n\t\t\tsize: lblTargetDec.getAttribute('font-size')\n\t\t};\n\n\t\tvar lblModeAttributes = {\n\t\t\tx: lblMode.getAttribute('x'),\n\t\t\ty: lblMode.getAttribute('y'),\n\t\t\tsize: lblMode.getAttribute('font-size')\n\t\t};\n\n\t\tvar lblRightAttributes = {\n\t\t\tx: lblRight.getAttribute('x'),\n\t\t\ty: lblRight.getAttribute('y'),\n\t\t\tsize: lblRight.getAttribute('font-size')\n\t\t};\n\n\t\tvar lblLeftAttributes = {\n\t\t\tx: lblLeft.getAttribute('x'),\n\t\t\ty: lblLeft.getAttribute('y'),\n\t\t\tsize: lblLeft.getAttribute('font-size')\n\t\t};\n\n\t\trender();\n\n\t\tfunction setAmbientTemperature(ambientTemp) {\n\t\t\tvar splitValues = separateDecValue(ambientTemp);\n\t\t\tlblAmbientText.textContent = splitValues.int;\n\t\t\tlblAmbientDecText.textContent = splitValues.dec;\n\t\t};\n\n\n\t\tfunction calcTargetTemperature(operation) {\n\t\t\tlet currentTemp = Number(parseFloat(lblTargetText.textContent + lblTargetDecText.textContent)).toFixed(1);\n\t\t\tlet targetTemp = (operation == '-' ? Number(Number(currentTemp) - 0.5).toFixed(1) : Number(Number(currentTemp) + 0.5).toFixed(1));\n\t\t\ttargetTemp = rangedTemperature(targetTemp);\n\t\t\tsetTargetTemperature(targetTemp);\n\t\t\tchkSwitchState();\n\t\t};\n\n\t\tfunction setTargetTemperature(targetTemp) {\n\t\t\tvar splitValues = separateDecValue(targetTemp);\n\t\t\tlblTargetText.textContent = splitValues.int;\n\t\t\tlblTargetDecText.textContent = splitValues.dec;\n\t\t\tif (state.target_temperature != targetTemp) {\n\t\t\t\tstate.target_temperature = targetTemp\n\t\t\t\tsendMsg();\n\t\t\t};\n\t\t};\n\n\t\tfunction separateDecValue(floatFalue) {\n\t\t\tvar int = Math.floor(floatFalue);\n\t\t\tvar dec = Math.floor(((floatFalue % 1) * 10)) > 0 ? (\".\" + Math.floor(((floatFalue % 1) * 10))) : \"\";\n\t\t\treturn {\n\t\t\t\tint,\n\t\t\t\tdec\n\t\t\t};\n\t\t};\n\n\t\tfunction rangedTemperature(temperature) {\n\t\t\ttemperature = temperature < options.mintemp ? options.maxtemp : temperature;\n\t\t\ttemperature = temperature > options.maxtemp ? options.mintemp : temperature;\n\t\t\treturn temperature;\n\t\t};\n\n\t\tfunction chkSwitchState() {\n\t\t console.log(\"chkSwitchState\");\n\t\t\tvar switchState = state.switch_state;\n\t\t\tswitch (state.mode) {\n\t\t\t\tcase 0:\n\t\t\t\t\tswitchState = state.ambient_temperature < state.target_temperature ? 'heating' : 'off';\n\t\t\t\t\tbreak;\n\t\t\t\tcase 1:\n\t\t\t\t\tswitchState = state.ambient_temperature > state.target_temperature ? 'cooling' : 'off';\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\tswitchState = 'off';\n\t\t\t};\n\n\t\t\tledRingGradientColorIn.setAttribute('stop-color', options.ledColors[state.switch_state]);\n\n\t\t\tif (state.switch_state != switchState) {\n\t\t\t\tstate.switch_state = switchState;\n\t\t\t\tsendMsg();\n\t\t\t};\n\t\t};\n\n\n\t\tfunction resetButton() {\n\t\t\tdocument.getElementById(\"btnLeft\").onmousedown = \"\";\n\t\t\tdocument.getElementById(\"btnLeft\").onmouseup = \"\";\n\t\t\tdocument.getElementById(\"btnLeft\").onclick = function() {\n\t\t\t\tsetTargetClick();\n\t\t\t};\n\t\t\tdocument.getElementById(\"btnRight\").onmousedown = \"\";\n\t\t\tdocument.getElementById(\"btnRight\").onmouseup = \"\";\n\t\t\tdocument.getElementById(\"btnRight\").onclick = function() {\n\t\t\t\tsetModeClick();\n\t\t\t};\n\t\t};\n\n\t\tfunction switchMainView(element, originalAttributes, mainLabel, leftLabel, rightLabel, panelState) {\n\t\t\tsetClass(lblAmbient, \"nodisplay\", panelState);\n\t\t\tsetClass(lblMain, \"animate\", panelState);\n\t\t\tsetClass(lblLeft, \"animate\", panelState);\n\t\t\tsetClass(lblRight, \"animate\", panelState);\n\t\t\tsetClass(element, \"animate\", panelState);\n\n\t\t\tlblMainText.textContent = panelState ? mainLabel : options.labels.ambient;\n\t\t\tlblLeftText.textContent = panelState ? leftLabel : options.labels.set;\n\n\t\t\tlblLeft.setAttribute('y', panelState ? Number(lblLeftAttributes.y) + 40 : lblLeftAttributes.y);\n\t\t\tlblLeft.setAttribute('font-size', panelState ? \"3.5em\" : \"1em\");\n\n\t\t\tlblRightText.textContent = panelState ? rightLabel : options.labels.mode;\n\t\t\tlblRight.setAttribute('y', panelState ? Number(lblRightAttributes.y) + 40 : lblRightAttributes.y);\n\t\t\tlblRight.setAttribute('font-size', panelState ? \"3.5em\" : \"1em\");\n\n\t\t\telement.setAttribute('x', panelState ? lblAmbientAttributes.x : originalAttributes.x);\n\t\t\telement.setAttribute('x', panelState ? lblAmbientAttributes.x : originalAttributes.x);\n\t\t\telement.setAttribute('y', panelState ? lblAmbientAttributes.y : originalAttributes.y);\n\t\t\telement.setAttribute('font-size', panelState ? lblAmbientAttributes.size : originalAttributes.size);\n\n\t\t};\n\n\n\t\tfunction setTargetClick() {\n\n\t\t\ttargetPanel = targetPanel ? false : true;\n\t\t\tsetClass(lblMode, \"nodisplay\", targetPanel);\n\t\t\tswitchMainView(lblTarget, lblTargetAttributes, options.labels.set, options.labels.minus, options.labels.plus, targetPanel);\n\n\t\t\tlblTargetDec.setAttribute('font-size', targetPanel ? lblAmbientDecAttributes.size : lblTargetDecAttributes.size);\n\n\t\t\tif (targetPanel) {\n\t\t\t\tdocument.getElementById(\"btnLeft\").onclick = \"\";\n\t\t\t\tdocument.getElementById(\"btnRight\").onclick = \"\";\n\n\t\t\t\tdocument.getElementById(\"btnLeft\").onmousedown = function() {\n\t\t\t\t\tcalcTargetTemperature(\"-\");\n\t\t\t\t\tif (mousedownID == -1) { //Prevent multimple loops!\n\t\t\t\t\t\tmousedownID = setInterval(calcTargetTemperature, 500, '-');\n\t\t\t\t\t}\n\t\t\t\t};\n\t\t\t\tdocument.getElementById(\"btnLeft\").onmouseup = function() {\n\t\t\t\t\tif (mousedownID != -1) { //Only stop if exists\n\t\t\t\t\t\tclearInterval(mousedownID);\n\t\t\t\t\t\tmousedownID = -1;\n\t\t\t\t\t}\n\t\t\t\t};\n\n\t\t\t\tdocument.getElementById(\"btnRight\").onmousedown = function() {\n\t\t\t\t\tcalcTargetTemperature(\"+\");\n\t\t\t\t\tif (mousedownID == -1) { //Prevent multimple loops!\n\t\t\t\t\t\tmousedownID = setInterval(calcTargetTemperature, 500, '+');\n\t\t\t\t\t}\n\t\t\t\t};\n\t\t\t\tdocument.getElementById(\"btnRight\").onmouseup = function() {\n\t\t\t\t\tif (mousedownID != -1) { //Only stop if exists\n\t\t\t\t\t\tclearInterval(mousedownID);\n\t\t\t\t\t\tmousedownID = -1;\n\t\t\t\t\t}\n\t\t\t\t};\n\n\t\t\t\tlblTarget.onclick = function() {\n\t\t\t\t\tsetTargetClick();\n\t\t\t\t};\n\t\t\t} else {\n\t\t\t\tresetButton()\n\t\t\t}\n\t\t};\n\n\t\tfunction setModeClick() {\n\n\t\t\tmodePanel = modePanel ? false : true;\n\t\t\tsetClass(lblTarget, \"nodisplay\", modePanel);\n\t\t\tswitchMainView(lblMode, lblModeAttributes, options.labels.mode, options.labels.left, options.labels.right, modePanel);\n\n\t\t\tif (modePanel) {\n\n\t\t\t\tdocument.getElementById(\"btnLeft\").onclick = function() {\n\t\t\t\t\tmode = state.mode;\n\t\t\t\t\tmode = --mode < 0 ? properties.modes.length - 1 : mode;\n\t\t\t\t\tconsole.log(\"MODE :\" + mode);\n\t\t\t\t\tsetModeName(properties.modeNames[mode]);\n\t\t\t\t\tchkSwitchState();\n\t\t\t\t\tsendMsg();\n\t\t\t\t};\n\n\t\t\t\tdocument.getElementById(\"btnRight\").onclick = function() {\n\t\t\t\t\tmode = state.mode;\n\t\t\t\t\tmode = ++mode > properties.modes.length - 1 ? 0 : mode;\n\t\t\t\t\tconsole.log(\"MODE :\" + mode);\n\t\t\t\t\tsetModeName(properties.modeNames[mode]);\n\t\t\t\t\tchkSwitchState();\n\t\t\t\t\tsendMsg();\n\t\t\t\t};\n\n\t\t\t\tlblMode.onclick = function() {\n\t\t\t\t\tsetModeClick();\n\t\t\t\t};\n\t\t\t} else {\n\t\t\t\tresetButton()\n\t\t\t}\n\t\t};\n\n\t\tfunction setModeName(modeName) {\n\t\t\tlblMode.textContent = properties.modes[properties.modeNames.indexOf(modeName)].icon;\n\t\t\tlblMode.style.fill = properties.modes[properties.modeNames.indexOf(modeName)].color;\n\t\t\tstate.mode = properties.modeNames.indexOf(modeName);\n\t\t};\n\t\t\n\t\tfunction sendMsg() {\n\t\t\tif (typeof options.onChangeState == 'function') {\n\t\t\t\toptions.onChangeState(state.switch_state);\n\t\t\t}\n\t\t};\n\n\t\tfunction render() {\n\t\t\tconsole.log(\"RENDER\");\n\t\t\tsetAmbientTemperature(self.ambient_temperature);\n\t\t\tsetTargetTemperature(self.target_temperature);\n\t\t\tsetModeName(self.mode_name);\n\t\t\tchkSwitchState();\n\t\t};\n\n\t};\n})();\n\nvar initializing = true;\n\n(function(scope) {\n\tvar ghostThermostat = new ghostThermostatDial(document.getElementById('GhostThermostat'), {\n\t\tonChangeState: function() {\n\t\t\tvar p = {\n\t\t\t\t\"ambient_temperature\": ghostThermostat.ambient_temperature,\n\t\t\t\t\"target_temperature\": ghostThermostat.target_temperature,\n\t\t\t\t\"mode\": ghostThermostat.mode_name,\n\t\t\t\t\"switch_state\": ghostThermostat.switch_state,\n\t\t\t\t\"away\": ghostThermostat.away\n\t\t\t};\n\t\t\tscope.send({\n\t\t\t\ttopic: \"changed_state\",\n\t\t\t\tpayload: p\n\t\t\t});\n\t\t}\n\t});\n\n\tscope.$watch('msg', function(data) {\n\t\tif (initializing) {\n\t\t\tinitializing = false;\n\t\t} else {\n\t\t\tghostThermostat.ambient_temperature = data.payload.ambient_temperature || ghostThermostat.ambient_temperature;\n\t\t\tghostThermostat.target_temperature = data.payload.target_temperature || ghostThermostat.target_temperature;\n\t\t\tghostThermostat.mode_name = data.payload.mode || ghostThermostat.mode_name;\n\t\t\tghostThermostat.switch_state = data.payload.switch_state || ghostThermostat.switch_state;\n\t\t\tghostThermostat.away = data.payload.away || ghostThermostat.away;\n\t\t}\n\t});\n})(scope);\n</script>","storeOutMessages":true,"fwdInMessages":false,"resendOnRefresh":true,"templateScope":"local","x":1108.008659362793,"y":177.00172233581543,"wires":[["2b1cbd00e389c687"]],"icon":"font-awesome/fa-tachometer"},{"id":"2b1cbd00e389c687","type":"debug","z":"f6858fe416b029b9","name":"","active":true,"tosidebar":true,"console":false,"tostatus":true,"complete":"payload","targetType":"msg","statusVal":"payload","statusType":"auto","x":1315.5608139038086,"y":178.9211082458496,"wires":[]},{"id":"8f8949fff0292467","type":"function","z":"f6858fe416b029b9","name":"","func":"msg.topic = 'ambient_temperature'; \nvar data = {\n 'ambient_temperature':msg.payload || 20\n}\nmsg.payload = data; \nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":1056.5625267028809,"y":127.82209873199463,"wires":[["223f3f0c0674a890","ab8fb1c28921f1dd"]]},{"id":"223f3f0c0674a890","type":"debug","z":"f6858fe416b029b9","name":"function","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":1204.5659828186035,"y":121.92283344268799,"wires":[]},{"id":"7dc3530e.8f07a4","type":"ui_group","name":"control","tab":"6b25e5f4.b44ecc","order":1,"disp":true,"width":"24","collapse":false},{"id":"31a99116.50a74e","type":"mqtt-broker","broker":"localhost","port":"1883","clientid":"","usetls":false,"compatmode":true,"keepalive":"60","cleansession":true,"birthTopic":"","birthQos":"0","birthPayload":"","willTopic":"","willQos":"0","willPayload":""},{"id":"6b25e5f4.b44ecc","type":"ui_tab","name":"Tasmota Control","icon":"dashboard","disabled":false,"hidden":false}]
I'm off out in a moment, but I think (looking at your screenshot) it is telling you the system is in cooling mode as the ambient_temp is higher than the target_temp. You could try other values as you my find a "heating" mode and maybe an "off" mode.
I may actually have this figured out.
[{"id":"a1935b0f08af8408","type":"ui_switch","z":"f6858fe416b029b9","name":"Wferm","label":"Wferm","tooltip":"","group":"7dc3530e.8f07a4","order":4,"width":"3","height":"1","passthru":false,"decouple":"true","topic":"cmnd/Wferm/POWER","style":"","onvalue":"ON","onvalueType":"str","onicon":"","oncolor":"","offvalue":"OFF","offvalueType":"str","officon":"","offcolor":"","x":389.6666831970215,"y":180.25003814697266,"wires":[["4349ab2a681252f2","dcb2c21aa32eec71"]]},{"id":"4349ab2a681252f2","type":"mqtt out","z":"f6858fe416b029b9","name":"","topic":"","qos":"","retain":"","respTopic":"","contentType":"","userProps":"","correl":"","expiry":"","broker":"31a99116.50a74e","x":534.2380714416504,"y":215.99999809265137,"wires":[]},{"id":"ce5c66f008f4cbf3","type":"mqtt in","z":"f6858fe416b029b9","name":"","topic":"stat/Wferm/POWER","qos":"1","datatype":"auto","broker":"31a99116.50a74e","nl":false,"rap":false,"inputs":0,"x":196.23805236816406,"y":140.00006866455078,"wires":[["a1935b0f08af8408","5a422122ed052f7d"]]},{"id":"dcb2c21aa32eec71","type":"debug","z":"f6858fe416b029b9","name":"out","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":528.2380905151367,"y":180.0000343322754,"wires":[]},{"id":"5a422122ed052f7d","type":"debug","z":"f6858fe416b029b9","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":393.8022689819336,"y":139.92453384399414,"wires":[]},{"id":"987d16ece3fa2e58","type":"ui_digital_display","z":"f6858fe416b029b9","name":"","group":"7dc3530e.8f07a4","order":2,"width":0,"height":0,"digits":5,"decimals":1,"x":963.5643920898438,"y":30.934907913208008,"wires":[]},{"id":"c978949d90d42b1e","type":"mqtt in","z":"f6858fe416b029b9","name":"","topic":"tele/Wferm/SENSOR","qos":"2","datatype":"auto","broker":"31a99116.50a74e","nl":false,"rap":true,"rh":0,"inputs":0,"x":126.56424713134766,"y":27.83073902130127,"wires":[["4c4294bd3b2d8cf3","a1bfb15585d7eb59"]]},{"id":"93a3046bdaeff2c8","type":"debug","z":"f6858fe416b029b9","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":742.5643463134766,"y":40.921030044555664,"wires":[]},{"id":"65f9fd5be1a51434","type":"change","z":"f6858fe416b029b9","name":"set payload to Temperature value","rules":[{"t":"move","p":"payload.Temperature","pt":"msg","to":"payload","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":656.0089416503906,"y":98.00179672241211,"wires":[["987d16ece3fa2e58","93a3046bdaeff2c8","8f8949fff0292467"]]},{"id":"4c4294bd3b2d8cf3","type":"debug","z":"f6858fe416b029b9","name":"","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":324.56079483032227,"y":20,"wires":[]},{"id":"a1bfb15585d7eb59","type":"json","z":"f6858fe416b029b9","name":"","property":"payload","action":"","pretty":false,"x":291.00868225097656,"y":66.33507347106934,"wires":[["27d0a195ec58a6a1"]]},{"id":"bb44589f1917700d","type":"switch","z":"f6858fe416b029b9","name":"If =442FBOIL","property":"payload.Id","propertyType":"msg","rules":[{"t":"cont","v":"3C01F096FCA3","vt":"str"},{"t":"else"}],"checkall":"true","repair":false,"outputs":2,"x":505.1198310852051,"y":50.001853942871094,"wires":[["65f9fd5be1a51434"],[]]},{"id":"27d0a195ec58a6a1","type":"split","z":"f6858fe416b029b9","name":"","splt":"\\n","spltType":"str","arraySplt":1,"arraySpltType":"len","stream":false,"addname":"","x":398.1163444519043,"y":96.97662925720215,"wires":[["bb44589f1917700d"]]},{"id":"ab8fb1c28921f1dd","type":"ui_template","z":"f6858fe416b029b9","group":"7dc3530e.8f07a4","name":"GhostThermostat","order":2,"width":"6","height":"6","format":"<style>\n @import url('https://fonts.googleapis.com/css2?family=Roboto:ital,wght@0,100;0,300;0,400;0,500;0,700;0,900;1,100;1,300;1,400;1,500;1,700;1,900&display=swap');\n \n svg {\n transition: all .6s cubic-bezier(0.175, 0.885, 0.32, 1.2);\n }\n\n stop {\n transition: all .5s;\n }\n \n\t.led {\n \t-webkit-transition: all 0.5s;\n \ttransition: all 0.5s;\n \tfill: url(#ledColor);\n }\n \n .fa-text {\n font-family: FontAwesome !important; \n }\n .dial {\n -webkit-user-select: none;\n -moz-user-select: none;\n -ms-user-select: none;\n user-select: none;\n }\n .qGradient {\n fill : url(#qGradient);\n }\n .qGradientT {\n fill : url(#qGradientT);\n }\n .eGradient {\n fill : url(#eGradient);\n }\n .lbl {\n font-family: 'Roboto', sans-serif;\n text-anchor: middle;\n fill : #ffffff;\n clip-path: url(#qClip);\n }\n .lblDial {\n fill: #dddddd;\n }\n \n .lblAmbient {\n font-weight: 400;\n clip-path: url(#qClip);\n }\n \n .lblAmbient tspan {\n font-weight: 400;\n }\n \n .lblTarget {\n font-weight: 400;\n fill: orange;\n }\n \n .lblTarget tspan {\n font-weight: 400;\n fill: orange;\n clip-path: url(#qClip);\n } \n \n .nodisplay {\n display: none !important;\n }\n \n .icon {\n font-family: FontAwesome !important;\n }\n \n .animate {\n transition: all 0.5s;\n }\n\n</style>\n<div id=\"GhostThermostat\"></div> \n<script>\nvar mousedownID = -1;\nvar ghostThermostatDial = (function() {\n\tconsole.log(\"START\");\n\n\tfunction createSVGElement(tag, attributes, appendTo) {\n\t\tvar element = document.createElementNS('http://www.w3.org/2000/svg', tag);\n\t\tattr(element, attributes);\n\t\tif (appendTo) {\n\t\t\tappendTo.appendChild(element);\n\t\t}\n\t\treturn element;\n\t}\n\n\tfunction attr(element, attrs) {\n\t\tfor (var i in attrs) {\n\t\t\telement.setAttribute(i, attrs[i]);\n\t\t}\n\t}\n\n\tfunction setClass(el, className, state) {\n\t\tel.classList[state ? 'add' : 'remove'](className);\n\t}\n\n\treturn function(targetElement, options) {\n\t\tconsole.log(\"RET FUN\");\n\t\tvar self = this;\n\n\t\t/*\n\t\t * Options\n\t\t */\n\t\toptions = options || {};\n\t\toptions = {\n\t\t\tdiameter: options.diameter || 400,\n\t\t\tmintemp: options.mintemp || 0, // Minimum value for target temperature\n\t\t\tmaxtemp: options.maxtemp || 200, // Maximum value for target temperature\n\t\t\tledColors: {\n\t\t\t\t'off': 'rgb(143,141,141)',\n\t\t\t\t'heating': 'rgb(255,128,0)',\n\t\t\t\t'cooling': 'rgb(81,170,214)'\n\t\t\t}, //Led Ring Colors\n\t\t\tlabels: {\n\t\t\t\tambient: \"AMBIENT\",\n\t\t\t\tset: \"SET\",\n\t\t\t\tmode: \"MODE\",\n\t\t\t\tminus: \"-\",\n\t\t\t\tplus: \"+\",\n\t\t\t\tleft: \"<\",\n\t\t\t\tright: \">\"\n\t\t\t},\n\t\t\tonChangeState: options.onChangeState || function() {} // Function called when switch state change\n\t\t};\n\n\t\t/*\n\t\t * Properties\n\t\t */\n\t\tvar properties = {\n\t\t\tradius: options.diameter / 2,\n\t\t\tmodes: [{\n\t\t\t\t\tlabel: \"heating\",\n\t\t\t\t\ticon: \"\\uf06d\",\n\t\t\t\t\tcolor: \"orange\"\n\t\t\t\t}, {\n\t\t\t\t\tlabel: 'cooling',\n\t\t\t\t\ticon: \"\\uf2dc\",\n\t\t\t\t\tcolor: \"rgb(81,170,214)\"\n\t\t\t\t}, {\n\t\t\t\t\tlabel: \"off\",\n\t\t\t\t\ticon: \"\\uf011\",\n\t\t\t\t\tcolor: \"rgb(230,0,0)\"\n\t\t\t\t}\n\t\t\t\t/*, {\n\t\t\t\tlabel: 'away',\n\t\t\t\ticon: \"\\uf1ce\",\n\t\t\t\tcolor: \"gray\"\n\t\t\t} */\n\t\t\t],\n\t\t\tmodeNames: [\"heating\", \"cooling\", \"off\"],\n\t\t\tswtitchStates: [\"heating\", \"cooling\", \"off\"]\n\t\t};\n\n\t\t/*\n\t\t * Object state\n\t\t */\n\t\tvar state = {\n\t\t\ttarget_temperature: options.mintemp,\n\t\t\tambient_temperature: options.maxtemp,\n\t\t\tmode: properties.modes.indexOf(properties.modes[0]),\n\t\t\tswitch_state: 'off',\n\t\t\taway: false\n\t\t};\n\n\t\t/*\n\t\t * Property getter / setters\n\t\t */\n\t\tObject.defineProperty(this, 'target_temperature', {\n\t\t\tget: function() {\n\t\t\t\treturn state.target_temperature;\n\t\t\t},\n\t\t\tset: function(val) {\n\t\t\t\tstate.target_temperature = rangedTemperature(+val);\n\t\t\t\t//render()\n\t\t\t}\n\t\t});\n\n\t\tObject.defineProperty(this, 'ambient_temperature', {\n\t\t\tget: function() {\n\t\t\t\treturn state.ambient_temperature;\n\t\t\t},\n\t\t\tset: function(val) {\n\t\t\t\tstate.ambient_temperature = +val;\n\t\t\t\trender();\n\t\t\t}\n\t\t});\n\n\t\tObject.defineProperty(this, 'mode_name', {\n\t\t\tget: function() {\n\t\t\t\treturn properties.modeNames[state.mode];\n\t\t\t},\n\t\t\tset: function(val) {\n\t\t\t\tif (properties.modeNames.indexOf(val) >= 0) {\n\t\t\t\t\tstate.mode = properties.modeNames.indexOf(val);\n\t\t\t\t\t//render();\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\n\t\tObject.defineProperty(this, 'switch_state', {\n\t\t\tget: function() {\n\t\t\t\treturn state.switch_state;\n\t\t\t},\n\t\t\tset: function(val) {\n\t\t\t\tif (properties.swtitchStates.indexOf(val) >= 0) {\n\t\t\t\t\tstate.switch_state = val;\n\t\t\t\t\t//render();\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\n\n\t\tfunction str2bool(strvalue) {\n\t\t\treturn (strvalue && typeof strvalue == 'string') ? (strvalue.toLowerCase() == 'true') : (strvalue == true);\n\t\t}\n\n\t\tObject.defineProperty(this, 'away', {\n\t\t\tget: function() {\n\t\t\t\treturn state.away;\n\t\t\t},\n\t\t\tset: function(val) {\n\t\t\t\tstate.away = !!str2bool(val);\n\t\t\t\t//render();\n\t\t\t}\n\t\t});\n\n\n\t\t/*\n\t\t * SVG\n\t\t */\n\t\tvar svg = createSVGElement('svg', {\n\t\t\twidth: '100%', //options.diameter+'px',\n\t\t\theight: '100%', //options.diameter+'px',\n\t\t\tviewBox: '0 0 ' + options.diameter + ' ' + options.diameter,\n\t\t\tclass: 'dial'\n\t\t}, targetElement);\n\n\t\t// DEFS \n\t\tvar defs = createSVGElement('defs', null, svg);\n\n\t\tvar qgradient = createSVGElement('linearGradient', {\n\t\t\t'id': 'qGradient',\n\t\t\tgradientTransform: 'rotate(65)'\n\t\t}, defs);\n\t\tvar stop = createSVGElement('stop', {\n\t\t\t'offset': '50%',\n\t\t\t'stop-color': 'rgb(86,89,94)'\n\t\t}, qgradient);\n\t\tvar stop = createSVGElement('stop', {\n\t\t\t'offset': '65%',\n\t\t\t'stop-color': 'rgb(30,30,30)'\n\t\t}, qgradient);\n\n\t\tvar qGradientT = createSVGElement('linearGradient', {\n\t\t\t'id': 'qGradientT',\n\t\t\tgradientTransform: 'rotate(65)'\n\t\t}, defs);\n\t\tvar stop = createSVGElement('stop', {\n\t\t\t'offset': '55%',\n\t\t\t'stop-color': '#3b3e43',\n\t\t\t'stop-opacity': '1'\n\t\t}, qGradientT);\n\t\tvar stop = createSVGElement('stop', {\n\t\t\t'offset': '90%',\n\t\t\t'stop-color': 'rgb(0,0,0)',\n\t\t\t'stop-opacity': '1'\n\t\t}, qGradientT);\n\n\t\tvar clipPath = createSVGElement('clipPath', {\n\t\t\t'id': 'qClip',\n\t\t}, defs);\n\t\tvar circle = createSVGElement('circle', {\n\t\t\tcx: properties.radius,\n\t\t\tcy: properties.radius,\n\t\t\tr: properties.radius - 25\n\t\t}, clipPath);\n\n\n\t\tvar ledRingGradient = createSVGElement('radialGradient', {\n\t\t\t'id': 'ledColor',\n\t\t\t'cx': \"50%\",\n\t\t\t'cy': \"50%\",\n\t\t\t'r': \"95%\",\n\t\t\t'fx': \"50%\",\n\t\t\t'fy': \"50%\"\n\t\t}, defs);\n\t\tvar ledRingGradientColorIn = createSVGElement('stop', {\n\t\t\t'offset': '45%',\n\t\t\t'stop-color': 'rgb(255,0,130)',\n\t\t\t'stop-opacity': '1'\n\t\t}, ledRingGradient);\n\t\tvar ledRingGradientColorOut = createSVGElement('stop', {\n\t\t\t'offset': '65%',\n\t\t\t'stop-color': 'rgb(0,0,0)',\n\t\t\t'stop-opacity': '1'\n\t\t}, ledRingGradient);\n\n\t\tvar egradient = createSVGElement('linearGradient', {\n\t\t\t'id': 'eGradient',\n\t\t\tgradientTransform: 'rotate(55)'\n\t\t}, defs);\n\t\tvar stop = createSVGElement('stop', {\n\t\t\t'offset': '55%',\n\t\t\t'stop-color': '#888888',\n\t\t\t'stop-opacity': '1'\n\t\t}, egradient);\n\t\tvar stop = createSVGElement('stop', {\n\t\t\t'offset': '95%',\n\t\t\t'stop-color': '#333333',\n\t\t\t'stop-opacity': '1'\n\t\t}, egradient);\n\n\t\t// DIAL\n\t\tvar circle = createSVGElement('circle', {\n\t\t\tcx: properties.radius,\n\t\t\tcy: properties.radius,\n\t\t\tr: properties.radius,\n\t\t\tclass: 'eGradient'\n\t\t}, svg);\n\t\tvar ledRing = createSVGElement('circle', {\n\t\t\tcx: properties.radius,\n\t\t\tcy: properties.radius,\n\t\t\tr: properties.radius - 3,\n\t\t\t'stroke': 'black',\n\t\t\t'stroke-width': '1',\n\t\t\tclass: 'led'\n\t\t}, svg);\n\t\tvar circle = createSVGElement('circle', {\n\t\t\tcx: properties.radius,\n\t\t\tcy: properties.radius,\n\t\t\tr: properties.radius - 20,\n\t\t\tclass: 'qGradient'\n\t\t}, svg);\n\t\tvar circle = createSVGElement('circle', {\n\t\t\tcx: properties.radius,\n\t\t\tcy: properties.radius,\n\t\t\tr: properties.radius - 25,\n\t\t\tclass: 'qGradient'\n\t\t}, svg);\n\t\tvar lblMain = createSVGElement('text', {\n\t\t\tx: properties.radius,\n\t\t\ty: 70,\n\t\t\tclass: 'lbl lblDial'\n\t\t}, svg);\n\t\tvar lblMainText = document.createTextNode(options.labels.ambient);\n\t\tlblMain.appendChild(lblMainText);\n\n\t\tvar lblAmbient = createSVGElement('text', {\n\t\t\tx: properties.radius,\n\t\t\ty: 210,\n\t\t\t'font-size': '160',\n\t\t\tclass: 'lbl lblAmbient'\n\t\t}, svg);\n\t\tvar lblAmbientText = document.createTextNode('21');\n\t\tlblAmbient.appendChild(lblAmbientText);\n\t\tvar lblAmbientDec = createSVGElement('tspan', {\n\t\t\t'font-size': '60',\n\t\t}, lblAmbient);\n\t\tvar lblAmbientDecText = document.createTextNode('.5');\n\t\tlblAmbientDec.appendChild(lblAmbientDecText);\n\n\t\tvar line = createSVGElement('line', {\n\t\t\tx1: 55,\n\t\t\ty1: properties.radius + 35,\n\t\t\tx2: options.diameter - 55,\n\t\t\ty2: properties.radius + 35,\n\t\t\t'stroke': '#DDDDDD',\n\t\t\t'stroke-width': '1',\n\t\t\t'opacity': '0.8'\n\t\t}, svg);\n\n\t\tvar lblLeft = createSVGElement('text', {\n\t\t\tx: 125,\n\t\t\ty: properties.radius + 75,\n\t\t\tclass: 'lbl lblDial'\n\t\t}, svg);\n\t\tvar lblLeftText = document.createTextNode(options.labels.set);\n\t\tlblLeft.appendChild(lblLeftText);\n\n\t\tvar lblTarget = createSVGElement('text', {\n\t\t\tx: 125,\n\t\t\ty: properties.radius + 115,\n\t\t\t'font-size': '35',\n\t\t\tclass: 'lbl lblTarget'\n\t\t}, svg);\n\t\tvar lblTargetText = document.createTextNode('20');\n\t\tlblTarget.appendChild(lblTargetText);\n\n\t\tvar lblTargetDec = createSVGElement('tspan', {\n\t\t\t'font-size': '20',\n\t\t}, lblTarget);\n\n\t\tvar lblTargetDecText = document.createTextNode('.5');\n\t\tlblTargetDec.appendChild(lblTargetDecText);\n\n\t\tvar lblRight = createSVGElement('text', {\n\t\t\tx: options.diameter - 125,\n\t\t\ty: properties.radius + 75,\n\t\t\tclass: 'lbl lblDial'\n\t\t}, svg);\n\t\tvar lblRightText = document.createTextNode(options.labels.mode);\n\t\tlblRight.appendChild(lblRightText);\n\n\t\tvar lblMode = createSVGElement('text', {\n\t\t\tx: options.diameter - 125,\n\t\t\ty: properties.radius + 115,\n\t\t\t'font-size': '35',\n\t\t\tclass: 'lbl lblTarget icon'\n\t\t}, svg);\n\t\tvar lblModeText = document.createTextNode(properties.modes[0].icon);\n\t\tlblMode.appendChild(lblModeText);\n\n\t\tvar btnSet = createSVGElement('g', {\n\t\t\ttransform: 'translate(200,200)'\n\t\t}, svg);\n\t\tvar path = createSVGElement('path', {\n\t\t\td: 'M0,40 L0,175 A175,175 0 0,1 -175,40 z',\n\t\t\tfill: 'blue',\n\t\t\topacity: '0',\n\t\t\t'id': 'btnLeft'\n\t\t}, btnSet);\n\t\tvar path = createSVGElement('path', {\n\t\t\td: 'M0,40 L175,40 A175,175 0 0,1 0,175 z',\n\t\t\tfill: 'red',\n\t\t\topacity: '0',\n\t\t\t'id': 'btnRight'\n\t\t}, btnSet);\n\n\n\n\t\tdocument.getElementById(\"btnLeft\").onclick = function() {\n\t\t\tsetTargetClick();\n\t\t};\n\n\t\tdocument.getElementById(\"btnRight\").onclick = function() {\n\t\t\tsetModeClick();\n\t\t};\n\n\t\tvar targetPanel = false;\n\t\tvar modePanel = false;\n\n\t\tvar lblAmbientAttributes = {\n\t\t\tx: lblAmbient.getAttribute('x'),\n\t\t\ty: lblAmbient.getAttribute('y'),\n\t\t\tsize: lblAmbient.getAttribute('font-size')\n\t\t};\n\n\t\tvar lblAmbientDecAttributes = {\n\t\t\tx: lblAmbientDec.getAttribute('x'),\n\t\t\ty: lblAmbientDec.getAttribute('y'),\n\t\t\tsize: lblAmbientDec.getAttribute('font-size')\n\t\t};\n\n\t\tvar lblTargetAttributes = {\n\t\t\tx: lblTarget.getAttribute('x'),\n\t\t\ty: lblTarget.getAttribute('y'),\n\t\t\tsize: lblTarget.getAttribute('font-size')\n\t\t};\n\n\t\tvar lblTargetDecAttributes = {\n\t\t\tx: lblTargetDec.getAttribute('x'),\n\t\t\ty: lblTargetDec.getAttribute('y'),\n\t\t\tsize: lblTargetDec.getAttribute('font-size')\n\t\t};\n\n\t\tvar lblModeAttributes = {\n\t\t\tx: lblMode.getAttribute('x'),\n\t\t\ty: lblMode.getAttribute('y'),\n\t\t\tsize: lblMode.getAttribute('font-size')\n\t\t};\n\n\t\tvar lblRightAttributes = {\n\t\t\tx: lblRight.getAttribute('x'),\n\t\t\ty: lblRight.getAttribute('y'),\n\t\t\tsize: lblRight.getAttribute('font-size')\n\t\t};\n\n\t\tvar lblLeftAttributes = {\n\t\t\tx: lblLeft.getAttribute('x'),\n\t\t\ty: lblLeft.getAttribute('y'),\n\t\t\tsize: lblLeft.getAttribute('font-size')\n\t\t};\n\n\t\trender();\n\n\t\tfunction setAmbientTemperature(ambientTemp) {\n\t\t\tvar splitValues = separateDecValue(ambientTemp);\n\t\t\tlblAmbientText.textContent = splitValues.int;\n\t\t\tlblAmbientDecText.textContent = splitValues.dec;\n\t\t};\n\n\n\t\tfunction calcTargetTemperature(operation) {\n\t\t\tlet currentTemp = Number(parseFloat(lblTargetText.textContent + lblTargetDecText.textContent)).toFixed(1);\n\t\t\tlet targetTemp = (operation == '-' ? Number(Number(currentTemp) - 0.5).toFixed(1) : Number(Number(currentTemp) + 0.5).toFixed(1));\n\t\t\ttargetTemp = rangedTemperature(targetTemp);\n\t\t\tsetTargetTemperature(targetTemp);\n\t\t\tchkSwitchState();\n\t\t};\n\n\t\tfunction setTargetTemperature(targetTemp) {\n\t\t\tvar splitValues = separateDecValue(targetTemp);\n\t\t\tlblTargetText.textContent = splitValues.int;\n\t\t\tlblTargetDecText.textContent = splitValues.dec;\n\t\t\tif (state.target_temperature != targetTemp) {\n\t\t\t\tstate.target_temperature = targetTemp\n\t\t\t\tsendMsg();\n\t\t\t};\n\t\t};\n\n\t\tfunction separateDecValue(floatFalue) {\n\t\t\tvar int = Math.floor(floatFalue);\n\t\t\tvar dec = Math.floor(((floatFalue % 1) * 10)) > 0 ? (\".\" + Math.floor(((floatFalue % 1) * 10))) : \"\";\n\t\t\treturn {\n\t\t\t\tint,\n\t\t\t\tdec\n\t\t\t};\n\t\t};\n\n\t\tfunction rangedTemperature(temperature) {\n\t\t\ttemperature = temperature < options.mintemp ? options.maxtemp : temperature;\n\t\t\ttemperature = temperature > options.maxtemp ? options.mintemp : temperature;\n\t\t\treturn temperature;\n\t\t};\n\n\t\tfunction chkSwitchState() {\n\t\t console.log(\"chkSwitchState\");\n\t\t\tvar switchState = state.switch_state;\n\t\t\tswitch (state.mode) {\n\t\t\t\tcase 0:\n\t\t\t\t\tswitchState = state.ambient_temperature < state.target_temperature ? 'heating' : 'off';\n\t\t\t\t\tbreak;\n\t\t\t\tcase 1:\n\t\t\t\t\tswitchState = state.ambient_temperature > state.target_temperature ? 'cooling' : 'off';\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\tswitchState = 'off';\n\t\t\t};\n\n\t\t\tledRingGradientColorIn.setAttribute('stop-color', options.ledColors[state.switch_state]);\n\n\t\t\tif (state.switch_state != switchState) {\n\t\t\t\tstate.switch_state = switchState;\n\t\t\t\tsendMsg();\n\t\t\t};\n\t\t};\n\n\n\t\tfunction resetButton() {\n\t\t\tdocument.getElementById(\"btnLeft\").onmousedown = \"\";\n\t\t\tdocument.getElementById(\"btnLeft\").onmouseup = \"\";\n\t\t\tdocument.getElementById(\"btnLeft\").onclick = function() {\n\t\t\t\tsetTargetClick();\n\t\t\t};\n\t\t\tdocument.getElementById(\"btnRight\").onmousedown = \"\";\n\t\t\tdocument.getElementById(\"btnRight\").onmouseup = \"\";\n\t\t\tdocument.getElementById(\"btnRight\").onclick = function() {\n\t\t\t\tsetModeClick();\n\t\t\t};\n\t\t};\n\n\t\tfunction switchMainView(element, originalAttributes, mainLabel, leftLabel, rightLabel, panelState) {\n\t\t\tsetClass(lblAmbient, \"nodisplay\", panelState);\n\t\t\tsetClass(lblMain, \"animate\", panelState);\n\t\t\tsetClass(lblLeft, \"animate\", panelState);\n\t\t\tsetClass(lblRight, \"animate\", panelState);\n\t\t\tsetClass(element, \"animate\", panelState);\n\n\t\t\tlblMainText.textContent = panelState ? mainLabel : options.labels.ambient;\n\t\t\tlblLeftText.textContent = panelState ? leftLabel : options.labels.set;\n\n\t\t\tlblLeft.setAttribute('y', panelState ? Number(lblLeftAttributes.y) + 40 : lblLeftAttributes.y);\n\t\t\tlblLeft.setAttribute('font-size', panelState ? \"3.5em\" : \"1em\");\n\n\t\t\tlblRightText.textContent = panelState ? rightLabel : options.labels.mode;\n\t\t\tlblRight.setAttribute('y', panelState ? Number(lblRightAttributes.y) + 40 : lblRightAttributes.y);\n\t\t\tlblRight.setAttribute('font-size', panelState ? \"3.5em\" : \"1em\");\n\n\t\t\telement.setAttribute('x', panelState ? lblAmbientAttributes.x : originalAttributes.x);\n\t\t\telement.setAttribute('x', panelState ? lblAmbientAttributes.x : originalAttributes.x);\n\t\t\telement.setAttribute('y', panelState ? lblAmbientAttributes.y : originalAttributes.y);\n\t\t\telement.setAttribute('font-size', panelState ? lblAmbientAttributes.size : originalAttributes.size);\n\n\t\t};\n\n\n\t\tfunction setTargetClick() {\n\n\t\t\ttargetPanel = targetPanel ? false : true;\n\t\t\tsetClass(lblMode, \"nodisplay\", targetPanel);\n\t\t\tswitchMainView(lblTarget, lblTargetAttributes, options.labels.set, options.labels.minus, options.labels.plus, targetPanel);\n\n\t\t\tlblTargetDec.setAttribute('font-size', targetPanel ? lblAmbientDecAttributes.size : lblTargetDecAttributes.size);\n\n\t\t\tif (targetPanel) {\n\t\t\t\tdocument.getElementById(\"btnLeft\").onclick = \"\";\n\t\t\t\tdocument.getElementById(\"btnRight\").onclick = \"\";\n\n\t\t\t\tdocument.getElementById(\"btnLeft\").onmousedown = function() {\n\t\t\t\t\tcalcTargetTemperature(\"-\");\n\t\t\t\t\tif (mousedownID == -1) { //Prevent multimple loops!\n\t\t\t\t\t\tmousedownID = setInterval(calcTargetTemperature, 500, '-');\n\t\t\t\t\t}\n\t\t\t\t};\n\t\t\t\tdocument.getElementById(\"btnLeft\").onmouseup = function() {\n\t\t\t\t\tif (mousedownID != -1) { //Only stop if exists\n\t\t\t\t\t\tclearInterval(mousedownID);\n\t\t\t\t\t\tmousedownID = -1;\n\t\t\t\t\t}\n\t\t\t\t};\n\n\t\t\t\tdocument.getElementById(\"btnRight\").onmousedown = function() {\n\t\t\t\t\tcalcTargetTemperature(\"+\");\n\t\t\t\t\tif (mousedownID == -1) { //Prevent multimple loops!\n\t\t\t\t\t\tmousedownID = setInterval(calcTargetTemperature, 500, '+');\n\t\t\t\t\t}\n\t\t\t\t};\n\t\t\t\tdocument.getElementById(\"btnRight\").onmouseup = function() {\n\t\t\t\t\tif (mousedownID != -1) { //Only stop if exists\n\t\t\t\t\t\tclearInterval(mousedownID);\n\t\t\t\t\t\tmousedownID = -1;\n\t\t\t\t\t}\n\t\t\t\t};\n\n\t\t\t\tlblTarget.onclick = function() {\n\t\t\t\t\tsetTargetClick();\n\t\t\t\t};\n\t\t\t} else {\n\t\t\t\tresetButton()\n\t\t\t}\n\t\t};\n\n\t\tfunction setModeClick() {\n\n\t\t\tmodePanel = modePanel ? false : true;\n\t\t\tsetClass(lblTarget, \"nodisplay\", modePanel);\n\t\t\tswitchMainView(lblMode, lblModeAttributes, options.labels.mode, options.labels.left, options.labels.right, modePanel);\n\n\t\t\tif (modePanel) {\n\n\t\t\t\tdocument.getElementById(\"btnLeft\").onclick = function() {\n\t\t\t\t\tmode = state.mode;\n\t\t\t\t\tmode = --mode < 0 ? properties.modes.length - 1 : mode;\n\t\t\t\t\tconsole.log(\"MODE :\" + mode);\n\t\t\t\t\tsetModeName(properties.modeNames[mode]);\n\t\t\t\t\tchkSwitchState();\n\t\t\t\t\tsendMsg();\n\t\t\t\t};\n\n\t\t\t\tdocument.getElementById(\"btnRight\").onclick = function() {\n\t\t\t\t\tmode = state.mode;\n\t\t\t\t\tmode = ++mode > properties.modes.length - 1 ? 0 : mode;\n\t\t\t\t\tconsole.log(\"MODE :\" + mode);\n\t\t\t\t\tsetModeName(properties.modeNames[mode]);\n\t\t\t\t\tchkSwitchState();\n\t\t\t\t\tsendMsg();\n\t\t\t\t};\n\n\t\t\t\tlblMode.onclick = function() {\n\t\t\t\t\tsetModeClick();\n\t\t\t\t};\n\t\t\t} else {\n\t\t\t\tresetButton()\n\t\t\t}\n\t\t};\n\n\t\tfunction setModeName(modeName) {\n\t\t\tlblMode.textContent = properties.modes[properties.modeNames.indexOf(modeName)].icon;\n\t\t\tlblMode.style.fill = properties.modes[properties.modeNames.indexOf(modeName)].color;\n\t\t\tstate.mode = properties.modeNames.indexOf(modeName);\n\t\t};\n\t\t\n\t\tfunction sendMsg() {\n\t\t\tif (typeof options.onChangeState == 'function') {\n\t\t\t\toptions.onChangeState(state.switch_state);\n\t\t\t}\n\t\t};\n\n\t\tfunction render() {\n\t\t\tconsole.log(\"RENDER\");\n\t\t\tsetAmbientTemperature(self.ambient_temperature);\n\t\t\tsetTargetTemperature(self.target_temperature);\n\t\t\tsetModeName(self.mode_name);\n\t\t\tchkSwitchState();\n\t\t};\n\n\t};\n})();\n\nvar initializing = true;\n\n(function(scope) {\n\tvar ghostThermostat = new ghostThermostatDial(document.getElementById('GhostThermostat'), {\n\t\tonChangeState: function() {\n\t\t\tvar p = {\n\t\t\t\t\"ambient_temperature\": ghostThermostat.ambient_temperature,\n\t\t\t\t\"target_temperature\": ghostThermostat.target_temperature,\n\t\t\t\t\"mode\": ghostThermostat.mode_name,\n\t\t\t\t\"switch_state\": ghostThermostat.switch_state,\n\t\t\t\t\"away\": ghostThermostat.away\n\t\t\t};\n\t\t\tscope.send({\n\t\t\t\ttopic: \"changed_state\",\n\t\t\t\tpayload: p\n\t\t\t});\n\t\t}\n\t});\n\n\tscope.$watch('msg', function(data) {\n\t\tif (initializing) {\n\t\t\tinitializing = false;\n\t\t} else {\n\t\t\tghostThermostat.ambient_temperature = data.payload.ambient_temperature || ghostThermostat.ambient_temperature;\n\t\t\tghostThermostat.target_temperature = data.payload.target_temperature || ghostThermostat.target_temperature;\n\t\t\tghostThermostat.mode_name = data.payload.mode || ghostThermostat.mode_name;\n\t\t\tghostThermostat.switch_state = data.payload.switch_state || ghostThermostat.switch_state;\n\t\t\tghostThermostat.away = data.payload.away || ghostThermostat.away;\n\t\t}\n\t});\n})(scope);\n</script>","storeOutMessages":true,"fwdInMessages":false,"resendOnRefresh":true,"templateScope":"local","x":804.0086669921875,"y":169.00172901153564,"wires":[["2b1cbd00e389c687","aef789a242325754"]],"icon":"font-awesome/fa-tachometer"},{"id":"2b1cbd00e389c687","type":"debug","z":"f6858fe416b029b9","name":"","active":false,"tosidebar":true,"console":false,"tostatus":true,"complete":"payload","targetType":"msg","statusVal":"payload","statusType":"auto","x":1011.5607604980469,"y":135.92111492156982,"wires":[]},{"id":"8f8949fff0292467","type":"function","z":"f6858fe416b029b9","name":"","func":"msg.topic = 'ambient_temperature'; \nvar data = {\n 'ambient_temperature':msg.payload || 20\n}\nmsg.payload = data; \nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":945.5625381469727,"y":66.82209968566895,"wires":[["223f3f0c0674a890","ab8fb1c28921f1dd"]]},{"id":"223f3f0c0674a890","type":"debug","z":"f6858fe416b029b9","name":"function","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":1101.566017150879,"y":62.9228401184082,"wires":[]},{"id":"aef789a242325754","type":"switch","z":"f6858fe416b029b9","name":"","property":"payload.switch_state","propertyType":"msg","rules":[{"t":"cont","v":"cooling","vt":"str"},{"t":"cont","v":"off","vt":"str"},{"t":"cont","v":"heating","vt":"str"}],"checkall":"true","repair":false,"outputs":3,"x":727.564453125,"y":247.81000328063965,"wires":[["ea0c1c476b61d5db"],["e4430035a5268ed6"],["279e59c6d23e9b46"]]},{"id":"ea0c1c476b61d5db","type":"change","z":"f6858fe416b029b9","name":"change","rules":[{"t":"change","p":"payload.switch_state","pt":"msg","from":"cooling","fromt":"str","to":"ON","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":903.5660629272461,"y":220.79778385162354,"wires":[["e9602ada53918b2c"]]},{"id":"e4430035a5268ed6","type":"change","z":"f6858fe416b029b9","name":"change2","rules":[{"t":"change","p":"payload.switch_state","pt":"msg","from":"off","fromt":"str","to":"OFF","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":900.5590972900391,"y":261.7995414733887,"wires":[["cf7a782de20a6415"]]},{"id":"279e59c6d23e9b46","type":"change","z":"f6858fe416b029b9","name":"change3","rules":[{"t":"change","p":"payload.switch_state","pt":"msg","from":"heating","fromt":"str","to":"ON","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":906.5608253479004,"y":307.80304527282715,"wires":[["956497398d39b36a"]]},{"id":"e9602ada53918b2c","type":"change","z":"f6858fe416b029b9","name":"","rules":[{"t":"move","p":"payload.switch_state","pt":"msg","to":"payload","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":1124.5660552978516,"y":184.80297470092773,"wires":[["851ba48d8fe01521","a1935b0f08af8408"]]},{"id":"cf7a782de20a6415","type":"change","z":"f6858fe416b029b9","name":"","rules":[{"t":"move","p":"payload.switch_state","pt":"msg","to":"payload","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":1121.0087051391602,"y":257.00175380706787,"wires":[["a518789a41e9ab86","a1935b0f08af8408"]]},{"id":"956497398d39b36a","type":"change","z":"f6858fe416b029b9","name":"","rules":[{"t":"move","p":"payload.switch_state","pt":"msg","to":"payload","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":1126.0087356567383,"y":317.00177001953125,"wires":[[]]},{"id":"851ba48d8fe01521","type":"debug","z":"f6858fe416b029b9","name":"sendon","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":1364.55908203125,"y":139.91928262939453,"wires":[]},{"id":"a518789a41e9ab86","type":"debug","z":"f6858fe416b029b9","name":"sendoff","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":1366.5643310546875,"y":301.92448587646487,"wires":[]},{"id":"7dc3530e.8f07a4","type":"ui_group","name":"control","tab":"6b25e5f4.b44ecc","order":1,"disp":true,"width":"24","collapse":false},{"id":"31a99116.50a74e","type":"mqtt-broker","broker":"localhost","port":"1883","clientid":"","usetls":false,"compatmode":true,"keepalive":"60","cleansession":true,"birthTopic":"","birthQos":"0","birthPayload":"","willTopic":"","willQos":"0","willPayload":""},{"id":"6b25e5f4.b44ecc","type":"ui_tab","name":"Tasmota Control","icon":"dashboard","disabled":false,"hidden":false}]
It's not a pretty flow. but this seams to work for my cooling needs if it is in fact turning on and off my relay connected to my eps8266
I wont use this for Heat because I have to run PWM so i dont burn my liquid.
but for cooling all i need is to have a valve open and close.
this is going to totally wicked!
apparently i need a Thinking Out Forum moment(like thinking out loud but in forum)
TOF
so i noticed that the switch node will show on. but in editor it says "on:off"
silly me didnt make sure the msg.payload had the proper topic to actually carry out the command in tasmota. so i added another change node.
Here is updated flow.
[{"id":"f6858fe416b029b9","type":"tab","label":"Flow 4","disabled":false,"info":"","env":[]},{"id":"a1935b0f08af8408","type":"ui_switch","z":"f6858fe416b029b9","name":"Wferm","label":"Wferm","tooltip":"","group":"10c36bea9197e264","order":8,"width":6,"height":1,"passthru":false,"decouple":"true","topic":"cmnd/Wferm/POWER","style":"","onvalue":"ON","onvalueType":"str","onicon":"","oncolor":"","offvalue":"OFF","offvalueType":"str","officon":"","offcolor":"","x":400.6666679382324,"y":322.2500858306885,"wires":[["4349ab2a681252f2","dcb2c21aa32eec71"]]},{"id":"4349ab2a681252f2","type":"mqtt out","z":"f6858fe416b029b9","name":"","topic":"","qos":"","retain":"","respTopic":"","contentType":"","userProps":"","correl":"","expiry":"","broker":"31a99116.50a74e","x":565.2381553649902,"y":374.0000877380371,"wires":[]},{"id":"ce5c66f008f4cbf3","type":"mqtt in","z":"f6858fe416b029b9","name":"","topic":"stat/Wferm/POWER","qos":"1","datatype":"auto","broker":"31a99116.50a74e","nl":false,"rap":false,"inputs":0,"x":211.23807525634766,"y":279.0000858306885,"wires":[["a1935b0f08af8408","5a422122ed052f7d"]]},{"id":"dcb2c21aa32eec71","type":"debug","z":"f6858fe416b029b9","name":"out","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":562.2381935119629,"y":325.00004482269287,"wires":[]},{"id":"5a422122ed052f7d","type":"debug","z":"f6858fe416b029b9","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":424.80228424072266,"y":279.9245195388794,"wires":[]},{"id":"987d16ece3fa2e58","type":"ui_digital_display","z":"f6858fe416b029b9","name":"","group":"10c36bea9197e264","order":3,"width":6,"height":1,"digits":5,"decimals":1,"x":738.5644187927246,"y":30.934907913208008,"wires":[]},{"id":"c978949d90d42b1e","type":"mqtt in","z":"f6858fe416b029b9","name":"","topic":"tele/Wferm/SENSOR","qos":"2","datatype":"auto","broker":"31a99116.50a74e","nl":false,"rap":true,"rh":0,"inputs":0,"x":126.56424713134766,"y":27.83073902130127,"wires":[["4c4294bd3b2d8cf3","a1bfb15585d7eb59"]]},{"id":"93a3046bdaeff2c8","type":"debug","z":"f6858fe416b029b9","name":"","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":635.5643692016602,"y":253.9210376739502,"wires":[]},{"id":"65f9fd5be1a51434","type":"change","z":"f6858fe416b029b9","name":"set payload to Temperature value","rules":[{"t":"move","p":"payload.Temperature","pt":"msg","to":"payload","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":468.0089569091797,"y":181.0018081665039,"wires":[["987d16ece3fa2e58","93a3046bdaeff2c8","8f8949fff0292467"]]},{"id":"4c4294bd3b2d8cf3","type":"debug","z":"f6858fe416b029b9","name":"","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":324.56079483032227,"y":20,"wires":[]},{"id":"a1bfb15585d7eb59","type":"json","z":"f6858fe416b029b9","name":"","property":"payload","action":"","pretty":false,"x":137.00869178771973,"y":134.33507919311523,"wires":[["27d0a195ec58a6a1"]]},{"id":"bb44589f1917700d","type":"switch","z":"f6858fe416b029b9","name":"If =442FBOIL","property":"payload.Id","propertyType":"msg","rules":[{"t":"cont","v":"3C01F096FCA3","vt":"str"},{"t":"else"}],"checkall":"true","repair":false,"outputs":2,"x":339.11983489990234,"y":103.00185203552246,"wires":[["65f9fd5be1a51434"],[]]},{"id":"27d0a195ec58a6a1","type":"split","z":"f6858fe416b029b9","name":"","splt":"\\n","spltType":"str","arraySplt":1,"arraySpltType":"len","stream":false,"addname":"","x":150.11634826660156,"y":192.97664165496826,"wires":[["bb44589f1917700d"]]},{"id":"ab8fb1c28921f1dd","type":"ui_template","z":"f6858fe416b029b9","group":"10c36bea9197e264","name":"WestFermenter","order":2,"width":6,"height":6,"format":"<style>\n @import url('https://fonts.googleapis.com/css2?family=Roboto:ital,wght@0,100;0,300;0,400;0,500;0,700;0,900;1,100;1,300;1,400;1,500;1,700;1,900&display=swap');\n \n svg {\n transition: all .6s cubic-bezier(0.175, 0.885, 0.32, 1.2);\n }\n\n stop {\n transition: all .5s;\n }\n \n\t.led {\n \t-webkit-transition: all 0.5s;\n \ttransition: all 0.5s;\n \tfill: url(#ledColor);\n }\n \n .fa-text {\n font-family: FontAwesome !important; \n }\n .dial {\n -webkit-user-select: none;\n -moz-user-select: none;\n -ms-user-select: none;\n user-select: none;\n }\n .qGradient {\n fill : url(#qGradient);\n }\n .qGradientT {\n fill : url(#qGradientT);\n }\n .eGradient {\n fill : url(#eGradient);\n }\n .lbl {\n font-family: 'Roboto', sans-serif;\n text-anchor: middle;\n fill : #ffffff;\n clip-path: url(#qClip);\n }\n .lblDial {\n fill: #dddddd;\n }\n \n .lblAmbient {\n font-weight: 400;\n clip-path: url(#qClip);\n }\n \n .lblAmbient tspan {\n font-weight: 400;\n }\n \n .lblTarget {\n font-weight: 400;\n fill: orange;\n }\n \n .lblTarget tspan {\n font-weight: 400;\n fill: orange;\n clip-path: url(#qClip);\n } \n \n .nodisplay {\n display: none !important;\n }\n \n .icon {\n font-family: FontAwesome !important;\n }\n \n .animate {\n transition: all 0.5s;\n }\n\n</style>\n<div id=\"GhostThermostat\"></div> \n<script>\nvar mousedownID = -1;\nvar ghostThermostatDial = (function() {\n\tconsole.log(\"START\");\n\n\tfunction createSVGElement(tag, attributes, appendTo) {\n\t\tvar element = document.createElementNS('http://www.w3.org/2000/svg', tag);\n\t\tattr(element, attributes);\n\t\tif (appendTo) {\n\t\t\tappendTo.appendChild(element);\n\t\t}\n\t\treturn element;\n\t}\n\n\tfunction attr(element, attrs) {\n\t\tfor (var i in attrs) {\n\t\t\telement.setAttribute(i, attrs[i]);\n\t\t}\n\t}\n\n\tfunction setClass(el, className, state) {\n\t\tel.classList[state ? 'add' : 'remove'](className);\n\t}\n\n\treturn function(targetElement, options) {\n\t\tconsole.log(\"RET FUN\");\n\t\tvar self = this;\n\n\t\t/*\n\t\t * Options\n\t\t */\n\t\toptions = options || {};\n\t\toptions = {\n\t\t\tdiameter: options.diameter || 400,\n\t\t\tmintemp: options.mintemp || 0, // Minimum value for target temperature\n\t\t\tmaxtemp: options.maxtemp || 200, // Maximum value for target temperature\n\t\t\tledColors: {\n\t\t\t\t'off': 'rgb(143,141,141)',\n\t\t\t\t'heating': 'rgb(255,128,0)',\n\t\t\t\t'cooling': 'rgb(81,170,214)'\n\t\t\t}, //Led Ring Colors\n\t\t\tlabels: {\n\t\t\t\tambient: \"AMBIENT\",\n\t\t\t\tset: \"SET\",\n\t\t\t\tmode: \"MODE\",\n\t\t\t\tminus: \"-\",\n\t\t\t\tplus: \"+\",\n\t\t\t\tleft: \"<\",\n\t\t\t\tright: \">\"\n\t\t\t},\n\t\t\tonChangeState: options.onChangeState || function() {} // Function called when switch state change\n\t\t};\n\n\t\t/*\n\t\t * Properties\n\t\t */\n\t\tvar properties = {\n\t\t\tradius: options.diameter / 2,\n\t\t\tmodes: [{\n\t\t\t\t\tlabel: \"heating\",\n\t\t\t\t\ticon: \"\\uf06d\",\n\t\t\t\t\tcolor: \"orange\"\n\t\t\t\t}, {\n\t\t\t\t\tlabel: 'cooling',\n\t\t\t\t\ticon: \"\\uf2dc\",\n\t\t\t\t\tcolor: \"rgb(81,170,214)\"\n\t\t\t\t}, {\n\t\t\t\t\tlabel: \"off\",\n\t\t\t\t\ticon: \"\\uf011\",\n\t\t\t\t\tcolor: \"rgb(230,0,0)\"\n\t\t\t\t}\n\t\t\t\t/*, {\n\t\t\t\tlabel: 'away',\n\t\t\t\ticon: \"\\uf1ce\",\n\t\t\t\tcolor: \"gray\"\n\t\t\t} */\n\t\t\t],\n\t\t\tmodeNames: [\"heating\", \"cooling\", \"off\"],\n\t\t\tswtitchStates: [\"heating\", \"cooling\", \"off\"]\n\t\t};\n\n\t\t/*\n\t\t * Object state\n\t\t */\n\t\tvar state = {\n\t\t\ttarget_temperature: options.mintemp,\n\t\t\tambient_temperature: options.maxtemp,\n\t\t\tmode: properties.modes.indexOf(properties.modes[0]),\n\t\t\tswitch_state: 'off',\n\t\t\taway: false\n\t\t};\n\n\t\t/*\n\t\t * Property getter / setters\n\t\t */\n\t\tObject.defineProperty(this, 'target_temperature', {\n\t\t\tget: function() {\n\t\t\t\treturn state.target_temperature;\n\t\t\t},\n\t\t\tset: function(val) {\n\t\t\t\tstate.target_temperature = rangedTemperature(+val);\n\t\t\t\t//render()\n\t\t\t}\n\t\t});\n\n\t\tObject.defineProperty(this, 'ambient_temperature', {\n\t\t\tget: function() {\n\t\t\t\treturn state.ambient_temperature;\n\t\t\t},\n\t\t\tset: function(val) {\n\t\t\t\tstate.ambient_temperature = +val;\n\t\t\t\trender();\n\t\t\t}\n\t\t});\n\n\t\tObject.defineProperty(this, 'mode_name', {\n\t\t\tget: function() {\n\t\t\t\treturn properties.modeNames[state.mode];\n\t\t\t},\n\t\t\tset: function(val) {\n\t\t\t\tif (properties.modeNames.indexOf(val) >= 0) {\n\t\t\t\t\tstate.mode = properties.modeNames.indexOf(val);\n\t\t\t\t\t//render();\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\n\t\tObject.defineProperty(this, 'switch_state', {\n\t\t\tget: function() {\n\t\t\t\treturn state.switch_state;\n\t\t\t},\n\t\t\tset: function(val) {\n\t\t\t\tif (properties.swtitchStates.indexOf(val) >= 0) {\n\t\t\t\t\tstate.switch_state = val;\n\t\t\t\t\t//render();\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\n\n\t\tfunction str2bool(strvalue) {\n\t\t\treturn (strvalue && typeof strvalue == 'string') ? (strvalue.toLowerCase() == 'true') : (strvalue == true);\n\t\t}\n\n\t\tObject.defineProperty(this, 'away', {\n\t\t\tget: function() {\n\t\t\t\treturn state.away;\n\t\t\t},\n\t\t\tset: function(val) {\n\t\t\t\tstate.away = !!str2bool(val);\n\t\t\t\t//render();\n\t\t\t}\n\t\t});\n\n\n\t\t/*\n\t\t * SVG\n\t\t */\n\t\tvar svg = createSVGElement('svg', {\n\t\t\twidth: '100%', //options.diameter+'px',\n\t\t\theight: '100%', //options.diameter+'px',\n\t\t\tviewBox: '0 0 ' + options.diameter + ' ' + options.diameter,\n\t\t\tclass: 'dial'\n\t\t}, targetElement);\n\n\t\t// DEFS \n\t\tvar defs = createSVGElement('defs', null, svg);\n\n\t\tvar qgradient = createSVGElement('linearGradient', {\n\t\t\t'id': 'qGradient',\n\t\t\tgradientTransform: 'rotate(65)'\n\t\t}, defs);\n\t\tvar stop = createSVGElement('stop', {\n\t\t\t'offset': '50%',\n\t\t\t'stop-color': 'rgb(86,89,94)'\n\t\t}, qgradient);\n\t\tvar stop = createSVGElement('stop', {\n\t\t\t'offset': '65%',\n\t\t\t'stop-color': 'rgb(30,30,30)'\n\t\t}, qgradient);\n\n\t\tvar qGradientT = createSVGElement('linearGradient', {\n\t\t\t'id': 'qGradientT',\n\t\t\tgradientTransform: 'rotate(65)'\n\t\t}, defs);\n\t\tvar stop = createSVGElement('stop', {\n\t\t\t'offset': '55%',\n\t\t\t'stop-color': '#3b3e43',\n\t\t\t'stop-opacity': '1'\n\t\t}, qGradientT);\n\t\tvar stop = createSVGElement('stop', {\n\t\t\t'offset': '90%',\n\t\t\t'stop-color': 'rgb(0,0,0)',\n\t\t\t'stop-opacity': '1'\n\t\t}, qGradientT);\n\n\t\tvar clipPath = createSVGElement('clipPath', {\n\t\t\t'id': 'qClip',\n\t\t}, defs);\n\t\tvar circle = createSVGElement('circle', {\n\t\t\tcx: properties.radius,\n\t\t\tcy: properties.radius,\n\t\t\tr: properties.radius - 25\n\t\t}, clipPath);\n\n\n\t\tvar ledRingGradient = createSVGElement('radialGradient', {\n\t\t\t'id': 'ledColor',\n\t\t\t'cx': \"50%\",\n\t\t\t'cy': \"50%\",\n\t\t\t'r': \"95%\",\n\t\t\t'fx': \"50%\",\n\t\t\t'fy': \"50%\"\n\t\t}, defs);\n\t\tvar ledRingGradientColorIn = createSVGElement('stop', {\n\t\t\t'offset': '45%',\n\t\t\t'stop-color': 'rgb(255,0,130)',\n\t\t\t'stop-opacity': '1'\n\t\t}, ledRingGradient);\n\t\tvar ledRingGradientColorOut = createSVGElement('stop', {\n\t\t\t'offset': '65%',\n\t\t\t'stop-color': 'rgb(0,0,0)',\n\t\t\t'stop-opacity': '1'\n\t\t}, ledRingGradient);\n\n\t\tvar egradient = createSVGElement('linearGradient', {\n\t\t\t'id': 'eGradient',\n\t\t\tgradientTransform: 'rotate(55)'\n\t\t}, defs);\n\t\tvar stop = createSVGElement('stop', {\n\t\t\t'offset': '55%',\n\t\t\t'stop-color': '#888888',\n\t\t\t'stop-opacity': '1'\n\t\t}, egradient);\n\t\tvar stop = createSVGElement('stop', {\n\t\t\t'offset': '95%',\n\t\t\t'stop-color': '#333333',\n\t\t\t'stop-opacity': '1'\n\t\t}, egradient);\n\n\t\t// DIAL\n\t\tvar circle = createSVGElement('circle', {\n\t\t\tcx: properties.radius,\n\t\t\tcy: properties.radius,\n\t\t\tr: properties.radius,\n\t\t\tclass: 'eGradient'\n\t\t}, svg);\n\t\tvar ledRing = createSVGElement('circle', {\n\t\t\tcx: properties.radius,\n\t\t\tcy: properties.radius,\n\t\t\tr: properties.radius - 3,\n\t\t\t'stroke': 'black',\n\t\t\t'stroke-width': '1',\n\t\t\tclass: 'led'\n\t\t}, svg);\n\t\tvar circle = createSVGElement('circle', {\n\t\t\tcx: properties.radius,\n\t\t\tcy: properties.radius,\n\t\t\tr: properties.radius - 20,\n\t\t\tclass: 'qGradient'\n\t\t}, svg);\n\t\tvar circle = createSVGElement('circle', {\n\t\t\tcx: properties.radius,\n\t\t\tcy: properties.radius,\n\t\t\tr: properties.radius - 25,\n\t\t\tclass: 'qGradient'\n\t\t}, svg);\n\t\tvar lblMain = createSVGElement('text', {\n\t\t\tx: properties.radius,\n\t\t\ty: 70,\n\t\t\tclass: 'lbl lblDial'\n\t\t}, svg);\n\t\tvar lblMainText = document.createTextNode(options.labels.ambient);\n\t\tlblMain.appendChild(lblMainText);\n\n\t\tvar lblAmbient = createSVGElement('text', {\n\t\t\tx: properties.radius,\n\t\t\ty: 210,\n\t\t\t'font-size': '160',\n\t\t\tclass: 'lbl lblAmbient'\n\t\t}, svg);\n\t\tvar lblAmbientText = document.createTextNode('21');\n\t\tlblAmbient.appendChild(lblAmbientText);\n\t\tvar lblAmbientDec = createSVGElement('tspan', {\n\t\t\t'font-size': '60',\n\t\t}, lblAmbient);\n\t\tvar lblAmbientDecText = document.createTextNode('.5');\n\t\tlblAmbientDec.appendChild(lblAmbientDecText);\n\n\t\tvar line = createSVGElement('line', {\n\t\t\tx1: 55,\n\t\t\ty1: properties.radius + 35,\n\t\t\tx2: options.diameter - 55,\n\t\t\ty2: properties.radius + 35,\n\t\t\t'stroke': '#DDDDDD',\n\t\t\t'stroke-width': '1',\n\t\t\t'opacity': '0.8'\n\t\t}, svg);\n\n\t\tvar lblLeft = createSVGElement('text', {\n\t\t\tx: 125,\n\t\t\ty: properties.radius + 75,\n\t\t\tclass: 'lbl lblDial'\n\t\t}, svg);\n\t\tvar lblLeftText = document.createTextNode(options.labels.set);\n\t\tlblLeft.appendChild(lblLeftText);\n\n\t\tvar lblTarget = createSVGElement('text', {\n\t\t\tx: 125,\n\t\t\ty: properties.radius + 115,\n\t\t\t'font-size': '35',\n\t\t\tclass: 'lbl lblTarget'\n\t\t}, svg);\n\t\tvar lblTargetText = document.createTextNode('20');\n\t\tlblTarget.appendChild(lblTargetText);\n\n\t\tvar lblTargetDec = createSVGElement('tspan', {\n\t\t\t'font-size': '20',\n\t\t}, lblTarget);\n\n\t\tvar lblTargetDecText = document.createTextNode('.5');\n\t\tlblTargetDec.appendChild(lblTargetDecText);\n\n\t\tvar lblRight = createSVGElement('text', {\n\t\t\tx: options.diameter - 125,\n\t\t\ty: properties.radius + 75,\n\t\t\tclass: 'lbl lblDial'\n\t\t}, svg);\n\t\tvar lblRightText = document.createTextNode(options.labels.mode);\n\t\tlblRight.appendChild(lblRightText);\n\n\t\tvar lblMode = createSVGElement('text', {\n\t\t\tx: options.diameter - 125,\n\t\t\ty: properties.radius + 115,\n\t\t\t'font-size': '35',\n\t\t\tclass: 'lbl lblTarget icon'\n\t\t}, svg);\n\t\tvar lblModeText = document.createTextNode(properties.modes[0].icon);\n\t\tlblMode.appendChild(lblModeText);\n\n\t\tvar btnSet = createSVGElement('g', {\n\t\t\ttransform: 'translate(200,200)'\n\t\t}, svg);\n\t\tvar path = createSVGElement('path', {\n\t\t\td: 'M0,40 L0,175 A175,175 0 0,1 -175,40 z',\n\t\t\tfill: 'blue',\n\t\t\topacity: '0',\n\t\t\t'id': 'btnLeft'\n\t\t}, btnSet);\n\t\tvar path = createSVGElement('path', {\n\t\t\td: 'M0,40 L175,40 A175,175 0 0,1 0,175 z',\n\t\t\tfill: 'red',\n\t\t\topacity: '0',\n\t\t\t'id': 'btnRight'\n\t\t}, btnSet);\n\n\n\n\t\tdocument.getElementById(\"btnLeft\").onclick = function() {\n\t\t\tsetTargetClick();\n\t\t};\n\n\t\tdocument.getElementById(\"btnRight\").onclick = function() {\n\t\t\tsetModeClick();\n\t\t};\n\n\t\tvar targetPanel = false;\n\t\tvar modePanel = false;\n\n\t\tvar lblAmbientAttributes = {\n\t\t\tx: lblAmbient.getAttribute('x'),\n\t\t\ty: lblAmbient.getAttribute('y'),\n\t\t\tsize: lblAmbient.getAttribute('font-size')\n\t\t};\n\n\t\tvar lblAmbientDecAttributes = {\n\t\t\tx: lblAmbientDec.getAttribute('x'),\n\t\t\ty: lblAmbientDec.getAttribute('y'),\n\t\t\tsize: lblAmbientDec.getAttribute('font-size')\n\t\t};\n\n\t\tvar lblTargetAttributes = {\n\t\t\tx: lblTarget.getAttribute('x'),\n\t\t\ty: lblTarget.getAttribute('y'),\n\t\t\tsize: lblTarget.getAttribute('font-size')\n\t\t};\n\n\t\tvar lblTargetDecAttributes = {\n\t\t\tx: lblTargetDec.getAttribute('x'),\n\t\t\ty: lblTargetDec.getAttribute('y'),\n\t\t\tsize: lblTargetDec.getAttribute('font-size')\n\t\t};\n\n\t\tvar lblModeAttributes = {\n\t\t\tx: lblMode.getAttribute('x'),\n\t\t\ty: lblMode.getAttribute('y'),\n\t\t\tsize: lblMode.getAttribute('font-size')\n\t\t};\n\n\t\tvar lblRightAttributes = {\n\t\t\tx: lblRight.getAttribute('x'),\n\t\t\ty: lblRight.getAttribute('y'),\n\t\t\tsize: lblRight.getAttribute('font-size')\n\t\t};\n\n\t\tvar lblLeftAttributes = {\n\t\t\tx: lblLeft.getAttribute('x'),\n\t\t\ty: lblLeft.getAttribute('y'),\n\t\t\tsize: lblLeft.getAttribute('font-size')\n\t\t};\n\n\t\trender();\n\n\t\tfunction setAmbientTemperature(ambientTemp) {\n\t\t\tvar splitValues = separateDecValue(ambientTemp);\n\t\t\tlblAmbientText.textContent = splitValues.int;\n\t\t\tlblAmbientDecText.textContent = splitValues.dec;\n\t\t};\n\n\n\t\tfunction calcTargetTemperature(operation) {\n\t\t\tlet currentTemp = Number(parseFloat(lblTargetText.textContent + lblTargetDecText.textContent)).toFixed(1);\n\t\t\tlet targetTemp = (operation == '-' ? Number(Number(currentTemp) - 0.5).toFixed(1) : Number(Number(currentTemp) + 0.5).toFixed(1));\n\t\t\ttargetTemp = rangedTemperature(targetTemp);\n\t\t\tsetTargetTemperature(targetTemp);\n\t\t\tchkSwitchState();\n\t\t};\n\n\t\tfunction setTargetTemperature(targetTemp) {\n\t\t\tvar splitValues = separateDecValue(targetTemp);\n\t\t\tlblTargetText.textContent = splitValues.int;\n\t\t\tlblTargetDecText.textContent = splitValues.dec;\n\t\t\tif (state.target_temperature != targetTemp) {\n\t\t\t\tstate.target_temperature = targetTemp\n\t\t\t\tsendMsg();\n\t\t\t};\n\t\t};\n\n\t\tfunction separateDecValue(floatFalue) {\n\t\t\tvar int = Math.floor(floatFalue);\n\t\t\tvar dec = Math.floor(((floatFalue % 1) * 10)) > 0 ? (\".\" + Math.floor(((floatFalue % 1) * 10))) : \"\";\n\t\t\treturn {\n\t\t\t\tint,\n\t\t\t\tdec\n\t\t\t};\n\t\t};\n\n\t\tfunction rangedTemperature(temperature) {\n\t\t\ttemperature = temperature < options.mintemp ? options.maxtemp : temperature;\n\t\t\ttemperature = temperature > options.maxtemp ? options.mintemp : temperature;\n\t\t\treturn temperature;\n\t\t};\n\n\t\tfunction chkSwitchState() {\n\t\t console.log(\"chkSwitchState\");\n\t\t\tvar switchState = state.switch_state;\n\t\t\tswitch (state.mode) {\n\t\t\t\tcase 0:\n\t\t\t\t\tswitchState = state.ambient_temperature < state.target_temperature ? 'heating' : 'off';\n\t\t\t\t\tbreak;\n\t\t\t\tcase 1:\n\t\t\t\t\tswitchState = state.ambient_temperature > state.target_temperature ? 'cooling' : 'off';\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\tswitchState = 'off';\n\t\t\t};\n\n\t\t\tledRingGradientColorIn.setAttribute('stop-color', options.ledColors[state.switch_state]);\n\n\t\t\tif (state.switch_state != switchState) {\n\t\t\t\tstate.switch_state = switchState;\n\t\t\t\tsendMsg();\n\t\t\t};\n\t\t};\n\n\n\t\tfunction resetButton() {\n\t\t\tdocument.getElementById(\"btnLeft\").onmousedown = \"\";\n\t\t\tdocument.getElementById(\"btnLeft\").onmouseup = \"\";\n\t\t\tdocument.getElementById(\"btnLeft\").onclick = function() {\n\t\t\t\tsetTargetClick();\n\t\t\t};\n\t\t\tdocument.getElementById(\"btnRight\").onmousedown = \"\";\n\t\t\tdocument.getElementById(\"btnRight\").onmouseup = \"\";\n\t\t\tdocument.getElementById(\"btnRight\").onclick = function() {\n\t\t\t\tsetModeClick();\n\t\t\t};\n\t\t};\n\n\t\tfunction switchMainView(element, originalAttributes, mainLabel, leftLabel, rightLabel, panelState) {\n\t\t\tsetClass(lblAmbient, \"nodisplay\", panelState);\n\t\t\tsetClass(lblMain, \"animate\", panelState);\n\t\t\tsetClass(lblLeft, \"animate\", panelState);\n\t\t\tsetClass(lblRight, \"animate\", panelState);\n\t\t\tsetClass(element, \"animate\", panelState);\n\n\t\t\tlblMainText.textContent = panelState ? mainLabel : options.labels.ambient;\n\t\t\tlblLeftText.textContent = panelState ? leftLabel : options.labels.set;\n\n\t\t\tlblLeft.setAttribute('y', panelState ? Number(lblLeftAttributes.y) + 40 : lblLeftAttributes.y);\n\t\t\tlblLeft.setAttribute('font-size', panelState ? \"3.5em\" : \"1em\");\n\n\t\t\tlblRightText.textContent = panelState ? rightLabel : options.labels.mode;\n\t\t\tlblRight.setAttribute('y', panelState ? Number(lblRightAttributes.y) + 40 : lblRightAttributes.y);\n\t\t\tlblRight.setAttribute('font-size', panelState ? \"3.5em\" : \"1em\");\n\n\t\t\telement.setAttribute('x', panelState ? lblAmbientAttributes.x : originalAttributes.x);\n\t\t\telement.setAttribute('x', panelState ? lblAmbientAttributes.x : originalAttributes.x);\n\t\t\telement.setAttribute('y', panelState ? lblAmbientAttributes.y : originalAttributes.y);\n\t\t\telement.setAttribute('font-size', panelState ? lblAmbientAttributes.size : originalAttributes.size);\n\n\t\t};\n\n\n\t\tfunction setTargetClick() {\n\n\t\t\ttargetPanel = targetPanel ? false : true;\n\t\t\tsetClass(lblMode, \"nodisplay\", targetPanel);\n\t\t\tswitchMainView(lblTarget, lblTargetAttributes, options.labels.set, options.labels.minus, options.labels.plus, targetPanel);\n\n\t\t\tlblTargetDec.setAttribute('font-size', targetPanel ? lblAmbientDecAttributes.size : lblTargetDecAttributes.size);\n\n\t\t\tif (targetPanel) {\n\t\t\t\tdocument.getElementById(\"btnLeft\").onclick = \"\";\n\t\t\t\tdocument.getElementById(\"btnRight\").onclick = \"\";\n\n\t\t\t\tdocument.getElementById(\"btnLeft\").onmousedown = function() {\n\t\t\t\t\tcalcTargetTemperature(\"-\");\n\t\t\t\t\tif (mousedownID == -1) { //Prevent multimple loops!\n\t\t\t\t\t\tmousedownID = setInterval(calcTargetTemperature, 500, '-');\n\t\t\t\t\t}\n\t\t\t\t};\n\t\t\t\tdocument.getElementById(\"btnLeft\").onmouseup = function() {\n\t\t\t\t\tif (mousedownID != -1) { //Only stop if exists\n\t\t\t\t\t\tclearInterval(mousedownID);\n\t\t\t\t\t\tmousedownID = -1;\n\t\t\t\t\t}\n\t\t\t\t};\n\n\t\t\t\tdocument.getElementById(\"btnRight\").onmousedown = function() {\n\t\t\t\t\tcalcTargetTemperature(\"+\");\n\t\t\t\t\tif (mousedownID == -1) { //Prevent multimple loops!\n\t\t\t\t\t\tmousedownID = setInterval(calcTargetTemperature, 500, '+');\n\t\t\t\t\t}\n\t\t\t\t};\n\t\t\t\tdocument.getElementById(\"btnRight\").onmouseup = function() {\n\t\t\t\t\tif (mousedownID != -1) { //Only stop if exists\n\t\t\t\t\t\tclearInterval(mousedownID);\n\t\t\t\t\t\tmousedownID = -1;\n\t\t\t\t\t}\n\t\t\t\t};\n\n\t\t\t\tlblTarget.onclick = function() {\n\t\t\t\t\tsetTargetClick();\n\t\t\t\t};\n\t\t\t} else {\n\t\t\t\tresetButton()\n\t\t\t}\n\t\t};\n\n\t\tfunction setModeClick() {\n\n\t\t\tmodePanel = modePanel ? false : true;\n\t\t\tsetClass(lblTarget, \"nodisplay\", modePanel);\n\t\t\tswitchMainView(lblMode, lblModeAttributes, options.labels.mode, options.labels.left, options.labels.right, modePanel);\n\n\t\t\tif (modePanel) {\n\n\t\t\t\tdocument.getElementById(\"btnLeft\").onclick = function() {\n\t\t\t\t\tmode = state.mode;\n\t\t\t\t\tmode = --mode < 0 ? properties.modes.length - 1 : mode;\n\t\t\t\t\tconsole.log(\"MODE :\" + mode);\n\t\t\t\t\tsetModeName(properties.modeNames[mode]);\n\t\t\t\t\tchkSwitchState();\n\t\t\t\t\tsendMsg();\n\t\t\t\t};\n\n\t\t\t\tdocument.getElementById(\"btnRight\").onclick = function() {\n\t\t\t\t\tmode = state.mode;\n\t\t\t\t\tmode = ++mode > properties.modes.length - 1 ? 0 : mode;\n\t\t\t\t\tconsole.log(\"MODE :\" + mode);\n\t\t\t\t\tsetModeName(properties.modeNames[mode]);\n\t\t\t\t\tchkSwitchState();\n\t\t\t\t\tsendMsg();\n\t\t\t\t};\n\n\t\t\t\tlblMode.onclick = function() {\n\t\t\t\t\tsetModeClick();\n\t\t\t\t};\n\t\t\t} else {\n\t\t\t\tresetButton()\n\t\t\t}\n\t\t};\n\n\t\tfunction setModeName(modeName) {\n\t\t\tlblMode.textContent = properties.modes[properties.modeNames.indexOf(modeName)].icon;\n\t\t\tlblMode.style.fill = properties.modes[properties.modeNames.indexOf(modeName)].color;\n\t\t\tstate.mode = properties.modeNames.indexOf(modeName);\n\t\t};\n\t\t\n\t\tfunction sendMsg() {\n\t\t\tif (typeof options.onChangeState == 'function') {\n\t\t\t\toptions.onChangeState(state.switch_state);\n\t\t\t}\n\t\t};\n\n\t\tfunction render() {\n\t\t\tconsole.log(\"RENDER\");\n\t\t\tsetAmbientTemperature(self.ambient_temperature);\n\t\t\tsetTargetTemperature(self.target_temperature);\n\t\t\tsetModeName(self.mode_name);\n\t\t\tchkSwitchState();\n\t\t};\n\n\t};\n})();\n\nvar initializing = true;\n\n(function(scope) {\n\tvar ghostThermostat = new ghostThermostatDial(document.getElementById('GhostThermostat'), {\n\t\tonChangeState: function() {\n\t\t\tvar p = {\n\t\t\t\t\"ambient_temperature\": ghostThermostat.ambient_temperature,\n\t\t\t\t\"target_temperature\": ghostThermostat.target_temperature,\n\t\t\t\t\"mode\": ghostThermostat.mode_name,\n\t\t\t\t\"switch_state\": ghostThermostat.switch_state,\n\t\t\t\t\"away\": ghostThermostat.away\n\t\t\t};\n\t\t\tscope.send({\n\t\t\t\ttopic: \"changed_state\",\n\t\t\t\tpayload: p\n\t\t\t});\n\t\t}\n\t});\n\n\tscope.$watch('msg', function(data) {\n\t\tif (initializing) {\n\t\t\tinitializing = false;\n\t\t} else {\n\t\t\tghostThermostat.ambient_temperature = data.payload.ambient_temperature || ghostThermostat.ambient_temperature;\n\t\t\tghostThermostat.target_temperature = data.payload.target_temperature || ghostThermostat.target_temperature;\n\t\t\tghostThermostat.mode_name = data.payload.mode || ghostThermostat.mode_name;\n\t\t\tghostThermostat.switch_state = data.payload.switch_state || ghostThermostat.switch_state;\n\t\t\tghostThermostat.away = data.payload.away || ghostThermostat.away;\n\t\t}\n\t});\n})(scope);\n</script>","storeOutMessages":true,"fwdInMessages":false,"resendOnRefresh":false,"templateScope":"local","x":784.0087127685547,"y":127.00176239013672,"wires":[["2b1cbd00e389c687","aef789a242325754"]],"icon":"font-awesome/fa-tachometer"},{"id":"2b1cbd00e389c687","type":"debug","z":"f6858fe416b029b9","name":"","active":false,"tosidebar":true,"console":false,"tostatus":true,"complete":"payload","targetType":"msg","statusVal":"payload","statusType":"auto","x":965.5608024597168,"y":82.92111587524414,"wires":[]},{"id":"8f8949fff0292467","type":"function","z":"f6858fe416b029b9","name":"","func":"msg.topic = 'ambient_temperature'; \nvar data = {\n 'ambient_temperature':msg.payload || 20\n}\nmsg.payload = data; \nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":756.562629699707,"y":78.822096824646,"wires":[["223f3f0c0674a890","ab8fb1c28921f1dd"]]},{"id":"223f3f0c0674a890","type":"debug","z":"f6858fe416b029b9","name":"function","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":935.5661315917969,"y":33.922842025756836,"wires":[]},{"id":"aef789a242325754","type":"switch","z":"f6858fe416b029b9","name":"","property":"payload.switch_state","propertyType":"msg","rules":[{"t":"cont","v":"cooling","vt":"str"},{"t":"cont","v":"off","vt":"str"},{"t":"cont","v":"heating","vt":"str"}],"checkall":"true","repair":false,"outputs":3,"x":777.5645751953125,"y":201.81002616882324,"wires":[["ea0c1c476b61d5db"],["e4430035a5268ed6"],["279e59c6d23e9b46"]]},{"id":"ea0c1c476b61d5db","type":"change","z":"f6858fe416b029b9","name":"change","rules":[{"t":"change","p":"payload.switch_state","pt":"msg","from":"cooling","fromt":"str","to":"ON","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":1072.5660667419434,"y":132.79781913757324,"wires":[["e9602ada53918b2c"]]},{"id":"e4430035a5268ed6","type":"change","z":"f6858fe416b029b9","name":"change2","rules":[{"t":"change","p":"payload.switch_state","pt":"msg","from":"off","fromt":"str","to":"OFF","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":1066.559310913086,"y":265.79957580566406,"wires":[["cf7a782de20a6415"]]},{"id":"279e59c6d23e9b46","type":"change","z":"f6858fe416b029b9","name":"change3","rules":[{"t":"change","p":"payload.switch_state","pt":"msg","from":"heating","fromt":"str","to":"ON","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":1061.560935974121,"y":355.8030643463135,"wires":[["956497398d39b36a"]]},{"id":"e9602ada53918b2c","type":"change","z":"f6858fe416b029b9","name":"","rules":[{"t":"move","p":"payload.switch_state","pt":"msg","to":"payload","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":1133.566146850586,"y":177.80300903320312,"wires":[["851ba48d8fe01521","4f18995700ded59f"]]},{"id":"cf7a782de20a6415","type":"change","z":"f6858fe416b029b9","name":"","rules":[{"t":"move","p":"payload.switch_state","pt":"msg","to":"payload","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":1133.0088577270508,"y":218.00178146362305,"wires":[["a518789a41e9ab86","4f18995700ded59f"]]},{"id":"956497398d39b36a","type":"change","z":"f6858fe416b029b9","name":"","rules":[{"t":"move","p":"payload.switch_state","pt":"msg","to":"payload","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":1135.008934020996,"y":303.0017957687378,"wires":[[]]},{"id":"851ba48d8fe01521","type":"debug","z":"f6858fe416b029b9","name":"sendon","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":1362.5593223571777,"y":140.9192886352539,"wires":[]},{"id":"a518789a41e9ab86","type":"debug","z":"f6858fe416b029b9","name":"sendoff","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":1361.5643692016602,"y":234.92451190948486,"wires":[]},{"id":"4f18995700ded59f","type":"change","z":"f6858fe416b029b9","name":"","rules":[{"t":"set","p":"topic","pt":"msg","to":"cmnd/Wferm/POWER","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":1372.000244140625,"y":187.33334398269653,"wires":[["4c5df1dee2c300dc","537abc11ffeffcca"]]},{"id":"4c5df1dee2c300dc","type":"debug","z":"f6858fe416b029b9","name":"settopic","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":1571.3334197998047,"y":148.33334159851074,"wires":[]},{"id":"537abc11ffeffcca","type":"mqtt out","z":"f6858fe416b029b9","name":"","topic":"","qos":"","retain":"","respTopic":"","contentType":"","userProps":"","correl":"","expiry":"","broker":"31a99116.50a74e","x":1557.000087738037,"y":217.6666774749756,"wires":[]},{"id":"10c36bea9197e264","type":"ui_group","name":"WestFermenter2","tab":"a74ffe3d.4eb73","order":11,"disp":true,"width":12,"collapse":false},{"id":"31a99116.50a74e","type":"mqtt-broker","broker":"localhost","port":"1883","clientid":"","usetls":false,"compatmode":true,"keepalive":"60","cleansession":true,"birthTopic":"","birthQos":"0","birthPayload":"","willTopic":"","willQos":"0","willPayload":""},{"id":"a74ffe3d.4eb73","type":"ui_tab","name":"Brewery","icon":"dashboard","order":1,"disabled":false,"hidden":false}]
for example and training purposes.
how should this flow be done so someone wanting to learn it could import and it work on there node red ?
so i l still have an issue . the gage will show 200 for temp and set temp will go to 0 when i open another device to the dashboard.
so it shows fine on computer
then when i open dashboard on cell phone it shows this.
and then takes about 5 minutes to update the right stuff
is this an html issue or something i am doing wrong? can it be fixed.
So I am going to guess that you have tasmota Telemetry period set to default 300 seconds ?
You can check this by going into configure logging
oh yes i bet i do. I remember now reading something about that.
ill have to take a look at it later tonight.
I had to bring my laptop home from the brewer.
Im lasering candle holders for my Niece and Nephew, and my Grandkids for there mothers.
It's always good to spend time with the young ones stimulating there mind to make things.
(plus i get to play with laser)
I purchased some small enclosures today that will have room for esp8266, an outlet, the relay, and room for step down register for the pressure transducer I plan on adding later, plus room for 2 more relays so i can close airlock and co2 supply to fermenter.
Really laughed at myself for posting this thread, I have really learned a great deal in the last month.
All that credit really goes to guys and gals like you stepping up and helping others.
i need to start a blog for what i have done so far but i suck at grammar.
cheers
ok another issue with this gage i realized after trying to set up other fermenters.
even though i have each gage set to a different group. they all appear on same group.
the only things i have changed in it is the range , and the "ambient" on gage i changed to the name of each fermenter.
here is the ui template code.
can anyone let me know how to have each appear on there own group.
<style>
@import url('https://fonts.googleapis.com/css2?family=Roboto:ital,wght@0,100;0,300;0,400;0,500;0,700;0,900;1,100;1,300;1,400;1,500;1,700;1,900&display=swap');
svg {
transition: all .6s cubic-bezier(0.175, 0.885, 0.32, 1.2);
}
stop {
transition: all .5s;
}
.led {
-webkit-transition: all 0.5s;
transition: all 0.5s;
fill: url(#ledColor);
}
.fa-text {
font-family: FontAwesome !important;
}
.dial {
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
.qGradient {
fill : url(#qGradient);
}
.qGradientT {
fill : url(#qGradientT);
}
.eGradient {
fill : url(#eGradient);
}
.lbl {
font-family: 'Roboto', sans-serif;
text-anchor: middle;
fill : #ffffff;
clip-path: url(#qClip);
}
.lblDial {
fill: #dddddd;
}
.lblAmbient {
font-weight: 400;
clip-path: url(#qClip);
}
.lblAmbient tspan {
font-weight: 400;
}
.lblTarget {
font-weight: 400;
fill: orange;
}
.lblTarget tspan {
font-weight: 400;
fill: orange;
clip-path: url(#qClip);
}
.nodisplay {
display: none !important;
}
.icon {
font-family: FontAwesome !important;
}
.animate {
transition: all 0.5s;
}
</style>
<div id="GhostThermostat"></div>
<script>
var mousedownID = -1;
var ghostThermostatDial = (function() {
console.log("START");
function createSVGElement(tag, attributes, appendTo) {
var element = document.createElementNS('http://www.w3.org/2000/svg', tag);
attr(element, attributes);
if (appendTo) {
appendTo.appendChild(element);
}
return element;
}
function attr(element, attrs) {
for (var i in attrs) {
element.setAttribute(i, attrs[i]);
}
}
function setClass(el, className, state) {
el.classList[state ? 'add' : 'remove'](className);
}
return function(targetElement, options) {
console.log("RET FUN");
var self = this;
/*
* Options
*/
options = options || {};
options = {
diameter: options.diameter || 400,
mintemp: options.mintemp || 0, // Minimum value for target temperature
maxtemp: options.maxtemp || 200, // Maximum value for target temperature
ledColors: {
'off': 'rgb(143,141,141)',
'heating': 'rgb(255,128,0)',
'cooling': 'rgb(81,170,214)'
}, //Led Ring Colors
labels: {
ambient: "EAST FERMENTER",
set: "SET",
mode: "MODE",
minus: "-",
plus: "+",
left: "<",
right: ">"
},
onChangeState: options.onChangeState || function() {} // Function called when switch state change
};
/*
* Properties
*/
var properties = {
radius: options.diameter / 2,
modes: [{
label: "heating",
icon: "\uf06d",
color: "orange"
}, {
label: 'cooling',
icon: "\uf2dc",
color: "rgb(81,170,214)"
}, {
label: "off",
icon: "\uf011",
color: "rgb(230,0,0)"
}
/*, {
label: 'away',
icon: "\uf1ce",
color: "gray"
} */
],
modeNames: ["heating", "cooling", "off"],
swtitchStates: ["heating", "cooling", "off"]
};
/*
* Object state
*/
var state = {
target_temperature: options.mintemp,
ambient_temperature: options.maxtemp,
mode: properties.modes.indexOf(properties.modes[0]),
switch_state: 'off',
away: false
};
/*
* Property getter / setters
*/
Object.defineProperty(this, 'target_temperature', {
get: function() {
return state.target_temperature;
},
set: function(val) {
state.target_temperature = rangedTemperature(+val);
//render()
}
});
Object.defineProperty(this, 'ambient_temperature', {
get: function() {
return state.ambient_temperature;
},
set: function(val) {
state.ambient_temperature = +val;
render();
}
});
Object.defineProperty(this, 'mode_name', {
get: function() {
return properties.modeNames[state.mode];
},
set: function(val) {
if (properties.modeNames.indexOf(val) >= 0) {
state.mode = properties.modeNames.indexOf(val);
//render();
}
}
});
Object.defineProperty(this, 'switch_state', {
get: function() {
return state.switch_state;
},
set: function(val) {
if (properties.swtitchStates.indexOf(val) >= 0) {
state.switch_state = val;
//render();
}
}
});
function str2bool(strvalue) {
return (strvalue && typeof strvalue == 'string') ? (strvalue.toLowerCase() == 'true') : (strvalue == true);
}
Object.defineProperty(this, 'away', {
get: function() {
return state.away;
},
set: function(val) {
state.away = !!str2bool(val);
//render();
}
});
/*
* SVG
*/
var svg = createSVGElement('svg', {
width: '100%', //options.diameter+'px',
height: '100%', //options.diameter+'px',
viewBox: '0 0 ' + options.diameter + ' ' + options.diameter,
class: 'dial'
}, targetElement);
// DEFS
var defs = createSVGElement('defs', null, svg);
var qgradient = createSVGElement('linearGradient', {
'id': 'qGradient',
gradientTransform: 'rotate(65)'
}, defs);
var stop = createSVGElement('stop', {
'offset': '50%',
'stop-color': 'rgb(86,89,94)'
}, qgradient);
var stop = createSVGElement('stop', {
'offset': '65%',
'stop-color': 'rgb(30,30,30)'
}, qgradient);
var qGradientT = createSVGElement('linearGradient', {
'id': 'qGradientT',
gradientTransform: 'rotate(65)'
}, defs);
var stop = createSVGElement('stop', {
'offset': '55%',
'stop-color': '#3b3e43',
'stop-opacity': '1'
}, qGradientT);
var stop = createSVGElement('stop', {
'offset': '90%',
'stop-color': 'rgb(0,0,0)',
'stop-opacity': '1'
}, qGradientT);
var clipPath = createSVGElement('clipPath', {
'id': 'qClip',
}, defs);
var circle = createSVGElement('circle', {
cx: properties.radius,
cy: properties.radius,
r: properties.radius - 25
}, clipPath);
var ledRingGradient = createSVGElement('radialGradient', {
'id': 'ledColor',
'cx': "50%",
'cy': "50%",
'r': "95%",
'fx': "50%",
'fy': "50%"
}, defs);
var ledRingGradientColorIn = createSVGElement('stop', {
'offset': '45%',
'stop-color': 'rgb(255,0,130)',
'stop-opacity': '1'
}, ledRingGradient);
var ledRingGradientColorOut = createSVGElement('stop', {
'offset': '65%',
'stop-color': 'rgb(0,0,0)',
'stop-opacity': '1'
}, ledRingGradient);
var egradient = createSVGElement('linearGradient', {
'id': 'eGradient',
gradientTransform: 'rotate(55)'
}, defs);
var stop = createSVGElement('stop', {
'offset': '55%',
'stop-color': '#888888',
'stop-opacity': '1'
}, egradient);
var stop = createSVGElement('stop', {
'offset': '95%',
'stop-color': '#333333',
'stop-opacity': '1'
}, egradient);
// DIAL
var circle = createSVGElement('circle', {
cx: properties.radius,
cy: properties.radius,
r: properties.radius,
class: 'eGradient'
}, svg);
var ledRing = createSVGElement('circle', {
cx: properties.radius,
cy: properties.radius,
r: properties.radius - 3,
'stroke': 'black',
'stroke-width': '1',
class: 'led'
}, svg);
var circle = createSVGElement('circle', {
cx: properties.radius,
cy: properties.radius,
r: properties.radius - 20,
class: 'qGradient'
}, svg);
var circle = createSVGElement('circle', {
cx: properties.radius,
cy: properties.radius,
r: properties.radius - 25,
class: 'qGradient'
}, svg);
var lblMain = createSVGElement('text', {
x: properties.radius,
y: 70,
class: 'lbl lblDial'
}, svg);
var lblMainText = document.createTextNode(options.labels.ambient);
lblMain.appendChild(lblMainText);
var lblAmbient = createSVGElement('text', {
x: properties.radius,
y: 210,
'font-size': '160',
class: 'lbl lblAmbient'
}, svg);
var lblAmbientText = document.createTextNode('21');
lblAmbient.appendChild(lblAmbientText);
var lblAmbientDec = createSVGElement('tspan', {
'font-size': '60',
}, lblAmbient);
var lblAmbientDecText = document.createTextNode('.5');
lblAmbientDec.appendChild(lblAmbientDecText);
var line = createSVGElement('line', {
x1: 55,
y1: properties.radius + 35,
x2: options.diameter - 55,
y2: properties.radius + 35,
'stroke': '#DDDDDD',
'stroke-width': '1',
'opacity': '0.8'
}, svg);
var lblLeft = createSVGElement('text', {
x: 125,
y: properties.radius + 75,
class: 'lbl lblDial'
}, svg);
var lblLeftText = document.createTextNode(options.labels.set);
lblLeft.appendChild(lblLeftText);
var lblTarget = createSVGElement('text', {
x: 125,
y: properties.radius + 115,
'font-size': '35',
class: 'lbl lblTarget'
}, svg);
var lblTargetText = document.createTextNode('20');
lblTarget.appendChild(lblTargetText);
var lblTargetDec = createSVGElement('tspan', {
'font-size': '20',
}, lblTarget);
var lblTargetDecText = document.createTextNode('.5');
lblTargetDec.appendChild(lblTargetDecText);
var lblRight = createSVGElement('text', {
x: options.diameter - 125,
y: properties.radius + 75,
class: 'lbl lblDial'
}, svg);
var lblRightText = document.createTextNode(options.labels.mode);
lblRight.appendChild(lblRightText);
var lblMode = createSVGElement('text', {
x: options.diameter - 125,
y: properties.radius + 115,
'font-size': '35',
class: 'lbl lblTarget icon'
}, svg);
var lblModeText = document.createTextNode(properties.modes[0].icon);
lblMode.appendChild(lblModeText);
var btnSet = createSVGElement('g', {
transform: 'translate(200,200)'
}, svg);
var path = createSVGElement('path', {
d: 'M0,40 L0,175 A175,175 0 0,1 -175,40 z',
fill: 'blue',
opacity: '0',
'id': 'btnLeft'
}, btnSet);
var path = createSVGElement('path', {
d: 'M0,40 L175,40 A175,175 0 0,1 0,175 z',
fill: 'red',
opacity: '0',
'id': 'btnRight'
}, btnSet);
document.getElementById("btnLeft").onclick = function() {
setTargetClick();
};
document.getElementById("btnRight").onclick = function() {
setModeClick();
};
var targetPanel = false;
var modePanel = false;
var lblAmbientAttributes = {
x: lblAmbient.getAttribute('x'),
y: lblAmbient.getAttribute('y'),
size: lblAmbient.getAttribute('font-size')
};
var lblAmbientDecAttributes = {
x: lblAmbientDec.getAttribute('x'),
y: lblAmbientDec.getAttribute('y'),
size: lblAmbientDec.getAttribute('font-size')
};
var lblTargetAttributes = {
x: lblTarget.getAttribute('x'),
y: lblTarget.getAttribute('y'),
size: lblTarget.getAttribute('font-size')
};
var lblTargetDecAttributes = {
x: lblTargetDec.getAttribute('x'),
y: lblTargetDec.getAttribute('y'),
size: lblTargetDec.getAttribute('font-size')
};
var lblModeAttributes = {
x: lblMode.getAttribute('x'),
y: lblMode.getAttribute('y'),
size: lblMode.getAttribute('font-size')
};
var lblRightAttributes = {
x: lblRight.getAttribute('x'),
y: lblRight.getAttribute('y'),
size: lblRight.getAttribute('font-size')
};
var lblLeftAttributes = {
x: lblLeft.getAttribute('x'),
y: lblLeft.getAttribute('y'),
size: lblLeft.getAttribute('font-size')
};
render();
function setAmbientTemperature(ambientTemp) {
var splitValues = separateDecValue(ambientTemp);
lblAmbientText.textContent = splitValues.int;
lblAmbientDecText.textContent = splitValues.dec;
};
function calcTargetTemperature(operation) {
let currentTemp = Number(parseFloat(lblTargetText.textContent + lblTargetDecText.textContent)).toFixed(1);
let targetTemp = (operation == '-' ? Number(Number(currentTemp) - 0.5).toFixed(1) : Number(Number(currentTemp) + 0.5).toFixed(1));
targetTemp = rangedTemperature(targetTemp);
setTargetTemperature(targetTemp);
chkSwitchState();
};
function setTargetTemperature(targetTemp) {
var splitValues = separateDecValue(targetTemp);
lblTargetText.textContent = splitValues.int;
lblTargetDecText.textContent = splitValues.dec;
if (state.target_temperature != targetTemp) {
state.target_temperature = targetTemp
sendMsg();
};
};
function separateDecValue(floatFalue) {
var int = Math.floor(floatFalue);
var dec = Math.floor(((floatFalue % 1) * 10)) > 0 ? ("." + Math.floor(((floatFalue % 1) * 10))) : "";
return {
int,
dec
};
};
function rangedTemperature(temperature) {
temperature = temperature < options.mintemp ? options.maxtemp : temperature;
temperature = temperature > options.maxtemp ? options.mintemp : temperature;
return temperature;
};
function chkSwitchState() {
console.log("chkSwitchState");
var switchState = state.switch_state;
switch (state.mode) {
case 0:
switchState = state.ambient_temperature < state.target_temperature ? 'heating' : 'off';
break;
case 1:
switchState = state.ambient_temperature > state.target_temperature ? 'cooling' : 'off';
break;
default:
switchState = 'off';
};
ledRingGradientColorIn.setAttribute('stop-color', options.ledColors[state.switch_state]);
if (state.switch_state != switchState) {
state.switch_state = switchState;
sendMsg();
};
};
function resetButton() {
document.getElementById("btnLeft").onmousedown = "";
document.getElementById("btnLeft").onmouseup = "";
document.getElementById("btnLeft").onclick = function() {
setTargetClick();
};
document.getElementById("btnRight").onmousedown = "";
document.getElementById("btnRight").onmouseup = "";
document.getElementById("btnRight").onclick = function() {
setModeClick();
};
};
function switchMainView(element, originalAttributes, mainLabel, leftLabel, rightLabel, panelState) {
setClass(lblAmbient, "nodisplay", panelState);
setClass(lblMain, "animate", panelState);
setClass(lblLeft, "animate", panelState);
setClass(lblRight, "animate", panelState);
setClass(element, "animate", panelState);
lblMainText.textContent = panelState ? mainLabel : options.labels.ambient;
lblLeftText.textContent = panelState ? leftLabel : options.labels.set;
lblLeft.setAttribute('y', panelState ? Number(lblLeftAttributes.y) + 40 : lblLeftAttributes.y);
lblLeft.setAttribute('font-size', panelState ? "3.5em" : "1em");
lblRightText.textContent = panelState ? rightLabel : options.labels.mode;
lblRight.setAttribute('y', panelState ? Number(lblRightAttributes.y) + 40 : lblRightAttributes.y);
lblRight.setAttribute('font-size', panelState ? "3.5em" : "1em");
element.setAttribute('x', panelState ? lblAmbientAttributes.x : originalAttributes.x);
element.setAttribute('x', panelState ? lblAmbientAttributes.x : originalAttributes.x);
element.setAttribute('y', panelState ? lblAmbientAttributes.y : originalAttributes.y);
element.setAttribute('font-size', panelState ? lblAmbientAttributes.size : originalAttributes.size);
};
function setTargetClick() {
targetPanel = targetPanel ? false : true;
setClass(lblMode, "nodisplay", targetPanel);
switchMainView(lblTarget, lblTargetAttributes, options.labels.set, options.labels.minus, options.labels.plus, targetPanel);
lblTargetDec.setAttribute('font-size', targetPanel ? lblAmbientDecAttributes.size : lblTargetDecAttributes.size);
if (targetPanel) {
document.getElementById("btnLeft").onclick = "";
document.getElementById("btnRight").onclick = "";
document.getElementById("btnLeft").onmousedown = function() {
calcTargetTemperature("-");
if (mousedownID == -1) { //Prevent multimple loops!
mousedownID = setInterval(calcTargetTemperature, 500, '-');
}
};
document.getElementById("btnLeft").onmouseup = function() {
if (mousedownID != -1) { //Only stop if exists
clearInterval(mousedownID);
mousedownID = -1;
}
};
document.getElementById("btnRight").onmousedown = function() {
calcTargetTemperature("+");
if (mousedownID == -1) { //Prevent multimple loops!
mousedownID = setInterval(calcTargetTemperature, 500, '+');
}
};
document.getElementById("btnRight").onmouseup = function() {
if (mousedownID != -1) { //Only stop if exists
clearInterval(mousedownID);
mousedownID = -1;
}
};
lblTarget.onclick = function() {
setTargetClick();
};
} else {
resetButton()
}
};
function setModeClick() {
modePanel = modePanel ? false : true;
setClass(lblTarget, "nodisplay", modePanel);
switchMainView(lblMode, lblModeAttributes, options.labels.mode, options.labels.left, options.labels.right, modePanel);
if (modePanel) {
document.getElementById("btnLeft").onclick = function() {
mode = state.mode;
mode = --mode < 0 ? properties.modes.length - 1 : mode;
console.log("MODE :" + mode);
setModeName(properties.modeNames[mode]);
chkSwitchState();
sendMsg();
};
document.getElementById("btnRight").onclick = function() {
mode = state.mode;
mode = ++mode > properties.modes.length - 1 ? 0 : mode;
console.log("MODE :" + mode);
setModeName(properties.modeNames[mode]);
chkSwitchState();
sendMsg();
};
lblMode.onclick = function() {
setModeClick();
};
} else {
resetButton()
}
};
function setModeName(modeName) {
lblMode.textContent = properties.modes[properties.modeNames.indexOf(modeName)].icon;
lblMode.style.fill = properties.modes[properties.modeNames.indexOf(modeName)].color;
state.mode = properties.modeNames.indexOf(modeName);
};
function sendMsg() {
if (typeof options.onChangeState == 'function') {
options.onChangeState(state.switch_state);
}
};
function render() {
console.log("RENDER");
setAmbientTemperature(self.ambient_temperature);
setTargetTemperature(self.target_temperature);
setModeName(self.mode_name);
chkSwitchState();
};
};
})();
var initializing = true;
(function(scope) {
var ghostThermostat = new ghostThermostatDial(document.getElementById('GhostThermostat'), {
onChangeState: function() {
var p = {
"ambient_temperature": ghostThermostat.ambient_temperature,
"target_temperature": ghostThermostat.target_temperature,
"mode": ghostThermostat.mode_name,
"switch_state": ghostThermostat.switch_state,
"away": ghostThermostat.away
};
scope.send({
topic: "changed_state",
payload: p
});
}
});
scope.$watch('msg', function(data) {
if (initializing) {
initializing = false;
} else {
ghostThermostat.ambient_temperature = data.payload.ambient_temperature || ghostThermostat.ambient_temperature;
ghostThermostat.target_temperature = data.payload.target_temperature || ghostThermostat.target_temperature;
ghostThermostat.mode_name = data.payload.mode || ghostThermostat.mode_name;
ghostThermostat.switch_state = data.payload.switch_state || ghostThermostat.switch_state;
ghostThermostat.away = data.payload.away || ghostThermostat.away;
}
});
})(scope);
</script>
[{"id":"788c66a4a685865f","type":"ui_template","z":"e1e19aab565f8160","group":"f232320f2795950b","name":"EFERM","order":27,"width":0,"height":0,"format":"<style>\n @import url('https://fonts.googleapis.com/css2?family=Roboto:ital,wght@0,100;0,300;0,400;0,500;0,700;0,900;1,100;1,300;1,400;1,500;1,700;1,900&display=swap');\n \n svg {\n transition: all .6s cubic-bezier(0.175, 0.885, 0.32, 1.2);\n }\n\n stop {\n transition: all .5s;\n }\n \n\t.led {\n \t-webkit-transition: all 0.5s;\n \ttransition: all 0.5s;\n \tfill: url(#ledColor);\n }\n \n .fa-text {\n font-family: FontAwesome !important; \n }\n .dial {\n -webkit-user-select: none;\n -moz-user-select: none;\n -ms-user-select: none;\n user-select: none;\n }\n .qGradient {\n fill : url(#qGradient);\n }\n .qGradientT {\n fill : url(#qGradientT);\n }\n .eGradient {\n fill : url(#eGradient);\n }\n .lbl {\n font-family: 'Roboto', sans-serif;\n text-anchor: middle;\n fill : #ffffff;\n clip-path: url(#qClip);\n }\n .lblDial {\n fill: #dddddd;\n }\n \n .lblAmbient {\n font-weight: 400;\n clip-path: url(#qClip);\n }\n \n .lblAmbient tspan {\n font-weight: 400;\n }\n \n .lblTarget {\n font-weight: 400;\n fill: orange;\n }\n \n .lblTarget tspan {\n font-weight: 400;\n fill: orange;\n clip-path: url(#qClip);\n } \n \n .nodisplay {\n display: none !important;\n }\n \n .icon {\n font-family: FontAwesome !important;\n }\n \n .animate {\n transition: all 0.5s;\n }\n\n</style>\n<div id=\"GhostThermostat\"></div> \n<script>\nvar mousedownID = -1;\nvar ghostThermostatDial = (function() {\n\tconsole.log(\"START\");\n\n\tfunction createSVGElement(tag, attributes, appendTo) {\n\t\tvar element = document.createElementNS('http://www.w3.org/2000/svg', tag);\n\t\tattr(element, attributes);\n\t\tif (appendTo) {\n\t\t\tappendTo.appendChild(element);\n\t\t}\n\t\treturn element;\n\t}\n\n\tfunction attr(element, attrs) {\n\t\tfor (var i in attrs) {\n\t\t\telement.setAttribute(i, attrs[i]);\n\t\t}\n\t}\n\n\tfunction setClass(el, className, state) {\n\t\tel.classList[state ? 'add' : 'remove'](className);\n\t}\n\n\treturn function(targetElement, options) {\n\t\tconsole.log(\"RET FUN\");\n\t\tvar self = this;\n\n\t\t/*\n\t\t * Options\n\t\t */\n\t\toptions = options || {};\n\t\toptions = {\n\t\t\tdiameter: options.diameter || 400,\n\t\t\tmintemp: options.mintemp || 0, // Minimum value for target temperature\n\t\t\tmaxtemp: options.maxtemp || 200, // Maximum value for target temperature\n\t\t\tledColors: {\n\t\t\t\t'off': 'rgb(143,141,141)',\n\t\t\t\t'heating': 'rgb(255,128,0)',\n\t\t\t\t'cooling': 'rgb(81,170,214)'\n\t\t\t}, //Led Ring Colors\n\t\t\tlabels: {\n\t\t\t\tambient: \"EAST FERMENTER\",\n\t\t\t\tset: \"SET\",\n\t\t\t\tmode: \"MODE\",\n\t\t\t\tminus: \"-\",\n\t\t\t\tplus: \"+\",\n\t\t\t\tleft: \"<\",\n\t\t\t\tright: \">\"\n\t\t\t},\n\t\t\tonChangeState: options.onChangeState || function() {} // Function called when switch state change\n\t\t};\n\n\t\t/*\n\t\t * Properties\n\t\t */\n\t\tvar properties = {\n\t\t\tradius: options.diameter / 2,\n\t\t\tmodes: [{\n\t\t\t\t\tlabel: \"heating\",\n\t\t\t\t\ticon: \"\\uf06d\",\n\t\t\t\t\tcolor: \"orange\"\n\t\t\t\t}, {\n\t\t\t\t\tlabel: 'cooling',\n\t\t\t\t\ticon: \"\\uf2dc\",\n\t\t\t\t\tcolor: \"rgb(81,170,214)\"\n\t\t\t\t}, {\n\t\t\t\t\tlabel: \"off\",\n\t\t\t\t\ticon: \"\\uf011\",\n\t\t\t\t\tcolor: \"rgb(230,0,0)\"\n\t\t\t\t}\n\t\t\t\t/*, {\n\t\t\t\tlabel: 'away',\n\t\t\t\ticon: \"\\uf1ce\",\n\t\t\t\tcolor: \"gray\"\n\t\t\t} */\n\t\t\t],\n\t\t\tmodeNames: [\"heating\", \"cooling\", \"off\"],\n\t\t\tswtitchStates: [\"heating\", \"cooling\", \"off\"]\n\t\t};\n\n\t\t/*\n\t\t * Object state\n\t\t */\n\t\tvar state = {\n\t\t\ttarget_temperature: options.mintemp,\n\t\t\tambient_temperature: options.maxtemp,\n\t\t\tmode: properties.modes.indexOf(properties.modes[0]),\n\t\t\tswitch_state: 'off',\n\t\t\taway: false\n\t\t};\n\n\t\t/*\n\t\t * Property getter / setters\n\t\t */\n\t\tObject.defineProperty(this, 'target_temperature', {\n\t\t\tget: function() {\n\t\t\t\treturn state.target_temperature;\n\t\t\t},\n\t\t\tset: function(val) {\n\t\t\t\tstate.target_temperature = rangedTemperature(+val);\n\t\t\t\t//render()\n\t\t\t}\n\t\t});\n\n\t\tObject.defineProperty(this, 'ambient_temperature', {\n\t\t\tget: function() {\n\t\t\t\treturn state.ambient_temperature;\n\t\t\t},\n\t\t\tset: function(val) {\n\t\t\t\tstate.ambient_temperature = +val;\n\t\t\t\trender();\n\t\t\t}\n\t\t});\n\n\t\tObject.defineProperty(this, 'mode_name', {\n\t\t\tget: function() {\n\t\t\t\treturn properties.modeNames[state.mode];\n\t\t\t},\n\t\t\tset: function(val) {\n\t\t\t\tif (properties.modeNames.indexOf(val) >= 0) {\n\t\t\t\t\tstate.mode = properties.modeNames.indexOf(val);\n\t\t\t\t\t//render();\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\n\t\tObject.defineProperty(this, 'switch_state', {\n\t\t\tget: function() {\n\t\t\t\treturn state.switch_state;\n\t\t\t},\n\t\t\tset: function(val) {\n\t\t\t\tif (properties.swtitchStates.indexOf(val) >= 0) {\n\t\t\t\t\tstate.switch_state = val;\n\t\t\t\t\t//render();\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\n\n\t\tfunction str2bool(strvalue) {\n\t\t\treturn (strvalue && typeof strvalue == 'string') ? (strvalue.toLowerCase() == 'true') : (strvalue == true);\n\t\t}\n\n\t\tObject.defineProperty(this, 'away', {\n\t\t\tget: function() {\n\t\t\t\treturn state.away;\n\t\t\t},\n\t\t\tset: function(val) {\n\t\t\t\tstate.away = !!str2bool(val);\n\t\t\t\t//render();\n\t\t\t}\n\t\t});\n\n\n\t\t/*\n\t\t * SVG\n\t\t */\n\t\tvar svg = createSVGElement('svg', {\n\t\t\twidth: '100%', //options.diameter+'px',\n\t\t\theight: '100%', //options.diameter+'px',\n\t\t\tviewBox: '0 0 ' + options.diameter + ' ' + options.diameter,\n\t\t\tclass: 'dial'\n\t\t}, targetElement);\n\n\t\t// DEFS \n\t\tvar defs = createSVGElement('defs', null, svg);\n\n\t\tvar qgradient = createSVGElement('linearGradient', {\n\t\t\t'id': 'qGradient',\n\t\t\tgradientTransform: 'rotate(65)'\n\t\t}, defs);\n\t\tvar stop = createSVGElement('stop', {\n\t\t\t'offset': '50%',\n\t\t\t'stop-color': 'rgb(86,89,94)'\n\t\t}, qgradient);\n\t\tvar stop = createSVGElement('stop', {\n\t\t\t'offset': '65%',\n\t\t\t'stop-color': 'rgb(30,30,30)'\n\t\t}, qgradient);\n\n\t\tvar qGradientT = createSVGElement('linearGradient', {\n\t\t\t'id': 'qGradientT',\n\t\t\tgradientTransform: 'rotate(65)'\n\t\t}, defs);\n\t\tvar stop = createSVGElement('stop', {\n\t\t\t'offset': '55%',\n\t\t\t'stop-color': '#3b3e43',\n\t\t\t'stop-opacity': '1'\n\t\t}, qGradientT);\n\t\tvar stop = createSVGElement('stop', {\n\t\t\t'offset': '90%',\n\t\t\t'stop-color': 'rgb(0,0,0)',\n\t\t\t'stop-opacity': '1'\n\t\t}, qGradientT);\n\n\t\tvar clipPath = createSVGElement('clipPath', {\n\t\t\t'id': 'qClip',\n\t\t}, defs);\n\t\tvar circle = createSVGElement('circle', {\n\t\t\tcx: properties.radius,\n\t\t\tcy: properties.radius,\n\t\t\tr: properties.radius - 25\n\t\t}, clipPath);\n\n\n\t\tvar ledRingGradient = createSVGElement('radialGradient', {\n\t\t\t'id': 'ledColor',\n\t\t\t'cx': \"50%\",\n\t\t\t'cy': \"50%\",\n\t\t\t'r': \"95%\",\n\t\t\t'fx': \"50%\",\n\t\t\t'fy': \"50%\"\n\t\t}, defs);\n\t\tvar ledRingGradientColorIn = createSVGElement('stop', {\n\t\t\t'offset': '45%',\n\t\t\t'stop-color': 'rgb(255,0,130)',\n\t\t\t'stop-opacity': '1'\n\t\t}, ledRingGradient);\n\t\tvar ledRingGradientColorOut = createSVGElement('stop', {\n\t\t\t'offset': '65%',\n\t\t\t'stop-color': 'rgb(0,0,0)',\n\t\t\t'stop-opacity': '1'\n\t\t}, ledRingGradient);\n\n\t\tvar egradient = createSVGElement('linearGradient', {\n\t\t\t'id': 'eGradient',\n\t\t\tgradientTransform: 'rotate(55)'\n\t\t}, defs);\n\t\tvar stop = createSVGElement('stop', {\n\t\t\t'offset': '55%',\n\t\t\t'stop-color': '#888888',\n\t\t\t'stop-opacity': '1'\n\t\t}, egradient);\n\t\tvar stop = createSVGElement('stop', {\n\t\t\t'offset': '95%',\n\t\t\t'stop-color': '#333333',\n\t\t\t'stop-opacity': '1'\n\t\t}, egradient);\n\n\t\t// DIAL\n\t\tvar circle = createSVGElement('circle', {\n\t\t\tcx: properties.radius,\n\t\t\tcy: properties.radius,\n\t\t\tr: properties.radius,\n\t\t\tclass: 'eGradient'\n\t\t}, svg);\n\t\tvar ledRing = createSVGElement('circle', {\n\t\t\tcx: properties.radius,\n\t\t\tcy: properties.radius,\n\t\t\tr: properties.radius - 3,\n\t\t\t'stroke': 'black',\n\t\t\t'stroke-width': '1',\n\t\t\tclass: 'led'\n\t\t}, svg);\n\t\tvar circle = createSVGElement('circle', {\n\t\t\tcx: properties.radius,\n\t\t\tcy: properties.radius,\n\t\t\tr: properties.radius - 20,\n\t\t\tclass: 'qGradient'\n\t\t}, svg);\n\t\tvar circle = createSVGElement('circle', {\n\t\t\tcx: properties.radius,\n\t\t\tcy: properties.radius,\n\t\t\tr: properties.radius - 25,\n\t\t\tclass: 'qGradient'\n\t\t}, svg);\n\t\tvar lblMain = createSVGElement('text', {\n\t\t\tx: properties.radius,\n\t\t\ty: 70,\n\t\t\tclass: 'lbl lblDial'\n\t\t}, svg);\n\t\tvar lblMainText = document.createTextNode(options.labels.ambient);\n\t\tlblMain.appendChild(lblMainText);\n\n\t\tvar lblAmbient = createSVGElement('text', {\n\t\t\tx: properties.radius,\n\t\t\ty: 210,\n\t\t\t'font-size': '160',\n\t\t\tclass: 'lbl lblAmbient'\n\t\t}, svg);\n\t\tvar lblAmbientText = document.createTextNode('21');\n\t\tlblAmbient.appendChild(lblAmbientText);\n\t\tvar lblAmbientDec = createSVGElement('tspan', {\n\t\t\t'font-size': '60',\n\t\t}, lblAmbient);\n\t\tvar lblAmbientDecText = document.createTextNode('.5');\n\t\tlblAmbientDec.appendChild(lblAmbientDecText);\n\n\t\tvar line = createSVGElement('line', {\n\t\t\tx1: 55,\n\t\t\ty1: properties.radius + 35,\n\t\t\tx2: options.diameter - 55,\n\t\t\ty2: properties.radius + 35,\n\t\t\t'stroke': '#DDDDDD',\n\t\t\t'stroke-width': '1',\n\t\t\t'opacity': '0.8'\n\t\t}, svg);\n\n\t\tvar lblLeft = createSVGElement('text', {\n\t\t\tx: 125,\n\t\t\ty: properties.radius + 75,\n\t\t\tclass: 'lbl lblDial'\n\t\t}, svg);\n\t\tvar lblLeftText = document.createTextNode(options.labels.set);\n\t\tlblLeft.appendChild(lblLeftText);\n\n\t\tvar lblTarget = createSVGElement('text', {\n\t\t\tx: 125,\n\t\t\ty: properties.radius + 115,\n\t\t\t'font-size': '35',\n\t\t\tclass: 'lbl lblTarget'\n\t\t}, svg);\n\t\tvar lblTargetText = document.createTextNode('20');\n\t\tlblTarget.appendChild(lblTargetText);\n\n\t\tvar lblTargetDec = createSVGElement('tspan', {\n\t\t\t'font-size': '20',\n\t\t}, lblTarget);\n\n\t\tvar lblTargetDecText = document.createTextNode('.5');\n\t\tlblTargetDec.appendChild(lblTargetDecText);\n\n\t\tvar lblRight = createSVGElement('text', {\n\t\t\tx: options.diameter - 125,\n\t\t\ty: properties.radius + 75,\n\t\t\tclass: 'lbl lblDial'\n\t\t}, svg);\n\t\tvar lblRightText = document.createTextNode(options.labels.mode);\n\t\tlblRight.appendChild(lblRightText);\n\n\t\tvar lblMode = createSVGElement('text', {\n\t\t\tx: options.diameter - 125,\n\t\t\ty: properties.radius + 115,\n\t\t\t'font-size': '35',\n\t\t\tclass: 'lbl lblTarget icon'\n\t\t}, svg);\n\t\tvar lblModeText = document.createTextNode(properties.modes[0].icon);\n\t\tlblMode.appendChild(lblModeText);\n\n\t\tvar btnSet = createSVGElement('g', {\n\t\t\ttransform: 'translate(200,200)'\n\t\t}, svg);\n\t\tvar path = createSVGElement('path', {\n\t\t\td: 'M0,40 L0,175 A175,175 0 0,1 -175,40 z',\n\t\t\tfill: 'blue',\n\t\t\topacity: '0',\n\t\t\t'id': 'btnLeft'\n\t\t}, btnSet);\n\t\tvar path = createSVGElement('path', {\n\t\t\td: 'M0,40 L175,40 A175,175 0 0,1 0,175 z',\n\t\t\tfill: 'red',\n\t\t\topacity: '0',\n\t\t\t'id': 'btnRight'\n\t\t}, btnSet);\n\n\n\n\t\tdocument.getElementById(\"btnLeft\").onclick = function() {\n\t\t\tsetTargetClick();\n\t\t};\n\n\t\tdocument.getElementById(\"btnRight\").onclick = function() {\n\t\t\tsetModeClick();\n\t\t};\n\n\t\tvar targetPanel = false;\n\t\tvar modePanel = false;\n\n\t\tvar lblAmbientAttributes = {\n\t\t\tx: lblAmbient.getAttribute('x'),\n\t\t\ty: lblAmbient.getAttribute('y'),\n\t\t\tsize: lblAmbient.getAttribute('font-size')\n\t\t};\n\n\t\tvar lblAmbientDecAttributes = {\n\t\t\tx: lblAmbientDec.getAttribute('x'),\n\t\t\ty: lblAmbientDec.getAttribute('y'),\n\t\t\tsize: lblAmbientDec.getAttribute('font-size')\n\t\t};\n\n\t\tvar lblTargetAttributes = {\n\t\t\tx: lblTarget.getAttribute('x'),\n\t\t\ty: lblTarget.getAttribute('y'),\n\t\t\tsize: lblTarget.getAttribute('font-size')\n\t\t};\n\n\t\tvar lblTargetDecAttributes = {\n\t\t\tx: lblTargetDec.getAttribute('x'),\n\t\t\ty: lblTargetDec.getAttribute('y'),\n\t\t\tsize: lblTargetDec.getAttribute('font-size')\n\t\t};\n\n\t\tvar lblModeAttributes = {\n\t\t\tx: lblMode.getAttribute('x'),\n\t\t\ty: lblMode.getAttribute('y'),\n\t\t\tsize: lblMode.getAttribute('font-size')\n\t\t};\n\n\t\tvar lblRightAttributes = {\n\t\t\tx: lblRight.getAttribute('x'),\n\t\t\ty: lblRight.getAttribute('y'),\n\t\t\tsize: lblRight.getAttribute('font-size')\n\t\t};\n\n\t\tvar lblLeftAttributes = {\n\t\t\tx: lblLeft.getAttribute('x'),\n\t\t\ty: lblLeft.getAttribute('y'),\n\t\t\tsize: lblLeft.getAttribute('font-size')\n\t\t};\n\n\t\trender();\n\n\t\tfunction setAmbientTemperature(ambientTemp) {\n\t\t\tvar splitValues = separateDecValue(ambientTemp);\n\t\t\tlblAmbientText.textContent = splitValues.int;\n\t\t\tlblAmbientDecText.textContent = splitValues.dec;\n\t\t};\n\n\n\t\tfunction calcTargetTemperature(operation) {\n\t\t\tlet currentTemp = Number(parseFloat(lblTargetText.textContent + lblTargetDecText.textContent)).toFixed(1);\n\t\t\tlet targetTemp = (operation == '-' ? Number(Number(currentTemp) - 0.5).toFixed(1) : Number(Number(currentTemp) + 0.5).toFixed(1));\n\t\t\ttargetTemp = rangedTemperature(targetTemp);\n\t\t\tsetTargetTemperature(targetTemp);\n\t\t\tchkSwitchState();\n\t\t};\n\n\t\tfunction setTargetTemperature(targetTemp) {\n\t\t\tvar splitValues = separateDecValue(targetTemp);\n\t\t\tlblTargetText.textContent = splitValues.int;\n\t\t\tlblTargetDecText.textContent = splitValues.dec;\n\t\t\tif (state.target_temperature != targetTemp) {\n\t\t\t\tstate.target_temperature = targetTemp\n\t\t\t\tsendMsg();\n\t\t\t};\n\t\t};\n\n\t\tfunction separateDecValue(floatFalue) {\n\t\t\tvar int = Math.floor(floatFalue);\n\t\t\tvar dec = Math.floor(((floatFalue % 1) * 10)) > 0 ? (\".\" + Math.floor(((floatFalue % 1) * 10))) : \"\";\n\t\t\treturn {\n\t\t\t\tint,\n\t\t\t\tdec\n\t\t\t};\n\t\t};\n\n\t\tfunction rangedTemperature(temperature) {\n\t\t\ttemperature = temperature < options.mintemp ? options.maxtemp : temperature;\n\t\t\ttemperature = temperature > options.maxtemp ? options.mintemp : temperature;\n\t\t\treturn temperature;\n\t\t};\n\n\t\tfunction chkSwitchState() {\n\t\t console.log(\"chkSwitchState\");\n\t\t\tvar switchState = state.switch_state;\n\t\t\tswitch (state.mode) {\n\t\t\t\tcase 0:\n\t\t\t\t\tswitchState = state.ambient_temperature < state.target_temperature ? 'heating' : 'off';\n\t\t\t\t\tbreak;\n\t\t\t\tcase 1:\n\t\t\t\t\tswitchState = state.ambient_temperature > state.target_temperature ? 'cooling' : 'off';\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\tswitchState = 'off';\n\t\t\t};\n\n\t\t\tledRingGradientColorIn.setAttribute('stop-color', options.ledColors[state.switch_state]);\n\n\t\t\tif (state.switch_state != switchState) {\n\t\t\t\tstate.switch_state = switchState;\n\t\t\t\tsendMsg();\n\t\t\t};\n\t\t};\n\n\n\t\tfunction resetButton() {\n\t\t\tdocument.getElementById(\"btnLeft\").onmousedown = \"\";\n\t\t\tdocument.getElementById(\"btnLeft\").onmouseup = \"\";\n\t\t\tdocument.getElementById(\"btnLeft\").onclick = function() {\n\t\t\t\tsetTargetClick();\n\t\t\t};\n\t\t\tdocument.getElementById(\"btnRight\").onmousedown = \"\";\n\t\t\tdocument.getElementById(\"btnRight\").onmouseup = \"\";\n\t\t\tdocument.getElementById(\"btnRight\").onclick = function() {\n\t\t\t\tsetModeClick();\n\t\t\t};\n\t\t};\n\n\t\tfunction switchMainView(element, originalAttributes, mainLabel, leftLabel, rightLabel, panelState) {\n\t\t\tsetClass(lblAmbient, \"nodisplay\", panelState);\n\t\t\tsetClass(lblMain, \"animate\", panelState);\n\t\t\tsetClass(lblLeft, \"animate\", panelState);\n\t\t\tsetClass(lblRight, \"animate\", panelState);\n\t\t\tsetClass(element, \"animate\", panelState);\n\n\t\t\tlblMainText.textContent = panelState ? mainLabel : options.labels.ambient;\n\t\t\tlblLeftText.textContent = panelState ? leftLabel : options.labels.set;\n\n\t\t\tlblLeft.setAttribute('y', panelState ? Number(lblLeftAttributes.y) + 40 : lblLeftAttributes.y);\n\t\t\tlblLeft.setAttribute('font-size', panelState ? \"3.5em\" : \"1em\");\n\n\t\t\tlblRightText.textContent = panelState ? rightLabel : options.labels.mode;\n\t\t\tlblRight.setAttribute('y', panelState ? Number(lblRightAttributes.y) + 40 : lblRightAttributes.y);\n\t\t\tlblRight.setAttribute('font-size', panelState ? \"3.5em\" : \"1em\");\n\n\t\t\telement.setAttribute('x', panelState ? lblAmbientAttributes.x : originalAttributes.x);\n\t\t\telement.setAttribute('x', panelState ? lblAmbientAttributes.x : originalAttributes.x);\n\t\t\telement.setAttribute('y', panelState ? lblAmbientAttributes.y : originalAttributes.y);\n\t\t\telement.setAttribute('font-size', panelState ? lblAmbientAttributes.size : originalAttributes.size);\n\n\t\t};\n\n\n\t\tfunction setTargetClick() {\n\n\t\t\ttargetPanel = targetPanel ? false : true;\n\t\t\tsetClass(lblMode, \"nodisplay\", targetPanel);\n\t\t\tswitchMainView(lblTarget, lblTargetAttributes, options.labels.set, options.labels.minus, options.labels.plus, targetPanel);\n\n\t\t\tlblTargetDec.setAttribute('font-size', targetPanel ? lblAmbientDecAttributes.size : lblTargetDecAttributes.size);\n\n\t\t\tif (targetPanel) {\n\t\t\t\tdocument.getElementById(\"btnLeft\").onclick = \"\";\n\t\t\t\tdocument.getElementById(\"btnRight\").onclick = \"\";\n\n\t\t\t\tdocument.getElementById(\"btnLeft\").onmousedown = function() {\n\t\t\t\t\tcalcTargetTemperature(\"-\");\n\t\t\t\t\tif (mousedownID == -1) { //Prevent multimple loops!\n\t\t\t\t\t\tmousedownID = setInterval(calcTargetTemperature, 500, '-');\n\t\t\t\t\t}\n\t\t\t\t};\n\t\t\t\tdocument.getElementById(\"btnLeft\").onmouseup = function() {\n\t\t\t\t\tif (mousedownID != -1) { //Only stop if exists\n\t\t\t\t\t\tclearInterval(mousedownID);\n\t\t\t\t\t\tmousedownID = -1;\n\t\t\t\t\t}\n\t\t\t\t};\n\n\t\t\t\tdocument.getElementById(\"btnRight\").onmousedown = function() {\n\t\t\t\t\tcalcTargetTemperature(\"+\");\n\t\t\t\t\tif (mousedownID == -1) { //Prevent multimple loops!\n\t\t\t\t\t\tmousedownID = setInterval(calcTargetTemperature, 500, '+');\n\t\t\t\t\t}\n\t\t\t\t};\n\t\t\t\tdocument.getElementById(\"btnRight\").onmouseup = function() {\n\t\t\t\t\tif (mousedownID != -1) { //Only stop if exists\n\t\t\t\t\t\tclearInterval(mousedownID);\n\t\t\t\t\t\tmousedownID = -1;\n\t\t\t\t\t}\n\t\t\t\t};\n\n\t\t\t\tlblTarget.onclick = function() {\n\t\t\t\t\tsetTargetClick();\n\t\t\t\t};\n\t\t\t} else {\n\t\t\t\tresetButton()\n\t\t\t}\n\t\t};\n\n\t\tfunction setModeClick() {\n\n\t\t\tmodePanel = modePanel ? false : true;\n\t\t\tsetClass(lblTarget, \"nodisplay\", modePanel);\n\t\t\tswitchMainView(lblMode, lblModeAttributes, options.labels.mode, options.labels.left, options.labels.right, modePanel);\n\n\t\t\tif (modePanel) {\n\n\t\t\t\tdocument.getElementById(\"btnLeft\").onclick = function() {\n\t\t\t\t\tmode = state.mode;\n\t\t\t\t\tmode = --mode < 0 ? properties.modes.length - 1 : mode;\n\t\t\t\t\tconsole.log(\"MODE :\" + mode);\n\t\t\t\t\tsetModeName(properties.modeNames[mode]);\n\t\t\t\t\tchkSwitchState();\n\t\t\t\t\tsendMsg();\n\t\t\t\t};\n\n\t\t\t\tdocument.getElementById(\"btnRight\").onclick = function() {\n\t\t\t\t\tmode = state.mode;\n\t\t\t\t\tmode = ++mode > properties.modes.length - 1 ? 0 : mode;\n\t\t\t\t\tconsole.log(\"MODE :\" + mode);\n\t\t\t\t\tsetModeName(properties.modeNames[mode]);\n\t\t\t\t\tchkSwitchState();\n\t\t\t\t\tsendMsg();\n\t\t\t\t};\n\n\t\t\t\tlblMode.onclick = function() {\n\t\t\t\t\tsetModeClick();\n\t\t\t\t};\n\t\t\t} else {\n\t\t\t\tresetButton()\n\t\t\t}\n\t\t};\n\n\t\tfunction setModeName(modeName) {\n\t\t\tlblMode.textContent = properties.modes[properties.modeNames.indexOf(modeName)].icon;\n\t\t\tlblMode.style.fill = properties.modes[properties.modeNames.indexOf(modeName)].color;\n\t\t\tstate.mode = properties.modeNames.indexOf(modeName);\n\t\t};\n\t\t\n\t\tfunction sendMsg() {\n\t\t\tif (typeof options.onChangeState == 'function') {\n\t\t\t\toptions.onChangeState(state.switch_state);\n\t\t\t}\n\t\t};\n\n\t\tfunction render() {\n\t\t\tconsole.log(\"RENDER\");\n\t\t\tsetAmbientTemperature(self.ambient_temperature);\n\t\t\tsetTargetTemperature(self.target_temperature);\n\t\t\tsetModeName(self.mode_name);\n\t\t\tchkSwitchState();\n\t\t};\n\n\t};\n})();\n\nvar initializing = true;\n\n(function(scope) {\n\tvar ghostThermostat = new ghostThermostatDial(document.getElementById('GhostThermostat'), {\n\t\tonChangeState: function() {\n\t\t\tvar p = {\n\t\t\t\t\"ambient_temperature\": ghostThermostat.ambient_temperature,\n\t\t\t\t\"target_temperature\": ghostThermostat.target_temperature,\n\t\t\t\t\"mode\": ghostThermostat.mode_name,\n\t\t\t\t\"switch_state\": ghostThermostat.switch_state,\n\t\t\t\t\"away\": ghostThermostat.away\n\t\t\t};\n\t\t\tscope.send({\n\t\t\t\ttopic: \"changed_state\",\n\t\t\t\tpayload: p\n\t\t\t});\n\t\t}\n\t});\n\n\tscope.$watch('msg', function(data) {\n\t\tif (initializing) {\n\t\t\tinitializing = false;\n\t\t} else {\n\t\t\tghostThermostat.ambient_temperature = data.payload.ambient_temperature || ghostThermostat.ambient_temperature;\n\t\t\tghostThermostat.target_temperature = data.payload.target_temperature || ghostThermostat.target_temperature;\n\t\t\tghostThermostat.mode_name = data.payload.mode || ghostThermostat.mode_name;\n\t\t\tghostThermostat.switch_state = data.payload.switch_state || ghostThermostat.switch_state;\n\t\t\tghostThermostat.away = data.payload.away || ghostThermostat.away;\n\t\t}\n\t});\n})(scope);\n</script>","storeOutMessages":true,"fwdInMessages":false,"resendOnRefresh":false,"templateScope":"local","x":790.5624923706055,"y":626.9592323303223,"wires":[["200e19571694acd5","53cc514e0c2ea118"]]},{"id":"f232320f2795950b","type":"ui_group","name":"Fermenter Control station","tab":"a74ffe3d.4eb73","order":16,"disp":true,"width":12,"collapse":false},{"id":"a74ffe3d.4eb73","type":"ui_tab","name":"Brewery","icon":"dashboard","order":1,"disabled":false,"hidden":false}]
I suspect that the code doesn't allow for more than one stat.
that will be a problem i need it to run 3 different ones and possibly more later down the road
it also constrains to one size in html. i made the layout bigger and all 3l gages are in one spot and i have to use a scroll bar to see them all
being in place is ok but i need to see them without using a scroll bar.
Ask the author or try something else - Dashboard Nest thermostat (flow) - Node-RED
ok i now have it where its in another group correctly,
however the origanl doesnt let me make changes.
instead if i click to make change on new one , it pops up on old one.
i posted a thread in codeforum.org
hopefully i can get some help with the CSS
fyi before i even made a post here for help i contaced the creater i never got a response.