From 4490284fb4c90fdb7681ab1eff3926daad8076b1 Mon Sep 17 00:00:00 2001
From: Theo Arends <11044339+arendst@users.noreply.github.com>
Date: Fri, 5 Sep 2025 22:35:42 +0200
Subject: [PATCH] Enhance berry example with full list of active devices
---
tasmota/berry/examples/statedata.be | 151 +++++++++++++++++++++++-----
1 file changed, 128 insertions(+), 23 deletions(-)
diff --git a/tasmota/berry/examples/statedata.be b/tasmota/berry/examples/statedata.be
index 860636c9c..f208abc01 100644
--- a/tasmota/berry/examples/statedata.be
+++ b/tasmota/berry/examples/statedata.be
@@ -1,15 +1,30 @@
-# Show hostname and IP address from MQTT STATE messages in GUI
+#-
+ Show topic/hostname and IP address with uptime from MQTT STATE messages in GUI
+
+ Enable either
+ self.line_option = 1 : Scroll 'line_cnt' lines
+ or
+ self.line_option = 2 : Show devices updating within 'line_teleperiod'
+-#
import mqtt
import string
class mqttdata_cls
- var list_buffer # Buffer storing lines
+ var line_option # Line option
var line_cnt # Number of lines
+ var line_teleperiod # Skip any device taking longer to respond (probably offline)
+ var line_duration # Duration option
+ var list_buffer # Buffer storing lines
def init()
+# self.line_option = 1 # Scroll line_cnt lines
+ self.line_option = 2 # Show devices updating within line_teleperiod
+ self.line_cnt = 10 # Option 1 number of lines to show
+ self.line_teleperiod = 1200 # Option 2 number of teleperiod for devices to be shown
+ self.line_duration = 0 # Show duration of last state message (1)
+
self.list_buffer = [] # Init line buffer list
- self.line_cnt = 10 # Number of lines to show
if global.mqttdata_driver
global.mqttdata_driver.stop() # Let previous instance bail out cleanly
@@ -27,37 +42,127 @@ class mqttdata_cls
var subtopic = string.split(full_topic, "/") # Assume default Fulltopic (%prefix%/%topic%/)
var topic = subtopic[1] # wemos7
- var line
var state = json.load(data)
- if state
- var uptime = state['Uptime']
+ if state # Valid JSON state message
+ var sub_option = 1
+ var ipaddress = "" # Not used
+ var uptime = state['Uptime'] # 129T10:52:41
if state.find('Hostname')
- var hostname = state['Hostname']
- var ipaddress = state['IPAddress']
- line = format("
| %s | %s | %s |
",
- hostname, hostname, ipaddress, ipaddress, uptime)
- else
- line = format("| %s |   | %s |
",
- topic, uptime)
+ sub_option = 2
+ topic = state['Hostname'] # wemos7
+ ipaddress = state['IPAddress'] # 192.168.2.123
end
- end
- self.list_buffer.push(line) # Add as last entry
- if self.list_buffer.size() > self.line_cnt # Max number of lines in buffer
- self.list_buffer.remove(0) # Remove first entry
+ var last_seen = tasmota.rtc('local')
+ var line = format("%s,%s,%s,%d,%d", topic, ipaddress, uptime, last_seen, sub_option)
+
+ if 1 == self.line_option
+ self.list_buffer.push(line) # Add state as last entry
+ if self.list_buffer.size() > self.line_cnt # Max number of lines in buffer
+ self.list_buffer.remove(0) # Remove first entry
+ end
+ elif 2 == self.line_option
+ if self.list_buffer.size()
+ var i = 0
+ var list_size = size(self.list_buffer)
+ while i < list_size # Use while loop as counter is decremented
+ if 0 == string.find(self.list_buffer[i], topic)
+ self.list_buffer.remove(i) # Remove current state
+ list_size -= 1 # Continue for duplicates
+ end
+ i += 1
+ end
+ end
+ self.list_buffer.push(line) # Add state as last entry
+ end
+
end
end
return true
end
+ def sort(l, cmp) # Sort list
+ for i:1..size(l)-1
+ var k = l[i]
+ var j = i
+ while (j > 0) && !cmp(l[j-1], k)
+ l[j] = l[j-1]
+ j -= 1
+ end
+ l[j] = k
+ end
+ return l
+ end
+
+ def dhm(last_time) # Duration
+ var since = tasmota.rtc('local') - last_time
+ var unit = "d"
+ if since > (24 * 3600)
+ since /= (24 * 3600)
+ if since > 99 since = 99 end
+ elif since > 3600
+ since /= 3600
+ unit = "h"
+ else
+ since /= 60
+ unit = "m"
+ end
+ return format("%02d%s", since, unit)
+ end
+
def web_sensor()
if self.list_buffer.size()
- var msg = format("{t}") # Terminate two column table and open new table
- for i: self.list_buffer.keys()
- msg += self.list_buffer[i]
+ var msg = ""
+
+ if 2 == self.line_option
+ # Sort list
+ var less = /a,b -> a < b
+ self.sort(self.list_buffer, less)
+ end
+
+ var stx = false # If list_buffer is empty due to removes show nothing
+ var time_window = tasmota.rtc('local') - self.line_teleperiod
+ var i = 0
+ var j = size(self.list_buffer)
+ while i < j
+ var splits = string.split(self.list_buffer[i], ",")
+ var last_seen = int(splits[3])
+
+ if time_window > last_seen # Remove offline devices
+ self.list_buffer.remove(i)
+ j -= 1
+ continue
+ end
+
+ var topic = splits[0] # topic or hostname
+ var ipaddress = splits[1]
+ var uptime = splits[2]
+ var sub_option = int(splits[4])
+
+ if !stx
+ stx = true
+ msg = format("{t}") # Terminate two column table and open new table
+ end
+# msg += format("", 90 - (self.line_duration * 10))
+ msg += "
"
+ if 1 == sub_option
+ msg += format("| %s |   | %s | ",
+ topic, uptime)
+ elif 2 == sub_option
+ msg += format("%s | %s | %s | ",
+ topic, topic, ipaddress, ipaddress, uptime)
+ end
+ if self.line_duration
+ msg += format("🕗%s | ", # Clock
+ self.dhm(last_seen))
+ end
+ msg += "
"
+ i += 1
+ end
+ if stx
+ msg += "{t}" # Terminate three column table and open new table
+ tasmota.web_send(msg) # Do not use tasmota.web_send_decimal() which will replace IPAddress dots
+ tasmota.web_send_decimal("") # Force horizontal line
end
- msg += "{t}" # Terminate three column table and open new table
- tasmota.web_send(msg) # Do not use tasmota.web_send_decimal() which will replace IPAddress dots
- tasmota.web_send_decimal("") # Force horizontal line
end
end
end