From 8a012452e806db9c592433453139b33fa8f53548 Mon Sep 17 00:00:00 2001
From: s-hadinger <49731213+s-hadinger@users.noreply.github.com>
Date: Mon, 8 Jan 2024 23:15:48 +0100
Subject: [PATCH] Berry `debug_panel.tapp` to display real-time heap and wifi
rssi (#20436)
---
CHANGELOG.md | 1 +
tasmota/berry/gpio_viewer/autoexec.be | 18 ++
tasmota/berry/gpio_viewer/debug_panel.be | 179 +++++++++++++++++++
tasmota/berry/gpio_viewer/debug_panel.tapp | Bin 0 -> 23076 bytes
tasmota/berry/gpio_viewer/webserver_async.be | 8 +-
5 files changed, 202 insertions(+), 4 deletions(-)
create mode 100644 tasmota/berry/gpio_viewer/autoexec.be
create mode 100644 tasmota/berry/gpio_viewer/debug_panel.be
create mode 100644 tasmota/berry/gpio_viewer/debug_panel.tapp
diff --git a/CHANGELOG.md b/CHANGELOG.md
index e0221235e520..9a55c15502da 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -15,6 +15,7 @@ All notable changes to this project will be documented in this file.
- Berry GPIO viewer initial version using async webserver (#20416)
- Berry add `string` to `bytes()` (#20420)
- Berry button to dynamically load GPIO Viewer with Berry backend (#20424)
+- Berry `debug_panel.tapp` to display real-time heap and wifi rssi
### Breaking Changed
diff --git a/tasmota/berry/gpio_viewer/autoexec.be b/tasmota/berry/gpio_viewer/autoexec.be
new file mode 100644
index 000000000000..d61c443132fb
--- /dev/null
+++ b/tasmota/berry/gpio_viewer/autoexec.be
@@ -0,0 +1,18 @@
+# demo for debug panel
+#
+# rm debug_panel.tapp; zip -j -0 debug_panel.tapp webserver_async.be debug_panel.be autoexec.be
+import webserver_async
+import debug_panel
+
+if tasmota.version() >= 0xD030002
+ if !tasmota.wifi()['up']
+ tasmota.add_rule("Wifi#Connected", def ()
+ global._debug_panel = debug_panel(5550)
+ tasmota.remove_rule("Wifi#Connected", "debug_panel_start")
+ end, "debug_panel_start")
+ else
+ global._debug_panel = debug_panel(5550)
+ end
+else
+ log("BRY: 'debug_panel' requires Tasmota v13.3.0.2")
+end
diff --git a/tasmota/berry/gpio_viewer/debug_panel.be b/tasmota/berry/gpio_viewer/debug_panel.be
new file mode 100644
index 000000000000..12f7c1c2fafc
--- /dev/null
+++ b/tasmota/berry/gpio_viewer/debug_panel.be
@@ -0,0 +1,179 @@
+#
+# debug_panel.be - implements a small panel on top of the Tasmota UI to view real-time information
+#
+# Copyright (C) 2023 Stephan Hadinger & Theo Arends
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+#
+
+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 =
+ "
"
+ static var HTML_URL_F =
+ ""
+ static var HTML_HEAD2 =
+ ""
+ ""
+ ""
+ static var HTML_CONTENT =
+ ''
+ 'Free Heap --- KB | '
+ 'Wifi RSSI | | '
+ '
'
+ static var HTML_END =
+ ""
+
+ def init(port)
+ 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)
+ self.web.on("/info_feed", self, self.send_info_feed)
+ self.web.on("/info", self, self.send_info_page)
+
+ tasmota.add_driver(self)
+ end
+
+ def close()
+ tasmota.remove_driver(self)
+ self.web.close()
+ end
+
+ def send_info_page(cnx, uri, verb)
+ import string
+
+ var host = cnx.header_host
+ var host_split = string.split(host, ':') # need to make it stronger
+ var ip = host_split[0]
+ var port = 80
+ if size(host_split) > 1
+ port = int(host_split[1])
+ end
+
+ cnx.send(200, "text/html")
+ cnx.write(self.HTML_HEAD1)
+ cnx.write(format(self.HTML_URL_F, ip, port))
+ cnx.write(self.HTML_HEAD2)
+ cnx.write(self.HTML_CONTENT)
+ cnx.write(self.HTML_END)
+
+ cnx.content_stop()
+ 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)
+ end
+
+ # Add button 'GPIO Viewer' redirects to '/part_wiz?'
+ def web_add_console_button()
+ self.send_iframe_code()
+ end
+ def web_add_main_button()
+ self.send_iframe_code()
+ end
+ def web_add_management_button()
+ self.send_iframe_code()
+ end
+ def web_add_config_button()
+ self.send_iframe_code()
+ end
+
+ def send_iframe_code()
+ import webserver
+ var ip = tasmota.wifi().find('ip')
+ if (ip == nil)
+ ip = tasmota.eth().find('ip')
+ end
+ if (ip != nil)
+ webserver.content_send(
+ f''
+ ''
+ '
'
+ '')
+ # f"")
+ end
+ end
+
+end
+
+return debug_panel
+
+# if tasmota
+# global.debug_panel = Debug_panel(8887)
+# end
+
+# return global.debug_panel
diff --git a/tasmota/berry/gpio_viewer/debug_panel.tapp b/tasmota/berry/gpio_viewer/debug_panel.tapp
new file mode 100644
index 0000000000000000000000000000000000000000..31c2f9fc4dbadc001c9a7fa5d179728476da45b4
GIT binary patch
literal 23076
zcmeHPOK&7ca^|jfZI}xfu;B%KFpN|a+HS7c&6i|rIc#byQ#a9YGzf=boM$F5
zr(qnXQO5{I6DM)KJxDq)qxjT3I5~N2bihR-yO)eE)9AFHo7%l)v$3|ZY0PmRj`~4t
z9t2&0gsJ&~Iq8Rq*-gW^n+e!SKg!G~O-|EbXz*t*4MUS9z5F~#!!2`}j7<
zK~Az$lZ*(q3|cSEAOKMgvN3n&7K#2LBM}vWBhi06Gz42fLBY_#_
z>Hf)s!zU+Z_u!d%x_flAdvNmX4*KT(1g+s&sF=8_5oQUlra_!vf^;Bww0Csx0U&no
z?myf=c?SB;NBbuSd&kG-qr)S!YaZ_&o$TLx@^JUaJbrTY`0#kIfmH>6zzD#XRjfVNJzL!&H9(byl&KDv36ckjI`Z+}iAmxCC8iD-(S>9Al8r~BB+UicAR0zF=GG4bNTx!H
z)~q+y6nt^%$B^|wL?Paq(%*@;r<1zf>ckg|i=9D`WoBNJF=s()mDAklvt$NQ{F2jE
zl{QKH%dnF#DkcnfBP{H+WctaVn~D79olys37w2j}qBzTgxDz_8gCq-EK@Y5LWiSY@
z8v+^*bxz0Ois=x{2pEWsX<017UXbO3BpJ0j?bqRwwk3m3Tyh6wakR(17PRk19}pFH
zk|C`jz#I)0
zR?NmKdS4Ebpo@_x;6E8d#_g#1)`IbBdtb+Cie;mJr65QldEV2pXpgjnU0}jDvuCu9i!(K2R_aX{5BtrhI
zso{csh%YD^2F78~YglvEw05m*piOJCbNR^Xe$!N|(+{mO17dBmiMwo5<)r2fs|xOV
zFPU*34N5>KHS-|Fy!lqK2@^FCt>dhzVpa+541GNWLZ5bDAJMcPt
zEdsTqU6#$mL9P-4zqr;gsVz%j&HncIUXtEUl7Zi08U^N3qvh~{b$$wIn=IIyNb3?P
zhieHt%uNhNBF4@t7KHODzFYB0!+e~^$}h_3hsB=_udCTAq~g{oq)6+2Mu$8cj`GVD
zGYo^67MR}9IUHx0Pp5r$Yimi*sae3tKrx|*w$rj{s#4q<)#oXkEN7_U^uVDLhd{Z0
zW`<+%9cEihxll*0w*;(qrmK%-sbwVfOzB&OlGt^ybgH$tlYmtFa}rsmop+4LzG4sJ
zbF-E%_Dcg=>9w`c`(5pQwyfm1ju3UFx}ttEpgxo2kEY?m`!g9C@7Tqx0@!8
z1~M&~CZ09KF6vY{1J2mBf!pYQt5buRV{2_+`tBj8rWQ;_htQM+2nD2R$qa*(4iA0H
zZa4^exRCy?jmJ)z3{GP_#7rTveI4J@aEM+0Ys_Q849{#9Oktx6v!E^nTE?9aph=N|
z88}U-O9Dpu=F(Wv$Di_;nj?>k6|;K7Q7LNp7h82+#Zf!SvWQ+?aiA?3q29bDMhC<}
z59j8>!NwUp_|OeWc?iyMtfZF+nb@(ra;c?J+=|0ga|RjC;c+9@5)Frlwjf}E1FkBxTNut<*M-Z0eISDCdP=|eG%=7DU7#C_ijzyC
z)wz`msD4$kB>3uBA_GgHo`e@ltEUp;);|TmX!y!{nn_K>kkCdsgx$2FWAzJ
zy4sc{!w|Mqb{MwuQ^3tL`r1X}qfynTjr0yl&U=L2ix0x3;XlRIz)|Lnhe!znlM6*Jn4rQwdJdcJzB`NhZ}dgu`HFLTMWCk
z7-(Q@!pd?u5Ea3q6p%bH^P~7^;}p`y6Rro`xCYQ3sT`n~-(S%}LIqt^<=3>JbGb@0
zJA~w)4w7~-Xn^V3ElbZD!yxbUH~jUanp-!#P#abHC9`*dgF0iHt8y@0#li4J`Xc68
zUG!;adCn0Fg&RI!``lj(7hD2G!{_Ut6Lj{2kn!HhD&oi|jYeY{8nGw=-na%Bsb}P$y5+-tx!ww$GXnR$(=;u%5(87y?8=>O*$bwI&5>4aqf6!*o!b8XF-Vf
z?YZSvqhXY`!hE~^R+L7)Su;;vC0>k4wzteHLG0LkWu6@EZ<%WO6zsdInol)3u~j~|
zEb{c&P53H*s`>R{}2!5i7?d38W$PEt1A`484a8QI3H|_Ssb|%nGeLoC?ImnFp{7
z=vyW|6@&~O!C0OJh=)5BD{YUZ*`5M?F;GVhjJJ`GU#SuIR=zN|^DNhSKcc6LfVCx+
zVuUP0t>T$UD@B-O#St~Mn#XA
z)yP_yl?@|Y&s|Hf3vK#r5qdqzE_P#%DIbZBOQ74gqqbkc6V+XXpwBSZ25r1o;G}B~
z!9zZ{(+NlT1(5g@ny1Tyv}?+=GftebS^wm2G0vo|^$m~(#JaVkQy4gS=;<9OM?4(i0tp!USU**WPAp?i}L*a3N-R!u&YmLr#(Ri5A(P2NoZnl`sy
zh6oedzhT73p>r>MTW>=^mdgOa4pzcr|AHBJ4+P0owPFKakOyxl#t|zI;3uP*?6nJG
zaWQ8FR6aG*OBdl+$2(CZGLJuImz$v`e+{Y`u&HUb8;x^0Y1I(Tw??7pWN`#>=_*qL
zGZB3t>!!A`LUYl?Im}r+;-nK~yJEp^dX(T=Dq0jfz#hUtT3;FwS_I5+yqUx;y+!&p
zz}Rqb;@a8`k}eUPAMYC-CXOU*#P2sz5~mBAcA@|B=GbnwDma&S?Pgw!gCR1#q7f@b
z&i%?stfPDrMp+%0N(l8TuLykw4^lOX+mmw-%M|XFy2jo?UHCqWZ07OR9Q+oUe+;9b
zaZ1R#beYE2gk~#;H&J}kNzu9CxXg1wi+dG%!8nz8L_4R}ydt(M>m2j%R-Ux%ah6|~
zuO^N%7+mtGGAwO~i!)C?Hs+BTf;v2p;r95T##H%t3x=(cc*K5dvyS
zS0?(6VIQb<^~Wuzm9Sb%CpLH8n1(}eUO2;W5C|786ru;`b4J)_dQ?jJn&}MTWnw~L
zmPGjFPw6K?$W|Jh*W7sCK4-tV4xb*#GFtp^Zt=7LNM-?QNT|-JOu*3&*!){n^|wlq)Q_At^{MC>)E_k<
zD92#zQ>F*!AKad^5!n&!Ot_`NA=k9Q?LQcfajEOv1U>8rB|+;cZ?S_y96ilq9=
zQCWoh+#YT9*Ct9`H#N}Ho5W-`m8`x+7v>t#5!ETh=WFw~Sl(Zkh>4K+Jh8$DuBeLY
z7L?_zhLEK#EkaFWxt9CWL2RtLjVTbNpq;J)bRJSsjRx1Kg^+VEx6&EE
zVP2qalB}$*vNVQDK_APh=hyv)+_EDOrl%-*m94QJ{Eyhl}g-WaP%P{ZLxRGV7
z^{I)Er=4H~+AXxhC9;Q1}WefMlK9F!52kPaDC3Kh|1%M%&YX6$TqKs
zD`lDz-|5;{oheGqgF#DpMij68uz=Mj{*Y--Q3d?L8YkxRVlE1EqA3y3L|)qJu-9UC
zjjT9>CZK+m@t-eH+p5rCSX_SY-N9V<8$K62jxi2AjxGmnk>$}aWD4{qpCzh@Rz+GU
z)*gU9dZX6Kv1rQ&Utck)9OYaC_(@1PdcZ)Zd(k(e)AdGy?NClqvIs0kyvaP-M=mWG
zLcr;^!AGk?rgk^TP;t47#${TVFJNN6Si!2%&OlAg7hra}HA2<&d6IU&U?D~uIki7~
zub56}c(Y1XC50)IaXJ8FhN!@#oKvQp2~gjY-t~x;_aGw-aNsSu=+@*nwn7#Y@Ty!-
z_8;x>CIa#)Yh|8bYGG$&bxV4SGqWQ>v%%OzPN|(9=kDSCXLc0BafF_!c~5?wi6R5*oBP<)1+i)dtN8h#u4eM8hvr7UW*t9}cb$Eqc7PZzHS11#Ydh16-Wh|!w|Q&A`bFDgKxt8@&Q`v#P#
zDKWM@5Ia@67EzZ4n0f<2Gt7Ll?d8clIHxHD1Wy5IjiK`iv*&f0iEFW;Tw`Omyo246WZrtH!ro*AYxy7w7-mvy9SkU#jQZ%
zHA2gHg$~&v8+Vgck$-IbVmb`5xKuYMyg#`}7IJ@W@?TXOy=Gtg;DZkkxToco1Ptp^
zbP*!*q9m$U*&gjY2$3x@IDO&*^sDGs@ou|F(cxh&r7W!Bm_MWzr4CO4qCmDN5Oh71jMbL2h2==##)+G-Ec|c$%y|XEWbf(MA4rKU&V9fI-x}s+
z#Bc~=55|U(%#(c@owEog81>hKI+qJOx&|D0^W(R#M&OnE-@Y2bd+h`dXi~oR|M%4h
znpE(V1FB3D$iuk(6Dn%|$^yPzB0j}s8Wgx8l1#mK0D2jHeZ^(EHs~36ANY>AH<+XW
zE~Dr@YnQ~MVg+Z36oX=SKrC3UQUS3SfH~fM^!TA(s)mi6Y=wXXzM<(vjWK3oI
z?fZxKPM$s9Lm27N!yRkFc>LU9#e#=efS(*aY<*M$XPq<}<14FaNimhXLQY#2p
z#y|pI?V&7CgC|Iu_vn?uLV(!i`qf(uiY}~
z(;XF`>UPfCn*eE6*4Ni6J0CH;AacKr!ZrjhyUkL}ck1=J`Q$EWL(|UoYTliL=10Kt
z6yjozj*s_EiN!5+1z6tkQGkR=T&o}t$T=&)KW%094({8zV@-4!q{2xI8hAplP!g>Y
zp%o^{*+wF;O=tV<;e5U`JOw$kGe8dnr8C>!AaTkau9FZ3nZ
zOn%LvqCx#7QI)vpi2A2st>~hD$~=l@9)Rbxi4R+B22dJ86^Cz>kri!pmOZ2e;B_#g
z;MA3tx#Pl|*tIy*F0M3tDf_x$U@^Bc*5+XnKVJwZ5pz$Rvzcz09ybVEn#*GW?E>EY
zV_z_%9tOX(GWZj$-?h1M`C4NOZXIk@tyrK?-QLO>1~1`sOJW_bhLXYuMHX=zDlxhn
zOU{!);|F@;f~}SMoU*T6cM+mdq{FOz6S|QL5lSBJFX);7(9xMI
z21Md(k7WOK`Q(~`6d^HmjrcX(8&vbNHp|;oWC0Y*Bws`NKy}DR7!2(ZU${^bh3xQ4
z62-~R6<)uC#5XyPy%aq0E(@MwAs|dJp&3P$jNdY}Xy=Y)Y>y~$z=ceg?<9`$QxTSI(MuAi?V4V6
zmpq&j5U(w3`AL;jw1@%Md)Ah(uxAmAkBL0eAd9UCv*J?BhTD~)%X2HUAvSTkd>Xtt
zPtJzDi@E${jFOi8k{6Zg_3HA~J2#jLF&FCW_+Ca~RKliDJ6>D{r8aW}kSIMV#JvE7
zD)H{V93h43$B*|9&8LVTglQG~TNf{0>L7cGvrl!E3Fp>%^wo!SI;e8kZd$bRP)xiw
zE^H|h8x!{AzlWD8hUnGRgeRvoiC~DZ*_%MeF#L4we|a33LN7Y~Ch&F&%HXLPLVvZk
z3LMhtoQNNFPWAw|mJ<>ZjpW+2=zfjev}{Ts@sN9mgF~HEAy@^Nvb6xWFD|3gywCscph$sl
z+}tu79}pC8*uV=Jk{Is>iY~$~Ujs>})@;5fn2|t9dnnf4GX1FA4PyX6m|%W&FzJI3
zm!(5VdLRBUN)A~}4tutxfAVFPpVjJU!>QA{vZ9pL?MN2JWND{au_xkJ(df!vN9FEy
zC5PjR!67$+L5okDsBfZ7-bh1lf!2b%yfK26!o%;sq)`{voOOM@vD`YBv
z-*I#0cY;h2zN(Z@&Zf`z2?;GF(iuQ3A5r3oKKSa9AX`|C#|R9yn4Z*n+Hog>^n;5Ezxe$J
I&-m^C0C^!5F8}}l
literal 0
HcmV?d00001
diff --git a/tasmota/berry/gpio_viewer/webserver_async.be b/tasmota/berry/gpio_viewer/webserver_async.be
index 640f79d30b88..2801484eda33 100644
--- a/tasmota/berry/gpio_viewer/webserver_async.be
+++ b/tasmota/berry/gpio_viewer/webserver_async.be
@@ -198,7 +198,7 @@ class Webserver_async_cnx
# pre: self.buf_in is not empty
# post: self.buf_in has made progress (smaller or '')
def parse()
- tasmota.log(f"WEB: incoming {bytes().fromstring(self.buf_in).tohex()}", 3)
+ # tasmota.log(f"WEB: incoming {bytes().fromstring(self.buf_in).tohex()}", 3)
if self.phase == 0
self.parse_http_req_line()
elif self.phase == 1
@@ -274,7 +274,7 @@ class Webserver_async_cnx
#
# Received header
def event_http_header(header_key, header_value)
- tasmota.log(f"WEB: header key '{header_key}' = '{header_value}'")
+ # tasmota.log(f"WEB: header key '{header_key}' = '{header_value}'")
if (header_key == "Host")
self.header_host = header_value
@@ -291,7 +291,7 @@ class Webserver_async_cnx
#############################################################
# parse incoming payload (if any)
def parse_http_payload()
- tasmota.log(f"WEB: parsing payload '{bytes().fromstring(self.buf_in).tohex()}'")
+ # tasmota.log(f"WEB: parsing payload '{bytes().fromstring(self.buf_in).tohex()}'")
# dispatch request before parsing payload
self.server.dispatch(self, self.req_uri, self.req_verb)
end
@@ -538,7 +538,7 @@ class webserver_async
end
-#return webserver_async
+return webserver_async
#- Test