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:
+41
-2
@@ -1616,8 +1616,17 @@
|
|||||||
Aus dem Gitea-Release. Der Satellit lädt die Datei selbst (einmal) und
|
Aus dem Gitea-Release. Der Satellit lädt die Datei selbst (einmal) und
|
||||||
installiert sie auf dem Gerät.
|
installiert sie auf dem Gerät.
|
||||||
</div>
|
</div>
|
||||||
|
<label style="display:flex;align-items:center;gap:8px;margin-top:12px;cursor:pointer;">
|
||||||
|
<input type="checkbox" id="apk-provision" checked>
|
||||||
|
<span>Gleich einrichten (RVS-Zugang übertragen)</span>
|
||||||
|
</label>
|
||||||
|
<div style="color:#666;font-size:11px;margin-left:26px;">
|
||||||
|
Spart das Abtippen auf dem Fernseher: Host und Token gehen direkt vom
|
||||||
|
Server an den Agenten — QR-Scan geht am Fire TV ja nicht.
|
||||||
|
</div>
|
||||||
<div style="margin-top:14px;">
|
<div style="margin-top:14px;">
|
||||||
<button class="btn" onclick="runApkDeploy()">📦 Jetzt ausrollen</button>
|
<button class="btn" onclick="runApkDeploy()">📦 Jetzt ausrollen</button>
|
||||||
|
<button class="btn secondary" onclick="runApkStatus()" style="margin-left:6px;">Status prüfen</button>
|
||||||
<span id="apk-status" style="margin-left:10px;color:#8888AA;"></span>
|
<span id="apk-status" style="margin-left:10px;color:#8888AA;"></span>
|
||||||
</div>
|
</div>
|
||||||
<pre id="apk-log" style="margin-top:12px;background:#0D0D1A;border-radius:6px;padding:10px;max-height:240px;overflow:auto;white-space:pre-wrap;color:#8888AA;font-size:12px;display:none;"></pre>
|
<pre id="apk-log" style="margin-top:12px;background:#0D0D1A;border-radius:6px;padding:10px;max-height:240px;overflow:auto;white-space:pre-wrap;color:#8888AA;font-size:12px;display:none;"></pre>
|
||||||
@@ -4742,8 +4751,26 @@
|
|||||||
'(Haken bei „Immer erlauben"). Ich warte bis zu 90 Sekunden darauf und\n' +
|
'(Haken bei „Immer erlauben"). Ich warte bis zu 90 Sekunden darauf und\n' +
|
||||||
'installiere dann automatisch weiter. Kein zweiter Klick nötig.\n\n' +
|
'installiere dann automatisch weiter. Kein zweiter Klick nötig.\n\n' +
|
||||||
'Danach: APK laden + installieren, zusammen 1–3 Minuten.';
|
'Danach: APK laden + installieren, zusammen 1–3 Minuten.';
|
||||||
|
const params = { ip: apkTarget.ip, url };
|
||||||
|
// Server ergaenzt Host/Port/Token — steht bewusst NICHT im Browser-Formular.
|
||||||
|
if (document.getElementById('apk-provision').checked) {
|
||||||
|
params.provision = true;
|
||||||
|
params.name = apkTarget.name;
|
||||||
|
}
|
||||||
send({ action: 'sat_command', satellite: apkTarget.satId, cmd: 'adb.install',
|
send({ action: 'sat_command', satellite: apkTarget.satId, cmd: 'adb.install',
|
||||||
requestId: apkRequestId, params: { ip: apkTarget.ip, url } });
|
requestId: apkRequestId, params });
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Prüft, ob der Agent auf dem Gerät installiert ist und läuft. */
|
||||||
|
function runApkStatus() {
|
||||||
|
if (!apkTarget) return;
|
||||||
|
apkRequestId = 'apk_' + Date.now() + '_' + Math.floor(Math.random() * 100000);
|
||||||
|
document.getElementById('apk-status').innerHTML = '<span style="color:#FFD60A;">prüfe …</span>';
|
||||||
|
const log = document.getElementById('apk-log');
|
||||||
|
log.style.display = 'block';
|
||||||
|
log.textContent = 'Frage das Gerät über ADB ab …';
|
||||||
|
send({ action: 'sat_command', satellite: apkTarget.satId, cmd: 'adb.status',
|
||||||
|
requestId: apkRequestId, params: { ip: apkTarget.ip } });
|
||||||
}
|
}
|
||||||
function onSatResult(p) {
|
function onSatResult(p) {
|
||||||
if (!apkRequestId || !p || p.requestId !== apkRequestId) return;
|
if (!apkRequestId || !p || p.requestId !== apkRequestId) return;
|
||||||
@@ -4753,7 +4780,19 @@
|
|||||||
if (p.ok) {
|
if (p.ok) {
|
||||||
const r = p.result || {};
|
const r = p.result || {};
|
||||||
status.innerHTML = '<span style="color:#34C759;">✓ erfolgreich</span>';
|
status.innerHTML = '<span style="color:#34C759;">✓ erfolgreich</span>';
|
||||||
log.textContent = r.message || JSON.stringify(r, null, 2);
|
let txt = r.message || JSON.stringify(r, null, 2);
|
||||||
|
if (typeof r.installed !== 'undefined') { // adb.status
|
||||||
|
txt = (r.running ? '🟢 Agent läuft' : r.installed ? '🟡 installiert, läuft nicht' : '🔴 nicht installiert') +
|
||||||
|
(r.version ? '\nVersion: ' + r.version : '') +
|
||||||
|
'\nPaket: ' + (r.package || '') + '\nGerät: ' + (r.target || '');
|
||||||
|
if (!r.running && r.installed) {
|
||||||
|
txt += '\n\nTipp: Auf dem Gerät die App öffnen und „Speichern & Verbinden" —' +
|
||||||
|
'\noder hier „Gleich einrichten" nutzen, dann startet sie von selbst.';
|
||||||
|
}
|
||||||
|
} else if (r.provisioned === false) {
|
||||||
|
status.innerHTML = '<span style="color:#FF9500;">⚠ installiert, Einrichtung offen</span>';
|
||||||
|
}
|
||||||
|
log.textContent = txt;
|
||||||
} else {
|
} else {
|
||||||
status.innerHTML = '<span style="color:#FF6E6E;">✗ fehlgeschlagen</span>';
|
status.innerHTML = '<span style="color:#FF6E6E;">✗ fehlgeschlagen</span>';
|
||||||
log.textContent = p.error || 'Unbekannter Fehler';
|
log.textContent = p.error || 'Unbekannter Fehler';
|
||||||
|
|||||||
+12
-1
@@ -2819,13 +2819,24 @@ wss.on("connection", (ws) => {
|
|||||||
} else if (msg.action === "sat_command") {
|
} else if (msg.action === "sat_command") {
|
||||||
// Browser loest eine Satelliten-Aktion aus (z.B. APK auf Fire TV ausrollen).
|
// Browser loest eine Satelliten-Aktion aus (z.B. APK auf Fire TV ausrollen).
|
||||||
// Die Antwort (sat_result) kommt ueber die rvsWs zurueck.
|
// Die Antwort (sat_result) kommt ueber die rvsWs zurueck.
|
||||||
|
const params = msg.params && typeof msg.params === "object" ? msg.params : {};
|
||||||
|
// Fern-Einrichtung: der RVS-Zugang wird SERVERSEITIG ergaenzt — so muss
|
||||||
|
// niemand Host/Token im Browser eintippen (und das Token steht nicht im
|
||||||
|
// Browser-Formular). Gilt fuer adb.provision und adb.install mit Setup.
|
||||||
|
if (params.provision === true) {
|
||||||
|
delete params.provision;
|
||||||
|
params.rvs_host = RVS_HOST;
|
||||||
|
params.rvs_port = Number(RVS_PORT) || 443;
|
||||||
|
params.rvs_tls = RVS_TLS === "true";
|
||||||
|
params.rvs_token = RVS_TOKEN;
|
||||||
|
}
|
||||||
sendToRVS_raw({
|
sendToRVS_raw({
|
||||||
type: "sat_command",
|
type: "sat_command",
|
||||||
payload: {
|
payload: {
|
||||||
satellite: String(msg.satellite || ""),
|
satellite: String(msg.satellite || ""),
|
||||||
device: String(msg.device || ""),
|
device: String(msg.device || ""),
|
||||||
action: String(msg.cmd || ""),
|
action: String(msg.cmd || ""),
|
||||||
params: msg.params && typeof msg.params === "object" ? msg.params : {},
|
params,
|
||||||
requestId: String(msg.requestId || ""),
|
requestId: String(msg.requestId || ""),
|
||||||
},
|
},
|
||||||
timestamp: Date.now(),
|
timestamp: Date.now(),
|
||||||
|
|||||||
@@ -87,6 +87,25 @@ Statt die APK von Hand aufs Gerät zu schaufeln, kann ein **Satellit** sie per A
|
|||||||
und einen **📦**-Button.
|
und einen **📦**-Button.
|
||||||
3. 📦 → APK-URL (aus dem Gitea-Release) → **Jetzt ausrollen**. Beim ersten Mal
|
3. 📦 → APK-URL (aus dem Gitea-Release) → **Jetzt ausrollen**. Beim ersten Mal
|
||||||
erscheint auf dem TV „USB-Debugging zulassen?" → bestätigen (Haken „Immer erlauben").
|
erscheint auf dem TV „USB-Debugging zulassen?" → bestätigen (Haken „Immer erlauben").
|
||||||
|
Der Rollout **wartet darauf** und macht dann automatisch weiter.
|
||||||
|
|
||||||
|
**Einrichtung ohne Tippen:** Am Fire TV gibt es keine Kamera (kein QR-Scan) und
|
||||||
|
Host/Token mit der Fernbedienung einzugeben ist eine Qual. Deshalb ist im Rollout-
|
||||||
|
Dialog **„Gleich einrichten"** vorausgewählt: die Diagnostic kennt den RVS-Zugang
|
||||||
|
selbst und gibt ihn dem Agenten über dieselbe ADB-Verbindung mit
|
||||||
|
(`adb.provision` → `am start` mit Intent-Extras). Der Agent speichert die Daten,
|
||||||
|
startet den Dienst und ist **sofort online** — null Eingaben am Fernseher.
|
||||||
|
Das Token geht dabei **serverseitig** raus, es steht nie im Browser-Formular.
|
||||||
|
|
||||||
|
Mit **„Status prüfen"** (`adb.status`) siehst Du jederzeit, ob der Agent auf dem
|
||||||
|
Gerät installiert ist, welche Version läuft und ob der Dienst aktiv ist.
|
||||||
|
|
||||||
|
> **Sicherheitshinweis:** Die Einrichtung per Intent geht an die (exportierte)
|
||||||
|
> Haupt-Activity. Wer ADB-Zugriff auf das Gerät hat, kann den Agenten damit
|
||||||
|
> umkonfigurieren — wer ADB hat, kontrolliert das Gerät ohnehin. Die App zeigt
|
||||||
|
> beim Empfang einen Hinweis („Von ARIA eingerichtet"), damit es nicht unbemerkt
|
||||||
|
> passiert. Auf einem dedizierten Stick ist das unkritisch; schalte ADB-Debugging
|
||||||
|
> nach dem Ausrollen ab, wenn Du ganz sicher gehen willst.
|
||||||
|
|
||||||
ARIA kann dasselbe: `satellite_command` mit `action='adb.install'`,
|
ARIA kann dasselbe: `satellite_command` mit `action='adb.install'`,
|
||||||
`params={'ip':'…','url':'…'}`. Der Satellit lädt die APK **einmal** und kann sie
|
`params={'ip':'…','url':'…'}`. Der Satellit lädt die APK **einmal** und kann sie
|
||||||
|
|||||||
@@ -122,7 +122,10 @@ class MainActivity : AppCompatActivity() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
loadIntoUi(AgentConfig.load(this))
|
// Fern-Einrichtung (z.B. Fire TV, wo QR-Scan und Tippen wegfallen): der
|
||||||
|
// Satellit startet die App per ADB mit den Zugangsdaten als Extras.
|
||||||
|
// Hat das geklappt, ist die UI schon befuellt — sonst normal laden.
|
||||||
|
if (!applyProvisioning(intent)) loadIntoUi(AgentConfig.load(this))
|
||||||
|
|
||||||
// Fire TV: kein Touchscreen — der QR-Scan ist dort sinnlos (keine Kamera),
|
// Fire TV: kein Touchscreen — der QR-Scan ist dort sinnlos (keine Kamera),
|
||||||
// und der erste Fokus muss gesetzt sein, damit die Fernbedienung greift.
|
// und der erste Fokus muss gesetzt sein, damit die Fernbedienung greift.
|
||||||
@@ -209,6 +212,48 @@ class MainActivity : AppCompatActivity() {
|
|||||||
statusView.text = "Status: ${AgentService.status} $dot"
|
statusView.text = "Status: ${AgentService.status} $dot"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onNewIntent(intent: Intent?) {
|
||||||
|
super.onNewIntent(intent)
|
||||||
|
if (intent != null && applyProvisioning(intent)) return
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fern-Einrichtung per Intent-Extras (vom Satelliten via ADB gestartet).
|
||||||
|
*
|
||||||
|
* Auf einem Fire TV gibt es keine Kamera (kein QR) und Tippen mit der
|
||||||
|
* Fernbedienung ist muehsam — deshalb kann der Satellit die Zugangsdaten
|
||||||
|
* direkt mitgeben:
|
||||||
|
* adb shell am start -n de.hackersoft.ariaagent/.MainActivity \
|
||||||
|
* -a de.hackersoft.ariaagent.PROVISION \
|
||||||
|
* -e host rvs.example.com --ei port 443 --ez tls true -e token XYZ
|
||||||
|
*
|
||||||
|
* Gibt true zurueck, wenn eingerichtet wurde (dann startet der Dienst gleich).
|
||||||
|
*/
|
||||||
|
private fun applyProvisioning(intent: Intent): Boolean {
|
||||||
|
val host = intent.getStringExtra("host")?.trim().orEmpty()
|
||||||
|
val token = intent.getStringExtra("token")?.trim().orEmpty()
|
||||||
|
if (host.isEmpty() || token.isEmpty()) return false
|
||||||
|
|
||||||
|
val old = AgentConfig.load(this)
|
||||||
|
val cfg = AgentConfig(
|
||||||
|
host = host,
|
||||||
|
port = intent.getIntExtra("port", old.port.takeIf { it > 0 } ?: 443),
|
||||||
|
tls = intent.getBooleanExtra("tls", old.tls),
|
||||||
|
token = token,
|
||||||
|
name = intent.getStringExtra("name")?.trim().takeUnless { it.isNullOrEmpty() } ?: old.name,
|
||||||
|
controlEnabled = intent.getBooleanExtra("control", true),
|
||||||
|
replyMode = intent.getStringExtra("reply_mode")?.trim()
|
||||||
|
.takeUnless { it.isNullOrEmpty() } ?: old.replyMode,
|
||||||
|
)
|
||||||
|
cfg.save(this)
|
||||||
|
AgentService.start(this)
|
||||||
|
// Sichtbar machen, dass die Konfiguration von aussen kam (Transparenz).
|
||||||
|
toast("Von ARIA eingerichtet: ${cfg.host} — Agent gestartet")
|
||||||
|
loadIntoUi(cfg)
|
||||||
|
refreshStatus()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
/** Sprech-Taste: startet/stoppt die Aufnahme im laufenden Dienst. */
|
/** Sprech-Taste: startet/stoppt die Aufnahme im laufenden Dienst. */
|
||||||
private fun talkToAria() {
|
private fun talkToAria() {
|
||||||
if (!AgentConfig.load(this).isValid()) { toast("Erst verbinden"); return }
|
if (!AgentConfig.load(this).isValid()) { toast("Erst verbinden"); return }
|
||||||
|
|||||||
@@ -45,9 +45,12 @@ CONTROL_ENABLED=true
|
|||||||
# adb.connect ADB-Verbindung aufbauen (params: ip)
|
# adb.connect ADB-Verbindung aufbauen (params: ip)
|
||||||
# adb.devices verbundene ADB-Geraete auflisten
|
# adb.devices verbundene ADB-Geraete auflisten
|
||||||
# adb.shell Shell-Kommando auf dem Android-Geraet (params: ip, cmd)
|
# 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
|
# ACHTUNG: Diese Liste ueberschreibt den Code-Default. Wird eine neue Aktion
|
||||||
# ergaenzt, MUSS sie hier nachgetragen werden — sonst lehnt der Satellit sie ab.
|
# 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) ──────────────────────────────
|
# ─── ADB / Android-Rollout (optional) ──────────────────────────────
|
||||||
# Fire TV Sticks & Android-Geraete im LAN: APK ausrollen ohne Datei-Kopieren.
|
# 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",
|
os.environ.get("CONTROL_ALLOWLIST",
|
||||||
"dial.launch,wol,http.get,snmp.get,snmp.walk,snmp.printer,"
|
"dial.launch,wol,http.get,snmp.get,snmp.walk,snmp.printer,"
|
||||||
"snmp.ports,snmp.info,fritzbox.info,fritzbox.hosts,ssh.exec,"
|
"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()
|
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)
|
return await loop.run_in_executor(None, _do_adb_install, params)
|
||||||
if action == "adb.shell":
|
if action == "adb.shell":
|
||||||
return await loop.run_in_executor(None, _do_adb_shell, params)
|
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."}
|
return {"ok": False, "error": f"Aktion '{action}' nicht implementiert."}
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
return {"ok": False, "error": f"{action} fehlgeschlagen: {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}}
|
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:
|
def _do_adb_shell(params: dict) -> dict:
|
||||||
"""Shell-Kommando auf dem Android-Geraet (Diagnose, z.B. 'pm list packages')."""
|
"""Shell-Kommando auf dem Android-Geraet (Diagnose, z.B. 'pm list packages')."""
|
||||||
target = _adb_target(params)
|
target = _adb_target(params)
|
||||||
@@ -1343,8 +1451,19 @@ def _do_adb_install(params: dict) -> dict:
|
|||||||
txt = f"{out} {err2}".strip()
|
txt = f"{out} {err2}".strip()
|
||||||
size_mb = round(os.path.getsize(apk) / 1024 / 1024, 1)
|
size_mb = round(os.path.getsize(apk) / 1024 / 1024, 1)
|
||||||
if rc == 0 and "success" in txt.lower():
|
if rc == 0 and "success" in txt.lower():
|
||||||
return {"ok": True, "result": {"target": target, "apk_mb": size_mb,
|
res = {"target": target, "apk_mb": size_mb,
|
||||||
"message": f"APK installiert ({size_mb} 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()
|
low = txt.lower()
|
||||||
if "signatures do not match" in low or "update_incompatible" in low:
|
if "signatures do not match" in low or "update_incompatible" in low:
|
||||||
return {"ok": False, "error": "Signatur passt nicht zur installierten Version — "
|
return {"ok": False, "error": "Signatur passt nicht zur installierten Version — "
|
||||||
|
|||||||
Reference in New Issue
Block a user