Last active
May 27, 2018 02:37
-
-
Save smadds/c27acc599cb9d8c983c5696c27fedd02 to your computer and use it in GitHub Desktop.
Thermostat flow
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
[ | |
{ | |
"id": "a91da1f0.be4eb", | |
"type": "mqtt out", | |
"z": "9603e70c.db5a58", | |
"name": "", | |
"topic": "cmnd/boiler-switch/POWER", | |
"qos": "", | |
"retain": "", | |
"broker": "e4e72b98.749778", | |
"x": 1240, | |
"y": 500, | |
"wires": [] | |
}, | |
{ | |
"id": "b534f7e3.9658e8", | |
"type": "function", | |
"z": "9603e70c.db5a58", | |
"name": "Leaf Detector", | |
"func": "minleaf = 18;\nmaxleaf = 22;\ntemperature = msg.payload;\nmsg.payload=false;\nif (temperature >= minleaf){\n if (temperature <= maxleaf){\n msg.payload = true;\n }\n}\nmsg.topic = \"has_leaf\";\nreturn msg;", | |
"outputs": 1, | |
"noerr": 0, | |
"x": 560, | |
"y": 520, | |
"wires": [ | |
[ | |
"76ad15eb.5dedbc" | |
] | |
] | |
}, | |
{ | |
"id": "27fd73b7.2af2cc", | |
"type": "function", | |
"z": "9603e70c.db5a58", | |
"name": "target_temperature", | |
"func": "msg.topic = \"target_temperature\";\nreturn msg;", | |
"outputs": 1, | |
"noerr": 0, | |
"x": 550, | |
"y": 560, | |
"wires": [ | |
[ | |
"76ad15eb.5dedbc" | |
] | |
] | |
}, | |
{ | |
"id": "1bf96285.8fde1d", | |
"type": "ui_chart", | |
"z": "9603e70c.db5a58", | |
"name": "Temperature Chart", | |
"group": "bc8f9e23.09b6", | |
"order": 1, | |
"width": "15", | |
"height": "7", | |
"label": "Temperature", | |
"chartType": "line", | |
"legend": "true", | |
"xformat": "dd HH:mm", | |
"interpolate": "step", | |
"nodata": "", | |
"dot": false, | |
"ymin": "-10", | |
"ymax": "40", | |
"removeOlder": "24", | |
"removeOlderPoints": "", | |
"removeOlderUnit": "3600", | |
"cutout": "", | |
"useOneColor": false, | |
"colors": [ | |
"#1f77b4", | |
"#aec7e8", | |
"#ff7f0e", | |
"#2ca02c", | |
"#98df8a", | |
"#d62728", | |
"#ff9896", | |
"#9467bd", | |
"#c5b0d5" | |
], | |
"useOldStyle": true, | |
"x": 850, | |
"y": 860, | |
"wires": [ | |
[], | |
[] | |
] | |
}, | |
{ | |
"id": "75c042f8.6c248c", | |
"type": "change", | |
"z": "9603e70c.db5a58", | |
"name": "Topic: Ambient", | |
"rules": [ | |
{ | |
"t": "set", | |
"p": "topic", | |
"pt": "msg", | |
"to": "Ambient", | |
"tot": "str" | |
} | |
], | |
"action": "", | |
"property": "", | |
"from": "", | |
"to": "", | |
"reg": false, | |
"x": 620, | |
"y": 800, | |
"wires": [ | |
[ | |
"1bf96285.8fde1d" | |
] | |
] | |
}, | |
{ | |
"id": "48b59a23.9fc894", | |
"type": "change", | |
"z": "9603e70c.db5a58", | |
"name": "Power > payload", | |
"rules": [ | |
{ | |
"t": "set", | |
"p": "payload", | |
"pt": "msg", | |
"to": "payload.POWER", | |
"tot": "msg" | |
}, | |
{ | |
"t": "change", | |
"p": "payload", | |
"pt": "msg", | |
"from": "ON", | |
"fromt": "str", | |
"to": "5", | |
"tot": "num" | |
}, | |
{ | |
"t": "change", | |
"p": "payload", | |
"pt": "msg", | |
"from": "OFF", | |
"fromt": "str", | |
"to": "0", | |
"tot": "num" | |
}, | |
{ | |
"t": "set", | |
"p": "topic", | |
"pt": "msg", | |
"to": "Boiler", | |
"tot": "str" | |
} | |
], | |
"action": "", | |
"property": "", | |
"from": "", | |
"to": "", | |
"reg": false, | |
"x": 610, | |
"y": 920, | |
"wires": [ | |
[ | |
"1bf96285.8fde1d", | |
"c30c5bec.2e42e8" | |
] | |
] | |
}, | |
{ | |
"id": "621c22a7.ccb68c", | |
"type": "json", | |
"z": "9603e70c.db5a58", | |
"name": "", | |
"x": 430, | |
"y": 920, | |
"wires": [ | |
[ | |
"48b59a23.9fc894", | |
"204ee869.ae3af8" | |
] | |
] | |
}, | |
{ | |
"id": "a421be4a.30089", | |
"type": "mqtt in", | |
"z": "9603e70c.db5a58", | |
"name": "", | |
"topic": "tele/boiler-switch/STATE", | |
"qos": "2", | |
"broker": "e4e72b98.749778", | |
"x": 190, | |
"y": 920, | |
"wires": [ | |
[ | |
"621c22a7.ccb68c" | |
] | |
] | |
}, | |
{ | |
"id": "ce7db6d2.eea988", | |
"type": "function", | |
"z": "9603e70c.db5a58", | |
"name": "Heat required?", | |
"func": "hysteresis = 0.5; // double this\nboostrequired = 3; //if ambient is degrees below target, need boosting\n\nif (msg.topic == \"ambient_temperature\") {\n ambient = parseInt(msg.payload);\n}\nif (msg.topic == \"target_temperature\" || msg.topic == \"set_target_temperature\") {\n target = parseInt(msg.payload);\n}\n\n\nif (typeof ambient !== 'undefined' && typeof target !== 'undefined') { //wait until both received\n if (target - ambient >= hysteresis + boostrequired){\n msg.payload = 2;\n node.status({fill:\"red\",shape:\"dot\",text:\"BOOST\"});\n return msg;\n } else if (target - ambient >= hysteresis){\n msg.payload = 1;\n node.status({fill:\"yellow\",shape:\"dot\",text:\"HEAT\"});\n return msg;\n } else if (ambient - target >= hysteresis) {\n msg.payload = 0;\n node.status({fill:\"gray\",shape:\"ring\",text:\"OFF\"});\n return msg;\n }\n}\n\n\n\n", | |
"outputs": 1, | |
"noerr": 0, | |
"x": 760, | |
"y": 380, | |
"wires": [ | |
[ | |
"75b89ed4.3cd9d" | |
] | |
] | |
}, | |
{ | |
"id": "c8435a3c.d5a3b8", | |
"type": "inject", | |
"z": "9603e70c.db5a58", | |
"name": "", | |
"topic": "", | |
"payload": "21", | |
"payloadType": "str", | |
"repeat": "", | |
"crontab": "", | |
"once": false, | |
"x": 1090, | |
"y": 700, | |
"wires": [ | |
[ | |
"4730260e.c5dac8" | |
] | |
] | |
}, | |
{ | |
"id": "73aa50a.de1eeb", | |
"type": "inject", | |
"z": "9603e70c.db5a58", | |
"name": "", | |
"topic": "", | |
"payload": "26", | |
"payloadType": "str", | |
"repeat": "", | |
"crontab": "", | |
"once": false, | |
"x": 1090, | |
"y": 740, | |
"wires": [ | |
[ | |
"4730260e.c5dac8" | |
] | |
] | |
}, | |
{ | |
"id": "7ea5e67d.dfcd58", | |
"type": "inject", | |
"z": "9603e70c.db5a58", | |
"name": "", | |
"topic": "", | |
"payload": "10", | |
"payloadType": "str", | |
"repeat": "", | |
"crontab": "", | |
"once": false, | |
"x": 1090, | |
"y": 660, | |
"wires": [ | |
[ | |
"4730260e.c5dac8" | |
] | |
] | |
}, | |
{ | |
"id": "ba9ac366.37ca7", | |
"type": "change", | |
"z": "9603e70c.db5a58", | |
"name": "Topic:Target", | |
"rules": [ | |
{ | |
"t": "set", | |
"p": "topic", | |
"pt": "msg", | |
"to": "Target", | |
"tot": "str" | |
} | |
], | |
"action": "", | |
"property": "", | |
"from": "", | |
"to": "", | |
"reg": false, | |
"x": 620, | |
"y": 860, | |
"wires": [ | |
[ | |
"1bf96285.8fde1d" | |
] | |
] | |
}, | |
{ | |
"id": "c30c5bec.2e42e8", | |
"type": "ui_template", | |
"z": "9603e70c.db5a58", | |
"group": "151be4a4.2a40bb", | |
"name": "Boiler Firing", | |
"order": 6, | |
"width": "0", | |
"height": "0", | |
"format": "<div layout=\"row\" layout-align=\"space-between center\" style=\"vertical-align:middle;\">\n <p flex>{{msg.topic}}</p>\n <ng-md-icon class=\"powerbutton\" icon=\"power_settings_new\" ng-style=\"{background:msg.payload=='0' ?'gray':'green'}\" height=\"100%\"></ng-md-icon>\n</div>\n\n<style>\n .powerbutton {\n color:white;\n padding:3px;\n border-radius:50%;\n height: auto;\n width: auto;\n }\n</style>", | |
"storeOutMessages": false, | |
"fwdInMessages": false, | |
"templateScope": "local", | |
"x": 830, | |
"y": 920, | |
"wires": [ | |
[] | |
] | |
}, | |
{ | |
"id": "76ad15eb.5dedbc", | |
"type": "ui_template", | |
"z": "9603e70c.db5a58", | |
"group": "3f0f8980.fdfed6", | |
"name": "Temperature Nest", | |
"order": 2, | |
"width": "6", | |
"height": "6", | |
"format": "<div id=\"thermostat\"></div>\n\n<style>\n@import url(http://fonts.googleapis.com/css?family=Open+Sans:300);\n#thermostat {\n margin: 0 auto;\n -webkit-tap-highlight-color: rgba(0, 0, 0, 0);\n}\n.dial {\n -webkit-user-select: none;\n -moz-user-select: none;\n -ms-user-select: none;\n user-select: none;\n}\n.dial.away .dial__ico__leaf {\n visibility: hidden;\n}\n.dial.away .dial__lbl--target {\n visibility: hidden;\n}\n.dial.away .dial__lbl--target--half {\n visibility: hidden;\n}\n.dial.away .dial__lbl--away {\n opacity: 1;\n}\n.dial .dial__shape {\n -webkit-transition: fill 0.5s;\n transition: fill 0.5s;\n}\n.dial__ico__leaf {\n fill: #13EB13;\n opacity: 0;\n -webkit-transition: opacity 0.5s;\n transition: opacity 0.5s;\n pointer-events: none;\n}\n.dial.has-leaf .dial__ico__leaf {\n display: block;\n opacity: 1;\n pointer-events: initial;\n}\n.dial__editableIndicator {\n fill: white;\n fill-rule: evenodd;\n opacity: 0;\n -webkit-transition: opacity 0.5s;\n transition: opacity 0.5s;\n}\n.dial--edit .dial__editableIndicator {\n opacity: 1;\n}\n.dial--state--off .dial__shape {\n fill: #3d3c3c;\n}\n.dial--state--heating .dial__shape {\n fill: #E36304;\n}\n.dial--state--boost .dial__shape {\n fill: #E30004;\n}\n.dial--state--cooling .dial__shape {\n fill: #007AF1;\n}\n.dial__ticks path {\n fill: rgba(255, 255, 255, 0.3);\n}\n.dial__ticks path.active {\n fill: rgba(255, 255, 255, 0.8);\n}\n.dial text {\n fill: white;\n text-anchor: middle;\n font-family: Helvetica, sans-serif;\n alignment-baseline: central;\n}\n.dial__lbl--target {\n font-size: 120px;\n font-weight: bold;\n}\n.dial__lbl--target--half {\n font-size: 40px;\n font-weight: bold;\n opacity: 0;\n -webkit-transition: opacity 0.1s;\n transition: opacity 0.1s;\n}\n.dial__lbl--target--half.shown {\n opacity: 1;\n -webkit-transition: opacity 0s;\n transition: opacity 0s;\n}\n.dial__lbl--ambient {\n font-size: 22px;\n font-weight: bold;\n}\n.dial__lbl--away {\n font-size: 72px;\n font-weight: bold;\n opacity: 0;\n pointer-events: none;\n}\n#controls {\n font-family: Open Sans;\n background-color: rgba(255, 255, 255, 0.25);\n padding: 20px;\n border-radius: 5px;\n position: absolute;\n left: 50%;\n -webkit-transform: translatex(-50%);\n transform: translatex(-50%);\n margin-top: 20px;\n}\n#controls label {\n text-align: left;\n display: block;\n}\n#controls label span {\n display: inline-block;\n width: 200px;\n text-align: right;\n font-size: 0.8em;\n text-transform: uppercase;\n}\n#controls p {\n margin: 0;\n margin-bottom: 1em;\n padding-bottom: 1em;\n border-bottom: 2px solid #ccc;\n}\n</style>\n<script>\n var thermostatDial = (function() {\n\t\n\t/*\n\t * Utility functions\n\t */\n\t\n\t// Create an element with proper SVG namespace, optionally setting its attributes and appending it to another element\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\t\n\t// Set attributes for an element\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\t\n\t// Rotate a cartesian point about given origin by X degrees\n\tfunction rotatePoint(point, angle, origin) {\n\t\tvar radians = angle * Math.PI/180;\n\t\tvar x = point[0]-origin[0];\n\t\tvar y = point[1]-origin[1];\n\t\tvar x1 = x*Math.cos(radians) - y*Math.sin(radians) + origin[0];\n\t\tvar y1 = x*Math.sin(radians) + y*Math.cos(radians) + origin[1];\n\t\treturn [x1,y1];\n\t}\n\t\n\t// Rotate an array of cartesian points about a given origin by X degrees\n\tfunction rotatePoints(points, angle, origin) {\n\t\treturn points.map(function(point) {\n\t\t\treturn rotatePoint(point, angle, origin);\n\t\t});\n\t}\n\t\n\t// Given an array of points, return an SVG path string representing the shape they define\n\tfunction pointsToPath(points) {\n\t\treturn points.map(function(point, iPoint) {\n\t\t\treturn (iPoint>0?'L':'M') + point[0] + ' ' + point[1];\n\t\t}).join(' ')+'Z';\n\t}\n\t\n\tfunction circleToPath(cx, cy, r) {\n\t\treturn [\n\t\t\t\"M\",cx,\",\",cy,\n\t\t\t\"m\",0-r,\",\",0,\n\t\t\t\"a\",r,\",\",r,0,1,\",\",0,r*2,\",\",0,\n\t\t\t\"a\",r,\",\",r,0,1,\",\",0,0-r*2,\",\",0,\n\t\t\t\"z\"\n\t\t].join(' ').replace(/\\s,\\s/g,\",\");\n\t}\n\t\n\tfunction donutPath(cx,cy,rOuter,rInner) {\n\t\treturn circleToPath(cx,cy,rOuter) + \" \" + circleToPath(cx,cy,rInner);\n\t}\n\t\n\t// Restrict a number to a min + max range\n\tfunction restrictToRange(val,min,max) {\n\t\tif (val < min) return min;\n\t\tif (val > max) return max;\n\t\treturn val;\n\t}\n\t\n\t// Round a number to the nearest 0.5\n\tfunction roundHalf(num) {\n\t\treturn Math.round(num*2)/2;\n\t}\n\t\n\tfunction setClass(el, className, state) {\n\t\tel.classList[state ? 'add' : 'remove'](className);\n\t}\n\t\n\t/*\n\t * The \"MEAT\"\n\t */\n\n\treturn function(targetElement, options) {\n\t\tvar self = this;\n\t\t\n\t\t/*\n\t\t * Options\n\t\t */\n\t\toptions = options || {};\n\t\toptions = {\n\t\t\tdiameter: options.diameter || 500,\n\t\t\tminValue: options.minValue || 10, // Minimum value for target temperature\n\t\t\tmaxValue: options.maxValue || 30, // Maximum value for target temperature\n\t\t\tnumTicks: options.numTicks || 200, // Number of tick lines to display around the dial\n\t\t\tonSetTargetTemperature: options.onSetTargetTemperature || function() {}, // Function called when new target temperature set by the dial\n\t\t};\n\t\t\n\t\t/*\n\t\t * Properties - calculated from options in many cases\n\t\t */\n\t\tvar properties = {\n\t\t\ttickDegrees: 300, // Degrees of the dial that should be covered in tick lines\n\t\t\trangeValue: options.maxValue - options.minValue,\n\t\t\tradius: options.diameter/2,\n\t\t\tticksOuterRadius: options.diameter / 30,\n\t\t\tticksInnerRadius: options.diameter / 8,\n\t\t\thvac_states: ['off', 'heating', 'cooling', 'boost'],\n\t\t\tdragLockAxisDistance: 15,\n\t\t}\n\t\tproperties.lblAmbientPosition = [properties.radius, properties.ticksOuterRadius-(properties.ticksOuterRadius-properties.ticksInnerRadius)/2]\n\t\tproperties.offsetDegrees = 180-(360-properties.tickDegrees)/2;\n\t\t\n\t\t/*\n\t\t * Object state\n\t\t */\n\t\tvar state = {\n\t\t\ttarget_temperature: options.minValue,\n\t\t\tambient_temperature: options.minValue,\n\t\t\thvac_state: properties.hvac_states[0],\n\t\t\thas_leaf: false,\n\t\t\taway: false\n\t\t};\n\t\t\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 = restrictTargetTemperature(+val);\n\t\t\t\trender();\n\t\t\t}\n\t\t});\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 = roundHalf(+val);\n\t\t\t\trender();\n\t\t\t}\n\t\t});\n\t\tObject.defineProperty(this,'hvac_state',{\n\t\t\tget: function() {\n\t\t\t\treturn state.hvac_state;\n\t\t\t},\n\t\t\tset: function(val) {\n\t\t\t\tif (properties.hvac_states.indexOf(val)>=0) {\n\t\t\t\t\tstate.hvac_state = val;\n\t\t\t\t\trender();\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t\tObject.defineProperty(this,'has_leaf',{\n\t\t\tget: function() {\n\t\t\t\treturn state.has_leaf;\n\t\t\t},\n\t\t\tset: function(val) {\n\t\t\t\tstate.has_leaf = !!val;\n\t\t\t\trender();\n\t\t\t}\n\t\t});\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 = !!val;\n\t\t\t\trender();\n\t\t\t}\n\t\t});\n\t\t\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\t\t// CIRCULAR 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: 'dial__shape'\n\t\t},svg);\n\t\t// EDITABLE INDICATOR\n\t\tvar editCircle = createSVGElement('path',{\n\t\t\td: donutPath(properties.radius,properties.radius,properties.radius-4,properties.radius-8),\n\t\t\tclass: 'dial__editableIndicator',\n\t\t},svg);\n\t\t\n\t\t/*\n\t\t * Ticks\n\t\t */\n\t\tvar ticks = createSVGElement('g',{\n\t\t\tclass: 'dial__ticks'\t\n\t\t},svg);\n\t\tvar tickPoints = [\n\t\t\t[properties.radius-1, properties.ticksOuterRadius],\n\t\t\t[properties.radius+1, properties.ticksOuterRadius],\n\t\t\t[properties.radius+1, properties.ticksInnerRadius],\n\t\t\t[properties.radius-1, properties.ticksInnerRadius]\n\t\t];\n\t\tvar tickPointsLarge = [\n\t\t\t[properties.radius-1.5, properties.ticksOuterRadius],\n\t\t\t[properties.radius+1.5, properties.ticksOuterRadius],\n\t\t\t[properties.radius+1.5, properties.ticksInnerRadius+20],\n\t\t\t[properties.radius-1.5, properties.ticksInnerRadius+20]\n\t\t];\n\t\tvar theta = properties.tickDegrees/options.numTicks;\n\t\tvar tickArray = [];\n\t\tfor (var iTick=0; iTick<options.numTicks; iTick++) {\n\t\t\ttickArray.push(createSVGElement('path',{d:pointsToPath(tickPoints)},ticks));\n\t\t};\n\t\t\n\t\t/*\n\t\t * Labels\n\t\t */\n\t\tvar lblTarget = createSVGElement('text',{\n\t\t\tx: properties.radius,\n\t\t\ty: properties.radius,\n\t\t\tclass: 'dial__lbl dial__lbl--target'\n\t\t},svg);\n\t\tvar lblTarget_text = document.createTextNode('');\n\t\tlblTarget.appendChild(lblTarget_text);\n\t\t//\n\t\tvar lblTargetHalf = createSVGElement('text',{\n\t\t\tx: properties.radius + properties.radius/2.5,\n\t\t\ty: properties.radius - properties.radius/8,\n\t\t\tclass: 'dial__lbl dial__lbl--target--half'\n\t\t},svg);\n\t\tvar lblTargetHalf_text = document.createTextNode('.5');\n\t\tlblTargetHalf.appendChild(lblTargetHalf_text);\n\t\t//\n\t\tvar lblAmbient = createSVGElement('text',{\n\t\t\tclass: 'dial__lbl dial__lbl--ambient'\n\t\t},svg);\n\t\tvar lblAmbient_text = document.createTextNode('');\n\t\tlblAmbient.appendChild(lblAmbient_text);\n\t\t//\n\t\tvar lblAway = createSVGElement('text',{\n\t\t\tx: properties.radius,\n\t\t\ty: properties.radius,\n\t\t\tclass: 'dial__lbl dial__lbl--away'\n\t\t},svg);\n\t\tvar lblAway_text = document.createTextNode('AWAY');\n\t\tlblAway.appendChild(lblAway_text);\n\t\t//\n\t\tvar icoLeaf = createSVGElement('path',{\n\t\t\tclass: 'dial__ico__leaf'\n\t\t},svg);\n\t\t\n\t\t/*\n\t\t * LEAF\n\t\t */\n\t\tvar leafScale = properties.radius/5/100;\n\t\tvar leafDef = [\"M\", 3, 84, \"c\", 24, 17, 51, 18, 73, -6, \"C\", 100, 52, 100, 22, 100, 4, \"c\", -13, 15, -37, 9, -70, 19, \"C\", 4, 32, 0, 63, 0, 76, \"c\", 6, -7, 18, -17, 33, -23, 24, -9, 34, -9, 48, -20, -9, 10, -20, 16, -43, 24, \"C\", 22, 63, 8, 78, 3, 84, \"z\"].map(function(x) {\n\t\t\treturn isNaN(x) ? x : x*leafScale;\n\t\t}).join(' ');\n\t\tvar translate = [properties.radius-(leafScale*100*0.5),properties.radius*1.5]\n\t\tvar icoLeaf = createSVGElement('path',{\n\t\t\tclass: 'dial__ico__leaf',\n\t\t\td: leafDef,\n\t\t\ttransform: 'translate('+translate[0]+','+translate[1]+')'\n\t\t},svg);\n\t\t\t\n\t\t/*\n\t\t * RENDER\n\t\t */\n\t\tfunction render() {\n\t\t\trenderAway();\n\t\t\trenderHvacState();\n\t\t\trenderTicks();\n\t\t\trenderTargetTemperature();\n\t\t\trenderAmbientTemperature();\n\t\t\trenderLeaf();\n\t\t}\n\t\trender();\n\n\t\t/*\n\t\t * RENDER - ticks\n\t\t */\n\t\tfunction renderTicks() {\n\t\t\tvar vMin, vMax;\n\t\t\tif (self.away) {\n\t\t\t\tvMin = self.ambient_temperature;\n\t\t\t\tvMax = vMin;\n\t\t\t} else {\n\t\t\t\tvMin = Math.min(self.ambient_temperature, self.target_temperature);\n\t\t\t\tvMax = Math.max(self.ambient_temperature, self.target_temperature);\n\t\t\t}\n\t\t\tvar min = restrictToRange(Math.round((vMin-options.minValue)/properties.rangeValue * options.numTicks),0,options.numTicks-1);\n\t\t\tvar max = restrictToRange(Math.round((vMax-options.minValue)/properties.rangeValue * options.numTicks),0,options.numTicks-1);\n\t\t\t//\n\t\t\ttickArray.forEach(function(tick,iTick) {\n\t\t\t\tvar isLarge = iTick==min || iTick==max;\n\t\t\t\tvar isActive = iTick >= min && iTick <= max;\n\t\t\t\tattr(tick,{\n\t\t\t\t\td: pointsToPath(rotatePoints(isLarge ? tickPointsLarge: tickPoints,iTick*theta-properties.offsetDegrees,[properties.radius, properties.radius])),\n\t\t\t\t\tclass: isActive ? 'active' : ''\n\t\t\t\t});\n\t\t\t});\n\t\t}\n\t\n\t\t/*\n\t\t * RENDER - ambient temperature\n\t\t */\n\t\tfunction renderAmbientTemperature() {\n\t\t\tlblAmbient_text.nodeValue = Math.floor(self.ambient_temperature);\n\t\t\tif (self.ambient_temperature%1!=0) {\n\t\t\t\tlblAmbient_text.nodeValue += '.5';\n\t\t\t}\n\t\t\tvar peggedValue = restrictToRange(self.ambient_temperature, options.minValue, options.maxValue);\n\t\t\tdegs = properties.tickDegrees * (peggedValue-options.minValue)/properties.rangeValue - properties.offsetDegrees;\n\t\t\tif (peggedValue > self.target_temperature) {\n\t\t\t\tdegs += 8;\n\t\t\t} else {\n\t\t\t\tdegs -= 8;\n\t\t\t}\n\t\t\tvar pos = rotatePoint(properties.lblAmbientPosition,degs,[properties.radius, properties.radius]);\n\t\t\tattr(lblAmbient,{\n\t\t\t\tx: pos[0],\n\t\t\t\ty: pos[1]\n\t\t\t});\n\t\t}\n\n\t\t/*\n\t\t * RENDER - target temperature\n\t\t */\n\t\tfunction renderTargetTemperature() {\n\t\t\tlblTarget_text.nodeValue = Math.floor(self.target_temperature);\n\t\t\tsetClass(lblTargetHalf,'shown',self.target_temperature%1!=0);\n\t\t}\n\t\t\n\t\t/*\n\t\t * RENDER - leaf\n\t\t */\n\t\tfunction renderLeaf() {\n\t\t\tsetClass(svg,'has-leaf',self.has_leaf);\n\t\t}\n\t\t\n\t\t/*\n\t\t * RENDER - HVAC state\n\t\t */\n\t\tfunction renderHvacState() {\n\t\t\tArray.prototype.slice.call(svg.classList).forEach(function(c) {\n\t\t\t\tif (c.match(/^dial--state--/)) {\n\t\t\t\t\tsvg.classList.remove(c);\n\t\t\t\t};\n\t\t\t});\n\t\t\tsvg.classList.add('dial--state--'+self.hvac_state);\n\t\t}\n\t\t\n\t\t/*\n\t\t * RENDER - away\n\t\t */\n\t\tfunction renderAway() {\n\t\t\tsvg.classList[self.away ? 'add' : 'remove']('away');\n\t\t}\n\t\t\n\t\t/*\n\t\t * Drag to control\n\t\t */\n\t\tvar _drag = {\n\t\t\tinProgress: false,\n\t\t\tstartPoint: null,\n\t\t\tstartTemperature: 0,\n\t\t\tlockAxis: undefined\n\t\t};\n\t\t\n\t\tfunction eventPosition(ev) {\n\t\t\tif (ev.targetTouches && ev.targetTouches.length) {\n\t\t\t\treturn [ev.targetTouches[0].clientX, ev.targetTouches[0].clientY];\n\t\t\t} else {\n\t\t\t\treturn [ev.x, ev.y];\n\t\t\t};\n\t\t}\n\t\t\n\t\tvar startDelay;\n\t\tfunction dragStart(ev) {\n\t\t\tstartDelay = setTimeout(function() {\n\t\t\t\tsetClass(svg, 'dial--edit', true);\n\t\t\t\t_drag.inProgress = true;\n\t\t\t\t_drag.startPoint = eventPosition(ev);\n\t\t\t\t_drag.startTemperature = self.target_temperature || options.minValue;\n\t\t\t\t_drag.lockAxis = undefined;\n\t\t\t},1000);\n\t\t};\n\t\t\n\t\tfunction dragEnd (ev) {\n\t\t\tclearTimeout(startDelay);\n\t\t\tsetClass(svg, 'dial--edit', false);\n\t\t\tif (!_drag.inProgress) return;\n\t\t\t_drag.inProgress = false;\n\t\t\tif (self.target_temperature != _drag.startTemperature) {\n\t\t\t\tif (typeof options.onSetTargetTemperature == 'function') {\n\t\t\t\t\toptions.onSetTargetTemperature(self.target_temperature);\n\t\t\t\t};\n\t\t\t};\n\t\t};\n\t\t\n\t\tfunction dragMove(ev) {\n\t\t\tev.preventDefault();\n\t\t\tif (!_drag.inProgress) return;\n\t\t\tvar evPos = eventPosition(ev);\n\t\t\tvar dy = _drag.startPoint[1]-evPos[1];\n\t\t\tvar dx = evPos[0] - _drag.startPoint[0];\n\t\t\tvar dxy;\n\t\t\tif (_drag.lockAxis == 'x') {\n\t\t\t\tdxy = dx;\n\t\t\t} else if (_drag.lockAxis == 'y') {\n\t\t\t\tdxy = dy;\n\t\t\t} else if (Math.abs(dy) > properties.dragLockAxisDistance) {\n\t\t\t\t_drag.lockAxis = 'y';\n\t\t\t\tdxy = dy;\n\t\t\t} else if (Math.abs(dx) > properties.dragLockAxisDistance) {\n\t\t\t\t_drag.lockAxis = 'x';\n\t\t\t\tdxy = dx;\n\t\t\t} else {\n\t\t\t\tdxy = (Math.abs(dy) > Math.abs(dx)) ? dy : dx;\n\t\t\t};\n\t\t\tvar dValue = (dxy*getSizeRatio())/(options.diameter)*properties.rangeValue;\n\t\t\tself.target_temperature = roundHalf(_drag.startTemperature+dValue);\n\t\t}\n\t\t\n\t\tsvg.addEventListener('mousedown',dragStart);\n\t\tsvg.addEventListener('touchstart',dragStart);\n\t\t\n\t\tsvg.addEventListener('mouseup',dragEnd);\n\t\tsvg.addEventListener('mouseleave',dragEnd);\n\t\tsvg.addEventListener('touchend',dragEnd);\n\t\t\n\t\tsvg.addEventListener('mousemove',dragMove);\n\t\tsvg.addEventListener('touchmove',dragMove);\n\t\t//\n\t\t\n\t\t/*\n\t\t * Helper functions\n\t\t */\n\t\tfunction restrictTargetTemperature(t) {\n\t\t\treturn restrictToRange(roundHalf(t),options.minValue,options.maxValue);\n\t\t}\n\t\t\n\t\tfunction angle(point) {\n\t\t\tvar dx = point[0] - properties.radius;\n\t\t\tvar dy = point[1] - properties.radius;\n\t\t\tvar theta = Math.atan(dx/dy) / (Math.PI/180);\n\t\t\tif (point[0]>=properties.radius && point[1] < properties.radius) {\n\t\t\t\ttheta = 90-theta - 90;\n\t\t\t} else if (point[0]>=properties.radius && point[1] >= properties.radius) {\n\t\t\t\ttheta = 90-theta + 90;\n\t\t\t} else if (point[0]<properties.radius && point[1] >= properties.radius) {\n\t\t\t\ttheta = 90-theta + 90;\n\t\t\t} else if (point[0]<properties.radius && point[1] < properties.radius) {\n\t\t\t\ttheta = 90-theta+270;\n\t\t\t}\n\t\t\treturn theta;\n\t\t};\n\t\t\n\t\tfunction getSizeRatio() {\n\t\t\treturn options.diameter / targetElement.clientWidth;\n\t\t}\n\t\t\n\t};\n})();\n\n/* ==== */\n(function(scope) {\n \n var nest = new thermostatDial(document.getElementById('thermostat'),{\n \tonSetTargetTemperature: function(v) {\n \t\tscope.send({topic: \"target_temperature\", payload: v});\n \t}\n });\n\n\n scope.$watch('msg', function(data) {\n // console.log(data.topic+\" \"+data.payload);\n if (data.topic == \"ambient_temperature\") {\n nest.ambient_temperature = data.payload;\n } if (data.topic == \"target_temperature\") {\n nest.target_temperature = data.payload;\n \t\tscope.send({topic: \"set_target_temperature\", payload: nest.target_temperature});\n } if (data.topic == \"+\") {\n nest.target_temperature = nest.target_temperature + 0.5;\n \t\tscope.send({topic: \"target_temperature\", payload: nest.target_temperature});\n } if (data.topic == \"-\") {\n nest.target_temperature = nest.target_temperature - 0.5;\n \t\tscope.send({topic: \"target_temperature\", payload: nest.target_temperature});\n } if (data.topic == \"hvac_state\") {\n nest.hvac_state = data.payload;\n } if (data.topic == \"has_leaf\") {\n nest.has_leaf = (data.payload===true);\n } if (data.topic == \"away\") {\n nest.away = data.payload;\n }\n render();\n });\n})(scope);\n\n\n</script>", | |
"storeOutMessages": true, | |
"fwdInMessages": false, | |
"templateScope": "local", | |
"x": 810, | |
"y": 620, | |
"wires": [ | |
[ | |
"f905e355.14a82" | |
] | |
] | |
}, | |
{ | |
"id": "f905e355.14a82", | |
"type": "function", | |
"z": "9603e70c.db5a58", | |
"name": "filter target", | |
"func": "//if (msg.topic == \"target_temperature\"|| msg.topic == \"set_target_temperature\") {\nif (msg.topic == \"target_temperature\") {\nreturn msg;\n}", | |
"outputs": 1, | |
"noerr": 0, | |
"x": 1090, | |
"y": 620, | |
"wires": [ | |
[ | |
"4730260e.c5dac8" | |
] | |
] | |
}, | |
{ | |
"id": "ad9c2b98.7be488", | |
"type": "mqtt in", | |
"z": "9603e70c.db5a58", | |
"name": "", | |
"topic": "stat/boiler-switch/RESULT", | |
"qos": "2", | |
"broker": "e4e72b98.749778", | |
"x": 200, | |
"y": 980, | |
"wires": [ | |
[ | |
"621c22a7.ccb68c" | |
] | |
] | |
}, | |
{ | |
"id": "204ee869.ae3af8", | |
"type": "function", | |
"z": "9603e70c.db5a58", | |
"name": "HVAC state", | |
"func": "switch(msg.payload.POWER) {\n case \"ON\":\n msg.payload = \"heating\";\n break;\n default:\n msg.payload = \"off\";\n}\n\nmsg.topic = \"hvac_state\";\nreturn msg;", | |
"outputs": 1, | |
"noerr": 0, | |
"x": 530, | |
"y": 700, | |
"wires": [ | |
[ | |
"76ad15eb.5dedbc" | |
] | |
] | |
}, | |
{ | |
"id": "5dfb522a.4f1d2c", | |
"type": "comment", | |
"z": "9603e70c.db5a58", | |
"name": "TEMPERATURE", | |
"info": "", | |
"x": 160, | |
"y": 220, | |
"wires": [] | |
}, | |
{ | |
"id": "4730260e.c5dac8", | |
"type": "mqtt out", | |
"z": "9603e70c.db5a58", | |
"name": "", | |
"topic": "settings/lounge/targets/temperature", | |
"qos": "2", | |
"retain": "true", | |
"broker": "e4e72b98.749778", | |
"x": 1390, | |
"y": 580, | |
"wires": [] | |
}, | |
{ | |
"id": "ab285580.4e9c78", | |
"type": "mqtt in", | |
"z": "9603e70c.db5a58", | |
"name": "", | |
"topic": "settings/lounge/targets/temperature", | |
"qos": "2", | |
"broker": "e4e72b98.749778", | |
"x": 230, | |
"y": 860, | |
"wires": [ | |
[ | |
"ba9ac366.37ca7" | |
] | |
] | |
}, | |
{ | |
"id": "6c23da4d.54f924", | |
"type": "mqtt in", | |
"z": "9603e70c.db5a58", | |
"name": "", | |
"topic": "settings/lounge/targets/temperature", | |
"qos": "2", | |
"broker": "e4e72b98.749778", | |
"x": 230, | |
"y": 360, | |
"wires": [ | |
[ | |
"abd8c22a.3cefc", | |
"b534f7e3.9658e8", | |
"27fd73b7.2af2cc" | |
] | |
] | |
}, | |
{ | |
"id": "abd8c22a.3cefc", | |
"type": "function", | |
"z": "9603e70c.db5a58", | |
"name": "target_temperature", | |
"func": "msg.topic = \"target_temperature\";\nglobal.set(\"lounge-target\",msg.payload);\nnode.status({text:msg.payload + \"°C\"});\nreturn msg;", | |
"outputs": 1, | |
"noerr": 0, | |
"x": 540, | |
"y": 360, | |
"wires": [ | |
[ | |
"ce7db6d2.eea988" | |
] | |
] | |
}, | |
{ | |
"id": "dda02db7.7376f", | |
"type": "mqtt out", | |
"z": "9603e70c.db5a58", | |
"name": "", | |
"topic": "settings/lounge/ambient/temperature", | |
"qos": "2", | |
"retain": "true", | |
"broker": "e4e72b98.749778", | |
"x": 1050, | |
"y": 280, | |
"wires": [] | |
}, | |
{ | |
"id": "f751b2e5.46214", | |
"type": "mqtt in", | |
"z": "9603e70c.db5a58", | |
"name": "", | |
"topic": "tele/sens-lounge01/SENSOR", | |
"qos": "2", | |
"broker": "e4e72b98.749778", | |
"x": 200, | |
"y": 280, | |
"wires": [ | |
[ | |
"5d558da9.ac3184" | |
] | |
] | |
}, | |
{ | |
"id": "999be20.4408f2", | |
"type": "change", | |
"z": "9603e70c.db5a58", | |
"name": "temperature>payload", | |
"rules": [ | |
{ | |
"t": "move", | |
"p": "payload.BME280.Temperature", | |
"pt": "msg", | |
"to": "payload", | |
"tot": "msg" | |
} | |
], | |
"action": "", | |
"property": "", | |
"from": "", | |
"to": "", | |
"reg": false, | |
"x": 580, | |
"y": 280, | |
"wires": [ | |
[ | |
"a4b5398.9d833c8" | |
] | |
] | |
}, | |
{ | |
"id": "5d558da9.ac3184", | |
"type": "json", | |
"z": "9603e70c.db5a58", | |
"name": "", | |
"x": 410, | |
"y": 280, | |
"wires": [ | |
[ | |
"999be20.4408f2" | |
] | |
] | |
}, | |
{ | |
"id": "28bd71d8.808cee", | |
"type": "mqtt in", | |
"z": "9603e70c.db5a58", | |
"name": "", | |
"topic": "settings/lounge/ambient/temperature", | |
"qos": "2", | |
"broker": "e4e72b98.749778", | |
"x": 230, | |
"y": 520, | |
"wires": [ | |
[ | |
"d6b2a62f.8c33d8", | |
"b534f7e3.9658e8" | |
] | |
] | |
}, | |
{ | |
"id": "d6b2a62f.8c33d8", | |
"type": "function", | |
"z": "9603e70c.db5a58", | |
"name": "ambient_temperature", | |
"func": "msg.topic = \"ambient_temperature\";\nglobal.set(\"lounge-ambient\",msg.payload);\nnode.status({text:msg.payload + \"°C\"});\nreturn msg;", | |
"outputs": 1, | |
"noerr": 0, | |
"x": 540, | |
"y": 420, | |
"wires": [ | |
[ | |
"ce7db6d2.eea988", | |
"76ad15eb.5dedbc" | |
] | |
] | |
}, | |
{ | |
"id": "50c8733c.87112c", | |
"type": "function", | |
"z": "9603e70c.db5a58", | |
"name": "Heat", | |
"func": "if (parseInt(msg.payload) > 0){\n msg.payload = true;\n node.status({fill:\"green\",shape:\"dot\",text:\"ON\"});\n} else {\n msg.payload = false;\n node.status({fill:\"gray\",shape:\"ring\",text:\"OFF\"});\n}\nreturn msg;", | |
"outputs": 1, | |
"noerr": 0, | |
"x": 1030, | |
"y": 500, | |
"wires": [ | |
[ | |
"a91da1f0.be4eb" | |
] | |
] | |
}, | |
{ | |
"id": "75b89ed4.3cd9d", | |
"type": "mqtt out", | |
"z": "9603e70c.db5a58", | |
"name": "", | |
"topic": "settings/lounge/heat", | |
"qos": "2", | |
"retain": "true", | |
"broker": "e4e72b98.749778", | |
"x": 1040, | |
"y": 380, | |
"wires": [] | |
}, | |
{ | |
"id": "d1962d.ea5499d", | |
"type": "mqtt in", | |
"z": "9603e70c.db5a58", | |
"name": "", | |
"topic": "settings/lounge/heat", | |
"qos": "2", | |
"broker": "e4e72b98.749778", | |
"x": 850, | |
"y": 500, | |
"wires": [ | |
[ | |
"50c8733c.87112c" | |
] | |
] | |
}, | |
{ | |
"id": "a4b5398.9d833c8", | |
"type": "function", | |
"z": "9603e70c.db5a58", | |
"name": "-5", | |
"func": "msg.payload = msg.payload - 5;\nreturn msg;", | |
"outputs": 1, | |
"noerr": 0, | |
"x": 780, | |
"y": 280, | |
"wires": [ | |
[ | |
"dda02db7.7376f" | |
] | |
] | |
}, | |
{ | |
"id": "2f67aebb.ea7942", | |
"type": "ui_button", | |
"z": "9603e70c.db5a58", | |
"name": "+temp", | |
"group": "3f0f8980.fdfed6", | |
"order": 3, | |
"width": "2", | |
"height": "2", | |
"passthru": false, | |
"label": "", | |
"color": "#a02020", | |
"bgcolor": "#404040", | |
"icon": "fa-chevron-up fa-3x", | |
"payload": "1", | |
"payloadType": "num", | |
"topic": "+", | |
"x": 570, | |
"y": 600, | |
"wires": [ | |
[ | |
"76ad15eb.5dedbc" | |
] | |
] | |
}, | |
{ | |
"id": "137dd63f.e46e7a", | |
"type": "ui_button", | |
"z": "9603e70c.db5a58", | |
"name": "-temp", | |
"group": "3f0f8980.fdfed6", | |
"order": 4, | |
"width": "2", | |
"height": "2", | |
"passthru": false, | |
"label": "", | |
"color": "#202080", | |
"bgcolor": "#404040", | |
"icon": "fa-chevron-down fa-3x", | |
"payload": "-1", | |
"payloadType": "num", | |
"topic": "-", | |
"x": 570, | |
"y": 640, | |
"wires": [ | |
[ | |
"76ad15eb.5dedbc" | |
] | |
] | |
}, | |
{ | |
"id": "ee29f968.d72258", | |
"type": "ui_ui_control", | |
"z": "9603e70c.db5a58", | |
"name": "ui change", | |
"x": 780, | |
"y": 580, | |
"wires": [ | |
[ | |
"aa1911c7.1db4d" | |
] | |
] | |
}, | |
{ | |
"id": "2edd6bbb.10cb94", | |
"type": "function", | |
"z": "9603e70c.db5a58", | |
"name": "global lounge-target", | |
"func": "msg.payload = global.get(\"lounge-target\");\nreturn msg;", | |
"outputs": 1, | |
"noerr": 0, | |
"x": 1100, | |
"y": 580, | |
"wires": [ | |
[ | |
"4730260e.c5dac8" | |
] | |
] | |
}, | |
{ | |
"id": "e5849995.be1af8", | |
"type": "ui_ui_control", | |
"z": "9603e70c.db5a58", | |
"name": "ui change", | |
"x": 120, | |
"y": 480, | |
"wires": [ | |
[ | |
"62310905.b59f28" | |
] | |
] | |
}, | |
{ | |
"id": "6a05c6e6.12a608", | |
"type": "function", | |
"z": "9603e70c.db5a58", | |
"name": "global lounge-ambient", | |
"func": "msg.payload = global.get(\"lounge-ambient\");\nmsg.topic = \"ambient_temperature\";\nreturn msg;", | |
"outputs": 1, | |
"noerr": 0, | |
"x": 560, | |
"y": 480, | |
"wires": [ | |
[ | |
"76ad15eb.5dedbc" | |
] | |
] | |
}, | |
{ | |
"id": "e820dcd0.9fc55", | |
"type": "mqtt in", | |
"z": "9603e70c.db5a58", | |
"name": "", | |
"topic": "settings/lounge/ambient/temperature", | |
"qos": "2", | |
"broker": "e4e72b98.749778", | |
"x": 230, | |
"y": 800, | |
"wires": [ | |
[ | |
"75c042f8.6c248c" | |
] | |
] | |
}, | |
{ | |
"id": "f5b59c21.79c62", | |
"type": "comment", | |
"z": "9603e70c.db5a58", | |
"name": "CHARTS & STATUS", | |
"info": "", | |
"x": 180, | |
"y": 740, | |
"wires": [] | |
}, | |
{ | |
"id": "9f488fd9.92e57", | |
"type": "ui_button", | |
"z": "9603e70c.db5a58", | |
"name": "Away", | |
"group": "3f0f8980.fdfed6", | |
"order": 5, | |
"width": "2", | |
"height": "2", | |
"passthru": false, | |
"label": "", | |
"color": "{{msg.colour}}", | |
"bgcolor": "#404040", | |
"icon": "fa-home fa-4x", | |
"payload": "toggle", | |
"payloadType": "str", | |
"topic": "away", | |
"x": 230, | |
"y": 700, | |
"wires": [ | |
[ | |
"e3b7020e.10d27" | |
] | |
] | |
}, | |
{ | |
"id": "ab8ad33.fd3213", | |
"type": "link out", | |
"z": "9603e70c.db5a58", | |
"name": "Away", | |
"links": [ | |
"c0f51c41.8af86" | |
], | |
"x": 415, | |
"y": 600, | |
"wires": [] | |
}, | |
{ | |
"id": "e3b7020e.10d27", | |
"type": "function", | |
"z": "9603e70c.db5a58", | |
"name": "Toggle", | |
"func": "away = global.get(\"away\") | false;\n\nswitch(msg.payload) {\n case true: \n away = true;\n break;\n case false:\n away = false;\n break;\n default:\n away = !away;\n break;\n }\n \n msg.payload = away;\n global.set(\"away\",away);\n\n if (msg.payload){\n node.status({fill:\"red\",shape:\"dot\",text:msg.payload});\n msg.colour = \"#2020a0\";\n }else{\n node.status({fill:\"grey\",shape:\"ring\",text:msg.payload});\n msg.colour = \"#808080\";\n }\n\nreturn msg;\n\n", | |
"outputs": 1, | |
"noerr": 0, | |
"x": 310, | |
"y": 620, | |
"wires": [ | |
[ | |
"9f488fd9.92e57", | |
"76ad15eb.5dedbc", | |
"ab8ad33.fd3213" | |
] | |
] | |
}, | |
{ | |
"id": "1d8b4763.260789", | |
"type": "inject", | |
"z": "9603e70c.db5a58", | |
"name": "startup", | |
"topic": "", | |
"payload": "false", | |
"payloadType": "bool", | |
"repeat": "", | |
"crontab": "", | |
"once": true, | |
"x": 160, | |
"y": 620, | |
"wires": [ | |
[ | |
"e3b7020e.10d27" | |
] | |
] | |
}, | |
{ | |
"id": "dfcdaea5.bb7e", | |
"type": "comment", | |
"z": "9603e70c.db5a58", | |
"name": "EXT TEMP", | |
"info": "", | |
"x": 150, | |
"y": 1080, | |
"wires": [] | |
}, | |
{ | |
"id": "ba69b2fb.a5914", | |
"type": "openweathermap in", | |
"z": "9603e70c.db5a58", | |
"name": "Reigate Weather", | |
"wtype": "current", | |
"lon": "", | |
"lat": "", | |
"city": "Reigate", | |
"country": "England", | |
"language": "en", | |
"x": 190, | |
"y": 1140, | |
"wires": [ | |
[ | |
"965f2e1b.d1f94", | |
"3c79083c.cb9ce8", | |
"5a63624a.b46e3c" | |
] | |
] | |
}, | |
{ | |
"id": "3c79083c.cb9ce8", | |
"type": "debug", | |
"z": "9603e70c.db5a58", | |
"name": "", | |
"active": false, | |
"console": "false", | |
"complete": "true", | |
"x": 450, | |
"y": 1180, | |
"wires": [] | |
}, | |
{ | |
"id": "965f2e1b.d1f94", | |
"type": "change", | |
"z": "9603e70c.db5a58", | |
"name": "Ext Temp", | |
"rules": [ | |
{ | |
"t": "move", | |
"p": "payload.tempc", | |
"pt": "msg", | |
"to": "payload", | |
"tot": "msg" | |
}, | |
{ | |
"t": "set", | |
"p": "topic", | |
"pt": "msg", | |
"to": "Ext Temp", | |
"tot": "str" | |
} | |
], | |
"action": "", | |
"property": "", | |
"from": "", | |
"to": "", | |
"reg": false, | |
"x": 640, | |
"y": 980, | |
"wires": [ | |
[ | |
"1bf96285.8fde1d" | |
] | |
] | |
}, | |
{ | |
"id": "a5b6d95b.182ce8", | |
"type": "ui_template", | |
"z": "9603e70c.db5a58", | |
"group": "299a229.5c740de", | |
"name": "Load CSS", | |
"order": 0, | |
"width": "0", | |
"height": "0", | |
"format": "<link rel=\"stylesheet\" href=\"/weather-icons/mycss/weather-icons.min.css\">\n<style>\n .nr-dashboard-theme-dark ui-card-panel {\n \tbackground-color: #404040;\n \toutline: 0;\n \t }\n</style>\n", | |
"storeOutMessages": false, | |
"fwdInMessages": false, | |
"templateScope": "local", | |
"x": 470, | |
"y": 1100, | |
"wires": [ | |
[] | |
] | |
}, | |
{ | |
"id": "62310905.b59f28", | |
"type": "delay", | |
"z": "9603e70c.db5a58", | |
"name": "", | |
"pauseType": "delay", | |
"timeout": "1", | |
"timeoutUnits": "seconds", | |
"rate": "1", | |
"nbRateUnits": "1", | |
"rateUnits": "second", | |
"randomFirst": "1", | |
"randomLast": "5", | |
"randomUnits": "seconds", | |
"drop": false, | |
"x": 280, | |
"y": 480, | |
"wires": [ | |
[ | |
"6a05c6e6.12a608" | |
] | |
] | |
}, | |
{ | |
"id": "aa1911c7.1db4d", | |
"type": "delay", | |
"z": "9603e70c.db5a58", | |
"name": "", | |
"pauseType": "delay", | |
"timeout": "1", | |
"timeoutUnits": "seconds", | |
"rate": "1", | |
"nbRateUnits": "1", | |
"rateUnits": "second", | |
"randomFirst": "1", | |
"randomLast": "5", | |
"randomUnits": "seconds", | |
"drop": false, | |
"x": 920, | |
"y": 580, | |
"wires": [ | |
[ | |
"2edd6bbb.10cb94" | |
] | |
] | |
}, | |
{ | |
"id": "5a63624a.b46e3c", | |
"type": "ui_template", | |
"z": "9603e70c.db5a58", | |
"group": "3f0f8980.fdfed6", | |
"name": "Weather Details", | |
"order": 8, | |
"width": "4", | |
"height": "2", | |
"format": "<div style=\"display: flex;height: 100%;justify-content: center;align-items: center;\n color:#808080; background-color: #202020;\">\n <p><strong>OUTSIDE</strong><br>\n {{msg.payload.tempc}}°C<br>\n {{msg.payload.weather}}<br>\n </p>\n <p><i class=\"fa-4x wi {{'wi-owm-'+msg.payload.icon}}\"></i></p>\n</div>", | |
"storeOutMessages": false, | |
"fwdInMessages": true, | |
"templateScope": "local", | |
"x": 480, | |
"y": 1140, | |
"wires": [ | |
[] | |
] | |
}, | |
{ | |
"id": "f3c1fa89.8608d8", | |
"type": "alexa-home", | |
"z": "9603e70c.db5a58", | |
"conf": "8134e8ac.4c7ef8", | |
"device": "12674", | |
"acknoledge": true, | |
"name": "Temperature", | |
"topic": "", | |
"x": 1070, | |
"y": 780, | |
"wires": [ | |
[ | |
"4730260e.c5dac8" | |
] | |
] | |
}, | |
{ | |
"id": "e4e72b98.749778", | |
"type": "mqtt-broker", | |
"z": "", | |
"broker": "localhost", | |
"port": "1883", | |
"clientid": "", | |
"usetls": false, | |
"compatmode": true, | |
"keepalive": "60", | |
"cleansession": true, | |
"willTopic": "", | |
"willQos": "0", | |
"willPayload": "", | |
"birthTopic": "", | |
"birthQos": "0", | |
"birthPayload": "" | |
}, | |
{ | |
"id": "bc8f9e23.09b6", | |
"type": "ui_group", | |
"z": "", | |
"name": "Lounge", | |
"tab": "36ea3f92.a414c", | |
"order": 2, | |
"disp": true, | |
"width": "15" | |
}, | |
{ | |
"id": "151be4a4.2a40bb", | |
"type": "ui_group", | |
"z": "", | |
"name": "Stat", | |
"tab": "5955d47b.d81d1c", | |
"order": 1, | |
"disp": false, | |
"width": "6" | |
}, | |
{ | |
"id": "3f0f8980.fdfed6", | |
"type": "ui_group", | |
"z": "", | |
"name": "Temperature", | |
"tab": "e5bc8a1a.f5e098", | |
"order": 1, | |
"disp": false, | |
"width": "8" | |
}, | |
{ | |
"id": "299a229.5c740de", | |
"type": "ui_group", | |
"z": "", | |
"name": "Test", | |
"tab": "7e7a26e0.2e41c8", | |
"disp": true, | |
"width": "6" | |
}, | |
{ | |
"id": "8134e8ac.4c7ef8", | |
"type": "alexa-home-conf", | |
"z": "", | |
"username": "simon.maddox" | |
}, | |
{ | |
"id": "36ea3f92.a414c", | |
"type": "ui_tab", | |
"z": "", | |
"name": "Charts", | |
"icon": "dashboard", | |
"order": 4 | |
}, | |
{ | |
"id": "5955d47b.d81d1c", | |
"type": "ui_tab", | |
"z": "", | |
"name": "Settings", | |
"icon": "dashboard", | |
"order": 2 | |
}, | |
{ | |
"id": "e5bc8a1a.f5e098", | |
"type": "ui_tab", | |
"z": "", | |
"name": "Home", | |
"icon": "dashboard", | |
"order": 1 | |
}, | |
{ | |
"id": "7e7a26e0.2e41c8", | |
"type": "ui_tab", | |
"z": "", | |
"name": "Test", | |
"icon": "dashboard" | |
} | |
] |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment