# steward/core/widgets.py # # Central widget registry. Each entry describes one widget type that can be # placed on a dashboard. Plugin widgets declare which plugin must be enabled. # # "params" is a list of configurable parameters for the widget. Each param: # key - form field name (stored in config_json) # label - human-readable label # type - "select" (only type currently supported) # default - default value (int or str determines how it's parsed from the form) # options - list of {value, label} dicts from __future__ import annotations WIDGET_REGISTRY: dict[str, dict] = { "monitors": { "key": "monitors", "label": "Monitors", "description": "Uptime checks of every type — ping, DNS, and HTTP — with up/down status and latency", "hx_url": "/monitors/widget", "detail_url": "/monitors/", "plugin": None, "poll": True, "params": [ { "key": "type_filter", "label": "Type", "type": "select", "default": "all", "options": [ {"value": "all", "label": "All types"}, {"value": "icmp", "label": "Ping (ICMP)"}, {"value": "tcp", "label": "Ping (TCP)"}, {"value": "dns", "label": "DNS"}, {"value": "http", "label": "HTTP"}, ], }, { "key": "show_down_only", "label": "Display filter", "type": "select", "default": "no", "options": [ {"value": "no", "label": "All monitors"}, {"value": "yes", "label": "Failing only"}, ], }, { "key": "limit", "label": "Max shown", "type": "select", "default": 10, "options": [ {"value": 5, "label": "5 monitors"}, {"value": 10, "label": "10 monitors"}, {"value": 20, "label": "20 monitors"}, ], }, ], }, "status_overview": { "key": "status_overview", "label": "Overview", "description": "At-a-glance summary — host count, monitor up/down/pending, and alerts", "hx_url": "/status/widget", "detail_url": "/status/", "plugin": None, "poll": True, "params": [], }, "hosts_overview": { "key": "hosts_overview", "label": "Hosts — Overview", "description": "One row per host: monitor status + uptime and agent CPU/memory/disk, linking to the host hub", "hx_url": "/hosts/overview/widget", "detail_url": "/hosts/", "plugin": None, "poll": True, "params": [], }, "uptime_summary": { "key": "uptime_summary", "label": "Uptime / SLA", "description": "Per-host rolling uptime % for 24h, 7d, and 30d windows", "hx_url": "/hosts/uptime/widget", "detail_url": "/hosts/uptime", "plugin": None, "poll": False, "params": [], }, "traefik_routers": { "key": "traefik_routers", "label": "Traefik — Routers", "description": "Top routers by request rate with error highlights and expiring certs", "hx_url": "/plugins/traefik/widget", "detail_url": "/plugins/traefik/", "plugin": "traefik", "poll": True, "params": [], }, "traefik_access_log": { "key": "traefik_access_log", "label": "Traefik — Access Log", "description": "Traffic origin summary — top IPs, countries, and request distribution", "hx_url": "/plugins/traefik/widget/access_log", "detail_url": "/plugins/traefik/", "plugin": "traefik", "poll": True, "params": [ { "key": "ip_filter", "label": "IP Filter", "type": "select", "default": "all", "options": [ {"value": "all", "label": "All traffic"}, {"value": "internal", "label": "Internal only"}, {"value": "external", "label": "External only"}, ], }, { "key": "limit", "label": "Top IPs shown", "type": "select", "default": 10, "options": [ {"value": 10, "label": "10 entries"}, {"value": 20, "label": "20 entries"}, {"value": 50, "label": "50 entries"}, ], }, ], }, "traefik_request_chart": { "key": "traefik_request_chart", "label": "Traefik — Request Chart", "description": "Request rate and error percentage over time (Chart.js)", "hx_url": "/plugins/traefik/widget/request_chart", "detail_url": "/plugins/traefik/", "plugin": "traefik", "poll": True, "params": [ { "key": "hours", "label": "Time range", "type": "select", "default": 6, "options": [ {"value": 1, "label": "Last 1 hour"}, {"value": 6, "label": "Last 6 hours"}, {"value": 24, "label": "Last 24 hours"}, ], }, ], }, "unifi_overview": { "key": "unifi_overview", "label": "UniFi — Overview", "description": "WAN status, client counts, offline devices, and active alarms", "hx_url": "/plugins/unifi/widget", "detail_url": "/plugins/unifi/", "plugin": "unifi", "poll": True, "params": [], }, "unifi_clients": { "key": "unifi_clients", "label": "UniFi — Top Clients", "description": "Active wireless and wired clients sorted by bandwidth", "hx_url": "/plugins/unifi/widget/clients", "detail_url": "/plugins/unifi/", "plugin": "unifi", "poll": True, "params": [ { "key": "limit", "label": "Clients shown", "type": "select", "default": 5, "options": [ {"value": 5, "label": "5 clients"}, {"value": 10, "label": "10 clients"}, {"value": 20, "label": "20 clients"}, ], }, ], }, "unifi_devices": { "key": "unifi_devices", "label": "UniFi — Devices", "description": "Network infrastructure devices with online/offline state", "hx_url": "/plugins/unifi/widget/devices", "detail_url": "/plugins/unifi/", "plugin": "unifi", "poll": True, "params": [ { "key": "type_filter", "label": "Device type", "type": "select", "default": "all", "options": [ {"value": "all", "label": "All devices"}, {"value": "wireless", "label": "Wireless APs"}, {"value": "wired", "label": "Wired only"}, {"value": "offline", "label": "Offline devices"}, ], }, ], }, "docker_containers": { "key": "docker_containers", "label": "Docker — Containers", "description": "Container status overview — running/stopped counts and per-container CPU", "hx_url": "/plugins/docker/widget", "detail_url": "/plugins/docker/", "plugin": "docker", "poll": True, "params": [ { "key": "show_stopped", "label": "Show stopped", "type": "select", "default": "no", "options": [ {"value": "no", "label": "Running only"}, {"value": "yes", "label": "All containers"}, ], }, ], }, "docker_resources": { "key": "docker_resources", "label": "Docker — Resources", "description": "CPU and memory usage bars for running containers", "hx_url": "/plugins/docker/widget/resources", "detail_url": "/plugins/docker/", "plugin": "docker", "poll": True, "params": [ { "key": "limit", "label": "Containers shown", "type": "select", "default": 10, "options": [ {"value": 5, "label": "5 containers"}, {"value": 10, "label": "10 containers"}, {"value": 20, "label": "20 containers"}, ], }, ], }, "snmp_devices": { "key": "snmp_devices", "label": "SNMP — Devices", "description": "Latest OID readings for all configured SNMP devices", "hx_url": "/plugins/snmp/widget", "detail_url": "/plugins/snmp/", "plugin": "snmp", "poll": True, "params": [], }, "host_resources": { "key": "host_resources", "label": "Host Agent — Resources", "description": "Fleet view: CPU, memory, disk, and load per host — each with a trend sparkline", "hx_url": "/plugins/host_agent/widget", "detail_url": "/hosts/", "plugin": "host_agent", "poll": True, "params": [], }, "host_resource_history": { "key": "host_resource_history", "label": "Host Agent — History Graph", "description": "CPU / memory / disk time-series graph for one host (the host-view chart, on your dashboard)", "hx_url": "/plugins/host_agent/widget/history", "detail_url": "/hosts/", "plugin": "host_agent", "poll": True, "params": [ # type "host" → the edit form renders a live dropdown of hosts. { "key": "host_id", "label": "Host", "type": "host", "default": "", }, { "key": "hours", "label": "Time range", "type": "select", "default": 6, "options": [ {"value": 1, "label": "Last 1 hour"}, {"value": 6, "label": "Last 6 hours"}, {"value": 24, "label": "Last 24 hours"}, ], }, ], }, } # Group each widget for the picker (Core monitors / Monitoring capabilities / # Integrations), mirroring the Settings → Plugins taxonomy. Capability plugins # are host/monitoring facets; traefik/unifi are discrete vendor integrations. _CORE_WIDGETS = {"monitors", "status_overview", "uptime_summary", "hosts_overview"} _INTEGRATION_PLUGINS = {"traefik", "unifi"} GROUP_LABELS = { "core": "Core monitors", "capability": "Monitoring capabilities", "integration": "Integrations", } GROUP_ORDER = ["core", "capability", "integration"] def _group_for(w: dict) -> str: if w["key"] in _CORE_WIDGETS: return "core" if w.get("plugin") in _INTEGRATION_PLUGINS: return "integration" return "capability" for _w in WIDGET_REGISTRY.values(): _w["group"] = _group_for(_w) def register_widget(definition: dict) -> None: """Register a widget at runtime (used by external plugins).""" definition.setdefault("group", _group_for(definition)) WIDGET_REGISTRY[definition["key"]] = definition def get_available_widgets(plugins_cfg: dict) -> list[dict]: """Return widgets whose plugin dependency is satisfied.""" result = [] for w in WIDGET_REGISTRY.values(): if w["plugin"] is None: result.append(w) elif plugins_cfg.get(w["plugin"], {}).get("enabled", False): result.append(w) return result