Update debug_panel (#20449)

This commit is contained in:
s-hadinger 2024-01-09 19:59:21 +01:00 committed by GitHub
parent 69d4e323d6
commit 151e201048
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 145 additions and 28 deletions

View File

@ -21,8 +21,6 @@ class debug_panel
var port var port
var web var web
var sampling_interval var sampling_interval
#
var payload1, payload2 # temporary object bytes() to avoid reallocation
static var SAMPLING = 100 static var SAMPLING = 100
static var HTML_HEAD1 = static var HTML_HEAD1 =
@ -73,8 +71,6 @@ class debug_panel
self.port = port self.port = port
self.web = webserver_async(port) self.web = webserver_async(port)
self.sampling_interval = self.SAMPLING 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_chunked(true)
self.web.set_cors(true) self.web.set_cors(true)
@ -110,30 +106,74 @@ class debug_panel
cnx.content_stop() cnx.content_stop()
end 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) def send_info_feed(cnx, uri, verb)
cnx.set_chunked(false) # no chunking since we use EventSource cnx.set_chunked(false) # no chunking since we use EventSource
cnx.send(200, "text/event-stream") cnx.send(200, "text/event-stream")
self.send_info_tick(cnx) #
end var feed = feeder(cnx)
feed.send_feed() # send first values immediately
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 end
# Add button 'GPIO Viewer' redirects to '/part_wiz?' # Add button 'GPIO Viewer' redirects to '/part_wiz?'

View File

@ -125,6 +125,7 @@ class GPIO_viewer
def send_events_page(cnx, uri, verb) def send_events_page(cnx, uri, verb)
cnx.set_chunked(false) # no chunking since we use EventSource cnx.set_chunked(false) # no chunking since we use EventSource
cnx.set_cors(true)
cnx.send(200, "text/event-stream") cnx.send(200, "text/event-stream")
self.send_events_tick(cnx) self.send_events_tick(cnx)

View File

@ -28,6 +28,9 @@
#@ solidify:webserver_async #@ solidify:webserver_async
#@ solidify:Webserver_async_cnx #@ solidify:Webserver_async_cnx
#############################################################
# class Webserver_async_cnx
#############################################################
class Webserver_async_cnx class Webserver_async_cnx
var server # link to server object var server # link to server object
var cnx # holds the tcpclientasync instance var cnx # holds the tcpclientasync instance
@ -46,7 +49,7 @@ class Webserver_async_cnx
var resp_headers var resp_headers
var resp_version var resp_version
var chunked # if true enable chunked encoding (default true) 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 # bytes objects to be reused
var payload1 var payload1
# conversion # conversion
@ -79,7 +82,7 @@ class Webserver_async_cnx
self.resp_headers = '' self.resp_headers = ''
self.resp_version = 1 # HTTP 1.1 # TODO self.resp_version = 1 # HTTP 1.1 # TODO
self.chunked = true self.chunked = true
self.cors = true self.cors = false
# register cb # register cb
self.fastloop_cb = def () self.loop() end self.fastloop_cb = def () self.loop() end
tasmota.add_fast_loop(self.fastloop_cb) tasmota.add_fast_loop(self.fastloop_cb)
@ -384,6 +387,9 @@ class Webserver_async_cnx
end end
end end
#############################################################
# class Webserver_dispatcher
#############################################################
class Webserver_dispatcher class Webserver_dispatcher
var uri_prefix # prefix string, must start with '/' var uri_prefix # prefix string, must start with '/'
var verb # verb to match, or nil for ANY var verb # verb to match, or nil for ANY
@ -412,6 +418,11 @@ class Webserver_dispatcher
end end
end end
#############################################################
# class webserver_async
#
# This is the main class to call
#############################################################
class webserver_async class webserver_async
var local_port # listening port, 80 is already used by Tasmota var local_port # listening port, 80 is already used by Tasmota
var server # instance of `tcpserver` var server # instance of `tcpserver`
@ -424,7 +435,9 @@ class webserver_async
var dispatchers var dispatchers
# copied in each connection # copied in each connection
var chunked # if true enable chunked encoding (default true) 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 TIMEOUT = 1000 # default timeout: 1000ms
static var HTTP_REQ = "^(\\w+) (\\S+) HTTP\\/(\\d\\.\\d)\r\n" static var HTTP_REQ = "^(\\w+) (\\S+) HTTP\\/(\\d\\.\\d)\r\n"
@ -438,6 +451,10 @@ class webserver_async
self.connections = [] self.connections = []
self.dispatchers = [] self.dispatchers = []
self.server = tcpserver(port) # throws an exception if port is not available 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 ? # TODO what about max_clients ?
self.compile_re() self.compile_re()
# register cb # register cb
@ -457,14 +474,73 @@ class webserver_async
end end
end end
#############################################################
# enable or disable chunked mode (enabled by default)
def set_chunked(chunked) def set_chunked(chunked)
self.chunked = bool(chunked) self.chunked = bool(chunked)
end end
#############################################################
# enable or disable CORS mode (enabled by default)
def set_cors(cors) def set_cors(cors)
self.cors = bool(cors) self.cors = bool(cors)
end 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 # closing web server
def close() def close()