Difference between revisions of "Heat Network Calculator v2"

From Open Source Controls Wiki
Jump to navigation Jump to search
Line 10: Line 10:


[[File:Hncalc.png]]
[[File:Hncalc.png]]
https://heatweb.mypinata.cloud/ipfs/QmRxu1RyQL34nRRuBYkwj45HzFwrniAhUEiri6au2yYgaQ


<pre>
<pre>
[{"id":"4444b4d.e5bf44c","type":"http in","z":"d24faa61.312688","name":"","url":"/ui/hncalc","method":"get","upload":false,"swaggerDoc":"","x":250,"y":420,"wires":[["293b34f8.1b1f0c"]]},{"id":"293b34f8.1b1f0c","type":"template","z":"d24faa61.312688","name":"editor","field":"payload.body","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"\n<script src=\"https://bossanova.uk/jspreadsheet/v4/jexcel.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v4/jexcel.css\" type=\"text/css\" />\n \n<script src=\"https://jsuites.net/v4/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v4/jsuites.css\" type=\"text/css\" />\n\n \n<p>This is a calculator for sizing and equipment selection on heat networks.</p>\n<p>Right click or press <i>Enter</i> in a cell to add new rows.</p>\n<br>\n\n<style>\n    .jexcel_content {\n        font-size: 15px;\n    }\n    \n    .jexcel {\n        white-space: normal;\n    }\n</style>\n\n \n<div id=\"spreadsheet1\"></div>\n<br>\n<table cellpadding=\"6\">\n  <tr><td>Central Heating Diversity:</td><td><input type=\"text\" id=\"chdiv\" name=\"chdiv\" value=\"70\" placeholder=\"% Peak\"> % </td></tr>\n  \n <tr><td>Peak Flow Temperature:</td><td><input type=\"text\" id=\"tHPeak\" name=\"tHPeak\" value=\"75\" placeholder=\"Input C\"> C</td></tr>\n \n</table>\n\n\n<br>\nHot Water Hourly Profile (default values from EST study):<br>\n<div id=\"profilesheet\"></div>\n<br>\n<input type=\"button\" onClick=\"runprofile()\" value=\"Level to 100%\"> <input type=\"button\" onClick=\"resetprofile()\" value=\"Reset to EST volumes\">\n<br>\n<br>\n<input type=\"button\" onClick=\"calc(1)\" value=\"Run Calculation\">\n<br><br>\n<div id=\"spreadsheet1results\"></div>\n\n<br>\n<div id=\"spreadsheet3\"></div>\n<br><br>\n<p>Heat input is initially set to the minimum, resulting in maximum storage sizes. Increase the heat input to reduce storage sizes.</p>\n<p>Network DHW Priority requires networked HIUs that can respond to signals from planroom to limit central heating loads for short periods to enable load prioritisation, and thereby limit the instantaneous demand on the system as required.  This allows peak heat input to be safely sized to average hourly loads and may avoid the need for storage.</p>\n<table cellpadding=\"6\">\n    <tr><td>Heat Input (kW):</td><td><input type=\"text\" id=\"boilerkw\" name=\"boilerkw\" value=\"\" placeholder=\"Input kW\"></td></tr>\n \n <tr><td>Network DHW Priority:</td><td><input type=\"checkbox\" id=\"dhwPriority\" name=\"dhwPriority\"></td></tr>\n</table>\n<br><input type=\"button\" onClick=\"runstore()\" value=\"Run Storage Calcs\">\n<br><br>\n<div id=\"spreadsheet2results\"></div>\n<br><br>\n<table>\n    <tr>\n        <td><div id=\"spreadsheet2\"></div></td>\n        <td width=\"100\">&nbsp;</td>\n        <td valign=\"top\"></td>\n    </tr>\n</table>\n\n<br><br>\n\n\n<br><br>\n<div id=\"spreadsheet\"></div>\n\n \n<br><br>\n\n<script>\n\nvar calcs = {};\n\n    function calc(n) {\n        \n        \n        console.log(myspreadsheet1.getJson());\n        \n        calcs[\"totalCH\"] = 0;\n        calcs[\"totalDHW\"] = 0;\n        calcs[\"kwhDHW\"] = 0;\n        calcs[\"weighCH\"] = 0;\n        calcs[\"vHCH\"] = 0;\n        calcs[\"vHDHW\"] = 0;\n        \n        calcs[\"tHPeak\"] = parseFloat(document.getElementById(\"tHPeak\").value || 75);\n        calcs[\"divCH\"] = parseFloat(document.getElementById(\"chdiv\").value || 100) / 100;\n        \n        var dat = myspreadsheet1.getJson();\n        for (var line in dat) {\n            \n            if (dat[line][\"enabled\"]==true) {\n                calcs[\"totalCH\"]  = calcs[\"totalCH\"] + (calcs[\"divCH\"] * parseFloat(dat[line][\"kwCH\"]) * parseFloat(dat[line][\"qty\"]));\n                calcs[\"totalDHW\"]  = calcs[\"totalDHW\"] + (parseFloat(dat[line][\"kwDHW\"]) * parseFloat(dat[line][\"qty\"]));\n                \n                var kwhdhw =  parseFloat(dat[line][\"qty\"]) * 4200 * parseFloat(dat[line][\"deltatDHW\"])  * (parseFloat(dat[line][\"vPropDHW\"]) +  (parseFloat(dat[line][\"vPersonDHW\"]) * parseFloat(dat[line][\"occupants\"])));\n                kwhdhw = kwhdhw / (1000*3600);\n                \n                calcs[\"kwhDHW\"]  = calcs[\"kwhDHW\"] + kwhdhw;\n                \n                var myvCH = (parseFloat(dat[line][\"qty\"]) * 24 * 3600 * calcs[\"divCH\"] * parseFloat(dat[line][\"kwCH\"]) / (4.200 * (calcs[\"tHPeak\"] - parseFloat(dat[line][\"tHoCH\"]))) );\n                calcs[\"vHCH\"] = calcs[\"vHCH\"] + myvCH;\n                \n                var myvDHW =  (3600 * kwhdhw / (4.200 * (calcs[\"tHPeak\"] - parseFloat(dat[line][\"tHoDHW\"]))) );\n                \n                calcs[\"vHDHW\"] = calcs[\"vHDHW\"] + myvDHW;\n                calcs[\"weighCH\"]  = calcs[\"weighCH\"] + (parseFloat(dat[line][\"tHoCH\"]) *  myvCH)  + (parseFloat(dat[line][\"tHoDHW\"]) *  myvDHW);\n              \n                \n                \n            }\n        }\n        calcs[\"ds439number\"] = Math.ceil(calcs[\"totalDHW\"] / 37.5);\n        \n        calcs[\"ds439load\"]  = Math.ceil((1.19* calcs[\"ds439number\"]) + (18.8* Math.pow(calcs[\"ds439number\"],0.5)) + 17.6);\n        \n        calcs[\"kwAvgDHW\"] = Math.ceil(10*(calcs[\"kwhDHW\"] / 24)) /10;\n        \n        calcs[\"kwAvg\"] = Math.ceil(10*(calcs[\"kwAvgDHW\"] + calcs[\"totalCH\"])) /10;\n        \n        //if (calcs[\"totalCH\"]) {\n            calcs[\"tHoPeak\"] =  Math.ceil(10*(  calcs[\"weighCH\"] / (calcs[\"vHCH\"] + calcs[\"vHDHW\"])  )) /10;\n        //}\n        \n        var result1 = \"Total CH: \" + (Math.ceil(10*(calcs[\"totalCH\"]))/10) + \"kW\";\n        \n        result1 += \"<br>Total DHW: \" +  (Math.ceil(10*(calcs[\"kwhDHW\"]))/10) + \"kWh\";\n        result1 += \"<br>Averaged DHW: \" + calcs[\"kwAvgDHW\"] + \"kW\";\n        \n        result1 += \"<br>Diversified DHW: \" + calcs[\"ds439load\"] + \"kW (\" + calcs[\"ds439number\"] +\" equivalent 37.5kW properties)\";\n        result1 += \"<br>Minimum Averaged Input: \" + calcs[\"kwAvg\"] + \"kW\";\n        \n        \n        result1 += \"<br>Daily Volume CH: \" +  (Math.ceil(10*(calcs[\"vHCH\"]))/10) + \" litres\";\n        result1 += \"<br>Daily Volume DHW: \" +  (Math.ceil(10*(calcs[\"vHDHW\"]))/10) + \" litres\";\n        \n        result1 += \"<br>Volume Weighed Average Return Temperature: \" + calcs[\"tHoPeak\"] + \"C\";\n        \n        document.getElementById(\"spreadsheet1results\").innerHTML = result1;\n        \n        var holdp = document.getElementById(\"dhwPriority\").checked;\n        ssets = [];\n        runstorearray();\n        \n        document.getElementById(\"dhwPriority\").checked = holdp;\n        document.getElementById(\"boilerkw\").value =  calcs[\"kwAvg\"];\n        \n        runstore();\n    }\n    \n    \n    function runstore() {\n        \n        \n        //var estVolumes = [1.8,1.2,1,0.8,1.3,1.7,4.5,8.7,7.6,6.1,5.4,4.8,4.2,3.6,3.1,3.2,3.7,5.3,7.7,7.3,6.1,4.9,3.7,2.4];\n        var estVolumes = dhwprofilesheet.getRowData(0);\n        \n        calcs[\"tHPeak\"] = parseFloat(document.getElementById(\"tHPeak\").value || 75);\n        \n        columns2 = [\n            {\n                type: 'numeric',\n                name: 'hour',\n                title:'Hour',\n                width:90,\n                decimal:'.'\n            },\n            {\n                type: 'numeric',\n                name: 'percent',\n                title:'EST Volume %',\n                width:80,\n                decimal:'.'\n            },\n            {\n                type: 'numeric',\n                name: 'dhwload',\n                title:'DHW Energy kWh',\n                width:80,\n                decimal:'.'\n            },\n            {\n                type: 'numeric',\n                name: 'dischargetime',\n                title:'Discharge Time h',\n                width:80,\n                decimal:'.'\n            },\n            {\n                type: 'numeric',\n                name: 'dischargestorage',\n                title:'Discharge Storage kWh',\n                width:80,\n                decimal:'.'\n            },\n            {\n                type: 'numeric',\n                name: 'energy',\n                title:'Balance kWh',\n                width:80,\n                decimal:'.'\n            },\n            {\n                type: 'numeric',\n                name: 'dischargebalance',\n                title:'Discharge Balance kWh',\n                width:80,\n                decimal:'.'\n            }\n        ];\n        \n        \n        var runload = 0;\n        var runinput = 0;\n        var mine = 999999;\n        var maxe = -999999;\n        var maxh = -999999;\n        var hr = 0;\n        volumecalcs = [];\n        var lastbalance = 0;\n        \n        if (parseFloat(document.getElementById(\"boilerkw\").value)  < calcs[\"kwAvg\"]) {\n            \n            alert(\"Not enough heat input, adjusting to minimum.\");\n            document.getElementById(\"boilerkw\").value = calcs[\"kwAvg\"];\n        }\n        \n        calcs[\"boilerkw\"] =parseFloat(document.getElementById(\"boilerkw\").value);\n        \n        var bin = calcs[\"boilerkw\"] - calcs[\"totalCH\"];\n        console.log(\"bin=\"+bin);\n        \n        calcs[\"dhwPriority\"] = document.getElementById(\"dhwPriority\").checked;\n        console.log(\"dhwPriority=\"+calcs[\"dhwPriority\"]);\n        \n        \n        \n        for (var h in estVolumes) {\n            \n            hr++;\n            \n            var houroff = hr + 5;  // start with first hour where output exceeds min input - where store would be full.\n            if (houroff>23) { houroff = houroff - 24; }\n            var ob = {};\n            ob[\"hour\"] = houroff; //hr;\n            ob[\"percent\"] = estVolumes[houroff];\n            ob[\"dhwload\"] = parseInt(estVolumes[houroff] * calcs[\"kwhDHW\"]) / 100;\n            \n            \n            ob[\"dischargetime\"] = Math.ceil(1000*(ob[\"dhwload\"] / calcs[\"ds439load\"]))/1000;\n            \n            if (calcs[\"dhwPriority\"]==true) {\n                \n                var dhwneeded = calcs[\"ds439load\"] - calcs[\"totalCH\"];\n                \n                ob[\"dischargestorage\"] = Math.ceil(10*(ob[\"dhwload\"] - (ob[\"dischargetime\"] * calcs[\"boilerkw\"]))) / 10;\n                \n            } else {\n                \n                ob[\"dischargestorage\"] = Math.ceil(10*(ob[\"dhwload\"] - (ob[\"dischargetime\"] * bin))) / 10;\n            }\n            \n            \n            if (ob[\"dischargestorage\"] < 0) { ob[\"dischargestorage\"] =0; }\n            \n            \n            ob[\"dischargebalance\"] = Math.ceil(10*(lastbalance - ob[\"dischargestorage\"]))/10;\n            \n            runload = runload + ob[\"dhwload\"];\n            runinput = runinput + bin;\n            \n            ob[\"energy\"] = parseInt(100*(runinput - runload))/100;\n            \n            if (ob[\"energy\"]>0) { // removed input beyond store capacity\n                \n                runinput = runinput - ob[\"energy\"];  \n                ob[\"energy\"] = 0; \n                \n            }\n            \n            if (ob[\"energy\"] < mine) { mine = ob[\"energy\"]; }\n            if (ob[\"energy\"] > maxe) { maxe = ob[\"energy\"]; }\n            //if (ob[\"dischargestorage\"] > maxh) { maxh = ob[\"dischargestorage\"]; }\n            \n            if ((0-ob[\"dischargebalance\"]) > maxh) { maxh = 0-ob[\"dischargebalance\"]; }\n            \n            lastbalance = ob[\"energy\"];\n            \n            volumecalcs.push(ob);\n            \n        }\n        calcs[\"khwStorage\"] = Math.ceil(Math.max(maxe - mine, maxh));\n        \n        \n        calcs[\"litreStorage\"] = Math.ceil(calcs[\"khwStorage\"] * 3600000 / (4200 * (calcs[\"tHPeak\"] - calcs[\"tHoPeak\"])));\n        \n        \n        document.getElementById('spreadsheet2').innerHTML=\"\";\n        var myspreadsheet2 = jspreadsheet(document.getElementById('spreadsheet2'), {\n            data: volumecalcs,\n            columns: columns2\n        });\n        \n        \n        var result2 = \"Minimum Averaged Storage: \" + calcs[\"khwStorage\"] + \"kWh\";\n        result2 += \" = \" + calcs[\"litreStorage\"] + \" litres\";\n        \n        document.getElementById(\"spreadsheet2results\").innerHTML = result2;\n        \n    }\n    \n    \n    var ssets = [];\n    \n    function runstorearray() {\n        \n        \n        \n        brun = [calcs[\"kwAvg\"]];\n        var b30 = 30* Math.ceil(calcs[\"kwAvg\"] / 30);\n        brun.push( b30 );\n        \n        if(calcs[\"kwAvg\"]>70) { brun.push( b30 + 30 ); }\n        if(calcs[\"kwAvg\"]>100) { brun.push( b30 + 60 ); }\n        \n        brun.push( calcs[\"ds439load\"] + calcs[\"totalCH\"] );\n        \n        for (var k in brun) {\n            \n            var ob2 =  {\"boilerkw\":brun[k] };\n            document.getElementById(\"boilerkw\").value = brun[k];\n            document.getElementById(\"dhwPriority\").checked = false;\n            runstore();\n            ob2[\"vStore\"] = calcs[\"litreStorage\"];\n            \n            document.getElementById(\"boilerkw\").value = brun[k];\n            document.getElementById(\"dhwPriority\").checked = true;\n            runstore();\n            ob2[\"vStorePriority\"] = calcs[\"litreStorage\"];\n            \n            ssets.push( ob2 )\n        }\n        \n        document.getElementById('spreadsheet3').innerHTML=\"\";\n        var myspreadsheet3 = jspreadsheet(document.getElementById('spreadsheet3'), {\n            data: ssets,\n            columns: [\n            {\n                type: 'numeric',\n                name: 'boilerkw',\n                title:'kW Input',\n                width:150,\n                decimal:'.'\n            },\n            {\n                type: 'numeric',\n                name: 'vStore',\n                title:'Storage Volume litres',\n                width:150,\n                decimal:'.'\n            },\n            {\n                type: 'numeric',\n                name: 'vStorePriority',\n                title:'Storage Volume (With Priority) litres',\n                width:150,\n                decimal:'.'\n            }],\n            updateTable:function(instance, cell, col, row, val, label, cellName) {\n              \n                if (row==2) {\n                        cell.style.backgroundColor = '#aaffaa';\n                    \n                }\n            },\n            columnSorting:false\n        });\n        \n    }\n    \n    \n    \n    function but() {\n        \n        \n        console.log(myspreadsheet.getJson());\n        console.log(myspreadsheet.getConfig());\n        \n    }\n    function fullscreen(mode) {\n    \n        myspreadsheet.fullscreen(mode);\n        \n    }\n\n\n\nvar columns1 = [\n        {\n            type: 'text',\n            name: 'propertyType',\n            title:'Property Type',\n            width:90\n        },\n        {\n            type: 'numeric',\n            name: 'bedrooms',\n            title:'Bedrooms',\n            width:80,\n            decimal:'.'\n        },\n        {\n            type: 'numeric',\n            name: 'occupants',\n            title:'Occupants',\n            width:80,\n            decimal:'.'\n        },\n        {\n            type: 'dropdown',\n            name: 'connectionTypeCH',\n            title:'Heating Connection',\n            width:120,\n            source:[\n                \"direct\",\n                \"indirect\",\n                \"none\",\n                // (...)\n              ]\n        },\n        {\n            type: 'dropdown',\n            name: 'emitterTypeCH',\n            title:'Heating Type',\n            width:120,\n            source:[\n                \"radiators\",\n                \"underfloor\",\n                \"none\",\n                // (...)\n              ]\n        },\n        {\n            type: 'numeric',\n            name: 'kwCH',\n            title:'Heating Load kW',\n            width:80,\n            decimal:'.'\n        },\n        {\n            type: 'numeric',\n            name: 'tHoCH',\n            title:'Primary CH Return Temp C',\n            width:80,\n            decimal:'.'\n        },\n        {\n            type: 'dropdown',\n            name: 'connectionTypeDHW',\n            title:'DHW Type',\n            width:120,\n            source:[\n                \"instantaneous\",\n                \"phe+coil+store\",\n                \"phe+store\",\n                \"coil+store\",\n                \"none\",\n                // (...)\n              ]\n        },\n        {\n            type: 'numeric',\n            name: 'kwDHW',\n            title:'DHW Peak kW',\n            width:80,\n            decimal:'.'\n        },\n        {\n            type: 'numeric',\n            name: 'vPropDHW',\n            title:'DHW Litres (property)',\n            width:80,\n            decimal:'.'\n        },\n        {\n            type: 'numeric',\n            name: 'vPersonDHW',\n            title:'DHW Litres (person)',\n            width:80,\n            decimal:'.'\n        },\n        {\n            type: 'numeric',\n            name: 'deltatDHW',\n            title:'DHW Temp Rise',\n            width:80,\n            decimal:'.'\n        },\n        {\n            type: 'numeric',\n            name: 'tHoDHW',\n            title:'Primary DHW Return Temp C',\n            width:80,\n            decimal:'.'\n        },\n        {\n            type: 'image',\n            name: 'graphic',\n            title:'Photo',\n            width:120\n        },\n        {\n            type: 'checkbox',\n            name: 'enabled',\n            title:'Enable',\n            width:80\n        },\n        {\n            type: 'numeric',\n            name: 'qty',\n            title:'Quantity',\n            width:80,\n            decimal:'.'\n        }\n    ];\n\nvar icon = \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAAAgY0hSTQAAeiYAAICEAAD6AAAAgOgAAHUwAADqYAAAOpgAABdwnLpRPAAAAOFQTFRFAAAAAIlARERARERAAYhARERARERABoNAiaRmh6FlAIlAX5h6Xpd6AYhAXpZ5rK2srK2tX5l7XpV4qqqq0NHR19jYsLGx4uPj4+Tk2NnZ6uvrX5h7BIVAqqurXZR3AodAtYVY6erq29zcz9DQgpyP09TUqXpSqHpS4eLi4OHhg56RoaKjnXBLnHBLmG5Ko6SlkGZE9cdoj2ZEjGREi2REimFAhl9A3N3dg109gFw9f1w91tfX1dbWflA4HHY8G3Y8GXk8gVE4iaRmkbRfjrBeh6FlaGlpa2tkampjZ2ho////c2A5CgAAAAp0Uk5TAGsBBGwJBXGho317A2MAAAABYktHREoeDLXGAAAACXBIWXMAAABIAAAASABGyWs+AAAAu0lEQVQY002O1xKCMBAAE7HGriAxVrChorGCih2U+P8/5BHGGfcl2Z27TBCSYEIw+gOTfKGI/71UrlTlTCL2Wl3VGlFJKD/XaRNKMpX+OWNQWu2MEu1Lh6J1uj2MSL6s6v0Iwxx0qgSh4UhnY2BiTU1tBm/OYd627cVyahmUQ+CwvlpvtjvLYEwGSul+7ziODhcZXNc9HE/HA5wuh59yzztfrrf7wwO4grJP3w+CwI955dA7DIUQYYwQny8RbRx9zgBJtwAAACV0RVh0ZGF0ZTpjcmVhdGUAMjAxOC0wNS0wNlQxNzo1NDoyNS0wNTowMMd7TXIAAAAldEVYdGRhdGU6bW9kaWZ5ADIwMTgtMDUtMDZUMTc6NTQ6MjUtMDU6MDC2JvXOAAAAAElFTkSuQmCC\";\n\nvar data1 = [\n    ['Flat', 1, 2, 'indirect', 'radiators', 3, 35, 'instantaneous', 37.5,40,28,35, 25, icon ,true, 130],\n    ['Duplex', 2, 4, 'indirect', 'underfloor', 5.5, 35, 'instantaneous', 45,40,28,35, 25, icon ,true, 20],\n];\n\nvar myspreadsheet1 = jspreadsheet(document.getElementById('spreadsheet1'), {\n    data:data1,\n    columns: columns1\n});\n\nvar rprof;\nvar dhwprofilesheet = jspreadsheet(document.getElementById('profilesheet'), {\n            data: [[1.8,1.2,1,0.8,1.3,1.7,4.5,8.7,7.6,6.1,5.4,4.8,4.2,3.6,3.1,3.2,3.7,5.3,7.7,7.3,6.1,4.9,3.7,2.4]],\n            columns: [\n            {\n                title:'0'\n            },\n            {\n                title:'1'\n            },\n            {\n                title:'2'\n            },\n            {\n                title:'3'\n            },\n            {\n                title:'4'\n            },\n            {\n                title:'5'\n            },\n            {\n                title:'6'\n            },\n            {\n                title:'7'\n            },\n            {\n                title:'8'\n            },\n            {\n                title:'9'\n            },\n            {\n                title:'10'\n            },\n            {\n                title:'11'\n            },\n            {\n                title:'12'\n            },\n            {\n                title:'13'\n            },\n            {\n                title:'14'\n            },\n            {\n                title:'15'\n            },\n            {\n                title:'16'\n            },\n            {\n                title:'17'\n            },\n            {\n                title:'18'\n            },\n            {\n                title:'19'\n            },\n            {\n                title:'20'\n            },\n            {\n                title:'21'\n            },\n            {\n                title:'22'\n            },\n            {\n                title:'23'\n            }],\n            columnSorting:false,\n            allowInsertColumn: false,\n            allowInsertRow: false,\n            allowDeleteColumn: false,\n            allowDeleteRow: false\n        });\n\nfunction runprofile() {\n    \n    if (dhwprofilesheet) {\n        \n        var prof = dhwprofilesheet.getRowData(0);\n        var tot = 0;\n        for (var h in prof) {\n            tot = tot + parseFloat(prof[h]);\n        }\n        if (tot>0) {\n            for (var h in prof) {\n                prof[h] = parseInt(100 * (100/tot) * parseFloat(prof[h])) / 100;\n            }\n        }\n        dhwprofilesheet.setRowData(0, prof);\n    }\n}\n\nfunction resetprofile() {\n    \n    if (dhwprofilesheet) {\n        \n        dhwprofilesheet.setRowData(0, [1.8,1.2,1,0.8,1.3,1.7,4.5,8.7,7.6,6.1,5.4,4.8,4.2,3.6,3.1,3.2,3.7,5.3,7.7,7.3,6.1,4.9,3.7,2.4]);\n    }\n}\n</script>","output":"str","x":430,"y":420,"wires":[["59fb20e9.ae98a"]],"info":"https://bossanova.uk/jspreadsheet/v4/"},{"id":"59fb20e9.ae98a","type":"template","z":"d24faa61.312688","name":"","field":"heads","fieldType":"msg","format":"handlebars","syntax":"plain","template":"<!doctype html>\n<html lang=\"en\">\n  <head>\n    <!-- Required meta tags -->\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1, shrink-to-fit=no\">\n\n    <!-- Bootstrap CSS -->\n    <link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/css/bootstrap.min.css\" integrity=\"sha384-TX8t27EcRE3e/ihU7zmQxVncDAy5uIKz4rEkgIXeMed4M0jlfIDPvg6uqKI2xXr2\" crossorigin=\"anonymous\">\n\n  \n    <script src=\"https://kit.fontawesome.com/c6b21b7a8f.js\" crossorigin=\"anonymous\"></script>\n\n \n    <title>Thermal Integration Heatweb Node</title>\n\n\n  <script src=\"https://code.jquery.com/jquery-3.5.1.js\"></script>\n  <script src=\"https://cdn.datatables.net/1.10.22/js/jquery.dataTables.min.js\" type=\"text/javascript\"></script>\n  <link rel=\"stylesheet\" href=\"https://cdn.datatables.net/1.10.22/css/jquery.dataTables.min.css\">\n\n\n  \n    <style>\n    \n    body, textarea, input, select {\n    font-size: 18px;\n}\n\nsection  {\n    border-bottom: 1px solid rgb(255, 255, 255);\n    padding: 20px 50px;\n    /* height: 100vh; */\n    scroll-snap-align: start;\n    /* text-align: center; */\n    position: relative;\n    background: #ffffff;\n    font-size: 18px;\n    }\n\nsection .table {\n            font-size: 16px;\n        }\n\ntable.dataTable.compact tbody td {\n    \n    padding: 2px;\n}  \n\nsection .jstree-node {\n    font-size: 16px;\n}\n\n.nowrap {\n    white-space: nowrap;\n}\n\n@media (max-width: 1480px) {\n\n    section {\n        \n        padding: 20px 20px;\n        \n        }\n\n}\n\n@media (max-width:480px) {\n\n    section {\n        \n        padding: 10px 8px;\n        \n        }\n\n}\n\n    \n        .maxsiz {\n\n        width: 100%; \n        height: 100vh;\n\n        }\n\n        .maxsiz2 {\n\n        width: 100%; \n        height: calc(100vh - 70px);\n\n        }\n\n        .dot {\n            height: 13px;\n            width: 13px;\n            background-color: rgb(52, 58, 52);\n            border-radius: 20%;\n            border: 2px;\n            border-color: rgb(5, 5, 20);\n            display: inline-block;\n        }\n\n        .thindot {\n            height: 13px;\n            width: 6px;\n            background-color: rgb(52, 58, 52);\n            border-radius: 20%;\n            border: 2px;\n            border-color: rgb(5, 5, 20);\n            display: inline-block;\n        }\n\n        .dotspacer {\n            height: 13px;\n            width: 2px;\n            display: inline-block;\n        }\n\n        .navReadings  {\n            /* border-bottom: 1px solid rgb(32, 28, 28); */\n            padding: 20px 50px 0px 50px;\n            /* background: #6d6d70; */\n            font-size: 80%;  \n            /* font-weight: bold; */\n            text-align: left;\n            color: #212529;  \n        }\n\n        .navReadings a {\n            color: #212529;        \n        }\n\n        \n\n          \n    </style>","output":"str","x":600,"y":420,"wires":[["2c0cb6ba.6d28ea"]]},{"id":"2c0cb6ba.6d28ea","type":"function","z":"d24faa61.312688","name":"","func":"msg.payload = msg.heads + \"<section>\" +  msg.payload.body + \"</section></body></html>\"\n\nreturn msg;","outputs":1,"noerr":0,"x":790,"y":420,"wires":[["dda7c150.c3342"]]},{"id":"dda7c150.c3342","type":"http response","z":"d24faa61.312688","name":"","statusCode":"","headers":{},"x":990,"y":420,"wires":[]}]
[{"id":"4444b4d.e5bf44c","type":"http in","z":"d24faa61.312688","name":"","url":"/ui/hncalc","method":"get","upload":false,"swaggerDoc":"","x":250,"y":420,"wires":[["293b34f8.1b1f0c"]]},{"id":"293b34f8.1b1f0c","type":"template","z":"d24faa61.312688","name":"editor","field":"payload.body","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"\n<script src=\"https://bossanova.uk/jspreadsheet/v4/jexcel.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v4/jexcel.css\" type=\"text/css\" />\n \n<script src=\"https://jsuites.net/v4/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v4/jsuites.css\" type=\"text/css\" />\n\n \n<p>This is a calculator for sizing and equipment selection on heat networks.</p>\n<p>Right click or press <i>Enter</i> in a cell to add new rows.</p>\n<br>\n\n<style>\n    .jexcel_content {\n        font-size: 15px;\n    }\n    \n    .jexcel {\n        white-space: normal;\n    }\n</style>\n\n \n<div id=\"spreadsheet1\"></div>\n<br>\n<table cellpadding=\"6\">\n  <tr><td>Central Heating Diversity:</td><td><input type=\"text\" id=\"chdiv\" name=\"chdiv\" value=\"70\" placeholder=\"% Peak\"> % </td></tr>\n  \n <tr><td>Peak Flow Temperature:</td><td><input type=\"text\" id=\"tHPeak\" name=\"tHPeak\" value=\"75\" placeholder=\"Input C\"> C</td></tr>\n \n</table>\n\n\n<br>\nHot Water Hourly Profile (default values from EST study):<br>\n<div id=\"profilesheet\"></div>\n<br>\n<input type=\"button\" onClick=\"runprofile()\" value=\"Level to 100%\"> <input type=\"button\" onClick=\"resetprofile()\" value=\"Reset to EST volumes\">\n<br>\n<br>\n<input type=\"button\" onClick=\"calc(1)\" value=\"Run Calculation\">\n<br><br>\n<div id=\"spreadsheet1results\"></div>\n\n<br>\n<div id=\"spreadsheet3\"></div>\n<br><br>\n<p>Heat input is initially set to the minimum, resulting in maximum storage sizes. Increase the heat input to reduce storage sizes.</p>\n<p>Network DHW Priority requires networked HIUs that can respond to signals from planroom to limit central heating loads for short periods to enable load prioritisation, and thereby limit the instantaneous demand on the system as required.  This allows peak heat input to be safely sized to average hourly loads and may avoid the need for storage.</p>\n<table cellpadding=\"6\">\n    <tr><td>Heat Input (kW):</td><td><input type=\"text\" id=\"boilerkw\" name=\"boilerkw\" value=\"\" placeholder=\"Input kW\"></td></tr>\n \n <tr><td>Network DHW Priority:</td><td><input type=\"checkbox\" id=\"dhwPriority\" name=\"dhwPriority\"></td></tr>\n</table>\n<br><input type=\"button\" onClick=\"runstore()\" value=\"Run Storage Calcs\">\n<br><br>\n<div id=\"spreadsheet2results\"></div>\n<br><br>\n<table>\n    <tr>\n        <td><div id=\"spreadsheet2\"></div></td>\n        <td width=\"100\">&nbsp;</td>\n        <td valign=\"top\"></td>\n    </tr>\n</table>\n\n<br><br>\n\n\n<br><br>\n<div id=\"spreadsheet\"></div>\n\n \n<br><br>\n\n<script>\n\nvar calcs = {};\n\n    function calc(n) {\n        \n        \n        console.log(myspreadsheet1.getJson());\n        \n        calcs[\"totalCH\"] = 0;\n        calcs[\"totalDHW\"] = 0;\n        calcs[\"kwhDHW\"] = 0;\n        calcs[\"weighCH\"] = 0;\n        calcs[\"vHCH\"] = 0;\n        calcs[\"vHDHW\"] = 0;\n        \n        calcs[\"tHPeak\"] = parseFloat(document.getElementById(\"tHPeak\").value || 75);\n        calcs[\"divCH\"] = parseFloat(document.getElementById(\"chdiv\").value || 100) / 100;\n        \n        var dat = myspreadsheet1.getJson();\n        for (var line in dat) {\n            \n            if (dat[line][\"enabled\"]==true) {\n                calcs[\"totalCH\"]  = calcs[\"totalCH\"] + (calcs[\"divCH\"] * parseFloat(dat[line][\"kwCH\"]) * parseFloat(dat[line][\"qty\"]));\n                calcs[\"totalDHW\"]  = calcs[\"totalDHW\"] + (parseFloat(dat[line][\"kwDHW\"]) * parseFloat(dat[line][\"qty\"]));\n                \n                var kwhdhw =  parseFloat(dat[line][\"qty\"]) * 4200 * parseFloat(dat[line][\"deltatDHW\"])  * (parseFloat(dat[line][\"vPropDHW\"]) +  (parseFloat(dat[line][\"vPersonDHW\"]) * parseFloat(dat[line][\"occupants\"])));\n                kwhdhw = kwhdhw / (1000*3600);\n                \n                calcs[\"kwhDHW\"]  = calcs[\"kwhDHW\"] + kwhdhw;\n                \n                var myvCH = (parseFloat(dat[line][\"qty\"]) * 24 * 3600 * calcs[\"divCH\"] * parseFloat(dat[line][\"kwCH\"]) / (4.200 * (calcs[\"tHPeak\"] - parseFloat(dat[line][\"tHoCH\"]))) );\n                calcs[\"vHCH\"] = calcs[\"vHCH\"] + myvCH;\n                \n                var myvDHW =  (3600 * kwhdhw / (4.200 * (calcs[\"tHPeak\"] - parseFloat(dat[line][\"tHoDHW\"]))) );\n                \n                calcs[\"vHDHW\"] = calcs[\"vHDHW\"] + myvDHW;\n                calcs[\"weighCH\"]  = calcs[\"weighCH\"] + (parseFloat(dat[line][\"tHoCH\"]) *  myvCH)  + (parseFloat(dat[line][\"tHoDHW\"]) *  myvDHW);\n              \n                \n                \n            }\n        }\n        calcs[\"ds439number\"] = Math.ceil(calcs[\"totalDHW\"] / 37.5);\n        \n        calcs[\"ds439load\"]  = Math.ceil((1.19* calcs[\"ds439number\"]) + (18.8* Math.pow(calcs[\"ds439number\"],0.5)) + 17.6);\n        \n        calcs[\"kwAvgDHW\"] = Math.ceil(10*(calcs[\"kwhDHW\"] / 24)) /10;\n        \n        calcs[\"kwAvg\"] = Math.ceil(10*(calcs[\"kwAvgDHW\"] + calcs[\"totalCH\"])) /10;\n        \n        //if (calcs[\"totalCH\"]) {\n            calcs[\"tHoPeak\"] =  Math.ceil(10*(  calcs[\"weighCH\"] / (calcs[\"vHCH\"] + calcs[\"vHDHW\"])  )) /10;\n        //}\n        \n        var result1 = \"Total CH: \" + (Math.ceil(10*(calcs[\"totalCH\"]))/10) + \"kW\";\n        \n        result1 += \"<br>Total DHW: \" +  (Math.ceil(10*(calcs[\"kwhDHW\"]))/10) + \"kWh\";\n        result1 += \"<br>Averaged DHW: \" + calcs[\"kwAvgDHW\"] + \"kW\";\n        \n        result1 += \"<br>Diversified DHW: \" + calcs[\"ds439load\"] + \"kW (\" + calcs[\"ds439number\"] +\" equivalent 37.5kW properties)\";\n        result1 += \"<br>Minimum Averaged Input: \" + calcs[\"kwAvg\"] + \"kW\";\n        \n        \n        result1 += \"<br>Daily Volume CH: \" +  (Math.ceil(10*(calcs[\"vHCH\"]))/10) + \" litres\";\n        result1 += \"<br>Daily Volume DHW: \" +  (Math.ceil(10*(calcs[\"vHDHW\"]))/10) + \" litres\";\n        \n        result1 += \"<br>Volume Weighed Average Return Temperature: \" + calcs[\"tHoPeak\"] + \"C\";\n        \n        document.getElementById(\"spreadsheet1results\").innerHTML = result1;\n        \n        var holdp = document.getElementById(\"dhwPriority\").checked;\n        ssets = [];\n        runstorearray();\n        \n        document.getElementById(\"dhwPriority\").checked = holdp;\n        document.getElementById(\"boilerkw\").value =  calcs[\"kwAvg\"];\n        \n        runstore();\n    }\n    \n    \n    function runstore() {\n        \n        \n        //var estVolumes = [1.8,1.2,1,0.8,1.3,1.7,4.5,8.7,7.6,6.1,5.4,4.8,4.2,3.6,3.1,3.2,3.7,5.3,7.7,7.3,6.1,4.9,3.7,2.4];\n        var estVolumes = dhwprofilesheet.getRowData(0);\n        \n        calcs[\"tHPeak\"] = parseFloat(document.getElementById(\"tHPeak\").value || 75);\n        \n        columns2 = [\n            {\n                type: 'numeric',\n                name: 'hour',\n                title:'Hour',\n                width:90,\n                decimal:'.'\n            },\n            {\n                type: 'numeric',\n                name: 'percent',\n                title:'EST Volume %',\n                width:80,\n                decimal:'.'\n            },\n            {\n                type: 'numeric',\n                name: 'dhwload',\n                title:'DHW Energy kWh',\n                width:80,\n                decimal:'.'\n            },\n            {\n                type: 'numeric',\n                name: 'dischargetime',\n                title:'Discharge Time h',\n                width:80,\n                decimal:'.'\n            },\n            {\n                type: 'numeric',\n                name: 'dischargestorage',\n                title:'Discharge Storage kWh',\n                width:80,\n                decimal:'.'\n            },\n            {\n                type: 'numeric',\n                name: 'energy',\n                title:'Balance kWh',\n                width:80,\n                decimal:'.'\n            },\n            {\n                type: 'numeric',\n                name: 'dischargebalance',\n                title:'Discharge Balance kWh',\n                width:80,\n                decimal:'.'\n            }\n        ];\n        \n        \n        var runload = 0;\n        var runinput = 0;\n        var mine = 999999;\n        var maxe = -999999;\n        var maxh = -999999;\n        var hr = 0;\n        volumecalcs = [];\n        var lastbalance = 0;\n        \n        if (parseFloat(document.getElementById(\"boilerkw\").value)  < calcs[\"kwAvg\"]) {\n            \n            alert(\"Not enough heat input, adjusting to minimum.\");\n            document.getElementById(\"boilerkw\").value = calcs[\"kwAvg\"];\n        }\n        \n        calcs[\"boilerkw\"] =parseFloat(document.getElementById(\"boilerkw\").value);\n        \n        var bin = calcs[\"boilerkw\"] - calcs[\"totalCH\"];\n        console.log(\"bin=\"+bin);\n        \n        calcs[\"dhwPriority\"] = document.getElementById(\"dhwPriority\").checked;\n        console.log(\"dhwPriority=\"+calcs[\"dhwPriority\"]);\n        \n        \n        \n        for (var h in estVolumes) {\n            \n            hr++;\n            \n            var houroff = hr + 5;  // start with first hour where output exceeds min input - where store would be full.\n            if (houroff>23) { houroff = houroff - 24; }\n            var ob = {};\n            ob[\"hour\"] = houroff; //hr;\n            ob[\"percent\"] = estVolumes[houroff];\n            ob[\"dhwload\"] = parseInt(estVolumes[houroff] * calcs[\"kwhDHW\"]) / 100;\n            \n            \n            ob[\"dischargetime\"] = Math.ceil(1000*(ob[\"dhwload\"] / calcs[\"ds439load\"]))/1000;\n            \n            if (calcs[\"dhwPriority\"]==true) {\n                \n                var dhwneeded = calcs[\"ds439load\"] - calcs[\"totalCH\"];\n                \n                ob[\"dischargestorage\"] = Math.ceil(10*(ob[\"dhwload\"] - (ob[\"dischargetime\"] * calcs[\"boilerkw\"]))) / 10;\n                \n            } else {\n                \n                ob[\"dischargestorage\"] = Math.ceil(10*(ob[\"dhwload\"] - (ob[\"dischargetime\"] * bin))) / 10;\n            }\n            \n            \n            if (ob[\"dischargestorage\"] < 0) { ob[\"dischargestorage\"] =0; }\n            \n            \n            ob[\"dischargebalance\"] = Math.ceil(10*(lastbalance - ob[\"dischargestorage\"]))/10;\n            \n            runload = runload + ob[\"dhwload\"];\n            runinput = runinput + bin;\n            \n            ob[\"energy\"] = parseInt(100*(runinput - runload))/100;\n            \n            if (ob[\"energy\"]>0) { // removed input beyond store capacity\n                \n                runinput = runinput - ob[\"energy\"];  \n                ob[\"energy\"] = 0; \n                \n            }\n            \n            if (ob[\"energy\"] < mine) { mine = ob[\"energy\"]; }\n            if (ob[\"energy\"] > maxe) { maxe = ob[\"energy\"]; }\n            //if (ob[\"dischargestorage\"] > maxh) { maxh = ob[\"dischargestorage\"]; }\n            \n            if ((0-ob[\"dischargebalance\"]) > maxh) { maxh = 0-ob[\"dischargebalance\"]; }\n            \n            lastbalance = ob[\"energy\"];\n            \n            volumecalcs.push(ob);\n            \n        }\n        calcs[\"khwStorage\"] = Math.ceil(Math.max(maxe - mine, maxh));\n        \n        \n        calcs[\"litreStorage\"] = Math.ceil(calcs[\"khwStorage\"] * 3600000 / (4200 * (calcs[\"tHPeak\"] - calcs[\"tHoPeak\"])));\n        \n        \n        document.getElementById('spreadsheet2').innerHTML=\"\";\n        var myspreadsheet2 = jspreadsheet(document.getElementById('spreadsheet2'), {\n            data: volumecalcs,\n            columns: columns2\n        });\n        \n        \n        var result2 = \"Minimum Averaged Storage: \" + calcs[\"khwStorage\"] + \"kWh\";\n        result2 += \" = \" + calcs[\"litreStorage\"] + \" litres\";\n        \n        document.getElementById(\"spreadsheet2results\").innerHTML = result2;\n        \n    }\n    \n    \n    var ssets = [];\n    \n    function runstorearray() {\n        \n        \n        \n        brun = [calcs[\"kwAvg\"]];\n        var b30 = 30* Math.ceil(calcs[\"kwAvg\"] / 30);\n        brun.push( b30 );\n        \n        if(calcs[\"kwAvg\"]>70) { brun.push( b30 + 30 ); }\n        if(calcs[\"kwAvg\"]>100) { brun.push( b30 + 60 ); }\n        \n        brun.push( calcs[\"ds439load\"] + calcs[\"totalCH\"] );\n        \n        for (var k in brun) {\n            \n            var ob2 =  {\"boilerkw\":brun[k] };\n            document.getElementById(\"boilerkw\").value = brun[k];\n            document.getElementById(\"dhwPriority\").checked = false;\n            runstore();\n            ob2[\"vStore\"] = calcs[\"litreStorage\"];\n            \n            document.getElementById(\"boilerkw\").value = brun[k];\n            document.getElementById(\"dhwPriority\").checked = true;\n            runstore();\n            ob2[\"vStorePriority\"] = calcs[\"litreStorage\"];\n            \n            ssets.push( ob2 )\n        }\n        \n        document.getElementById('spreadsheet3').innerHTML=\"\";\n        var myspreadsheet3 = jspreadsheet(document.getElementById('spreadsheet3'), {\n            data: ssets,\n            columns: [\n            {\n                type: 'numeric',\n                name: 'boilerkw',\n                title:'kW Input',\n                width:150,\n                decimal:'.'\n            },\n            {\n                type: 'numeric',\n                name: 'vStore',\n                title:'Storage Volume litres',\n                width:150,\n                decimal:'.'\n            },\n            {\n                type: 'numeric',\n                name: 'vStorePriority',\n                title:'Storage Volume (With Priority) litres',\n                width:150,\n                decimal:'.'\n            }],\n            updateTable:function(instance, cell, col, row, val, label, cellName) {\n              \n                if (row==2) {\n                        cell.style.backgroundColor = '#aaffaa';\n                    \n                }\n            },\n            columnSorting:false\n        });\n        \n    }\n    \n    \n    \n    function but() {\n        \n        \n        console.log(myspreadsheet.getJson());\n        console.log(myspreadsheet.getConfig());\n        \n    }\n    function fullscreen(mode) {\n    \n        myspreadsheet.fullscreen(mode);\n        \n    }\n\n\n\nvar columns1 = [\n        {\n            type: 'text',\n            name: 'propertyType',\n            title:'Property Type',\n            width:90\n        },\n        {\n            type: 'numeric',\n            name: 'bedrooms',\n            title:'Bedrooms',\n            width:80,\n            decimal:'.'\n        },\n        {\n            type: 'numeric',\n            name: 'occupants',\n            title:'Occupants',\n            width:80,\n            decimal:'.'\n        },\n        {\n            type: 'dropdown',\n            name: 'connectionTypeCH',\n            title:'Heating Connection',\n            width:120,\n            source:[\n                \"direct\",\n                \"indirect\",\n                \"none\",\n                // (...)\n              ]\n        },\n        {\n            type: 'dropdown',\n            name: 'emitterTypeCH',\n            title:'Heating Type',\n            width:120,\n            source:[\n                \"radiators\",\n                \"underfloor\",\n                \"none\",\n                // (...)\n              ]\n        },\n        {\n            type: 'numeric',\n            name: 'kwCH',\n            title:'Heating Load kW',\n            width:80,\n            decimal:'.'\n        },\n        {\n            type: 'numeric',\n            name: 'tHoCH',\n            title:'Primary CH Return Temp C',\n            width:80,\n            decimal:'.'\n        },\n        {\n            type: 'dropdown',\n            name: 'connectionTypeDHW',\n            title:'DHW Type',\n            width:120,\n            source:[\n                \"instantaneous\",\n                \"phe+coil+store\",\n                \"phe+store\",\n                \"coil+store\",\n                \"none\",\n                // (...)\n              ]\n        },\n        {\n            type: 'numeric',\n            name: 'kwDHW',\n            title:'DHW Peak kW',\n            width:80,\n            decimal:'.'\n        },\n        {\n            type: 'numeric',\n            name: 'vPropDHW',\n            title:'DHW Litres (property)',\n            width:80,\n            decimal:'.'\n        },\n        {\n            type: 'numeric',\n            name: 'vPersonDHW',\n            title:'DHW Litres (person)',\n            width:80,\n            decimal:'.'\n        },\n        {\n            type: 'numeric',\n            name: 'deltatDHW',\n            title:'DHW Temp Rise',\n            width:80,\n            decimal:'.'\n        },\n        {\n            type: 'numeric',\n            name: 'tHoDHW',\n            title:'Primary DHW Return Temp C',\n            width:80,\n            decimal:'.'\n        },\n        {\n            type: 'image',\n            name: 'graphic',\n            title:'Photo',\n            width:120\n        },\n        {\n            type: 'checkbox',\n            name: 'enabled',\n            title:'Enable',\n            width:80\n        },\n        {\n            type: 'numeric',\n            name: 'qty',\n            title:'Quantity',\n            width:80,\n            decimal:'.'\n        }\n    ];\n\nvar icon = \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAAAgY0hSTQAAeiYAAICEAAD6AAAAgOgAAHUwAADqYAAAOpgAABdwnLpRPAAAAOFQTFRFAAAAAIlARERARERAAYhARERARERABoNAiaRmh6FlAIlAX5h6Xpd6AYhAXpZ5rK2srK2tX5l7XpV4qqqq0NHR19jYsLGx4uPj4+Tk2NnZ6uvrX5h7BIVAqqurXZR3AodAtYVY6erq29zcz9DQgpyP09TUqXpSqHpS4eLi4OHhg56RoaKjnXBLnHBLmG5Ko6SlkGZE9cdoj2ZEjGREi2REimFAhl9A3N3dg109gFw9f1w91tfX1dbWflA4HHY8G3Y8GXk8gVE4iaRmkbRfjrBeh6FlaGlpa2tkampjZ2ho////c2A5CgAAAAp0Uk5TAGsBBGwJBXGho317A2MAAAABYktHREoeDLXGAAAACXBIWXMAAABIAAAASABGyWs+AAAAu0lEQVQY002O1xKCMBAAE7HGriAxVrChorGCih2U+P8/5BHGGfcl2Z27TBCSYEIw+gOTfKGI/71UrlTlTCL2Wl3VGlFJKD/XaRNKMpX+OWNQWu2MEu1Lh6J1uj2MSL6s6v0Iwxx0qgSh4UhnY2BiTU1tBm/OYd627cVyahmUQ+CwvlpvtjvLYEwGSul+7ziODhcZXNc9HE/HA5wuh59yzztfrrf7wwO4grJP3w+CwI955dA7DIUQYYwQny8RbRx9zgBJtwAAACV0RVh0ZGF0ZTpjcmVhdGUAMjAxOC0wNS0wNlQxNzo1NDoyNS0wNTowMMd7TXIAAAAldEVYdGRhdGU6bW9kaWZ5ADIwMTgtMDUtMDZUMTc6NTQ6MjUtMDU6MDC2JvXOAAAAAElFTkSuQmCC\";\n\nvar data1 = [\n    ['Flat', 1, 2, 'indirect', 'radiators', 3, 35, 'instantaneous', 37.5,40,28,35, 25, icon ,true, 130],\n    ['Duplex', 2, 4, 'indirect', 'underfloor', 5.5, 35, 'instantaneous', 45,40,28,35, 25, icon ,true, 20],\n];\n\nvar myspreadsheet1 = jspreadsheet(document.getElementById('spreadsheet1'), {\n    data:data1,\n    columns: columns1\n});\n\nvar rprof;\nvar dhwprofilesheet = jspreadsheet(document.getElementById('profilesheet'), {\n            data: [[1.8,1.2,1,0.8,1.3,1.7,4.5,8.7,7.6,6.1,5.4,4.8,4.2,3.6,3.1,3.2,3.7,5.3,7.7,7.3,6.1,4.9,3.7,2.4]],\n            columns: [\n            {\n                title:'0'\n            },\n            {\n                title:'1'\n            },\n            {\n                title:'2'\n            },\n            {\n                title:'3'\n            },\n            {\n                title:'4'\n            },\n            {\n                title:'5'\n            },\n            {\n                title:'6'\n            },\n            {\n                title:'7'\n            },\n            {\n                title:'8'\n            },\n            {\n                title:'9'\n            },\n            {\n                title:'10'\n            },\n            {\n                title:'11'\n            },\n            {\n                title:'12'\n            },\n            {\n                title:'13'\n            },\n            {\n                title:'14'\n            },\n            {\n                title:'15'\n            },\n            {\n                title:'16'\n            },\n            {\n                title:'17'\n            },\n            {\n                title:'18'\n            },\n            {\n                title:'19'\n            },\n            {\n                title:'20'\n            },\n            {\n                title:'21'\n            },\n            {\n                title:'22'\n            },\n            {\n                title:'23'\n            }],\n            columnSorting:false,\n            allowInsertColumn: false,\n            allowInsertRow: false,\n            allowDeleteColumn: false,\n            allowDeleteRow: false\n        });\n\nfunction runprofile() {\n    \n    if (dhwprofilesheet) {\n        \n        var prof = dhwprofilesheet.getRowData(0);\n        var tot = 0;\n        for (var h in prof) {\n            tot = tot + parseFloat(prof[h]);\n        }\n        if (tot>0) {\n            for (var h in prof) {\n                prof[h] = parseInt(100 * (100/tot) * parseFloat(prof[h])) / 100;\n            }\n        }\n        dhwprofilesheet.setRowData(0, prof);\n    }\n}\n\nfunction resetprofile() {\n    \n    if (dhwprofilesheet) {\n        \n        dhwprofilesheet.setRowData(0, [1.8,1.2,1,0.8,1.3,1.7,4.5,8.7,7.6,6.1,5.4,4.8,4.2,3.6,3.1,3.2,3.7,5.3,7.7,7.3,6.1,4.9,3.7,2.4]);\n    }\n}\n</script>","output":"str","x":430,"y":420,"wires":[["59fb20e9.ae98a"]],"info":"https://bossanova.uk/jspreadsheet/v4/"},{"id":"59fb20e9.ae98a","type":"template","z":"d24faa61.312688","name":"","field":"heads","fieldType":"msg","format":"handlebars","syntax":"plain","template":"<!doctype html>\n<html lang=\"en\">\n  <head>\n    <!-- Required meta tags -->\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1, shrink-to-fit=no\">\n\n    <!-- Bootstrap CSS -->\n    <link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/css/bootstrap.min.css\" integrity=\"sha384-TX8t27EcRE3e/ihU7zmQxVncDAy5uIKz4rEkgIXeMed4M0jlfIDPvg6uqKI2xXr2\" crossorigin=\"anonymous\">\n\n  \n    <script src=\"https://kit.fontawesome.com/c6b21b7a8f.js\" crossorigin=\"anonymous\"></script>\n\n \n    <title>Thermal Integration Heatweb Node</title>\n\n\n  <script src=\"https://code.jquery.com/jquery-3.5.1.js\"></script>\n  <script src=\"https://cdn.datatables.net/1.10.22/js/jquery.dataTables.min.js\" type=\"text/javascript\"></script>\n  <link rel=\"stylesheet\" href=\"https://cdn.datatables.net/1.10.22/css/jquery.dataTables.min.css\">\n\n\n  \n    <style>\n    \n    body, textarea, input, select {\n    font-size: 18px;\n}\n\nsection  {\n    border-bottom: 1px solid rgb(255, 255, 255);\n    padding: 20px 50px;\n    /* height: 100vh; */\n    scroll-snap-align: start;\n    /* text-align: center; */\n    position: relative;\n    background: #ffffff;\n    font-size: 18px;\n    }\n\nsection .table {\n            font-size: 16px;\n        }\n\ntable.dataTable.compact tbody td {\n    \n    padding: 2px;\n}  \n\nsection .jstree-node {\n    font-size: 16px;\n}\n\n.nowrap {\n    white-space: nowrap;\n}\n\n@media (max-width: 1480px) {\n\n    section {\n        \n        padding: 20px 20px;\n        \n        }\n\n}\n\n@media (max-width:480px) {\n\n    section {\n        \n        padding: 10px 8px;\n        \n        }\n\n}\n\n    \n        .maxsiz {\n\n        width: 100%; \n        height: 100vh;\n\n        }\n\n        .maxsiz2 {\n\n        width: 100%; \n        height: calc(100vh - 70px);\n\n        }\n\n        .dot {\n            height: 13px;\n            width: 13px;\n            background-color: rgb(52, 58, 52);\n            border-radius: 20%;\n            border: 2px;\n            border-color: rgb(5, 5, 20);\n            display: inline-block;\n        }\n\n        .thindot {\n            height: 13px;\n            width: 6px;\n            background-color: rgb(52, 58, 52);\n            border-radius: 20%;\n            border: 2px;\n            border-color: rgb(5, 5, 20);\n            display: inline-block;\n        }\n\n        .dotspacer {\n            height: 13px;\n            width: 2px;\n            display: inline-block;\n        }\n\n        .navReadings  {\n            /* border-bottom: 1px solid rgb(32, 28, 28); */\n            padding: 20px 50px 0px 50px;\n            /* background: #6d6d70; */\n            font-size: 80%;  \n            /* font-weight: bold; */\n            text-align: left;\n            color: #212529;  \n        }\n\n        .navReadings a {\n            color: #212529;        \n        }\n\n        \n\n          \n    </style>","output":"str","x":600,"y":420,"wires":[["2c0cb6ba.6d28ea"]]},{"id":"2c0cb6ba.6d28ea","type":"function","z":"d24faa61.312688","name":"","func":"msg.payload = msg.heads + \"<section>\" +  msg.payload.body + \"</section></body></html>\"\n\nreturn msg;","outputs":1,"noerr":0,"x":790,"y":420,"wires":[["dda7c150.c3342"]]},{"id":"dda7c150.c3342","type":"http response","z":"d24faa61.312688","name":"","statusCode":"","headers":{},"x":990,"y":420,"wires":[]}]
</pre>
</pre>

Revision as of 21:47, 21 May 2022

The following calculator is driven from Node-RED installed locally on the wiki server.

Source Code

The following is the Node-RED source code.

Hncalc.png

https://heatweb.mypinata.cloud/ipfs/QmRxu1RyQL34nRRuBYkwj45HzFwrniAhUEiri6au2yYgaQ


[{"id":"4444b4d.e5bf44c","type":"http in","z":"d24faa61.312688","name":"","url":"/ui/hncalc","method":"get","upload":false,"swaggerDoc":"","x":250,"y":420,"wires":[["293b34f8.1b1f0c"]]},{"id":"293b34f8.1b1f0c","type":"template","z":"d24faa61.312688","name":"editor","field":"payload.body","fieldType":"msg","format":"handlebars","syntax":"mustache","template":"\n<script src=\"https://bossanova.uk/jspreadsheet/v4/jexcel.js\"></script>\n<link rel=\"stylesheet\" href=\"https://bossanova.uk/jspreadsheet/v4/jexcel.css\" type=\"text/css\" />\n \n<script src=\"https://jsuites.net/v4/jsuites.js\"></script>\n<link rel=\"stylesheet\" href=\"https://jsuites.net/v4/jsuites.css\" type=\"text/css\" />\n\n \n<p>This is a calculator for sizing and equipment selection on heat networks.</p>\n<p>Right click or press <i>Enter</i> in a cell to add new rows.</p>\n<br>\n\n<style>\n    .jexcel_content {\n        font-size: 15px;\n    }\n    \n    .jexcel {\n        white-space: normal;\n    }\n</style>\n\n \n<div id=\"spreadsheet1\"></div>\n<br>\n<table cellpadding=\"6\">\n  <tr><td>Central Heating Diversity:</td><td><input type=\"text\" id=\"chdiv\" name=\"chdiv\" value=\"70\" placeholder=\"% Peak\"> % </td></tr>\n  \n <tr><td>Peak Flow Temperature:</td><td><input type=\"text\" id=\"tHPeak\" name=\"tHPeak\" value=\"75\" placeholder=\"Input C\"> C</td></tr>\n \n</table>\n\n\n<br>\nHot Water Hourly Profile (default values from EST study):<br>\n<div id=\"profilesheet\"></div>\n<br>\n<input type=\"button\" onClick=\"runprofile()\" value=\"Level to 100%\"> <input type=\"button\" onClick=\"resetprofile()\" value=\"Reset to EST volumes\">\n<br>\n<br>\n<input type=\"button\" onClick=\"calc(1)\" value=\"Run Calculation\">\n<br><br>\n<div id=\"spreadsheet1results\"></div>\n\n<br>\n<div id=\"spreadsheet3\"></div>\n<br><br>\n<p>Heat input is initially set to the minimum, resulting in maximum storage sizes. Increase the heat input to reduce storage sizes.</p>\n<p>Network DHW Priority requires networked HIUs that can respond to signals from planroom to limit central heating loads for short periods to enable load prioritisation, and thereby limit the instantaneous demand on the system as required.  This allows peak heat input to be safely sized to average hourly loads and may avoid the need for storage.</p>\n<table cellpadding=\"6\">\n    <tr><td>Heat Input (kW):</td><td><input type=\"text\" id=\"boilerkw\" name=\"boilerkw\" value=\"\" placeholder=\"Input kW\"></td></tr>\n \n <tr><td>Network DHW Priority:</td><td><input type=\"checkbox\" id=\"dhwPriority\" name=\"dhwPriority\"></td></tr>\n</table>\n<br><input type=\"button\" onClick=\"runstore()\" value=\"Run Storage Calcs\">\n<br><br>\n<div id=\"spreadsheet2results\"></div>\n<br><br>\n<table>\n    <tr>\n        <td><div id=\"spreadsheet2\"></div></td>\n        <td width=\"100\"> </td>\n        <td valign=\"top\"></td>\n    </tr>\n</table>\n\n<br><br>\n\n\n<br><br>\n<div id=\"spreadsheet\"></div>\n\n \n<br><br>\n\n<script>\n\nvar calcs = {};\n\n    function calc(n) {\n        \n        \n        console.log(myspreadsheet1.getJson());\n        \n        calcs[\"totalCH\"] = 0;\n        calcs[\"totalDHW\"] = 0;\n        calcs[\"kwhDHW\"] = 0;\n        calcs[\"weighCH\"] = 0;\n        calcs[\"vHCH\"] = 0;\n        calcs[\"vHDHW\"] = 0;\n        \n        calcs[\"tHPeak\"] = parseFloat(document.getElementById(\"tHPeak\").value || 75);\n        calcs[\"divCH\"] = parseFloat(document.getElementById(\"chdiv\").value || 100) / 100;\n        \n        var dat = myspreadsheet1.getJson();\n        for (var line in dat) {\n            \n            if (dat[line][\"enabled\"]==true) {\n                calcs[\"totalCH\"]  = calcs[\"totalCH\"] + (calcs[\"divCH\"] * parseFloat(dat[line][\"kwCH\"]) * parseFloat(dat[line][\"qty\"]));\n                calcs[\"totalDHW\"]  = calcs[\"totalDHW\"] + (parseFloat(dat[line][\"kwDHW\"]) * parseFloat(dat[line][\"qty\"]));\n                \n                var kwhdhw =  parseFloat(dat[line][\"qty\"]) * 4200 * parseFloat(dat[line][\"deltatDHW\"])  * (parseFloat(dat[line][\"vPropDHW\"]) +  (parseFloat(dat[line][\"vPersonDHW\"]) * parseFloat(dat[line][\"occupants\"])));\n                kwhdhw = kwhdhw / (1000*3600);\n                \n                calcs[\"kwhDHW\"]  = calcs[\"kwhDHW\"] + kwhdhw;\n                \n                var myvCH = (parseFloat(dat[line][\"qty\"]) * 24 * 3600 * calcs[\"divCH\"] * parseFloat(dat[line][\"kwCH\"]) / (4.200 * (calcs[\"tHPeak\"] - parseFloat(dat[line][\"tHoCH\"]))) );\n                calcs[\"vHCH\"] = calcs[\"vHCH\"] + myvCH;\n                \n                var myvDHW =  (3600 * kwhdhw / (4.200 * (calcs[\"tHPeak\"] - parseFloat(dat[line][\"tHoDHW\"]))) );\n                \n                calcs[\"vHDHW\"] = calcs[\"vHDHW\"] + myvDHW;\n                calcs[\"weighCH\"]  = calcs[\"weighCH\"] + (parseFloat(dat[line][\"tHoCH\"]) *  myvCH)  + (parseFloat(dat[line][\"tHoDHW\"]) *  myvDHW);\n               \n                \n                \n            }\n        }\n        calcs[\"ds439number\"] = Math.ceil(calcs[\"totalDHW\"] / 37.5);\n        \n        calcs[\"ds439load\"]  = Math.ceil((1.19* calcs[\"ds439number\"]) + (18.8* Math.pow(calcs[\"ds439number\"],0.5)) + 17.6);\n        \n        calcs[\"kwAvgDHW\"] = Math.ceil(10*(calcs[\"kwhDHW\"] / 24)) /10;\n        \n        calcs[\"kwAvg\"] = Math.ceil(10*(calcs[\"kwAvgDHW\"] + calcs[\"totalCH\"])) /10;\n        \n        //if (calcs[\"totalCH\"]) {\n            calcs[\"tHoPeak\"] =  Math.ceil(10*(  calcs[\"weighCH\"] / (calcs[\"vHCH\"] + calcs[\"vHDHW\"])  )) /10;\n        //}\n        \n        var result1 = \"Total CH: \" + (Math.ceil(10*(calcs[\"totalCH\"]))/10) + \"kW\";\n        \n        result1 += \"<br>Total DHW: \" +  (Math.ceil(10*(calcs[\"kwhDHW\"]))/10) + \"kWh\";\n        result1 += \"<br>Averaged DHW: \" + calcs[\"kwAvgDHW\"] + \"kW\";\n        \n        result1 += \"<br>Diversified DHW: \" + calcs[\"ds439load\"] + \"kW (\" + calcs[\"ds439number\"] +\" equivalent 37.5kW properties)\";\n        result1 += \"<br>Minimum Averaged Input: \" + calcs[\"kwAvg\"] + \"kW\";\n        \n        \n        result1 += \"<br>Daily Volume CH: \" +  (Math.ceil(10*(calcs[\"vHCH\"]))/10) + \" litres\";\n        result1 += \"<br>Daily Volume DHW: \" +  (Math.ceil(10*(calcs[\"vHDHW\"]))/10) + \" litres\";\n        \n        result1 += \"<br>Volume Weighed Average Return Temperature: \" + calcs[\"tHoPeak\"] + \"C\";\n        \n        document.getElementById(\"spreadsheet1results\").innerHTML = result1;\n        \n        var holdp = document.getElementById(\"dhwPriority\").checked;\n        ssets = [];\n        runstorearray();\n        \n        document.getElementById(\"dhwPriority\").checked = holdp;\n        document.getElementById(\"boilerkw\").value =  calcs[\"kwAvg\"];\n        \n        runstore();\n    }\n    \n    \n    function runstore() {\n        \n        \n        //var estVolumes = [1.8,1.2,1,0.8,1.3,1.7,4.5,8.7,7.6,6.1,5.4,4.8,4.2,3.6,3.1,3.2,3.7,5.3,7.7,7.3,6.1,4.9,3.7,2.4];\n        var estVolumes = dhwprofilesheet.getRowData(0);\n        \n        calcs[\"tHPeak\"] = parseFloat(document.getElementById(\"tHPeak\").value || 75);\n        \n        columns2 = [\n            {\n                type: 'numeric',\n                name: 'hour',\n                title:'Hour',\n                width:90,\n                decimal:'.'\n            },\n            {\n                type: 'numeric',\n                name: 'percent',\n                title:'EST Volume %',\n                width:80,\n                decimal:'.'\n            },\n            {\n                type: 'numeric',\n                name: 'dhwload',\n                title:'DHW Energy kWh',\n                width:80,\n                decimal:'.'\n            },\n            {\n                type: 'numeric',\n                name: 'dischargetime',\n                title:'Discharge Time h',\n                width:80,\n                decimal:'.'\n            },\n            {\n                type: 'numeric',\n                name: 'dischargestorage',\n                title:'Discharge Storage kWh',\n                width:80,\n                decimal:'.'\n            },\n            {\n                type: 'numeric',\n                name: 'energy',\n                title:'Balance kWh',\n                width:80,\n                decimal:'.'\n            },\n            {\n                type: 'numeric',\n                name: 'dischargebalance',\n                title:'Discharge Balance kWh',\n                width:80,\n                decimal:'.'\n            }\n        ];\n        \n        \n        var runload = 0;\n        var runinput = 0;\n        var mine = 999999;\n        var maxe = -999999;\n        var maxh = -999999;\n        var hr = 0;\n        volumecalcs = [];\n        var lastbalance = 0;\n        \n        if (parseFloat(document.getElementById(\"boilerkw\").value)  < calcs[\"kwAvg\"]) {\n            \n            alert(\"Not enough heat input, adjusting to minimum.\");\n            document.getElementById(\"boilerkw\").value = calcs[\"kwAvg\"];\n        }\n        \n        calcs[\"boilerkw\"] =parseFloat(document.getElementById(\"boilerkw\").value);\n         \n        var bin = calcs[\"boilerkw\"] - calcs[\"totalCH\"];\n        console.log(\"bin=\"+bin);\n        \n        calcs[\"dhwPriority\"] = document.getElementById(\"dhwPriority\").checked;\n        console.log(\"dhwPriority=\"+calcs[\"dhwPriority\"]);\n        \n        \n        \n        for (var h in estVolumes) {\n            \n            hr++;\n            \n            var houroff = hr + 5;  // start with first hour where output exceeds min input - where store would be full.\n            if (houroff>23) { houroff = houroff - 24; }\n            var ob = {};\n            ob[\"hour\"] = houroff; //hr;\n            ob[\"percent\"] = estVolumes[houroff];\n            ob[\"dhwload\"] = parseInt(estVolumes[houroff] * calcs[\"kwhDHW\"]) / 100;\n            \n            \n             ob[\"dischargetime\"] = Math.ceil(1000*(ob[\"dhwload\"] / calcs[\"ds439load\"]))/1000;\n             \n            if (calcs[\"dhwPriority\"]==true) {\n                \n                var dhwneeded = calcs[\"ds439load\"] - calcs[\"totalCH\"];\n                \n                ob[\"dischargestorage\"] = Math.ceil(10*(ob[\"dhwload\"] - (ob[\"dischargetime\"] * calcs[\"boilerkw\"]))) / 10;\n                \n            } else {\n                \n                ob[\"dischargestorage\"] = Math.ceil(10*(ob[\"dhwload\"] - (ob[\"dischargetime\"] * bin))) / 10;\n            }\n            \n            \n            if (ob[\"dischargestorage\"] < 0) { ob[\"dischargestorage\"] =0; }\n            \n            \n            ob[\"dischargebalance\"] = Math.ceil(10*(lastbalance - ob[\"dischargestorage\"]))/10;\n            \n            runload = runload + ob[\"dhwload\"];\n            runinput = runinput + bin;\n            \n            ob[\"energy\"] = parseInt(100*(runinput - runload))/100;\n            \n            if (ob[\"energy\"]>0) { // removed input beyond store capacity\n                \n                runinput = runinput - ob[\"energy\"];   \n                ob[\"energy\"] = 0; \n                \n            }\n            \n            if (ob[\"energy\"] < mine) { mine = ob[\"energy\"]; }\n            if (ob[\"energy\"] > maxe) { maxe = ob[\"energy\"]; }\n            //if (ob[\"dischargestorage\"] > maxh) { maxh = ob[\"dischargestorage\"]; }\n            \n            if ((0-ob[\"dischargebalance\"]) > maxh) { maxh = 0-ob[\"dischargebalance\"]; }\n            \n            lastbalance = ob[\"energy\"];\n            \n            volumecalcs.push(ob);\n            \n        }\n        calcs[\"khwStorage\"] = Math.ceil(Math.max(maxe - mine, maxh));\n        \n        \n        calcs[\"litreStorage\"] = Math.ceil(calcs[\"khwStorage\"] * 3600000 / (4200 * (calcs[\"tHPeak\"] - calcs[\"tHoPeak\"])));\n        \n        \n        document.getElementById('spreadsheet2').innerHTML=\"\";\n        var myspreadsheet2 = jspreadsheet(document.getElementById('spreadsheet2'), {\n            data: volumecalcs,\n            columns: columns2\n        });\n        \n        \n        var result2 = \"Minimum Averaged Storage: \" + calcs[\"khwStorage\"] + \"kWh\";\n        result2 += \" = \" + calcs[\"litreStorage\"] + \" litres\";\n        \n        document.getElementById(\"spreadsheet2results\").innerHTML = result2;\n        \n    }\n    \n    \n    var ssets = [];\n    \n    function runstorearray() {\n        \n        \n        \n        brun = [calcs[\"kwAvg\"]];\n        var b30 = 30* Math.ceil(calcs[\"kwAvg\"] / 30);\n        brun.push( b30 );\n        \n        if(calcs[\"kwAvg\"]>70) { brun.push( b30 + 30 ); }\n        if(calcs[\"kwAvg\"]>100) { brun.push( b30 + 60 ); }\n        \n        brun.push( calcs[\"ds439load\"] + calcs[\"totalCH\"] );\n        \n        for (var k in brun) {\n            \n            var ob2 =  {\"boilerkw\":brun[k] };\n            document.getElementById(\"boilerkw\").value = brun[k];\n            document.getElementById(\"dhwPriority\").checked = false;\n            runstore();\n            ob2[\"vStore\"] = calcs[\"litreStorage\"];\n            \n            document.getElementById(\"boilerkw\").value = brun[k];\n            document.getElementById(\"dhwPriority\").checked = true;\n            runstore();\n            ob2[\"vStorePriority\"] = calcs[\"litreStorage\"];\n            \n            ssets.push( ob2 )\n        }\n        \n        document.getElementById('spreadsheet3').innerHTML=\"\";\n        var myspreadsheet3 = jspreadsheet(document.getElementById('spreadsheet3'), {\n            data: ssets,\n            columns: [\n            {\n                type: 'numeric',\n                name: 'boilerkw',\n                title:'kW Input',\n                width:150,\n                decimal:'.'\n            },\n            {\n                type: 'numeric',\n                name: 'vStore',\n                title:'Storage Volume litres',\n                width:150,\n                decimal:'.'\n            },\n            {\n                type: 'numeric',\n                name: 'vStorePriority',\n                title:'Storage Volume (With Priority) litres',\n                width:150,\n                decimal:'.'\n            }],\n            updateTable:function(instance, cell, col, row, val, label, cellName) {\n               \n                if (row==2) {\n                        cell.style.backgroundColor = '#aaffaa';\n                    \n                }\n            },\n            columnSorting:false\n        });\n        \n    }\n    \n    \n    \n    function but() {\n        \n        \n        console.log(myspreadsheet.getJson());\n        console.log(myspreadsheet.getConfig());\n        \n    }\n    function fullscreen(mode) {\n    \n        myspreadsheet.fullscreen(mode);\n        \n    }\n\n\n\nvar columns1 = [\n        {\n            type: 'text',\n            name: 'propertyType',\n            title:'Property Type',\n            width:90\n        },\n        {\n            type: 'numeric',\n            name: 'bedrooms',\n            title:'Bedrooms',\n            width:80,\n            decimal:'.'\n        },\n        {\n            type: 'numeric',\n            name: 'occupants',\n            title:'Occupants',\n            width:80,\n            decimal:'.'\n        },\n        {\n            type: 'dropdown',\n            name: 'connectionTypeCH',\n            title:'Heating Connection',\n            width:120,\n            source:[\n                \"direct\",\n                \"indirect\",\n                \"none\",\n                // (...)\n              ]\n        },\n        {\n            type: 'dropdown',\n            name: 'emitterTypeCH',\n            title:'Heating Type',\n            width:120,\n            source:[\n                \"radiators\",\n                \"underfloor\",\n                \"none\",\n                // (...)\n              ]\n        },\n        {\n            type: 'numeric',\n            name: 'kwCH',\n            title:'Heating Load kW',\n            width:80,\n            decimal:'.'\n        },\n        {\n            type: 'numeric',\n            name: 'tHoCH',\n            title:'Primary CH Return Temp C',\n            width:80,\n            decimal:'.'\n        },\n        {\n            type: 'dropdown',\n            name: 'connectionTypeDHW',\n            title:'DHW Type',\n            width:120,\n            source:[\n                \"instantaneous\",\n                \"phe+coil+store\",\n                \"phe+store\",\n                \"coil+store\",\n                \"none\",\n                // (...)\n              ]\n        },\n        {\n            type: 'numeric',\n            name: 'kwDHW',\n            title:'DHW Peak kW',\n            width:80,\n            decimal:'.'\n        },\n        {\n            type: 'numeric',\n            name: 'vPropDHW',\n            title:'DHW Litres (property)',\n            width:80,\n            decimal:'.'\n        },\n        {\n            type: 'numeric',\n            name: 'vPersonDHW',\n            title:'DHW Litres (person)',\n            width:80,\n            decimal:'.'\n        },\n        {\n            type: 'numeric',\n            name: 'deltatDHW',\n            title:'DHW Temp Rise',\n            width:80,\n            decimal:'.'\n        },\n        {\n            type: 'numeric',\n            name: 'tHoDHW',\n            title:'Primary DHW Return Temp C',\n            width:80,\n            decimal:'.'\n        },\n        {\n            type: 'image',\n            name: 'graphic',\n            title:'Photo',\n            width:120\n        },\n        {\n            type: 'checkbox',\n            name: 'enabled',\n            title:'Enable',\n            width:80\n        },\n        {\n            type: 'numeric',\n            name: 'qty',\n            title:'Quantity',\n            width:80,\n            decimal:'.'\n        }\n     ];\n\nvar icon = \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAAAgY0hSTQAAeiYAAICEAAD6AAAAgOgAAHUwAADqYAAAOpgAABdwnLpRPAAAAOFQTFRFAAAAAIlARERARERAAYhARERARERABoNAiaRmh6FlAIlAX5h6Xpd6AYhAXpZ5rK2srK2tX5l7XpV4qqqq0NHR19jYsLGx4uPj4+Tk2NnZ6uvrX5h7BIVAqqurXZR3AodAtYVY6erq29zcz9DQgpyP09TUqXpSqHpS4eLi4OHhg56RoaKjnXBLnHBLmG5Ko6SlkGZE9cdoj2ZEjGREi2REimFAhl9A3N3dg109gFw9f1w91tfX1dbWflA4HHY8G3Y8GXk8gVE4iaRmkbRfjrBeh6FlaGlpa2tkampjZ2ho////c2A5CgAAAAp0Uk5TAGsBBGwJBXGho317A2MAAAABYktHREoeDLXGAAAACXBIWXMAAABIAAAASABGyWs+AAAAu0lEQVQY002O1xKCMBAAE7HGriAxVrChorGCih2U+P8/5BHGGfcl2Z27TBCSYEIw+gOTfKGI/71UrlTlTCL2Wl3VGlFJKD/XaRNKMpX+OWNQWu2MEu1Lh6J1uj2MSL6s6v0Iwxx0qgSh4UhnY2BiTU1tBm/OYd627cVyahmUQ+CwvlpvtjvLYEwGSul+7ziODhcZXNc9HE/HA5wuh59yzztfrrf7wwO4grJP3w+CwI955dA7DIUQYYwQny8RbRx9zgBJtwAAACV0RVh0ZGF0ZTpjcmVhdGUAMjAxOC0wNS0wNlQxNzo1NDoyNS0wNTowMMd7TXIAAAAldEVYdGRhdGU6bW9kaWZ5ADIwMTgtMDUtMDZUMTc6NTQ6MjUtMDU6MDC2JvXOAAAAAElFTkSuQmCC\";\n\nvar data1 = [\n    ['Flat', 1, 2, 'indirect', 'radiators', 3, 35, 'instantaneous', 37.5,40,28,35, 25, icon ,true, 130],\n    ['Duplex', 2, 4, 'indirect', 'underfloor', 5.5, 35, 'instantaneous', 45,40,28,35, 25, icon ,true, 20],\n];\n\nvar myspreadsheet1 = jspreadsheet(document.getElementById('spreadsheet1'), {\n    data:data1,\n    columns: columns1\n});\n\nvar rprof;\nvar dhwprofilesheet = jspreadsheet(document.getElementById('profilesheet'), {\n            data: [[1.8,1.2,1,0.8,1.3,1.7,4.5,8.7,7.6,6.1,5.4,4.8,4.2,3.6,3.1,3.2,3.7,5.3,7.7,7.3,6.1,4.9,3.7,2.4]],\n            columns: [\n            {\n                title:'0'\n            },\n            {\n                title:'1'\n            },\n            {\n                title:'2'\n            },\n            {\n                title:'3'\n            },\n            {\n                title:'4'\n            },\n            {\n                title:'5'\n            },\n            {\n                title:'6'\n            },\n            {\n                title:'7'\n            },\n            {\n                title:'8'\n            },\n            {\n                title:'9'\n            },\n            {\n                title:'10'\n            },\n            {\n                title:'11'\n            },\n            {\n                title:'12'\n            },\n            {\n                title:'13'\n            },\n            {\n                title:'14'\n            },\n            {\n                title:'15'\n            },\n            {\n                title:'16'\n            },\n            {\n                title:'17'\n            },\n            {\n                title:'18'\n            },\n            {\n                title:'19'\n            },\n            {\n                title:'20'\n            },\n            {\n                title:'21'\n            },\n            {\n                title:'22'\n            },\n            {\n                title:'23'\n            }],\n            columnSorting:false,\n            allowInsertColumn: false,\n            allowInsertRow: false,\n            allowDeleteColumn: false,\n            allowDeleteRow: false\n        });\n\nfunction runprofile() {\n    \n    if (dhwprofilesheet) {\n        \n        var prof = dhwprofilesheet.getRowData(0);\n        var tot = 0;\n        for (var h in prof) {\n            tot = tot + parseFloat(prof[h]);\n        }\n        if (tot>0) {\n            for (var h in prof) {\n                prof[h] = parseInt(100 * (100/tot) * parseFloat(prof[h])) / 100;\n            }\n        }\n        dhwprofilesheet.setRowData(0, prof);\n    }\n}\n\nfunction resetprofile() {\n    \n    if (dhwprofilesheet) {\n        \n        dhwprofilesheet.setRowData(0, [1.8,1.2,1,0.8,1.3,1.7,4.5,8.7,7.6,6.1,5.4,4.8,4.2,3.6,3.1,3.2,3.7,5.3,7.7,7.3,6.1,4.9,3.7,2.4]);\n    }\n}\n</script>","output":"str","x":430,"y":420,"wires":[["59fb20e9.ae98a"]],"info":"https://bossanova.uk/jspreadsheet/v4/"},{"id":"59fb20e9.ae98a","type":"template","z":"d24faa61.312688","name":"","field":"heads","fieldType":"msg","format":"handlebars","syntax":"plain","template":"<!doctype html>\n<html lang=\"en\">\n  <head>\n    <!-- Required meta tags -->\n    <meta charset=\"utf-8\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1, shrink-to-fit=no\">\n\n    <!-- Bootstrap CSS -->\n    <link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/bootstrap@4.5.3/dist/css/bootstrap.min.css\" integrity=\"sha384-TX8t27EcRE3e/ihU7zmQxVncDAy5uIKz4rEkgIXeMed4M0jlfIDPvg6uqKI2xXr2\" crossorigin=\"anonymous\">\n\n   \n    <script src=\"https://kit.fontawesome.com/c6b21b7a8f.js\" crossorigin=\"anonymous\"></script>\n\n \n    <title>Thermal Integration Heatweb Node</title>\n\n\n  <script src=\"https://code.jquery.com/jquery-3.5.1.js\"></script>\n  <script src=\"https://cdn.datatables.net/1.10.22/js/jquery.dataTables.min.js\" type=\"text/javascript\"></script>\n  <link rel=\"stylesheet\" href=\"https://cdn.datatables.net/1.10.22/css/jquery.dataTables.min.css\">\n\n\n  \n    <style>\n    \n    body, textarea, input, select {\n    font-size: 18px;\n}\n\nsection  {\n    border-bottom: 1px solid rgb(255, 255, 255);\n    padding: 20px 50px;\n    /* height: 100vh; */\n    scroll-snap-align: start;\n    /* text-align: center; */\n    position: relative;\n    background: #ffffff;\n    font-size: 18px;\n    }\n\nsection .table {\n            font-size: 16px;\n        }\n\ntable.dataTable.compact tbody td {\n    \n    padding: 2px;\n}   \n\nsection .jstree-node {\n    font-size: 16px;\n}\n\n.nowrap {\n    white-space: nowrap;\n}\n\n@media (max-width: 1480px) {\n\n    section {\n        \n        padding: 20px 20px;\n        \n        }\n\n}\n\n@media (max-width:480px) {\n\n    section {\n        \n        padding: 10px 8px;\n        \n        }\n\n}\n\n    \n        .maxsiz {\n\n        width: 100%; \n        height: 100vh;\n\n        }\n\n        .maxsiz2 {\n\n        width: 100%; \n        height: calc(100vh - 70px);\n\n        }\n\n        .dot {\n            height: 13px;\n            width: 13px;\n            background-color: rgb(52, 58, 52);\n            border-radius: 20%;\n            border: 2px;\n            border-color: rgb(5, 5, 20);\n            display: inline-block;\n        }\n\n        .thindot {\n            height: 13px;\n            width: 6px;\n            background-color: rgb(52, 58, 52);\n            border-radius: 20%;\n            border: 2px;\n            border-color: rgb(5, 5, 20);\n            display: inline-block;\n        }\n\n        .dotspacer {\n            height: 13px;\n            width: 2px;\n            display: inline-block;\n        }\n\n        .navReadings  {\n            /* border-bottom: 1px solid rgb(32, 28, 28); */\n            padding: 20px 50px 0px 50px;\n            /* background: #6d6d70; */\n            font-size: 80%;   \n            /* font-weight: bold; */\n            text-align: left;\n            color: #212529;   \n        }\n\n        .navReadings a {\n            color: #212529;         \n        }\n\n        \n\n           \n    </style>","output":"str","x":600,"y":420,"wires":[["2c0cb6ba.6d28ea"]]},{"id":"2c0cb6ba.6d28ea","type":"function","z":"d24faa61.312688","name":"","func":"msg.payload = msg.heads + \"<section>\" +  msg.payload.body + \"</section></body></html>\"\n\nreturn msg;","outputs":1,"noerr":0,"x":790,"y":420,"wires":[["dda7c150.c3342"]]},{"id":"dda7c150.c3342","type":"http response","z":"d24faa61.312688","name":"","statusCode":"","headers":{},"x":990,"y":420,"wires":[]}]