One full-screen, live view of everything going on around the node, stacked coarsest first. Activity: a three-row braille bar chart of every packet heard over the trailing two hours — the header indicator's big sibling, drawn from the same monitor buckets — with a pulse line reading the rate, nodes heard, and the window's busiest transmitter. Traffic: session tallies by packet class with proportional bars (the monitor grows per-kind counters). RF health: median SNR on the trace tool's quality bar and RSSI extremes from the stored window, beside the radio's own live numbers — noise floor, last RSSI/SNR, airtime, battery — polled the way Device info reads them. Feed: the latest packets in fixed lanes (time, class, node, reception, relay path), seeded from stored history via a new recent_observations window query so the screen opens full, then streamed straight off the event hub. Menu-only (it rides the hub and repaints every second); scripted inspection stays with the monitor and nodes subcommands. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
71 lines
2.4 KiB
Python
71 lines
2.4 KiB
Python
"""The ``dashboard`` tool: the live mesh overview, at the top of the Mesh section.
|
|
|
|
Opens the full-screen dashboard (see :mod:`meshterm.ui.dashboard_screen`): the two-hour
|
|
all-packet activity chart with its pulse line, the session's traffic tallies by packet
|
|
class, the window's RF health beside the radio's own live numbers, and the streaming
|
|
packet feed. It leads the Mesh section because it answers the section's first question —
|
|
*what's going on out there?* — before any specific tool is reached for.
|
|
|
|
Menu-only: the dashboard is inherently live (it rides the event hub and repaints every
|
|
second), so there is no scripted one-shot to register — the ``monitor`` and ``nodes``
|
|
subcommands cover scripted inspection.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from typing import Any, Optional
|
|
|
|
import typer
|
|
|
|
from ..context import AppContext
|
|
from .base import Tool, ToolResult, register
|
|
|
|
|
|
@register
|
|
class DashboardTool(Tool):
|
|
"""Watch everything going on on the mesh: activity, traffic, RF health, feed."""
|
|
|
|
name = "dashboard"
|
|
title = "Dashboard"
|
|
icon = "📊"
|
|
help = "Watch the mesh live — activity, traffic, RF health, packet feed"
|
|
category = "Mesh"
|
|
order = 5 # the section's overview, above the tools it summarizes
|
|
|
|
async def prompt_params(self, ctx: AppContext) -> Optional[dict[str, Any]]:
|
|
"""Run the live dashboard; there are never parameters to collect.
|
|
|
|
The screen presents everything itself and returns when dismissed, so
|
|
returning ``None`` tells the menu the invocation is complete (the same
|
|
pattern as the ``advert`` tool).
|
|
|
|
Args:
|
|
ctx: Shared application context.
|
|
|
|
Returns:
|
|
Always ``None``.
|
|
"""
|
|
from ..ui.dashboard_screen import open_dashboard
|
|
|
|
await open_dashboard(ctx)
|
|
return None
|
|
|
|
async def run(self, ctx: AppContext, params: dict[str, Any]) -> ToolResult:
|
|
"""Nothing runs here: the dashboard lives inside :meth:`prompt_params`.
|
|
|
|
Args:
|
|
ctx: Shared application context.
|
|
params: Unused.
|
|
|
|
Returns:
|
|
An empty :class:`ToolResult` (only reachable from a scripted call).
|
|
"""
|
|
return ToolResult(summary={})
|
|
|
|
def register_cli(self, app: typer.Typer) -> None:
|
|
"""Register no CLI command — the dashboard is a live, menu-only screen.
|
|
|
|
Args:
|
|
app: The Typer application (untouched).
|
|
"""
|