mirror of https://github.com/lumapu/ahoy.git
				
				
			
			
			
				Browse Source
			
			
			
			
				
		* converted /update to xhr * started web serial console /serial * /save does not work yet - not debuggedpull/283/head
				 16 changed files with 306 additions and 391 deletions
			
			
		| @ -0,0 +1,3 @@ | |||
| #include "dbg.h" | |||
| 
 | |||
| DBG_CB mCb = NULL; | |||
| @ -0,0 +1,50 @@ | |||
| <!doctype html> | |||
| <html> | |||
|     <head> | |||
|         <title>Serial Console</title> | |||
|         <link rel="stylesheet" type="text/css" href="style.css"/> | |||
|         <meta name="viewport" content="width=device-width, initial-scale=1"> | |||
|         <script type="text/javascript" src="api.js"></script> | |||
|     </head> | |||
|     <body> | |||
|         <h1>Serial Console</h1> | |||
|         <div id="content" class="content"> | |||
|             <textarea rows="20" cols="90" id="serial" readonly></textarea> | |||
|         </div> | |||
|         <div id="footer"> | |||
|             <p class="left">© 2022</p> | |||
|             <p class="left"><a href="/">Home</a></p> | |||
|             <p class="right" id="version"></p> | |||
|         </div> | |||
|         <script type="text/javascript"> | |||
|             function parseSys(obj) { | |||
|                 document.getElementById("version").innerHTML = "Git SHA: " + obj["build"] + " :: " + obj["version"]; | |||
|             } | |||
|             var con = document.getElementById("serial"); | |||
|             if (!!window.EventSource) { | |||
|                 var source = new EventSource('/events'); | |||
| 
 | |||
|                 source.addEventListener('open', function(e) { | |||
|                     //console.log("Events Connected"); | |||
|                 }, false); | |||
| 
 | |||
|                 source.addEventListener('error', function(e) { | |||
|                     if (e.target.readyState != EventSource.OPEN) { | |||
|                         //console.log("Events Disconnected"); | |||
|                     } | |||
|                 }, false); | |||
| 
 | |||
|                 source.addEventListener('serial', function(e) { | |||
|                     //var ascii = ""; | |||
|                     //for(i = 0; i < e.data.length; i++) | |||
|                     //    ascii += e.data.charCodeAt(i).toString(16) + " "; | |||
|                     //console.log(ascii); | |||
|                     con.value += e.data.replace(/\<rn\>/g, '\r\n'); | |||
|                     con.scrollTop = con.scrollHeight; | |||
|                 }, false); | |||
|             } | |||
| 
 | |||
|             getAjax("/api/system", parseSys); | |||
|         </script> | |||
|     </body> | |||
| </html> | |||
| @ -1,43 +1,116 @@ | |||
| <!doctype html> | |||
| <html> | |||
|     <head> | |||
|         <title>Index - {DEVICE}</title> | |||
|         <title>Live</title> | |||
|         <link rel="stylesheet" type="text/css" href="style.css"/> | |||
|         <meta name="viewport" content="width=device-width, initial-scale=1"> | |||
|         <meta name="apple-mobile-web-app-capable" content="yes"> | |||
|         <script type="text/javascript"> | |||
|             getAjax('/livedata', 'livedata'); | |||
|             window.setInterval("getAjax('/livedata', 'livedata')", {JS_TS}); | |||
| 
 | |||
|             function getAjax(url, resid) { | |||
|                 var http = null; | |||
|                 http = new XMLHttpRequest(); | |||
|                 if(http != null) { | |||
|                    http.open("GET", url, true); | |||
|                    http.onreadystatechange = print; | |||
|                    http.send(null); | |||
|                 } | |||
| 
 | |||
|                 function print() { | |||
|                     if(http.readyState == 4) { | |||
|                         document.getElementById(resid).innerHTML = http.responseText; | |||
|                     } | |||
|                 } | |||
|             } | |||
|         </script> | |||
|         <style type="text/css"> | |||
|         </style> | |||
|         <script type="text/javascript" src="api.js"></script> | |||
|     </head> | |||
|     <body> | |||
|         <h1>AHOY - {DEVICE}</h1> | |||
|         <h1>AHOY</h1> | |||
|         <div id="content" class="content"> | |||
|             <div id="livedata"></div> | |||
|             <p>Every {TS}seconds the values are updated</p> | |||
|             <div id="live"></div> | |||
|             <p>Every <span id="refresh"></span> seconds the values are updated</p> | |||
|         </div> | |||
|         <div id="footer"> | |||
|             <p class="left">© 2022</p> | |||
|             <p class="left"><a href="/">Home</a></p> | |||
|             <p class="right">AHOY :: {VERSION}</p> | |||
|             <p class="right" id="version"></p> | |||
|         </div> | |||
|         <script type="text/javascript"> | |||
|             var intervalSet = false; | |||
|             function parseSys(obj) { | |||
|                 document.getElementById("version").innerHTML = "Git SHA: " + obj["build"] + " :: " + obj["version"]; | |||
|             } | |||
| 
 | |||
|             function parseIv(obj, root) { | |||
|                 var ivHtml = []; | |||
| 
 | |||
|                 var tDiv = div(["ch-all", "iv"]); | |||
|                 tDiv.appendChild(span("Total", ["head"])); | |||
|                 var total = new Array(root.ch0_fld_names.length).fill(0); | |||
|                 if(obj.length > 1) | |||
|                     ivHtml.push(tDiv); | |||
| 
 | |||
|                 for(var iv of obj) { | |||
|                     main = div(["iv"]); | |||
|                     var ch0 = div(["ch-iv"]); | |||
|                     var ctrl = (iv["power_limit_active"]) ? "" : " (not controlled)"; | |||
|                     ch0.appendChild(span(iv["name"] + " Limit " + iv["power_limit_read"] + "%" + ctrl + " | last Alarm: " + iv["last_alarm"], ["head"])); | |||
| 
 | |||
|                     for(var j = 0; j < root.ch0_fld_names.length; j++) { | |||
|                         var val = Math.round(iv["ch"][0][j] * 100) / 100; | |||
|                         if(val > 0) { | |||
|                             var sub = div(["subgrp"]); | |||
|                             sub.appendChild(span(val + " " + span(root["ch0_fld_units"][j], ["unit"]).innerHTML, ["value"])); | |||
|                             sub.appendChild(span(root["ch0_fld_names"][j], ["info"])); | |||
|                             ch0.appendChild(sub); | |||
| 
 | |||
|                             switch(j) { | |||
|                                 case 2: total[j]  += val; break; | |||
|                                 case 6: total[j]  += val; break; | |||
|                                 case 7: total[j]  += val; break; | |||
|                                 case 8: total[j]  += val; break; | |||
|                                 case 10: total[j] += val; break; | |||
|                             } | |||
|                         } | |||
|                     } | |||
|                     main.appendChild(ch0); | |||
| 
 | |||
| 
 | |||
|                     for(var i = 1; i < 5; i++) { | |||
|                         var ch = div(["ch"]); | |||
|                         ch.appendChild(span(("" == iv["ch_names"][i]) ? ("CHANNEL " + i) : iv["ch_names"][i], ["head"])); | |||
| 
 | |||
|                         for(var j = 0; j < root.fld_names.length; j++) { | |||
|                             var val = Math.round(iv["ch"][i][j] * 100) / 100; | |||
|                             if(val > 0) { | |||
|                                 ch.appendChild(span(val + " " + span(root["fld_units"][j], ["unit"]).innerHTML, ["value"])); | |||
|                                 ch.appendChild(span(root["fld_names"][j], ["info"])); | |||
|                             } | |||
|                         } | |||
|                         main.appendChild(ch); | |||
|                     } | |||
| 
 | |||
|                     var ts = div(["ts"]); | |||
|                     var date = new Date(iv["ts_last_success"] * 1000); | |||
|                     ts.innerHTML = "Last received data requested at: " + date.toLocaleString('de-DE', {timeZone: 'UTC'}); | |||
|                     main.appendChild(ts); | |||
|                     ivHtml.push(main); | |||
|                 } | |||
| 
 | |||
|                 // total | |||
|                 if(obj.length > 1) { | |||
|                     for(var j = 0; j < root.ch0_fld_names.length; j++) { | |||
|                         var val = total[j]; | |||
|                         if(val > 0) { | |||
|                             var sub = div(["subgrp"]); | |||
|                             sub.appendChild(span(val + " " + span(root["ch0_fld_units"][j], ["unit"]).innerHTML, ["value"])); | |||
|                             sub.appendChild(span(root["ch0_fld_names"][j], ["info"])); | |||
|                             tDiv.appendChild(sub); | |||
|                         } | |||
|                     } | |||
|                 } | |||
| 
 | |||
|                 document.getElementById("live").replaceChildren(...ivHtml); | |||
|             } | |||
| 
 | |||
|             function parse(obj) { | |||
|                 if(null != obj) { | |||
|                     parseSys(obj["system"]); | |||
|                     parseIv(obj["inverter"], obj); | |||
|                     document.getElementById("refresh").innerHTML = obj["refresh_interval"]; | |||
|                     if(false == intervalSet) { | |||
|                         window.setInterval("getAjax('/api/live', parse)", obj["refresh_interval"] * 1000); | |||
|                         intervalSet = true; | |||
|                     } | |||
|                 } | |||
|                 else | |||
|                     document.getElementById("refresh").innerHTML = "n/a"; | |||
|             } | |||
| 
 | |||
|             getAjax("/api/live", parse); | |||
|         </script> | |||
|     </body> | |||
| </html> | |||
|  | |||
| @ -1,108 +0,0 @@ | |||
| //-----------------------------------------------------------------------------
 | |||
| // 2022 Ahoy, https://www.mikrocontroller.net/topic/525778
 | |||
| // Creative Commons - http://creativecommons.org/licenses/by-nc-sa/3.0/de/
 | |||
| //-----------------------------------------------------------------------------
 | |||
| 
 | |||
| #ifndef __TMPL_PROC__ | |||
| #define __TMPL_PROC__ | |||
| 
 | |||
| // HTML template processor, searches keywords and calls callback
 | |||
| // inspired by: https://github.com/plapointe6/EspHtmlTemplateProcessor
 | |||
| 
 | |||
| #include "dbg.h" | |||
| #include <string.h> | |||
| #include <functional> | |||
| #include "ESPAsyncWebServer.h" | |||
| 
 | |||
| #define MAX_BUFFER_SIZE     256 | |||
| #define MAX_KEY_LEN         20 | |||
| enum { ST_NONE = 0, ST_BUF, ST_PROC, ST_START, ST_KEY }; | |||
| 
 | |||
| typedef std::function<String(char* key)> TmplCb; | |||
| 
 | |||
| class tmplProc { | |||
|     public: | |||
|         tmplProc(AsyncWebServerRequest *request, uint32_t bufStartSize = 1000) { | |||
|             // Note: don't choose the bufStartSize to small. A too small start
 | |||
|             //       size will result in fractioned memory and maybe in a zero
 | |||
|             //       increase -> fail (Arduino - cbuf.cpp)
 | |||
|             mRequest  = request; | |||
|             mResponse = request->beginResponseStream("text/html", bufStartSize); | |||
|         } | |||
| 
 | |||
|         ~tmplProc() {} | |||
| 
 | |||
|         void process(const char* tmpl, const uint32_t tmplLen, TmplCb cb) { | |||
|         char* buf = new char[MAX_BUFFER_SIZE]; | |||
|         char* p = buf; | |||
|         uint32_t len = 0, pos = 0, i = 0; | |||
|         uint8_t state = ST_BUF; | |||
|         bool complete = false; | |||
| 
 | |||
|         while (i < tmplLen) { | |||
|             switch (state) { | |||
|                 default: | |||
|                     DPRINTLN(DBG_DEBUG, F("unknown state")); | |||
|                     break; | |||
|                 case ST_BUF: | |||
|                     if(0 != i) { | |||
|                         buf[pos] = '\0'; | |||
|                         mResponse->print(p); | |||
|                     } | |||
|                     pos = 0; | |||
|                     len = ((tmplLen - i) > MAX_BUFFER_SIZE) ? MAX_BUFFER_SIZE : (tmplLen - i); | |||
|                     if((len + i) == tmplLen) | |||
|                         complete = true; | |||
|                     memcpy_P(buf, &tmpl[i], len); | |||
|                     if(len < MAX_BUFFER_SIZE) | |||
|                         buf[len] = '\0'; | |||
|                     p = buf; | |||
|                     state = ST_PROC; | |||
|                     break; | |||
| 
 | |||
|                 case ST_PROC: | |||
|                     if(((pos + MAX_KEY_LEN) >= len) && !complete) | |||
|                         state = ST_BUF; | |||
|                     else if(buf[pos] == '{') | |||
|                         state = ST_START; | |||
|                     break; | |||
| 
 | |||
|                 case ST_START: | |||
|                     if(buf[pos] == '#') { | |||
|                         if(pos != 0) | |||
|                             buf[pos-1] = '\0'; | |||
|                         mResponse->print(p); | |||
|                         p = &buf[pos+1]; | |||
|                         state = ST_KEY; | |||
|                     } | |||
|                     else | |||
|                         state = ST_PROC; | |||
|                     break; | |||
| 
 | |||
|                 case ST_KEY: | |||
|                     if(buf[pos] == '}') { | |||
|                         buf[pos] = '\0'; | |||
|                         mResponse->print((cb)(p)); | |||
|                         p = &buf[pos+1]; | |||
|                         state = ST_PROC; | |||
|                     } | |||
|                     break; | |||
|             } | |||
| 
 | |||
|             if(ST_BUF != state) { | |||
|                 pos++; | |||
|                 i++; | |||
|             } | |||
|         } | |||
| 
 | |||
|         mResponse->print(p); | |||
|         delete[] buf; | |||
|         mRequest->send(mResponse); | |||
|     } | |||
| 
 | |||
|     private: | |||
|         AsyncWebServerRequest *mRequest; | |||
|         AsyncResponseStream *mResponse; | |||
| }; | |||
| 
 | |||
| #endif /*__TMPL_PROC__*/ | |||
					Loading…
					
					
				
		Reference in new issue