Adding visitor stats app

This commit is contained in:
Dan Baker 2026-02-21 16:47:13 +00:00
commit 690069aa16
2 changed files with 157 additions and 0 deletions

157
visitor-stats/__init__.py Normal file
View file

@ -0,0 +1,157 @@
APP_DIR = "/system/apps/visitor_stats"
import sys
import os
os.chdir(APP_DIR)
sys.path.insert(0, APP_DIR)
from badgeware import run, State, rtc, set_brightness, clamp
import wifi
import urequests
# Enable RTC interrupts for hourly refresh
rtc.enable_timer_interrupt(True)
rtc.set_timer(150)
class AppState:
Display = 0
ConnectWiFi = 1
FetchData = 2
Error = 3
# Default state
state = {
"total_hits": 0,
"unique_visitors": 0,
"last_updated": "Never",
"font": "ignore",
"brightness": 0.1,
}
State.load("visitor_stats", state)
set_brightness(state["brightness"])
font_list = dir(rom_font)
font_index = font_list.index(state["font"])
app_state = AppState.Display
error_message = ""
API_URL = "https://api.ritual.sh/analytics/stats"
def fetch_stats():
try:
print(f"Fetching from {API_URL}")
response = urequests.get(API_URL)
print(f"Response status: {response.status_code}")
if response.status_code == 200:
data = response.json()
print(f"Data received: {data}")
response.close()
return data
response.close()
except Exception as e:
print(f"Fetch error: {e}")
return None
def format_number(n):
if n >= 1_000_000:
value = n / 1000000
return f"{value:.1f}".rstrip("0").rstrip(".") + "m"
elif n >= 1_000:
value = n / 1000
return f"{value:.1f}".rstrip("0").rstrip(".") + "k"
else:
return str(n)
def draw_display():
# Pen variables
screen.pen = color.white
## Variables
visitor_count = format_number(state["total_hits"])
unique_count = format_number(state["unique_visitors"]) + "d"
screen.font = rom_font.troll
visitor_size = screen.measure_text(visitor_count)
visitor_x = (screen.width - visitor_size[0]) // 2
screen.text(visitor_count, vec2(visitor_x, -4))
screen.font = rom_font.desert
unique_size = screen.measure_text(unique_count)
unique_x = (screen.width - unique_size[0]) // 2
screen.text(unique_count, vec2(unique_x, visitor_size[1] - 10))
def update():
"""Main update loop."""
global app_state, state, error_message, font_index
wifi.tick()
# Brightness controls
if io.BUTTON_UP in io.pressed:
state["brightness"] += 0.1
if io.BUTTON_DOWN in io.pressed:
state["brightness"] -= 0.1
state["brightness"] = clamp(state["brightness"], 0.1, 1.0)
set_brightness(state["brightness"])
# Manual refresh with button B
if io.BUTTON_B in io.pressed:
print("Button B pressed - manual refresh")
app_state = AppState.ConnectWiFi
if rtc.read_timer_flag():
print("RTC timer fired - auto refresh")
rtc.clear_timer_flag()
rtc.set_timer(150)
app_state = AppState.ConnectWiFi
# State machine
if app_state == AppState.Display:
draw_display()
elif app_state == AppState.ConnectWiFi:
draw_display()
print("Connecting to WiFi...")
if wifi.connect():
print("WiFi connected")
app_state = AppState.FetchData
else:
# WiFi failed - silently revert to display with old data
print("WiFi connection failed - keeping old data")
app_state = AppState.Display
elif app_state == AppState.FetchData:
draw_display()
print("WiFi connected, fetching data...")
data = fetch_stats()
print(f"Result: {data}")
if data:
state["total_hits"] = data.get("totalHits", 0)
state["unique_visitors"] = data.get("uniqueVisitors", 0)
# Extract time from timestamp (format: "2026-01-29 15:33:29")
ts = data.get("lastUpdated", "")
if " " in ts:
state["last_updated"] = ts.split(" ")[1][:5] # HH:MM
else:
state["last_updated"] = "--:--"
print(f"Updated state: {state}")
State.save("visitor_stats", state)
else:
# Fetch failed - silently revert to display with old data
print("Fetch failed - keeping old data")
app_state = AppState.Display
elif app_state == AppState.Error:
# Any button press returns to display
if io.pressed:
app_state = AppState.Display
def on_exit():
"""Save state when exiting."""
State.save("visitor_stats", state)
if __name__ == "__main__":
run(update=update, on_exit=on_exit)