Adding visitor stats app
This commit is contained in:
commit
690069aa16
2 changed files with 157 additions and 0 deletions
157
visitor-stats/__init__.py
Normal file
157
visitor-stats/__init__.py
Normal 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)
|
||||
Loading…
Add table
Add a link
Reference in a new issue