From 151e201048d1aaa19c6c3263004ba76e6fb04bfc Mon Sep 17 00:00:00 2001 From: s-hadinger <49731213+s-hadinger@users.noreply.github.com> Date: Tue, 9 Jan 2024 19:59:21 +0100 Subject: [PATCH] Update debug_panel (#20449) --- tasmota/berry/gpio_viewer/debug_panel.be | 90 +++++++++++++------ tasmota/berry/gpio_viewer/debug_panel.tapp | Bin 23076 -> 26546 bytes tasmota/berry/gpio_viewer/gpioviewer.be | 1 + tasmota/berry/gpio_viewer/webserver_async.be | 82 ++++++++++++++++- 4 files changed, 145 insertions(+), 28 deletions(-) diff --git a/tasmota/berry/gpio_viewer/debug_panel.be b/tasmota/berry/gpio_viewer/debug_panel.be index 12f7c1c2f..31759a72f 100644 --- a/tasmota/berry/gpio_viewer/debug_panel.be +++ b/tasmota/berry/gpio_viewer/debug_panel.be @@ -21,8 +21,6 @@ class debug_panel var port var web var sampling_interval - # - var payload1, payload2 # temporary object bytes() to avoid reallocation static var SAMPLING = 100 static var HTML_HEAD1 = @@ -73,8 +71,6 @@ class debug_panel self.port = port self.web = webserver_async(port) self.sampling_interval = self.SAMPLING - self.payload1 = bytes(100) # reserve 100 bytes by default - self.payload2 = bytes(100) # reserve 100 bytes by default self.web.set_chunked(true) self.web.set_cors(true) @@ -110,30 +106,74 @@ class debug_panel cnx.content_stop() end + static class feeder + var cnx # connection object + + def init(cnx) + self.cnx = cnx + tasmota.add_driver(self) + end + + def close() + tasmota.remove_driver(self) + end + + def every_100ms() + self.send_feed() + end + + def send_feed() + var cnx = self.cnx + if !cnx.connected() + self.close() + return + end + + var payload1 = self.cnx.server.payload1 + var payload2 = self.cnx.server.payload2 + var server = self.cnx.server + if cnx.buf_out_empty() + # if out buffer is not empty, do not send any new information + # var payload + # send free heap + payload1.clear() + payload1 .. "id:" + server.bytes_format_int(payload2, tasmota.millis()) + payload1 .. payload2 + payload1 .. "\r\nevent:free_heap\r\ndata:" + server.bytes_format_int(payload2, tasmota.memory('heap_free'), '---') + payload1 .. payload2 + payload1 .. " KB\r\n\r\n" + # payload = f"id:{tasmota.millis()}\r\n" + # "event:free_heap\r\n" + # "data:{tasmota.memory().find('heap_free', 0)} KB\r\n\r\n" + cnx.write(payload1) + + # send wifi rssi + payload1.clear() + payload1 .. "id:" + server.bytes_format_int(payload2, tasmota.millis()) + payload1 .. payload2 + payload1 .. "\r\nevent:wifi_rssi\r\ndata:" + server.bytes_format_int(payload2, tasmota.wifi('quality'), '--') + payload1 .. payload2 + payload1 .. "%\r\n\r\n" + + # payload = f"id:{tasmota.millis()}\r\n" + # "event:wifi_rssi\r\n" + # "data:{tasmota.wifi().find('quality', '--')}%\r\n\r\n" + cnx.write(payload1) + end + end + + end + def send_info_feed(cnx, uri, verb) cnx.set_chunked(false) # no chunking since we use EventSource cnx.send(200, "text/event-stream") - self.send_info_tick(cnx) - end - - def send_info_tick(cnx) - if cnx.buf_out_empty() - # if out buffer is not empty, do not send any new information - var payload - # send free heap - payload = f"id:{tasmota.millis()}\r\n" - "event:free_heap\r\n" - "data:{tasmota.memory().find('heap_free', 0)} KB\r\n\r\n" - cnx.write(payload) - - # send wifi rssi - payload = f"id:{tasmota.millis()}\r\n" - "event:wifi_rssi\r\n" - "data:{tasmota.wifi().find('quality', '--')}%\r\n\r\n" - cnx.write(payload) - end - - tasmota.set_timer(self.sampling_interval, def () self.send_info_tick(cnx) end) + # + var feed = feeder(cnx) + feed.send_feed() # send first values immediately end # Add button 'GPIO Viewer' redirects to '/part_wiz?' diff --git a/tasmota/berry/gpio_viewer/debug_panel.tapp b/tasmota/berry/gpio_viewer/debug_panel.tapp index 31c2f9fc4dbadc001c9a7fa5d179728476da45b4..f7a886133f616d9f9ce805df49345b005539dc8c 100644 GIT binary patch delta 3022 zcmc&$-ES0C6yJ9H0o&L{YNZuOPg!tx(ste57Q3mmrcfGa16{BJ70h~f=5Bjoc4nCm z+EtQGpG@?@$c^w+NRS6#Ow=_#nUJW7G4bO;)bqwf3JrxHuaMU7UTpcSrlROY8LAOY0x* z{XWe97?^C10`F>hO59evS{B1C1)|&4v+djcyEh_l4v57aQBZYa+i-&BZEBsTR!(Df zi8$&qwIb5JXXr+=xOpxxU8$K8yKJbDXe0^?%ghG+bCxL-2{U|D#{`w=x(D<_;*)Tj z$b%~Uz3CyIZF!9UI6YCll0MhW)yS?^xyEm7-o}5My-@AglWpen1N+kk$?FtM3otWz zt1i37S*k(N)F{NMT-P9928|Yot2+w66Pe+!b?i%eYxTbS=a0Sc0LQ703zdUZFHs8$ zuAw^2GysdBhALt)!=X#KL~K~5s}OffXs8k_C<0(R#9^w)0aJ0Pohz7DkvKWDjOT}d z4b{>^V}N`@)hV&!!WDoOAkJW70t{Bs6|oG7MBpF{Eb6$H0hoy<9im_pgE?iujZ)9n zVeEyZ@CQVLVJ7YZ7cWMz9stU9VFn0UqAIHp9XHLUfceAWLq`rBS*U}@6eWH>VNsjC zO5-V+?WyF;7%#axl0={Y8UssM|5>sU-#L=psYE1V4I|@`aGqG8(~2RGq`}HE!<}a^ z=|2Mq6OAn~2W=QUjS-VG($u5STiOe$q=zWV%AeAyQOj=7bN78HiqXHV8CGN+W8hmN zEm+o=tZp;~@z_*MfnkggK=>O1n7RRj6Oan>K^NT9$w?UXk_q-9iUeV!A;YGnVVB7y z13Gp3xZ#-#cBEOHmpTqVoGMYE2zNgv;_4$Qv0jK3*;yjhy4{tji!Cwe5{myk#gb(c z%Y{dfXq~@fxfG)WWCfzFc&ZM9-=*6BTC|nBRhyF1M|~8@Du`Z?9W#=KJacdu8hD8f zr&FV2qnVLsN5^6{wuDd~R^q#TrB#rV2Pyg9;(Mmn33Yr1WXpUSP}9@?y9PArI)d^4 zM!5IXEq|+XxH_HrW>aU+Rpn&I+|5#dUntby$NQ)H+JD7E>-MGf>hn`)!qwIT-?moS z+(5YB*ROXl6(|jwL?}%)R{2^>bP^9E(=ezvq0Rgmyz_>Nn7<4sV;n7mAz)LzkichT z9j{m;Ke1W}dk)Z#&!E0lY zC&qi5F>iM~%4d4I`Ir6Oyr^ueU@izD4&6kFly#G6!R!!~`mfTMbddjFtkeE(32P7M zZd>03+ek#qyM>(TIyqV_Ib~j&-^^F$XSRr)t&1r5`D%U#e@E%#x0J3|m^QxChY@wl zd>c+UF;{|Yhrl6r(R4_n$aI|{VZkOqP*F-8ZGpWAc2n6}L5>Y)ykJp^2PP?iO0PC&+#A#BCr;n;n1*(BH(d}1=)Pr}?jk%0K$s65JV%_p0pd`nir zYbHC#*RtJhDS4Lh%h~^huN78cIm@=$zv4@v#AD}NqT>gJ&(-~@l1znts(N;B5LMGi zz|=32skWmB1yfBaezfO_b~F-Q!e2jf`K74z5Xuuhgxd9beWP>5bA6NH4Dx??wjc}? zm}NVtUEQTPaf?N&G2&3YT>bQDYioGzn8Lp}KG6PI*E;>YYrT5=c&fRx@za?PE)ItA z+nK+=xINQ2p+cd~P_O*j7MJU5KjXKyM)U4cu0#x~C-U^gh1O6QXP9u7!Ma=_*wN8%%)TS%kK$1p)OZVX=aB delta 644 zcmdmVo^i<*Mu`A#W)?065D49-5i#SM@#)+}(G7KrHNu}xW1&Mj7IeJN{p&^_M%pZ#8q<$)z zvsuhLi+OUjiT31;Cia{A{0^{A?lzyc*)K$qd2&qL6!GHJoHV`S)V!4V%)GSx_>#=z zY>ni+ipjB|l3Xy3CfDTJNX5wuB3(4f5{ncH5-W4^6H~Ypz(836q+21aC^c0fBQ>!A zB0RZ0)?o6qXtl`)qCG@_=INCeWtOCBK(%UeaZOf?k)7NTt3P>nEJ%^~;Rh21va)Mu{bxsBoXM|5}-?SQ;Rep&QHuO$jQu02YR6-wWusHM@JzgHBCW76X-Oo zzS9I5sR=X^YO=C|V@irb(&p&+FD%+>JAm=G{^V&8UFlTdBiXjspYJ^YTnWoJQjPc3V=`t3WaU}sxBOqc)BfBF5M$EB-V-BR58R(G7 bQR&j++rt=89RO4Vbim}EbOkoSNRUwg$Zy$= diff --git a/tasmota/berry/gpio_viewer/gpioviewer.be b/tasmota/berry/gpio_viewer/gpioviewer.be index 91df7624b..58bb566a9 100644 --- a/tasmota/berry/gpio_viewer/gpioviewer.be +++ b/tasmota/berry/gpio_viewer/gpioviewer.be @@ -125,6 +125,7 @@ class GPIO_viewer def send_events_page(cnx, uri, verb) cnx.set_chunked(false) # no chunking since we use EventSource + cnx.set_cors(true) cnx.send(200, "text/event-stream") self.send_events_tick(cnx) diff --git a/tasmota/berry/gpio_viewer/webserver_async.be b/tasmota/berry/gpio_viewer/webserver_async.be index 2801484ed..fadbb3196 100644 --- a/tasmota/berry/gpio_viewer/webserver_async.be +++ b/tasmota/berry/gpio_viewer/webserver_async.be @@ -28,6 +28,9 @@ #@ solidify:webserver_async #@ solidify:Webserver_async_cnx +############################################################# +# class Webserver_async_cnx +############################################################# class Webserver_async_cnx var server # link to server object var cnx # holds the tcpclientasync instance @@ -46,7 +49,7 @@ class Webserver_async_cnx var resp_headers var resp_version var chunked # if true enable chunked encoding (default true) - var cors # if true send CORS headers (default true) + var cors # if true send CORS headers (default false) # bytes objects to be reused var payload1 # conversion @@ -79,7 +82,7 @@ class Webserver_async_cnx self.resp_headers = '' self.resp_version = 1 # HTTP 1.1 # TODO self.chunked = true - self.cors = true + self.cors = false # register cb self.fastloop_cb = def () self.loop() end tasmota.add_fast_loop(self.fastloop_cb) @@ -384,6 +387,9 @@ class Webserver_async_cnx end end +############################################################# +# class Webserver_dispatcher +############################################################# class Webserver_dispatcher var uri_prefix # prefix string, must start with '/' var verb # verb to match, or nil for ANY @@ -412,6 +418,11 @@ class Webserver_dispatcher end end +############################################################# +# class webserver_async +# +# This is the main class to call +############################################################# class webserver_async var local_port # listening port, 80 is already used by Tasmota var server # instance of `tcpserver` @@ -424,7 +435,9 @@ class webserver_async var dispatchers # copied in each connection var chunked # if true enable chunked encoding (default true) - var cors # if true send CORS headers (default true) + var cors # if true send CORS headers (default false) + # + var payload1, payload2 # temporary object bytes() to avoid reallocation static var TIMEOUT = 1000 # default timeout: 1000ms static var HTTP_REQ = "^(\\w+) (\\S+) HTTP\\/(\\d\\.\\d)\r\n" @@ -438,6 +451,10 @@ class webserver_async self.connections = [] self.dispatchers = [] self.server = tcpserver(port) # throws an exception if port is not available + self.chunked = true + self.cors = false + self.payload1 = bytes(100) # reserve 100 bytes by default + self.payload2 = bytes(100) # reserve 100 bytes by default # TODO what about max_clients ? self.compile_re() # register cb @@ -457,14 +474,73 @@ class webserver_async end end + ############################################################# + # enable or disable chunked mode (enabled by default) def set_chunked(chunked) self.chunked = bool(chunked) end + ############################################################# + # enable or disable CORS mode (enabled by default) def set_cors(cors) self.cors = bool(cors) end + ############################################################# + # Helper function to encode integer as hex (uppercase) + static def bytes_format_hex(b, i, default) + b.clear() + if (i == nil) b .. default return end + # sanity check + if (i < 0) i = -i end + if (i < 0) return end # special case for MININT + if (i == 0) b.resize(1) b[0] = 0x30 return end # return bytes("30") + + b.resize(8) + var len = 0 + while i > 0 + var digit = i & 0x0F + if (digit < 10) + b[len] = 0x30 + digit + else + b[len] = 0x37 + digit # 0x37 = 0x41 ('A') - 10 + end + len += 1 + i = (i >> 4) + end + # reverse order + b.resize(len) + b.reverse() + end + + ############################################################# + # Helper function to encode integer as int + static def bytes_format_int(b, i, default) + b.clear() + if (i == nil) b .. default return end + var negative = false + # sanity check + if (i < 0) i = -i negative = true end + if (i < 0) return end # special case for MININT + if (i == 0) b.resize(1) b[0] = 0x30 return end # return bytes("30") + + b.resize(11) # max size for 32 bits ints '-2147483647' + var len = 0 + while i > 0 + var digit = i % 10 + b[len] = 0x30 + digit + len += 1 + i = (i / 10) + end + if negative + b[len] = 0x2D + len += 1 + end + # reverse order + b.resize(len) + b.reverse() + end + ############################################################# # closing web server def close()