feat: Fern-Einrichtung des Agents per ADB (kein Abtippen am Fire TV)
Am Fire TV gibt es keine Kamera (kein QR) und Host/Token mit der Fernbedienung einzugeben ist eine Qual. Der Satellit hat aber ohnehin schon eine ADB-Verbindung — darueber bekommt der Agent seine Zugangsdaten direkt. - Android: MainActivity.applyProvisioning() liest Intent-Extras (host/port/tls/ token/name/control/reply_mode), speichert sie, startet den Dienst und zeigt 'Von ARIA eingerichtet' (Transparenz). Auch via onNewIntent. - Satellit: adb.provision (am start mit Extras) und adb.status (installiert? laeuft? welche Version?). adb.install richtet direkt mit ein, wenn rvs_host/ rvs_token mitkommen -> ein Klick = installieren + einrichten + online. - Diagnostic: Checkbox 'Gleich einrichten' (default an) + 'Status pruefen'-Button. Der RVS-Zugang wird SERVERSEITIG ergaenzt (params.provision) — das Token steht nie im Browser-Formular. - Allowlists (Code + .env.example) und README ergaenzt, inkl. Sicherheitshinweis. Android-Version 0.0.1.1. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -45,9 +45,12 @@ CONTROL_ENABLED=true
|
||||
# adb.connect ADB-Verbindung aufbauen (params: ip)
|
||||
# adb.devices verbundene ADB-Geraete auflisten
|
||||
# adb.shell Shell-Kommando auf dem Android-Geraet (params: ip, cmd)
|
||||
# adb.provision Agent FERN einrichten: RVS-Zugang uebergeben, statt ihn mit der
|
||||
# Fernbedienung abzutippen (params: ip, rvs_host, rvs_token, ...)
|
||||
# adb.status laeuft/installiert der Agent auf dem Geraet? (params: ip)
|
||||
# ACHTUNG: Diese Liste ueberschreibt den Code-Default. Wird eine neue Aktion
|
||||
# ergaenzt, MUSS sie hier nachgetragen werden — sonst lehnt der Satellit sie ab.
|
||||
CONTROL_ALLOWLIST=dial.launch,wol,http.get,snmp.get,snmp.walk,snmp.printer,snmp.ports,snmp.info,fritzbox.info,fritzbox.hosts,ssh.exec,adb.devices,adb.connect,adb.install,adb.shell
|
||||
CONTROL_ALLOWLIST=dial.launch,wol,http.get,snmp.get,snmp.walk,snmp.printer,snmp.ports,snmp.info,fritzbox.info,fritzbox.hosts,ssh.exec,adb.devices,adb.connect,adb.install,adb.shell,adb.provision,adb.status
|
||||
|
||||
# ─── ADB / Android-Rollout (optional) ──────────────────────────────
|
||||
# Fire TV Sticks & Android-Geraete im LAN: APK ausrollen ohne Datei-Kopieren.
|
||||
|
||||
+122
-3
@@ -135,7 +135,7 @@ CONTROL_ALLOWLIST = [
|
||||
os.environ.get("CONTROL_ALLOWLIST",
|
||||
"dial.launch,wol,http.get,snmp.get,snmp.walk,snmp.printer,"
|
||||
"snmp.ports,snmp.info,fritzbox.info,fritzbox.hosts,ssh.exec,"
|
||||
"adb.devices,adb.connect,adb.install,adb.shell").split(",")
|
||||
"adb.devices,adb.connect,adb.install,adb.shell,adb.provision,adb.status").split(",")
|
||||
if a.strip()
|
||||
]
|
||||
|
||||
@@ -633,6 +633,10 @@ async def _control(action: str, params: dict, devices: list[dict]) -> dict:
|
||||
return await loop.run_in_executor(None, _do_adb_install, params)
|
||||
if action == "adb.shell":
|
||||
return await loop.run_in_executor(None, _do_adb_shell, params)
|
||||
if action == "adb.provision":
|
||||
return await loop.run_in_executor(None, _do_adb_provision, params)
|
||||
if action == "adb.status":
|
||||
return await loop.run_in_executor(None, _do_adb_status, params)
|
||||
return {"ok": False, "error": f"Aktion '{action}' nicht implementiert."}
|
||||
except Exception as exc:
|
||||
return {"ok": False, "error": f"{action} fehlgeschlagen: {exc}"}
|
||||
@@ -1222,6 +1226,110 @@ def _do_adb_connect(params: dict) -> dict:
|
||||
return {"ok": True, "result": {"target": target, "state": state, "message": msg}}
|
||||
|
||||
|
||||
AGENT_PACKAGE = os.environ.get("AGENT_PACKAGE", "de.hackersoft.ariaagent")
|
||||
|
||||
|
||||
def _adb_app_installed(target: str, package: str) -> bool:
|
||||
rc, out, _ = _adb_run(["-s", target, "shell", "pm", "list", "packages", package],
|
||||
timeout=30)
|
||||
return rc == 0 and f"package:{package}" in out
|
||||
|
||||
|
||||
def _adb_app_running(target: str, package: str) -> bool:
|
||||
"""Laeuft der Vordergrund-Dienst des Agents? (Fire TV / Android)."""
|
||||
rc, out, _ = _adb_run(["-s", target, "shell", "dumpsys", "activity", "services",
|
||||
package], timeout=30)
|
||||
return rc == 0 and "ServiceRecord" in out
|
||||
|
||||
|
||||
def _do_adb_status(params: dict) -> dict:
|
||||
"""Ist der ARIA-Agent auf dem Geraet installiert und laeuft er?"""
|
||||
target = _adb_target(params)
|
||||
if not target:
|
||||
return {"ok": False, "error": "ip/host erforderlich."}
|
||||
package = (params.get("package") or AGENT_PACKAGE).strip()
|
||||
ok, msg = _adb_connect(target)
|
||||
if not ok:
|
||||
return {"ok": False, "error": msg}
|
||||
installed = _adb_app_installed(target, package)
|
||||
running = _adb_app_running(target, package) if installed else False
|
||||
ver = ""
|
||||
if installed:
|
||||
rc, out, _ = _adb_run(["-s", target, "shell", "dumpsys", "package", package],
|
||||
timeout=30)
|
||||
m = re.search(r"versionName=(\S+)", out or "")
|
||||
ver = m.group(1) if m else ""
|
||||
return {"ok": True, "result": {
|
||||
"target": target, "package": package, "installed": installed,
|
||||
"running": running, "version": ver,
|
||||
"message": ("Agent laeuft" if running else
|
||||
"installiert, laeuft aber nicht" if installed else
|
||||
"nicht installiert")}}
|
||||
|
||||
|
||||
def _do_adb_provision(params: dict) -> dict:
|
||||
"""Richtet den ARIA-Agent auf dem Geraet FERN ein (RVS-Zugang uebergeben).
|
||||
|
||||
Auf einem Fire TV gibt es keine Kamera (kein QR-Scan) und Tippen mit der
|
||||
Fernbedienung ist muehsam. Der Satellit startet die App deshalb per ADB
|
||||
direkt mit den Zugangsdaten als Intent-Extras.
|
||||
params: {ip, rvs_host, rvs_token, rvs_port?, rvs_tls?, name?, control?,
|
||||
reply_mode?, package?}
|
||||
"""
|
||||
target = _adb_target(params)
|
||||
if not target:
|
||||
return {"ok": False, "error": "ip/host erforderlich."}
|
||||
rvs_host = (params.get("rvs_host") or "").strip()
|
||||
rvs_token = (params.get("rvs_token") or "").strip()
|
||||
if not rvs_host or not rvs_token:
|
||||
return {"ok": False, "error": "rvs_host und rvs_token erforderlich."}
|
||||
package = (params.get("package") or AGENT_PACKAGE).strip()
|
||||
|
||||
ok, msg = _adb_connect(target)
|
||||
state = _adb_state(target)
|
||||
wait = float(params.get("auth_wait") or ADB_AUTH_WAIT_SEC)
|
||||
if (not ok or state != "device") and wait > 0:
|
||||
authed, state = _adb_wait_authorized(target, wait)
|
||||
if not authed:
|
||||
return {"ok": False, "error":
|
||||
"Nicht autorisiert — 'USB-Debugging zulassen?' auf dem "
|
||||
f"Bildschirm bestaetigen. (Zustand: '{state or '?'}')"}
|
||||
elif not ok:
|
||||
return {"ok": False, "error": msg}
|
||||
|
||||
if not _adb_app_installed(target, package):
|
||||
return {"ok": False, "error": f"'{package}' ist nicht installiert — "
|
||||
"erst adb.install ausfuehren."}
|
||||
|
||||
args = ["-s", target, "shell", "am", "start",
|
||||
"-n", f"{package}/.MainActivity",
|
||||
"-a", f"{package}.PROVISION",
|
||||
"-e", "host", rvs_host,
|
||||
"-e", "token", rvs_token,
|
||||
"--ei", "port", str(int(params.get("rvs_port") or 443)),
|
||||
"--ez", "tls", "true" if _as_bool(params.get("rvs_tls"), True) else "false",
|
||||
"--ez", "control", "true" if _as_bool(params.get("control"), True) else "false"]
|
||||
if params.get("name"):
|
||||
args += ["-e", "name", str(params["name"])]
|
||||
if params.get("reply_mode"):
|
||||
args += ["-e", "reply_mode", str(params["reply_mode"])]
|
||||
|
||||
rc, out, err = _adb_run(args, timeout=60)
|
||||
if rc != 0 or "Error" in f"{out} {err}":
|
||||
return {"ok": False, "error": (f"{out} {err}".strip() or f"exit {rc}")[:400]}
|
||||
return {"ok": True, "result": {
|
||||
"target": target, "package": package,
|
||||
"message": f"Agent eingerichtet (RVS {rvs_host}) und gestartet"}}
|
||||
|
||||
|
||||
def _as_bool(v, default: bool) -> bool:
|
||||
if v is None:
|
||||
return default
|
||||
if isinstance(v, bool):
|
||||
return v
|
||||
return str(v).strip().lower() in ("1", "true", "yes", "on", "ja")
|
||||
|
||||
|
||||
def _do_adb_shell(params: dict) -> dict:
|
||||
"""Shell-Kommando auf dem Android-Geraet (Diagnose, z.B. 'pm list packages')."""
|
||||
target = _adb_target(params)
|
||||
@@ -1343,8 +1451,19 @@ def _do_adb_install(params: dict) -> dict:
|
||||
txt = f"{out} {err2}".strip()
|
||||
size_mb = round(os.path.getsize(apk) / 1024 / 1024, 1)
|
||||
if rc == 0 and "success" in txt.lower():
|
||||
return {"ok": True, "result": {"target": target, "apk_mb": size_mb,
|
||||
"message": f"APK installiert ({size_mb} MB)"}}
|
||||
res = {"target": target, "apk_mb": size_mb,
|
||||
"message": f"APK installiert ({size_mb} MB)"}
|
||||
# Direkt einrichten, wenn die RVS-Daten mitkamen — auf einem Fire TV
|
||||
# spart das der Fernbedienung das Abtippen von Host und Token.
|
||||
if params.get("rvs_host") and params.get("rvs_token"):
|
||||
prov = _do_adb_provision(params)
|
||||
res["provisioned"] = bool(prov.get("ok"))
|
||||
if prov.get("ok"):
|
||||
res["message"] += " und eingerichtet — der Agent sollte gleich online sein"
|
||||
else:
|
||||
res["message"] += (f". Einrichtung fehlgeschlagen: "
|
||||
f"{prov.get('error', '?')}")
|
||||
return {"ok": True, "result": res}
|
||||
low = txt.lower()
|
||||
if "signatures do not match" in low or "update_incompatible" in low:
|
||||
return {"ok": False, "error": "Signatur passt nicht zur installierten Version — "
|
||||
|
||||
Reference in New Issue
Block a user