From d693321ba1e0f20a7f27cffc1a2f16bd70469cd6 Mon Sep 17 00:00:00 2001 From: Christoph Gasser Date: Mon, 27 Apr 2026 15:48:50 +0200 Subject: [PATCH] =?UTF-8?q?Add=20IP=20range=20filter=20button=20(.150?= =?UTF-8?q?=E2=80=93.155)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit New "Filter .150–.155" button fetches all connected clients via GET /api/all-clients (one request per site, grouped by AP MAC), then hides any AP row that has no client with a last-octet IP between 150 and 155. Clicking again clears the filter. The name/site search and IP filter compose (AND logic) via a shared applyFilters() function. Client data is cached in-memory for the current page session so repeated toggles don't re-fetch. Co-Authored-By: Claude Sonnet 4.6 --- app/main.py | 13 ++++++ app/omada.py | 21 +++++++++ app/templates/index.html | 98 ++++++++++++++++++++++++++++++++++++++-- 3 files changed, 127 insertions(+), 5 deletions(-) diff --git a/app/main.py b/app/main.py index b8ba21e..8834c14 100644 --- a/app/main.py +++ b/app/main.py @@ -263,6 +263,19 @@ async def api_reboot(request: Request, db: Session = Depends(get_db)): return {"status": "ok", "mac": mac} +@app.get("/api/all-clients") +async def api_all_clients(request: Request): + user = get_current_user(request) + if not user: + raise HTTPException(status_code=401, detail="Not authenticated") + try: + data = await omada_client.get_all_clients() + return {"ap_clients": data} + except Exception as exc: + logger.error("Failed to fetch all clients: %s", exc) + raise HTTPException(status_code=502, detail=str(exc)) + + @app.get("/api/ap-clients") async def api_ap_clients(request: Request, mac: str = "", site_key: str = ""): user = get_current_user(request) diff --git a/app/omada.py b/app/omada.py index 03df688..99fb325 100644 --- a/app/omada.py +++ b/app/omada.py @@ -188,6 +188,27 @@ class OmadaClient: return int(device.get("uptimeLong", 0)) raise RuntimeError(f"AP {mac} not found in site {site_key}") + async def get_all_clients(self) -> dict[str, list[dict]]: + """Return {ap_mac: [clients]} for every site (wireless clients only).""" + await self._ensure_ready() + ap_clients: dict[str, list] = {} + for site_name, site_key in self._sites.items(): + try: + data = await self._request_with_retry( + "GET", + f"{OMADA_BASE_URL}/{self._omadac_id}/api/v2/sites/{site_key}/clients", + params={"page": 1, "pageSize": 1000, "filters.active": "true"}, + ) + result = data.get("result", {}) + clients = result.get("data", []) if isinstance(result, dict) else (result if isinstance(result, list) else []) + for c in clients: + mac = c.get("apMac", "").upper().replace("-", ":") + if mac: + ap_clients.setdefault(mac, []).append(c) + except Exception as exc: + logger.warning("Failed to fetch clients for site '%s': %s", site_name, exc) + return ap_clients + async def get_ap_clients(self, ap_mac: str, site_key: str) -> list[dict]: """Fetch wireless clients currently connected to a specific AP.""" await self._ensure_ready() diff --git a/app/templates/index.html b/app/templates/index.html index fdad425..130d2c9 100644 --- a/app/templates/index.html +++ b/app/templates/index.html @@ -30,6 +30,14 @@ Refresh +