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 --- KBWifi 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 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 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