feat(satellite): fritzbox.hosts mit LAN-Port, Verbindungsart und Tempo

ARIA konnte "an welchem Port der FritzBox Keller haengt X" nicht
beantworten: fritzbox.hosts warf alles ausser Name/IP/MAC/aktiv weg. Die
Host-Liste der Box (TR-064 X_AVM-DE_GetHostListPath) enthaelt je nach
FRITZ!OS auch InterfaceType, X_AVM-DE_Port, X_AVM-DE_Speed und
X_AVM-DE_Guest — die gehen jetzt als interface, lan_port, speed_mbit,
guest mit. params.find filtert nach Name/IP/MAC. Hinweis im Ergebnis:
lan_port ist die Buchse DIESER Box (hinter Switch/Repeater dessen Port).
Tool-Beschreibung im Brain nennt die neuen Felder.

Getestet mit nachgebauter AVM-Host-Liste; gegen eine echte Box noch nicht.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-31 17:06:21 +01:00
co-authored by Claude Opus 5.5
parent c934866bba
commit 0bf00f9fc4
2 changed files with 31 additions and 7 deletions
+3 -1
View File
@@ -1292,7 +1292,9 @@ META_TOOLS = [
"existiert, weiss SNMP NICHT). 'snmp.get'/'snmp.walk' "
"(params={'ip':'...','oid':'...','community':'public'}) fuer beliebige "
"SNMP-Werte. 'fritzbox.info' / 'fritzbox.hosts' (params={'ip':'<fritzbox>'}) "
"— Internetverbindung/Datenrate/externe IP bzw. verbundene Geraete (braucht "
"— Internetverbindung/Datenrate/externe IP bzw. verbundene Geraete MIT "
"Verbindungsart (LAN/WLAN), lan_port (an welcher LAN-Buchse der Box) und "
"speed_mbit; 'find' filtert nach Name/IP/MAC (braucht "
"hinterlegten FritzBox-Login). 'ssh.exec' (params={'ip':'...','cmd':'...'}) "
"— fuehrt EIN Shell-Kommando per SSH auf einem Geraet aus (Server, Pi, NAS) "
"und gibt exit_code + stdout (gefenstert: contains/offset/max_chars) + "
+28 -6
View File
@@ -1199,7 +1199,9 @@ def _tr064(ip: str, user: str, pw: str, service: str, control: str,
def _do_fritzbox(action: str, params: dict) -> dict:
"""fritzbox.info -> Modell/Firmware/Verbindung/externe IP/Datenrate.
fritzbox.hosts -> Liste der bekannten Geraete (Name/IP/MAC/aktiv)."""
fritzbox.hosts -> Liste der bekannten Geraete (Name/IP/MAC/aktiv) plus
Verbindungsart (LAN/WLAN), LAN-Port und Geschwindigkeit.
params.find: nur Geraete, deren Name/IP/MAC das enthaelt."""
ip = _snmp_target(params)
if not ip:
return {"ok": False, "error": "ip/host erforderlich."}
@@ -1227,16 +1229,36 @@ def _do_fritzbox(action: str, params: dict) -> dict:
except Exception as exc:
return {"ok": False, "error": f"Host-Liste nicht abrufbar: {exc}"}
hosts = []
find = str(params.get("find") or "").strip().lower()
for item in re.finditer(r"<Item>(.*?)</Item>", r.text, re.DOTALL):
blk = item.group(1)
def _t(tag):
m = re.search(rf"<{tag}>(.*?)</{tag}>", blk, re.DOTALL)
return m.group(1) if m else ""
hosts.append({"name": _t("HostName"), "ip": _t("IPAddress"),
"mac": _t("MACAddress"),
"active": _t("Active") in ("1", "true")})
return {"ok": True, "result": {"ip": ip, "count": len(hosts), "hosts": hosts}}
return m.group(1).strip() if m else ""
h = {"name": _t("HostName"), "ip": _t("IPAddress"),
"mac": _t("MACAddress"),
"active": _t("Active") in ("1", "true")}
# AVM-Zusatzfelder (je nach FRITZ!OS vorhanden): Verbindungsart,
# LAN-Port der Box (0 = kein direkter LAN-Port, z.B. WLAN/Mesh),
# Geschwindigkeit in Mbit/s, Gast-Netz.
iface = _t("InterfaceType")
if iface:
h["interface"] = iface
port = _to_int(_t("X_AVM-DE_Port"))
if port:
h["lan_port"] = port
speed = _to_int(_t("X_AVM-DE_Speed"))
if speed:
h["speed_mbit"] = speed
if _t("X_AVM-DE_Guest") in ("1", "true"):
h["guest"] = True
if find and not any(find in str(h.get(k, "")).lower() for k in ("name", "ip", "mac")):
continue
hosts.append(h)
return {"ok": True, "result": {"ip": ip, "count": len(hosts), "hosts": hosts,
"hinweis": "lan_port = LAN-Buchse DIESER Box. Haengt ein Geraet "
"hinter einem Switch/Repeater, steht hier dessen Port."}}
# fritzbox.info (Default): mehrere Services, Teil-Fehler tolerieren.
info = {"ip": ip}