121 lines
3.0 KiB
Python
121 lines
3.0 KiB
Python
"""Terminal TUI for device status monitoring."""
|
|
|
|
import os
|
|
import sys
|
|
import threading
|
|
import time
|
|
|
|
from devices import status_all, is_curfew_allowed
|
|
|
|
|
|
# ANSI escape codes
|
|
CLEAR = "\033[2J\033[H"
|
|
BOLD = "\033[1m"
|
|
RESET = "\033[0m"
|
|
GREEN = "\033[92m"
|
|
RED = "\033[91m"
|
|
YELLOW = "\033[93m"
|
|
CYAN = "\033[96m"
|
|
GRAY = "\033[90m"
|
|
|
|
|
|
def _get_terminal_size():
|
|
"""Get terminal width and height."""
|
|
try:
|
|
return os.get_terminal_size()
|
|
except OSError:
|
|
return (80, 24)
|
|
|
|
|
|
def _color(text: str, color_code: str) -> str:
|
|
"""Wrap text in ANSI color code."""
|
|
return f"{color_code}{text}{RESET}"
|
|
|
|
|
|
def _separator(width: int = 60) -> str:
|
|
"""Create a separator line."""
|
|
return "─" * width
|
|
|
|
|
|
def render(devices_status, curfew, refresh_interval: int = 10):
|
|
"""Render the TUI screen."""
|
|
width, height = _get_terminal_size()
|
|
lines = []
|
|
|
|
# Header
|
|
lines.append(_color("⚡ KIDS DEVICES — STATUS", BOLD))
|
|
lines.append("")
|
|
|
|
# Curfew status
|
|
if curfew["in_curfew"]:
|
|
curfew_icon = _color("🔴 BLOCKED", RED)
|
|
else:
|
|
curfew_icon = _color("🟢 ALLOWED", GREEN)
|
|
lines.append(f" Curfew: {curfew_icon} | {curfew['message']}")
|
|
lines.append("")
|
|
|
|
# Device list
|
|
for device in devices_status:
|
|
icon = device["icon"]
|
|
name = device["title"]
|
|
detail = device["detail"]
|
|
|
|
# Parse budget from detail
|
|
parts = detail.split(" · ")
|
|
status_text = parts[0] if parts else "Unknown"
|
|
budget_text = parts[1] if len(parts) > 1 else ""
|
|
|
|
# Color the status
|
|
if "Online" in status_text:
|
|
status_color = GREEN
|
|
elif "Offline" in status_text:
|
|
status_color = GRAY
|
|
else:
|
|
status_color = YELLOW
|
|
|
|
status_colored = _color(status_text, status_color)
|
|
|
|
# Color the budget
|
|
if "🔴" in device["icon"]:
|
|
budget_color = RED
|
|
elif "🟠" in device["icon"]:
|
|
budget_color = YELLOW
|
|
else:
|
|
budget_color = CYAN
|
|
|
|
budget_colored = _color(budget_text, budget_color) if budget_text else ""
|
|
|
|
lines.append(f" {icon} {name:<12} {status_colored} · {budget_colored}")
|
|
|
|
# Footer with timestamp
|
|
now = time.strftime("%H:%M:%S")
|
|
lines.append(f" {_color('Last update:', GRAY)} {now} | Refresh: {refresh_interval}s")
|
|
|
|
return "\n".join(lines)
|
|
|
|
|
|
def run_tui(refresh_interval: int = 10):
|
|
"""Run the TUI in a loop."""
|
|
# Run in a separate thread to not block the server
|
|
while True:
|
|
try:
|
|
devices_status, curfew = status_all()
|
|
screen = render(devices_status, curfew, refresh_interval)
|
|
sys.stdout.write(CLEAR + screen)
|
|
sys.stdout.flush()
|
|
except Exception:
|
|
pass # Don't crash TUI on errors
|
|
|
|
time.sleep(refresh_interval)
|
|
|
|
|
|
def start_tui(refresh_interval: int = 10):
|
|
"""Start the TUI in a background thread."""
|
|
tui_thread = threading.Thread(
|
|
target=run_tui,
|
|
args=(refresh_interval,),
|
|
daemon=True,
|
|
)
|
|
tui_thread.start()
|
|
return tui_thread
|