20 KiB
04 — Modding: Rezepte für eigene UI, Apps, Services, Bloat
Voraussetzung: Root + SSH (siehe ../root/ROOT-GUIDE.md).
Spielregel: Eine Änderung, dann testen, dann die nächste. Backup vorher. SSH/Root-Loop als Rettungsweg behalten. Kein Flash/.pkg.
Die Rezepte sind nach Risiko sortiert:
| Route | Ziel | Risiko | Reversibel |
|---|---|---|---|
| A | Eigene App-Tile (Web-App im Browser) | niedrig | ja (Datei-Backup) |
| B | Eigene UI-Seite (/3rd_rw/UI/index.html) |
mittel | ja (Backup + UI-Restart) |
| C | Launcher-Daten/Skin (Tiles, Kategorien, Icons) | niedrig–mittel | ja (Backup) |
| D | Eigener Service/eigene Funktion (root) | mittel | ja (Skripte/Wrapper) |
| E | Ro-Dateien per Bind-Mount/Overlay patchen | mittel–hoch | ja (Unmount/Reboot) |
| F | Statische Tools/SSH/Deploy | niedrig | ja |
Route A — Eigene App-Tile (der sichere Einstieg)
Ziel: Ein eigener Eintrag im Launcher, der eine lokale oder HTTP-Seite im Web-Browser öffnet (nicht die UI ersetzt).
A.1 Seite bauen
Lokal auf dem TV (rw):
/3rd_rw/appdata/custom/index.html
/3rd_rw/appdata/custom/app.js
/3rd_rw/appdata/custom/icon.png
Beispiel index.html:
<!doctype html>
<html><head><meta charset="utf-8"><title>MyApp</title>
<style>body{background:#111;color:#eee;font-family:sans-serif;margin:0;padding:2em}</style>
</head><body>
<h1>MyApp</h1>
<p>Läuft im hisense_browser (root, --allow-file-access-from-files).</p>
<button onclick="hi()">HiBrowser test</button>
<pre id="out"></pre>
<script>
function hi(){
var o={};
try{o.hb=(typeof HiBrowser);}catch(e){o.hb=String(e);}
try{o.file=(typeof HiBrowser!=='undefined')?String(HiBrowser.File.download("file:///3rd_rw/root_proof.txt","myapp_proof.txt",1)):"no";}catch(e){o.file=String(e);}
document.getElementById('out').textContent=JSON.stringify(o,null,2);
}
</script>
</body></html>
- HTTP-Variante: Seite auf dem Host (
http://<host>:9000/...) servieren; der TV-Browser lädt sie. - Lokal-Variante:
file:///3rd_rw/appdata/custom/index.html; der Browser hat--allow-file-access-from-files. - Die Seite kann
HiBrowser(File,System,loadLibrary,Cache,JsonWrite) undHisense_*nutzen. Root-Kontext.
A.2 Appinfo.json ergänzen
Datei: /3rd_rw/appdata/launcher/Appinfo.json (rw). Backup:
cp /3rd_rw/appdata/launcher/Appinfo.json /3rd_rw/backup/Appinfo.json.$(date +%s)
Eintrag ergänzen (bestehende AppInfo-Einträge behalten!):
{"AppInfo":[
{"AppId":"MyApp",
"Thumb":"file:///3rd_rw/appdata/custom/icon.png",
"Icon_96":"file:///3rd_rw/appdata/custom/icon.png",
"Image":"file:///3rd_rw/appdata/custom/icon.png",
"URL":"file:///3rd_rw/appdata/custom/index.html",
"AppName":"MyApp",
"Title":"MyApp",
"IconURL":"file:///3rd_rw/appdata/custom/icon.png",
"StartCommand":"file:///3rd_rw/appdata/custom/index.html",
"InstallTime":"2026-10-26",
"RunTimes":0,
"StoreType":"foxxum",
"PreInstall":false}
]}
StartCommand/URL= HTTP oderfile://.Image/IconURL/Thumb/Icon_96= Icon; HTTP/file://; FallbackuseDefaultImg.StoreTypeals String ("foxxum"; die UI mappt überstoreTypeList).PreInstall:false(User-App);canRemove=true.
Per TV-Seite/JS (ohne SSH):
// Backup lesen (optional)
var old = fileXHR("file:///3rd_rw/appdata/launcher/Appinfo.json");
// Neuen Inhalt schreiben
HiBrowser.File.write("launcher/Appinfo.json", newJsonString, 1);
File.write schreibt Text; das JSON ist Text. (Für Binär-Icons: File.download von HTTP/file://.)
A.3 Launcher/UI neu laden
- UI neu starten (siehe
03, Abschnitt 8):kill <hisenseUI-PID>;nohup /3rd/opera4x/hisenseUI/start-UI.sh &. - Oder TV rebooten.
- Im Launcher/„Alle Apps“ erscheint die Tile; Launch öffnet den Web-Browser mit der URL (nicht die UI-Seite).
A.4 Test / Rollback
- Test: Tile öffnen;
HiBrowser-Testbutton;file://-Seite; HTTP-Seite. - Rollback:
Appinfo.json-Backup zurückkopieren; UI neu starten. Oder Eintrag entfernen. - Nicht
preset.txtfür User-Apps missbrauchen; derpreset-Cache kann überschrieben werden.Appinfo.jsonist der richtige Ort.
Route B — Eigene UI-Seite (/3rd_rw/UI/index.html)
Ziel: Die TV-UI (PIXI/hisenseUI) durch eine eigene Seite ersetzen. Höchstes Risiko (schwarzer Bildschirm), aber machbar.
B.1 Backup
mkdir -p /3rd_rw/backup
cp -a /3rd_rw/UI /3rd_rw/backup/UI.$(date +%Y%m%d-%H%M%S)
B.2 Minimale Custom-UI bauen
Datei: /3rd_rw/UI/custom.html (neu; index.html bleibt vorerst unangetastet).
Vorlage: templates/custom-ui.html.
<!doctype html>
<html><head><meta charset="utf-8">
<meta name="viewport" content="width=1280,height=720">
<title>My TV</title>
<style>
html,body{margin:0;width:100%;height:100%;background:#0b0b0b;color:#eee;font-family:sans-serif;overflow:hidden}
.wrap{padding:2vw}.row{display:flex;gap:1vw;flex-wrap:wrap}
button{font-size:1.4vw;padding:1vw 1.6vw;border-radius:.6vw;border:1px solid #555;background:#1c1c1c;color:#eee}
pre{font-size:1vw;color:#9f9}
</style></head>
<body><div class="wrap">
<h1>My TV</h1>
<div class="row">
<button onclick="launch('netflix')">Netflix</button>
<button onclick="launch('youtube')">YouTube</button>
<button onclick="launch('opera4x')">TV Browser</button>
<button onclick="launch('dtv_app_mtk')">TV</button>
<button onclick="probe()">APIs</button>
</div>
<pre id="out"></pre>
<script>
function launch(id){
var o={};
try{
if (typeof Hisense_StartApp === 'function') { Hisense_StartApp(':am,'+id+',:start='+id); o.via='Hisense_StartApp'; }
else if (typeof HiBrowser !== 'undefined' && HiBrowser.System) { HiBrowser.System.startApp(':am,'+id+',:start='+id); o.via='HiBrowser'; }
else if (typeof TVAPI !== 'undefined' && TVAPI.StartApp) { TVAPI.StartApp(id); o.via='TVAPI'; }
else if (typeof guijs !== 'undefined' && guijs.StartApp) { guijs.StartApp(id); o.via='guijs'; }
else o.via='none';
}catch(e){o.err=String(e);}
document.getElementById('out').textContent=JSON.stringify(o,null,2);
}
function probe(){
var o={Hisense_StartApp:typeof Hisense_StartApp,HiBrowser:typeof HiBrowser,TVAPI:typeof TVAPI,guijs:typeof guijs,model:typeof model,UI:typeof UI};
document.getElementById('out').textContent=JSON.stringify(o,null,2);
}
</script></div></body></html>
- API-Verfügbarkeit ist
UNVERIFIED: Im UI-DevTools (9223) prüfen, welche Globals existieren und welche SignaturHisense_StartApp/TVAPI.StartApp/guijs.StartApphat. Der Web-Browser hatHiBrowser.System.startApp(":am,…")(belegt); die UI kann andere Wrapper haben. - Größe: UI nutzt 1280×720 CSS (PIXI-Canvas); der Screen ist 1920×1080 (
OPERA_EGL_SCREEN_SIZE).100vw/100vh/meta viewportnutzen.
B.3 Testen, ohne die UI zu ersetzen
custom.htmlper Web-Browser öffnen:http://<host>:9000/...oderfile:///3rd_rw/UI/custom.htmlimhisense_browser(TV-Browser). DortHiBrowser/Hisense_*testen (die UI-spezifischen APIs fehlen evtl.).- UI DevTools 9223:
window.location = "file:///3rd_rw/UI/custom.html"nur in einem Test-Tab? Die UI hat ein Fenster; ein Navigieren ersetzt die UI. Besser: Custom-Seite in einem neuenhisenseUI-Prozess starten (siehe B.5) oder per Web-Browser testen. - Wenn die Custom-Seite im Web-Browser gut aussieht, UI übernehmen.
B.4 index.html ersetzen
Variante 1: Datei ersetzen (einfach, reversibel per Backup):
cp /3rd_rw/UI/index.html /3rd_rw/backup/index.orig.html
cp /3rd_rw/UI/custom.html /3rd_rw/UI/index.html
# UI neu starten
kill $(pidof hisenseUI)
nohup /3rd/opera4x/hisenseUI/start-UI.sh >/tmp/hisenseUI.log 2>&1 &
Variante 2: start-UI.sh per Bind-Mount auf custom.html zeigen (lässt index.html unangetastet):
# /3rd_rw/patch/start-UI-custom.sh (siehe templates/start-UI-custom.sh)
mount --bind /3rd_rw/patch/start-UI-custom.sh /3rd/opera4x/hisenseUI/start-UI.sh
# UI neu starten
Das ist ein Datei-Bind-Mount; kein full overlay. start-UI-custom.sh startet hisenseUI mit -u file:///3rd_rw/UI/custom.html und den gleichen Flags wie das Original.
Variante 3: Vendor-Overlay /3rd_rw/patch/opera4x (full dir bind):
mkdir -p /3rd_rw/patch/opera4x
cp -a /3rd/opera4x/. /3rd_rw/patch/opera4x/ # vollständige Kopie!
# eigene Dateien überschreiben, z. B. hisenseUI/start-UI.sh
reboot
- Nur mit vollständiger Kopie/Symlink-Baum. Der
mount -o bindersetzt/3rd/opera4xkomplett. Fehlende Dateien = Browser/UI kaputt. - Vorteil: überlebt Reboots (autorun bindet vor UI-Start). Nachteil: groß/fehleranfällig.
B.5 UI-Restart / Rettung
# UI-Prozess
ps | grep hisenseUI | grep -v grep
kill <PID>
nohup /3rd/opera4x/hisenseUI/start-UI.sh >/tmp/hisenseUI.log 2>&1 &
# Rettung bei schwarzem Bildschirm:
cp /3rd_rw/backup/index.orig.html /3rd_rw/UI/index.html
kill $(pidof hisenseUI)
nohup /3rd/opera4x/hisenseUI/start-UI.sh >/tmp/hisenseUI.log 2>&1 &
- SSH/Root-Loop funktionieren unabhängig von der UI. Der Web-Browser (
hisense_browser/Recon) kann ebenfalls weiterlaufen. hisenseUI-Log:/tmp/hisenseUI.log; UI-DevTools 9223 (falls die Seite lädt).- Kein Flash:
index.html/custom.htmlliegen in/3rd_rw; Backup/Rollback per Datei.
B.6 Was die UI-Seite kann
- Vollbild-HTML/CSS/JS im Chromium/Opera-UI-Browser.
Hisense_*/TVAPI/guijs/model(UI-Kontext; Signaturen im DevTools prüfen).file://-Zugriff (--disable-web-security,--allow-file-access-from-files? Das UI hat--disable-web-security; lokale Files ja).- Video/Canvas/WebGL; die UI nutzt GLES/Mali.
- Kein direkter Shell-Zugriff aus JS; der Root-Loop/SSH ist der Weg für native Aktionen.
Route C — Launcher-Daten/Skin (Tiles, Kategorien, Icons)
Ziel: Apps hinzufügen/ausblenden, Kategorien/Icons ändern, ohne die UI-Seite zu ersetzen.
C.1 Tiles ausblenden (einfach, persistent)
Datei: /3rd_rw/appdata/launcher/deletedPresetApp.txt (JSON-Array).
Beispiel: templates/hide-preset.json; die Preinstalled-Liste in 05-bloat.md.
[
"netflix",
"amazon",
"youtube",
"https://cd-dmgz.bamgrid.com/bbd/hisense_tv/index.html",
"http://8577004de253-ott-tv.maxdome.de",
"https://prod-hisense-ui40-app.rakuten.tv/",
"https://tvapp.rlaxxtv.com/r/no-animations/720/?customer=Hisense"
]
- Der Launcher/
ldi.jsfiltert die Kacheln; diepreset.txtist nur Cache. - Stand 2026-10-26: Die alten Probe-URLs (data:, javascript:, 127.0.0.1;cd…) wurden beim Cleanup entfernt; die Datei ist jetzt
[]. Zum Ausblenden die gewünschten Hide-Einträge setzen. - Backup:
cp deletedPresetApp.txt /3rd_rw/backup/.... - Wirkung: UI/Launcher neu starten (oder reboot).
C.2 Tiles hinzufügen
- User-App:
Appinfo.json(Route A). - Preset:
preset.txtergänzen (AppName,IconURL,URL,StartCommand,UrlType37,StoreType0,PreInstall:true). Aber:preset.txtist ein Cache; die UI/allAppsWriteToFileForUsekann sie überschreiben. Für dauerhaft:Appinfo.json(User-App) oder dieCategory*.xml-Quelle. - Kategorie-XML:
/3rd_rw/appdata/launcher/data/Category_1001_*.xml/Category_57_*.xml/CategoryIndex.xml/CategoryList_*.xml— die App-Tiles. Backup; XML-Struktur beibehalten;preset/Cache danach neu erzeugen lassen (UI-Restart). serviceUpdate.xml: Land/Timestamp; wenn es nicht zuDEUpasst, nutztldi.jsUI/data/-Default. Für eigene Daten ggf.UI/data/bearbeiten (Fallback) oderserviceUpdate/Categoryinlauncher/datakonsistent halten.
C.3 Icons / Skin
- Icons:
res/*.png;cache/index.json;Image/IconURL/Thumb/Icon_96;useDefaultImgals Fallback. - Skin/Assets:
/3rd_rw/UI/assets,/3rd_rw/UI/templates,/3rd_rw/UI/module,/3rd_rw/launcher/{css,img,html,js},/3rd_rw/VIDAAU2Launcher/*(Alt/Alternativ). - Aktiv:
/3rd_rw/UI(PIXI) +/3rd_rw/appdata/launcher(Daten). Dielauncher/VIDAAU2Launcher-HTML/JS sind Alt-/Alternativ-Skins; Änderungen dort nur testen, wenn die UI sie lädt (UNVERIFIED).
C.4 Test / Rollback
- Backup der
Appinfo/preset/deleted/Category*/res. - UI neu starten; Tiles prüfen; bei Problemen Backup zurück, UI neu starten/reboot.
- Die
deletedPresetApp.txtist der sicherste Hide-Hebel (kein XML-Eingriff).
Route D — Eigener Service / eigene Funktion (root)
Ziel: Ein eigener Prozess/Skript läuft als root beim Boot oder auf Kommando; native Aktionen, die JS nicht kann.
D.1 Früher Hook: /3rd_rw/developer.sh
Wird von /basic/autorun.sh Zeile 90 als root ausgeführt — vor ms_service/hisenseUI/app_man/delayrun.
Vorlage: templates/developer.sh.
#!/bin/sh
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/basic/bin
export PATH
exec >>/3rd_rw/developer.log 2>&1
echo "developer.sh $(date) boot=$(cat /proc/sys/kernel/random/boot_id 2>/dev/null)"
# Beispiel 1: eigenen Dienst starten
# /3rd_rw/bin/mydaemon &
# Beispiel 2: ro-Datei per Bind-Mount ersetzen (vor app_man)
# mount --bind /3rd_rw/etc/am_start.ini /basic/am_start.ini
# mount --bind /3rd_rw/etc/am_config /basic/am_config
# Beispiel 3: unerwünschten Dienst stubben (vor autorun-Zeile 162)
# mkdir -p /3rd_rw/stubs
# printf '#!/bin/sh\nexit 0\n' > /3rd_rw/stubs/true
# chmod 755 /3rd_rw/stubs/true
# mount --bind /3rd_rw/stubs/true /3rd/ms-screen/ms_service
# Beispiel 4: custom UI starten (vor autorun-Zeile 168)
# mount --bind /3rd_rw/patch/start-UI-custom.sh /3rd/opera4x/hisenseUI/start-UI.sh
# Beispiel 5: Root-Loop/SSH früher starten (statt HamService-Wrapper)
# /bin/sh /3rd_rw/appdata/root_loop.sh &
# /bin/sh /3rd_rw/sshd/start_sshd.sh &
- Reihenfolge:
developer.sh(90) →ms_service(162) →hisenseUI(168) →app_man(177) →delayrun(190). - Bind-Mounts von ro-Dateien in
developer.shwirken für die späteren Zeilen. - Log:
/3rd_rw/developer.log;set -xoptional. - Fallback: SSH/Root-Loop; wenn
developer.shdie UI/AM blockiert, per SSH korrigieren/entfernen, reboot.
D.2 Später Hook: HamService-Wrapper (aktuell für SSH/Root-Loop)
/3rd_rw/hamservice/HamService (Wrapper; startHam.sh ruft ihn nach delayrun + 15 s):
#!/bin/sh
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/basic/bin
export PATH
/bin/sh /3rd_rw/appdata/root_loop.sh >/dev/null 2>&1 &
[ -x /3rd_rw/sshd/start_sshd.sh ] && /bin/sh /3rd_rw/sshd/start_sshd.sh >/dev/null 2>&1 &
exec /3rd/hamservice/HamService "$@"
- Aktuelle Persistenz: Root-Loop + Dropbear. Das echte
HamServiceläuft weiter. - ACR abschalten:
exec /3rd/hamservice/HamService "$@"durchexit 0ersetzen (Root-Loop/SSH bleiben); oder ACR im HamService-Config deaktivieren (UNVERIFIED). - Eigene späte Services: vor dem
execstarten (z. B./3rd_rw/bin/mydaemon &). - Rollback:
rm /3rd_rw/hamservice/HamService; ln -s /3rd/hamservice/HamService /3rd_rw/hamservice/HamService.
D.3 Kommando-Kanal (root_loop)
/3rd_rw/appdata/root_loop.shliestroot_cmd.sh, schreibtroot_out.txt.- Auf dem TV:
HiBrowser.File.write("root_cmd.sh", "…\n", 1);fileXHR("file:///3rd_rw/appdata/root_out.txt"). - Host-Snippet:
../root/scripts/host/run_root_cmd.js. - Root-equivalent: nur vertrauenswürdige Schreiber (Browser root) zulassen.
D.4 Native App/Funktion über AM (:am)
HiBrowser.System.startApp(":am,<app>,:start=<val>")im Web-Browser (belegt).:am,<app>,:URL=<url>(ausapp_man-Strings);:stop,:backgm,:foregm,:pause,:resume,:shutdown,:suspend,:wakeup.- Neue native App braucht einen
am_config-Eintrag (exec/args) — ro; perdeveloper.sh-Bind-Mount einer modifiziertenam_config(vorapp_man). Oder ein eigener Service überdeveloper.sh/HamService(kein AM nötig). Hisense_StartAppin der UI: Signatur/Verhalten im UI-DevTools prüfen (UNVERIFIED).
D.5 Native UI (DirectFB/GLES) — optional
libdirectfb.so//basic/lib, Mali/GLES; die UI nutzt GLES, der Web-Browser DirectFB.- Ein eigener nativer UI-Prozess ist möglich, aber aufwendig; die Web-UI-Route (B) ist der pragmatische Weg.
dtv_svc/libdtv/app_manfür TV-Funktionen;libhspdk-jsx/HiBrowserfür die JS-Brücke.
Route E — Ro-Dateien patchen (Bind-Mount/Overlay)
Ziel: /basic/am_config, am_start.ini, delayrun.sh, ms_service, start-UI.sh, /3rd/...-Dateien ersetzen, ohne den ro Rootfs//3rd zu flashen.
E.1 Datei-Bind-Mount (empfohlen)
# Kopie + Änderung in /3rd_rw
cp /basic/am_start.ini /3rd_rw/etc/am_start.ini
vi /3rd_rw/etc/am_start.ini # oder per File.write
# in developer.sh (vor app_man):
mount --bind /3rd_rw/etc/am_start.ini /basic/am_start.ini
- Wirkt nur, wenn der Mount vor dem Lesen/Ausführen passiert:
developer.sh(Zeile 90) ist vorms_service/UI/app_man/delayrun. - Reboot-fest:
developer.shführt den Mount bei jedem Boot aus. - Rollback:
umount /basic/am_start.ini(oder reboot); Original bleibt unangetastet. - Für
ms_service/start-UI.sh/app_man: Stub-Skript (#!/bin/sh\nexit 0) permount --bindüber die Binärdatei; der-x-Check läuft danach ins Leere. - Achtung:
mount --bindauf eine Datei benötigt das Ziel; die ro-Datei existiert. Kein Overlay nötig.
E.2 patch/opera4x (full overlay)
autorun.shbindet/3rd_rw/patch/opera4xals Ganzes über/3rd/opera4x.- Nur mit vollständiger Kopie (
cp -a /3rd/opera4x/. /3rd_rw/patch/opera4x/) oder Symlink-Baum; sonst fehlen Browser/UI-Dateien. - Für einzelne Dateien: E.1 (Datei-Bind) statt full overlay.
- Rollback:
rm -rf /3rd_rw/patch/opera4x+ reboot.
E.3 am_config/am_start.ini (Bloat)
- Kopie nach
/3rd_rw/etc/, Sektionen entfernen/ändern, perdeveloper.shbinden. - Vor
app_man(Zeile 177);developer.sh(90). - Risiko:
dtv_app_mtk/app_mannicht entfernen (TV/Apps).netflix/dialserver/samba/browser_base/securetimeoptional (siehe05).
E.4 delayrun.sh
- Kopie nach
/3rd_rw/etc/delayrun.sh; unerwünschte Zeilen entfernen; perdeveloper.shüber/basic/delayrun.shbinden. - Vor
autorunZeile 190;developer.sh(90). - Risiko:
LauncherService(UI-Daten) undHamService(unsere Persistenz) behalten;swrc/runlog/Seriallogoptional.
Route F — Statische Tools / Deploy / SSH
- Kein Package Manager (siehe
06): statische ARM-Binaries bauen, nach/3rd_rw/binlegen, chmod +x. - Deploy:
File.download(HTTP → appdata), SSH (ssh root@… 'cat > /3rd_rw/bin/x' < x/base64), USB-Symlink,tar. scp/sftp: Dropbear allein hat keinen sftp-server. Der finale Ordner enthält einen statischen OpenSSH-9.9p1-sftp-server + gepatchten Dropbear; Installation:../root/scripts/host/install_sftp.sh(testet Port 2222, dann Tausch auf 22). Danach:Modernessftp root@192.168.188.94 # put -r /lokaler/UI-Ordner /3rd_rw/ # get /3rd_rw/UI/index.htmlscp(OpenSSH 9+) nutzt ebenfalls SFTP und funktioniert dann. Vor UI-Uploads: Backup/3rd_rw/backup/UI...; nach dem Upload UI neu starten (siehe Route B). SFTP ist root-equivalent, key-only, LAN.- SSH: siehe
../root/ROOT-GUIDE.md; key-only, PTY via devpts-Workaround. - Root-Loop: der bequemste Deploy-Kanal für kleine Dateien/Kommandos (siehe D.3).
Test- und Rollback-Plan
- Backup:
/3rd_rw/backup/<datum>/für jede geänderte Datei (UI/index.html,Appinfo.json,deletedPresetApp.txt,preset.txt,Category*.xml,am_config,am_start.ini,delayrun.sh,HamService-Wrapper). - Eine Änderung: nicht UI + AM + Bloat gleichzeitig.
- Test: UI neu starten; SSH/Recon offen halten;
log/DevTools prüfen. - Rollback: Backup zurück;
umountder Bind-Mounts; UI/AM neu starten; notfalls reboot. - Kein Flash: alle Änderungen in
/3rd_rw//data//perm/Bind-Mounts; der ro Rootfs//3rdbleibt unangetastet. - SSH/Root-Loop: nie aussperren; vor UI-/AM-Änderungen testen, dass SSH noch geht.
Templates
templates/developer.shtemplates/custom-app.jsontemplates/hide-preset.jsontemplates/custom-ui.htmltemplates/start-UI-custom.shtemplates/README.md
Dead Ends / verworfene Wege: ../dead-ends.md.