Compare commits
4
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ac40afc856 | ||
|
|
ff6d474f29 | ||
|
|
44aef71813 | ||
|
|
27381e7038 |
+46
-1
@@ -1624,8 +1624,19 @@
|
|||||||
Spart das Abtippen auf dem Fernseher: Host und Token gehen direkt vom
|
Spart das Abtippen auf dem Fernseher: Host und Token gehen direkt vom
|
||||||
Server an den Agenten — QR-Scan geht am Fire TV ja nicht.
|
Server an den Agenten — QR-Scan geht am Fire TV ja nicht.
|
||||||
</div>
|
</div>
|
||||||
|
<div style="margin-top:10px;margin-left:26px;">
|
||||||
|
<label style="display:block;margin-bottom:4px;color:#8888AA;">Name des Geräts</label>
|
||||||
|
<input id="apk-name" type="text" style="width:100%;padding:6px;background:#0D0D1A;border:1px solid #1E1E2E;border-radius:4px;color:#E0E0F0;"
|
||||||
|
placeholder="z.B. Wohnzimmer Fire TV">
|
||||||
|
<div style="color:#666;font-size:11px;margin-top:4px;">
|
||||||
|
So spricht ARIA das Gerät an („mach einen Screenshot vom Wohnzimmer Fire TV").
|
||||||
|
Hier am PC eingeben — auf dem Fernseher wäre das Tippen mühsam.
|
||||||
|
</div>
|
||||||
|
</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="runApkProvision()" style="margin-left:6px;"
|
||||||
|
title="Nur Zugang/Namen setzen — App bleibt wie sie ist">Nur einrichten</button>
|
||||||
<button class="btn secondary" onclick="runApkStatus()" style="margin-left:6px;">Status prüfen</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>
|
||||||
@@ -4716,11 +4727,22 @@
|
|||||||
'<strong style="color:#E0E0F0;">💻 ' + escapeHtml(h.name || h.hostId) + '</strong> ' +
|
'<strong style="color:#E0E0F0;">💻 ' + escapeHtml(h.name || h.hostId) + '</strong> ' +
|
||||||
'<span style="color:#666;font-size:11px;">id=' + escapeHtml(h.hostId) + '</span></div>' +
|
'<span style="color:#666;font-size:11px;">id=' + escapeHtml(h.hostId) + '</span></div>' +
|
||||||
'<button onclick="openAgentHistory(\'' + escapeHtml(h.hostId) + '\')" style="background:#12121E;color:#FFD60A;border:1px solid #FFD60A44;border-radius:6px;padding:4px 10px;font-size:12px;cursor:pointer;">🕘 Historie</button>' +
|
'<button onclick="openAgentHistory(\'' + escapeHtml(h.hostId) + '\')" style="background:#12121E;color:#FFD60A;border:1px solid #FFD60A44;border-radius:6px;padding:4px 10px;font-size:12px;cursor:pointer;">🕘 Historie</button>' +
|
||||||
|
'<button onclick="forgetHost(\'' + escapeHtml(h.hostId) + '\',\'' + escapeHtml(h.name || h.hostId) + '\')" title="Aus der Liste entfernen (z.B. alter Name nach dem Umbenennen). Meldet sich der Agent wieder, erscheint er von selbst."' +
|
||||||
|
' style="background:#12121E;color:#8888AA;border:1px solid #1E1E2E;border-radius:6px;padding:4px 9px;font-size:12px;cursor:pointer;margin-left:4px;">✕</button>' +
|
||||||
'</div>' +
|
'</div>' +
|
||||||
'<div style="color:#8888AA;font-size:11px;margin-top:4px;">' + escapeHtml(h.os || '') + ' · kann: ' + escapeHtml(caps) + ' · ' + ctrl + '</div>' +
|
'<div style="color:#8888AA;font-size:11px;margin-top:4px;">' + escapeHtml(h.os || '') + ' · kann: ' + escapeHtml(caps) + ' · ' + ctrl + '</div>' +
|
||||||
'</div>';
|
'</div>';
|
||||||
}).join('');
|
}).join('');
|
||||||
}
|
}
|
||||||
|
/** Entfernt einen Agenten aus der Liste (z.B. den alten Namen nach dem
|
||||||
|
* Umbenennen). Läuft er noch, meldet er sich von selbst wieder. */
|
||||||
|
function forgetHost(hostId, name) {
|
||||||
|
if (!confirm('"' + name + '" aus der Liste entfernen?\n\n' +
|
||||||
|
'Das stoppt den Agenten NICHT — läuft er noch, erscheint er ' +
|
||||||
|
'beim nächsten Lebenszeichen wieder.')) return;
|
||||||
|
send({ action: 'host_forget', hostId });
|
||||||
|
}
|
||||||
|
|
||||||
// ── APK-Rollout auf Android/Fire TV (per Satellit + ADB) ─────────
|
// ── APK-Rollout auf Android/Fire TV (per Satellit + ADB) ─────────
|
||||||
let apkTarget = null; // {satId, ip, name}
|
let apkTarget = null; // {satId, ip, name}
|
||||||
let apkRequestId = null;
|
let apkRequestId = null;
|
||||||
@@ -4731,6 +4753,11 @@
|
|||||||
document.getElementById('apk-status').textContent = '';
|
document.getElementById('apk-status').textContent = '';
|
||||||
const log = document.getElementById('apk-log');
|
const log = document.getElementById('apk-log');
|
||||||
log.style.display = 'none'; log.textContent = '';
|
log.style.display = 'none'; log.textContent = '';
|
||||||
|
// Namensfeld vorbelegen — aber nicht mit Seriennummern/IP-Platzhaltern,
|
||||||
|
// die die Netz-Erkennung bei Fire TV oft liefert (z.B. 0d94400fa5b63301).
|
||||||
|
const looksTechnical = !name || /^[0-9a-f]{8,}$/i.test(name) ||
|
||||||
|
/^\d{1,3}(\.\d{1,3}){3}$/.test(name);
|
||||||
|
document.getElementById('apk-name').value = looksTechnical ? '' : name;
|
||||||
document.getElementById('apk-modal').classList.add('open');
|
document.getElementById('apk-modal').classList.add('open');
|
||||||
}
|
}
|
||||||
function closeApkDeploy() {
|
function closeApkDeploy() {
|
||||||
@@ -4755,12 +4782,30 @@
|
|||||||
// Server ergaenzt Host/Port/Token — steht bewusst NICHT im Browser-Formular.
|
// Server ergaenzt Host/Port/Token — steht bewusst NICHT im Browser-Formular.
|
||||||
if (document.getElementById('apk-provision').checked) {
|
if (document.getElementById('apk-provision').checked) {
|
||||||
params.provision = true;
|
params.provision = true;
|
||||||
params.name = apkTarget.name;
|
// Am PC getippter Name gewinnt; sonst der aus der Netz-Erkennung (der
|
||||||
|
// ist bei Fire TV oft nur die Seriennummer).
|
||||||
|
params.name = document.getElementById('apk-name').value.trim() || 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 });
|
requestId: apkRequestId, params });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Nur einrichten: RVS-Zugang + Namen setzen, ohne die APK neu zu installieren
|
||||||
|
* (z.B. um ein bereits ausgerolltes Gerät umzubenennen). */
|
||||||
|
function runApkProvision() {
|
||||||
|
if (!apkTarget) return;
|
||||||
|
apkRequestId = 'apk_' + Date.now() + '_' + Math.floor(Math.random() * 100000);
|
||||||
|
document.getElementById('apk-status').innerHTML = '<span style="color:#FFD60A;">richte ein …</span>';
|
||||||
|
const log = document.getElementById('apk-log');
|
||||||
|
log.style.display = 'block';
|
||||||
|
log.textContent = 'Übertrage Zugang und Namen an den Agenten …\n' +
|
||||||
|
'Die App startet dabei kurz auf dem Bildschirm — das ist normal.';
|
||||||
|
send({ action: 'sat_command', satellite: apkTarget.satId, cmd: 'adb.provision',
|
||||||
|
requestId: apkRequestId,
|
||||||
|
params: { ip: apkTarget.ip, provision: true,
|
||||||
|
name: document.getElementById('apk-name').value.trim() || apkTarget.name } });
|
||||||
|
}
|
||||||
|
|
||||||
/** Prüft, ob der Agent auf dem Gerät installiert ist und läuft. */
|
/** Prüft, ob der Agent auf dem Gerät installiert ist und läuft. */
|
||||||
function runApkStatus() {
|
function runApkStatus() {
|
||||||
if (!apkTarget) return;
|
if (!apkTarget) return;
|
||||||
|
|||||||
@@ -2812,6 +2812,14 @@ wss.on("connection", (ws) => {
|
|||||||
} else if (msg.action === "host_list") {
|
} else if (msg.action === "host_list") {
|
||||||
// Browser will die aktuelle Host-Agenten-Liste.
|
// Browser will die aktuelle Host-Agenten-Liste.
|
||||||
ws.send(JSON.stringify({ type: "host_update", hosts: hostList() }));
|
ws.send(JSON.stringify({ type: "host_update", hosts: hostList() }));
|
||||||
|
} else if (msg.action === "host_forget") {
|
||||||
|
// Alten/umbenannten Agenten aus der Liste werfen. Meldet er sich wieder
|
||||||
|
// (host_hello), taucht er automatisch neu auf — das ist also gefahrlos.
|
||||||
|
const id = String(msg.hostId || "");
|
||||||
|
if (id && hosts.delete(id)) {
|
||||||
|
log("info", "host", `Agent '${id}' aus der Liste entfernt`);
|
||||||
|
broadcastHosts();
|
||||||
|
}
|
||||||
} else if (msg.action === "rooms_query") {
|
} else if (msg.action === "rooms_query") {
|
||||||
// Browser will die RVS-Raum-Diagnose — via persistente rvsWs anfragen,
|
// Browser will die RVS-Raum-Diagnose — via persistente rvsWs anfragen,
|
||||||
// die Antwort (rooms_info) kommt auf derselben Verbindung zurueck.
|
// die Antwort (rooms_info) kommt auf derselben Verbindung zurueck.
|
||||||
|
|||||||
@@ -11,8 +11,8 @@ android {
|
|||||||
applicationId 'de.hackersoft.ariaagent'
|
applicationId 'de.hackersoft.ariaagent'
|
||||||
minSdk 26
|
minSdk 26
|
||||||
targetSdk 33 // 33 vermeidet die Foreground-Service-Typ-Pflicht von 34
|
targetSdk 33 // 33 vermeidet die Foreground-Service-Typ-Pflicht von 34
|
||||||
versionCode 100
|
versionCode 101
|
||||||
versionName '0.0.1.0'
|
versionName '0.0.1.1'
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fester Signaturschlüssel: jeder Build signiert mit DEMSELBEN Key, damit
|
// Fester Signaturschlüssel: jeder Build signiert mit DEMSELBEN Key, damit
|
||||||
|
|||||||
@@ -38,6 +38,7 @@
|
|||||||
<activity
|
<activity
|
||||||
android:name=".MainActivity"
|
android:name=".MainActivity"
|
||||||
android:exported="true"
|
android:exported="true"
|
||||||
|
android:launchMode="singleTop"
|
||||||
android:label="@string/app_name">
|
android:label="@string/app_name">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN" />
|
<action android:name="android.intent.action.MAIN" />
|
||||||
|
|||||||
@@ -214,7 +214,9 @@ class MainActivity : AppCompatActivity() {
|
|||||||
|
|
||||||
override fun onNewIntent(intent: Intent?) {
|
override fun onNewIntent(intent: Intent?) {
|
||||||
super.onNewIntent(intent)
|
super.onNewIntent(intent)
|
||||||
if (intent != null && applyProvisioning(intent)) return
|
if (intent == null) return
|
||||||
|
setIntent(intent)
|
||||||
|
applyProvisioning(intent)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -162,7 +162,7 @@ OUT_MAX_CHARS_HARD = int(os.environ.get("OUT_MAX_CHARS_HARD", "200000") or "2000
|
|||||||
FILE_MAX_BYTES = int(os.environ.get("FILE_MAX_BYTES", str(10 * 1024 * 1024)) or str(10 * 1024 * 1024))
|
FILE_MAX_BYTES = int(os.environ.get("FILE_MAX_BYTES", str(10 * 1024 * 1024)) or str(10 * 1024 * 1024))
|
||||||
|
|
||||||
# Version (wird von release_agent.sh beim Release gesetzt).
|
# Version (wird von release_agent.sh beim Release gesetzt).
|
||||||
AGENT_VERSION = "0.0.1.0"
|
AGENT_VERSION = "0.0.1.1"
|
||||||
|
|
||||||
HEARTBEAT_SEC = 25
|
HEARTBEAT_SEC = 25
|
||||||
|
|
||||||
|
|||||||
+25
-6
@@ -1301,22 +1301,41 @@ def _do_adb_provision(params: dict) -> dict:
|
|||||||
return {"ok": False, "error": f"'{package}' ist nicht installiert — "
|
return {"ok": False, "error": f"'{package}' ist nicht installiert — "
|
||||||
"erst adb.install ausfuehren."}
|
"erst adb.install ausfuehren."}
|
||||||
|
|
||||||
|
# WICHTIG: 'adb shell' haengt die Argumente zu EINER Kommandozeile zusammen,
|
||||||
|
# die auf dem Geraet nochmal von der Shell zerlegt wird. Werte mit Leerzeichen
|
||||||
|
# ("Wohnzimmer Fire TV") wuerden dabei abgeschnitten -> jeden Wert quoten.
|
||||||
|
import shlex
|
||||||
|
q = shlex.quote
|
||||||
|
# Der Intent MUSS eindeutig sein: Android vergleicht beim Start Komponente,
|
||||||
|
# Action und Daten — NICHT die Extras. Ohne eigene Daten-URI gilt ein zweites
|
||||||
|
# Provisioning als "gleicher Intent", die Activity wird nur nach vorn geholt
|
||||||
|
# und onCreate/onNewIntent laufen nie -> der alte Name bliebe stehen.
|
||||||
|
stamp = str(int(time.time() * 1000))
|
||||||
args = ["-s", target, "shell", "am", "start",
|
args = ["-s", target, "shell", "am", "start",
|
||||||
"-n", f"{package}/.MainActivity",
|
"-n", f"{package}/.MainActivity",
|
||||||
"-a", f"{package}.PROVISION",
|
"-a", f"{package}.PROVISION",
|
||||||
"-e", "host", rvs_host,
|
"-d", q(f"aria://provision/{stamp}"),
|
||||||
"-e", "token", rvs_token,
|
"-e", "host", q(rvs_host),
|
||||||
|
"-e", "token", q(rvs_token),
|
||||||
"--ei", "port", str(int(params.get("rvs_port") or 443)),
|
"--ei", "port", str(int(params.get("rvs_port") or 443)),
|
||||||
"--ez", "tls", "true" if _as_bool(params.get("rvs_tls"), True) else "false",
|
"--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"]
|
"--ez", "control", "true" if _as_bool(params.get("control"), True) else "false"]
|
||||||
if params.get("name"):
|
if params.get("name"):
|
||||||
args += ["-e", "name", str(params["name"])]
|
args += ["-e", "name", q(str(params["name"]))]
|
||||||
if params.get("reply_mode"):
|
if params.get("reply_mode"):
|
||||||
args += ["-e", "reply_mode", str(params["reply_mode"])]
|
args += ["-e", "reply_mode", q(str(params["reply_mode"]))]
|
||||||
|
|
||||||
rc, out, err = _adb_run(args, timeout=60)
|
rc, out, err = _adb_run(args, timeout=60)
|
||||||
if rc != 0 or "Error" in f"{out} {err}":
|
txt = f"{out} {err}".strip()
|
||||||
return {"ok": False, "error": (f"{out} {err}".strip() or f"exit {rc}")[:400]}
|
if rc != 0 or "Error" in txt:
|
||||||
|
return {"ok": False, "error": (txt or f"exit {rc}")[:400]}
|
||||||
|
# Android meldet das, wenn es den Intent als "schon offen" abtut und die
|
||||||
|
# Activity nur nach vorn holt — dann kaeme die neue Konfiguration NICHT an.
|
||||||
|
if "brought to the front" in txt.lower() or "not started" in txt.lower():
|
||||||
|
return {"ok": False, "error":
|
||||||
|
"Android hat den Einrichtungs-Intent nicht ausgeliefert (Activity "
|
||||||
|
"war schon offen). Auf dem Geraet die Agent-App einmal schliessen "
|
||||||
|
"und erneut 'Nur einrichten' druecken. Details: " + txt[:200]}
|
||||||
return {"ok": True, "result": {
|
return {"ok": True, "result": {
|
||||||
"target": target, "package": package,
|
"target": target, "package": package,
|
||||||
"message": f"Agent eingerichtet (RVS {rvs_host}) und gestartet"}}
|
"message": f"Agent eingerichtet (RVS {rvs_host}) und gestartet"}}
|
||||||
|
|||||||
Reference in New Issue
Block a user